Added frontend support for new features

This commit is contained in:
Digital Artifex
2025-09-21 06:59:49 -04:00
parent 2f645c32c6
commit 5c2db68353
10 changed files with 1974 additions and 139 deletions

View File

@@ -5,6 +5,10 @@
<kcfgfile name=""/>
<group name="General">
<entry name="shader_updated" type="Bool">
<label>shaderUpdated</label>
<default>false</default>
</entry>
<entry name="selectedShaderPath" type="String">
<label>SelectedShader</label>
<default>Shaders6/Ps3menu.frag.qsb</default>

View File

@@ -58,14 +58,6 @@ Rectangle
property var pack: wallpaper.configuration.shader_package
// onPackChanged: () =>
// {
// if(mainItem.ready)
// {
// shaderPackModel.loadJson(pack)
// }
// }
id: mainItem
color: "black"
@@ -108,11 +100,7 @@ Rectangle
property var bufferB
property var bufferC
property var bufferD
// ShaderChannel { id: bufferA; visible: false; anchors.fill: parent }
// ShaderChannel { id: bufferB; visible: false; anchors.fill: parent }
// ShaderChannel { id: bufferC; visible: false; anchors.fill: parent }
// ShaderChannel { id: bufferD; visible: false; anchors.fill: parent }
iTime: mainItem.iTime
iMouse: mainItem.iMouse
iResolution: mainItem.iResolution
@@ -230,35 +218,7 @@ Rectangle
// Recursive helper function to parse channels
function parseChannel(channel, json, typeDefault = 2, autodestroy = true)
{
var source = getFilePath(json.source)
channel.frameBufferChannel = json.frame_buffer_channel !== undefined ? json.frame_buffer_channel : -1
channel.type = json.type !== undefined ? json.type : typeDefault
channel.visible = false
channel.iMouse = Qt.binding(() => { return mainItem.iMouse; })
channel.iTime = Qt.binding(() => { return mainItem.iTime; })
channel.iResolutionScale = json.resolution_scale ? json.resolution_scale : 1.0
channel.iResolution = Qt.binding(() => { return json.resolution_x ? Qt.vector3d(json.resolution_x, json.resolution_y, 1.0) : Qt.vector3d(mainItem.iResolution.x,mainItem.iResolution.y,1.0); })
channel.mouseBias = json.mouse_scale ? json.mouse_scale : 1.0
channel.iTimeScale = json.time_scale ? json.time_scale : 1.0
channel.iTimeDelta = Qt.binding(() => { return mainItem.iTimeDelta; })
channel.width = Qt.binding(() => channel.iResolution.x)
channel.height = Qt.binding(() => channel.iResolution.y)
channel.iChannelTime = Qt.binding(() => {
return [
mainItem.iTime * channel.iTimeScale,
mainItem.iTime * channel.iTimeScale,
mainItem.iTime * channel.iTimeScale,
mainItem.iTime * channel.iTimeScale
];
});
channel.iFrameRate = Qt.binding(() => { return mainItem.iFrameRate; })
channel.iFrame = mainItem.iFrame
channel.invert = json.invert ? json.invert : false
channel.source = source
var component = Qt.createComponent("./ShaderChannel.qml")
if (json.channel0)
{
@@ -271,9 +231,8 @@ Rectangle
}
else if(typeof json.channel0 === "object")
{
var component = Qt.createComponent("./ShaderChannel.qml")
if (component.status === Component.Ready) {
if (component.status === Component.Ready)
{
channel.iChannel0 = component.createObject(mainItem, { })
parseChannel(channel.iChannel0, json.channel0)
}
@@ -293,9 +252,8 @@ Rectangle
}
else if(typeof json.channel1 === "object")
{
var component = Qt.createComponent("./ShaderChannel.qml")
if (component.status === Component.Ready) {
if (component.status === Component.Ready)
{
channel.iChannel1 = component.createObject(mainItem, { })
parseChannel(channel.iChannel1, json.channel1)
}
@@ -315,9 +273,8 @@ Rectangle
}
else if(typeof json.channel2 === "object")
{
var component = Qt.createComponent("./ShaderChannel.qml")
if (component.status === Component.Ready) {
if (component.status === Component.Ready)
{
channel.iChannel2 = component.createObject(mainItem, { })
parseChannel(channel.iChannel2, json.channel2)
}
@@ -337,9 +294,8 @@ Rectangle
}
else if(typeof json.channel3 === "object")
{
var component = Qt.createComponent("./ShaderChannel.qml")
if (component.status === Component.Ready) {
if (component.status === Component.Ready)
{
channel.iChannel3 = component.createObject(mainItem, { })
parseChannel(channel.iChannel3, json.channel3)
}
@@ -348,6 +304,37 @@ Rectangle
console.log('Uknown channel type 3 ' + typeof json.channel3)
}
channel.frameBufferChannel = json.frame_buffer_channel !== undefined ? json.frame_buffer_channel : -1
channel.type = json.type !== undefined ? json.type : typeDefault
channel.visible = false
channel.iMouse = Qt.binding(() => { return mainItem.iMouse; })
channel.iTime = Qt.binding(() => { return mainItem.iTime; })
channel.iResolutionScale = json.resolution_scale ? json.resolution_scale : 1.0
channel.iResolution = Qt.binding(() => { return json.resolution_x ? Qt.vector3d(json.resolution_x, json.resolution_y, 1.0) : Qt.vector3d(mainItem.iResolution.x,mainItem.iResolution.y,1.0); })
channel.mouseBias = json.mouse_scale ? json.mouse_scale : 1.0
channel.iTimeScale = json.time_scale ? json.time_scale : 1.0
channel.iTimeDelta = Qt.binding(() => { return mainItem.iTimeDelta; })
channel.width = Qt.binding(() => channel.iResolution.x)
channel.height = Qt.binding(() => channel.iResolution.y)
channel.materialTexture = json.materialTexture !== undefined ? getFilePath(json.materialTexture) : ""
channel.materialShader = json.materialShader !== undefined ? getFilePath(json.materialShader) : ""
channel.iChannelTime = Qt.binding(() => {
return [
mainItem.iTime * channel.iTimeScale,
mainItem.iTime * channel.iTimeScale,
mainItem.iTime * channel.iTimeScale,
mainItem.iTime * channel.iTimeScale
];
});
channel.iFrameRate = Qt.binding(() => { return mainItem.iFrameRate; })
channel.iFrame = mainItem.iFrame
channel.invert = json.invert ? json.invert : false
var source = getFilePath(json.source)
channel.source = source
if(autodestroy)
data.channels.push(channel)
}
@@ -364,11 +351,13 @@ Rectangle
var component = Qt.createComponent("./ShaderChannel.qml")
if (component.status === Component.Ready) {
if (component.status === Component.Ready)
{
channelOutput.bufferA = component.createObject(channelOutput, { visible: false })
channelOutput.bufferB = component.createObject(channelOutput, { visible: false })
channelOutput.bufferC = component.createObject(channelOutput, { visible: false })
channelOutput.bufferD = component.createObject(channelOutput, { visible: false })
data.buffers.set("{bufferA}", channelOutput.bufferA)
data.buffers.set("{bufferB}", channelOutput.bufferB)
data.buffers.set("{bufferC}", channelOutput.bufferC)
@@ -401,11 +390,11 @@ Rectangle
// Generate a new ShaderEffectSource for the requested buffer
function createBufferAssociation(buffer)
{
var component = Qt.createComponent('./ShaderBuffer.qml')
var result
if (component.status === Component.Ready) {
if (component.status === Component.Ready)
{
result = component.createObject(mainItem, {
x:0,
y:0,

View File

@@ -0,0 +1,434 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Dialogs
import QtQuick.Layouts
import Komplex.Pexels.Image as Pexels
Item
{
id: mainItem
anchors.fill: parent
Pexels.SearchModel
{
id: searchModel
}
ColumnLayout
{
anchors.fill: parent
RowLayout
{
Layout.fillHeight: false
Layout.fillWidth: true
Layout.margins: 6
TextField
{
Layout.preferredHeight: 32
Layout.fillWidth: true
id: searchField
placeholderText: "Search"
onEditingFinished: mainItem.updateSearch()
Keys.onPressed: (event) =>
{
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter)
{
searchField.focus = false; // Unfocus the TextField
event.accepted = true; // Prevent further propagation of the key event
}
}
}
Button
{
Layout.preferredHeight: 32
Layout.preferredWidth: 32
icon.name: "search-symbolic"
onClicked: mainItem.updateSearch()
}
}
Component
{
id: highlight
Rectangle {
width: view.cellWidth; height: view.cellHeight
color: palette.highlight; radius: 5
x: view.currentItem.x
y: view.currentItem.y
Behavior on x { SpringAnimation { spring: 3; damping: 0.2 } }
Behavior on y { SpringAnimation { spring: 3; damping: 0.2 } }
}
}
Rectangle
{
Layout.fillHeight: true
Layout.fillWidth: true
color: palette.base
clip: true
RowLayout
{
anchors.fill: parent
GridView
{
// The standard size
property int idealCellHeight: 300
property int idealCellWidth: 300
cellWidth: width / Math.floor(width / idealCellWidth)
cellHeight: idealCellHeight
id: view
model: searchModel
highlight: highlight
highlightFollowsCurrentItem: false
Layout.fillHeight: true
Layout.fillWidth: true
Layout.margins: 6
delegate: Column
{
property int itemIndex: index
property int originalHeight: imageHeight
property int originalWidth: imageWidth
property int imageId: id
property string author: photographer
property string authorUrl: photographerUrl
property string imageUrl: original
property string thumbnailUrl: thumbnail
property string altText: alt
property string largeThumbnail: large
id: entry
leftPadding: Math.floor((width - thumbnailImage.width) / 2)
topPadding: 10
rightPadding: Math.floor((width - thumbnailImage.width) / 2)
bottomPadding: 10
width: view.cellWidth
Image
{
width: 280
height: 200
id: thumbnailImage
source: thumbnail
anchors.horizontalCenter: parent.horizontalCenter
MouseArea
{
anchors.fill: parent
onClicked: (mouse) => {
view.currentIndex = parent.parent.itemIndex
}
}
Rectangle
{
color: palette.base
anchors.fill: parent
visible: thumbnailImage.status === Image.Loading
RowLayout
{
anchors.fill: parent
BusyIndicator
{
Layout.alignment: Qt.AlignCenter
Layout.preferredHeight: 64
Layout.preferredWidth: 64
visible: running
}
}
}
}
Text
{
property string externalLink: photographerUrl
elide: Text.ElideRight
topPadding: 4
bottomPadding: 2
text: "<h3>" + photographer + "</h3>"
anchors.horizontalCenter: parent.horizontalCenter
width: 280
color: palette.link
font.bold: true
MouseArea
{
anchors.fill: parent
onClicked: (mouse) => {
Qt.openUrlExternally(parent.externalLink)
}
}
}
Text
{
leftPadding: 8
rightPadding: 8
text: qsTr(alt)
anchors.horizontalCenter: parent.horizontalCenter
elide: Text.ElideRight
width: 280
color: palette.text
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
maximumLineCount: 2
font.italic: true
}
RowLayout
{
visible: parent.itemIndex === view.currentIndex
width: 280
Button
{
Layout.topMargin: 4
Layout.alignment: Qt.AlignRight
Layout.preferredHeight: 32
Layout.preferredWidth: 32
icon.name: "emblem-downloads"
onClicked: {
downloadDialog.imageHeight = entry.originalHeight
downloadDialog.imageWidth = entry.originalWidth
downloadDialog.photographer = entry.author
downloadDialog.photographerUrl = entry.authorUrl
downloadDialog.alt = entry.altText
downloadDialog.thumbnail = entry.largeThumbnail
downloadDialog.imageUrl = entry.imageUrl
downloadDialog.id = entry.imageId
downloadDialog.open()
}
}
}
}
populate: Transition
{
NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 1000 }
}
}
}
}
RowLayout
{
Layout.margins: 6
Layout.fillWidth: true
Button
{
text: "Previous"
enabled: searchModel.previousPage !== ""
onClicked: searchModel.back()
}
RowLayout
{
Layout.margins: 6
Layout.fillWidth: true
Text
{
visible: searchModel.totalResults > 0
color: palette.text
text: ((searchModel.resultsPerPage * searchModel.currentPage) - searchModel.resultsPerPage + 1) + "-" + (searchModel.resultsPerPage * searchModel.currentPage) + " of " + searchModel.totalResults
}
Text
{
color: palette.text
Layout.fillWidth: true
text: "Page " + searchModel.currentPage + " of " + Math.ceil(searchModel.totalResults / searchModel.resultsPerPage)
}
Text
{
color: palette.link
text: "<a href=\"https://www.pexels.com\">Photos provided by Pexels</a>"
font.bold: true
onLinkActivated: (link) => Qt.openUrlExternally(link)
}
}
Button
{
text: "Next"
enabled: searchModel.nextPage !== ""
onClicked: searchModel.next()
}
}
}
Rectangle
{
color: palette.base
anchors.fill: parent
visible: searchModel.status === Pexels.SearchModel.Searching
RowLayout
{
anchors.fill: parent
BusyIndicator
{
Layout.alignment: Qt.AlignCenter
Layout.preferredHeight: 128
Layout.preferredWidth: 128
visible: running
}
}
}
Dialog
{
property string photographer
property string photographerUrl
property string imageUrl
property string thumbnail
property string alt
property int imageHeight
property int imageWidth
property int id
id: downloadDialog
modal: Qt.WindowModal
width: 600
height: 420
anchors.centerIn: parent
clip: true
ColumnLayout
{
anchors.fill: parent
Text
{
property string externalLink: downloadDialog.photographerUrl
elide: Text.ElideRight
topPadding: 4
bottomPadding: 2
text: "<h3>" + downloadDialog.photographer + "</h3>"
width: 280
color: palette.link
font.bold: true
MouseArea
{
anchors.fill: parent
onClicked: (mouse) => {
Qt.openUrlExternally(parent.externalLink)
}
}
}
Image
{
Layout.fillHeight: true
Layout.fillWidth: true
fillMode: Image.PreserveAspectCrop
source: downloadDialog.thumbnail
}
Text
{
Layout.fillWidth: true
leftPadding: 8
rightPadding: 8
text: qsTr(downloadDialog.alt)
elide: Text.ElideRight
width: 280
color: palette.text
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
maximumLineCount: 4
font.italic: true
}
Button
{
Layout.fillWidth: true
text: "Download (" + downloadDialog.imageWidth + "x" + downloadDialog.imageHeight + ")"
icon.name: "image-symbolic"
onClicked: () =>
{
downloadDialog.close();
progressDialog.thumbnail = downloadDialog.thumbnail
progressDialog.photographer = downloadDialog.photographer
progressDialog.open()
searchModel.download(downloadDialog.imageUrl, downloadDialog.id)
}
}
}
}
Dialog
{
property string photographer
property string thumbnail
modal: Qt.WindowModal
width: 600
height: 420
anchors.centerIn: parent
clip: true
id: progressDialog
ColumnLayout
{
anchors.fill: parent
Image
{
Layout.fillHeight: true
Layout.fillWidth: true
fillMode: Image.PreserveAspectCrop
source: downloadDialog.thumbnail
}
Text
{
text: "Downloading Photo..."
color: palette.text
}
ProgressBar
{
value: searchModel.downloadProgress
Layout.fillWidth: true
Layout.preferredHeight: 6
}
}
Connections
{
target: searchModel
function onDownloadFinished()
{
progressDialog.close()
}
}
}
function updateSearch()
{
console.log(searchField.text)
searchModel.query = searchField.text
}
}

View File

@@ -0,0 +1,587 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Dialogs
import QtQuick.Layouts
import QtMultimedia
import Komplex.Pexels.Video as Pexels
Item
{
property alias selectedFile: searchModel.lastSavedFile
id: mainItem
anchors.fill: parent
Pexels.SearchModel
{
id: searchModel
}
ColumnLayout
{
anchors.fill: parent
RowLayout
{
Layout.fillHeight: false
Layout.fillWidth: true
Layout.margins: 6
TextField
{
Layout.preferredHeight: 32
Layout.fillWidth: true
id: searchField
placeholderText: "Search"
onEditingFinished: mainItem.updateSearch()
Keys.onPressed: (event) =>
{
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter)
{
searchField.focus = false; // Unfocus the TextField
event.accepted = true; // Prevent further propagation of the key event
}
}
}
Button
{
Layout.preferredHeight: 32
Layout.preferredWidth: 32
icon.name: "search-symbolic"
onClicked: mainItem.updateSearch()
}
}
Component
{
id: highlight
Rectangle
{
width: view.cellWidth; height: view.cellHeight
color: palette.highlight; radius: 5
x: view.currentItem.x
y: view.currentItem.y
Behavior on x { SpringAnimation { spring: 3; damping: 0.2 } }
Behavior on y { SpringAnimation { spring: 3; damping: 0.2 } }
}
}
Rectangle
{
Layout.fillHeight: true
Layout.fillWidth: true
color: palette.base
clip: true
RowLayout
{
anchors.fill: parent
GridView
{
// The standard size
property int idealCellHeight: 220
property int idealCellWidth: 250
cellWidth: width / Math.floor(width / idealCellWidth)
cellHeight: idealCellHeight
id: view
model: searchModel
highlight: highlight
highlightFollowsCurrentItem: false
Layout.fillHeight: true
Layout.fillWidth: true
Layout.margins: 6
delegate: Column
{
property int itemIndex: index
property int originalHeight: videoHeight
property int originalWidth: videoWidth
property int videoId: id
property string author: user
property string authorUrl: userUrl
property string videoUrl: videoUrl
property string thumbnailUrl: thumbnail
id: entry
leftPadding: Math.floor((width - thumbnailImage.width) / 2)
topPadding: 10
rightPadding: Math.floor((width - thumbnailImage.width) / 2)
bottomPadding: 10
width: view.cellWidth
Image
{
width: 250
height: 140
id: thumbnailImage
source: thumbnail
anchors.horizontalCenter: parent.horizontalCenter
Rectangle
{
color: palette.base
anchors.fill: parent
visible: thumbnailImage.status === Image.Loading
RowLayout
{
anchors.fill: parent
BusyIndicator
{
Layout.alignment: Qt.AlignCenter
Layout.preferredHeight: 64
Layout.preferredWidth: 64
visible: running
}
}
}
MouseArea
{
anchors.fill: parent
onClicked: (mouse) =>
{
view.currentIndex = parent.parent.itemIndex
searchModel.currentIndex = view.currentIndex
}
}
}
Text
{
property string externalLink: authorUrl
elide: Text.ElideRight
topPadding: 4
bottomPadding: 2
text: "<h3>" + author + "</h3>"
anchors.horizontalCenter: parent.horizontalCenter
width: 280
color: palette.link
MouseArea
{
anchors.fill: parent
onClicked: (mouse) =>
{
Qt.openUrlExternally(parent.externalLink)
}
cursorShape: Qt.PointingHandCursor
}
}
RowLayout
{
visible: parent.itemIndex === view.currentIndex
width: thumbnailImage.width
Button
{
Layout.topMargin: 4
Layout.alignment: Qt.AlignRight
Layout.preferredHeight: 32
Layout.fillWidth: true
icon.name: "emblem-downloads"
text: qsTr("Preview & Download")
onClicked: () =>
{
downloadDialog.user = entry.author
downloadDialog.userUrl = entry.authorUrl
downloadDialog.thumbnail = entry.thumbnailUrl
downloadDialog.id = entry.videoId
downloadDialog.open()
searchModel.videoModel.update()
}
}
}
}
populate: Transition
{
NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 1000 }
}
}
}
}
RowLayout
{
Layout.margins: 6
Layout.fillWidth: true
Button
{
text: "Previous"
enabled: searchModel.previousPage !== ""
onClicked: searchModel.back()
}
RowLayout
{
Layout.margins: 6
Layout.fillWidth: true
Text
{
visible: searchModel.totalResults > 0
color: palette.text
text: ((searchModel.resultsPerPage * searchModel.currentPage) - searchModel.resultsPerPage + 1) + "-" + (searchModel.resultsPerPage * searchModel.currentPage) + " of " + searchModel.totalResults
}
Text
{
color: palette.text
Layout.fillWidth: true
text: "Page " + searchModel.currentPage + " of " + Math.ceil(searchModel.totalResults / searchModel.resultsPerPage)
}
Text
{
color: palette.text
text: "Videos provided by Pexels"
font.bold: true
onLinkActivated: (link) => Qt.openUrlExternally(link)
MouseArea
{
anchors.fill: parent
onClicked: (mouse) =>
{
Qt.openUrlExternally("https://www.pexels.com")
}
cursorShape: Qt.PointingHandCursor
}
}
}
Button
{
text: "Next"
enabled: searchModel.nextPage !== ""
onClicked: searchModel.next()
}
}
}
Rectangle
{
color: palette.base
anchors.fill: parent
visible: searchModel.status === Pexels.SearchModel.Searching
RowLayout
{
anchors.fill: parent
BusyIndicator
{
Layout.alignment: Qt.AlignCenter
Layout.preferredHeight: 128
Layout.preferredWidth: 128
visible: running
}
}
}
Dialog
{
property string user
property string userUrl
property string thumbnail
property string preview
property int id
id: downloadDialog
modal: Qt.WindowModal
width: 600
height: 440
anchors.centerIn: parent
clip: true
ColumnLayout
{
anchors.fill: parent
Text
{
property string externalLink: downloadDialog.userUrl
elide: Text.ElideRight
text: "<h3>" + downloadDialog.user + "</h3>"
width: 280
color: palette.link
MouseArea
{
anchors.fill: parent
onClicked: (mouse) =>
{
Qt.openUrlExternally(parent.externalLink)
}
cursorShape: Qt.PointingHandCursor
}
}
Rectangle
{
Layout.fillWidth: true
Layout.preferredHeight: 300
color: "black"
ColumnLayout
{
anchors.fill: parent
spacing: 0
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
Layout.preferredWidth: 500
Layout.preferredHeight: 281
Layout.alignment: Qt.AlignHCenter
fillMode: VideoOutput.PreserveAspectCrop
smooth: true
onHeightChanged: this.sizeChanged()
MediaPlayer
{
id: mediaPlayer
videoOutput: videoOutput
source: Qt.resolvedUrl(downloadSelector.currentValue ? downloadSelector.currentValue : "")
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); }
Image
{
visible: !mediaPlayer.playing
anchors.fill: parent
fillMode: Image.PreserveAspectFill
source: downloadDialog.thumbnail
}
}
Rectangle
{
Layout.fillWidth: true
Layout.preferredHeight: 18
color: palette.alternateBase
RowLayout
{
anchors.fill: parent
spacing: 0
ProgressBar
{
Layout.fillWidth: true
Layout.fillHeight: true
from: 0
to: mediaPlayer.duration
value: mediaPlayer.position
}
Button
{
Layout.preferredHeight: 18
Layout.preferredWidth: 18
icon.name: mediaPlayer.playing ? "stop-symbolic" : "play-symbolic"
icon.height: 16
icon.width: 16
onClicked: () =>
{
if(mediaPlayer.playing)
mediaPlayer.stop()
else
mediaPlayer.play()
}
}
}
}
}
}
Text
{
text: qsTr("Download Options")
color: palette.text
font.bold: true
Layout.fillHeight: true
verticalAlignment: Text.AlignBottom
}
RowLayout
{
Layout.fillWidth: true
Layout.preferredHeight: downloadSelector.height
ComboBox
{
id: downloadSelector
Layout.fillWidth: true
model: searchModel.videoModel
textRole: "text"
valueRole: "url"
}
Button
{
enabled: downloadSelector.currentIndex >= 0
Layout.preferredHeight: downloadSelector.height
Layout.preferredWidth: downloadSelector.height
icon.name: "image-symbolic"
id: downloadButton
onClicked: () =>
{
downloadDialog.close();
progressDialog.thumbnail = downloadDialog.thumbnail
progressDialog.author = downloadDialog.user
progressDialog.open()
searchModel.videoModel.download(downloadSelector.currentIndex)
}
}
}
}
Connections
{
target: searchModel.videoModel
function onStatusChanged()
{
if(searchModel.videoModel.status === 0)
downloadSelector.currentIndex = 0
}
}
onClosed: () =>
{
mediaPlayer.stop()
}
Rectangle
{
color: palette.base
anchors.fill: parent
visible: searchModel.videoModel.status === 1
RowLayout
{
anchors.fill: parent
BusyIndicator
{
Layout.alignment: Qt.AlignCenter
Layout.preferredHeight: 128
Layout.preferredWidth: 128
visible: running
}
}
}
}
Dialog
{
property string author
property string thumbnail
modal: Qt.WindowModal
width: 600
height: 420
anchors.centerIn: parent
clip: true
id: progressDialog
ColumnLayout
{
anchors.fill: parent
Image
{
Layout.fillHeight: true
Layout.fillWidth: true
fillMode: Image.PreserveAspectCrop
source: downloadDialog.thumbnail
}
Text
{
text: "Downloading Video..."
color: palette.text
}
ProgressBar
{
value: searchModel.videoModel.downloadProgress
Layout.fillWidth: true
Layout.preferredHeight: 6
}
}
Connections
{
target: searchModel.videoModel
function onDownloadFinished()
{
progressDialog.close()
}
}
}
function updateSearch()
{
console.log(searchField.text)
searchModel.query = searchField.text
}
}

View File

@@ -43,7 +43,8 @@ Item
VideoChannel,
ShaderChannel,
CubeMapChannel,
AudioChannel
AudioChannel,
SceneChannel
}
property int type: ShaderChannel.Type.ImageChannel
@@ -70,6 +71,9 @@ Item
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 int frameBufferChannel: -1
property bool blending: false
property string materialTexture:""
property string materialShader:""
property var windowModel
// bind to ShaderEffectSource.live to prevent data being updated causing a refresh between intended frames. I think this may be why
// the shaders are using so many resources. This is likely to cause issues of its own since we have no way of knowing the progress
@@ -80,8 +84,20 @@ Item
// but god damn does it feel hacky. Try to find a better way to actually limit framerate
onIFrameChanged: () =>
{
active = true;
active = false;
// this method of frame limiting breaks video playback
// even when the video is the source of an item being limited
// in this way
// if(type === ShaderChannel.ShaderChannel)
// {
// active = true;
// active = false;
// return;
// }
// if(active === false)
// active = true;
}
property bool invert
@@ -162,10 +178,14 @@ Item
PropertyChanges
{
loader.sourceComponent: channelAudio
// loader.width: 512
// loader.height: 2
// loader.and
}
},
State
{
when: channel.type === ShaderChannel.Type.SceneChannel
PropertyChanges
{
loader.sourceComponent: Qt.createComponent(channel.source)
}
}
]
@@ -206,35 +226,42 @@ Item
Component
{
id: channelVideo
VideoOutput
Rectangle
{
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: VideoOutput.PreserveAspectCrop
smooth: true
color: "black"
onHeightChanged: this.sizeChanged()
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
property bool loaded: false
MediaPlayer
signal sizeChanged
signal fatalError
id: videoComponent
visible: true
anchors.fill: parent
fillMode: VideoOutput.PreserveAspectCrop
smooth: true
onHeightChanged: this.sizeChanged()
}
MediaPlayer
{
id: mediaPlayer
videoOutput: videoOutput
videoOutput: videoComponent
loops: MediaPlayer.Infinite
source: Qt.resolvedUrl(channel.source)
playbackRate: channel.iTimeScale >= 0.01 ? channel.iTimeScale : 0.01
audioOutput: AudioOutput
{
@@ -242,22 +269,96 @@ Item
volume: 0
}
onErrorOccurred: function(error, errorString)
onErrorOccurred: (error, errorString) =>
{
if (MediaPlayer.NoError !== error)
{
console.log("[qmlvideo] VideoItem.onError error " + error + " errorString " + errorString)
videoOutput.fatalError()
videoComponent.fatalError()
}
}
onSourceChanged:
{
if(mediaPlayer.source != "")
mediaPlayer.play()
else
mediaPlayer.stop()
if(!videoComponent.loaded)
return;
delayedStartTimer.start()
}
onMediaStatusChanged:
{
switch(mediaStatus)
{
case MediaPlayer.NoMedia:
console.log("No media loaded")
break;
case MediaPlayer.LoadingMedia:
console.log("Video loading")
break;
case MediaPlayer.LoadedMedia:
console.log("Video Loaded")
break;
case MediaPlayer.BufferingMedia:
console.log("Video buffering")
break;
case MediaPlayer.StalledMedia:
console.log("Video stalled")
break;
case MediaPlayer.BufferedMedia:
console.log("Video buffered")
break;
case MediaPlayer.EndOfMedia:
console.log("Video EOF")
break;
case MediaPlayer.InvalidMedia:
console.log("Video invalid")
break;
}
}
onPlaybackStateChanged:
{
switch(playbackState)
{
case MediaPlayer.PlayingState:
console.log("Video playback started")
break;
case MediaPlayer.PausedState:
console.log("Video playback paused")
break;
case MediaPlayer.StoppedState:
console.log("Video playback stopped")
break;
}
}
Component.onCompleted:
{
videoComponent.loaded = true
delayedStartTimer.start()
}
function autoStart()
{
if(mediaPlayer.source !== "" && mediaPlayer.source !== undefined)
{
console.log("Starting playback of " + mediaPlayer.source)
mediaPlayer.play()
}
else
{
console.log("Stopping playback")
mediaPlayer.stop()
}
}
}
Timer
{
id: delayedStartTimer
interval: 500
repeat: false
onTriggered: mediaPlayer.autoStart()
}
function start() { mediaPlayer.play() }
@@ -395,14 +496,6 @@ Item
blending: channel.blending
}
// ShaderEffectSource
// {
// anchors.fill: parent
// sourceItem: channelShaderOutput
// hideSource: true
// visible: true
// }
}
}
@@ -530,4 +623,129 @@ Item
}
}
}
}
// Scene is directly loaded, no comp needed
// 3D Model
Component
{
id: modelComponent
Item
{
anchors.fill: parent
id: channelModelContent
// recursive frame buffer
ShaderEffectSource
{
id: frameBufferSource
sourceItem: channel.frameBufferChannel === -1 ? null : channelModelContent
sourceRect: Qt.rect(0,0, channelModelContent.width, channelModelContent.height)
wrapMode: ShaderEffectSource.ClampToEdge
live: channel.active
mipmap: true
recursive: true
textureSize: Qt.size(channelModelContent.width, channelModelContent.height)
visible: false
textureMirroring: ShaderEffectSource.NoMirroring
width: channel.iResolution.x
height: channel.iResolution.y
}
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 -= (data.iMouse.x - lastX) * 0.5
pitch -= (data.iMouse.y - lastY) * -0.5
pitch = Math.max(-89, Math.min(89, pitch))
lastX = data.iMouse.x
lastY = data.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()
}
}
Model
{
source: Qt.resolvedUrl(channel.source)
scale: Qt.vector3d(channel.scale)
geometry: Komplex.GeometryProvider
{
source: Qt.resolvedUrl(channel.source)
}
materials:
[
CustomMaterial
{
property var iChannel0: channel.frameBufferChannel === 0 ? frameBufferSource : channelSource0
property var iChannel1: channel.frameBufferChannel === 1 ? frameBufferSource : channelSource1
property var iChannel2: channel.frameBufferChannel === 2 ? frameBufferSource : channelSource2
property var iChannel3: channel.frameBufferChannel === 3 ? frameBufferSource : channelSource3
property var iResolution: channel.iResolution
property var iTime: data.iTime
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: data.iMouse
property var iDate: channel.iDate
property var iChannelResolution: channel.iResolution
Texture
{
id: baseColorMap
source: Qt.resolvedUrl(channel.materialTexture)
}
cullMode: PrincipledMaterial.NoCulling
fragmentShader: Qt.resolvedUrl(channel.materialShader)
}
]
}
}
}
}
}

View File

@@ -74,6 +74,7 @@ Item
property int resolution_y
property bool enabled
property bool invert
property bool changed
id: window
@@ -116,6 +117,16 @@ Item
type: ShaderChannel.Type.ImageChannel
}
ListElement
{
file: true
name: "Scene"
icon: "./icons/image.svg"
title: "Select a scene file"
filter: "Image Files (*.qml)"
type: ShaderChannel.Type.SceneChannel
}
ListElement
{
file: true
@@ -574,8 +585,8 @@ Item
function accept()
{
// copy over temp values
source = tmp_source
type = tmp_type
source = tmp_source
timeScale = tmp_timeScale
resolution_scale = tmp_resolution_scale
resolution_x = tmp_resolution_x
@@ -629,6 +640,12 @@ Item
// Function to reset the selection to default values
function resetSelection()
{
if((tmp_source !== source) || (tmp_enabled !== enabled) ||
(tmp_invert !== invert) || (tmp_resolution_scale !== resolution_scale) ||
(tmp_resolution_x !== resolution_x) || (tmp_resolution_y !== resolution_y) ||
(tmp_timeScale !== timeScale) || (tmp_type !== type))
changed = true;
tmp_source = source
tmp_timeScale = timeScale
tmp_resolution_scale = resolution_scale

View File

@@ -0,0 +1,577 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Dialogs
import QtQuick.Layouts
import QtMultimedia
import QtWebView
import Komplex.ShaderToy as ShaderToy
Item
{
id: mainItem
anchors.fill: parent
ShaderToy.SearchModel
{
id: searchModel
}
ColumnLayout
{
anchors.fill: parent
RowLayout
{
Layout.fillHeight: false
Layout.fillWidth: true
Layout.margins: 6
TextField
{
Layout.preferredHeight: 32
Layout.fillWidth: true
id: searchField
placeholderText: "Search"
onEditingFinished: mainItem.updateSearch()
Keys.onPressed: (event) =>
{
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter)
{
searchField.focus = false; // Unfocus the TextField
event.accepted = true; // Prevent further propagation of the key event
}
}
}
Button
{
Layout.preferredHeight: 32
Layout.preferredWidth: 32
icon.name: "search-symbolic"
onClicked: mainItem.updateSearch()
}
}
Component
{
id: highlight
Rectangle
{
width: view.cellWidth; height: view.cellHeight
color: palette.highlight; radius: 5
x: view.currentItem.x
y: view.currentItem.y
Behavior on x { SpringAnimation { spring: 3; damping: 0.2 } }
Behavior on y { SpringAnimation { spring: 3; damping: 0.2 } }
}
}
Rectangle
{
Layout.fillHeight: true
Layout.fillWidth: true
color: palette.base
clip: true
RowLayout
{
anchors.fill: parent
GridView
{
// The standard size
property int idealCellHeight: 200
property int idealCellWidth: 250
cellWidth: width / Math.floor(width / idealCellWidth)
cellHeight: idealCellHeight
id: view
model: searchModel
highlight: highlight
highlightFollowsCurrentItem: false
Layout.fillHeight: true
Layout.fillWidth: true
Layout.margins: 6
delegate: Column
{
property int itemIndex: index
property string shaderThumbnail: thumbnail
property string shaderEmbed: embedUrl
property string shaderId: id
property string author: username
property string shaderDescription: model.description
id: entry
leftPadding: Math.floor((width - thumbnailImage.width) / 2)
topPadding: 10
rightPadding: Math.floor((width - thumbnailImage.width) / 2)
bottomPadding: 10
width: view.cellWidth
Image
{
width: 250
height: 140
id: thumbnailImage
source: thumbnail
anchors.horizontalCenter: parent.horizontalCenter
Rectangle
{
color: palette.base
anchors.fill: parent
visible: thumbnailImage.status === Image.Loading
RowLayout
{
anchors.fill: parent
BusyIndicator
{
Layout.alignment: Qt.AlignCenter
Layout.preferredHeight: 64
Layout.preferredWidth: 64
visible: running
}
}
}
Rectangle
{
color: palette.dark
anchors.fill: parent
visible: thumbnailImage.status === Image.Error
Text
{
color: palette.text
anchors.centerIn: parent
text: qsTr("Error Loading Image")
}
}
MouseArea
{
anchors.fill: parent
onClicked:
(mouse) => {
view.currentIndex = parent.parent.itemIndex
//searchModel.currentIndex = view.currentIndex
}
}
}
RowLayout
{
visible: parent.itemIndex === view.currentIndex
width: thumbnailImage.width
Button
{
Layout.topMargin: 4
Layout.alignment: Qt.AlignRight
Layout.preferredHeight: 32
Layout.fillWidth: true
icon.name: "emblem-downloads"
text: qsTr("Preview & Download")
onClicked: () =>
{
downloadDialog.open()
}
}
}
Dialog
{
id: downloadDialog
modal: Qt.WindowModal
width: 600
height: 440
parent: mainItem
anchors.centerIn: mainItem
clip: true
ColumnLayout
{
anchors.fill: parent
WebView
{
Layout.fillHeight: true
Layout.fillWidth: true
id: shaderPreview
Layout.preferredWidth: 500
Layout.preferredHeight: 281
Layout.alignment: Qt.AlignHCenter
url: ""
}
Text
{
Layout.preferredHeight: 64
Layout.fillHeight: true
Layout.fillWidth: true
id: shaderDescription
text: model.description
elide: Text.ElideRight
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
maximumLineCount: 3
color: palette.text
}
Button
{
Layout.fillWidth: true
icon.name: "image-symbolic"
id: downloadButton
text: qsTr("Convert to Komplex Pack")
onClicked: () =>
{
workingThumbnail.source = model.thumbnail
searchModel.convert(model.index);
downloadDialog.close();
}
}
}
onClosed: () =>
{
shaderPreview.loadHtml(`<html><head></head><body></body></html>`)
}
onOpened: () =>
{
shaderDescription.text = model.description
shaderPreview.loadHtml(`<html><head></head><body style="background-color:${palette.base.toString(16)};"><center><iframe src="${model.embedUrl}?gui=true&t=10&paused=false&muted=true" width="500" height="281" frameborder="0" allowfullscreen="allowfullscreen" /></center></body></html>`)
}
}
}
populate: Transition
{
NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 1000 }
}
}
}
}
RowLayout
{
Layout.margins: 6
Layout.fillWidth: true
Button
{
text: "Previous"
enabled: searchModel.currentPage > 1
onClicked: searchModel.previous()
}
RowLayout
{
Layout.margins: 6
Layout.fillWidth: true
Text
{
visible: searchModel.totalResults > 0
color: palette.text
text: ((searchModel.resultsPerPage * searchModel.currentPage) - searchModel.resultsPerPage + 1) + "-" + (searchModel.resultsPerPage * searchModel.currentPage) + " of " + searchModel.totalResults
}
Text
{
color: palette.text
Layout.fillWidth: true
text: "Page " + searchModel.currentPage + " of " + Math.ceil(searchModel.totalResults / searchModel.resultsPerPage)
}
Text
{
color: palette.text
text: "Shaders provided by ShaderToy"
font.bold: true
onLinkActivated: (link) => Qt.openUrlExternally(link)
MouseArea
{
anchors.fill: parent
onClicked: (mouse) => Qt.openUrlExternally("https://www.shadertoy.com")
cursorShape: Qt.PointingHandCursor
}
}
}
Button
{
text: "Next"
enabled: searchModel.currentPage <= searchModel.totalPages
onClicked: searchModel.next()
}
}
}
Rectangle
{
color: palette.base
anchors.fill: parent
visible: searchModel.status === ShaderToy.SearchModel.Searching || searchModel.status === ShaderToy.SearchModel.Compiling
RowLayout
{
anchors.fill: parent
Image
{
visible: searchModel.status === ShaderToy.SearchModel.Compiling
Layout.fillHeight: true
Layout.fillWidth: true
fillMode: Image.PreserveAspectCrop
id: workingThumbnail
}
Text
{
id: stateText
text: searchModel.statusMessage
color: palette.text
elide: Text.ElideRight
visible: searchModel.status === ShaderToy.SearchModel.Compiling
}
ProgressBar
{
id: totalProgress
Layout.fillWidth: true
Layout.preferredHeight: 6
visible: searchModel.status === ShaderToy.SearchModel.Compiling
}
Text
{
id: downloadText
text: qsTr(searchModel.downloadText)
color: palette.text
elide: Text.ElideRight
visible: searchModel.totalDownloads > 0 && searchModel.status === ShaderToy.SearchModel.Compiling
}
ProgressBar
{
id: downloadProgress
Layout.fillWidth: true
Layout.preferredHeight: 6
from: 0
to: searchModel.totalDownloads
value: searchModel.completedDownloads
visible: searchModel.totalDownloads > 0 && searchModel.status === ShaderToy.SearchModel.Compiling
}
}
BusyIndicator
{
Layout.alignment: Qt.AlignCenter
width: 128
height: 128
visible: running
anchors.centerIn: parent
}
}
Rectangle
{
property int totalVideos: searchModel.videoSelections.length
property int selectedVideos: 0
anchors.fill: parent
color: palette.base
visible: searchModel.status === ShaderToy.SearchModel.Compiled
enabled: visible
id: mediaSelectionItem
signal accepted
ColumnLayout
{
anchors.fill: parent
Text
{
Layout.margins: 6
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
Layout.preferredHeight: 50
color: palette.text
text: "<h2>Select Media</h2>";
}
Text
{
Layout.margins: 6
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
color: palette.text
text: qsTr("The shader you selected contains one or more video references. However, the videos available on ShaderToy are not likely to be desired.\n\nPlease select a new video source from your local drive or from Pexels.")
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
}
Repeater
{
Layout.fillWidth: true
Layout.margins: 6
model: searchModel.videoSelections
Item
{
width: parent.width
required property string modelData
RowLayout
{
anchors.fill: parent
TextField
{
Layout.fillWidth: true
Layout.preferredHeight: 35
id: mediaSelectionField
placeholderText: "Select Media Source"
color: palette.text
onEditingFinished:
{
if(text.length > 0)
mediaSelectionItem.selectedVideos += 1
else if(mediaItem.selectedVideos > 0)
mediaSelectionItem.selectedVideos -= 1
}
}
Button
{
icon.name: "folder-symbolic"
onClicked: fileDialog.open()
}
Button
{
icon.name: "network-symbolic"
onClicked: pexelsDialog.open()
}
}
FileDialog
{
id: fileDialog
currentFolder: StandardPaths.standardLocations(StandardPaths.HomeLocation)[0]
onAccepted:
{
mediaSelectionField.text = selectedFile
close();
}
}
Dialog
{
width: 640
height: 480
id: pexelsDialog
parent: mainItem
anchors.centerIn: parent
PexelsVideoHub
{
onSelectedFileChanged:
{
mediaSelectionField.text = selectedFile
mediaSelectionField.editingFinished()
pexelsDialog.close()
}
}
}
Connections
{
target: mediaSelectionItem
function onAccepted()
{
searchModel.replaceSource(view.currentIndex, modelData, mediaSelectionField.text)
}
}
}
}
Rectangle
{
color: "transparent"
Layout.fillHeight: true
Layout.fillWidth: true
}
Button
{
Layout.preferredHeight: 35
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
text: qsTr("Accept")
enabled: mediaSelectionItem.totalVideos === mediaSelectionItem.selectedVideos
onClicked: mediaSelectionItem.accepted()
}
}
}
MessageDialog
{
buttons: MessageDialog.Ok
id: warningDialog
text: searchModel.statusMessage
Connections
{
target: searchModel
function onStatusChanged()
{
console.log("Search Model Status " + searchModel.status)
if(searchModel.status === ShaderToy.SearchModel.Error)
{
warningDialog.open();
}
}
}
}
MessageDialog
{
buttons: MessageDialog.Ok
id: messageDialog
text: searchModel.statusMessage
Connections
{
target: searchModel
function onShaderInstalled()
{
messageDialog.open();
}
}
}
function updateSearch()
{
console.log(searchField.text)
searchModel.currentPage = 1
searchModel.query = searchField.text
}
}

View File

@@ -24,7 +24,6 @@
* 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
@@ -94,7 +93,7 @@ Item
source: wallpaper.configuration.iChannel0_flag ? Qt.resolvedUrl(wallpaper.configuration.iChannel0) : ""
// ShaderToy seems to start at bottom left for 0,0
invert: wallpaper.configuration.iChannel0_inverted
invert: wallpaper.configuration.iChannel0_inverted !== undefined ? wallpaper.configuration.iChannel0_inverted : true
}
ShaderChannel

