Files
qt-creator/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml
Tim Jenssen 90c4826c29 QmlDesigner: remove annoying output
Change-Id: I7f74ece071923208764b767a70436145888fbb54
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Qt CI Patch Build Bot <ci_patchbuild_bot@qt.io>
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
2024-04-18 06:20:46 +00:00

399 lines
13 KiB
QML

// 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 EffectComposerBackend
Column {
id: root
property real animatedTime: previewFrameTimer.elapsedTime
property int animatedFrame: previewFrameTimer.currentFrame
property bool timeRunning: previewAnimationRunning
required property Item mainRoot
property var effectComposerModel: EffectComposerBackend.effectComposerModel
property alias source: source
// The delay in ms to wait until updating the effect
readonly property int updateDelay: 100
readonly property int previewMargin: 5
readonly property int extraMargin: 200
property real previewScale: 1
// Create a dummy parent to host the effect qml object
function createNewComponent() {
// If we have a working effect, do not show preview image as it shows through
// transparent parts of the final image
placeHolder.visible = false;
var oldComponent = componentParent.children[0];
if (oldComponent)
oldComponent.destroy();
try {
const newObject = Qt.createQmlObject(
effectComposerModel.qmlComponentString(),
componentParent,
""
);
effectComposerModel.resetEffectError(0);
} catch (error) {
let errorString = "QML: ERROR: ";
let errorLine = -1;
if (error.qmlErrors.length > 0) {
// Show the first QML error
let e = error.qmlErrors[0];
errorString += e.lineNumber + ": " + e.message;
errorLine = e.lineNumber;
}
effectComposerModel.setEffectError(errorString, 0, errorLine);
placeHolder.visible = true;
}
}
Rectangle { // toolbar
width: parent.width
height: StudioTheme.Values.toolbarHeight
color: StudioTheme.Values.themeToolbarBackground
Row {
spacing: 5
anchors.leftMargin: 5
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
PreviewImagesComboBox {
id: imagesComboBox
mainRoot: root.mainRoot
}
StudioControls.ColorEditor {
id: colorEditor
actionIndicatorVisible: false
showHexTextField: false
color: "#dddddd"
}
}
Row {
spacing: 5
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
HelperWidgets.AbstractButton {
enabled: root.previewScale < 3
style: StudioTheme.Values.viewBarButtonStyle
buttonIcon: StudioTheme.Constants.zoomIn_medium
tooltip: qsTr("Zoom In")
onClicked: {
imageScaler.enableAnim(true)
root.previewScale += .2
imageScaler.enableAnim(false)
zoomIndicator.show()
}
}
HelperWidgets.AbstractButton {
enabled: root.previewScale > .4
style: StudioTheme.Values.viewBarButtonStyle
buttonIcon: StudioTheme.Constants.zoomOut_medium
tooltip: qsTr("Zoom out")
onClicked: {
imageScaler.enableAnim(true)
root.previewScale -= .2
imageScaler.enableAnim(false)
zoomIndicator.show()
}
}
HelperWidgets.AbstractButton {
enabled: root.previewScale !== 1 || imageScaler.x !== root.previewMargin
|| imageScaler.y !== root.previewMargin
style: StudioTheme.Values.viewBarButtonStyle
buttonIcon: StudioTheme.Constants.fitAll_medium
tooltip: qsTr("Reset View")
onClicked: {
imageScaler.resetTransforms()
}
}
}
Row {
spacing: 5
anchors.rightMargin: 5
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
Column {
Text {
text: animatedTime >= 100
? animatedTime.toFixed(1) + " s" : animatedTime.toFixed(3) + " s"
color: StudioTheme.Values.themeTextColor
font.pixelSize: 10
}
Text {
text: (animatedFrame).toString().padStart(6, '0')
color: StudioTheme.Values.themeTextColor
font.pixelSize: 10
}
}
HelperWidgets.AbstractButton {
style: StudioTheme.Values.viewBarButtonStyle
buttonIcon: StudioTheme.Constants.toStartFrame_medium
tooltip: qsTr("Restart Animation")
onClicked: {
previewFrameTimer.reset()
}
}
HelperWidgets.AbstractButton {
style: StudioTheme.Values.viewBarButtonStyle
buttonIcon: previewAnimationRunning ? StudioTheme.Constants.pause_medium
: StudioTheme.Constants.playOutline_medium
tooltip: qsTr("Play Animation")
onClicked: {
previewAnimationRunning = !previewAnimationRunning
}
}
}
}
Rectangle { // preview image
id: preview
color: colorEditor.color
width: parent.width
height: root.height - y
clip: true
MouseArea {
id: mouseArea
anchors.fill: parent
acceptedButtons: Qt.LeftButton
property real pressX: 0
property real pressY: 0
property bool panning: false
onPressed: {
pressX = mouseX - imageScaler.x
pressY = mouseY - imageScaler.y
panning = true
}
onReleased: {
panning = false
}
onWheel: (wheel) => {
let oldPoint = imageScaler.mapFromItem(mouseArea, Qt.point(wheel.x, wheel.y))
if (wheel.angleDelta.y > 0) {
if (root.previewScale < 3)
root.previewScale += .2
} else {
if (root.previewScale > .4)
root.previewScale -= .2
}
let newPoint = imageScaler.mapFromItem(mouseArea, Qt.point(wheel.x, wheel.y))
imageScaler.x -= (oldPoint.x - newPoint.x) * imageScaler.scale
imageScaler.y -= (oldPoint.y - newPoint.y) * imageScaler.scale
imageScaler.checkBounds()
zoomIndicator.show()
}
Timer { // pan timer
running: parent.panning
interval: 16
repeat: true
onTriggered: {
imageScaler.x = mouseArea.mouseX - mouseArea.pressX
imageScaler.y = mouseArea.mouseY - mouseArea.pressY
imageScaler.checkBounds()
}
}
}
Image {
id: placeHolder
anchors.fill: parent
anchors.margins: root.previewMargin
fillMode: Image.PreserveAspectFit
source: imagesComboBox.selectedImage
smooth: true
}
Item { // Source item as a canvas (render target) for effect
id: source
width: sourceImage.sourceSize.width
height: sourceImage.sourceSize.height
layer.enabled: true
layer.mipmap: true
layer.smooth: true
layer.sourceRect: Qt.rect(-root.extraMargin, -root.extraMargin,
width + root.extraMargin * 2, height + root.extraMargin * 2)
visible: false
Image {
id: sourceImage
onSourceChanged: imageScaler.resetTransforms()
fillMode: Image.Pad
source: imagesComboBox.selectedImage
smooth: true
}
}
BlurHelper {
id: blurHelper
source: source
property int blurMax: g_propertyData.blur_helper_max_level ? g_propertyData.blur_helper_max_level : 64
property real blurMultiplier: g_propertyData.blurMultiplier ? g_propertyData.blurMultiplier : 0
}
Item {
id: imageScaler
x: root.previewMargin
y: root.previewMargin
width: parent.width - root.previewMargin * 2
height: parent.height - root.previewMargin * 2
scale: root.previewScale * (width > height ? height / sourceImage.sourceSize.height
: width / sourceImage.sourceSize.width)
Behavior on x {
id: xBehavior
enabled: false
NumberAnimation {
duration: 200
easing.type: Easing.OutQuad
}
}
Behavior on y {
id: yBehavior
enabled: false
NumberAnimation {
duration: 200
easing.type: Easing.OutQuad
}
}
Behavior on scale {
id: scaleBehavior
enabled: false
NumberAnimation {
duration: 200
easing.type: Easing.OutQuad
}
}
function checkBounds() {
let edgeMargin = 10
// correction factor to account for an observation that edgeMargin decreases
// with increased zoom
let corrFactor = 10 * imageScaler.scale
let imgW2 = sourceImage.paintedWidth * imageScaler.scale * .5
let imgH2 = sourceImage.paintedHeight * imageScaler.scale * .5
let srcW2 = width * .5
let srcH2 = height * .5
if (imageScaler.x < -srcW2 - imgW2 + edgeMargin + corrFactor)
imageScaler.x = -srcW2 - imgW2 + edgeMargin + corrFactor
else if (x > srcW2 + imgW2 - edgeMargin - corrFactor)
imageScaler.x = srcW2 + imgW2 - edgeMargin - corrFactor
if (imageScaler.y < -srcH2 - imgH2 + edgeMargin + corrFactor)
imageScaler.y = -srcH2 - imgH2 + edgeMargin + corrFactor
else if (y > srcH2 + imgH2 - edgeMargin - corrFactor)
imageScaler.y = srcH2 + imgH2 - edgeMargin - corrFactor
}
function resetTransforms() {
imageScaler.enableAnim(true)
root.previewScale = 1
imageScaler.x = root.previewMargin
imageScaler.y = root.previewMargin
imageScaler.enableAnim(false)
}
function enableAnim(flag) {
xBehavior.enabled = flag
yBehavior.enabled = flag
scaleBehavior.enabled = flag
}
Item {
id: componentParent
width: source.width
height: source.height
anchors.centerIn: parent
}
}
Rectangle {
id: zoomIndicator
width: 40
height: 20
color: StudioTheme.Values.themeDialogBackground
visible: false
function show() {
zoomIndicator.visible = true
zoomIndicatorTimer.start()
}
Text {
text: Math.round(root.previewScale * 100) + "%"
color: StudioTheme.Values.themeTextColor
anchors.centerIn: parent
}
Timer {
id: zoomIndicatorTimer
interval: 1000
onTriggered: zoomIndicator.visible = false
}
}
Connections {
target: effectComposerModel
function onShadersBaked() {
updateTimer.restart()
}
}
Timer {
id: updateTimer
interval: updateDelay
onTriggered: {
effectComposerModel.updateQmlComponent()
createNewComponent()
}
}
}
}