Commit 164e8bef authored by Julius Künzel's avatar Julius Künzel
Browse files

Referactoring: Move ZoomBar to a generic component

This allows to use the same code for monitor zoom bar, timeline zoom bar
and possible future implementations. The main goal of this change is
better maintainability.
parent a8f591ea
Pipeline #117482 passed with stage
in 5 minutes and 12 seconds
......@@ -6,6 +6,7 @@
import QtQuick.Controls 2.4
import Kdenlive.Controls 1.0
import QtQuick 2.11
import org.kde.kdenlive 1.0 as Kdenlive
// Monitor ruler
Rectangle {
......@@ -48,20 +49,16 @@ Rectangle {
var scaledPosition = ruler.playheadPosition * root.timeScale - ruler.rulerZoomOffset
if (scaledPosition < root.baseUnit) {
if (scaledPosition < 0) {
zoomBar.x = Math.max(0, zoomBar.x + (scaledPosition * root.zoomFactor ) - (zoomBar.width / 2))
root.zoomStart = zoomBar.x / zoomHandleContainer.width
root.zoomStart = Math.max(0, (rulerZoomOffset + scaledPosition) * root.zoomFactor - (rulerZoomWidth / 2)) / ruler.width
} else {
zoomBar.x = Math.max(0, zoomBar.x - root.baseUnit * root.zoomFactor)
root.zoomStart = zoomBar.x / zoomHandleContainer.width
root.zoomStart = Math.max(0, (rulerZoomOffset - root.baseUnit) * root.zoomFactor) / ruler.width
scrollTimer.start()
}
} else if (scaledPosition > zoomHandleContainer.width - root.baseUnit) {
if (scaledPosition > zoomHandleContainer.width) {
zoomBar.x = Math.min(zoomHandleContainer.width - zoomBar.width, zoomBar.x + (scaledPosition * root.zoomFactor) - (zoomBar.width / 2))
root.zoomStart = zoomBar.x / zoomHandleContainer.width
} else if (scaledPosition > ruler.width - root.baseUnit) {
if (scaledPosition > ruler.width) {
root.zoomStart = Math.min(ruler.width - rulerZoomWidth, (rulerZoomOffset + scaledPosition) * root.zoomFactor - (rulerZoomWidth / 2)) / ruler.width
} else {
zoomBar.x = Math.min(zoomHandleContainer.width - zoomBar.width, zoomBar.x + root.baseUnit * root.zoomFactor)
root.zoomStart = zoomBar.x / zoomHandleContainer.width
root.zoomStart = Math.min(ruler.width - rulerZoomWidth, (rulerZoomOffset + root.baseUnit) * root.zoomFactor) / ruler.width
scrollTimer.start()
}
}
......@@ -75,9 +72,9 @@ Rectangle {
// Adjust zoom factor
root.zoomFactor = Math.min(1, root.zoomFactor / 1.2)
if (root.zoomFactor * zoomHandleContainer.width < root.baseUnit / 2) {
if (root.zoomFactor * ruler.width < root.baseUnit / 2) {
// Don't allow too large zoom
root.zoomFactor = root.baseUnit / 2 / zoomHandleContainer.width
root.zoomFactor = root.baseUnit / 2 / ruler.width
}
// Always try to have cursor pos centered in zoom
var cursorPos = Math.max(0, controller.position / root.duration - root.zoomFactor / 2)
......@@ -85,8 +82,6 @@ Rectangle {
cursorPos = 1 - root.zoomFactor
}
root.zoomStart = cursorPos
zoomBar.x = root.zoomStart * zoomHandleContainer.width
zoomBar.width = root.zoomFactor * zoomHandleContainer.width
}
function zoomOutRuler(xPos)
......@@ -103,209 +98,42 @@ Rectangle {
}
root.zoomStart = cursorPos
}
zoomBar.x = root.zoomStart * zoomHandleContainer.width
zoomBar.width = root.zoomFactor * zoomHandleContainer.width
}
// Zoom bar container
Rectangle {
id: zoomContainer
SystemPalette { id: barPalette; colorGroup: SystemPalette.Disabled }
height: root.baseUnit
property bool hoveredBar: zoomArea.containsMouse || zoomArea.pressed || zoomStart.isActive || zoomEnd.isActive
color: hoveredBar ? barPalette.text : activePalette.dark
border.color: activePalette.dark
border.width: 1
Kdenlive.ZoomBar {
id: horZoomBar
visible: root.showZoomBar
onVisibleChanged: {
root.zoomOffset = visible ? height : 0
}
toolTipText: controller.toTimecode((root.duration + 1 )* root.zoomFactor)
anchors {
left: parent.left
right: parent.right
bottom: parent.top
}
MouseArea {
anchors.fill: parent
onWheel: {
if (wheel.modifiers & Qt.ControlModifier) {
if (wheel.angleDelta.y < 0) {
// zoom out
zoomOutRuler(wheel.x)
} else {
// zoom in
zoomInRuler(wheel.x)
}
} else {
if (wheel.angleDelta.y < 0) {
var newPos = Math.min(zoomHandleContainer.width - zoomBar.width, zoomBar.x + 10)
zoomBar.x = newPos
} else {
var newPos = Math.max(0, zoomBar.x - 10)
zoomBar.x = newPos
}
root.zoomStart = zoomBar.x / zoomHandleContainer.width
}
}
}
Item {
id: zoomHandleContainer
property int previousX: 0
property int previousWidth: zoomHandleContainer.width
anchors.fill: parent
anchors.margins: 1
Rectangle {
id: zoomBar
color: zoomContainer.hoveredBar ? activePalette.highlight : barPalette.text
height: parent.height
width: parent.width
MouseArea {
id: zoomArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
drag.target: zoomBar
drag.axis: Drag.XAxis
drag.smoothed: false
drag.minimumX: 0
drag.maximumX: zoomHandleContainer.width - zoomBar.width
onPositionChanged: {
root.zoomStart = zoomBar.x / zoomHandleContainer.width
}
onDoubleClicked: {
if (zoomBar.x == 0 && zoomBar.width == zoomHandleContainer.width) {
// Restore previous pos
zoomBar.width = zoomHandleContainer.previousWidth
zoomBar.x = zoomHandleContainer.previousX
root.zoomStart = zoomBar.x / zoomHandleContainer.width
root.zoomFactor = zoomBar.width / zoomHandleContainer.width
} else {
zoomHandleContainer.previousWidth = zoomBar.width
zoomHandleContainer.previousX = zoomBar.x
zoomBar.x = 0
zoomBar.width = zoomHandleContainer.width
root.zoomStart = 0
root.zoomFactor = 1
}
}
onWheel: {
if (wheel.modifiers & Qt.ControlModifier) {
if (wheel.angleDelta.y < 0) {
// zoom out
zoomOutRuler(wheel.x)
} else {
// zoom in
zoomInRuler(wheel.x)
}
} else {
if (wheel.angleDelta.y < 0) {
var newPos = Math.min(zoomHandleContainer.width - zoomBar.width, zoomBar.x + 10)
zoomBar.x = newPos
} else {
var newPos = Math.max(0, zoomBar.x - 10)
zoomBar.x = newPos
}
root.zoomStart = zoomBar.x / zoomHandleContainer.width
}
}
}
}
MouseArea {
id: zoomStart
property bool isActive: zoomStart.containsMouse || zoomStart.pressed
anchors.left: zoomBar.left
anchors.leftMargin: - root.baseUnit / 2
anchors.bottom: zoomBar.bottom
width: root.baseUnit
height: zoomBar.height
hoverEnabled: true
cursorShape: Qt.SizeHorCursor
onPressed: {
anchors.left = undefined
startHandleRect.anchors.fill = undefined
}
onReleased: {
anchors.left = zoomBar.left
startHandleRect.anchors.fill = zoomStart
}
onPositionChanged: {
if (mouse.buttons === Qt.LeftButton) {
var updatedPos = Math.max(0, x + mouseX + root.baseUnit / 2)
updatedPos = Math.min(updatedPos, zoomBar.x + zoomBar.width - root.baseUnit / 2)
zoomBar.width = zoomBar.x + zoomBar.width - updatedPos
zoomBar.x = updatedPos
root.zoomStart = updatedPos / zoomHandleContainer.width
root.zoomFactor = zoomBar.width / zoomHandleContainer.width
startHandleRect.x = mouseX
}
}
Rectangle {
id: startHandleRect
anchors.fill: parent
radius: height / 2
color: zoomStart.isActive ? activePalette.text : barPalette.light
Rectangle {
anchors.fill: parent
anchors.leftMargin: height / 2
color: parent.color
}
}
}
MouseArea {
id: zoomEnd
property bool isActive: zoomEnd.containsMouse || zoomEnd.pressed
anchors.left: zoomBar.right
anchors.leftMargin: - root.baseUnit / 2
anchors.bottom: zoomBar.bottom
width: root.baseUnit
height: zoomBar.height
hoverEnabled: true
cursorShape: Qt.SizeHorCursor
onPressed: {
anchors.left = undefined
endHandleRect.anchors.fill = undefined
}
onReleased: {
anchors.left = zoomBar.right
endHandleRect.anchors.fill = zoomEnd
}
onPositionChanged: {
if (mouse.buttons === Qt.LeftButton) {
var updatedPos = Math.min(zoomHandleContainer.width, x + mouseX + root.baseUnit / 2)
updatedPos = Math.max(updatedPos, zoomBar.x + root.baseUnit / 2)
zoomBar.width = updatedPos - zoomBar.x
root.zoomFactor = zoomBar.width / zoomHandleContainer.width
endHandleRect.x = mouseX
}
}
Rectangle {
id: endHandleRect
anchors.fill: parent
radius: height / 2
color: zoomEnd.isActive ? activePalette.text : barPalette.light
Rectangle {
anchors.fill: parent
anchors.rightMargin: height / 2
color: parent.color
}
}
height: root.baseUnit
fitsZoom: root.zoomFactor === 1 && root.zoomStart === 0
zoomFactor: root.zoomFactor
onProposeZoomFactor: root.zoomFactor = proposedValue
contentPos: root.zoomStart
onProposeContentPos: root.zoomStart = proposedValue
onZoomByWheel: {
if (wheel.angleDelta.y < 0) {
// zoom out
zoomOutRuler(wheel.x)
} else {
// zoom in
zoomInRuler(wheel.x)
}
}
ToolTip {
visible: zoomArea.containsMouse
delay: 1000
timeout: 5000
background: Rectangle {
color: activePalette.alternateBase
border.color: activePalette.light
}
contentItem: Label {
color: activePalette.text
font: fixedFont
text: controller.toTimecode((root.duration + 1 )* root.zoomFactor)
}
onFitZoom: {
root.zoomFactor = 1
root.zoomStart = 0
}
}
onSeekingFinishedChanged : {
playhead.opacity = seekingFinished ? 1 : 0.5
}
......
......@@ -5,18 +5,28 @@
SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
import QtQuick 2.0
import QtQml.Models 2.11
import QtQuick 2.11
import QtQuick.Controls 2.4
Rectangle {
id: zoomContainer
SystemPalette { id: barPalette; colorGroup: SystemPalette.Disabled }
property int barMinWidth: 1
property bool hoveredBar: barArea.containsMouse || barArea.pressed || zoomStart.isActive || zoomEnd.isActive
property int barMinWidth: 1
required property real contentPos
required property real zoomFactor
required property bool fitsZoom
property string toolTipText
signal proposeContentPos(real proposedValue)
signal proposeZoomFactor(real proposedValue)
signal zoomByWheel(var wheel)
signal fitZoom()
color: hoveredBar || containerArea.containsMouse ? barPalette.text : activePalette.window
radius: height / 2
property Flickable flickable: undefined
border {
color: activePalette.window
width: 1
}
MouseArea {
id: containerArea
anchors.fill: parent
......@@ -25,73 +35,74 @@ Rectangle {
if (wheel.modifiers & Qt.ControlModifier) {
zoomByWheel(wheel)
} else {
var newPos = zoomBar.x
if (wheel.angleDelta.y < 0) {
var newPos = Math.min(zoomHandleContainer.width - zoomBar.width, zoomBar.x + 10)
newPos = Math.min(zoomHandleContainer.width - zoomBar.width, newPos + 10)
} else {
var newPos = Math.max(0, zoomBar.x - 10)
newPos = Math.max(0, newPos - 10)
}
flickable.contentX = newPos / zoomHandleContainer.width * flickable.contentWidth
proposeContentPos(newPos / zoomHandleContainer.width)
}
}
onPressed: {
if (mouse.buttons === Qt.LeftButton) {
if (mouseX > zoomEnd.x + zoomEnd.width) {
flickable.contentX = (zoomBar.x + zoomBar.width) / zoomHandleContainer.width * flickable.contentWidth
proposeContentPos((zoomBar.x + zoomBar.width) / zoomHandleContainer.width)
} else if (mouseX < zoomBar.x) {
flickable.contentX = Math.max(0, (zoomBar.x - zoomBar.width) / zoomHandleContainer.width * flickable.contentWidth)
proposeContentPos(Math.max(0, (zoomBar.x - zoomBar.width) / zoomHandleContainer.width))
}
}
}
}
Item {
id: zoomHandleContainer
property int previousX: 0
property double previousScale: -1
anchors.fill: parent
Item {
id: zoomBar
height: parent.height
property int minWidth: barMinWidth + zoomEnd.width + zoomStart.width
property int preferedWidth: scrollView.visibleArea.widthRatio * zoomHandleContainer.width
property int preferedWidth: zoomFactor * zoomHandleContainer.width
width: !zoomStart.pressed && !zoomEnd.pressed && preferedWidth < minWidth ? minWidth : preferedWidth
x: scrollView.visibleArea.xPosition * zoomHandleContainer.width
x: contentPos * zoomHandleContainer.width
MouseArea {
id: barArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
drag.target: zoomBar
drag.axis: Drag.XAxis
drag.smoothed: false
drag.minimumX: 0
drag.maximumX: zoomHandleContainer.width - zoomBar.width
property real previousPos: 0
property real previousFactor: -1
drag {
target: zoomBar
axis: Drag.XAxis
smoothed: false
minimumX: 0
maximumX: zoomHandleContainer.width - zoomBar.width
}
onPositionChanged: {
if (mouse.buttons === Qt.LeftButton) {
flickable.contentX = zoomBar.x / zoomHandleContainer.width * flickable.contentWidth
proposeContentPos(zoomBar.x / zoomHandleContainer.width)
}
}
onDoubleClicked: {
// Switch between current zoom and fit all project
if (zoomBar.x == 0 && timeline.scaleFactor === root.fitZoom()) {
// Switch between current zoom and fit whole content
if (zoomBar.x == 0 && fitsZoom) {
// Restore previous pos
if (zoomHandleContainer.previousScale > -1) {
root.zoomOnBar = zoomHandleContainer.previousX
timeline.scaleFactor = zoomHandleContainer.previousScale
if (previousFactor > -1) {
proposeZoomFactor(previousFactor)
proposeContentPos(previousPos)
}
} else {
zoomHandleContainer.previousX = Math.round(zoomBar.x / (zoomHandleContainer.width) * flickable.contentWidth / timeline.scaleFactor)
zoomHandleContainer.previousScale = timeline.scaleFactor
root.zoomOnBar = 0
timeline.scaleFactor = root.fitZoom()
previousPos = contentPos
previousFactor = zoomFactor
fitZoom()
}
}
Rectangle {
color: hoveredBar ? activePalette.highlight : containerArea.containsMouse ? activePalette.text : barPalette.text
opacity: hoveredBar || containerArea.containsMouse ? 0.6 : 1
x: parent.x + parent.height
x: parent.x + zoomStart.width
height: parent.height
width: parent.width - parent.height * 2
width: parent.width - zoomStart.width - zoomEnd.width
}
}
}
......@@ -112,10 +123,8 @@ Rectangle {
if (mouse.buttons === Qt.LeftButton) {
var updatedPos = Math.max(0, x + mouseX)
updatedPos = Math.min(updatedPos, zoomEnd.x - width - 1)
var firstFrame = Math.round(updatedPos / (zoomHandleContainer.width) * flickable.contentWidth / timeline.scaleFactor)
var lastFrame = Math.round((zoomBar.x + zoomBar.width + 0.5) / (zoomHandleContainer.width) * flickable.contentWidth / timeline.scaleFactor)
root.zoomOnBar = firstFrame
timeline.scaleFactor = flickable.width / (lastFrame - firstFrame)
proposeZoomFactor((zoomBar.x + zoomBar.width + 0.5 - updatedPos) / zoomHandleContainer.width)
proposeContentPos(updatedPos / zoomHandleContainer.width)
startHandleRect.x = updatedPos - x
}
}
......@@ -148,10 +157,9 @@ Rectangle {
if (mouse.buttons === Qt.LeftButton) {
var updatedPos = Math.min(zoomHandleContainer.width, x + mouseX)
updatedPos = Math.max(updatedPos, zoomBar.x + width * 2 + 1)
var lastFrame = Math.round(updatedPos / (zoomHandleContainer.width) * flickable.contentWidth / timeline.scaleFactor)
var firstFrame = Math.round((zoomBar.x) / (zoomHandleContainer.width) * flickable.contentWidth / timeline.scaleFactor)
root.zoomOnBar = firstFrame
timeline.scaleFactor = flickable.width / (lastFrame - firstFrame)
var zoomBarX = zoomBar.x // we need to save the value before we change zoomFactor, but apply it afterwards
proposeZoomFactor((updatedPos - zoomBar.x) / zoomHandleContainer.width)
proposeContentPos(zoomBarX / zoomHandleContainer.width)
endHandleRect.x = updatedPos - x - width
}
}
......@@ -161,12 +169,27 @@ Rectangle {
radius: height / 2
color: zoomEnd.isActive ? activePalette.highlight : hoveredBar || containerArea.containsMouse ? activePalette.text : barPalette.text
Rectangle {
anchors.fill: parent
anchors.rightMargin: height / 2
anchors {
fill: parent
rightMargin: height / 2
}
color: parent.color
}
}
}
}
ToolTip {
visible: barArea.containsMouse && toolTipText
delay: 1000
timeout: 5000
background: Rectangle {
color: activePalette.alternateBase
border.color: activePalette.light
}
contentItem: Label {
color: activePalette.text
font: fixedFont
text: toolTipText
}
}
}
module org.kdenlive
ZoomBar 1.0 ZoomBar.qml
......@@ -4,6 +4,7 @@ import QtQuick.Controls 2.4
import Kdenlive.Controls 1.0
import 'Timeline.js' as Logic
import com.enums 1.0
import org.kde.kdenlive 1.0 as Kdenlive
Rectangle {
id: root
......@@ -407,8 +408,7 @@ Rectangle {
property int trackHeight
property int copiedClip: -1
property int zoomOnMouse: -1
// The first visible frame in case of scaling triggered by zoombar
property int zoomOnBar: -1
property bool zoomOnBar: false // Whether the scaling was done with the zoombar
property int viewActiveTrack: timeline.activeTrack
property int wheelAccumulatedDelta: 0
readonly property int defaultDeltasPerStep: 120
......@@ -439,9 +439,8 @@ Rectangle {
if (root.zoomOnMouse >= 0) {
scrollView.contentX = Math.max(0, root.zoomOnMouse * timeline.scaleFactor - getMouseX())
root.zoomOnMouse = -1
} else if (root.zoomOnBar >= 0) {
scrollView.contentX = Math.max(0, root.zoomOnBar * timeline.scaleFactor )
root.zoomOnBar = -1
} else if (root.zoomOnBar) {
root.zoomOnBar = false
} else {
scrollView.contentX = Math.max(0, root.consumerPosition * timeline.scaleFactor - (scrollView.width / 2))
}
......@@ -1834,7 +1833,7 @@ Rectangle {
x: root.consumerPosition * timeline.scaleFactor
}
}
ZoomBar {
Kdenlive.ZoomBar {
id: horZoomBar
visible: scrollView.visibleArea.widthRatio < 1
anchors {
......@@ -1844,7 +1843,20 @@ Rectangle {
}
height: Math.round(root.baseUnit * 0.7)
barMinWidth: root.baseUnit
flickable: scrollView
fitsZoom: timeline.scaleFactor === root.fitZoom() && root.scrollPos() === 0
zoomFactor: scrollView.visibleArea.widthRatio
onProposeZoomFactor: {
timeline.scaleFactor = scrollView.width / Math.round(proposedValue * scrollView.contentWidth / timeline.scaleFactor)
zoomOnBar = true
}
contentPos: scrollView.contentX / scrollView.contentWidth
onProposeContentPos: scrollView.contentX = Math.max(0, proposedValue * scrollView.contentWidth)
onZoomByWheel: root.zoomByWheel(wheel)
onFitZoom: {
timeline.scaleFactor = root.fitZoom()
scrollView.contentX = 0
zoomOnBar = true
}
}
}
}
......
......@@ -369,7 +369,7 @@ void TimelineWidget::slotFitZoom()
double scale = returnedValue.toDouble();
QMetaObject::invokeMethod(rootObject(), "scrollPos", Q_RETURN_ARG(QVariant, returnedValue));
int scrollPos = returnedValue.toInt();
if (qFuzzyCompare(prevScale, scale)) {
if (qFuzzyCompare(prevScale, scale) && scrollPos == 0) {
scale = m_prevScale;
scrollPos = m_scrollPos;
} else {
......
......@@ -43,7 +43,6 @@
<file alias="BuiltStack.qml">effects/effectstack/view/qml/BuiltStack.qml</file>
<file alias="EffectSlider.qml">effects/effectstack/view/qml/EffectSlider.qml</file>
<file alias="LiftGammaGain.qml">effects/effectstack/view/qml/LiftGammaGain.qml</file>
<file alias="ZoomBar.qml">timeline2/view/qml/ZoomBar.qml</file>
<file alias="kdenlivemonitortrimming.qml">monitor/view/kdenlivemonitortrimming.qml</file>
</qresource>
<qresource prefix="/data">
......@@ -57,4 +56,8 @@
<file alias="kdenlive_keyboardschemes.knsrc">../data/knewstuff/kdenlive_keyboardschemes.knsrc</file>
<file alias="kdenlive_luts.knsrc">../data/knewstuff/kdenlive_luts.knsrc</file>
</qresource>
<qresource prefix="/qt-project.org/imports/org/kde/kdenlive">
<file alias="ZoomBar.qml">qml/ZoomBar.qml</file>
<file alias="qmldir">qml/qmldir</file>
</qresource>
</RCC>
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment