diff --git a/src/assets/keyframes/view/keyframeview.cpp b/src/assets/keyframes/view/keyframeview.cpp
index 996fb4799..840c809a0 100644
--- a/src/assets/keyframes/view/keyframeview.cpp
+++ b/src/assets/keyframes/view/keyframeview.cpp
@@ -1,337 +1,340 @@
/***************************************************************************
* Copyright (C) 2011 by Till Theato (root@ttill.de) *
* Copyright (C) 2017 by Nicolas Carion *
* This file is part of Kdenlive (www.kdenlive.org). *
* *
* Kdenlive is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 2 of the License, or *
* (at your option) any later version. *
* *
* Kdenlive is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with Kdenlive. If not, see . *
***************************************************************************/
#include "keyframeview.hpp"
#include "assets/keyframes/model/keyframemodellist.hpp"
#include "core.h"
#include "kdenlivesettings.h"
#include
#include
#include
#include
KeyframeView::KeyframeView(std::shared_ptr model, int duration, QWidget *parent)
: QWidget(parent)
, m_model(model)
, m_duration(duration)
, m_position(0)
, m_currentKeyframe(-1)
, m_currentKeyframeOriginal(-1)
, m_hoverKeyframe(-1)
, m_scale(1)
{
setMouseTracking(true);
setMinimumSize(QSize(150, 20));
- setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum));
+ setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum));
setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
QPalette p = palette();
KColorScheme scheme(p.currentColorGroup(), KColorScheme::Window);
m_colSelected = palette().highlight().color();
m_colKeyframe = scheme.foreground(KColorScheme::NormalText).color();
m_size = QFontInfo(font()).pixelSize() * 1.8;
m_lineHeight = m_size / 2;
- setMinimumHeight(m_size);
- setMaximumHeight(m_size);
+ m_offset = m_lineHeight;
+ setFixedHeight(m_size);
connect(m_model.get(), &KeyframeModelList::modelChanged, this, &KeyframeView::slotModelChanged);
}
void KeyframeView::slotModelChanged()
{
int offset = pCore->getItemIn(m_model->getOwnerId());
emit atKeyframe(m_model->hasKeyframe(m_position + offset), m_model->singleKeyframe());
emit modified();
update();
}
void KeyframeView::slotSetPosition(int pos, bool isInRange)
{
if (!isInRange) {
m_position = -1;
update();
return;
}
if (pos != m_position) {
m_position = pos;
int offset = pCore->getItemIn(m_model->getOwnerId());
emit atKeyframe(m_model->hasKeyframe(pos + offset), m_model->singleKeyframe());
update();
}
}
void KeyframeView::initKeyframePos()
{
emit atKeyframe(m_model->hasKeyframe(m_position), m_model->singleKeyframe());
}
void KeyframeView::slotAddKeyframe(int pos)
{
if (pos < 0) {
pos = m_position;
}
int offset = pCore->getItemIn(m_model->getOwnerId());
m_model->addKeyframe(GenTime(size_t(pos + offset), pCore->getCurrentFps()), (KeyframeType)KdenliveSettings::defaultkeyframeinterp());
}
void KeyframeView::slotAddRemove()
{
int offset = pCore->getItemIn(m_model->getOwnerId());
if (m_model->hasKeyframe(m_position + offset)) {
slotRemoveKeyframe(m_position);
} else {
slotAddKeyframe(m_position);
}
}
void KeyframeView::slotEditType(int type, const QPersistentModelIndex &index)
{
int offset = pCore->getItemIn(m_model->getOwnerId());
if (m_model->hasKeyframe(m_position + offset)) {
m_model->updateKeyframeType(GenTime(size_t(m_position + offset), pCore->getCurrentFps()), type, index);
}
}
void KeyframeView::slotRemoveKeyframe(int pos)
{
if (pos < 0) {
pos = m_position;
}
int offset = pCore->getItemIn(m_model->getOwnerId());
m_model->removeKeyframe(GenTime(size_t(pos + offset), pCore->getCurrentFps()));
}
void KeyframeView::setDuration(int dur)
{
m_duration = dur;
int offset = pCore->getItemIn(m_model->getOwnerId());
emit atKeyframe(m_model->hasKeyframe(m_position + offset), m_model->singleKeyframe());
update();
}
void KeyframeView::slotGoToNext()
{
- if (m_position == m_duration) {
+ if (m_position == m_duration - 1) {
return;
}
bool ok;
int offset = pCore->getItemIn(m_model->getOwnerId());
auto next = m_model->getNextKeyframe(GenTime(size_t(m_position + offset), pCore->getCurrentFps()), &ok);
if (ok) {
emit seekToPos(qMin((int)next.first.frames(pCore->getCurrentFps()) - offset, m_duration - 1));
} else {
// no keyframe after current position
emit seekToPos(m_duration - 1);
}
}
void KeyframeView::slotGoToPrev()
{
if (m_position == 0) {
return;
}
bool ok;
int offset = pCore->getItemIn(m_model->getOwnerId());
auto prev = m_model->getPrevKeyframe(GenTime(m_position + offset, pCore->getCurrentFps()), &ok);
if (ok) {
emit seekToPos(qMax(0, (int)prev.first.frames(pCore->getCurrentFps()) - offset));
} else {
// no keyframe after current position
- emit seekToPos(m_duration);
+ emit seekToPos(m_duration - 1);
}
}
void KeyframeView::mousePressEvent(QMouseEvent *event)
{
int offset = pCore->getItemIn(m_model->getOwnerId());
- int pos = event->x() / m_scale;
+ int pos = (event->x() - m_offset) / m_scale;
if (event->y() < m_lineHeight && event->button() == Qt::LeftButton) {
bool ok;
GenTime position(pos + offset, pCore->getCurrentFps());
auto keyframe = m_model->getClosestKeyframe(position, &ok);
if (ok && qAbs(keyframe.first.frames(pCore->getCurrentFps()) - pos - offset) * m_scale < ceil(m_lineHeight / 1.5)) {
m_currentKeyframeOriginal = keyframe.first.frames(pCore->getCurrentFps()) - offset;
// Select and seek to keyframe
m_currentKeyframe = m_currentKeyframeOriginal;
emit seekToPos(m_currentKeyframeOriginal);
return;
}
}
// no keyframe next to mouse
m_currentKeyframe = m_currentKeyframeOriginal = -1;
emit seekToPos(pos);
update();
}
void KeyframeView::mouseMoveEvent(QMouseEvent *event)
{
int offset = pCore->getItemIn(m_model->getOwnerId());
- int pos = qBound(0, (int)(event->x() / m_scale), m_duration);
+ int pos = qBound(0, (int)((event->x() - m_offset) / m_scale), m_duration - 1);
GenTime position(pos + offset, pCore->getCurrentFps());
if ((event->buttons() & Qt::LeftButton) != 0u) {
if (m_currentKeyframe == pos) {
return;
}
if (m_currentKeyframe > 0) {
if (!m_model->hasKeyframe(pos + offset)) {
GenTime currentPos(m_currentKeyframe + offset, pCore->getCurrentFps());
if (m_model->moveKeyframe(currentPos, position, false)) {
m_currentKeyframe = pos;
}
}
}
emit seekToPos(pos);
return;
}
if (event->y() < m_lineHeight) {
bool ok;
auto keyframe = m_model->getClosestKeyframe(position, &ok);
if (ok && qAbs(keyframe.first.frames(pCore->getCurrentFps()) - pos - offset) * m_scale < ceil(m_lineHeight / 1.5)) {
m_hoverKeyframe = keyframe.first.frames(pCore->getCurrentFps()) - offset;
setCursor(Qt::PointingHandCursor);
update();
return;
}
}
if (m_hoverKeyframe != -1) {
m_hoverKeyframe = -1;
setCursor(Qt::ArrowCursor);
update();
}
}
void KeyframeView::mouseReleaseEvent(QMouseEvent *event)
{
Q_UNUSED(event)
if (m_currentKeyframe >= 0) {
int offset = pCore->getItemIn(m_model->getOwnerId());
GenTime initPos(m_currentKeyframeOriginal + offset, pCore->getCurrentFps());
GenTime targetPos(m_currentKeyframe + offset, pCore->getCurrentFps());
bool ok1 = m_model->moveKeyframe(targetPos, initPos, false);
bool ok2 = m_model->moveKeyframe(initPos, targetPos, true);
qDebug() << "RELEASING keyframe move" << ok1 << ok2 << initPos.frames(pCore->getCurrentFps()) << targetPos.frames(pCore->getCurrentFps());
}
}
void KeyframeView::mouseDoubleClickEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton && event->y() < m_lineHeight) {
- int pos = qBound(0, (int)(event->x() / m_scale), m_duration);
+ int pos = qBound(0, (int)((event->x() - m_offset) / m_scale), m_duration - 1);
int offset = pCore->getItemIn(m_model->getOwnerId());
GenTime position(pos + offset, pCore->getCurrentFps());
bool ok;
auto keyframe = m_model->getClosestKeyframe(position, &ok);
if (ok && qAbs(keyframe.first.frames(pCore->getCurrentFps()) - pos - offset) * m_scale < ceil(m_lineHeight / 1.5)) {
m_model->removeKeyframe(keyframe.first);
if (keyframe.first.frames(pCore->getCurrentFps()) == m_currentKeyframe + offset) {
m_currentKeyframe = m_currentKeyframeOriginal = -1;
}
if (keyframe.first.frames(pCore->getCurrentFps()) == m_position + offset) {
emit atKeyframe(false, m_model->singleKeyframe());
}
return;
}
// add new keyframe
m_model->addKeyframe(position, (KeyframeType)KdenliveSettings::defaultkeyframeinterp());
} else {
QWidget::mouseDoubleClickEvent(event);
}
}
void KeyframeView::wheelEvent(QWheelEvent *event)
{
if (event->modifiers() & Qt::AltModifier) {
if (event->delta() > 0) {
slotGoToPrev();
} else {
slotGoToNext();
}
return;
}
int change = event->delta() > 0 ? -1 : 1;
- int pos = qBound(0, m_position + change, m_duration);
+ int pos = qBound(0, m_position + change, m_duration - 1);
emit seekToPos(pos);
}
void KeyframeView::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event)
QStylePainter p(this);
- m_scale = width() / (double)(m_duration);
+ m_scale = (width() - 2 * m_offset) / (double)(m_duration - 1);
// p.translate(0, m_lineHeight);
int headOffset = m_lineHeight / 1.5;
int offset = pCore->getItemIn(m_model->getOwnerId());
/*
* keyframes
*/
for (const auto &keyframe : *m_model.get()) {
int pos = keyframe.first.frames(pCore->getCurrentFps()) - offset;
if (pos == m_currentKeyframe || pos == m_hoverKeyframe) {
p.setBrush(m_colSelected);
} else {
p.setBrush(m_colKeyframe);
}
- double scaledPos = pos * m_scale;
+ double scaledPos = m_offset + (pos * m_scale);
p.drawLine(QPointF(scaledPos, headOffset), QPointF(scaledPos, m_lineHeight + headOffset / 2.0));
switch (keyframe.second.first) {
case KeyframeType::Linear: {
QPolygonF position = QPolygonF() << QPointF(-headOffset / 2.0, headOffset / 2.0) << QPointF(0, 0) << QPointF(headOffset / 2.0, headOffset / 2.0)
<< QPointF(0, headOffset);
position.translate(scaledPos, 0);
p.drawPolygon(position);
break;
}
case KeyframeType::Discrete:
p.drawRect(QRectF(scaledPos - headOffset / 2.0, 0, headOffset, headOffset));
break;
default:
p.drawEllipse(QRectF(scaledPos - headOffset / 2.0, 0, headOffset, headOffset));
break;
}
}
p.setPen(palette().dark().color());
/*
* Time-"line"
*/
p.setPen(m_colKeyframe);
- p.drawLine(0, m_lineHeight + (headOffset / 2), (m_duration - 1) * m_scale, m_lineHeight + (headOffset / 2));
+ p.drawLine(m_offset, m_lineHeight + (headOffset / 2), width() - m_offset, m_lineHeight + (headOffset / 2));
/*
* current position
*/
- if (m_position >= 0) {
+ if (m_position >= 0 && m_position < m_duration) {
QPolygon pa(3);
int cursorwidth = (m_size - (m_lineHeight + headOffset / 2)) / 2 + 1;
QPolygonF position = QPolygonF() << QPointF(-cursorwidth, m_size) << QPointF(cursorwidth, m_size) << QPointF(0, m_lineHeight + (headOffset / 2) + 1);
- position.translate(m_position * m_scale, 0);
+ position.translate(m_offset + (m_position * m_scale), 0);
p.setBrush(m_colKeyframe);
p.drawPolygon(position);
}
+ p.setOpacity(0.5);
+ p.drawLine(m_offset, m_lineHeight, m_offset, m_lineHeight + headOffset );
+ p.drawLine(width() - m_offset, m_lineHeight, width() - m_offset, m_lineHeight + headOffset );
}
diff --git a/src/assets/keyframes/view/keyframeview.hpp b/src/assets/keyframes/view/keyframeview.hpp
index d59dd493d..e79237617 100644
--- a/src/assets/keyframes/view/keyframeview.hpp
+++ b/src/assets/keyframes/view/keyframeview.hpp
@@ -1,89 +1,90 @@
/***************************************************************************
* Copyright (C) 2011 by Till Theato (root@ttill.de) *
* Copyright (C) 2017 by Nicolas Carion *
* This file is part of Kdenlive (www.kdenlive.org). *
* *
* Kdenlive is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 2 of the License, or *
* (at your option) any later version. *
* *
* Kdenlive is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with Kdenlive. If not, see . *
***************************************************************************/
#ifndef KEYFRAMEVIEW2_H
#define KEYFRAMEVIEW2_H
#include "assets/keyframes/model/keyframemodel.hpp"
#include "assets/keyframes/model/keyframemodellist.hpp"
#include
#include
class KeyframeModelList;
class KeyframeView : public QWidget
{
Q_OBJECT
public:
explicit KeyframeView(std::shared_ptr model, int duration, QWidget *parent = nullptr);
void setDuration(int dur);
public slots:
/* @brief moves the current position*/
void slotSetPosition(int pos, bool isInRange);
/* @brief remove the keyframe at given position
If pos is negative, we remove keyframe at current position
*/
void slotRemoveKeyframe(int pos);
/* @brief Add a keyframe with given parameter value at given pos.
If pos is negative, then keyframe is added at current position
*/
void slotAddKeyframe(int pos = -1);
/* @brief If there is a keyframe at current position, it is removed.
Otherwise, we add a new one with given value.
*/
void slotAddRemove();
void slotGoToNext();
void slotGoToPrev();
void slotModelChanged();
void slotEditType(int type, const QPersistentModelIndex &index);
/* @brief Emit initial info for monitor. */
void initKeyframePos();
protected:
void paintEvent(QPaintEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void mouseDoubleClickEvent(QMouseEvent *event) override;
void wheelEvent(QWheelEvent *event) override;
private:
std::shared_ptr m_model;
int m_duration;
int m_position;
int m_currentKeyframe;
int m_currentKeyframeOriginal;
int m_hoverKeyframe;
int m_lineHeight;
+ int m_offset;
double m_scale;
int m_size;
QColor m_colSelected;
QColor m_colKeyframe;
QColor m_colKeyframeBg;
signals:
void seekToPos(int pos);
void atKeyframe(bool isKeyframe, bool singleKeyframe);
void modified();
};
#endif
diff --git a/src/timeline2/view/qml/KeyframeView.qml b/src/timeline2/view/qml/KeyframeView.qml
index 80a3c8188..659401a85 100644
--- a/src/timeline2/view/qml/KeyframeView.qml
+++ b/src/timeline2/view/qml/KeyframeView.qml
@@ -1,261 +1,289 @@
/***************************************************************************
* Copyright (C) 2017 by Jean-Baptiste Mardelle *
* This file is part of Kdenlive. See www.kdenlive.org. *
* *
* 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 *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) version 3 or any later version accepted by the *
* membership of KDE e.V. (or its successor approved by the membership *
* of KDE e.V.), which shall act as a proxy defined in Section 14 of *
* version 3 of the license. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see . *
***************************************************************************/
import QtQuick 2.6
import QtQuick.Controls 1.4
import QtQml.Models 2.2
Rectangle
{
property alias kfrCount : keyframes.count
anchors.fill: parent
color: Qt.rgba(1,1,0.8, 0.3)
id: keyframeContainer
property int activeFrame
property int activeIndex
property int inPoint
property int outPoint
property bool selected
property var masterObject
property var kfrModel
onKfrCountChanged: {
keyframecanvas.requestPaint()
}
+
Keys.onShortcutOverride: {
if (event.key == Qt.Key_Left) {
if (event.modifiers & Qt.AltModifier) {
activeFrame = keyframes.itemAt(Math.max(0, --activeIndex)).frame
} else {
var oldFrame = activeFrame
activeFrame -= 1
if (activeFrame < 0) {
activeFrame = 0
} else {
timeline.updateEffectKeyframe(masterObject.clipId, oldFrame, activeFrame)
}
}
event.accepted = true
}
else if (event.key == Qt.Key_Right) {
if (event.modifiers & Qt.AltModifier) {
activeFrame = keyframes.itemAt(Math.min(keyframes.count - 1, ++activeIndex)).frame
} else {
var oldFrame = activeFrame
activeFrame += 1
- timeline.updateEffectKeyframe(masterObject.clipId, oldFrame, activeFrame)
+ activeFrame = Math.min(activeFrame, parent.width / timeScale)
+ if (activeFrame > oldFrame) {
+ timeline.updateEffectKeyframe(masterObject.clipId, oldFrame, activeFrame)
+ }
}
event.accepted = true
}
else if (event.key == Qt.Key_Return || event.key == Qt.Key_Escape) {
keyframeContainer.focus = false
event.accepted = true
}
if ((event.key == Qt.Key_Plus) && !(event.modifiers & Qt.ControlModifier)) {
var newVal = Math.min(keyframes.itemAt(activeIndex).value / parent.height + .05, 1)
kfrModel.updateKeyframe(activeFrame, newVal)
event.accepted = true
}
else if ((event.key == Qt.Key_Minus) && !(event.modifiers & Qt.ControlModifier)) {
var newVal = Math.max(keyframes.itemAt(activeIndex).value / parent.height - .05, 0)
kfrModel.updateKeyframe(activeFrame, newVal)
event.accepted = true
+ } else {
+ event.accepted = false
}
- event.accepted = false
}
Repeater {
id: keyframes
model: kfrModel
Rectangle {
id: keyframe
property int frame : model.frame
property int frameType : model.type
x: (model.frame - inPoint) * timeScale
height: parent.height
property int value: parent.height * model.normalizedValue
property int tmpVal : keyframeVal.y + root.baseUnit / 2
property int tmpPos : x + keyframeVal.x + root.baseUnit / 2
+ property int dragPos : -1
anchors.bottom: parent.bottom
onFrameTypeChanged: {
keyframecanvas.requestPaint()
}
onValueChanged: {
keyframecanvas.requestPaint()
}
width: Math.max(1, timeScale)
color: kfMouseArea.containsMouse ? 'darkred' : 'transparent'
visible: keyframeContainer.selected
MouseArea {
id: kfMouseArea
anchors.fill: parent
anchors.leftMargin: - root.baseUnit/3
anchors.rightMargin: - root.baseUnit/3
hoverEnabled: true
cursorShape: Qt.SizeHorCursor
drag.target: parent
drag.smoothed: false
drag.axis: Drag.XAxis
onReleased: {
root.stopScrolling = false
+ dragPos = -1
var newPos = Math.round(parent.x / timeScale) + inPoint
if (frame != inPoint && newPos != frame) {
if (mouse.modifiers & Qt.ShiftModifier) {
// offset all subsequent keyframes
// TODO: rewrite using timeline to ensure all kf parameters are updated
timeline.offsetKeyframes(masterObject.clipId, frame, newPos)
} else {
timeline.updateEffectKeyframe(masterObject.clipId, frame, newPos)
}
}
}
onPositionChanged: {
- if (mouse.buttons === Qt.LeftButton && frame != inPoint) {
- var newPos = Math.round(parent.x / timeScale)
- parent.x = newPos * timeScale
- keyframecanvas.requestPaint()
+ if (mouse.buttons === Qt.LeftButton) {
+ if (frame == inPoint) {
+ parent.x = inPoint * timeScale
+ return
+ }
+ var newPos = Math.min(Math.round(parent.x / timeScale), Math.round(keyframeContainer.width / timeScale) - 1)
+ if (newPos < 1) {
+ newPos = 1
+ }
+ if (newPos != dragPos) {
+ dragPos = newPos
+ parent.x = newPos * timeScale
+ keyframecanvas.requestPaint()
+ } else {
+ parent.x = dragPos * timeScale
+ }
}
}
}
Rectangle {
id: keyframeVal
x: - root.baseUnit / 2
y: keyframeContainer.height - keyframe.value - root.baseUnit / 2
width: root.baseUnit
height: width
radius: width / 2
color: keyframeContainer.activeFrame == keyframe.frame ? 'red' : kf1MouseArea.containsMouse || kf1MouseArea.pressed ? root.textColor : root.videoColor
border.color: kf1MouseArea.containsMouse || kf1MouseArea.pressed ? activePalette.highlight : root.textColor
MouseArea {
id: kf1MouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
drag.target: parent
drag.smoothed: false
onClicked: {
keyframeContainer.activeFrame = frame
keyframeContainer.activeIndex = index
keyframeContainer.focus = true
}
onReleased: {
root.stopScrolling = false
var newPos = frame == inPoint ? inPoint : Math.round((keyframe.x + parent.x + root.baseUnit / 2) / timeScale) + inPoint
if (newPos == frame && keyframe.value == keyframe.height - parent.y - root.baseUnit / 2) {
var pos = masterObject.modelStart + frame - inPoint
if (timeline.position != pos) {
timeline.seekPosition = pos
timeline.position = timeline.seekPosition
}
return
}
var newVal = (keyframeContainer.height - (parent.y + mouse.y)) / keyframeContainer.height
if (newVal > 1.5 || newVal < -0.5) {
if (frame != inPoint) {
timeline.removeEffectKeyframe(masterObject.clipId, frame);
} else {
if (newVal < 0) {
newVal = 0;
} else if (newVal > 1) {
newVal = 1;
}
timeline.updateEffectKeyframe(masterObject.clipId, frame, frame, newVal)
}
} else {
if (newVal < 0) {
newVal = 0;
} else if (newVal > 1) {
newVal = 1;
}
timeline.updateEffectKeyframe(masterObject.clipId, frame, frame == inPoint ? frame : newPos, newVal)
}
}
onPositionChanged: {
if (mouse.buttons === Qt.LeftButton) {
if (frame == inPoint) {
parent.x = - root.baseUnit / 2
} else {
- var newPos = Math.round(parent.x / timeScale)
- parent.x = newPos * timeScale
+ var newPos = Math.min(Math.round(parent.x / timeScale), Math.round(keyframeContainer.width / timeScale) - frame + inPoint - 1)
+ if (frame + newPos <= inPoint) {
+ newPos = inPoint + 1 - frame
+ }
+ if (newPos != dragPos) {
+ dragPos = newPos
+ parent.x = newPos * timeScale - root.baseUnit / 2
+ keyframecanvas.requestPaint()
+ } else {
+ parent.x = dragPos * timeScale - root.baseUnit / 2
+ }
}
keyframecanvas.requestPaint()
}
}
onDoubleClicked: {
timeline.removeEffectKeyframe(masterObject.clipId, frame);
}
}
}
}
}
Canvas {
id: keyframecanvas
anchors.fill: parent
contextType: "2d"
Component {
id: comp
PathCurve { }
}
Component {
id: compline
PathLine { }
}
property var paths : []
Path {
id: myPath
startX: 0; startY: parent.height
}
onPaint: {
if (keyframes.count == 0) {
return
}
context.beginPath()
context.fillStyle = Qt.rgba(0,0,0.8, 0.4);
paths = []
var xpos
var ypos
for(var i = 0; i < keyframes.count; i++)
{
var type = i > 0 ? keyframes.itemAt(i-1).frameType : keyframes.itemAt(i).frameType
xpos = keyframes.itemAt(i).tmpPos
if (type == 0) {
// discrete
paths.push(compline.createObject(keyframecanvas, {"x": xpos, "y": ypos} ))
}
ypos = keyframes.itemAt(i).tmpVal
if (type < 2) {
// linear
paths.push(compline.createObject(keyframecanvas, {"x": xpos, "y": ypos} ))
} else if (type == 2) {
// curve
paths.push(comp.createObject(keyframecanvas, {"x": xpos, "y": ypos} ))
}
}
paths.push(compline.createObject(keyframecanvas, {"x": parent.width, "y": ypos} ))
paths.push(compline.createObject(keyframecanvas, {"x": parent.width, "y": parent.height} ))
myPath.pathElements = paths
context.clearRect(0,0, width, height);
context.path = myPath;
context.closePath()
context.fill()
}
}
}
diff --git a/src/transitions/view/transitionstackview.cpp b/src/transitions/view/transitionstackview.cpp
index 356b9920a..f736055f7 100644
--- a/src/transitions/view/transitionstackview.cpp
+++ b/src/transitions/view/transitionstackview.cpp
@@ -1,113 +1,114 @@
/***************************************************************************
* Copyright (C) 2017 by Nicolas Carion *
* This file is part of Kdenlive. See www.kdenlive.org. *
* *
* 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 *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) version 3 or any later version accepted by the *
* membership of KDE e.V. (or its successor approved by the membership *
* of KDE e.V.), which shall act as a proxy defined in Section 14 of *
* version 3 of the license. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see . *
***************************************************************************/
#include "transitionstackview.hpp"
#include "assets/keyframes/model/keyframemodellist.hpp"
#include "assets/model/assetparametermodel.hpp"
#include "core.h"
#include "monitor/monitor.h"
#include
#include
#include
#include
#include
#include
TransitionStackView::TransitionStackView(QWidget *parent)
: AssetParameterView(parent)
{
}
void TransitionStackView::setModel(const std::shared_ptr &model, QSize frameSize, bool addSpacer)
{
QHBoxLayout *lay = new QHBoxLayout;
m_trackBox = new QComboBox(this);
QMapIterator i(pCore->getVideoTrackNames());
QPair aTrack = pCore->getCompositionATrack(model->getOwnerId().second);
m_trackBox->addItem(i18n("Automatic"), -1);
while (i.hasNext()) {
i.next();
if (i.key() < aTrack.second) {
m_trackBox->addItem(i.value(), i.key());
}
}
m_trackBox->addItem(i18n("Background"), 0);
AssetParameterView::setModel(model, frameSize, addSpacer);
if (!pCore->compositionAutoTrack(model->getOwnerId().second)) {
m_trackBox->setCurrentIndex(m_trackBox->findData(aTrack.first));
}
QLabel *title = new QLabel(i18n("Composition track: "), this);
lay->addWidget(title);
lay->addWidget(m_trackBox);
m_lay->insertLayout(0, lay);
auto kfr = model->getKeyframeModel();
if (kfr) {
connect(kfr.get(), &KeyframeModelList::modelChanged, this, &AssetParameterView::slotRefresh);
}
connect(model.get(), &AssetParameterModel::compositionTrackChanged, this, &TransitionStackView::checkCompoTrack);
connect(m_trackBox, SIGNAL(currentIndexChanged(int)), this, SLOT(updateTrack(int)));
connect(this, &AssetParameterView::seekToPos, [this](int pos) {
// at this point, the effects returns a pos relative to the clip. We need to convert it to a global time
int clipIn = pCore->getItemPosition(m_model->getOwnerId());
emit seekToTransPos(pos + clipIn);
});
initKeyframeView(true);
pCore->getMonitor(m_model->monitorId)->slotShowEffectScene(needsMonitorEffectScene());
+ m_lay->addStretch(10);
slotRefresh();
}
void TransitionStackView::unsetModel()
{
if (m_model) {
auto kfr = m_model->getKeyframeModel();
if (kfr) {
disconnect(kfr.get(), &KeyframeModelList::modelChanged, this, &AssetParameterView::slotRefresh);
}
pCore->getMonitor(m_model->monitorId)->slotShowEffectScene(MonitorSceneDefault);
}
AssetParameterView::unsetModel();
}
void TransitionStackView::updateTrack(int newTrack)
{
Q_UNUSED(newTrack)
qDebug() << "// Update transition TRACK to: " << m_trackBox->currentData().toInt();
pCore->setCompositionATrack(m_model->getOwnerId().second, m_trackBox->currentData().toInt());
}
ObjectId TransitionStackView::stackOwner() const
{
if (m_model) {
return m_model->getOwnerId();
}
return ObjectId(ObjectType::NoItem, -1);
}
void TransitionStackView::checkCompoTrack()
{
bool autoTrack = pCore->compositionAutoTrack(m_model->getOwnerId().second);
QPair aTrack = autoTrack ? QPair(-1, -1) : pCore->getCompositionATrack(m_model->getOwnerId().second);
if (m_trackBox->currentData().toInt() != aTrack.first) {
const QSignalBlocker blocker(m_trackBox);
m_trackBox->setCurrentIndex(m_trackBox->findData(aTrack.first));
}
}