Added frontend support for new features
This commit is contained in:
@@ -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,
|
||||
|
||||
434
package/contents/ui/PexelsImageHub.qml
Normal file
434
package/contents/ui/PexelsImageHub.qml
Normal 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
|
||||
}
|
||||
|
||||
}
|
||||
587
package/contents/ui/PexelsVideoHub.qml
Normal file
587
package/contents/ui/PexelsVideoHub.qml
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
577
package/contents/ui/ShaderToyHub.qml
Normal file
577
package/contents/ui/ShaderToyHub.qml
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user