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
|
2024-01-23 18:57:01 +02:00
|
|
|
import QtQuick.Controls
|
|
|
|
|
import QtQuick.Layouts
|
2023-06-29 15:06:11 +03:00
|
|
|
import HelperWidgets as HelperWidgets
|
|
|
|
|
import StudioControls as StudioControls
|
|
|
|
|
import StudioTheme as StudioTheme
|
2024-01-26 14:55:50 +02:00
|
|
|
import EffectComposerBackend
|
2023-06-29 15:06:11 +03:00
|
|
|
|
|
|
|
|
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() {
|
2024-01-26 14:55:50 +02:00
|
|
|
root.onSaveChangesCallback = () => { EffectComposerBackend.rootView.doOpenComposition() }
|
2023-12-12 15:15:15 +02:00
|
|
|
|
|
|
|
|
saveChangesDialog.open()
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-13 12:11:06 +02:00
|
|
|
Connections {
|
2024-01-26 14:55:50 +02:00
|
|
|
target: EffectComposerBackend.effectComposerModel
|
2023-12-13 12:11:06 +02:00
|
|
|
function onIsEmptyChanged() {
|
2024-01-26 14:55:50 +02:00
|
|
|
if (EffectComposerBackend.effectComposerModel.isEmpty)
|
2023-12-13 12:11:06 +02:00
|
|
|
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: {
|
2024-01-26 14:55:50 +02:00
|
|
|
if (EffectComposerBackend.effectComposerModel.currentComposition === "") {
|
2023-12-11 17:36:03 +02:00
|
|
|
// 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
|
|
|
}
|
|
|
|
|
|
2024-01-23 18:57:01 +02:00
|
|
|
SplitView {
|
2023-06-29 15:06:11 +03:00
|
|
|
anchors.fill: parent
|
2024-01-23 18:57:01 +02:00
|
|
|
orientation: Qt.Vertical
|
2023-06-29 15:06:11 +03:00
|
|
|
|
2024-01-23 18:57:01 +02:00
|
|
|
ColumnLayout {
|
|
|
|
|
spacing: 1
|
|
|
|
|
SplitView.minimumHeight: 200
|
|
|
|
|
SplitView.preferredHeight: 300
|
2023-12-12 15:15:15 +02:00
|
|
|
|
2024-01-26 14:55:50 +02:00
|
|
|
EffectComposerTopBar {
|
2024-01-25 11:41:51 +02:00
|
|
|
Layout.fillWidth: true
|
|
|
|
|
|
2024-01-23 18:57:01 +02:00
|
|
|
onAddClicked: {
|
2024-01-26 14:55:50 +02:00
|
|
|
root.onSaveChangesCallback = () => { EffectComposerBackend.effectComposerModel.clear(true) }
|
2023-12-08 14:58:04 +02:00
|
|
|
|
2024-01-26 14:55:50 +02:00
|
|
|
if (EffectComposerBackend.effectComposerModel.hasUnsavedChanges)
|
2024-01-23 18:57:01 +02:00
|
|
|
saveChangesDialog.open()
|
|
|
|
|
else
|
2024-01-26 14:55:50 +02:00
|
|
|
EffectComposerBackend.effectComposerModel.clear(true)
|
2024-01-23 18:57:01 +02:00
|
|
|
}
|
2023-12-08 14:58:04 +02:00
|
|
|
|
2024-01-23 18:57:01 +02:00
|
|
|
onSaveClicked: {
|
2024-01-26 14:55:50 +02:00
|
|
|
let name = EffectComposerBackend.effectComposerModel.currentComposition
|
2024-01-23 18:57:01 +02:00
|
|
|
|
|
|
|
|
if (name === "")
|
|
|
|
|
saveAsDialog.open()
|
|
|
|
|
else
|
2024-01-26 14:55:50 +02:00
|
|
|
EffectComposerBackend.effectComposerModel.saveComposition(name)
|
2024-01-23 18:57:01 +02:00
|
|
|
}
|
2023-12-13 12:11:06 +02:00
|
|
|
|
2024-01-23 18:57:01 +02:00
|
|
|
onSaveAsClicked: saveAsDialog.open()
|
2023-12-13 15:54:57 +02:00
|
|
|
|
2024-01-23 18:57:01 +02:00
|
|
|
onAssignToSelectedClicked: {
|
2024-01-26 14:55:50 +02:00
|
|
|
EffectComposerBackend.effectComposerModel.assignToSelected()
|
2024-01-23 18:57:01 +02:00
|
|
|
}
|
2023-12-13 15:54:57 +02:00
|
|
|
}
|
2023-08-16 12:57:41 +03:00
|
|
|
|
2024-01-26 14:55:50 +02:00
|
|
|
EffectComposerPreview {
|
2024-01-23 18:57:01 +02:00
|
|
|
mainRoot: root
|
|
|
|
|
|
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
Layout.fillHeight: true
|
2023-10-19 16:39:21 +03:00
|
|
|
|
2024-01-23 18:57:01 +02:00
|
|
|
FrameAnimation {
|
|
|
|
|
id: previewFrameTimer
|
|
|
|
|
running: true
|
|
|
|
|
paused: !previewAnimationRunning
|
|
|
|
|
}
|
2023-10-19 16:39:21 +03:00
|
|
|
}
|
2023-06-29 15:06:11 +03:00
|
|
|
}
|
|
|
|
|
|
2024-01-23 18:57:01 +02:00
|
|
|
Column {
|
|
|
|
|
id: lowerSplitCol
|
2023-08-16 12:57:41 +03:00
|
|
|
|
2024-01-23 18:57:01 +02:00
|
|
|
spacing: 1
|
2023-11-21 12:43:49 +02:00
|
|
|
|
2024-01-23 18:57:01 +02:00
|
|
|
SplitView.minimumHeight: 200
|
2023-11-21 12:43:49 +02:00
|
|
|
|
2024-01-23 18:57:01 +02:00
|
|
|
Rectangle {
|
|
|
|
|
width: parent.width
|
|
|
|
|
height: StudioTheme.Values.toolbarHeight
|
|
|
|
|
color: StudioTheme.Values.themeToolbarBackground
|
2023-11-21 12:43:49 +02:00
|
|
|
|
2024-01-23 18:57:01 +02:00
|
|
|
EffectNodesComboBox {
|
|
|
|
|
mainRoot: root
|
2023-06-29 15:06:11 +03:00
|
|
|
|
2024-01-23 18:57:01 +02:00
|
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
|
|
|
x: 5
|
|
|
|
|
width: parent.width - 50
|
|
|
|
|
}
|
2023-06-29 15:06:11 +03:00
|
|
|
|
2024-01-23 18:57:01 +02:00
|
|
|
HelperWidgets.AbstractButton {
|
|
|
|
|
anchors.right: parent.right
|
|
|
|
|
anchors.rightMargin: 5
|
|
|
|
|
anchors.verticalCenter: parent.verticalCenter
|
2023-08-16 12:57:41 +03:00
|
|
|
|
2024-01-23 18:57:01 +02:00
|
|
|
style: StudioTheme.Values.viewBarButtonStyle
|
|
|
|
|
buttonIcon: StudioTheme.Constants.clearList_medium
|
|
|
|
|
tooltip: qsTr("Remove all effect nodes.")
|
2024-01-26 14:55:50 +02:00
|
|
|
enabled: !EffectComposerBackend.effectComposerModel.isEmpty
|
2023-06-29 15:06:11 +03:00
|
|
|
|
2024-01-26 14:55:50 +02:00
|
|
|
onClicked: EffectComposerBackend.effectComposerModel.clear()
|
2024-01-23 18:57:01 +02:00
|
|
|
}
|
2023-10-30 11:39:57 +02:00
|
|
|
|
2024-01-23 18:57:01 +02:00
|
|
|
HelperWidgets.AbstractButton {
|
|
|
|
|
anchors.right: parent.right
|
|
|
|
|
anchors.rightMargin: 5
|
|
|
|
|
anchors.verticalCenter: parent.verticalCenter
|
2023-06-29 15:06:11 +03:00
|
|
|
|
2024-01-23 18:57:01 +02:00
|
|
|
style: StudioTheme.Values.viewBarButtonStyle
|
|
|
|
|
buttonIcon: StudioTheme.Constants.code
|
|
|
|
|
tooltip: qsTr("Open Shader in Code Editor.")
|
|
|
|
|
visible: false // TODO: to be implemented
|
2023-06-29 15:06:11 +03:00
|
|
|
|
2024-01-23 18:57:01 +02:00
|
|
|
onClicked: {} // TODO
|
2024-01-19 14:50:22 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-23 18:57:01 +02:00
|
|
|
Component.onCompleted: HelperWidgets.Controller.mainScrollView = scrollView
|
2023-08-17 15:59:46 +03:00
|
|
|
|
2024-01-23 18:57:01 +02:00
|
|
|
HelperWidgets.ScrollView {
|
|
|
|
|
id: scrollView
|
2023-08-17 15:59:46 +03:00
|
|
|
|
2024-01-23 18:57:01 +02:00
|
|
|
width: parent.width
|
|
|
|
|
height: parent.height - y
|
|
|
|
|
clip: true
|
|
|
|
|
interactive: !HelperWidgets.Controller.contextMenuOpened
|
2023-08-17 15:59:46 +03:00
|
|
|
|
2024-01-23 18:57:01 +02:00
|
|
|
onContentHeightChanged: {
|
|
|
|
|
if (scrollView.contentItem.height > scrollView.height) {
|
|
|
|
|
let lastItemH = repeater.itemAt(repeater.count - 1).height
|
|
|
|
|
scrollView.contentY = scrollView.contentItem.height - lastItemH
|
2023-09-06 15:18:02 +03:00
|
|
|
}
|
2024-01-23 18:57:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Column {
|
|
|
|
|
id: nodesCol
|
|
|
|
|
width: scrollView.width
|
|
|
|
|
spacing: 1
|
|
|
|
|
|
|
|
|
|
Repeater {
|
|
|
|
|
id: repeater
|
2023-09-06 15:18:02 +03:00
|
|
|
|
2023-08-17 15:59:46 +03:00
|
|
|
width: root.width
|
2024-01-26 14:55:50 +02:00
|
|
|
model: EffectComposerBackend.effectComposerModel
|
2023-09-04 14:17:01 +03:00
|
|
|
|
2024-01-23 18:57:01 +02:00
|
|
|
onCountChanged: {
|
2024-01-26 14:55:50 +02:00
|
|
|
HelperWidgets.Controller.setCount("EffectComposer", repeater.count)
|
2023-09-04 14:17:01 +03:00
|
|
|
}
|
|
|
|
|
|
2024-01-23 18:57:01 +02:00
|
|
|
delegate: EffectCompositionNode {
|
|
|
|
|
width: root.width
|
|
|
|
|
modelIndex: index
|
2023-09-04 14:17:01 +03:00
|
|
|
|
2024-01-23 18:57:01 +02:00
|
|
|
Behavior on y {
|
|
|
|
|
PropertyAnimation {
|
|
|
|
|
duration: 300
|
|
|
|
|
easing.type: Easing.InOutQuad
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-09-04 14:17:01 +03:00
|
|
|
|
2024-01-23 18:57:01 +02:00
|
|
|
onStartDrag: (section) => {
|
|
|
|
|
root.draggedSec = section
|
|
|
|
|
root.moveFromIdx = index
|
2023-09-04 14:17:01 +03:00
|
|
|
|
2024-01-23 18:57:01 +02:00
|
|
|
highlightBorder = true
|
2023-09-04 14:17:01 +03:00
|
|
|
|
2024-01-23 18:57:01 +02:00
|
|
|
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
|
2024-01-26 14:55:50 +02:00
|
|
|
EffectComposerBackend.effectComposerModel.moveNode(root.moveFromIdx, root.moveToIdx)
|
2024-01-23 18:57:01 +02:00
|
|
|
|
|
|
|
|
highlightBorder = false
|
|
|
|
|
root.draggedSec = null
|
|
|
|
|
}
|
2023-09-04 14:17:01 +03:00
|
|
|
}
|
2024-01-23 18:57:01 +02: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) {
|
|
|
|
|
currItem.y = root.secsY[i] - root.draggedSec.height - nodesCol.spacing
|
|
|
|
|
root.moveToIdx = i
|
|
|
|
|
} else {
|
|
|
|
|
currItem.y = root.secsY[i]
|
|
|
|
|
}
|
|
|
|
|
} else if (i < root.moveFromIdx) {
|
|
|
|
|
if (!repeater.model.isDependencyNode(i)
|
|
|
|
|
&& root.draggedSec.y < currItem.y + (currItem.height - root.draggedSec.height) * .5) {
|
|
|
|
|
currItem.y = root.secsY[i] + root.draggedSec.height + nodesCol.spacing
|
|
|
|
|
root.moveToIdx = Math.min(root.moveToIdx, i)
|
|
|
|
|
} else {
|
|
|
|
|
currItem.y = root.secsY[i]
|
|
|
|
|
}
|
2023-09-04 14:17:01 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-01-23 18:57:01 +02:00
|
|
|
} // Timer
|
|
|
|
|
} // Column
|
|
|
|
|
} // ScrollView
|
|
|
|
|
}
|
|
|
|
|
} // SplitView
|
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
|
2024-01-23 18:57:01 +02:00
|
|
|
y: lowerSplitCol.y + lowerSplitCol.height * .5
|
2023-09-08 11:07:10 +03:00
|
|
|
|
2024-01-26 14:55:50 +02:00
|
|
|
visible: EffectComposerBackend.effectComposerModel.isEmpty
|
2023-09-08 11:07:10 +03:00
|
|
|
}
|
2023-06-29 15:06:11 +03:00
|
|
|
}
|