2023-06-29 15:06:11 +03:00
|
|
|
// Copyright (C) 2023 The Qt Company Ltd.
|
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
|
|
|
|
|
|
|
|
import QtQuick
|
|
|
|
|
import HelperWidgets as HelperWidgets
|
|
|
|
|
import StudioControls as StudioControls
|
|
|
|
|
import StudioTheme as StudioTheme
|
|
|
|
|
import EffectMakerBackend
|
|
|
|
|
|
|
|
|
|
Item {
|
|
|
|
|
id: root
|
|
|
|
|
|
2023-09-04 14:17:01 +03:00
|
|
|
property var draggedSec: null
|
|
|
|
|
property var secsY: []
|
|
|
|
|
property int moveFromIdx: 0
|
|
|
|
|
property int moveToIdx: 0
|
2023-11-06 14:41:31 +02:00
|
|
|
property bool previewAnimationRunning: false
|
2023-09-04 14:17:01 +03:00
|
|
|
|
2023-12-12 15:15:15 +02:00
|
|
|
// Invoked after save changes is done
|
|
|
|
|
property var onSaveChangesCallback: () => {}
|
|
|
|
|
|
|
|
|
|
// Invoked from C++ side when open composition is requested and there are unsaved changes
|
|
|
|
|
function promptToSaveBeforeOpen() {
|
|
|
|
|
root.onSaveChangesCallback = () => { EffectMakerBackend.rootView.doOpenComposition() }
|
|
|
|
|
|
|
|
|
|
saveChangesDialog.open()
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-13 12:11:06 +02:00
|
|
|
Connections {
|
|
|
|
|
target: EffectMakerBackend.effectMakerModel
|
|
|
|
|
function onIsEmptyChanged() {
|
|
|
|
|
if (EffectMakerBackend.effectMakerModel.isEmpty)
|
|
|
|
|
saveAsDialog.close()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-08 14:58:04 +02:00
|
|
|
SaveAsDialog {
|
2023-12-13 12:11:06 +02:00
|
|
|
id: saveAsDialog
|
2023-10-19 19:21:31 +03:00
|
|
|
anchors.centerIn: parent
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-08 14:58:04 +02:00
|
|
|
SaveChangesDialog {
|
|
|
|
|
id: saveChangesDialog
|
|
|
|
|
anchors.centerIn: parent
|
|
|
|
|
|
2023-12-11 17:36:03 +02:00
|
|
|
onSave: {
|
|
|
|
|
if (EffectMakerBackend.effectMakerModel.currentComposition === "") {
|
|
|
|
|
// if current composition is unsaved, show save as dialog and clear afterwards
|
2023-12-13 12:11:06 +02:00
|
|
|
saveAsDialog.clearOnClose = true
|
|
|
|
|
saveAsDialog.open()
|
2023-12-11 17:36:03 +02:00
|
|
|
} else {
|
2023-12-12 15:15:15 +02:00
|
|
|
root.onSaveChangesCallback()
|
2023-12-11 17:36:03 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onDiscard: {
|
2023-12-12 15:15:15 +02:00
|
|
|
root.onSaveChangesCallback()
|
2023-12-11 17:36:03 +02:00
|
|
|
}
|
2023-12-08 14:58:04 +02:00
|
|
|
}
|
|
|
|
|
|
2023-06-29 15:06:11 +03:00
|
|
|
Column {
|
|
|
|
|
id: col
|
|
|
|
|
anchors.fill: parent
|
2023-08-16 12:57:41 +03:00
|
|
|
spacing: 1
|
2023-06-29 15:06:11 +03:00
|
|
|
|
2023-08-16 12:57:41 +03:00
|
|
|
EffectMakerTopBar {
|
2023-12-08 14:58:04 +02:00
|
|
|
onAddClicked: {
|
2024-01-17 19:36:03 +02:00
|
|
|
root.onSaveChangesCallback = () => { EffectMakerBackend.effectMakerModel.clear(true) }
|
2023-12-12 15:15:15 +02:00
|
|
|
|
2023-12-08 14:58:04 +02:00
|
|
|
if (EffectMakerBackend.effectMakerModel.hasUnsavedChanges)
|
|
|
|
|
saveChangesDialog.open()
|
|
|
|
|
else
|
2024-01-17 19:36:03 +02:00
|
|
|
EffectMakerBackend.effectMakerModel.clear(true)
|
2023-12-08 14:58:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onSaveClicked: {
|
|
|
|
|
let name = EffectMakerBackend.effectMakerModel.currentComposition
|
|
|
|
|
|
|
|
|
|
if (name === "")
|
2023-12-13 12:11:06 +02:00
|
|
|
saveAsDialog.open()
|
2023-12-08 14:58:04 +02:00
|
|
|
else
|
|
|
|
|
EffectMakerBackend.effectMakerModel.saveComposition(name)
|
|
|
|
|
}
|
2023-12-13 12:11:06 +02:00
|
|
|
|
|
|
|
|
onSaveAsClicked: saveAsDialog.open()
|
2023-12-13 15:54:57 +02:00
|
|
|
|
|
|
|
|
onAssignToSelectedClicked: {
|
|
|
|
|
EffectMakerBackend.effectMakerModel.assignToSelected()
|
|
|
|
|
}
|
2023-08-16 12:57:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EffectMakerPreview {
|
2023-09-19 14:10:39 +03:00
|
|
|
mainRoot: root
|
2023-10-19 16:39:21 +03:00
|
|
|
|
|
|
|
|
FrameAnimation {
|
|
|
|
|
id: previewFrameTimer
|
|
|
|
|
running: true
|
2023-11-06 14:41:31 +02:00
|
|
|
paused: !previewAnimationRunning
|
2023-10-19 16:39:21 +03:00
|
|
|
}
|
2023-06-29 15:06:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Rectangle {
|
|
|
|
|
width: parent.width
|
|
|
|
|
height: StudioTheme.Values.toolbarHeight
|
|
|
|
|
color: StudioTheme.Values.themeToolbarBackground
|
|
|
|
|
|
2023-08-15 15:40:46 +03:00
|
|
|
EffectNodesComboBox {
|
|
|
|
|
mainRoot: root
|
2023-08-16 12:57:41 +03:00
|
|
|
|
|
|
|
|
anchors.verticalCenter: parent.verticalCenter
|
2023-11-21 12:43:49 +02:00
|
|
|
x: 5
|
|
|
|
|
width: parent.width - 50
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HelperWidgets.AbstractButton {
|
|
|
|
|
anchors.right: parent.right
|
|
|
|
|
anchors.rightMargin: 5
|
|
|
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
|
|
|
|
|
|
|
|
style: StudioTheme.Values.viewBarButtonStyle
|
|
|
|
|
buttonIcon: StudioTheme.Constants.clearList_medium
|
|
|
|
|
tooltip: qsTr("Remove all effect nodes.")
|
|
|
|
|
enabled: !EffectMakerBackend.effectMakerModel.isEmpty
|
|
|
|
|
|
2023-11-21 15:01:00 +02:00
|
|
|
onClicked: EffectMakerBackend.effectMakerModel.clear()
|
2023-08-09 18:15:56 +03:00
|
|
|
}
|
2023-06-29 15:06:11 +03:00
|
|
|
|
2023-08-16 12:57:41 +03:00
|
|
|
HelperWidgets.AbstractButton {
|
|
|
|
|
anchors.right: parent.right
|
|
|
|
|
anchors.rightMargin: 5
|
|
|
|
|
anchors.verticalCenter: parent.verticalCenter
|
2023-06-29 15:06:11 +03:00
|
|
|
|
2023-08-16 12:57:41 +03:00
|
|
|
style: StudioTheme.Values.viewBarButtonStyle
|
|
|
|
|
buttonIcon: StudioTheme.Constants.code
|
2023-11-21 12:43:49 +02:00
|
|
|
tooltip: qsTr("Open Shader in Code Editor.")
|
2023-11-08 16:03:23 +02:00
|
|
|
visible: false // TODO: to be implemented
|
2023-08-16 12:57:41 +03:00
|
|
|
|
|
|
|
|
onClicked: {} // TODO
|
|
|
|
|
}
|
2023-06-29 15:06:11 +03:00
|
|
|
}
|
|
|
|
|
|
2023-10-30 11:39:57 +02:00
|
|
|
Component.onCompleted: HelperWidgets.Controller.mainScrollView = scrollView
|
|
|
|
|
|
2023-06-29 15:06:11 +03:00
|
|
|
HelperWidgets.ScrollView {
|
|
|
|
|
id: scrollView
|
|
|
|
|
|
|
|
|
|
width: parent.width
|
|
|
|
|
height: parent.height - y
|
|
|
|
|
clip: true
|
2024-01-19 12:52:53 +02:00
|
|
|
interactive: !HelperWidgets.Controller.contextMenuOpened
|
2023-06-29 15:06:11 +03:00
|
|
|
|
2024-01-19 14:50:22 +02:00
|
|
|
onContentHeightChanged: {
|
|
|
|
|
if (scrollView.contentItem.height > scrollView.height) {
|
|
|
|
|
let lastItemH = repeater.itemAt(repeater.count - 1).height
|
|
|
|
|
scrollView.contentY = scrollView.contentItem.height - lastItemH
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-29 15:06:11 +03:00
|
|
|
Column {
|
2024-01-19 17:41:03 +02:00
|
|
|
id: nodesCol
|
2023-08-17 15:59:46 +03:00
|
|
|
width: scrollView.width
|
|
|
|
|
spacing: 1
|
|
|
|
|
|
|
|
|
|
Repeater {
|
2023-09-04 14:17:01 +03:00
|
|
|
id: repeater
|
2023-08-17 15:59:46 +03:00
|
|
|
|
|
|
|
|
width: root.width
|
|
|
|
|
model: EffectMakerBackend.effectMakerModel
|
|
|
|
|
|
2023-09-06 15:18:02 +03:00
|
|
|
onCountChanged: {
|
|
|
|
|
HelperWidgets.Controller.setCount("EffectMaker", repeater.count)
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-17 15:59:46 +03:00
|
|
|
delegate: EffectCompositionNode {
|
|
|
|
|
width: root.width
|
2023-11-20 17:38:25 +02:00
|
|
|
modelIndex: index
|
2023-09-04 14:17:01 +03:00
|
|
|
|
|
|
|
|
Behavior on y {
|
|
|
|
|
PropertyAnimation {
|
|
|
|
|
duration: 300
|
|
|
|
|
easing.type: Easing.InOutQuad
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onStartDrag: (section) => {
|
|
|
|
|
root.draggedSec = section
|
|
|
|
|
root.moveFromIdx = index
|
|
|
|
|
|
|
|
|
|
highlightBorder = true
|
|
|
|
|
|
|
|
|
|
root.secsY = []
|
|
|
|
|
for (let i = 0; i < repeater.count; ++i)
|
|
|
|
|
root.secsY[i] = repeater.itemAt(i).y
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onStopDrag: {
|
|
|
|
|
if (root.moveFromIdx === root.moveToIdx)
|
|
|
|
|
root.draggedSec.y = root.secsY[root.moveFromIdx]
|
|
|
|
|
else
|
|
|
|
|
EffectMakerBackend.effectMakerModel.moveNode(root.moveFromIdx, root.moveToIdx)
|
|
|
|
|
|
|
|
|
|
highlightBorder = false
|
|
|
|
|
root.draggedSec = null
|
|
|
|
|
}
|
2023-06-29 15:06:11 +03:00
|
|
|
}
|
2023-09-04 14:17:01 +03:00
|
|
|
} // Repeater
|
|
|
|
|
|
|
|
|
|
Timer {
|
|
|
|
|
running: root.draggedSec
|
|
|
|
|
interval: 50
|
|
|
|
|
repeat: true
|
|
|
|
|
|
|
|
|
|
onTriggered: {
|
|
|
|
|
root.moveToIdx = root.moveFromIdx
|
|
|
|
|
for (let i = 0; i < repeater.count; ++i) {
|
|
|
|
|
let currItem = repeater.itemAt(i)
|
|
|
|
|
if (i > root.moveFromIdx) {
|
|
|
|
|
if (root.draggedSec.y > currItem.y + (currItem.height - root.draggedSec.height) * .5) {
|
2024-01-19 17:41:03 +02:00
|
|
|
currItem.y = root.secsY[i] - root.draggedSec.height - nodesCol.spacing
|
2023-09-04 14:17:01 +03:00
|
|
|
root.moveToIdx = i
|
|
|
|
|
} else {
|
|
|
|
|
currItem.y = root.secsY[i]
|
|
|
|
|
}
|
|
|
|
|
} else if (i < root.moveFromIdx) {
|
2023-12-07 18:32:55 +02:00
|
|
|
if (!repeater.model.isDependencyNode(i)
|
|
|
|
|
&& root.draggedSec.y < currItem.y + (currItem.height - root.draggedSec.height) * .5) {
|
2024-01-19 17:41:03 +02:00
|
|
|
currItem.y = root.secsY[i] + root.draggedSec.height + nodesCol.spacing
|
2023-09-04 14:17:01 +03:00
|
|
|
root.moveToIdx = Math.min(root.moveToIdx, i)
|
|
|
|
|
} else {
|
|
|
|
|
currItem.y = root.secsY[i]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} // Timer
|
|
|
|
|
} // Column
|
|
|
|
|
} // ScrollView
|
2023-06-29 15:06:11 +03:00
|
|
|
}
|
2023-09-08 11:07:10 +03:00
|
|
|
|
|
|
|
|
Text {
|
|
|
|
|
id: emptyText
|
|
|
|
|
|
|
|
|
|
text: qsTr("Add an effect node to start")
|
|
|
|
|
color: StudioTheme.Values.themeTextColor
|
|
|
|
|
font.pixelSize: StudioTheme.Values.baseFontSize
|
|
|
|
|
|
|
|
|
|
x: scrollView.x + (scrollView.width - emptyText.width) * .5
|
|
|
|
|
y: scrollView.y + scrollView.height * .5
|
|
|
|
|
|
|
|
|
|
visible: EffectMakerBackend.effectMakerModel.isEmpty
|
|
|
|
|
}
|
2023-06-29 15:06:11 +03:00
|
|
|
}
|