View File

@@ -94,6 +94,7 @@ Kirigami.FormLayout
property alias cfg_resolution_y: resolutionYField.value
property alias cfg_framerate_limit: frameRateField.value
property bool cfg_shader_updated
Palette
{
@@ -366,7 +367,13 @@ Kirigami.FormLayout
id: shaderChannelConfig0
palette: palette
height: 350
onAccepted: shaderChannelOverlay0.close(); // Close the overlay after configuration
onAccepted: () =>
{
if(shaderChannelConfig0.changed)
root.cfg_shader_updated = true
shaderChannelOverlay0.close(); // Close the overlay after configuration
}
onRejected: shaderChannelOverlay0.close();
}
}
@@ -411,7 +418,13 @@ Kirigami.FormLayout
{
id: shaderChannelConfig1
height: 350
onAccepted: shaderChannelOverlay1.close(); // Close the overlay after configuration
onAccepted: () =>
{
if(shaderChannelConfig1.changed)
root.cfg_shader_updated = true
shaderChannelOverlay1.close(); // Close the overlay after configuration
}
onRejected: shaderChannelOverlay1.close();
}
}
@@ -456,7 +469,13 @@ Kirigami.FormLayout
{
id: shaderChannelConfig2
height: 350
onAccepted: shaderChannelOverlay2.close(); // Close the overlay after configuration
onAccepted: () =>
{
if(shaderChannelConfig2.changed)
root.cfg_shader_updated = true
shaderChannelOverlay2.close(); // Close the overlay after configuration
}
onRejected: shaderChannelOverlay2.close();
}
}
@@ -500,7 +519,13 @@ Kirigami.FormLayout
{
id: shaderChannelConfig3
height: 350
onAccepted: shaderChannelOverlay3.close(); // Close the overlay after configuration
onAccepted: () =>
{
if(shaderChannelConfig3.changed)
root.cfg_shader_updated = true
shaderChannelOverlay3.close(); // Close the overlay after configuration
}
onRejected: shaderChannelOverlay3.close();
}
}

