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)); } }