From ef4bed61a24ad3b81e85a465949b7771b5102a54 Mon Sep 17 00:00:00 2001 From: Dan Dennedy Date: Tue, 24 Oct 2023 14:59:33 -0700 Subject: [PATCH] improve performance of timeline zoom --- src/docks/timelinedock.cpp | 2 ++ src/qml/views/timeline/Clip.qml | 9 ++++--- src/qml/views/timeline/Ruler.qml | 35 ++++++++++++++++++++-------- src/qml/views/timeline/TrackHead.qml | 6 ++--- src/qml/views/timeline/timeline.qml | 12 +++++----- 5 files changed, 40 insertions(+), 24 deletions(-) diff --git a/src/docks/timelinedock.cpp b/src/docks/timelinedock.cpp index 55a497d3cd..e0f71d8347 100644 --- a/src/docks/timelinedock.cpp +++ b/src/docks/timelinedock.cpp @@ -198,7 +198,9 @@ TimelineDock::TimelineDock(QWidget *parent) : }); connect(&m_model, &MultitrackModel::scaleFactorChanged, zoomSlider, [ = ]() { double value = round(pow(m_model.scaleFactor() - 0.01, 1.0 / 3.0) * 100.0); + zoomSlider->blockSignals(true); zoomSlider->setValue(value); + zoomSlider->blockSignals(false); }); toolbar->addWidget(zoomSlider); toolbar->addAction(Actions["timelineZoomInAction"]); diff --git a/src/qml/views/timeline/Clip.qml b/src/qml/views/timeline/Clip.qml index 965912db07..b4e2a70f87 100644 --- a/src/qml/views/timeline/Clip.qml +++ b/src/qml/views/timeline/Clip.qml @@ -73,7 +73,7 @@ Rectangle { // This is needed to make the model have the correct count. // Model as a property expression is not working in all cases. - waveformRepeater.model = Math.ceil(waveform.innerWidth / waveform.maxWidth); + waveformRepeater.model = Math.ceil(clipRoot.width / waveform.maxWidth); for (var i = 0; i < waveformRepeater.count; i++) waveformRepeater.itemAt(0).update(); } @@ -191,8 +191,7 @@ Rectangle { Row { id: waveform - property int maxWidth: Math.max(application.maxTextureSize / 2, 2048) - property int innerWidth: clipRoot.width - clipRoot.border.width * 2 + readonly property int maxWidth: Math.max(application.maxTextureSize / 2, 2048) visible: !elided && !isBlank && settings.timelineShowWaveforms && (parseInt(audioIndex) > -1 || audioIndex === 'all') height: (isAudio || parent.height <= 20) ? parent.height : parent.height / 2 @@ -204,7 +203,7 @@ Rectangle { Repeater { id: waveformRepeater - model: Math.ceil(waveform.innerWidth / waveform.maxWidth) + model: Math.ceil(clipRoot.width / waveform.maxWidth) Shotcut.TimelineWaveform { @@ -213,7 +212,7 @@ Rectangle { // bottom edge property int channels: 2 - width: Math.min(waveform.innerWidth, waveform.maxWidth) + width: Math.min(clipRoot.width, waveform.maxWidth) height: waveform.height fillColor: getColor() inPoint: Math.round((clipRoot.inPoint + index * waveform.maxWidth / timeScale) * speed) * channels diff --git a/src/qml/views/timeline/Ruler.qml b/src/qml/views/timeline/Ruler.qml index 8c88a425da..311829a286 100644 --- a/src/qml/views/timeline/Ruler.qml +++ b/src/qml/views/timeline/Ruler.qml @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2022 Meltytech, LLC + * Copyright (c) 2013-2023 Meltytech, LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,8 +22,7 @@ Rectangle { id: rulerTop property real timeScale: 1 - property int adjustment: 0 - property real intervalSeconds: ((timeScale > 5) ? 1 : (5 * Math.max(1, Math.floor(1.5 / timeScale)))) + adjustment + readonly property real intervalFrames: profile.fps * ((timeScale > 5) ? 1 : (5 * Math.max(1, Math.floor(1.5 / timeScale)))) signal editMarkerRequested(int index) signal deleteMarkerRequested(int index) @@ -31,12 +30,18 @@ Rectangle { height: 28 color: activePalette.base + Timer { + id: updateTimer + interval: 100 + onTriggered: repeater.model = Math.round(width / intervalFrames / timeScale) + } + SystemPalette { id: activePalette } Repeater { - model: parent.width / (intervalSeconds * profile.fps * timeScale) + id: repeater Rectangle { @@ -45,7 +50,7 @@ Rectangle { height: 18 width: 1 color: activePalette.windowText - x: index * intervalSeconds * profile.fps * timeScale + x: index * intervalFrames * timeScale visible: ((x + width) > tracksFlickable.contentX) && (x < tracksFlickable.contentX + tracksFlickable.width) // left edge Label { @@ -54,7 +59,7 @@ Rectangle { anchors.bottom: parent.bottom anchors.bottomMargin: 2 color: activePalette.windowText - text: application.timecode(index * intervalSeconds * profile.fps + 2).substr(0, 8) + text: application.timecode(index * intervalFrames + 2).substr(0, 8) } } } @@ -74,7 +79,7 @@ Rectangle { anchors.top: rulerTop.top anchors.left: parent.left anchors.right: parent.right - timeScale: root.timeScale ? root.timescale : 1 + timeScale: rulerTop.timeScale model: markers onExited: bubbleHelp.hide() onMouseStatusChanged: (mouseX, mouseY, text, start, end) => { @@ -124,11 +129,21 @@ Rectangle { Connections { function onProfileChanged() { - // Force a repeater model change to update the labels. - ++adjustment; - --adjustment; + updateTimer.restart(); } target: profile } + + Connections { + function onDurationChanged() { + updateTimer.restart(); + } + + function onScaleFactorChanged() { + updateTimer.restart(); + } + + target: multitrack + } } diff --git a/src/qml/views/timeline/TrackHead.qml b/src/qml/views/timeline/TrackHead.qml index cf2cc5fbf1..9ee1be89d8 100644 --- a/src/qml/views/timeline/TrackHead.qml +++ b/src/qml/views/timeline/TrackHead.qml @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2022 Meltytech, LLC + * Copyright (c) 2013-2023 Meltytech, LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -94,10 +94,10 @@ Rectangle { MouseArea { anchors.fill: parent acceptedButtons: Qt.LeftButton | Qt.RightButton - onClicked: { + onClicked: mouse => { parent.clicked(); nameEdit.focus = false; - if (mouse.button == Qt.RightButton) + if (mouse.button === Qt.RightButton) root.timelineRightClicked(); } } diff --git a/src/qml/views/timeline/timeline.qml b/src/qml/views/timeline/timeline.qml index bd2c60a049..bc4dcf64b9 100644 --- a/src/qml/views/timeline/timeline.qml +++ b/src/qml/views/timeline/timeline.qml @@ -41,21 +41,21 @@ Rectangle { function setZoom(value, targetX) { if (!targetX) targetX = tracksFlickable.contentX + tracksFlickable.width / 2; - var offset = targetX - tracksFlickable.contentX; - var before = multitrack.scaleFactor; + let offset = targetX - tracksFlickable.contentX; + let before = multitrack.scaleFactor; if (isNaN(value)) value = 0; multitrack.scaleFactor = Math.pow(Math.max(value, 0), 3) + 0.01; if (settings.timelineScrolling !== Shotcut.Settings.CenterPlayhead && !settings.timelineScrollZoom) tracksFlickable.contentX = (targetX * multitrack.scaleFactor / before) - offset; - for (var i = 0; i < tracksRepeater.count; i++) + for (let i = 0; i < tracksRepeater.count; i++) tracksRepeater.itemAt(i).redrawWaveforms(false); if (settings.timelineScrollZoom && settings.timelineScrolling !== Shotcut.Settings.CenterPlayhead) scrollZoomTimer.restart(); } function adjustZoom(by, targetX) { - var value = Math.pow(multitrack.scaleFactor - 0.01, 1 / 3); + let value = Math.pow(multitrack.scaleFactor - 0.01, 1 / 3); setZoom(value + by, targetX); } @@ -64,7 +64,7 @@ Rectangle { } function selectMultitrack() { - for (var i = 0; i < trackHeaderRepeater.count; i++) + for (let i = 0; i < trackHeaderRepeater.count; i++) trackHeaderRepeater.itemAt(i).selected = false; cornerstone.selected = true; } @@ -399,7 +399,7 @@ Rectangle { timeline.position = (tracksFlickable.contentX + mouse.x) / multitrack.scaleFactor; bubbleHelp.hide(); } - onWheel: Logic.onMouseWheel(wheel) + onWheel: wheel => Logic.onMouseWheel(wheel) onReleased: skim = false onExited: skim = false onPositionChanged: mouse => {