View File

@@ -43,6 +43,13 @@ WallpaperItem
property string shaderPack: wallpaper.configuration.shader_package
property bool changing: false
property bool updated: wallpaper.configuration.shader_updated
property bool iChannel0_inverted: wallpaper.configuration.iChannel0_inverted
property bool iChannel1_inverted: wallpaper.configuration.iChannel1_inverted
property bool iChannel2_inverted: wallpaper.configuration.iChannel2_inverted
property bool iChannel3_inverted: wallpaper.configuration.iChannel3_inverted
anchors.fill: parent
Loader
@@ -78,6 +85,7 @@ WallpaperItem
ShaderToyModel
{
//wallpaper: wallpaper
screenGeometry: wallpaperItem.parent.screenGeometry
anchors.fill: parent
}
@@ -95,41 +103,18 @@ WallpaperItem
}
// band-aid section
onResolution_xChanged: () =>
onResolution_xChanged: () => reload();
onResolution_yChanged: () => reload();
onShaderPackChanged: () => reload();
onIChannel0_invertedChanged: () => reload();
onUpdatedChanged: () =>
{
if(changing)
return;
changing = true
pageLoader.sourceComponent = null
if(wallpaper.configuration.komplex_mode === 0)
pageLoader.sourceComponent = shaderToyContent
else
pageLoader.sourceComponent = packContent
changing = false
if(updated)
reload();
}
onResolution_yChanged: () =>
{
if(changing)
return;
changing = true
pageLoader.sourceComponent = null
if(wallpaper.configuration.komplex_mode === 0)
pageLoader.sourceComponent = shaderToyContent
else
pageLoader.sourceComponent = packContent
changing = false
}
onShaderPackChanged: () =>
function reload()
{
if(changing)
return;