Initial Commit
218
package/contents/config/main.xml
Normal file
@@ -0,0 +1,218 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
|
||||
<kcfgfile name=""/>
|
||||
|
||||
<group name="General">
|
||||
<entry name="selectedShaderPath" type="String">
|
||||
<label>SelectedShader</label>
|
||||
<default>Shaders6/Ps3menu.frag.qsb</default>
|
||||
</entry>
|
||||
<entry name="selectedShaderContent" type="String">
|
||||
<label>selectedShaderContent</label>
|
||||
<default></default>
|
||||
</entry>
|
||||
<entry name="selectedShaderIndex" type="Int">
|
||||
<label>The index of the currently selectedShader</label>
|
||||
<default>0</default>
|
||||
</entry>
|
||||
<entry name="mouseAllowed" type="Bool">
|
||||
<label>Allow use of mouse in shader</label>
|
||||
<default>false</default>
|
||||
</entry>
|
||||
<entry name="mouseSpeedBias" type="Double">
|
||||
<label>Change the mouse's bias speed</label>
|
||||
<default>1.0</default>
|
||||
</entry>
|
||||
<entry name="running" type="Bool">
|
||||
<label>Shader is running.</label>
|
||||
<default>true</default>
|
||||
</entry>
|
||||
<entry name="checkedSmartPlay" type="Bool">
|
||||
<label>Pause the video when existing a maximized or full-screen windows.</label>
|
||||
<default>false</default>
|
||||
</entry>
|
||||
<entry name="shaderSpeed" type="Double">
|
||||
<label>Change the shader speed</label>
|
||||
<default>1.0</default>
|
||||
</entry>
|
||||
<entry name="iChannel0" type="String">
|
||||
<label>iChannel0's path</label>
|
||||
<default>./Resources/wallpaper2.png</default>
|
||||
</entry>
|
||||
<entry name="iChannel0_type" type="Int">
|
||||
<label>iChannel0's Channel Type</label>
|
||||
<default>0</default>
|
||||
</entry>
|
||||
<entry name="iChannel0_resolution_x" type="Int">
|
||||
<label>iChannel0's Resolution (Width)</label>
|
||||
<default>1920</default>
|
||||
</entry>
|
||||
<entry name="iChannel0_resolution_y" type="Int">
|
||||
<label>iChannel0's Resolution (Height)</label>
|
||||
<default>1080</default>
|
||||
</entry>
|
||||
<entry name="iChannel0_resolution_scale" type="Double">
|
||||
<label>iChannel0's Resolution (Scale)</label>
|
||||
<default>1</default>
|
||||
</entry>
|
||||
<entry name="iChannel0_flag" type="Bool">
|
||||
<label>iChannel0 on/off flag</label>
|
||||
<default>true</default>
|
||||
</entry>
|
||||
<entry name="iChannel0_timeScale" type="Double">
|
||||
<label>iChannel0 time scale</label>
|
||||
<default>1.0</default>
|
||||
</entry>
|
||||
<entry name="iChannel1" type="String">
|
||||
<label>iChannel1's path</label>
|
||||
<default>./Resources/wallpaper2.png</default>
|
||||
</entry>
|
||||
<entry name="iChannel1_type" type="Int">
|
||||
<label>iChannel1's Channel Type</label>
|
||||
<default>0</default>
|
||||
</entry>
|
||||
<entry name="iChannel1_resolution_x" type="Int">
|
||||
<label>iChannel1's Resolution (Width)</label>
|
||||
<default>1920</default>
|
||||
</entry>
|
||||
<entry name="iChannel1_resolution_y" type="Int">
|
||||
<label>iChannel1's Resolution (Height)</label>
|
||||
<default>1080</default>
|
||||
</entry>
|
||||
<entry name="iChannel1_resolution_scale" type="Double">
|
||||
<label>iChannel1's Resolution (Scale)</label>
|
||||
<default>1</default>
|
||||
</entry>
|
||||
<entry name="iChannel1_flag" type="Bool">
|
||||
<label>iChannel1 on/off flag</label>
|
||||
<default>true</default>
|
||||
</entry>
|
||||
<entry name="iChannel1_timeScale" type="Double">
|
||||
<label>iChannel1 time scale</label>
|
||||
<default>1.0</default>
|
||||
</entry>
|
||||
<entry name="iChannel2" type="String">
|
||||
<label>iChannel2's path</label>
|
||||
<default>./Resources/Shadertoy_Organic_2.jpg</default>
|
||||
</entry>
|
||||
<entry name="iChannel2_type" type="Int">
|
||||
<label>iChannel2's Channel Type</label>
|
||||
<default>0</default>
|
||||
</entry>
|
||||
<entry name="iChannel2_resolution_x" type="Int">
|
||||
<label>iChannel2's Resolution (Width)</label>
|
||||
<default>1920</default>
|
||||
</entry>
|
||||
<entry name="iChannel2_resolution_y" type="Int">
|
||||
<label>iChannel2's Resolution (Height)</label>
|
||||
<default>1080</default>
|
||||
</entry>
|
||||
<entry name="iChannel2_resolution_scale" type="Double">
|
||||
<label>iChannel2's Resolution (Scale)</label>
|
||||
<default>1</default>
|
||||
</entry>
|
||||
<entry name="iChannel2_flag" type="Bool">
|
||||
<label>iChannel2 on/off flag</label>
|
||||
<default>true</default>
|
||||
</entry>
|
||||
<entry name="iChannel2_timeScale" type="Double">
|
||||
<label>iChannel2 time scale</label>
|
||||
<default>1.0</default>
|
||||
</entry>
|
||||
<entry name="iChannel3" type="String">
|
||||
<label>iChannel3's path</label>
|
||||
<default>./Resources/Shadertoy_Organic_2.jpg</default>
|
||||
</entry>
|
||||
<entry name="iChannel3_type" type="Int">
|
||||
<label>iChannel3's Channel Type</label>
|
||||
<default>0</default>
|
||||
</entry>
|
||||
<entry name="iChannel3_resolution_x" type="Int">
|
||||
<label>iChannel3's Resolution (Width)</label>
|
||||
<default>1920</default>
|
||||
</entry>
|
||||
<entry name="iChannel3_resolution_y" type="Int">
|
||||
<label>iChannel3's Resolution (Height)</label>
|
||||
<default>1080</default>
|
||||
</entry>
|
||||
<entry name="iChannel3_resolution_scale" type="Double">
|
||||
<label>iChannel3's Resolution (Scale)</label>
|
||||
<default>1</default>
|
||||
</entry>
|
||||
<entry name="iChannel3_flag" type="Bool">
|
||||
<label>iChannel3 on/off flag</label>
|
||||
<default>false</default>
|
||||
</entry>
|
||||
<entry name="iChannel3_timeScale" type="Double">
|
||||
<label>iChannel3 time scale</label>
|
||||
<default>1.0</default>
|
||||
</entry>
|
||||
<entry name="resolution_x" type="Int">
|
||||
<label>Output Channel's Resolution (Width)</label>
|
||||
<default>1920</default>
|
||||
</entry>
|
||||
<entry name="resolution_y" type="Int">
|
||||
<label>Output Channel's Resolution (Height)</label>
|
||||
<default>1080</default>
|
||||
</entry>
|
||||
<entry name="resolution_scale" type="Double">
|
||||
<label>Output resolution scale</label>
|
||||
<default>1.0</default>
|
||||
</entry>
|
||||
<entry name="framerate_limit" type="Double">
|
||||
<label>Frame Rate Limit</label>
|
||||
<default>60</default>
|
||||
</entry>
|
||||
<entry name="shader_package" type="String">
|
||||
<label>Shader Package</label>
|
||||
<default></default>
|
||||
</entry>
|
||||
<entry name="komplex_mode" type="int">
|
||||
<label>Use the Komplex Mode</label>
|
||||
<default>0</default>
|
||||
</entry>
|
||||
<entry name="fadeToNext" type="Bool">
|
||||
<label>fade to next shader in folder</label>
|
||||
<default>false</default>
|
||||
</entry>
|
||||
<entry name="fadeToRandom" type="Bool">
|
||||
<label>fade to random shader in folder</label>
|
||||
<default>false</default>
|
||||
</entry>
|
||||
<entry name="infoPlasma6Preview_dismissed" type="Bool">
|
||||
<label>Hide the infoPlasma6Preview message</label>
|
||||
<default>false</default>
|
||||
</entry>
|
||||
<entry name="warningResources_dismissed" type="Bool">
|
||||
<label>Hide the warningResources message</label>
|
||||
<default>false</default>
|
||||
</entry>
|
||||
<entry name="emergencyHelp_dismissed" type="Bool">
|
||||
<label>Hide the emergencyHelp message</label>
|
||||
<default>false</default>
|
||||
</entry>
|
||||
<entry name="infoiChannelSettings_dismissed" type="Bool">
|
||||
<label>Hide the infoiChannelSettings message</label>
|
||||
<default>false</default>
|
||||
</entry>
|
||||
<entry name="pauseMode" type="Int">
|
||||
<label>Shader pause mode
|
||||
0: when existing a maximized or full-screen windows
|
||||
1: when a visible window is active
|
||||
2: when a window is visible
|
||||
3: never</label>
|
||||
<default>0</default>
|
||||
</entry>
|
||||
<entry name="checkActiveScreen" type="Bool">
|
||||
<label>Whether or not check for windows from all screens instead of just the active one</label>
|
||||
<default>true</default>
|
||||
</entry>
|
||||
<entry name="excludeWindows" type="StringList">
|
||||
<label>A list of app-IDs whose windows to exclude from triggering pauseMode.</label>
|
||||
<default></default>
|
||||
</entry>
|
||||
</group>
|
||||
|
||||
</kcfg>
|
||||
207
package/contents/ui/KomplexModel.qml
Normal file
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Komplex Wallpaper Engine
|
||||
* Copyright (C) 2025 @DigitalArtifex | github.com/DigitalArtifex
|
||||
*
|
||||
* WallpaperModel.qml
|
||||
*
|
||||
* This component provides a model for parsing and managing wallpaper configurations from
|
||||
* a JSON file, specifically designed for the Komplex Wallpaper Engine.
|
||||
*
|
||||
* This enables infinite complexity in shader configurations
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
|
||||
import com.github.digitalartifex.komplex 1.0 as Komplex
|
||||
|
||||
Item
|
||||
{
|
||||
property real pixelRatio: 1 //This will (hopefully) be set to PlasmaCore.Units.devicePixelRatio in onCompleted
|
||||
property vector3d iResolution: Qt.vector3d(wallpaper.configuration.resolution_x ? wallpaper.configuration.resolution_x : 1920, wallpaper.configuration.resolution_y ? wallpaper.configuration.resolution_y : 1080, 1)//width, height, pixel aspect ratio
|
||||
property real iTime: 0 //used by most motion shaders
|
||||
property real iTimeDelta: iTime
|
||||
property var iChannelTime: [iTime, iTime, iTime, iTime] //individual channel time values
|
||||
property real iSampleRate: 44100 //used by audio shaders
|
||||
property int iFrame: 0
|
||||
property real iFrameRate: wallpaper.configuration.framerate_limit ? wallpaper.configuration.framerate_limit : 60 // Default frame rate for the shader
|
||||
property vector4d iMouse
|
||||
property var iDate
|
||||
property bool running: true
|
||||
|
||||
// Individual channel resolutions to customize performance and quality
|
||||
property var iChannelResolution: [Qt.vector3d(wallpaper.configuration.iChannel0_resolution_x, wallpaper.configuration.iChannel0_resolution_y, pixelRatio),
|
||||
Qt.vector3d(wallpaper.configuration.iChannel1_resolution_x, wallpaper.configuration.iChannel1_resolution_y, pixelRatio),
|
||||
Qt.vector3d(wallpaper.configuration.iChannel2_resolution_x, wallpaper.configuration.iChannel2_resolution_y, pixelRatio),
|
||||
Qt.vector3d(wallpaper.configuration.iChannel3_resolution_x, wallpaper.configuration.iChannel3_resolution_y, pixelRatio)]
|
||||
|
||||
property string iChannel0: ""
|
||||
property string iChannel1: ""
|
||||
property string iChannel2: ""
|
||||
property string iChannel3: ""
|
||||
|
||||
id: mainItem
|
||||
|
||||
Komplex.ShaderPackModel
|
||||
{
|
||||
id: shaderPackModel
|
||||
onJsonChanged:
|
||||
{
|
||||
// Handle the JSON change if needed
|
||||
console.log("Shader pack JSON changed:", shaderPackModel.json);
|
||||
mainItem.parsePack(shaderPackModel.json);
|
||||
}
|
||||
}
|
||||
|
||||
// The WindowModel is used to manage the interaction with the desktop environment
|
||||
WindowModel
|
||||
{
|
||||
id: windowModel
|
||||
screenGeometry: mainItem.parent.screenGeometry
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
anchors.fill: parent
|
||||
color: "black"
|
||||
|
||||
// The output channel that combines all the input channels and displays the final shader output
|
||||
// This channel must be set to a shader source file that has been pre-compiled to a QSB Fragment Shader
|
||||
ShaderChannel
|
||||
{
|
||||
iTime: mainItem.iTime
|
||||
iMouse: mainItem.iMouse
|
||||
iResolution: mainItem.iResolution
|
||||
|
||||
id: channelOutput
|
||||
anchors.fill: parent
|
||||
type: ShaderChannel.Type.ShaderChannel
|
||||
|
||||
visible: true // Set to true to display the output
|
||||
z: 9000
|
||||
}
|
||||
|
||||
// To save on performance, just use one timer for all channels and shaders
|
||||
// they will need to be bound to the mainItem properties they need to access
|
||||
Timer
|
||||
{
|
||||
id: channelTimer
|
||||
|
||||
//Not entirely sure if this will actually limit the frame rate
|
||||
interval: (1 / mainItem.iFrameRate) * 1000 //fps to ms cycles :: fps = 60 = 1 / 60 = 0.01666 * 1000 = 16
|
||||
repeat: true
|
||||
|
||||
running: true //wallpaper.configuration.running ? mainItem.running : true
|
||||
|
||||
triggeredOnStart: true
|
||||
onTriggered:
|
||||
{
|
||||
var date = new Date();
|
||||
var startOfDay = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
|
||||
var secondsSinceMidnight = (date - startOfDay) / 1000;
|
||||
|
||||
mainItem.iTime += (interval / 1000) * (wallpaper.configuration.shaderSpeed ? wallpaper.configuration.shaderSpeed : 1.0)
|
||||
mainItem.iDate = Qt.vector4d(date.getFullYear(), date.getMonth() + 1, date.getDate(), secondsSinceMidnight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setup the connection to shader pack model for updates
|
||||
Connections
|
||||
{
|
||||
target: shaderPackModel
|
||||
|
||||
function onJsonChanged()
|
||||
{
|
||||
// Parse the JSON configuration and set the properties of the ShaderChannel
|
||||
// mainItem.parsePack(shaderPackModel.json);
|
||||
}
|
||||
}
|
||||
|
||||
// Load the default shader pack configuration on component completion
|
||||
Component.onCompleted:
|
||||
{
|
||||
if(wallpaper.configuration.shader_package)
|
||||
shaderPackModel.loadJson(wallpaper.configuration.shader_package);
|
||||
else
|
||||
shaderPackModel.loadJson("/home/parametheus/kde/src/komplex/pack.json"); // Load a default shader pack if none is specified
|
||||
}
|
||||
|
||||
// Function to parse the pack.json file and set the properties of the ShaderChannel
|
||||
function parsePack(json)
|
||||
{
|
||||
var pack = JSON.parse(json);
|
||||
var currentChannel = null;
|
||||
|
||||
if (pack.channel0)
|
||||
channelOutput.iChannel0 = parseChannel(pack.channel0);
|
||||
if (pack.channel1)
|
||||
channelOutput.iChannel1 = parseChannel(pack.channel1);
|
||||
if (pack.channel2)
|
||||
channelOutput.iChannel2 = parseChannel(pack.channel2);
|
||||
if (pack.channel3)
|
||||
channelOutput.iChannel3 = parseChannel(pack.channel3);
|
||||
|
||||
var source
|
||||
|
||||
// Ensure the source path is correctly resolved for relative paths
|
||||
if(!pack.source.startsWith("/") && !pack.source.startsWith("file://"))
|
||||
source = Qt.resolvedUrl(shaderPackModel.shaderPackPath + "/" + pack.source);
|
||||
else if(!pack.source.startsWith("file://"))
|
||||
source = Qt.resolvedUrl("file://" + pack.source);
|
||||
|
||||
channelOutput.source = "file://" + (source || ""); // Set the shader source file
|
||||
channelOutput.type = ShaderChannel.Type.ShaderChannel; // Set the shader
|
||||
}
|
||||
|
||||
// Recursive helper function to parse channels
|
||||
function parseChannel(channel)
|
||||
{
|
||||
if (!channel) return;
|
||||
|
||||
var source
|
||||
|
||||
// Ensure the source path is correctly resolved for relative paths
|
||||
if(!channel.source.startsWith("/") && !channel.source.startsWith("file://"))
|
||||
source = Qt.resolvedUrl(shaderPackModel.shaderPackPath + "/" + channel.source);
|
||||
else if(!channel.source.startsWith("file://"))
|
||||
source = Qt.resolvedUrl("file://" + channel.source);
|
||||
|
||||
var result = Qt.createQmlObject(`ShaderChannel
|
||||
{
|
||||
z: 0
|
||||
iTimeScale: ${channel.time_scale || 1.0}
|
||||
iResolutionScale: ${channel.resolution_scale || 1.0}
|
||||
visible: false
|
||||
anchors.fill: parent
|
||||
source: "file://" + "${source || ''}"
|
||||
type: ${channel.type || 0}
|
||||
iTime: mainItem.iTime * ${channel.time_scale || 1.0}
|
||||
iMouse: mainItem.iMouse
|
||||
iResolution: Qt.vector3d(${(channel.resolution_x || mainItem.width) * (channel.resolution_scale || 1.0)}, ${(channel.resolution_y || mainItem.width) * (channel.resolution_scale || 1.0)}, pixelRatio)
|
||||
}`, mainItem);
|
||||
|
||||
if (channel.channel0)
|
||||
result.iChannel0 = parseChannel(channel.channel0);
|
||||
if (channel.channel1)
|
||||
result.iChannel1 = parseChannel(channel.channel1);
|
||||
if (channel.channel2)
|
||||
result.iChannel2 = parseChannel(channel.channel2);
|
||||
if (channel.channel3)
|
||||
result.iChannel3 = parseChannel(channel.channel3);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
422
package/contents/ui/ShaderChannel.qml
Normal file
@@ -0,0 +1,422 @@
|
||||
/*
|
||||
* Komplex Wallpaper Engine
|
||||
* Copyright (C) 2025 @DigitalArtifex | github.com/DigitalArtifex
|
||||
*
|
||||
* ShaderChannel.qml
|
||||
*
|
||||
* This component provides a configurable channel type to more closely resemble how ShaderToys
|
||||
* works. It can be configured to be an Image, Video, Shader, Audio, or CubeMap that can be
|
||||
* used as the input channel of another ShaderChannel, allowing for more complex shader compositions.
|
||||
*
|
||||
* It would also appear that in order to support the audio channel, we will need to
|
||||
* implement a way to capture the desktop audio in C++, as the MediaPlayer component does not support this.
|
||||
* This will have to come later.
|
||||
*
|
||||
* Volumetric shaders do not seem to be widely used in ShaderToys, so it has not been implemented here.
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtMultimedia
|
||||
import QtQuick3D
|
||||
|
||||
import com.github.digitalartifex.komplex 1.0 as Komplex
|
||||
|
||||
Item
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
ImageChannel,
|
||||
VideoChannel,
|
||||
ShaderChannel,
|
||||
CubeMapChannel
|
||||
// AudioChannel
|
||||
}
|
||||
|
||||
property int type: ShaderChannel.Type.ImageChannel
|
||||
property string source: ""
|
||||
|
||||
property int fillMode: Image.PreserveAspectCrop
|
||||
|
||||
property var iChannel0: null
|
||||
property var iChannel1: null
|
||||
property var iChannel2: null
|
||||
property var iChannel3: null
|
||||
|
||||
property vector3d iResolution
|
||||
property real iResolutionScale: 1 // This is used to scale the resolution of the shader, allowing for performance optimizations
|
||||
property real iTime: 0 //used by most motion shaders
|
||||
property real lastITime: 0
|
||||
property real iTimeDelta: 0
|
||||
property var iChannelTime: [iTimeActual, iTimeActual, iTimeActual, iTimeActual] //individual channel time values
|
||||
property real iSampleRate: 44100 //used by audio shaders
|
||||
property int iFrame: 0
|
||||
property var iFrameRate: 60
|
||||
property vector4d iMouse
|
||||
property var iDate
|
||||
property real iTimeActual: 0 // This is used to track the actual time for the shader, according to the time scale
|
||||
property real iTimeScale: 1 // This is used to scale the time for the shader, allowing for slow motion or fast forward effects per channel
|
||||
|
||||
property var iChannelResolution: [Qt.vector3d(channel.width * iResolutionScale, channel.height * iResolutionScale, 1), Qt.vector3d(channel.width * iResolutionScale, channel.height * iResolutionScale, 1), Qt.vector3d(channel.width * iResolutionScale, channel.height * iResolutionScale, 1)]
|
||||
|
||||
onITimeChanged:
|
||||
{
|
||||
iTimeDelta = iTime - lastITime
|
||||
lastITime = iTime
|
||||
iTimeActual += iTimeDelta * iTimeScale
|
||||
}
|
||||
|
||||
id: channel
|
||||
visible: false // Set to false by default, main shader needs be set to true in MainWindow.qml
|
||||
|
||||
// This is used to dynamically load the appropriate channel type based on the `type` property of the channel
|
||||
Loader
|
||||
{
|
||||
id: loader
|
||||
anchors.fill: parent
|
||||
|
||||
sourceComponent: channelImage
|
||||
|
||||
states:[
|
||||
State
|
||||
{
|
||||
when: channel.type === ShaderChannel.Type.ImageChannel
|
||||
PropertyChanges
|
||||
{
|
||||
loader.sourceComponent: channelImage
|
||||
}
|
||||
},
|
||||
State
|
||||
{
|
||||
when: channel.type === ShaderChannel.Type.VideoChannel
|
||||
PropertyChanges
|
||||
{
|
||||
loader.sourceComponent: channelVideo
|
||||
}
|
||||
},
|
||||
State
|
||||
{
|
||||
when: channel.type === ShaderChannel.Type.CubeMapChannel
|
||||
PropertyChanges
|
||||
{
|
||||
loader.sourceComponent: channelCubeMap
|
||||
}
|
||||
},
|
||||
State
|
||||
{
|
||||
when: channel.type === ShaderChannel.Type.ShaderChannel
|
||||
PropertyChanges
|
||||
{
|
||||
loader.sourceComponent: channelShader
|
||||
}
|
||||
}
|
||||
// State
|
||||
// {
|
||||
// when: channel.type === ShaderChannel.Type.AudioChannel
|
||||
// PropertyChanges
|
||||
// {
|
||||
// loader.sourceComponent: channelAudio
|
||||
// }
|
||||
// }
|
||||
]
|
||||
}
|
||||
|
||||
//The image channel will be the default channel type for backwards compatability
|
||||
Component
|
||||
{
|
||||
id: channelImage
|
||||
|
||||
Image
|
||||
{
|
||||
anchors.fill: parent
|
||||
source: Qt.resolvedUrl(channel.source)
|
||||
fillMode: channel.fillMode
|
||||
}
|
||||
}
|
||||
|
||||
//Video channel
|
||||
Component
|
||||
{
|
||||
id: channelVideo
|
||||
|
||||
VideoOutput
|
||||
{
|
||||
property alias duration: mediaPlayer.duration
|
||||
property alias mediaSource: mediaPlayer.source
|
||||
property alias metaData: mediaPlayer.metaData
|
||||
property alias playbackRate: mediaPlayer.playbackRate
|
||||
property alias position: mediaPlayer.position
|
||||
property alias seekable: mediaPlayer.seekable
|
||||
property alias volume: audioOutput.volume
|
||||
|
||||
signal sizeChanged
|
||||
signal fatalError
|
||||
|
||||
id: videoOutput
|
||||
|
||||
visible: true
|
||||
anchors.fill: parent
|
||||
fillMode: channel.fillMode
|
||||
|
||||
onHeightChanged: this.sizeChanged()
|
||||
|
||||
MediaPlayer
|
||||
{
|
||||
id: mediaPlayer
|
||||
videoOutput: videoOutput
|
||||
loops: MediaPlayer.Infinite
|
||||
source: Qt.resolvedUrl(channel.source)
|
||||
|
||||
audioOutput: AudioOutput
|
||||
{
|
||||
id: audioOutput
|
||||
volume: 0
|
||||
}
|
||||
|
||||
onErrorOccurred: function(error, errorString)
|
||||
{
|
||||
if (MediaPlayer.NoError !== error)
|
||||
{
|
||||
console.log("[qmlvideo] VideoItem.onError error " + error + " errorString " + errorString)
|
||||
videoOutput.fatalError()
|
||||
}
|
||||
}
|
||||
|
||||
onSourceChanged:
|
||||
{
|
||||
if(mediaPlayer.source != "")
|
||||
mediaPlayer.play()
|
||||
else
|
||||
mediaPlayer.stop()
|
||||
}
|
||||
}
|
||||
|
||||
function start() { mediaPlayer.play() }
|
||||
function stop() { mediaPlayer.stop() }
|
||||
function seek(position) { mediaPlayer.setPosition(position); }
|
||||
}
|
||||
}
|
||||
|
||||
// A shader channel can be a shader
|
||||
Component
|
||||
{
|
||||
id: channelShader
|
||||
|
||||
Item
|
||||
{
|
||||
// Setup the shader effect sources for each channel
|
||||
// These are needed to provide the uniform data to the shader channel buffers
|
||||
ShaderEffectSource
|
||||
{
|
||||
id: channelSource0
|
||||
sourceItem: channel.iChannel0 ? channel.iChannel0 : null
|
||||
live: true
|
||||
smooth: true
|
||||
sourceRect: Qt.rect(0,0, sourceItem ? sourceItem.width : 0, sourceItem ? sourceItem.height : 0)
|
||||
}
|
||||
|
||||
ShaderEffectSource
|
||||
{
|
||||
id: channelSource1
|
||||
sourceItem: channel.iChannel1 ? channel.iChannel1 : null
|
||||
live: true
|
||||
smooth: true
|
||||
sourceRect: Qt.rect(0,0, sourceItem ? sourceItem.width : 0, sourceItem ? sourceItem.height : 0)
|
||||
}
|
||||
|
||||
ShaderEffectSource
|
||||
{
|
||||
id: channelSource2
|
||||
sourceItem: channel.iChannel2 ? channel.iChannel2 : null
|
||||
live: true
|
||||
smooth: true
|
||||
sourceRect: Qt.rect(0,0, sourceItem ? sourceItem.width : 0, sourceItem ? sourceItem.height : 0)
|
||||
}
|
||||
|
||||
ShaderEffectSource
|
||||
{
|
||||
id: channelSource3
|
||||
sourceItem: channel.iChannel3 ? channel.iChannel3 : null
|
||||
live: true
|
||||
smooth: true
|
||||
sourceRect: Qt.rect(0,0, sourceItem ? sourceItem.width : 0, sourceItem ? sourceItem.height : 0)
|
||||
}
|
||||
|
||||
// The shader effect that will be used to render the shader
|
||||
ShaderEffect
|
||||
{
|
||||
property var iChannel0: channelSource0
|
||||
property var iChannel1: channelSource1
|
||||
property var iChannel2: channelSource2
|
||||
property var iChannel3: channelSource3
|
||||
|
||||
property var iResolution: channel.iResolution
|
||||
property var iTime: channel.iTimeActual
|
||||
property var iTimeDelta: channel.iTimeDelta
|
||||
property var iChannelTime: channel.iChannelTime
|
||||
property var iSampleRate: channel.iSampleRate
|
||||
property var iFrame: channel.iFrame
|
||||
property var iFrameRate: channel.iFrameRate
|
||||
property var iMouse: channel.iMouse
|
||||
property var iDate: channel.iDate
|
||||
|
||||
property var iChannelResolution: channel.iResolution
|
||||
|
||||
fragmentShader: Qt.resolvedUrl(channel.source)
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//In order to support CubeMaps, we will need to select a folder that contains a series of JPGs, named as described
|
||||
//in the CubeMapTexture documentation ie "posx.jpg;negx.jpg;posy.jpg;negy.jpg;posz.jpg;negz.jpg"
|
||||
Component
|
||||
{
|
||||
id: channelCubeMap
|
||||
|
||||
View3D
|
||||
{
|
||||
id: view3d
|
||||
anchors.fill: parent
|
||||
|
||||
property real lastX: 0
|
||||
property real lastY: 0
|
||||
property bool mousePressed: false
|
||||
property real yaw: 0
|
||||
property real pitch: 0
|
||||
|
||||
environment: SceneEnvironment
|
||||
{
|
||||
backgroundMode: SceneEnvironment.SkyBoxCubeMap
|
||||
skyBoxCubeMap: CubeMapTexture
|
||||
{
|
||||
source: channel.source !== "" ? Qt.resolvedUrl(channel.source) + "/%p.jpg" : ""
|
||||
}
|
||||
}
|
||||
|
||||
camera: PerspectiveCamera
|
||||
{
|
||||
id: camera
|
||||
position: Qt.vector3d(0, 0, 10)
|
||||
}
|
||||
|
||||
function updateCamera()
|
||||
{
|
||||
yaw -= (channel.iMouse.x - lastX) * 0.5
|
||||
pitch -= (channel.iMouse.y - lastY) * -0.5
|
||||
pitch = Math.max(-89, Math.min(89, pitch))
|
||||
lastX = channel.iMouse.x
|
||||
lastY = channel.iMouse.y
|
||||
|
||||
var radYaw = yaw * Math.PI / 180
|
||||
var radPitch = pitch * Math.PI / 180
|
||||
var x = Math.cos(radPitch) * Math.sin(radYaw)
|
||||
var y = Math.sin(radPitch)
|
||||
var z = Math.cos(radPitch) * Math.cos(radYaw)
|
||||
|
||||
camera.eulerRotation = Qt.vector3d((radPitch * 180 / Math.PI), (radYaw * 180 / Math.PI), 0)
|
||||
}
|
||||
|
||||
Connections
|
||||
{
|
||||
target: channel
|
||||
function onIMouseChanged()
|
||||
{
|
||||
view3d.updateCamera()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Supporting this as an mp3 feels kinda silly. Should really figure out how to capture desktop audio with qml
|
||||
// UPDATE: This is not currently supported in QML, so we will need to implement this in C++ later
|
||||
// Component
|
||||
// {
|
||||
// id: channelAudio
|
||||
// Rectangle
|
||||
// {
|
||||
// anchors.fill: parent
|
||||
// color: "black"
|
||||
|
||||
// property var audioData: new Array(512).fill(0)
|
||||
// property alias texture: audioTexture
|
||||
|
||||
// MediaDevices
|
||||
// {
|
||||
// id: devices
|
||||
// }
|
||||
|
||||
// AudioInput
|
||||
// {
|
||||
// id: audioInput
|
||||
// device: devices.defaultAudioInput
|
||||
// }
|
||||
|
||||
// CaptureSession
|
||||
// {
|
||||
// id: captureSession
|
||||
// audioInput: audioInput
|
||||
// }
|
||||
|
||||
// Timer
|
||||
// {
|
||||
// interval: 16 // ~60fps update
|
||||
// running: true
|
||||
// repeat: true
|
||||
// onTriggered:
|
||||
// {
|
||||
// // Get audio levels and update texture
|
||||
// // let buffer = audioInput.probe.audioBuffer()
|
||||
// // if (buffer)
|
||||
// // {
|
||||
// // for (let i = 0; i < Math.min(buffer.length, 512); i++) {
|
||||
// // audioData[i] = Math.abs(buffer[i])
|
||||
// // }
|
||||
// // audioTexture.update()
|
||||
// // }
|
||||
// }
|
||||
// }
|
||||
|
||||
// ShaderEffectSource
|
||||
// {
|
||||
// id: audioTexture
|
||||
// width: 512
|
||||
// height: 1
|
||||
// format: ShaderEffectSource.Alpha
|
||||
// smooth: false
|
||||
// hideSource: true
|
||||
// sourceItem: Row
|
||||
// {
|
||||
// Repeater
|
||||
// {
|
||||
// model: 512
|
||||
// Rectangle
|
||||
// {
|
||||
// width: 1
|
||||
// height: 1
|
||||
// color: Qt.rgba(1, 1, 1, root.audioData[index])
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// Component.onCompleted:
|
||||
// {
|
||||
// //captureSession.start()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
623
package/contents/ui/ShaderChannelConfiguration.qml
Normal file
@@ -0,0 +1,623 @@
|
||||
/*
|
||||
* Komplex Wallpaper Engine
|
||||
* Copyright (C) 2025 @DigitalArtifex | github.com/DigitalArtifex
|
||||
*
|
||||
* ShaderChannelConfiguration.qml
|
||||
*
|
||||
* This component is used to configure the shader channels for the output shader.
|
||||
* It allows the user to select a file or folder for each channel type (Image,
|
||||
* Video, Shader, Audio, CubeMap) and sets the appropriate properties on the
|
||||
* ShaderChannel component.
|
||||
*
|
||||
* --------------------------------------------------------------------------------------------------------
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtCore
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
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 QtQuick.Dialogs as Dialogs
|
||||
import com.github.digitalartifex.komplex 1.0 as Komplex
|
||||
|
||||
Item
|
||||
{
|
||||
signal accepted()
|
||||
signal rejected()
|
||||
|
||||
property bool file: true
|
||||
property string selectionTitle: "Select a file"
|
||||
property var selectionFilter: ["All Files (*)"]
|
||||
|
||||
// The current folder is used to set the initial folder for the file and folder dialogs
|
||||
property string currentFolder: StandardPaths.standardLocations(StandardPaths.HomeLocation)[0]
|
||||
|
||||
// Individual folders for each channel type
|
||||
property string shaderFolder: shaderPackModel.shadersPath
|
||||
property string imageFolder: shaderPackModel.imagesPath
|
||||
property string videoFolder: shaderPackModel.videosPath
|
||||
property string cubemapFolder: shaderPackModel.cubeMapsPath
|
||||
|
||||
property alias tmp_source: sourceEdit.text
|
||||
property int tmp_type: 1
|
||||
property alias tmp_timeScale: speedSlider.value
|
||||
property alias tmp_resolution_scale: resolutionScaleSlider.value
|
||||
property alias tmp_resolution_x: resolutionXEdit.value
|
||||
property alias tmp_resolution_y: resolutionYEdit.value
|
||||
property bool tmp_enabled: tmp_source !== ""
|
||||
property Palette palette
|
||||
|
||||
property string source
|
||||
property int type
|
||||
property real timeScale
|
||||
property real resolution_scale
|
||||
property int resolution_x
|
||||
property int resolution_y
|
||||
property bool enabled
|
||||
|
||||
id: window
|
||||
|
||||
Komplex.ShaderPackModel
|
||||
{
|
||||
id: shaderPackModel
|
||||
}
|
||||
|
||||
// The selection model contains the list of available shader channel types
|
||||
// and their respective properties.
|
||||
ListModel
|
||||
{
|
||||
id: selectionModel
|
||||
|
||||
// Commented out Audio channel as it is not implemented yet
|
||||
// ListElement
|
||||
// {
|
||||
// file: true
|
||||
// name: "Audio"
|
||||
// icon: "qrc:/icons/audio.svg"
|
||||
// title: "Select an Audio File"
|
||||
// filter: "MP3 Files (*.mp3):WAV Files (*.wav)"
|
||||
// type: ShaderChannel.Type.AudioChannel
|
||||
// }
|
||||
|
||||
ListElement
|
||||
{
|
||||
file: false
|
||||
name: "Cubemap"
|
||||
icon: "./icons/cube.svg"
|
||||
title: "Select a CubeMap folder"
|
||||
filter: ""
|
||||
type: ShaderChannel.Type.CubeMapChannel
|
||||
}
|
||||
|
||||
ListElement
|
||||
{
|
||||
file: true
|
||||
name: "Image"
|
||||
icon: "./icons/image.svg"
|
||||
title: "Select an Image File"
|
||||
filter: "Image Files (*.jpg, *.jpeg, *.png, *.svg)"
|
||||
type: ShaderChannel.Type.ImageChannel
|
||||
}
|
||||
|
||||
ListElement
|
||||
{
|
||||
file: true
|
||||
name: "Shader"
|
||||
icon: "./icons/3d-glasses.svg"
|
||||
title: "Select a Shader File"
|
||||
filter: "Shader Files (*frag.qsb)"
|
||||
type: ShaderChannel.Type.ShaderChannel
|
||||
}
|
||||
|
||||
ListElement
|
||||
{
|
||||
file: true
|
||||
name: "Video"
|
||||
icon: "./icons/video.svg"
|
||||
title: "Select a Video File"
|
||||
filter: "Video Files (*.mov, *.avi, *.mkv, *.mp4)"
|
||||
type: ShaderChannel.Type.VideoChannel
|
||||
}
|
||||
|
||||
function indexOf(type)
|
||||
{
|
||||
for(var i = 0; i < count; ++i) if (get(i).type == type)
|
||||
return i
|
||||
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
Component
|
||||
{
|
||||
id: selectionDelegate
|
||||
|
||||
Item
|
||||
{
|
||||
required property int index
|
||||
required property int type
|
||||
required property string name
|
||||
required property string icon
|
||||
required property string title
|
||||
required property var filter
|
||||
required property bool file
|
||||
|
||||
width: 100
|
||||
height: 75
|
||||
|
||||
ColumnLayout
|
||||
{
|
||||
anchors.fill: parent
|
||||
Image
|
||||
{
|
||||
source: icon
|
||||
|
||||
Layout.preferredHeight: 50
|
||||
Layout.preferredWidth: 50
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
id: label
|
||||
text: name
|
||||
color: "white"
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
||||
Layout.fillHeight: false
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea
|
||||
{
|
||||
hoverEnabled: true
|
||||
anchors.fill: parent
|
||||
z: 9000 // this is dumb but I can't get the mouse area to propogate without it
|
||||
|
||||
onClicked:
|
||||
{
|
||||
list.currentIndex = parent.index
|
||||
window.tmp_type = parent.type
|
||||
window.selectionFilter = parent.filter.split(':')
|
||||
window.selectionTitle = parent.title
|
||||
window.file = parent.file
|
||||
|
||||
switch(parent.type)
|
||||
{
|
||||
// case ShaderChannel.Type.AudioChannel:
|
||||
// break;
|
||||
case ShaderChannel.Type.CubeMapChannel:
|
||||
window.currentFolder = window.cubemapFolder
|
||||
break;
|
||||
case ShaderChannel.Type.ImageChannel:
|
||||
window.currentFolder = window.imageFolder
|
||||
break;
|
||||
case ShaderChannel.Type.ShaderChannel:
|
||||
window.currentFolder = window.shaderFolder
|
||||
break;
|
||||
case ShaderChannel.Type.VideoChannel:
|
||||
window.currentFolder = window.videoFolder
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component
|
||||
{
|
||||
id: highlight
|
||||
Rectangle
|
||||
{
|
||||
width: list.currentItem.width; height: list.currentItem.height
|
||||
color: "lightsteelblue"; radius: 5
|
||||
y: list.currentItem.y
|
||||
Behavior on y
|
||||
{
|
||||
SpringAnimation
|
||||
{
|
||||
spring: 2
|
||||
damping: 0.1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout
|
||||
{
|
||||
anchors.fill: parent
|
||||
spacing: 10
|
||||
|
||||
ListView
|
||||
{
|
||||
id: list
|
||||
model: selectionModel
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 100
|
||||
Layout.alignment: Qt.AlignTop
|
||||
|
||||
delegate: selectionDelegate
|
||||
orientation: Qt.Horizontal
|
||||
clip: true
|
||||
|
||||
highlight: highlight
|
||||
highlightFollowsCurrentItem: true
|
||||
focus: true
|
||||
}
|
||||
|
||||
RowLayout
|
||||
{
|
||||
Layout.alignment: Qt.AlignTop
|
||||
|
||||
Label
|
||||
{
|
||||
color: palette.text
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
text: i18nd("@option:shader_source_label", "Source")
|
||||
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 6
|
||||
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
|
||||
}
|
||||
|
||||
TextField
|
||||
{
|
||||
id: sourceEdit
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
|
||||
}
|
||||
|
||||
Button
|
||||
{
|
||||
icon.name: "folder-symbolic"
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 2
|
||||
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
|
||||
|
||||
onClicked:
|
||||
{
|
||||
if(window.file === true)
|
||||
fileDialog.open()
|
||||
else
|
||||
folderDialog.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout
|
||||
{
|
||||
Layout.alignment: Qt.AlignTop
|
||||
id: speedLayout
|
||||
|
||||
Kirigami.FormData.label: i18nd("com.github.digitalartifex.komplex", "Shader speed:")
|
||||
|
||||
Label
|
||||
{
|
||||
color: palette.text
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 6
|
||||
|
||||
text: i18nd("@option:time_scale_label", "Shader Speed")
|
||||
}
|
||||
|
||||
Slider
|
||||
{
|
||||
id: speedSlider
|
||||
Layout.fillWidth: true
|
||||
from: 0
|
||||
to: 16
|
||||
stepSize: 0.1
|
||||
onValueChanged: shaderSpeedField.text = String(value.toFixed(2));
|
||||
}
|
||||
|
||||
TextField
|
||||
{
|
||||
id: shaderSpeedField
|
||||
inputMethodHints: Qt.ImhFormattedNumbersOnly
|
||||
horizontalAlignment: Text.AlignRight
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 4
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout
|
||||
{
|
||||
Layout.alignment: Qt.AlignTop
|
||||
id: resolutionScaleLayout
|
||||
|
||||
Kirigami.FormData.label: i18nd("com.github.digitalartifex.komplex", "Shader speed:")
|
||||
|
||||
Label
|
||||
{
|
||||
color: palette.text
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
text: i18nd("@option:time_scale_label", "Resolution Scale")
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 6
|
||||
}
|
||||
|
||||
Slider
|
||||
{
|
||||
id: resolutionScaleSlider
|
||||
Layout.fillWidth: true
|
||||
from: 0.125
|
||||
to: 1
|
||||
stepSize: 0.01
|
||||
onValueChanged: resolutionScaleField.text = String(value.toFixed(3));
|
||||
}
|
||||
|
||||
TextField
|
||||
{
|
||||
id: resolutionScaleField
|
||||
inputMethodHints: Qt.ImhFormattedNumbersOnly
|
||||
horizontalAlignment: Text.AlignRight
|
||||
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 4
|
||||
|
||||
onEditingFinished: () =>
|
||||
{
|
||||
let inputValue = parseFloat(text);
|
||||
|
||||
if (isNaN(inputValue) || inputValue < resolutionScaleSlider.from)
|
||||
inputValue = resolutionScaleSlider.from;
|
||||
else if (inputValue > resolutionScaleSlider.to)
|
||||
inputValue = resolutionScaleSlider.to;
|
||||
|
||||
text = inputValue.toFixed(3);
|
||||
resolutionScaleSlider.value = inputValue;
|
||||
}
|
||||
Keys.onPressed: (event) =>
|
||||
{
|
||||
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter)
|
||||
{
|
||||
resolutionScaleField.focus = false; // Unfocus the TextField
|
||||
event.accepted = true; // Prevent further propagation of the key event
|
||||
}
|
||||
}
|
||||
background: Rectangle
|
||||
{
|
||||
color: resolutionScaleField.activeFocus ? palette.base : "transparent"
|
||||
border.color: resolutionScaleField.activeFocus ? palette.highlight : "transparent"
|
||||
border.width: 1
|
||||
radius: 4
|
||||
anchors.fill: resolutionScaleField
|
||||
anchors.margins: -2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout
|
||||
{
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
|
||||
Label
|
||||
{
|
||||
color: palette.text
|
||||
text: i18nd("@option:resolution_label_x", "Resolution X")
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 6
|
||||
}
|
||||
|
||||
TextField
|
||||
{
|
||||
property int value
|
||||
|
||||
Layout.preferredHeight: 35
|
||||
Layout.fillWidth: true
|
||||
|
||||
id: resolutionXEdit
|
||||
text: value
|
||||
onEditingFinished: () =>
|
||||
{
|
||||
var inputValue = parseInt(text);
|
||||
|
||||
if (isNaN(inputValue) || inputValue < 0)
|
||||
inputValue = 0
|
||||
|
||||
value = inputValue;
|
||||
}
|
||||
Keys.onPressed: (event) =>
|
||||
{
|
||||
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter)
|
||||
{
|
||||
resolutionScaleField.focus = false; // Unfocus the TextField
|
||||
event.accepted = true; // Prevent further propagation of the key event
|
||||
}
|
||||
}
|
||||
}
|
||||
Label
|
||||
{
|
||||
color: palette.text
|
||||
text: i18nd("@option:resolution_label_y", "Resolution Y")
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 6
|
||||
}
|
||||
|
||||
TextField
|
||||
{
|
||||
property int value
|
||||
|
||||
Layout.preferredHeight: 35
|
||||
Layout.fillWidth: true
|
||||
|
||||
id: resolutionYEdit
|
||||
text: value
|
||||
onEditingFinished: () =>
|
||||
{
|
||||
var inputValue = parseInt(text);
|
||||
|
||||
if (isNaN(inputValue) || inputValue < 0)
|
||||
inputValue = 0
|
||||
|
||||
value = inputValue;
|
||||
}
|
||||
Keys.onPressed: (event) =>
|
||||
{
|
||||
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter)
|
||||
{
|
||||
resolutionScaleField.focus = false; // Unfocus the TextField
|
||||
event.accepted = true; // Prevent further propagation of the key event
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RowLayout
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignBottom | Qt.AlignRight
|
||||
Layout.bottomMargin: 5
|
||||
|
||||
|
||||
Button
|
||||
{
|
||||
id: okayButton
|
||||
text: "Okay"
|
||||
onClicked: window.accept()
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
}
|
||||
|
||||
Button
|
||||
{
|
||||
id: cancelButton
|
||||
text: "Cancel"
|
||||
onClicked: window.reject()
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FileDialog is used to select a file for the Image, Shader, and Video channels
|
||||
Dialogs.FileDialog
|
||||
{
|
||||
id: fileDialog
|
||||
currentFolder: "file://" + window.currentFolder
|
||||
nameFilters: window.selectionFilter
|
||||
title: window.selectionTitle
|
||||
|
||||
onAccepted: window.tmp_source = selectedFile
|
||||
}
|
||||
|
||||
// FolderDialog is used to select a folder for the CubeMap channel
|
||||
Dialogs.FolderDialog
|
||||
{
|
||||
id: folderDialog
|
||||
currentFolder: window.cubemapFolder
|
||||
title: window.selectionTitle
|
||||
|
||||
onAccepted: window.tmp_source = selectedFolder
|
||||
}
|
||||
|
||||
function accept()
|
||||
{
|
||||
// copy over temp values
|
||||
source = tmp_source
|
||||
type = tmp_type
|
||||
timeScale = tmp_timeScale
|
||||
resolution_scale = tmp_resolution_scale
|
||||
resolution_x = tmp_resolution_x
|
||||
resolution_y = tmp_resolution_y
|
||||
enabled = tmp_enabled
|
||||
|
||||
// Emit the accepted signal and reset the selection
|
||||
window.accepted()
|
||||
}
|
||||
|
||||
function reject()
|
||||
{
|
||||
// Emit the rejected signal and reset the selection
|
||||
resetSelection()
|
||||
window.rejected()
|
||||
}
|
||||
|
||||
function configureChannel()
|
||||
{
|
||||
resetSelection()
|
||||
}
|
||||
|
||||
// Function to update the current selection based on the channel type
|
||||
function updateCurrentSelection()
|
||||
{
|
||||
// Set the dialog properties based on the channel properties
|
||||
switch(type)
|
||||
{
|
||||
case ShaderChannel.Type.CubeMapChannel:
|
||||
window.currentFolder = window.cubemapFolder
|
||||
break;
|
||||
case ShaderChannel.Type.ImageChannel:
|
||||
window.currentFolder = window.imageFolder
|
||||
break;
|
||||
case ShaderChannel.Type.ShaderChannel:
|
||||
window.currentFolder = window.shaderFolder
|
||||
break;
|
||||
case ShaderChannel.Type.VideoChannel:
|
||||
window.currentFolder = window.videoFolder
|
||||
break;
|
||||
}
|
||||
|
||||
// Set the current selection index
|
||||
list.currentIndex = selectionModel.indexOf(type)
|
||||
}
|
||||
|
||||
// Function to reset the selection to default values
|
||||
function resetSelection()
|
||||
{
|
||||
tmp_source = source
|
||||
tmp_timeScale = timeScale
|
||||
tmp_resolution_scale = resolution_scale
|
||||
tmp_resolution_x = resolution_x
|
||||
tmp_resolution_y = resolution_y
|
||||
|
||||
tmp_type = type
|
||||
updateCurrentSelection()
|
||||
}
|
||||
}
|
||||
226
package/contents/ui/ShaderToyModel.qml
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* Komplex Wallpaper Engine
|
||||
* Copyright (C) 2025 @DigitalArtifex | github.com/DigitalArtifex
|
||||
*
|
||||
* This was originally part of the KDE Shader Wallpaper Project, which was the inspiration for this project.
|
||||
* It has been modified to use the new channel structure and is being used to support
|
||||
* ShaderToy imports.
|
||||
*
|
||||
* --------------------------------------------------------------------------------------------------------
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software uses some of the QML code from JaredTao/jared2020@163.com's ToyShader for Android.
|
||||
* See: https://github.com/jaredtao/TaoShaderToy/
|
||||
*/
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtCore
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Dialogs as Dialogs
|
||||
|
||||
import org.kde.plasma.core as PlasmaCore
|
||||
|
||||
Item
|
||||
{
|
||||
property real pixelRatio: 1 //This will (hopefully) be set to PlasmaCore.Units.devicePixelRatio in onCompleted
|
||||
property vector3d iResolution: Qt.vector3d(wallpaper.configuration.resolution_x,wallpaper.configuration.resolution_y,1)//width, height, pixel aspect ratio
|
||||
property real iTime: 0 //used by most motion shaders
|
||||
property real iTimeDelta: iTime
|
||||
property var iChannelTime: [iTime, iTime, iTime, iTime] //individual channel time values
|
||||
property real iSampleRate: 44100 //used by audio shaders
|
||||
property int iFrame: 0
|
||||
property real iFrameRate: wallpaper.configuration.framerate_limit // Default frame rate for the shader
|
||||
property vector4d iMouse
|
||||
property var iDate
|
||||
property bool running: windowModel.runShader // Controls whether the wallpaper is running or paused
|
||||
|
||||
// Individual channel resolutions to customize performance and quality
|
||||
property var iChannelResolution: [Qt.vector3d(wallpaper.configuration.iChannel0_resolution_x, wallpaper.configuration.iChannel0_resolution_y, pixelRatio),
|
||||
Qt.vector3d(wallpaper.configuration.iChannel1_resolution_x, wallpaper.configuration.iChannel1_resolution_y, pixelRatio),
|
||||
Qt.vector3d(wallpaper.configuration.iChannel2_resolution_x, wallpaper.configuration.iChannel2_resolution_y, pixelRatio),
|
||||
Qt.vector3d(wallpaper.configuration.iChannel3_resolution_x, wallpaper.configuration.iChannel3_resolution_y, pixelRatio)]
|
||||
|
||||
id: mainItem
|
||||
|
||||
// The WindowModel is used to manage the interaction with the desktop environment
|
||||
WindowModel
|
||||
{
|
||||
id: windowModel
|
||||
screenGeometry: mainItem.parent.screenGeometry
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
anchors.fill: parent
|
||||
color: "black"
|
||||
|
||||
Text
|
||||
{
|
||||
color: "white"
|
||||
text: "<h1>" + wallpaper.configuration.selectedShaderPath + "</h1>"
|
||||
}
|
||||
|
||||
// Setup the shader channels (iChannel0, iChannel1, iChannel2, iChannel3)
|
||||
// These channels are used to pass channel data to the shader sources and can
|
||||
// be configured to use different types of media (Image, Video, Shader, Audio, CubeMap)
|
||||
ShaderChannel
|
||||
{
|
||||
//Fallback to a channel if the output channel is not set or there was an error loading the shader
|
||||
visible: wallpaper.configuration.iChannel0_flag && (channelOutput.source === "" || channelOutput.source === undefined)
|
||||
iTime: mainItem.iTime
|
||||
iMouse: mainItem.iMouse
|
||||
iResolution: mainItem.iChannelResolution[0]
|
||||
|
||||
id: channel0
|
||||
anchors.fill: parent
|
||||
type: wallpaper.configuration.iChannel0_flag ? wallpaper.configuration.iChannel0_type : 0
|
||||
|
||||
source: wallpaper.configuration.iChannel0_flag ? Qt.resolvedUrl(wallpaper.configuration.iChannel0) : ""
|
||||
}
|
||||
|
||||
ShaderChannel
|
||||
{
|
||||
//Fallback to a channel if the output channel is not set or there was an error loading the shader
|
||||
visible: wallpaper.configuration.iChannel1_flag && (channelOutput.source === "" || channelOutput.source === undefined)
|
||||
iTime: mainItem.iTime
|
||||
iResolution: mainItem.iChannelResolution[1]
|
||||
|
||||
id: channel1
|
||||
anchors.fill: parent
|
||||
type: wallpaper.configuration.iChannel1_flag ? wallpaper.configuration.iChannel1_type : 0
|
||||
|
||||
source: wallpaper.configuration.iChannel1_flag ? Qt.resolvedUrl(wallpaper.configuration.iChannel1) : ""
|
||||
}
|
||||
|
||||
ShaderChannel
|
||||
{
|
||||
//Fallback to a channel if the output channel is not set or there was an error loading the shader
|
||||
visible: wallpaper.configuration.iChannel2_flag && (channelOutput.source === "" || channelOutput.source === undefined)
|
||||
iTime: mainItem.iTime
|
||||
iResolution: mainItem.iChannelResolution[2]
|
||||
|
||||
id: channel2
|
||||
anchors.fill: parent
|
||||
type: wallpaper.configuration.iChannel2_flag ? wallpaper.configuration.iChannel2_type : 0
|
||||
|
||||
source: wallpaper.configuration.iChannel2_flag ? Qt.resolvedUrl(wallpaper.configuration.iChannel2) : ""
|
||||
}
|
||||
|
||||
ShaderChannel
|
||||
{
|
||||
//Fallback to a channel if the output channel is not set or there was an error loading the shader
|
||||
visible: wallpaper.configuration.iChannel3_flag && (channelOutput.source === "" || channelOutput.source === undefined)
|
||||
iTime: mainItem.iTime
|
||||
iChannelTime: [mainItem.iTime, mainItem.iTime, mainItem.iTime, mainItem.iTime]
|
||||
iResolution: mainItem.iChannelResolution[3]
|
||||
iDate: mainItem.iDate
|
||||
|
||||
id: channel3
|
||||
anchors.fill: parent
|
||||
type: wallpaper.configuration.iChannel3_flag ? wallpaper.configuration.iChannel3_type : 0
|
||||
|
||||
source: wallpaper.configuration.iChannel3_flag ? Qt.resolvedUrl(wallpaper.configuration.iChannel3) : ""
|
||||
}
|
||||
|
||||
// The output channel that combines all the input channels and displays the final shader output
|
||||
// This channel must be set to a shader source file that has been pre-compiled to a QSB Fragment Shader
|
||||
ShaderChannel
|
||||
{
|
||||
iTime: mainItem.iTime
|
||||
iMouse: mainItem.iMouse
|
||||
iResolution: mainItem.iResolution
|
||||
|
||||
id: channelOutput
|
||||
anchors.fill: parent
|
||||
type: ShaderChannel.Type.ShaderChannel
|
||||
source: wallpaper.configuration.selectedShaderPath ? wallpaper.configuration.selectedShaderPath : ""
|
||||
|
||||
iChannel0: channel0
|
||||
iChannel1: channel1
|
||||
iChannel2: channel2
|
||||
iChannel3: channel3
|
||||
|
||||
visible: true // Set to true to display the output
|
||||
}
|
||||
|
||||
// To save on performance, just use one timer for all channels
|
||||
Timer
|
||||
{
|
||||
id: channelTimer
|
||||
|
||||
//Not entirely sure if this will actually limit the frame rate
|
||||
interval: (1 / mainItem.iFrameRate) * 1000 //fps to ms cycles :: fps = 60 = 1 / 60 = 0.01666 * 1000 = 16
|
||||
repeat: true
|
||||
|
||||
running: mainItem.running
|
||||
|
||||
triggeredOnStart: true
|
||||
onTriggered:
|
||||
{
|
||||
var date = new Date();
|
||||
var startOfDay = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
|
||||
var secondsSinceMidnight = (date - startOfDay) / 1000;
|
||||
|
||||
mainItem.iTime += (interval / 1000) * (wallpaper.configuration.shaderSpeed ? wallpaper.configuration.shaderSpeed : 1.0)
|
||||
mainItem.iDate = Qt.vector4d(date.getFullYear(), date.getMonth() + 1, date.getDate(), secondsSinceMidnight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted:
|
||||
{
|
||||
Qt.createQmlObject(`import QtQuick
|
||||
MouseArea
|
||||
{
|
||||
id: mouseTrackingArea
|
||||
propagateComposedEvents: true
|
||||
preventStealing: false
|
||||
enabled: wallpaper.configuration.mouseAllowed
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onPositionChanged: (mouse) => {
|
||||
mouse.accepted = false
|
||||
mainItem.iMouse.x = mouse.x * wallpaper.configuration.mouseSpeedBias
|
||||
mainItem.iMouse.y = -mouse.y * wallpaper.configuration.mouseSpeedBias
|
||||
}
|
||||
onClicked:(mouse) => {
|
||||
mouse.accepted = false
|
||||
mainItem.iMouse.z = mouse.x
|
||||
mainItem.iMouse.w = mouse.y
|
||||
}
|
||||
// this still doesnt work... guess a C++ wrapper is all that can be done?
|
||||
onPressed:(mouse) => {
|
||||
mouse.accepted = false
|
||||
}
|
||||
onPressAndHold:(mouse) => {
|
||||
mouse.accepted = false
|
||||
}
|
||||
onDoubleClicked:(mouse) => {
|
||||
mouse.accepted = false
|
||||
}
|
||||
//cancelled, entered, and exited do not pass mouse events, so we can remove them
|
||||
onReleased:(mouse) => {
|
||||
mouse.accepted = false
|
||||
}
|
||||
onWheel: (mouse) => {
|
||||
mouse.accepted = false
|
||||
}
|
||||
}`, parent.parent, "mouseTrackerArea");
|
||||
|
||||
// Initialize pixelRatio for shader use
|
||||
pixelRatio = 1 //PlasmaCore.Units.devicePixelRatio was removed in Plasma6
|
||||
}
|
||||
}
|
||||
178
package/contents/ui/WindowModel.qml
Normal file
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Komplex Wallpaper Engine
|
||||
* Copyright (C) 2025 @DigitalArtifex | github.com/DigitalArtifex
|
||||
*
|
||||
* WindowModel.qml
|
||||
*
|
||||
* Copyright 2018 Rog131 <samrog131@hotmail.com>
|
||||
* Copyright 2019 adhe <adhemarks2@gmail.com>
|
||||
* Copyright 2024 Luis Bocanegra <luisbocanegra17b@gmail.com>
|
||||
*
|
||||
* This file was part of the KDE Shader Wallpaper project and is used to track the
|
||||
* desktop state.
|
||||
*
|
||||
* --------------------------------------------------------------------------------------------------------
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software uses some of the QML code from JaredTao/jared2020@163.com's ToyShader for Android.
|
||||
* See: https://github.com/jaredtao/TaoShaderToy/
|
||||
*/
|
||||
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import org.kde.taskmanager 0.1 as TaskManager
|
||||
import org.kde.kwindowsystem
|
||||
|
||||
import com.github.digitalartifex.komplex as Komplex
|
||||
|
||||
Item
|
||||
{
|
||||
id: wModel
|
||||
property var screenGeometry
|
||||
property int pauseMode: wallpaper.configuration.pauseMode
|
||||
property bool runShader: false
|
||||
property bool maximizedExists: false
|
||||
property bool visibleExists: false
|
||||
property bool activeExists: false
|
||||
property var abstractTasksModel: TaskManager.AbstractTasksModel
|
||||
property var appId: abstractTasksModel.AppId
|
||||
property var isWindow: abstractTasksModel.IsWindow
|
||||
property var isMinimized: abstractTasksModel.IsMinimized
|
||||
property var isMaximized: abstractTasksModel.IsMaximized
|
||||
property var isFullScreen: abstractTasksModel.IsFullScreen
|
||||
property var isActive: abstractTasksModel.IsActive
|
||||
property var isHidden: abstractTasksModel.IsHidden
|
||||
property bool activeScreenOnly: wallpaper.configuration.checkActiveScreen
|
||||
property var excludeWindows: wallpaper.configuration.excludeWindows
|
||||
|
||||
Komplex.ShaderPackModel
|
||||
{
|
||||
id: shaderPackModel
|
||||
}
|
||||
|
||||
Connections
|
||||
{
|
||||
target: wallpaper.configuration
|
||||
function onValueChanged()
|
||||
{
|
||||
wModel.updateWindowsInfo();
|
||||
}
|
||||
}
|
||||
|
||||
Connections
|
||||
{
|
||||
target: KWindowSystem
|
||||
function onShowingDesktopChanged()
|
||||
{
|
||||
wModel.updateWindowsInfo();
|
||||
}
|
||||
}
|
||||
|
||||
onPauseModeChanged:
|
||||
{
|
||||
updateWindowsInfo();
|
||||
}
|
||||
|
||||
function updateRun()
|
||||
{
|
||||
let shouldRun = true;
|
||||
switch (pauseMode)
|
||||
{
|
||||
case 0:
|
||||
shouldRun = !maximizedExists;
|
||||
break;
|
||||
case 1:
|
||||
shouldRun = !activeExists;
|
||||
break;
|
||||
case 2:
|
||||
shouldRun = !visibleExists;
|
||||
break;
|
||||
case 3:
|
||||
shouldRun = true;
|
||||
}
|
||||
runShader = shouldRun;
|
||||
}
|
||||
|
||||
TaskManager.VirtualDesktopInfo
|
||||
{
|
||||
id: virtualDesktopInfo
|
||||
}
|
||||
|
||||
TaskManager.ActivityInfo
|
||||
{
|
||||
id: activityInfo
|
||||
readonly property string nullUuid: "00000000-0000-0000-0000-000000000000"
|
||||
}
|
||||
|
||||
TaskManager.TasksModel
|
||||
{
|
||||
id: tasksModel
|
||||
sortMode: TaskManager.TasksModel.SortVirtualDesktop
|
||||
groupMode: TaskManager.TasksModel.GroupDisabled
|
||||
virtualDesktop: virtualDesktopInfo.currentDesktop
|
||||
activity: activityInfo.currentActivity
|
||||
screenGeometry: wModel.screenGeometry
|
||||
filterByVirtualDesktop: true
|
||||
filterByScreen: parent.activeScreenOnly
|
||||
filterByActivity: true
|
||||
filterMinimized: true
|
||||
|
||||
onActiveTaskChanged:
|
||||
{
|
||||
updateWindowsInfo();
|
||||
}
|
||||
|
||||
onDataChanged:
|
||||
{
|
||||
updateWindowsInfo();
|
||||
}
|
||||
|
||||
onCountChanged:
|
||||
{
|
||||
updateWindowsInfo();
|
||||
}
|
||||
}
|
||||
|
||||
function updateWindowsInfo()
|
||||
{
|
||||
let activeCount = 0;
|
||||
let visibleCount = 0;
|
||||
let maximizedCount = 0;
|
||||
|
||||
if (!KWindowSystem.showingDesktop)
|
||||
{
|
||||
for (var i = 0; i < tasksModel.count; i++)
|
||||
{
|
||||
const currentTask = tasksModel.index(i, 0);
|
||||
|
||||
// Long line
|
||||
if (currentTask === undefined || excludeWindows.includes(tasksModel.data(currentTask, appId).replace(/\.desktop$/, "")) || tasksModel.data(currentTask, isHidden) || !tasksModel.data(currentTask, isWindow) || tasksModel.data(currentTask, isMinimized))
|
||||
continue;
|
||||
|
||||
visibleCount += 1;
|
||||
if (tasksModel.data(currentTask, isMaximized) || tasksModel.data(currentTask, isFullScreen))
|
||||
maximizedCount += 1;
|
||||
if (tasksModel.data(currentTask, isActive))
|
||||
activeCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
visibleExists = visibleCount > 0;
|
||||
maximizedExists = maximizedCount > 0;
|
||||
activeExists = activeCount > 0;
|
||||
updateRun();
|
||||
}
|
||||
}
|
||||
740
package/contents/ui/config.qml
Normal file
@@ -0,0 +1,740 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
// 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 com.github.digitalartifex.komplex 1.0 as Komplex
|
||||
|
||||
Kirigami.FormLayout
|
||||
{
|
||||
id: root
|
||||
twinFormLayouts: parentLayout // required by parent
|
||||
property alias formLayout: root // required by parent
|
||||
property alias cfg_pauseMode: pauseModeCombo.currentIndex
|
||||
property alias cfg_isPaused: runningCombo.checked
|
||||
property alias cfg_selectedShaderIndex: selectedShader.currentIndex
|
||||
property alias cfg_selectedShaderPath: selectedShader.shader
|
||||
property alias cfg_iChannel0: shaderChannelConfig0.source
|
||||
property alias cfg_iChannel1: shaderChannelConfig1.source
|
||||
property alias cfg_iChannel2: shaderChannelConfig2.source
|
||||
property alias cfg_iChannel3: shaderChannelConfig3.source
|
||||
property alias cfg_iChannel0_flag: shaderChannelConfig0.enabled
|
||||
property alias cfg_iChannel1_flag: shaderChannelConfig1.enabled
|
||||
property alias cfg_iChannel2_flag: shaderChannelConfig2.enabled
|
||||
property alias cfg_iChannel3_flag: shaderChannelConfig3.enabled
|
||||
property alias cfg_iChannel0_type: shaderChannelConfig0.type
|
||||
property alias cfg_iChannel1_type: shaderChannelConfig1.type
|
||||
property alias cfg_iChannel2_type: shaderChannelConfig2.type
|
||||
property alias cfg_iChannel3_type: shaderChannelConfig3.type
|
||||
property alias cfg_iChannel0_timeScale: shaderChannelConfig0.timeScale
|
||||
property alias cfg_iChannel1_timeScale: shaderChannelConfig1.timeScale
|
||||
property alias cfg_iChannel2_timeScale: shaderChannelConfig2.timeScale
|
||||
property alias cfg_iChannel3_timeScale: shaderChannelConfig3.timeScale
|
||||
property alias cfg_iChannel0_resolution_scale: shaderChannelConfig0.resolution_scale
|
||||
property alias cfg_iChannel1_resolution_scale: shaderChannelConfig1.resolution_scale
|
||||
property alias cfg_iChannel2_resolution_scale: shaderChannelConfig2.resolution_scale
|
||||
property alias cfg_iChannel3_resolution_scale: shaderChannelConfig3.resolution_scale
|
||||
property alias cfg_iChannel0_resolution_x: shaderChannelConfig0.resolution_x
|
||||
property alias cfg_iChannel0_resolution_y: shaderChannelConfig0.resolution_y
|
||||
property alias cfg_iChannel1_resolution_x: shaderChannelConfig1.resolution_x
|
||||
property alias cfg_iChannel1_resolution_y: shaderChannelConfig1.resolution_y
|
||||
property alias cfg_iChannel2_resolution_x: shaderChannelConfig2.resolution_x
|
||||
property alias cfg_iChannel2_resolution_y: shaderChannelConfig2.resolution_y
|
||||
property alias cfg_iChannel3_resolution_x: shaderChannelConfig3.resolution_x
|
||||
property alias cfg_iChannel3_resolution_y: shaderChannelConfig3.resolution_y
|
||||
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_shader_package: selectedShaderPack.shader
|
||||
property alias cfg_shader_package_index: selectedShaderPack.currentIndex
|
||||
property alias cfg_komplex_mode: engineModeSelect.currentIndex
|
||||
|
||||
Palette
|
||||
{
|
||||
id: palette
|
||||
}
|
||||
|
||||
Komplex.ShaderPackModel
|
||||
{
|
||||
id: shaderPackModel
|
||||
onJsonChanged:
|
||||
{
|
||||
// Handle the JSON change if needed
|
||||
console.log("Shader pack JSON changed:", shaderPackModel.json);
|
||||
root.cfg_shader_package = shaderPackModel.shaderPackPath + "/" + shaderPackModel.shaderPackName + "/pack.json"
|
||||
}
|
||||
}
|
||||
|
||||
// Engine Mode Selection
|
||||
|
||||
RowLayout
|
||||
{
|
||||
Kirigami.FormData.label: i18nd("com.github.digitalartifex.komplex", "Engine Mode:")
|
||||
ComboBox
|
||||
{
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 11
|
||||
id: engineModeSelect
|
||||
currentIndex: root.cfg_komplex_mode
|
||||
onCurrentIndexChanged: root.cfg_komplex_mode = currentIndex
|
||||
textRole: "label"
|
||||
model: [
|
||||
{ "label": i18nd("@option:komplex_mode", "ShaderToys") },
|
||||
{ "label": i18nd("@option:komplex_mode", "Komplex") }
|
||||
]
|
||||
}
|
||||
}
|
||||
TabBar
|
||||
{
|
||||
Layout.fillWidth: true
|
||||
id: navBar
|
||||
width: parent.width
|
||||
TabButton {
|
||||
text: qsTr("Shader")
|
||||
}
|
||||
TabButton {
|
||||
text: qsTr("Shader Settings")
|
||||
}
|
||||
TabButton {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
RowLayout
|
||||
{
|
||||
visible: root.cfg_komplex_mode === 0 && navBar.currentIndex === 0
|
||||
|
||||
id: shaderOutputLayout
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
Kirigami.FormData.label: i18nd("com.github.digitalartifex.komplex", "Output Shader:")
|
||||
|
||||
ComboBox
|
||||
{
|
||||
property string shader
|
||||
|
||||
id: selectedShader
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 11
|
||||
model: FolderListModel
|
||||
{
|
||||
id: folderListModel
|
||||
showDirs: false
|
||||
showFiles: true
|
||||
showHidden: true
|
||||
nameFilters: ["*.frag.qsb"]
|
||||
folder: "file://" + shaderPackModel.shadersPath + "/generative"
|
||||
}
|
||||
delegate: Component
|
||||
{
|
||||
id: folderListDelegate
|
||||
ItemDelegate
|
||||
{
|
||||
width: parent ? parent.width : 0
|
||||
text: fileBaseName.replace("_", " ").charAt(0).toUpperCase() + fileBaseName.replace("_", " ").slice(1)
|
||||
}
|
||||
}
|
||||
|
||||
textRole: "fileBaseName"
|
||||
currentIndex: root.cfg_selectedShaderIndex
|
||||
displayText: currentIndex === -1 ? "Custom File" : currentText.replace("_", " ").charAt(0).toUpperCase() + currentText.replace("_", " ").slice(1)
|
||||
|
||||
onCurrentTextChanged:
|
||||
{
|
||||
root.cfg_selectedShaderIndex = currentIndex;
|
||||
|
||||
if (root.cfg_selectedShaderIndex === -1)
|
||||
return;
|
||||
|
||||
var source = model.get(currentIndex, "fileUrl");
|
||||
shader = source;
|
||||
}
|
||||
}
|
||||
|
||||
Button
|
||||
{
|
||||
id: shaderFileButton
|
||||
icon.name: "folder-symbolic"
|
||||
text: i18nd("@button:toggle_select_shader", "Select File")
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 9
|
||||
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
|
||||
onClicked:
|
||||
{
|
||||
fileDialog.currentFolder = "file://" + shaderPackModel.shadersPath + "/generative";
|
||||
fileDialog.open();
|
||||
}
|
||||
}
|
||||
|
||||
FileDialog
|
||||
{
|
||||
id: fileDialog
|
||||
fileMode: FileDialog.OpenFile
|
||||
title: i18nd("@dialog_title:choose_shader", "Choose a shader")
|
||||
// will accept and auto convert .frag in the near future
|
||||
nameFilters: ["Shader files (*.frag.qsb)", "All files (*)"]
|
||||
visible: false
|
||||
currentFolder: `${StandardPaths.writableLocation(StandardPaths.HomeLocation)}/.local/share/komplex/shaders/`
|
||||
onAccepted:
|
||||
{
|
||||
root.cfg_selectedShaderIndex = -1;
|
||||
root.cfg_selectedShaderPath = selectedFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// iChannel0 Configuration
|
||||
RowLayout
|
||||
{
|
||||
visible: root.cfg_komplex_mode === 0 && navBar.currentIndex === 0
|
||||
id: channel0ConfigLayout
|
||||
Kirigami.FormData.label: i18nd("com.github.digitalartifex.komplex", "Channel 1:")
|
||||
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
Text
|
||||
{
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 11
|
||||
|
||||
color: palette.text
|
||||
text: i18nd("@iChannel0_source", root.cfg_iChannel0 ? root.cfg_iChannel0 : "No source selected")
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideLeft
|
||||
}
|
||||
|
||||
Button
|
||||
{
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 2
|
||||
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
|
||||
icon.name: "configure-symbolic"
|
||||
onClicked: () =>
|
||||
{
|
||||
shaderChannelConfig0.configureChannel();
|
||||
shaderChannelOverlay0.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.OverlaySheet
|
||||
{
|
||||
id: shaderChannelOverlay0
|
||||
parent: applicationWindow().overlay
|
||||
implicitHeight: 400
|
||||
|
||||
ShaderChannelConfiguration
|
||||
{
|
||||
id: shaderChannelConfig0
|
||||
palette: palette
|
||||
height: 350
|
||||
onAccepted: shaderChannelOverlay0.close(); // Close the overlay after configuration
|
||||
onRejected: shaderChannelOverlay0.close();
|
||||
}
|
||||
}
|
||||
|
||||
// iChannel1 Configuration
|
||||
RowLayout
|
||||
{
|
||||
visible: root.cfg_komplex_mode === 0 && navBar.currentIndex === 0
|
||||
id: channel1ConfigLayout
|
||||
|
||||
Kirigami.FormData.label: i18nd("com.github.digitalartifex.komplex", "Channel 2:")
|
||||
|
||||
Text
|
||||
{
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 11
|
||||
color: palette.text
|
||||
text: i18nd("@iChannel1_source", root.cfg_iChannel1 ? root.cfg_iChannel1 : "No source selected")
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Button
|
||||
{
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 2
|
||||
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
|
||||
icon.name: "configure-symbolic"
|
||||
onClicked: () =>
|
||||
{
|
||||
shaderChannelConfig1.configureChannel();
|
||||
shaderChannelOverlay1.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.OverlaySheet
|
||||
{
|
||||
id: shaderChannelOverlay1
|
||||
parent: applicationWindow().overlay
|
||||
implicitHeight: 400
|
||||
|
||||
ShaderChannelConfiguration
|
||||
{
|
||||
id: shaderChannelConfig1
|
||||
height: 350
|
||||
onAccepted: shaderChannelOverlay1.close(); // Close the overlay after configuration
|
||||
onRejected: shaderChannelOverlay1.close();
|
||||
}
|
||||
}
|
||||
|
||||
// iChannel2 Configuration
|
||||
RowLayout
|
||||
{
|
||||
visible: root.cfg_komplex_mode === 0 && navBar.currentIndex === 0
|
||||
id: channel2ConfigLayout
|
||||
|
||||
Kirigami.FormData.label: i18nd("com.github.digitalartifex.komplex", "Channel 3:")
|
||||
|
||||
Text
|
||||
{
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 11
|
||||
color: palette.text
|
||||
text: i18nd("@iChannel2_source", root.cfg_iChannel2 ? root.cfg_iChannel2 : "No source selected")
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Button
|
||||
{
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 2
|
||||
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
|
||||
icon.name: "configure-symbolic"
|
||||
onClicked: () =>
|
||||
{
|
||||
shaderChannelConfig2.configureChannel();
|
||||
shaderChannelOverlay2.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.OverlaySheet
|
||||
{
|
||||
id: shaderChannelOverlay2
|
||||
parent: applicationWindow().overlay
|
||||
implicitHeight: 400
|
||||
|
||||
ShaderChannelConfiguration
|
||||
{
|
||||
id: shaderChannelConfig2
|
||||
height: 350
|
||||
onAccepted: shaderChannelOverlay2.close(); // Close the overlay after configuration
|
||||
onRejected: shaderChannelOverlay2.close();
|
||||
}
|
||||
}
|
||||
|
||||
// iChannel3 Configuration
|
||||
RowLayout
|
||||
{
|
||||
visible: root.cfg_komplex_mode === 0 && navBar.currentIndex === 0
|
||||
Layout.fillWidth: true
|
||||
Kirigami.FormData.label: i18nd("com.github.digitalartifex.komplex", "Channel 4:")
|
||||
|
||||
Text
|
||||
{
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 11
|
||||
color: palette.text
|
||||
text: i18nd("@iChannel0_source", root.cfg_iChannel3 ? root.cfg_iChannel3 : "No source selected")
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideLeft
|
||||
}
|
||||
|
||||
Button
|
||||
{
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 2
|
||||
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
|
||||
icon.name: "configure-symbolic"
|
||||
onClicked: () =>
|
||||
{
|
||||
shaderChannelConfig3.configureChannel();
|
||||
shaderChannelOverlay3.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.OverlaySheet
|
||||
{
|
||||
id: shaderChannelOverlay3
|
||||
parent: applicationWindow().overlay
|
||||
implicitHeight: 400
|
||||
|
||||
ShaderChannelConfiguration
|
||||
{
|
||||
id: shaderChannelConfig3
|
||||
height: 350
|
||||
onAccepted: shaderChannelOverlay3.close(); // Close the overlay after configuration
|
||||
onRejected: shaderChannelOverlay3.close();
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout
|
||||
{
|
||||
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: -10.0
|
||||
to: 10.0
|
||||
stepSize: 0.01
|
||||
onValueChanged: shaderSpeedField.text = String(value.toFixed(2));
|
||||
}
|
||||
|
||||
TextField
|
||||
{
|
||||
id: shaderSpeedField
|
||||
inputMethodHints: Qt.ImhFormattedNumbersOnly
|
||||
horizontalAlignment: Text.AlignRight
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 4
|
||||
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 === 1
|
||||
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 === 1
|
||||
|
||||
Kirigami.FormData.label: i18nd("com.github.digitalartifex.komplex", "Mouse bias:")
|
||||
ColumnLayout
|
||||
{
|
||||
Slider
|
||||
{
|
||||
id: mouseBiasSlider
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 16
|
||||
from: -10.0
|
||||
to: 10.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
|
||||
{
|
||||
visible: navBar.currentIndex === 1
|
||||
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 === 1
|
||||
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
|
||||
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
Kirigami.FormData.label: i18nd("com.github.digitalartifex.komplex", "Shader Pack:")
|
||||
|
||||
ComboBox
|
||||
{
|
||||
property string shader
|
||||
|
||||
id: selectedShaderPack
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 11
|
||||
model: FolderListModel
|
||||
{
|
||||
id: packListModel
|
||||
showDirs: true
|
||||
showFiles: false
|
||||
showHidden: false
|
||||
nameFilters: ["*.frag.qsb"]
|
||||
folder: "file://" + shaderPackModel.shadersPath + "/generative"
|
||||
}
|
||||
delegate: Component
|
||||
{
|
||||
id: packListDelegate
|
||||
ItemDelegate
|
||||
{
|
||||
width: parent ? parent.width : 0
|
||||
text: fileBaseName.replace("_", " ").charAt(0).toUpperCase() + fileBaseName.replace("_", " ").slice(1)
|
||||
}
|
||||
}
|
||||
|
||||
textRole: "fileBaseName"
|
||||
currentIndex: root.cfg_shader_package
|
||||
displayText: currentIndex === -1 ? "Custom File" : currentText.replace("_", " ").charAt(0).toUpperCase() + currentText.replace("_", " ").slice(1)
|
||||
|
||||
onCurrentTextChanged:
|
||||
{
|
||||
root.cfg_selectedShaderIndex = currentIndex;
|
||||
|
||||
if (root.cfg_selectedShaderIndex === -1)
|
||||
return;
|
||||
|
||||
var source = model.get(currentIndex, "fileUrl");
|
||||
shader = source;
|
||||
}
|
||||
}
|
||||
|
||||
Button
|
||||
{
|
||||
id: packFileButton
|
||||
icon.name: "folder-symbolic"
|
||||
text: i18nd("@button:toggle_select_shader", "Select File")
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 9
|
||||
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
|
||||
onClicked:
|
||||
{
|
||||
packDialog.currentFolder = "file://" + shaderPackModel.shaderPackInstallPath;
|
||||
packDialog.open();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
package/contents/ui/icons/3d-glasses.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50px" height="50px"><path d="M 14 10 C 12.128906 10 8.585938 12.070313 2.3125 21.925781 C 1.996094 22.421875 1.929688 23.023438 2.097656 23.574219 C 2.035156 23.707031 2 23.851563 2 24 L 2 39 C 2 40.070313 2.863281 41 3.957031 41 L 19.992188 41 C 21.109375 41 22.125 40.332031 22.605469 39.324219 C 22.617188 39.300781 22.628906 39.277344 22.636719 39.25 L 24.523438 34.324219 C 24.648438 34.066406 24.8125 34 25 34 C 25.1875 34 25.351563 34.066406 25.476563 34.324219 L 27.363281 39.25 C 27.371094 39.277344 27.382813 39.300781 27.394531 39.324219 C 27.875 40.332031 28.890625 41 30.007813 41 L 46.042969 41 C 47.136719 41 48 40.070313 48 39 L 48 24 C 48 23.855469 47.96875 23.714844 47.90625 23.582031 C 48.0625 23.074219 48.015625 22.519531 47.753906 22.042969 C 44.757813 16.550781 40.460938 10 37 10 C 32 10 32 15.269531 32 17 C 32 17.027344 32 17.054688 32 17.082031 C 32.066406 18.65625 33.1875 21 36 21 C 36.496094 21 36.972656 20.851563 37.386719 20.578125 C 37.585938 21.382813 37.773438 22.203125 37.949219 23 L 28.828125 23 C 28.007813 23 27.441406 23.355469 26.941406 23.605469 C 26.445313 23.855469 26.007813 24 25.953125 24 L 24.042969 24 C 23.992188 24 23.554688 23.855469 23.058594 23.605469 C 22.558594 23.355469 21.992188 23 21.171875 23 L 12.121094 23 C 12.441406 22.0625 12.777344 21.097656 13.125 20.171875 C 13.5625 20.6875 14.164063 21 15 21 C 17.8125 21 18.933594 18.65625 19 17.082031 C 19 17.054688 19 17.027344 19 17 C 19 15.269531 19 10 14 10 Z M 14 12 C 17 12 17 15.082031 17 17 C 17 17 16.917969 19 15 19 C 14 19 14 15.582031 14 14 C 13.003906 14 10 23 10 23 L 4 23 C 4 23 11 12 14 12 Z M 37 12 C 40 12 46 23 46 23 L 40 23 C 40 23 38.082031 14 37 14 C 37 15.582031 37.082031 19 36 19 C 34.082031 19 34 17 34 17 C 34 15.082031 34 12 37 12 Z M 4 25 L 21.171875 25 C 21.226563 25 21.660156 25.144531 22.160156 25.394531 C 22.660156 25.644531 23.226563 26 24.042969 26 L 25.953125 26 C 26.773438 26 27.339844 25.644531 27.839844 25.394531 C 28.335938 25.144531 28.773438 25 28.828125 25 L 46 25 L 46 39 L 30.007813 39 C 29.679688 39 29.371094 38.808594 29.203125 38.464844 L 27.316406 33.535156 C 27.308594 33.511719 27.296875 33.484375 27.285156 33.460938 C 26.839844 32.53125 25.910156 32 25 32 C 24.089844 32 23.160156 32.53125 22.714844 33.460938 C 22.703125 33.484375 22.691406 33.511719 22.683594 33.535156 L 20.796875 38.464844 C 20.628906 38.808594 20.320313 39 19.992188 39 L 4 39 Z M 6.976563 27.03125 C 6.421875 27.03125 5.96875 27.464844 5.96875 28 L 5.96875 36.011719 C 5.96875 36.515625 6.480469 37.011719 7 37.011719 L 18 37.011719 C 18.507813 37.011719 18.839844 36.386719 19.035156 36.011719 C 19.964844 33.976563 20.230469 31.71875 21.21875 28.296875 C 21.410156 27.707031 21.261719 27.03125 19.988281 27.03125 Z M 29.972656 27.03125 C 28.699219 27.03125 28.550781 27.707031 28.746094 28.296875 C 29.734375 31.71875 29.996094 33.976563 30.925781 36.011719 C 31.121094 36.386719 31.457031 37.011719 31.964844 37.011719 L 42.964844 37.011719 C 43.484375 37.011719 43.996094 36.515625 43.996094 36.011719 L 43.996094 28 C 43.996094 27.464844 43.539063 27.03125 42.984375 27.03125 Z"/></svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
1
package/contents/ui/icons/3d-model.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0"?><svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50px" height="50px"> <path d="M15,29h13V16H15V29z M23.167,9c0.238,0,0.47,0.085,0.65,0.241L29.37,14H46L34.418,3.256C34.234,3.091,33.997,3,33.75,3 H4.357l5.5,6H23.167z M8,24.167V10c0-0.016,0.006-0.03,0.007-0.046c0-0.004-0.001-0.009-0.001-0.013L3,4.48v30.27 c0,0.23,0.08,0.454,0.226,0.632L13,47V30.369l-4.759-5.552C8.085,24.636,8,24.405,8,24.167z M30,16v5h9.586l-1.293-1.293 c-0.391-0.391-0.391-1.023,0-1.414s1.023-0.391,1.414,0l3,3c0.391,0.391,0.391,1.023,0,1.414l-3,3C39.512,25.902,39.256,26,39,26 s-0.512-0.098-0.707-0.293c-0.391-0.391-0.391-1.023,0-1.414L39.586,23H30v7c0,0.552-0.447,1-1,1h-7v9.586l1.293-1.293 c0.391-0.391,1.023-0.391,1.414,0s0.391,1.023,0,1.414l-3,3C21.512,43.902,21.256,44,21,44s-0.512-0.098-0.707-0.293l-3-3 c-0.391-0.391-0.391-1.023,0-1.414s1.023-0.391,1.414,0L20,40.586V31h-5v17h31c0.553,0,1-0.448,1-1V16H30z M10,23.796l3,3.5V15.414 l-3-3V23.796z M22.797,11H11.69l2.75,3h11.857L22.797,11z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
1
package/contents/ui/icons/audio.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50px" height="50px"><path d="M 12.78125 5.96875 C 11.75 6.082031 10.976563 6.964844 11 8 L 11 42 C 10.988281 42.722656 11.367188 43.390625 11.992188 43.753906 C 12.613281 44.121094 13.386719 44.121094 14.007813 43.753906 C 14.632813 43.390625 15.011719 42.722656 15 42 L 15 8 C 15.007813 7.457031 14.796875 6.9375 14.414063 6.554688 C 14.03125 6.171875 13.511719 5.960938 12.96875 5.96875 C 12.90625 5.964844 12.84375 5.964844 12.78125 5.96875 Z M 36.78125 5.96875 C 35.75 6.082031 34.976563 6.964844 35 8 L 35 42 C 34.988281 42.722656 35.367188 43.390625 35.992188 43.753906 C 36.613281 44.121094 37.386719 44.121094 38.007813 43.753906 C 38.632813 43.390625 39.011719 42.722656 39 42 L 39 8 C 39.007813 7.457031 38.796875 6.9375 38.414063 6.554688 C 38.03125 6.171875 37.511719 5.960938 36.96875 5.96875 C 36.90625 5.964844 36.84375 5.964844 36.78125 5.96875 Z M 6.78125 14.96875 C 5.75 15.082031 4.976563 15.964844 5 17 L 5 33 C 4.988281 33.722656 5.367188 34.390625 5.992188 34.753906 C 6.613281 35.121094 7.386719 35.121094 8.007813 34.753906 C 8.632813 34.390625 9.011719 33.722656 9 33 L 9 17 C 9.007813 16.457031 8.796875 15.9375 8.414063 15.554688 C 8.03125 15.171875 7.511719 14.960938 6.96875 14.96875 C 6.90625 14.964844 6.84375 14.964844 6.78125 14.96875 Z M 42.78125 14.96875 C 41.75 15.082031 40.976563 15.964844 41 17 L 41 33 C 40.988281 33.722656 41.367188 34.390625 41.992188 34.753906 C 42.613281 35.121094 43.386719 35.121094 44.007813 34.753906 C 44.632813 34.390625 45.011719 33.722656 45 33 L 45 17 C 45.007813 16.457031 44.796875 15.9375 44.414063 15.554688 C 44.03125 15.171875 43.511719 14.960938 42.96875 14.96875 C 42.90625 14.964844 42.84375 14.964844 42.78125 14.96875 Z M 18.78125 15.96875 C 17.75 16.082031 16.976563 16.964844 17 18 L 17 32 C 16.988281 32.722656 17.367188 33.390625 17.992188 33.753906 C 18.613281 34.121094 19.386719 34.121094 20.007813 33.753906 C 20.632813 33.390625 21.011719 32.722656 21 32 L 21 18 C 21.007813 17.457031 20.796875 16.9375 20.414063 16.554688 C 20.03125 16.171875 19.511719 15.960938 18.96875 15.96875 C 18.90625 15.964844 18.84375 15.964844 18.78125 15.96875 Z M 30.78125 15.96875 C 29.75 16.082031 28.976563 16.964844 29 18 L 29 32 C 28.988281 32.722656 29.367188 33.390625 29.992188 33.753906 C 30.613281 34.121094 31.386719 34.121094 32.007813 33.753906 C 32.632813 33.390625 33.011719 32.722656 33 32 L 33 18 C 33.007813 17.457031 32.796875 16.9375 32.414063 16.554688 C 32.03125 16.171875 31.511719 15.960938 30.96875 15.96875 C 30.90625 15.964844 30.84375 15.964844 30.78125 15.96875 Z M 0.90625 20.96875 C 0.863281 20.976563 0.820313 20.988281 0.78125 21 C 0.316406 21.105469 -0.0117188 21.523438 0 22 L 0 28 C -0.00390625 28.359375 0.183594 28.695313 0.496094 28.878906 C 0.808594 29.058594 1.191406 29.058594 1.503906 28.878906 C 1.816406 28.695313 2.003906 28.359375 2 28 L 2 22 C 2.011719 21.710938 1.894531 21.433594 1.6875 21.238281 C 1.476563 21.039063 1.191406 20.941406 0.90625 20.96875 Z M 24.78125 20.96875 C 23.75 21.082031 22.976563 21.964844 23 23 L 23 27 C 22.988281 27.722656 23.367188 28.390625 23.992188 28.753906 C 24.613281 29.121094 25.386719 29.121094 26.007813 28.753906 C 26.632813 28.390625 27.011719 27.722656 27 27 L 27 23 C 27.007813 22.457031 26.796875 21.9375 26.414063 21.554688 C 26.03125 21.171875 25.511719 20.960938 24.96875 20.96875 C 24.90625 20.964844 24.84375 20.964844 24.78125 20.96875 Z M 48.90625 20.96875 C 48.863281 20.976563 48.820313 20.988281 48.78125 21 C 48.316406 21.105469 47.988281 21.523438 48 22 L 48 28 C 47.996094 28.359375 48.183594 28.695313 48.496094 28.878906 C 48.808594 29.058594 49.191406 29.058594 49.503906 28.878906 C 49.816406 28.695313 50.003906 28.359375 50 28 L 50 22 C 50.011719 21.710938 49.894531 21.433594 49.6875 21.238281 C 49.476563 21.039063 49.191406 20.941406 48.90625 20.96875 Z"/></svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
1
package/contents/ui/icons/codepen.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50px" height="50px"><path d="M 25 4 L 4 17.34375 L 4 32.652344 L 25 46 L 46 32.65625 L 46 17.34375 Z M 25 29.183594 L 19.066406 25.070313 L 25 21.023438 L 30.933594 25.070313 Z M 27 17.605469 L 27 9.949219 L 40.429688 18.484375 L 34.410156 22.65625 Z M 23 17.605469 L 15.589844 22.65625 L 9.570313 18.484375 L 23 9.949219 Z M 12.09375 25.042969 L 8 27.832031 L 8 22.203125 Z M 15.570313 27.453125 L 23 32.605469 L 23 40.050781 L 9.589844 31.527344 Z M 27 32.605469 L 34.429688 27.453125 L 40.410156 31.527344 L 27 40.050781 Z M 37.90625 25.042969 L 42 22.203125 L 42 27.832031 Z"/></svg>
|
||||
|
After Width: | Height: | Size: 670 B |
1
package/contents/ui/icons/cube.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50px" height="50px"><path d="M25 23.844l18.096-10.447L25.504 3.136c-.311-.182-.697-.182-1.008 0L6.905 13.397 25 23.844zM24 25.577L6 15.185V35.5c0 .355.188.685.496.864L24 46.575V25.577zM26 25.577v20.998l17.504-10.211C43.812 36.185 44 35.855 44 35.5V15.186L26 25.577z"/></svg>
|
||||
|
After Width: | Height: | Size: 357 B |
1
package/contents/ui/icons/image.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50px" height="50px"><path d="M 0 5 L 0 29.585938 L 11.15625 18.429688 L 15.460938 24.890625 L 20.328125 23.914063 L 24.414063 28 L 30.320313 28 L 37.320313 33 L 50 33 L 50 5 Z M 37.5 14 C 39.433594 14 41 15.566406 41 17.5 C 41 19.433594 39.433594 21 37.5 21 C 35.566406 21 34 19.433594 34 17.5 C 34 15.566406 35.566406 14 37.5 14 Z M 10.84375 21.570313 L 0 32.414063 L 0 45 L 50 45 L 50 35 L 36.679688 35 L 29.679688 30 L 23.585938 30 L 19.671875 26.085938 L 14.539063 27.109375 Z"/></svg>
|
||||
|
After Width: | Height: | Size: 572 B |
BIN
package/contents/ui/icons/kofi.gif
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
1
package/contents/ui/icons/video.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50px" height="50px"><path d="M 2 4 C 1.448 4 1 4.448 1 5 L 1 45 C 1 45.552 1.448 46 2 46 L 48 46 C 48.552 46 49 45.552 49 45 L 49 5 C 49 4.448 48.552 4 48 4 L 2 4 z M 3 6 L 6 6 L 6 9 L 3 9 L 3 6 z M 8 6 L 12 6 L 12 9 L 8 9 L 8 6 z M 14 6 L 18 6 L 18 9 L 14 9 L 14 6 z M 20 6 L 24 6 L 24 9 L 20 9 L 20 6 z M 26 6 L 30 6 L 30 9 L 26 9 L 26 6 z M 32 6 L 36 6 L 36 9 L 32 9 L 32 6 z M 38 6 L 42 6 L 42 9 L 38 9 L 38 6 z M 44 6 L 47 6 L 47 9 L 44 9 L 44 6 z M 21 19 L 31 25 L 21 31 L 21 19 z M 3 41 L 6 41 L 6 44 L 3 44 L 3 41 z M 8 41 L 12 41 L 12 44 L 8 44 L 8 41 z M 14 41 L 18 41 L 18 44 L 14 44 L 14 41 z M 20 41 L 24 41 L 24 44 L 20 44 L 20 41 z M 26 41 L 30 41 L 30 44 L 26 44 L 26 41 z M 32 41 L 36 41 L 36 44 L 32 44 L 32 41 z M 38 41 L 42 41 L 42 44 L 38 44 L 38 41 z M 44 41 L 47 41 L 47 44 L 44 44 L 44 41 z"/></svg>
|
||||
|
After Width: | Height: | Size: 906 B |
63
package/contents/ui/main.qml
Normal file
@@ -0,0 +1,63 @@
|
||||
import QtCore
|
||||
import QtQuick
|
||||
import QtQml
|
||||
|
||||
import org.kde.plasma.core
|
||||
import org.kde.plasma.components as PlasmaComponents
|
||||
import org.kde.plasma.extras as PlasmaExtras
|
||||
import org.kde.plasma.plasmoid
|
||||
|
||||
WallpaperItem
|
||||
{
|
||||
Item
|
||||
{
|
||||
anchors.fill: parent
|
||||
|
||||
Loader
|
||||
{
|
||||
id: pageLoader
|
||||
anchors.fill: parent
|
||||
active: true
|
||||
sourceComponent: shaderToysContent
|
||||
|
||||
states: [
|
||||
State
|
||||
{
|
||||
when: wallpaper.komplex_mode === 0
|
||||
PropertyChanges
|
||||
{
|
||||
pageLoader.sourceComponent: shaderToysContent
|
||||
}
|
||||
},
|
||||
State
|
||||
{
|
||||
when: wallpaper.komplex_mode === 1
|
||||
PropertyChanges
|
||||
{
|
||||
pageLoader.sourceComponent: packContent
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Component
|
||||
{
|
||||
id: shaderToysContent
|
||||
|
||||
ShaderToyModel
|
||||
{
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
|
||||
Component
|
||||
{
|
||||
id: packContent
|
||||
|
||||
KomplexModel
|
||||
{
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
22
package/metadata.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"KPackageStructure": "Plasma/Wallpaper",
|
||||
"KPlugin": {
|
||||
"Authors": [
|
||||
{
|
||||
"Name": "DigitalArtifex"
|
||||
}
|
||||
],
|
||||
"Category": "",
|
||||
"Description": "Allows you to use Shaders as your wallpaper",
|
||||
"Icon": "preferences-desktop-wallpaper",
|
||||
"Id": "com.github.digitalartifex.komplex",
|
||||
"License": "GPLv3+",
|
||||
"Version": "1.0.0",
|
||||
"Name": "Komplex Wallpaper Engine",
|
||||
"ServiceTypes": [
|
||||
"Plasma/Wallpaper"
|
||||
]
|
||||
},
|
||||
"X-KDE-ParentApp": "org.kde.plasmashell",
|
||||
"X-Plasma-MainScript": "ui/main.qml"
|
||||
}
|
||||