Revision control

++ // Copyright (C) 2026 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
++
++ import QtQuick
++ import QtQuick.Layouts
++ import QtQuick.Controls
++ import QtQuick.Dialogs
++ import Qt.labs.lottieqt
++ import QtQuick.VectorImage
++
++ ColumnLayout {
++ id: top
++ spacing: 10
++ property string source: ""
++
++ Rectangle {
++ id: canvas
++ color: "lightgray"
++ border.color: "black"
++ border.width: 2
++ radius: 10
++ Layout.fillWidth: true
++ Layout.fillHeight: true
++ clip: true
++
++ Rectangle {
++ id: vectorImgBackground
++ anchors.centerIn: parent
++ width: vectorImg.implicitWidth
++ height: vectorImg.implicitHeight
++ color: "#F0F0F0"
++
++ VectorImage {
++ id: vectorImg
++ property LottieVectorImageController controller: LottieVectorImageController {}
++ source: top.source
++ assumeTrustedSource: true
++ anchors.centerIn: parent
++ preferredRendererType: VectorImage.CurveRenderer
++ animations.loops: Animation.Infinite
++ asynchronous: true
++ asynchronousShapes: true
++
++ onImplicitHeightChanged: {
++ if (implicitWidth > 0 && implicitHeight > 0) {
++ let newScale = Math.min(canvas.width / (implicitWidth * 1.2),
++ canvas.height / (implicitHeight * 1.2))
++ scaleSlider.from = newScale * 10
++ scaleSlider.to = newScale * 400
++ scaleSlider.value = newScale * 100
++ }
++ }
++ }
++ }
++
++ BusyIndicator {
++ anchors.centerIn: parent
++ visible: running
++ running: vectorImg.status === VectorImage.Loading
++ }
++ }
++
++ RowLayout {
++ id: controlRow
++ spacing: top.spacing
++
++ Button {
++ id: openFileButton
++ text: "Open..."
++ onClicked: {
++ fileDialog.visible = true
++ }
++ Layout.preferredHeight: aGroupBox.height
++ }
++
++ Button {
++ id: playButton
++ enabled: (vectorImg.status === VectorImage.Ready)
++ checkable: true
++ text: checked ? "Play" : "Pause"
++ onCheckedChanged: { vectorImg.controller.togglePause() }
++ Layout.preferredHeight: aGroupBox.height
++ }
++
++ GroupBox {
++ id: frameBox
++ title: "Current Frame: " + vectorImg.controller.currentFrame.toFixed(1)
++ enabled: playButton.checked && (vectorImg.status === VectorImage.Ready)
++ contentItem: RowLayout {
++ Text { text: vectorImg.controller.startFrame }
++ Slider {
++ id: gotoSlider
++ from: vectorImg.controller.startFrame
++ to: vectorImg.controller.endFrame
++ stepSize: 1
++ value: 0
++ onValueChanged: { if (frameBox.enabled) vectorImg.controller.gotoAndStop(value) }
++ Binding on value {
++ when: !frameBox.enabled
++ value: vectorImg.controller.currentFrame
++ restoreMode: Binding.RestoreNone
++ }
++ }
++ Text { text: vectorImg.controller.endFrame }
++ }
++ }
++
++ GroupBox {
++ id: aGroupBox
++ title: "Frame Rate"
++ contentItem: RowLayout {
++ Slider {
++ enabled: (vectorImg.status === VectorImage.Ready)
++ from: 1
++ to: 100
++ stepSize: 1
++ value: vectorImg.controller.frameRate
++ onValueChanged: { vectorImg.controller.frameRate = value }
++ }
++ Text { text: vectorImg.controller.frameRate }
++ }
++ }
++
++ GroupBox {
++ title: "Scale"
++ contentItem: RowLayout {
++ Slider {
++ id: scaleSlider
++ enabled: (vectorImg.status === VectorImage.Ready)
++ from: 1
++ to: 200
++ stepSize: 1
++ value: 100
++ onValueChanged: { vectorImgBackground.scale = value / 100 }
++ }
++ Text { text: vectorImgBackground.scale.toFixed(2) }
++ }
++ }
++
++ GroupBox {
++ title: "Markers"
++ enabled: vectorImg.controller.markers.length > 0 && vectorImg.status === VectorImage.Ready
++ contentItem: ComboBox {
++ id: markerCombo
++ model: vectorImg.controller.markers
++ onActivated: {
++ if (playButton.checked)
++ vectorImg.controller.gotoAndStop(currentText);
++ else
++ vectorImg.controller.gotoAndPlay(currentText);
++ }
++ }
++ }
++
++ GroupBox {
++ title: "Status"
++ contentItem: Label {
++ text: (vectorImg.status === VectorImage.Null) ? "Null" :
++ (vectorImg.status === VectorImage.Loading) ? "Loading" :
++ (vectorImg.status === VectorImage.Ready) ? "Ready" :
++ (vectorImg.status === VectorImage.Error) ? "Error" : "Unknown"
++ color: (vectorImg.status === VectorImage.Ready) ? "green" :
++ (vectorImg.status === VectorImage.Error) ? "red" : "yellow"
++ }
++ }
++ }
++ }