refined keyframeview and curveview
This commit is contained in:
@@ -58,6 +58,10 @@ double Bezier::CalculateTFromX(bool cubic, double x, double a, double b, double
|
||||
double top = 1.0;
|
||||
|
||||
while (true) {
|
||||
if (bottom == top) {
|
||||
return bottom;
|
||||
}
|
||||
|
||||
double mid = (bottom + top) * 0.5;
|
||||
double test = cubic ? CubicTtoY(a, b, c, d, mid) : QuadraticTtoY(a, b, c, mid);
|
||||
|
||||
|
||||
@@ -19,11 +19,28 @@ SequenceDialogParameterTab::SequenceDialogParameterTab(Sequence* sequence, QWidg
|
||||
// Set up video section
|
||||
QGroupBox* video_group = new QGroupBox();
|
||||
video_group->setTitle(tr("Video"));
|
||||
QHBoxLayout* video_layout = new QHBoxLayout(video_group);
|
||||
video_section_ = new VideoParamEdit();
|
||||
video_section_->SetParameterMask(Sequence::kVideoParamEditMask);
|
||||
connect(video_section_, &VideoParamEdit::Changed, this, &SequenceDialogParameterTab::UpdatePreviewResolutionLabel);
|
||||
video_layout->addWidget(video_section_);
|
||||
QGridLayout *video_layout = new QGridLayout(video_group);
|
||||
video_layout->addWidget(new QLabel(tr("Width:")), row, 0);
|
||||
width_slider_ = new IntegerSlider();
|
||||
width_slider_->SetMinimum(0);
|
||||
video_layout->addWidget(width_slider_, row, 1);
|
||||
row++;
|
||||
video_layout->addWidget(new QLabel(tr("Height:")), row, 0);
|
||||
height_slider_ = new IntegerSlider();
|
||||
height_slider_->SetMinimum(0);
|
||||
video_layout->addWidget(height_slider_, row, 1);
|
||||
row++;
|
||||
video_layout->addWidget(new QLabel(tr("Frame Rate:")), row, 0);
|
||||
framerate_combo_ = new FrameRateComboBox();
|
||||
video_layout->addWidget(framerate_combo_, row, 1);
|
||||
row++;
|
||||
video_layout->addWidget(new QLabel(tr("Pixel Aspect Ratio:")), row, 0);
|
||||
pixelaspect_combo_ = new PixelAspectRatioComboBox();
|
||||
video_layout->addWidget(pixelaspect_combo_, row, 1);
|
||||
row++;
|
||||
video_layout->addWidget(new QLabel(tr("Interlacing:")), row, 0);
|
||||
interlacing_combo_ = new InterlacedComboBox();
|
||||
video_layout->addWidget(interlacing_combo_, row, 1);
|
||||
layout->addWidget(video_group);
|
||||
|
||||
row = 0;
|
||||
@@ -65,7 +82,11 @@ SequenceDialogParameterTab::SequenceDialogParameterTab(Sequence* sequence, QWidg
|
||||
// Set values based on input sequence
|
||||
VideoParams vp = sequence->GetVideoParams();
|
||||
AudioParams ap = sequence->GetAudioParams();
|
||||
video_section_->SetVideoParams(vp);
|
||||
width_slider_->SetValue(vp.width());
|
||||
height_slider_->SetValue(vp.height());
|
||||
framerate_combo_->SetFrameRate(vp.time_base().flipped());
|
||||
pixelaspect_combo_->SetPixelAspectRatio(vp.pixel_aspect_ratio());
|
||||
interlacing_combo_->SetInterlaceMode(vp.interlacing());
|
||||
preview_resolution_field_->SetDivider(vp.divider());
|
||||
preview_format_field_->SetPixelFormat(vp.format());
|
||||
preview_autocache_field_->setChecked(sequence->GetVideoAutoCacheEnabled());
|
||||
@@ -86,11 +107,11 @@ SequenceDialogParameterTab::SequenceDialogParameterTab(Sequence* sequence, QWidg
|
||||
|
||||
void SequenceDialogParameterTab::PresetChanged(const SequencePreset &preset)
|
||||
{
|
||||
video_section_->SetWidth(preset.width());
|
||||
video_section_->SetHeight(preset.height());
|
||||
video_section_->SetFrameRate(preset.frame_rate());
|
||||
video_section_->SetPixelAspectRatio(preset.pixel_aspect());
|
||||
video_section_->SetInterlaceMode(preset.interlacing());
|
||||
width_slider_->SetValue(preset.width());
|
||||
height_slider_->SetValue(preset.height());
|
||||
framerate_combo_->SetFrameRate(preset.frame_rate());
|
||||
pixelaspect_combo_->SetPixelAspectRatio(preset.pixel_aspect());
|
||||
interlacing_combo_->SetInterlaceMode(preset.interlacing());
|
||||
audio_sample_rate_field_->SetSampleRate(preset.sample_rate());
|
||||
audio_channels_field_->SetChannelLayout(preset.channel_layout());
|
||||
preview_resolution_field_->SetDivider(preset.preview_divider());
|
||||
@@ -115,8 +136,8 @@ void SequenceDialogParameterTab::SavePresetClicked()
|
||||
|
||||
void SequenceDialogParameterTab::UpdatePreviewResolutionLabel()
|
||||
{
|
||||
VideoParams test_param(video_section_->GetWidth(),
|
||||
video_section_->GetHeight(),
|
||||
VideoParams test_param(GetSelectedVideoWidth(),
|
||||
GetSelectedVideoHeight(),
|
||||
VideoParams::kFormatInvalid,
|
||||
VideoParams::kInternalChannelCount,
|
||||
rational(1),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef SEQUENCEDIALOGPARAMETERTAB_H
|
||||
#define SEQUENCEDIALOGPARAMETERTAB_H
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QList>
|
||||
#include <QSpinBox>
|
||||
@@ -9,7 +10,6 @@
|
||||
#include "sequencepreset.h"
|
||||
#include "widget/slider/integerslider.h"
|
||||
#include "widget/standardcombos/standardcombos.h"
|
||||
#include "widget/videoparamedit/videoparamedit.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -21,27 +21,27 @@ public:
|
||||
|
||||
int GetSelectedVideoWidth() const
|
||||
{
|
||||
return video_section_->GetWidth();
|
||||
return width_slider_->GetValue();
|
||||
}
|
||||
|
||||
int GetSelectedVideoHeight() const
|
||||
{
|
||||
return video_section_->GetHeight();
|
||||
return height_slider_->GetValue();
|
||||
}
|
||||
|
||||
rational GetSelectedVideoFrameRate() const
|
||||
{
|
||||
return video_section_->GetFrameRate();
|
||||
return framerate_combo_->GetFrameRate();
|
||||
}
|
||||
|
||||
rational GetSelectedVideoPixelAspect() const
|
||||
{
|
||||
return video_section_->GetPixelAspectRatio();
|
||||
return pixelaspect_combo_->GetPixelAspectRatio();
|
||||
}
|
||||
|
||||
VideoParams::Interlacing GetSelectedVideoInterlacingMode() const
|
||||
{
|
||||
return video_section_->GetInterlaceMode();
|
||||
return interlacing_combo_->GetInterlaceMode();
|
||||
}
|
||||
|
||||
int GetSelectedAudioSampleRate() const
|
||||
@@ -76,7 +76,15 @@ signals:
|
||||
void SaveParametersAsPreset(const SequencePreset& preset);
|
||||
|
||||
private:
|
||||
VideoParamEdit* video_section_;
|
||||
IntegerSlider *width_slider_;
|
||||
|
||||
IntegerSlider *height_slider_;
|
||||
|
||||
FrameRateComboBox *framerate_combo_;
|
||||
|
||||
PixelAspectRatioComboBox *pixelaspect_combo_;
|
||||
|
||||
InterlacedComboBox *interlacing_combo_;
|
||||
|
||||
SampleRateComboBox* audio_sample_rate_field_;
|
||||
|
||||
|
||||
+8
-4
@@ -2246,7 +2246,6 @@ void Node::childEvent(QChildEvent *event)
|
||||
GetImmediate(key->input(), key->element())->insert_keyframe(key);
|
||||
|
||||
connect(key, &NodeKeyframe::TimeChanged, this, &Node::InvalidateFromKeyframeTimeChange);
|
||||
connect(key, &NodeKeyframe::TimeChanged, this, &Node::KeyframeTimeChanged);
|
||||
connect(key, &NodeKeyframe::ValueChanged, this, &Node::InvalidateFromKeyframeValueChange);
|
||||
connect(key, &NodeKeyframe::TypeChanged, this, &Node::InvalidateFromKeyframeTypeChanged);
|
||||
connect(key, &NodeKeyframe::BezierControlInChanged, this, &Node::InvalidateFromKeyframeBezierInChange);
|
||||
@@ -2258,15 +2257,14 @@ void Node::childEvent(QChildEvent *event)
|
||||
TimeRange time_affected = GetRangeAffectedByKeyframe(key);
|
||||
|
||||
disconnect(key, &NodeKeyframe::TimeChanged, this, &Node::InvalidateFromKeyframeTimeChange);
|
||||
disconnect(key, &NodeKeyframe::TimeChanged, this, &Node::KeyframeTimeChanged);
|
||||
disconnect(key, &NodeKeyframe::ValueChanged, this, &Node::InvalidateFromKeyframeValueChange);
|
||||
disconnect(key, &NodeKeyframe::TypeChanged, this, &Node::InvalidateFromKeyframeTypeChanged);
|
||||
disconnect(key, &NodeKeyframe::BezierControlInChanged, this, &Node::InvalidateFromKeyframeBezierInChange);
|
||||
disconnect(key, &NodeKeyframe::BezierControlOutChanged, this, &Node::InvalidateFromKeyframeBezierOutChange);
|
||||
|
||||
GetImmediate(key->input(), key->element())->remove_keyframe(key);
|
||||
|
||||
emit KeyframeRemoved(key);
|
||||
|
||||
GetImmediate(key->input(), key->element())->remove_keyframe(key);
|
||||
ParameterValueChanged(i, time_affected);
|
||||
}
|
||||
}
|
||||
@@ -2329,12 +2327,16 @@ void Node::InvalidateFromKeyframeTimeChange()
|
||||
foreach (const TimeRange& r, invalidate_range) {
|
||||
ParameterValueChanged(key->key_track_ref().input(), r);
|
||||
}
|
||||
|
||||
emit KeyframeTimeChanged(key);
|
||||
}
|
||||
|
||||
void Node::InvalidateFromKeyframeValueChange()
|
||||
{
|
||||
NodeKeyframe* key = static_cast<NodeKeyframe*>(sender());
|
||||
ParameterValueChanged(key->key_track_ref().input(), GetRangeAffectedByKeyframe(key));
|
||||
|
||||
emit KeyframeValueChanged(key);
|
||||
}
|
||||
|
||||
void Node::InvalidateFromKeyframeTypeChanged()
|
||||
@@ -2349,6 +2351,8 @@ void Node::InvalidateFromKeyframeTypeChanged()
|
||||
|
||||
// Invalidate entire range
|
||||
ParameterValueChanged(key->key_track_ref().input(), GetRangeAroundIndex(key->input(), track.indexOf(key), key->track(), key->element()));
|
||||
|
||||
emit KeyframeTypeChanged(key);
|
||||
}
|
||||
|
||||
Project *Node::ArrayInsertCommand::GetRelevantProject() const
|
||||
|
||||
+5
-1
@@ -1066,7 +1066,11 @@ signals:
|
||||
|
||||
void KeyframeRemoved(NodeKeyframe* key);
|
||||
|
||||
void KeyframeTimeChanged();
|
||||
void KeyframeTimeChanged(NodeKeyframe* key);
|
||||
|
||||
void KeyframeTypeChanged(NodeKeyframe* key);
|
||||
|
||||
void KeyframeValueChanged(NodeKeyframe* key);
|
||||
|
||||
void KeyframeEnableChanged(const NodeInput& input, bool enabled);
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "config/config.h"
|
||||
#include "core.h"
|
||||
#include "node/traverser.h"
|
||||
#include "widget/videoparamedit/videoparamedit.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -34,8 +33,6 @@ const QString ViewerOutput::kSamplesInput = QStringLiteral("samples_in");
|
||||
const QString ViewerOutput::kVideoAutoCacheInput = QStringLiteral("video_autocache_in");
|
||||
const QString ViewerOutput::kAudioAutoCacheInput = QStringLiteral("audio_autocache_in");
|
||||
|
||||
const uint64_t ViewerOutput::kVideoParamEditMask = VideoParamEdit::kWidthHeight | VideoParamEdit::kInterlacing | VideoParamEdit::kFrameRate | VideoParamEdit::kPixelAspect;
|
||||
|
||||
#define super Node
|
||||
|
||||
ViewerOutput::ViewerOutput(bool create_buffer_inputs, bool create_default_streams) :
|
||||
@@ -48,7 +45,6 @@ ViewerOutput::ViewerOutput(bool create_buffer_inputs, bool create_default_stream
|
||||
audio_cache_enabled_(true)
|
||||
{
|
||||
AddInput(kVideoParamsInput, NodeValue::kVideoParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable | kInputFlagArray | kInputFlagHidden));
|
||||
SetInputProperty(kVideoParamsInput, QStringLiteral("mask"), QVariant::fromValue(kVideoParamEditMask));
|
||||
|
||||
AddInput(kAudioParamsInput, NodeValue::kAudioParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable | kInputFlagArray | kInputFlagHidden));
|
||||
|
||||
|
||||
@@ -200,8 +200,6 @@ public:
|
||||
static const QString kVideoAutoCacheInput;
|
||||
static const QString kAudioAutoCacheInput;
|
||||
|
||||
static const uint64_t kVideoParamEditMask;
|
||||
|
||||
signals:
|
||||
void FrameRateChanged(const rational&);
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
#include "core.h"
|
||||
#include "render/job/footagejob.h"
|
||||
#include "ui/icons/icons.h"
|
||||
#include "widget/videoparamedit/videoparamedit.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -144,39 +143,6 @@ void Footage::InputValueChangedEvent(const QString &input, int element)
|
||||
AddStream(Track::kVideo, QVariant::fromValue(vp));
|
||||
}
|
||||
|
||||
if (!footage_info.GetVideoStreams().isEmpty()) {
|
||||
// FIXME: This will break on multiple video streams. Currently we don't have
|
||||
// infrastructure for different properties per element. We'll see if this becomes
|
||||
// a problem.
|
||||
VideoParams vp = footage_info.GetVideoStreams().first();
|
||||
|
||||
uint64_t video_param_mask = 0;
|
||||
|
||||
video_param_mask |= VideoParamEdit::kEnabled;
|
||||
video_param_mask |= VideoParamEdit::kColorspace;
|
||||
video_param_mask |= VideoParamEdit::kPixelAspect;
|
||||
video_param_mask |= VideoParamEdit::kInterlacing;
|
||||
video_param_mask |= VideoParamEdit::kFrameRateIsArbitrary;
|
||||
|
||||
if (vp.channel_count() == VideoParams::kRGBAChannelCount) {
|
||||
// Add premultiplied setting if this footage has an alpha channel
|
||||
video_param_mask |= VideoParamEdit::kPremultipliedAlpha;
|
||||
}
|
||||
|
||||
if (vp.video_type() == VideoParams::kVideoTypeVideo) {
|
||||
// This is video, ensure that the frame rate does not overwrite the timebase
|
||||
video_param_mask |= VideoParamEdit::kFrameRateIsNotTimebase;
|
||||
} else {
|
||||
// This is not a video, so it's either a still image or an image sequence
|
||||
video_param_mask |= VideoParamEdit::kIsImageSequence;
|
||||
video_param_mask |= VideoParamEdit::kStartTime;
|
||||
video_param_mask |= VideoParamEdit::kEndTime;
|
||||
video_param_mask |= VideoParamEdit::kFrameRate;
|
||||
}
|
||||
|
||||
SetInputProperty(kVideoParamsInput, QStringLiteral("mask"), QVariant::fromValue(video_param_mask));
|
||||
}
|
||||
|
||||
for (int i=0; i<footage_info.GetAudioStreams().size(); i++) {
|
||||
AddStream(Track::kAudio, QVariant::fromValue(footage_info.GetAudioStreams().at(i)));
|
||||
}
|
||||
|
||||
@@ -39,6 +39,18 @@ public:
|
||||
virtual void DeselectAll() override;
|
||||
|
||||
public slots:
|
||||
void SetNode(Node *node)
|
||||
{
|
||||
// Convert single pointer to either an empty vector or a vector of one
|
||||
QVector<Node *> nodes;
|
||||
|
||||
if (node) {
|
||||
nodes.append(node);
|
||||
}
|
||||
|
||||
SetNodes(nodes);
|
||||
}
|
||||
|
||||
void SetNodes(const QVector<Node *> &nodes);
|
||||
|
||||
virtual void IncreaseTrackHeight() override;
|
||||
|
||||
@@ -51,7 +51,6 @@ add_subdirectory(timelinewidget)
|
||||
add_subdirectory(timeruler)
|
||||
add_subdirectory(timetarget)
|
||||
add_subdirectory(toolbar)
|
||||
add_subdirectory(videoparamedit)
|
||||
add_subdirectory(viewer)
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
|
||||
@@ -16,11 +16,9 @@
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
widget/curvewidget/beziercontrolpointitem.h
|
||||
widget/curvewidget/beziercontrolpointitem.cpp
|
||||
widget/curvewidget/curveview.h
|
||||
widget/curvewidget/curveview.cpp
|
||||
widget/curvewidget/curvewidget.h
|
||||
widget/curvewidget/curveview.h
|
||||
widget/curvewidget/curvewidget.cpp
|
||||
widget/curvewidget/curvewidget.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "beziercontrolpointitem.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QPainter>
|
||||
#include <QStyleOptionGraphicsItem>
|
||||
#include <QWidget>
|
||||
|
||||
#include "common/qtutils.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
BezierControlPointItem::BezierControlPointItem(NodeKeyframe* key, NodeKeyframe::BezierType mode, QGraphicsItem *parent) :
|
||||
QGraphicsRectItem(parent),
|
||||
key_(key),
|
||||
mode_(mode),
|
||||
x_scale_(1.0),
|
||||
y_scale_(1.0)
|
||||
{
|
||||
setFlag(QGraphicsItem::ItemIsMovable);
|
||||
|
||||
connect(key, &NodeKeyframe::TimeChanged, this, &BezierControlPointItem::UpdatePos);
|
||||
|
||||
if (mode_ == NodeKeyframe::kInHandle) {
|
||||
connect(key, &NodeKeyframe::BezierControlInChanged, this, &BezierControlPointItem::UpdatePos);
|
||||
} else {
|
||||
connect(key, &NodeKeyframe::BezierControlOutChanged, this, &BezierControlPointItem::UpdatePos);
|
||||
}
|
||||
|
||||
|
||||
int control_point_size = QtUtils::QFontMetricsWidth(qApp->fontMetrics(), "o");
|
||||
int half_sz = control_point_size / 2;
|
||||
setRect(-half_sz, -half_sz, control_point_size, control_point_size);
|
||||
}
|
||||
|
||||
void BezierControlPointItem::SetXScale(double scale)
|
||||
{
|
||||
x_scale_ = scale;
|
||||
UpdatePos();
|
||||
}
|
||||
|
||||
void BezierControlPointItem::SetYScale(double scale)
|
||||
{
|
||||
y_scale_ = scale;
|
||||
UpdatePos();
|
||||
}
|
||||
|
||||
NodeKeyframe* BezierControlPointItem::key() const
|
||||
{
|
||||
return key_;
|
||||
}
|
||||
|
||||
const NodeKeyframe::BezierType &BezierControlPointItem::mode() const
|
||||
{
|
||||
return mode_;
|
||||
}
|
||||
|
||||
QPointF BezierControlPointItem::GetCorrespondingKeyframeHandle() const
|
||||
{
|
||||
return key_->bezier_control(mode_);
|
||||
}
|
||||
|
||||
void BezierControlPointItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
|
||||
{
|
||||
if (option->state & QStyle::State_Selected) {
|
||||
painter->setPen(widget->palette().highlight().color());
|
||||
} else {
|
||||
painter->setPen(widget->palette().text().color());
|
||||
}
|
||||
|
||||
painter->drawEllipse(rect());
|
||||
}
|
||||
|
||||
void BezierControlPointItem::UpdatePos()
|
||||
{
|
||||
QPointF handle_offset = GetCorrespondingKeyframeHandle();
|
||||
|
||||
// Scale handle offset
|
||||
handle_offset.setX(handle_offset.x() * x_scale_);
|
||||
|
||||
// Flip the Y coordinate because bezier curves are drawn bottom to top
|
||||
handle_offset.setY(-handle_offset.y() * y_scale_);
|
||||
|
||||
setPos(handle_offset - rect().center());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef BEZIERCONTROLPOINTITEM_H
|
||||
#define BEZIERCONTROLPOINTITEM_H
|
||||
|
||||
#include <QGraphicsRectItem>
|
||||
|
||||
#include "node/keyframe.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class BezierControlPointItem : public QObject, public QGraphicsRectItem
|
||||
{
|
||||
public:
|
||||
BezierControlPointItem(NodeKeyframe* key, NodeKeyframe::BezierType mode, QGraphicsItem* parent = nullptr);
|
||||
|
||||
void SetXScale(double scale);
|
||||
|
||||
void SetYScale(double scale);
|
||||
|
||||
NodeKeyframe* key() const;
|
||||
|
||||
const NodeKeyframe::BezierType& mode() const;
|
||||
|
||||
QPointF GetCorrespondingKeyframeHandle() const;
|
||||
|
||||
void SetCorrespondingKeyframeHandle(const QPointF& handle);
|
||||
|
||||
void SetOpposingKeyframeHandle(const QPointF& handle);
|
||||
|
||||
protected:
|
||||
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
|
||||
|
||||
private:
|
||||
NodeKeyframe* key_;
|
||||
|
||||
NodeKeyframe::BezierType mode_;
|
||||
|
||||
double x_scale_;
|
||||
|
||||
double y_scale_;
|
||||
|
||||
private slots:
|
||||
void UpdatePos();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // BEZIERCONTROLPOINTITEM_H
|
||||
@@ -20,31 +20,33 @@
|
||||
|
||||
#include "curveview.h"
|
||||
|
||||
#include <cfloat>
|
||||
#include <QHash>
|
||||
#include <QMouseEvent>
|
||||
#include <QPainterPath>
|
||||
#include <QScrollBar>
|
||||
#include <QtMath>
|
||||
#include <cfloat>
|
||||
|
||||
#include "common/qtutils.h"
|
||||
#include "widget/keyframeview/keyframeviewundo.h"
|
||||
#include "widget/nodeparamview/nodeparamviewundo.h"
|
||||
#include "widget/slider/floatslider.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
#define super KeyframeViewBase
|
||||
#define super KeyframeView
|
||||
|
||||
CurveView::CurveView(QWidget *parent) :
|
||||
KeyframeViewBase(parent)
|
||||
KeyframeView(parent),
|
||||
dragging_bezier_pt_(nullptr)
|
||||
{
|
||||
setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
setDragMode(RubberBandDrag);
|
||||
setViewportUpdateMode(FullViewportUpdate);
|
||||
SetYAxisEnabled(true);
|
||||
SetAutoSelectSiblings(false);
|
||||
|
||||
text_padding_ = QtUtils::QFontMetricsWidth(fontMetrics(), QStringLiteral("i"));
|
||||
|
||||
minimum_grid_space_ = QtUtils::QFontMetricsWidth(fontMetrics(), QStringLiteral("00000"));
|
||||
|
||||
connect(scene(), &QGraphicsScene::selectionChanged, this, &CurveView::SelectionChanged);
|
||||
}
|
||||
|
||||
void CurveView::ConnectInput(const NodeKeyframeTrackReference& ref)
|
||||
@@ -90,7 +92,9 @@ void CurveView::SelectKeyframesOfInput(const NodeKeyframeTrackReference& ref)
|
||||
|
||||
void CurveView::ZoomToFitInput(const NodeKeyframeTrackReference& ref)
|
||||
{
|
||||
ZoomToFitInternal(track_connections_.value(ref)->GetKeyframes());
|
||||
if (KeyframeViewInputConnection *con = track_connections_.value(ref)) {
|
||||
ZoomToFitInternal(con->GetKeyframes());
|
||||
}
|
||||
}
|
||||
|
||||
void CurveView::SetKeyframeTrackColor(const NodeKeyframeTrackReference &ref, const QColor &color)
|
||||
@@ -98,8 +102,10 @@ void CurveView::SetKeyframeTrackColor(const NodeKeyframeTrackReference &ref, con
|
||||
// Insert color into hashmap
|
||||
keyframe_colors_.insert(ref, color);
|
||||
|
||||
// Update all keyframes
|
||||
track_connections_.value(ref)->SetBrush(color);
|
||||
if (KeyframeViewInputConnection *con = track_connections_.value(ref)) {
|
||||
// Update all keyframes
|
||||
con->SetBrush(color);
|
||||
}
|
||||
}
|
||||
|
||||
void CurveView::drawBackground(QPainter *painter, const QRectF &rect)
|
||||
@@ -235,40 +241,13 @@ void CurveView::drawBackground(QPainter *painter, const QRectF &rect)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw bezier control point lines
|
||||
/*if (!bezier_control_points_.isEmpty()) {
|
||||
painter->setPen(QPen(palette().text().color(), 1));
|
||||
|
||||
QVector<QLineF> bezier_lines;
|
||||
foreach (BezierControlPointItem* item, bezier_control_points_) {
|
||||
// All BezierControlPointItems should be children of a KeyframeViewItem
|
||||
KeyframeViewItem* par = static_cast<KeyframeViewItem*>(item->parentItem());
|
||||
|
||||
bezier_lines.append(QLineF(par->pos(), par->pos() + item->pos()));
|
||||
}
|
||||
painter->drawLines(bezier_lines);
|
||||
}*/
|
||||
}
|
||||
|
||||
void CurveView::ScaleChangedEvent(const double& scale)
|
||||
void CurveView::drawForeground(QPainter *painter, const QRectF &rect)
|
||||
{
|
||||
KeyframeViewBase::ScaleChangedEvent(scale);
|
||||
bezier_pts_.clear();
|
||||
|
||||
foreach (BezierControlPointItem* item, bezier_control_points_) {
|
||||
item->SetXScale(scale);
|
||||
}
|
||||
}
|
||||
|
||||
void CurveView::VerticalScaleChangedEvent(double scale)
|
||||
{
|
||||
Q_UNUSED(scale)
|
||||
|
||||
foreach (BezierControlPointItem* item, bezier_control_points_) {
|
||||
item->SetYScale(scale);
|
||||
}
|
||||
|
||||
viewport()->update();
|
||||
super::drawForeground(painter, rect);
|
||||
}
|
||||
|
||||
void CurveView::ContextMenuEvent(Menu &m)
|
||||
@@ -290,6 +269,225 @@ void CurveView::SceneRectUpdateEvent(QRectF &r)
|
||||
r.setBottom(r.bottom() + this->height());
|
||||
}
|
||||
|
||||
qreal CurveView::GetKeyframeSceneY(KeyframeViewInputConnection *track, NodeKeyframe *key)
|
||||
{
|
||||
return GetItemYFromKeyframeValue(key);
|
||||
}
|
||||
|
||||
void CurveView::DrawKeyframe(QPainter *painter, NodeKeyframe *key, KeyframeViewInputConnection *track, const QRectF &key_rect)
|
||||
{
|
||||
if (IsKeyframeSelected(key) && key->type() == NodeKeyframe::kBezier) {
|
||||
// Draw bezier control points if keyframe is selected
|
||||
int control_point_size = QtUtils::QFontMetricsWidth(fontMetrics(), "o");
|
||||
int half_sz = control_point_size / 2;
|
||||
QRectF control_point_rect(-half_sz, -half_sz, control_point_size, control_point_size);
|
||||
|
||||
painter->setPen(palette().text().color());
|
||||
painter->setBrush(Qt::NoBrush);
|
||||
|
||||
QRectF cp_in = control_point_rect.translated(key_rect.center() + ScalePoint(key->bezier_control_in()));
|
||||
QRectF cp_out = control_point_rect.translated(key_rect.center() + ScalePoint(key->bezier_control_out()));
|
||||
|
||||
painter->drawLine(key_rect.center(), cp_in.center());
|
||||
painter->drawLine(key_rect.center(), cp_out.center());
|
||||
|
||||
painter->drawEllipse(cp_in);
|
||||
painter->drawEllipse(cp_out);
|
||||
|
||||
bezier_pts_.append({cp_in, key, NodeKeyframe::kInHandle});
|
||||
bezier_pts_.append({cp_out, key, NodeKeyframe::kOutHandle});
|
||||
}
|
||||
|
||||
super::DrawKeyframe(painter, key, track, key_rect);
|
||||
}
|
||||
|
||||
bool CurveView::FirstChanceMousePress(QMouseEvent *event)
|
||||
{
|
||||
dragging_bezier_pt_ = nullptr;
|
||||
QPointF scene_pt = mapToScene(event->pos());
|
||||
foreach (const BezierPoint &b, bezier_pts_) {
|
||||
if (b.rect.contains(scene_pt)) {
|
||||
dragging_bezier_pt_ = &b;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (dragging_bezier_pt_) {
|
||||
NodeKeyframe *key = dragging_bezier_pt_->keyframe;
|
||||
dragging_bezier_point_start_ = (dragging_bezier_pt_->type == NodeKeyframe::kInHandle) ? key->bezier_control_in() : key->bezier_control_out();
|
||||
dragging_bezier_point_opposing_start_ = (dragging_bezier_pt_->type == NodeKeyframe::kInHandle) ? key->bezier_control_out() : key->bezier_control_in();
|
||||
|
||||
drag_start_ = mapToScene(event->pos());
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void CurveView::FirstChanceMouseMove(QMouseEvent *event)
|
||||
{
|
||||
// Calculate cursor difference and scale it
|
||||
QPointF scene_pos = mapToScene(event->pos());
|
||||
QPointF mouse_diff_scaled = GetScaledCursorPos(scene_pos - drag_start_);
|
||||
|
||||
if (event->modifiers() & Qt::ShiftModifier) {
|
||||
// If holding shift, only move one axis
|
||||
mouse_diff_scaled.setY(0);
|
||||
}
|
||||
|
||||
// Flip the mouse Y because bezier control points are drawn bottom to top, not top to bottom
|
||||
mouse_diff_scaled.setY(-mouse_diff_scaled.y());
|
||||
|
||||
QPointF new_bezier_pos = GenerateBezierControlPosition(dragging_bezier_pt_->type,
|
||||
dragging_bezier_point_start_,
|
||||
mouse_diff_scaled);
|
||||
|
||||
// If the user is NOT holding control, we set the other handle to the exact negative of this handle
|
||||
QPointF new_opposing_pos;
|
||||
NodeKeyframe::BezierType opposing_type = NodeKeyframe::get_opposing_bezier_type(dragging_bezier_pt_->type);
|
||||
|
||||
|
||||
if (!(event->modifiers() & Qt::ControlModifier)) {
|
||||
new_opposing_pos = GenerateBezierControlPosition(opposing_type,
|
||||
dragging_bezier_point_opposing_start_,
|
||||
-mouse_diff_scaled);
|
||||
} else {
|
||||
new_opposing_pos = dragging_bezier_point_opposing_start_;
|
||||
}
|
||||
|
||||
dragging_bezier_pt_->keyframe->set_bezier_control(dragging_bezier_pt_->type,
|
||||
new_bezier_pos);
|
||||
|
||||
dragging_bezier_pt_->keyframe->set_bezier_control(opposing_type,
|
||||
new_opposing_pos);
|
||||
|
||||
Redraw();
|
||||
}
|
||||
|
||||
void CurveView::FirstChanceMouseRelease(QMouseEvent *event)
|
||||
{
|
||||
MultiUndoCommand* command = new MultiUndoCommand();
|
||||
|
||||
// Create undo command with the current bezier point and the old one
|
||||
command->add_child(new KeyframeSetBezierControlPoint(dragging_bezier_pt_->keyframe,
|
||||
dragging_bezier_pt_->type,
|
||||
dragging_bezier_pt_->keyframe->bezier_control(dragging_bezier_pt_->type),
|
||||
dragging_bezier_point_start_));
|
||||
|
||||
if (!(event->modifiers() & Qt::ControlModifier)) {
|
||||
auto opposing_type = NodeKeyframe::get_opposing_bezier_type(dragging_bezier_pt_->type);
|
||||
|
||||
command->add_child(new KeyframeSetBezierControlPoint(dragging_bezier_pt_->keyframe,
|
||||
opposing_type,
|
||||
dragging_bezier_pt_->keyframe->bezier_control(opposing_type),
|
||||
dragging_bezier_point_opposing_start_));
|
||||
}
|
||||
|
||||
dragging_bezier_pt_ = nullptr;
|
||||
|
||||
Core::instance()->undo_stack()->push(command);
|
||||
}
|
||||
|
||||
void CurveView::KeyframeDragStart(QMouseEvent *event)
|
||||
{
|
||||
drag_keyframe_values_.resize(GetSelectedKeyframes().size());
|
||||
for (int i=0; i<GetSelectedKeyframes().size(); i++) {
|
||||
drag_keyframe_values_[i] = GetSelectedKeyframes().at(i)->value();
|
||||
}
|
||||
|
||||
drag_start_ = mapToScene(event->pos());
|
||||
}
|
||||
|
||||
void CurveView::KeyframeDragMove(QMouseEvent *event, QString &tip)
|
||||
{
|
||||
if (event->modifiers() & Qt::ShiftModifier) {
|
||||
// Lock to X axis only
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate cursor difference
|
||||
double scaled_diff = (mapToScene(event->pos()).y() - drag_start_.y()) / GetYScale();
|
||||
|
||||
// Validate movement - ensure no keyframe goes above its max point or below its min point
|
||||
for (int i=0; i<GetSelectedKeyframes().size(); i++) {
|
||||
NodeKeyframe *key = GetSelectedKeyframes().at(i);
|
||||
|
||||
Node* node = key->parent();
|
||||
double original_val = drag_keyframe_values_.at(i).toDouble();
|
||||
const QString& input = key->input();
|
||||
double new_val = original_val - scaled_diff;
|
||||
double limited = new_val;
|
||||
|
||||
if (node->HasInputProperty(input, QStringLiteral("min"))) {
|
||||
limited = qMax(limited, node->GetInputProperty(input, QStringLiteral("min")).toDouble());
|
||||
}
|
||||
|
||||
if (node->HasInputProperty(input, QStringLiteral("max"))) {
|
||||
limited = qMin(limited, node->GetInputProperty(input, QStringLiteral("max")).toDouble());
|
||||
}
|
||||
|
||||
if (limited != new_val) {
|
||||
scaled_diff = original_val - limited;
|
||||
}
|
||||
}
|
||||
|
||||
// Set values
|
||||
for (int i=0; i<GetSelectedKeyframes().size(); i++) {
|
||||
NodeKeyframe *key = GetSelectedKeyframes().at(i);
|
||||
key->set_value(drag_keyframe_values_.at(i).toDouble() - scaled_diff);
|
||||
}
|
||||
|
||||
NodeKeyframe *tip_item = GetSelectedKeyframes().first();
|
||||
FloatSlider::DisplayType display_type = FloatSlider::kNormal;
|
||||
Node* initial_drag_input = tip_item->parent();
|
||||
const QString& initial_drag_input_id = tip_item->input();
|
||||
if (initial_drag_input->HasInputProperty(initial_drag_input_id, QStringLiteral("view"))) {
|
||||
display_type = static_cast<FloatSlider::DisplayType>(initial_drag_input->GetInputProperty(initial_drag_input_id, QStringLiteral("view")).toInt());
|
||||
}
|
||||
|
||||
bool ok;
|
||||
double num_value = tip_item->value().toDouble(&ok);
|
||||
|
||||
if (ok) {
|
||||
tip = QStringLiteral("%1\n");
|
||||
tip.append(FloatSlider::ValueToString(num_value, display_type, 2, true));
|
||||
}
|
||||
}
|
||||
|
||||
void CurveView::KeyframeDragRelease(QMouseEvent *event, MultiUndoCommand *command)
|
||||
{
|
||||
for (int i=0; i<GetSelectedKeyframes().size(); i++) {
|
||||
NodeKeyframe *k = GetSelectedKeyframes().at(i);
|
||||
command->add_child(new NodeParamSetKeyframeValueCommand(k, k->value(), drag_keyframe_values_.at(i)));
|
||||
}
|
||||
}
|
||||
|
||||
QPointF CurveView::GenerateBezierControlPosition(const NodeKeyframe::BezierType mode, const QPointF &start_point, const QPointF &scaled_cursor_diff)
|
||||
{
|
||||
QPointF new_bezier_pos = start_point;
|
||||
|
||||
new_bezier_pos += scaled_cursor_diff;
|
||||
|
||||
// LIMIT bezier handles from overlapping each other
|
||||
if (mode == NodeKeyframe::kInHandle) {
|
||||
if (new_bezier_pos.x() > 0) {
|
||||
new_bezier_pos.setX(0);
|
||||
}
|
||||
} else {
|
||||
if (new_bezier_pos.x() < 0) {
|
||||
new_bezier_pos.setX(0);
|
||||
}
|
||||
}
|
||||
|
||||
return new_bezier_pos;
|
||||
}
|
||||
|
||||
QPointF CurveView::GetScaledCursorPos(const QPointF &cursor_pos)
|
||||
{
|
||||
return QPointF(cursor_pos.x() / GetScale(),
|
||||
cursor_pos.y() / GetYScale());
|
||||
}
|
||||
|
||||
void CurveView::ZoomToFitInternal(const QVector<NodeKeyframe *> &keys)
|
||||
{
|
||||
if (keys.isEmpty()) {
|
||||
@@ -345,36 +543,11 @@ QPointF CurveView::ScalePoint(const QPointF &point)
|
||||
return QPointF(point.x() * GetScale(), - point.y() * GetYScale());
|
||||
}
|
||||
|
||||
void CurveView::CreateBezierControlPoints(NodeKeyframe* item)
|
||||
{
|
||||
qDebug() << "STUB!";
|
||||
/*BezierControlPointItem* bezier_in_pt = new BezierControlPointItem(item, NodeKeyframe::kInHandle, item);
|
||||
bezier_in_pt->SetXScale(GetScale());
|
||||
bezier_in_pt->SetYScale(GetYScale());
|
||||
bezier_control_points_.append(bezier_in_pt);
|
||||
connect(bezier_in_pt, &QObject::destroyed, this, &CurveView::BezierControlPointDestroyed, Qt::DirectConnection);
|
||||
|
||||
BezierControlPointItem* bezier_out_pt = new BezierControlPointItem(item, NodeKeyframe::kOutHandle, item);
|
||||
bezier_out_pt->SetXScale(GetScale());
|
||||
bezier_out_pt->SetYScale(GetYScale());
|
||||
bezier_control_points_.append(bezier_out_pt);
|
||||
connect(bezier_out_pt, &QObject::destroyed, this, &CurveView::BezierControlPointDestroyed, Qt::DirectConnection);*/
|
||||
}
|
||||
|
||||
QPointF CurveView::GetKeyframePosition(NodeKeyframe *key)
|
||||
{
|
||||
return QPointF(GetKeyframeSceneX(key), GetItemYFromKeyframeValue(key));
|
||||
}
|
||||
|
||||
void CurveView::KeyframeValueChanged()
|
||||
{
|
||||
qDebug() << "STUB!";
|
||||
/*NodeKeyframe* key = static_cast<NodeKeyframe*>(sender());
|
||||
KeyframeViewItem* item = item_map().value(key);
|
||||
|
||||
SetItemYFromKeyframeValue(key, item);*/
|
||||
}
|
||||
|
||||
void CurveView::KeyframeTypeChanged()
|
||||
{
|
||||
qDebug() << "STUB!";
|
||||
@@ -387,33 +560,6 @@ void CurveView::KeyframeTypeChanged()
|
||||
}*/
|
||||
}
|
||||
|
||||
void CurveView::SelectionChanged()
|
||||
{
|
||||
qDebug() << "STUB!";
|
||||
/*
|
||||
// Clear current bezier handles
|
||||
while (!bezier_control_points_.isEmpty()) {
|
||||
delete bezier_control_points_.first();
|
||||
}
|
||||
|
||||
QList<QGraphicsItem*> selected = scene()->selectedItems();
|
||||
|
||||
foreach (QGraphicsItem* item, selected) {
|
||||
KeyframeViewItem* this_item = static_cast<KeyframeViewItem*>(item);
|
||||
|
||||
if (this_item->key()->type() == NodeKeyframe::kBezier) {
|
||||
CreateBezierControlPoints(this_item);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void CurveView::BezierControlPointDestroyed()
|
||||
{
|
||||
BezierControlPointItem* item = static_cast<BezierControlPointItem*>(sender());
|
||||
bezier_control_points_.removeOne(item);
|
||||
}
|
||||
|
||||
void CurveView::ZoomToFit()
|
||||
{
|
||||
QVector<NodeKeyframe*> keys;
|
||||
|
||||
@@ -21,13 +21,12 @@
|
||||
#ifndef CURVEVIEW_H
|
||||
#define CURVEVIEW_H
|
||||
|
||||
#include "beziercontrolpointitem.h"
|
||||
#include "node/keyframe.h"
|
||||
#include "widget/keyframeview/keyframeview.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class CurveView : public KeyframeViewBase
|
||||
class CurveView : public KeyframeView
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
@@ -52,15 +51,24 @@ public slots:
|
||||
|
||||
protected:
|
||||
virtual void drawBackground(QPainter* painter, const QRectF& rect) override;
|
||||
|
||||
virtual void ScaleChangedEvent(const double &scale) override;
|
||||
|
||||
virtual void VerticalScaleChangedEvent(double scale) override;
|
||||
virtual void drawForeground(QPainter *painter, const QRectF &rect) override;
|
||||
|
||||
virtual void ContextMenuEvent(Menu &m) override;
|
||||
|
||||
virtual void SceneRectUpdateEvent(QRectF &r) override;
|
||||
|
||||
virtual qreal GetKeyframeSceneY(KeyframeViewInputConnection *track, NodeKeyframe *key) override;
|
||||
|
||||
virtual void DrawKeyframe(QPainter *painter, NodeKeyframe *key, KeyframeViewInputConnection *track, const QRectF &key_rect) override;
|
||||
|
||||
virtual bool FirstChanceMousePress(QMouseEvent *event) override;
|
||||
virtual void FirstChanceMouseMove(QMouseEvent *event) override;
|
||||
virtual void FirstChanceMouseRelease(QMouseEvent *event) override;
|
||||
|
||||
virtual void KeyframeDragStart(QMouseEvent *event) override;
|
||||
virtual void KeyframeDragMove(QMouseEvent *event, QString &tip) override;
|
||||
virtual void KeyframeDragRelease(QMouseEvent *event, MultiUndoCommand *command) override;
|
||||
|
||||
private:
|
||||
void ZoomToFitInternal(const QVector<NodeKeyframe *> &keys);
|
||||
|
||||
@@ -71,10 +79,14 @@ private:
|
||||
|
||||
void AdjustLines();
|
||||
|
||||
void CreateBezierControlPoints(NodeKeyframe *item);
|
||||
|
||||
QPointF GetKeyframePosition(NodeKeyframe *key);
|
||||
|
||||
static QPointF GenerateBezierControlPosition(const NodeKeyframe::BezierType mode,
|
||||
const QPointF& start_point,
|
||||
const QPointF& scaled_cursor_diff);
|
||||
|
||||
QPointF GetScaledCursorPos(const QPointF &cursor_pos);
|
||||
|
||||
QHash<NodeKeyframeTrackReference, QColor> keyframe_colors_;
|
||||
QHash<NodeKeyframeTrackReference, KeyframeViewInputConnection*> track_connections_;
|
||||
|
||||
@@ -82,21 +94,27 @@ private:
|
||||
|
||||
int minimum_grid_space_;
|
||||
|
||||
QVector<QGraphicsLineItem*> lines_;
|
||||
|
||||
QVector<BezierControlPointItem*> bezier_control_points_;
|
||||
|
||||
QVector<NodeKeyframeTrackReference> connected_inputs_;
|
||||
|
||||
struct BezierPoint
|
||||
{
|
||||
QRectF rect;
|
||||
NodeKeyframe *keyframe;
|
||||
NodeKeyframe::BezierType type;
|
||||
};
|
||||
|
||||
QVector<BezierPoint> bezier_pts_;
|
||||
const BezierPoint *dragging_bezier_pt_;
|
||||
|
||||
QPointF dragging_bezier_point_start_;
|
||||
QPointF dragging_bezier_point_opposing_start_;
|
||||
QPointF drag_start_;
|
||||
|
||||
QVector<QVariant> drag_keyframe_values_;
|
||||
|
||||
private slots:
|
||||
void KeyframeValueChanged();
|
||||
|
||||
void KeyframeTypeChanged();
|
||||
|
||||
void SelectionChanged();
|
||||
|
||||
void BezierControlPointDestroyed();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -34,8 +34,10 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
#define super TimeBasedWidget
|
||||
|
||||
CurveWidget::CurveWidget(QWidget *parent) :
|
||||
TimeBasedWidget(parent)
|
||||
super(parent)
|
||||
{
|
||||
QHBoxLayout* outer_layout = new QHBoxLayout(this);
|
||||
|
||||
@@ -152,7 +154,7 @@ void CurveWidget::SetNodes(const QVector<Node *> &nodes)
|
||||
|
||||
void CurveWidget::TimeChangedEvent(const rational &time)
|
||||
{
|
||||
TimeBasedWidget::TimeChangedEvent(time);
|
||||
super::TimeChangedEvent(time);
|
||||
|
||||
view_->SetTime(time);
|
||||
UpdateBridgeTime(time);
|
||||
@@ -160,14 +162,14 @@ void CurveWidget::TimeChangedEvent(const rational &time)
|
||||
|
||||
void CurveWidget::TimebaseChangedEvent(const rational &timebase)
|
||||
{
|
||||
TimeBasedWidget::TimebaseChangedEvent(timebase);
|
||||
super::TimebaseChangedEvent(timebase);
|
||||
|
||||
view_->SetTimebase(timebase);
|
||||
}
|
||||
|
||||
void CurveWidget::ScaleChangedEvent(const double &scale)
|
||||
{
|
||||
TimeBasedWidget::ScaleChangedEvent(scale);
|
||||
super::ScaleChangedEvent(scale);
|
||||
|
||||
view_->SetScale(scale);
|
||||
}
|
||||
@@ -238,7 +240,7 @@ void CurveWidget::ConnectInput(Node *node, const QString &input, bool connect)
|
||||
NodeKeyframeTrackReference ref(NodeInput(node, input, i), j);
|
||||
|
||||
if (!keyframe_colors_.contains(ref)) {
|
||||
QColor c = QColor::fromHsv(std::rand()%360, std::rand()%255, 255);
|
||||
QColor c = QColor::fromHsl(std::rand()%360, 255, 160);
|
||||
|
||||
keyframe_colors_.insert(ref, c);
|
||||
tree_view_->SetKeyframeTrackColor(ref, c);
|
||||
|
||||
@@ -18,8 +18,6 @@ set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
widget/keyframeview/keyframeview.cpp
|
||||
widget/keyframeview/keyframeview.h
|
||||
widget/keyframeview/keyframeviewbase.cpp
|
||||
widget/keyframeview/keyframeviewbase.h
|
||||
widget/keyframeview/keyframeviewinputconnection.cpp
|
||||
widget/keyframeview/keyframeviewinputconnection.h
|
||||
widget/keyframeview/keyframeviewundo.cpp
|
||||
|
||||
@@ -20,15 +20,334 @@
|
||||
|
||||
#include "keyframeview.h"
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include <QToolTip>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "common/qtutils.h"
|
||||
#include "dialog/keyframeproperties/keyframeproperties.h"
|
||||
#include "keyframeviewundo.h"
|
||||
#include "node/node.h"
|
||||
#include "widget/menu/menu.h"
|
||||
#include "widget/menu/menushared.h"
|
||||
#include "widget/nodeparamview/nodeparamviewundo.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
#define super KeyframeViewBase
|
||||
#define super TimeBasedView
|
||||
|
||||
KeyframeView::KeyframeView(QWidget *parent) :
|
||||
KeyframeViewBase(parent),
|
||||
max_scroll_(0)
|
||||
super(parent),
|
||||
selection_manager_(this),
|
||||
autoselect_siblings_(true),
|
||||
max_scroll_(0),
|
||||
first_chance_mouse_event_(false)
|
||||
{
|
||||
setAlignment(Qt::AlignLeft | Qt::AlignTop);
|
||||
SetDefaultDragMode(RubberBandDrag);
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
connect(this, &KeyframeView::customContextMenuRequested, this, &KeyframeView::ShowContextMenu);
|
||||
}
|
||||
|
||||
void KeyframeView::DeleteSelected()
|
||||
{
|
||||
MultiUndoCommand* command = new MultiUndoCommand();
|
||||
|
||||
foreach (NodeKeyframe *key, GetSelectedKeyframes()) {
|
||||
command->add_child(new NodeParamRemoveKeyframeCommand(key));
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
}
|
||||
|
||||
KeyframeView::NodeConnections KeyframeView::AddKeyframesOfNode(Node *n)
|
||||
{
|
||||
NodeConnections map;
|
||||
|
||||
foreach (const QString& i, n->inputs()) {
|
||||
map.insert(i, AddKeyframesOfInput(n, i));
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
KeyframeView::InputConnections KeyframeView::AddKeyframesOfInput(Node* n, const QString& input)
|
||||
{
|
||||
InputConnections vec;
|
||||
|
||||
if (n->IsInputKeyframable(input)) {
|
||||
int arr_sz = n->InputArraySize(input);
|
||||
vec.resize(arr_sz + 1);
|
||||
for (int i=-1; i<arr_sz; i++) {
|
||||
vec[i+1] = AddKeyframesOfElement(NodeInput(n, input, i));
|
||||
}
|
||||
}
|
||||
|
||||
return vec;
|
||||
}
|
||||
|
||||
KeyframeView::ElementConnections KeyframeView::AddKeyframesOfElement(const NodeInput& input)
|
||||
{
|
||||
const QVector<NodeKeyframeTrack>& tracks = input.node()->GetKeyframeTracks(input);
|
||||
ElementConnections vec(tracks.size());
|
||||
|
||||
for (int i=0; i<tracks.size(); i++) {
|
||||
vec[i] = AddKeyframesOfTrack(NodeKeyframeTrackReference(input, i));
|
||||
}
|
||||
|
||||
return vec;
|
||||
}
|
||||
|
||||
KeyframeViewInputConnection *KeyframeView::AddKeyframesOfTrack(const NodeKeyframeTrackReference& ref)
|
||||
{
|
||||
KeyframeViewInputConnection *track = new KeyframeViewInputConnection(ref, this);
|
||||
connect(track, &KeyframeViewInputConnection::RequireUpdate, this, &KeyframeView::Redraw);
|
||||
tracks_.append(track);
|
||||
Redraw();
|
||||
return track;
|
||||
}
|
||||
|
||||
void KeyframeView::RemoveKeyframesOfTrack(KeyframeViewInputConnection *connection)
|
||||
{
|
||||
if (tracks_.removeOne(connection)) {
|
||||
foreach (NodeKeyframe *key, connection->GetKeyframes()) {
|
||||
selection_manager_.Deselect(key);
|
||||
}
|
||||
delete connection;
|
||||
Redraw();
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeView::SelectAll()
|
||||
{
|
||||
foreach (KeyframeViewInputConnection *track, tracks_) {
|
||||
foreach (NodeKeyframe *key, track->GetKeyframes()) {
|
||||
SelectKeyframe(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeView::DeselectAll()
|
||||
{
|
||||
selection_manager_.ClearSelection();
|
||||
|
||||
Redraw();
|
||||
}
|
||||
|
||||
void KeyframeView::Clear()
|
||||
{
|
||||
if (!tracks_.isEmpty()) {
|
||||
qDeleteAll(tracks_);
|
||||
tracks_.clear();
|
||||
Redraw();
|
||||
}
|
||||
|
||||
selection_manager_.ClearSelection();
|
||||
}
|
||||
|
||||
void KeyframeView::SelectionManagerSelectEvent(void *obj)
|
||||
{
|
||||
if (autoselect_siblings_) {
|
||||
NodeKeyframe *key = static_cast<NodeKeyframe*>(obj);
|
||||
QVector<NodeKeyframe*> keys = key->parent()->GetKeyframesAtTime(key->input(), key->time(), key->element());
|
||||
foreach (NodeKeyframe* k, keys) {
|
||||
if (k != key) {
|
||||
SelectKeyframe(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeView::SelectionManagerDeselectEvent(void *obj)
|
||||
{
|
||||
if (autoselect_siblings_) {
|
||||
NodeKeyframe *key = static_cast<NodeKeyframe*>(obj);
|
||||
QVector<NodeKeyframe*> keys = key->parent()->GetKeyframesAtTime(key->input(), key->time(), key->element());
|
||||
foreach (NodeKeyframe* k, keys) {
|
||||
if (k != key) {
|
||||
DeselectKeyframe(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeView::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
NodeKeyframe *key_under_cursor = selection_manager_.GetObjectAtPoint(event->pos());
|
||||
|
||||
if (HandPress(event) || (!key_under_cursor && PlayheadPress(event))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Do mouse press things
|
||||
if (FirstChanceMousePress(event)) {
|
||||
first_chance_mouse_event_ = true;
|
||||
} else if (NodeKeyframe *initial_key = selection_manager_.MousePress(event)) {
|
||||
selection_manager_.DragStart(initial_key, event);
|
||||
KeyframeDragStart(event);
|
||||
} else {
|
||||
selection_manager_.RubberBandStart(event);
|
||||
}
|
||||
|
||||
// Update view
|
||||
Redraw();
|
||||
}
|
||||
|
||||
void KeyframeView::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
if (HandMove(event) || PlayheadMove(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (first_chance_mouse_event_) {
|
||||
FirstChanceMouseMove(event);
|
||||
} else if (selection_manager_.IsDragging()) {
|
||||
QString tip;
|
||||
KeyframeDragMove(event, tip);
|
||||
selection_manager_.DragMove(event, tip);
|
||||
} else if (selection_manager_.IsRubberBanding()) {
|
||||
selection_manager_.RubberBandMove(event);
|
||||
Redraw();
|
||||
}
|
||||
|
||||
if (event->buttons()) {
|
||||
// Signal cursor pos in case we should scroll to catch up to it
|
||||
QPointF scene_pos = mapToScene(event->pos());
|
||||
emit Dragged(scene_pos.x(), scene_pos.y());
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeView::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if (HandRelease(event) || PlayheadRelease(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (first_chance_mouse_event_) {
|
||||
FirstChanceMouseRelease(event);
|
||||
first_chance_mouse_event_ = false;
|
||||
} else if (selection_manager_.IsDragging()) {
|
||||
MultiUndoCommand* command = new MultiUndoCommand();
|
||||
selection_manager_.DragStop(command);
|
||||
KeyframeDragRelease(event, command);
|
||||
Core::instance()->undo_stack()->push(command);
|
||||
} else if (selection_manager_.IsRubberBanding()) {
|
||||
selection_manager_.RubberBandStop();
|
||||
Redraw();
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeView::drawForeground(QPainter *painter, const QRectF &rect)
|
||||
{
|
||||
int key_sz = QtUtils::QFontMetricsWidth(fontMetrics(), "Oi");
|
||||
int key_rad = key_sz/2;
|
||||
|
||||
selection_manager_.ClearDrawnObjects();
|
||||
|
||||
painter->setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
foreach (KeyframeViewInputConnection *track, tracks_) {
|
||||
foreach (NodeKeyframe *key, track->GetKeyframes()) {
|
||||
QRectF key_rect(-key_rad, -key_rad, key_sz, key_sz);
|
||||
key_rect.translate(GetKeyframeSceneX(key), GetKeyframeSceneY(track, key));
|
||||
|
||||
if (!rect.intersects(key_rect)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DrawKeyframe(painter, key, track, key_rect);
|
||||
}
|
||||
}
|
||||
|
||||
super::drawForeground(painter, rect);
|
||||
}
|
||||
|
||||
void KeyframeView::DrawKeyframe(QPainter *painter, NodeKeyframe *key, KeyframeViewInputConnection *track, const QRectF &key_rect)
|
||||
{
|
||||
painter->setPen(Qt::black);
|
||||
|
||||
if (IsKeyframeSelected(key)) {
|
||||
painter->setBrush(palette().highlight());
|
||||
} else {
|
||||
painter->setBrush(track->GetBrush());
|
||||
}
|
||||
|
||||
selection_manager_.DeclareDrawnObject(key, key_rect);
|
||||
|
||||
switch (key->type()) {
|
||||
case NodeKeyframe::kLinear:
|
||||
{
|
||||
QPointF points[] = {
|
||||
QPointF(key_rect.center().x(), key_rect.top()),
|
||||
QPointF(key_rect.right(), key_rect.center().y()),
|
||||
QPointF(key_rect.center().x(), key_rect.bottom()),
|
||||
QPointF(key_rect.left(), key_rect.center().y())
|
||||
};
|
||||
|
||||
painter->drawPolygon(points, 4);
|
||||
break;
|
||||
}
|
||||
case NodeKeyframe::kBezier:
|
||||
painter->drawEllipse(key_rect);
|
||||
break;
|
||||
case NodeKeyframe::kHold:
|
||||
painter->drawRect(key_rect);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeView::ScaleChangedEvent(const double &scale)
|
||||
{
|
||||
super::ScaleChangedEvent(scale);
|
||||
|
||||
Redraw();
|
||||
}
|
||||
|
||||
void KeyframeView::TimeTargetChangedEvent(Node *target)
|
||||
{
|
||||
Redraw();
|
||||
}
|
||||
|
||||
void KeyframeView::TimebaseChangedEvent(const rational &timebase)
|
||||
{
|
||||
super::TimebaseChangedEvent(timebase);
|
||||
|
||||
selection_manager_.SetTimebase(timebase);
|
||||
}
|
||||
|
||||
void KeyframeView::ContextMenuEvent(Menu& m)
|
||||
{
|
||||
Q_UNUSED(m)
|
||||
}
|
||||
|
||||
void KeyframeView::SelectKeyframe(NodeKeyframe *key)
|
||||
{
|
||||
if (selection_manager_.Select(key)) {
|
||||
Redraw();
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeView::DeselectKeyframe(NodeKeyframe *key)
|
||||
{
|
||||
if (selection_manager_.Deselect(key)) {
|
||||
Redraw();
|
||||
}
|
||||
}
|
||||
|
||||
rational KeyframeView::GetAdjustedKeyframeTime(NodeKeyframe *key)
|
||||
{
|
||||
return GetAdjustedTime(key->parent(), GetTimeTarget(), key->time(), false);
|
||||
}
|
||||
|
||||
double KeyframeView::GetKeyframeSceneX(NodeKeyframe *key)
|
||||
{
|
||||
return TimeToScene(GetAdjustedKeyframeTime(key));
|
||||
}
|
||||
|
||||
qreal KeyframeView::GetKeyframeSceneY(KeyframeViewInputConnection *track, NodeKeyframe *key)
|
||||
{
|
||||
return mapFromGlobal(QPoint(0, track->GetKeyframeY())).y();
|
||||
}
|
||||
|
||||
void KeyframeView::SceneRectUpdateEvent(QRectF &rect)
|
||||
@@ -37,4 +356,108 @@ void KeyframeView::SceneRectUpdateEvent(QRectF &rect)
|
||||
rect.setHeight(max_scroll_);
|
||||
}
|
||||
|
||||
rational KeyframeView::CalculateNewTimeFromScreen(const rational &old_time, double cursor_diff)
|
||||
{
|
||||
return rational::fromDouble(old_time.toDouble() + cursor_diff);
|
||||
}
|
||||
|
||||
void KeyframeView::ShowContextMenu()
|
||||
{
|
||||
Menu m;
|
||||
|
||||
MenuShared::instance()->AddItemsForEditMenu(&m, false);
|
||||
|
||||
QAction* linear_key_action = nullptr;
|
||||
QAction* bezier_key_action = nullptr;
|
||||
QAction* hold_key_action = nullptr;
|
||||
|
||||
if (!GetSelectedKeyframes().isEmpty()) {
|
||||
bool all_keys_are_same_type = true;
|
||||
NodeKeyframe::Type type = GetSelectedKeyframes().first()->type();
|
||||
|
||||
for (int i=1;i<GetSelectedKeyframes().size();i++) {
|
||||
NodeKeyframe* key_item = GetSelectedKeyframes().at(i);
|
||||
NodeKeyframe* prev_item = GetSelectedKeyframes().at(i-1);
|
||||
|
||||
if (key_item->type() != prev_item->type()) {
|
||||
all_keys_are_same_type = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m.addSeparator();
|
||||
|
||||
linear_key_action = m.addAction(tr("Linear"));
|
||||
bezier_key_action = m.addAction(tr("Bezier"));
|
||||
hold_key_action = m.addAction(tr("Hold"));
|
||||
|
||||
if (all_keys_are_same_type) {
|
||||
switch (type) {
|
||||
case NodeKeyframe::kLinear:
|
||||
linear_key_action->setChecked(true);
|
||||
break;
|
||||
case NodeKeyframe::kBezier:
|
||||
bezier_key_action->setChecked(true);
|
||||
break;
|
||||
case NodeKeyframe::kHold:
|
||||
hold_key_action->setChecked(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m.addSeparator();
|
||||
|
||||
AddSetScrollZoomsByDefaultActionToMenu(&m);
|
||||
|
||||
m.addSeparator();
|
||||
|
||||
ContextMenuEvent(m);
|
||||
|
||||
if (!GetSelectedKeyframes().isEmpty()) {
|
||||
m.addSeparator();
|
||||
|
||||
QAction* properties_action = m.addAction(tr("P&roperties"));
|
||||
connect(properties_action, &QAction::triggered, this, &KeyframeView::ShowKeyframePropertiesDialog);
|
||||
}
|
||||
|
||||
QAction* selected = m.exec(QCursor::pos());
|
||||
|
||||
// Process keyframe type changes
|
||||
if (selected) {
|
||||
if (selected == linear_key_action
|
||||
|| selected == bezier_key_action
|
||||
|| selected == hold_key_action) {
|
||||
NodeKeyframe::Type new_type;
|
||||
|
||||
if (selected == hold_key_action) {
|
||||
new_type = NodeKeyframe::kHold;
|
||||
} else if (selected == bezier_key_action) {
|
||||
new_type = NodeKeyframe::kBezier;
|
||||
} else {
|
||||
new_type = NodeKeyframe::kLinear;
|
||||
}
|
||||
|
||||
MultiUndoCommand* command = new MultiUndoCommand();
|
||||
foreach (NodeKeyframe* item, GetSelectedKeyframes()) {
|
||||
command->add_child(new KeyframeSetTypeCommand(item, new_type));
|
||||
}
|
||||
Core::instance()->undo_stack()->push(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeView::ShowKeyframePropertiesDialog()
|
||||
{
|
||||
if (!GetSelectedKeyframes().isEmpty()) {
|
||||
KeyframePropertiesDialog kd(GetSelectedKeyframes(), timebase(), this);
|
||||
kd.exec();
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeView::Redraw()
|
||||
{
|
||||
viewport()->update();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,33 +18,133 @@
|
||||
|
||||
***/
|
||||
|
||||
#ifndef KEYFRAMEVIEW_H
|
||||
#define KEYFRAMEVIEW_H
|
||||
#ifndef KEYFRAMEVIEWBASE_H
|
||||
#define KEYFRAMEVIEWBASE_H
|
||||
|
||||
#include "keyframeviewbase.h"
|
||||
#include "keyframeviewinputconnection.h"
|
||||
#include "node/keyframe.h"
|
||||
#include "widget/menu/menu.h"
|
||||
#include "widget/timebased/timebasedview.h"
|
||||
#include "widget/timebased/timebasedviewselectionmanager.h"
|
||||
#include "widget/timetarget/timetarget.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class KeyframeView : public KeyframeViewBase
|
||||
class KeyframeView : public TimeBasedView, public TimeTargetObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
KeyframeView(QWidget* parent = nullptr);
|
||||
|
||||
void DeleteSelected();
|
||||
|
||||
using ElementConnections = QVector<KeyframeViewInputConnection *>;
|
||||
using InputConnections = QVector<ElementConnections>;
|
||||
using NodeConnections = QMap<QString, InputConnections>;
|
||||
|
||||
NodeConnections AddKeyframesOfNode(Node* n);
|
||||
|
||||
InputConnections AddKeyframesOfInput(Node *n, const QString &input);
|
||||
|
||||
ElementConnections AddKeyframesOfElement(const NodeInput &input);
|
||||
|
||||
KeyframeViewInputConnection *AddKeyframesOfTrack(const NodeKeyframeTrackReference &ref);
|
||||
|
||||
void RemoveKeyframesOfTrack(KeyframeViewInputConnection *connection);
|
||||
|
||||
void SelectAll();
|
||||
|
||||
void DeselectAll();
|
||||
|
||||
void Clear();
|
||||
|
||||
const QVector<NodeKeyframe*> &GetSelectedKeyframes() const
|
||||
{
|
||||
return selection_manager_.GetSelectedObjects();
|
||||
}
|
||||
|
||||
virtual void SelectionManagerSelectEvent(void *obj) override;
|
||||
virtual void SelectionManagerDeselectEvent(void *obj) override;
|
||||
|
||||
void SetMaxScroll(int i)
|
||||
{
|
||||
max_scroll_ = i;
|
||||
UpdateSceneRect();
|
||||
}
|
||||
|
||||
signals:
|
||||
void Dragged(int current_x, int current_y);
|
||||
|
||||
protected:
|
||||
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||
virtual void mouseMoveEvent(QMouseEvent *event) override;
|
||||
virtual void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
|
||||
virtual void drawForeground(QPainter *painter, const QRectF &rect) override;
|
||||
|
||||
virtual void DrawKeyframe(QPainter *painter, NodeKeyframe *key, KeyframeViewInputConnection *track, const QRectF &key_rect);
|
||||
|
||||
virtual void ScaleChangedEvent(const double& scale) override;
|
||||
|
||||
virtual void TimeTargetChangedEvent(Node*) override;
|
||||
|
||||
virtual void TimebaseChangedEvent(const rational &timebase) override;
|
||||
|
||||
virtual void ContextMenuEvent(Menu &m);
|
||||
|
||||
virtual bool FirstChanceMousePress(QMouseEvent *event){return false;}
|
||||
virtual void FirstChanceMouseMove(QMouseEvent *event){}
|
||||
virtual void FirstChanceMouseRelease(QMouseEvent *event){}
|
||||
|
||||
virtual void KeyframeDragStart(QMouseEvent *event){}
|
||||
virtual void KeyframeDragMove(QMouseEvent *event, QString &tip){}
|
||||
virtual void KeyframeDragRelease(QMouseEvent *event, MultiUndoCommand *command){}
|
||||
|
||||
void SelectKeyframe(NodeKeyframe *key);
|
||||
|
||||
void DeselectKeyframe(NodeKeyframe *key);
|
||||
|
||||
bool IsKeyframeSelected(NodeKeyframe *key) const
|
||||
{
|
||||
return selection_manager_.IsSelected(key);
|
||||
}
|
||||
|
||||
rational GetAdjustedKeyframeTime(NodeKeyframe *key);
|
||||
|
||||
double GetKeyframeSceneX(NodeKeyframe *key);
|
||||
|
||||
virtual qreal GetKeyframeSceneY(KeyframeViewInputConnection *track, NodeKeyframe *key);
|
||||
|
||||
void SetAutoSelectSiblings(bool e)
|
||||
{
|
||||
autoselect_siblings_ = e;
|
||||
}
|
||||
|
||||
virtual void SceneRectUpdateEvent(QRectF& rect) override;
|
||||
|
||||
protected slots:
|
||||
void Redraw();
|
||||
|
||||
private:
|
||||
rational CalculateNewTimeFromScreen(const rational& old_time, double cursor_diff);
|
||||
|
||||
QVector<KeyframeViewInputConnection*> tracks_;
|
||||
|
||||
TimeBasedViewSelectionManager<NodeKeyframe> selection_manager_;
|
||||
|
||||
bool autoselect_siblings_;
|
||||
|
||||
int max_scroll_;
|
||||
|
||||
bool first_chance_mouse_event_;
|
||||
|
||||
private slots:
|
||||
void ShowContextMenu();
|
||||
|
||||
void ShowKeyframePropertiesDialog();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // KEYFRAMEVIEW_H
|
||||
#endif // KEYFRAMEVIEWBASE_H
|
||||
|
||||
@@ -1,602 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "keyframeviewbase.h"
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include <QToolTip>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "common/qtutils.h"
|
||||
#include "dialog/keyframeproperties/keyframeproperties.h"
|
||||
#include "keyframeviewundo.h"
|
||||
#include "node/node.h"
|
||||
#include "widget/menu/menu.h"
|
||||
#include "widget/menu/menushared.h"
|
||||
#include "widget/nodeparamview/nodeparamviewundo.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
#define super TimeBasedView
|
||||
|
||||
KeyframeViewBase::KeyframeViewBase(QWidget *parent) :
|
||||
super(parent),
|
||||
dragging_bezier_point_(nullptr),
|
||||
currently_autoselecting_(false),
|
||||
dragging_(false),
|
||||
selection_manager_(this)
|
||||
{
|
||||
SetDefaultDragMode(RubberBandDrag);
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
connect(this, &KeyframeViewBase::customContextMenuRequested, this, &KeyframeViewBase::ShowContextMenu);
|
||||
}
|
||||
|
||||
void KeyframeViewBase::DeleteSelected()
|
||||
{
|
||||
MultiUndoCommand* command = new MultiUndoCommand();
|
||||
|
||||
foreach (NodeKeyframe *key, GetSelectedKeyframes()) {
|
||||
command->add_child(new NodeParamRemoveKeyframeCommand(key));
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
}
|
||||
|
||||
KeyframeViewBase::NodeConnections KeyframeViewBase::AddKeyframesOfNode(Node *n)
|
||||
{
|
||||
NodeConnections map;
|
||||
|
||||
foreach (const QString& i, n->inputs()) {
|
||||
map.insert(i, AddKeyframesOfInput(n, i));
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
KeyframeViewBase::InputConnections KeyframeViewBase::AddKeyframesOfInput(Node* n, const QString& input)
|
||||
{
|
||||
InputConnections vec;
|
||||
|
||||
if (n->IsInputKeyframable(input)) {
|
||||
int arr_sz = n->InputArraySize(input);
|
||||
vec.resize(arr_sz + 1);
|
||||
for (int i=-1; i<arr_sz; i++) {
|
||||
vec[i+1] = AddKeyframesOfElement(NodeInput(n, input, i));
|
||||
}
|
||||
}
|
||||
|
||||
return vec;
|
||||
}
|
||||
|
||||
KeyframeViewBase::ElementConnections KeyframeViewBase::AddKeyframesOfElement(const NodeInput& input)
|
||||
{
|
||||
const QVector<NodeKeyframeTrack>& tracks = input.node()->GetKeyframeTracks(input);
|
||||
ElementConnections vec(tracks.size());
|
||||
|
||||
for (int i=0; i<tracks.size(); i++) {
|
||||
vec[i] = AddKeyframesOfTrack(NodeKeyframeTrackReference(input, i));
|
||||
}
|
||||
|
||||
return vec;
|
||||
}
|
||||
|
||||
KeyframeViewInputConnection *KeyframeViewBase::AddKeyframesOfTrack(const NodeKeyframeTrackReference& ref)
|
||||
{
|
||||
KeyframeViewInputConnection *track = new KeyframeViewInputConnection(ref, this);
|
||||
connect(track, &KeyframeViewInputConnection::RequireUpdate, this, &KeyframeViewBase::Redraw);
|
||||
tracks_.append(track);
|
||||
Redraw();
|
||||
return track;
|
||||
}
|
||||
|
||||
void KeyframeViewBase::RemoveKeyframesOfTrack(KeyframeViewInputConnection *connection)
|
||||
{
|
||||
if (tracks_.removeOne(connection)) {
|
||||
delete connection;
|
||||
Redraw();
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeViewBase::SelectAll()
|
||||
{
|
||||
foreach (KeyframeViewInputConnection *track, tracks_) {
|
||||
foreach (NodeKeyframe *key, track->GetKeyframes()) {
|
||||
SelectKeyframe(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeViewBase::DeselectAll()
|
||||
{
|
||||
selection_manager_.ClearSelection();
|
||||
|
||||
Redraw();
|
||||
}
|
||||
|
||||
void KeyframeViewBase::Clear()
|
||||
{
|
||||
if (!tracks_.isEmpty()) {
|
||||
qDeleteAll(tracks_);
|
||||
tracks_.clear();
|
||||
Redraw();
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeViewBase::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
NodeKeyframe *key_under_cursor = selection_manager_.MousePress(event);
|
||||
if (key_under_cursor) {
|
||||
AutoSelectKeyTimeNeighbors();
|
||||
}
|
||||
|
||||
BezierControlPointItem *bezier_under_cursor = dynamic_cast<BezierControlPointItem*>(itemAt(event->pos()));
|
||||
|
||||
Redraw();
|
||||
|
||||
if (HandPress(event) || (!bezier_under_cursor && !key_under_cursor && PlayheadPress(event))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
if (key_under_cursor || bezier_under_cursor) {
|
||||
dragging_ = true;
|
||||
drag_start_ = mapToScene(event->pos());
|
||||
|
||||
// Determine what type of item is under the cursor
|
||||
dragging_bezier_point_ = bezier_under_cursor;
|
||||
|
||||
if (dragging_bezier_point_) {
|
||||
|
||||
dragging_bezier_point_start_ = dragging_bezier_point_->GetCorrespondingKeyframeHandle();
|
||||
dragging_bezier_point_opposing_start_ = dragging_bezier_point_->key()->bezier_control(NodeKeyframe::get_opposing_bezier_type(dragging_bezier_point_->mode()));
|
||||
|
||||
} else {
|
||||
|
||||
selection_manager_.DragStart(key_under_cursor, event);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeViewBase::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
if (HandMove(event) || PlayheadMove(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->buttons() & Qt::LeftButton) {
|
||||
if (dragging_) {
|
||||
// Calculate cursor difference and scale it
|
||||
QPointF scene_pos = mapToScene(event->pos());
|
||||
QPointF mouse_diff_scaled = GetScaledCursorPos(scene_pos - drag_start_);
|
||||
|
||||
if (event->modifiers() & Qt::ShiftModifier) {
|
||||
// If holding shift, only move one axis
|
||||
mouse_diff_scaled.setY(0);
|
||||
}
|
||||
|
||||
if (dragging_bezier_point_) {
|
||||
|
||||
// Flip the mouse Y because bezier control points are drawn bottom to top, not top to bottom
|
||||
mouse_diff_scaled.setY(-mouse_diff_scaled.y());
|
||||
|
||||
QPointF new_bezier_pos = GenerateBezierControlPosition(dragging_bezier_point_->mode(),
|
||||
dragging_bezier_point_start_,
|
||||
mouse_diff_scaled);
|
||||
|
||||
// If the user is NOT holding control, we set the other handle to the exact negative of this handle
|
||||
QPointF new_opposing_pos;
|
||||
NodeKeyframe::BezierType opposing_type = NodeKeyframe::get_opposing_bezier_type(dragging_bezier_point_->mode());
|
||||
|
||||
|
||||
if (!(event->modifiers() & Qt::ControlModifier)) {
|
||||
new_opposing_pos = GenerateBezierControlPosition(opposing_type,
|
||||
dragging_bezier_point_opposing_start_,
|
||||
-mouse_diff_scaled);
|
||||
} else {
|
||||
new_opposing_pos = dragging_bezier_point_opposing_start_;
|
||||
}
|
||||
|
||||
dragging_bezier_point_->key()->set_bezier_control(dragging_bezier_point_->mode(),
|
||||
new_bezier_pos);
|
||||
|
||||
dragging_bezier_point_->key()->set_bezier_control(opposing_type,
|
||||
new_opposing_pos);
|
||||
|
||||
// Bezier control points are parented to keyframe items making their positions relative
|
||||
// to those items. We need to map them to the scene coordinates for this to work properly.
|
||||
QPointF bezier_pos = dragging_bezier_point_->pos() + dragging_bezier_point_->parentItem()->pos();
|
||||
emit Dragged(qRound(bezier_pos.x()), qRound(bezier_pos.y()));
|
||||
|
||||
} else if (selection_manager_.IsDragging()) {
|
||||
|
||||
QString tip;
|
||||
|
||||
/*
|
||||
// Validate movement - ensure no keyframe goes above its max point or below its min point
|
||||
FloatSlider::DisplayType display_type = FloatSlider::kNormal;
|
||||
|
||||
if (IsYAxisEnabled()) {
|
||||
foreach (const KeyframeItemAndTime& keypair, dragging_keyframes_) {
|
||||
NodeKeyframe *key = keypair.key;
|
||||
Node* node = key->parent();
|
||||
const QString& input = key->input();
|
||||
double new_val = keypair.value - mouse_diff_scaled.y();
|
||||
double limited = new_val;
|
||||
|
||||
if (node->HasInputProperty(input, QStringLiteral("min"))) {
|
||||
limited = qMax(limited, node->GetInputProperty(input, QStringLiteral("min")).toDouble());
|
||||
}
|
||||
|
||||
if (node->HasInputProperty(input, QStringLiteral("max"))) {
|
||||
limited = qMin(limited, node->GetInputProperty(input, QStringLiteral("max")).toDouble());
|
||||
}
|
||||
|
||||
if (limited != new_val) {
|
||||
mouse_diff_scaled.setY(keypair.value - limited);
|
||||
}
|
||||
}
|
||||
|
||||
Node* initial_drag_input = initial_drag_item_->parent();
|
||||
const QString& initial_drag_input_id = initial_drag_item_->input();
|
||||
if (initial_drag_input->HasInputProperty(initial_drag_input_id, QStringLiteral("view"))) {
|
||||
display_type = static_cast<FloatSlider::DisplayType>(initial_drag_input->GetInputProperty(initial_drag_input_id, QStringLiteral("view")).toInt());
|
||||
}
|
||||
}
|
||||
|
||||
foreach (const KeyframeItemAndTime& keypair, dragging_keyframes_) {
|
||||
if (IsYAxisEnabled()) {
|
||||
key->set_value(keypair.value - mouse_diff_scaled.y());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (IsYAxisEnabled()) {
|
||||
bool ok;
|
||||
double num_value = initial_drag_item_->value().toDouble(&ok);
|
||||
|
||||
if (ok) {
|
||||
tip = QStringLiteral("%1\n");
|
||||
tip.append(FloatSlider::ValueToString(num_value, display_type, 2, true));
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
selection_manager_.DragMove(event, tip);
|
||||
|
||||
Redraw();
|
||||
|
||||
emit Dragged(scene_pos.x(), scene_pos.y());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeViewBase::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if (HandRelease(event) || PlayheadRelease(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
if (dragging_) {
|
||||
if (dragging_bezier_point_) {
|
||||
MultiUndoCommand* command = new MultiUndoCommand();
|
||||
|
||||
// Create undo command with the current bezier point and the old one
|
||||
command->add_child(new KeyframeSetBezierControlPoint(dragging_bezier_point_->key(),
|
||||
dragging_bezier_point_->mode(),
|
||||
dragging_bezier_point_->key()->bezier_control(dragging_bezier_point_->mode()),
|
||||
dragging_bezier_point_start_));
|
||||
|
||||
if (!(event->modifiers() & Qt::ControlModifier)) {
|
||||
auto opposing_type = NodeKeyframe::get_opposing_bezier_type(dragging_bezier_point_->mode());
|
||||
|
||||
command->add_child(new KeyframeSetBezierControlPoint(dragging_bezier_point_->key(),
|
||||
opposing_type,
|
||||
dragging_bezier_point_->key()->bezier_control(opposing_type),
|
||||
dragging_bezier_point_opposing_start_));
|
||||
}
|
||||
|
||||
dragging_bezier_point_ = nullptr;
|
||||
|
||||
Core::instance()->undo_stack()->push(command);
|
||||
} else if (selection_manager_.IsDragging()) {
|
||||
MultiUndoCommand* command = new MultiUndoCommand();
|
||||
|
||||
selection_manager_.DragStop(command);
|
||||
/*if (IsYAxisEnabled()) {
|
||||
command->add_child(new NodeParamSetKeyframeValueCommand(item,
|
||||
item->value(),
|
||||
keypair.value));
|
||||
}*/
|
||||
|
||||
Core::instance()->undo_stack()->push(command);
|
||||
}
|
||||
|
||||
dragging_ = false;
|
||||
|
||||
QToolTip::hideText();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeViewBase::drawForeground(QPainter *painter, const QRectF &rect)
|
||||
{
|
||||
int key_sz = QtUtils::QFontMetricsWidth(fontMetrics(), "Oi");
|
||||
int key_rad = key_sz/2;
|
||||
|
||||
selection_manager_.ClearDrawnObjects();
|
||||
|
||||
painter->setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
painter->setPen(Qt::black);
|
||||
|
||||
foreach (KeyframeViewInputConnection *track, tracks_) {
|
||||
foreach (NodeKeyframe *key, track->GetKeyframes()) {
|
||||
QRectF key_rect(-key_rad, -key_rad, key_sz, key_sz);
|
||||
key_rect.translate(GetKeyframeSceneX(key), mapFromGlobal(QPoint(0, track->GetKeyframeY())).y());
|
||||
|
||||
if (!rect.intersects(key_rect)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsKeyframeSelected(key)) {
|
||||
painter->setBrush(palette().highlight());
|
||||
} else {
|
||||
painter->setBrush(track->GetBrush());
|
||||
}
|
||||
|
||||
selection_manager_.DeclareDrawnObject(key, key_rect);
|
||||
|
||||
switch (key->type()) {
|
||||
case NodeKeyframe::kLinear:
|
||||
{
|
||||
QPointF points[] = {
|
||||
QPointF(key_rect.center().x(), key_rect.top()),
|
||||
QPointF(key_rect.right(), key_rect.center().y()),
|
||||
QPointF(key_rect.center().x(), key_rect.bottom()),
|
||||
QPointF(key_rect.left(), key_rect.center().y())
|
||||
};
|
||||
|
||||
painter->drawPolygon(points, 4);
|
||||
break;
|
||||
}
|
||||
case NodeKeyframe::kBezier:
|
||||
painter->drawEllipse(key_rect);
|
||||
break;
|
||||
case NodeKeyframe::kHold:
|
||||
painter->drawRect(key_rect);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super::drawForeground(painter, rect);
|
||||
}
|
||||
|
||||
void KeyframeViewBase::ScaleChangedEvent(const double &scale)
|
||||
{
|
||||
super::ScaleChangedEvent(scale);
|
||||
|
||||
Redraw();
|
||||
}
|
||||
|
||||
void KeyframeViewBase::TimeTargetChangedEvent(Node *target)
|
||||
{
|
||||
Redraw();
|
||||
}
|
||||
|
||||
void KeyframeViewBase::TimebaseChangedEvent(const rational &timebase)
|
||||
{
|
||||
super::TimebaseChangedEvent(timebase);
|
||||
|
||||
selection_manager_.SetTimebase(timebase);
|
||||
}
|
||||
|
||||
void KeyframeViewBase::ContextMenuEvent(Menu& m)
|
||||
{
|
||||
Q_UNUSED(m)
|
||||
}
|
||||
|
||||
void KeyframeViewBase::SelectKeyframe(NodeKeyframe *key)
|
||||
{
|
||||
if (selection_manager_.Select(key)) {
|
||||
Redraw();
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeViewBase::DeselectKeyframe(NodeKeyframe *key)
|
||||
{
|
||||
if (selection_manager_.Deselect(key)) {
|
||||
Redraw();
|
||||
}
|
||||
}
|
||||
|
||||
rational KeyframeViewBase::GetAdjustedKeyframeTime(NodeKeyframe *key)
|
||||
{
|
||||
return GetAdjustedTime(key->parent(), GetTimeTarget(), key->time(), false);
|
||||
}
|
||||
|
||||
double KeyframeViewBase::GetKeyframeSceneX(NodeKeyframe *key)
|
||||
{
|
||||
return TimeToScene(GetAdjustedKeyframeTime(key));
|
||||
}
|
||||
|
||||
rational KeyframeViewBase::CalculateNewTimeFromScreen(const rational &old_time, double cursor_diff)
|
||||
{
|
||||
return rational::fromDouble(old_time.toDouble() + cursor_diff);
|
||||
}
|
||||
|
||||
QPointF KeyframeViewBase::GenerateBezierControlPosition(const NodeKeyframe::BezierType mode, const QPointF &start_point, const QPointF &scaled_cursor_diff)
|
||||
{
|
||||
QPointF new_bezier_pos = start_point;
|
||||
|
||||
new_bezier_pos += scaled_cursor_diff;
|
||||
|
||||
// LIMIT bezier handles from overlapping each other
|
||||
if (mode == NodeKeyframe::kInHandle) {
|
||||
if (new_bezier_pos.x() > 0) {
|
||||
new_bezier_pos.setX(0);
|
||||
}
|
||||
} else {
|
||||
if (new_bezier_pos.x() < 0) {
|
||||
new_bezier_pos.setX(0);
|
||||
}
|
||||
}
|
||||
|
||||
return new_bezier_pos;
|
||||
}
|
||||
|
||||
QPointF KeyframeViewBase::GetScaledCursorPos(const QPointF &cursor_pos)
|
||||
{
|
||||
return QPointF(cursor_pos.x() / GetScale(),
|
||||
cursor_pos.y() / GetYScale());
|
||||
}
|
||||
|
||||
void KeyframeViewBase::ShowContextMenu()
|
||||
{
|
||||
Menu m;
|
||||
|
||||
MenuShared::instance()->AddItemsForEditMenu(&m, false);
|
||||
|
||||
QAction* linear_key_action = nullptr;
|
||||
QAction* bezier_key_action = nullptr;
|
||||
QAction* hold_key_action = nullptr;
|
||||
|
||||
if (!GetSelectedKeyframes().isEmpty()) {
|
||||
bool all_keys_are_same_type = true;
|
||||
NodeKeyframe::Type type = GetSelectedKeyframes().first()->type();
|
||||
|
||||
for (int i=1;i<GetSelectedKeyframes().size();i++) {
|
||||
NodeKeyframe* key_item = GetSelectedKeyframes().at(i);
|
||||
NodeKeyframe* prev_item = GetSelectedKeyframes().at(i-1);
|
||||
|
||||
if (key_item->type() != prev_item->type()) {
|
||||
all_keys_are_same_type = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m.addSeparator();
|
||||
|
||||
linear_key_action = m.addAction(tr("Linear"));
|
||||
bezier_key_action = m.addAction(tr("Bezier"));
|
||||
hold_key_action = m.addAction(tr("Hold"));
|
||||
|
||||
if (all_keys_are_same_type) {
|
||||
switch (type) {
|
||||
case NodeKeyframe::kLinear:
|
||||
linear_key_action->setChecked(true);
|
||||
break;
|
||||
case NodeKeyframe::kBezier:
|
||||
bezier_key_action->setChecked(true);
|
||||
break;
|
||||
case NodeKeyframe::kHold:
|
||||
hold_key_action->setChecked(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m.addSeparator();
|
||||
|
||||
AddSetScrollZoomsByDefaultActionToMenu(&m);
|
||||
|
||||
m.addSeparator();
|
||||
|
||||
ContextMenuEvent(m);
|
||||
|
||||
if (!GetSelectedKeyframes().isEmpty()) {
|
||||
m.addSeparator();
|
||||
|
||||
QAction* properties_action = m.addAction(tr("P&roperties"));
|
||||
connect(properties_action, &QAction::triggered, this, &KeyframeViewBase::ShowKeyframePropertiesDialog);
|
||||
}
|
||||
|
||||
QAction* selected = m.exec(QCursor::pos());
|
||||
|
||||
// Process keyframe type changes
|
||||
if (selected) {
|
||||
if (selected == linear_key_action
|
||||
|| selected == bezier_key_action
|
||||
|| selected == hold_key_action) {
|
||||
NodeKeyframe::Type new_type;
|
||||
|
||||
if (selected == hold_key_action) {
|
||||
new_type = NodeKeyframe::kHold;
|
||||
} else if (selected == bezier_key_action) {
|
||||
new_type = NodeKeyframe::kBezier;
|
||||
} else {
|
||||
new_type = NodeKeyframe::kLinear;
|
||||
}
|
||||
|
||||
MultiUndoCommand* command = new MultiUndoCommand();
|
||||
foreach (NodeKeyframe* item, GetSelectedKeyframes()) {
|
||||
command->add_child(new KeyframeSetTypeCommand(item, new_type));
|
||||
}
|
||||
Core::instance()->undo_stack()->push(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeViewBase::ShowKeyframePropertiesDialog()
|
||||
{
|
||||
if (!GetSelectedKeyframes().isEmpty()) {
|
||||
KeyframePropertiesDialog kd(GetSelectedKeyframes(), timebase(), this);
|
||||
kd.exec();
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeViewBase::AutoSelectKeyTimeNeighbors()
|
||||
{
|
||||
if (currently_autoselecting_ || IsYAxisEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevents infinite loop
|
||||
currently_autoselecting_ = true;
|
||||
|
||||
QVector<NodeKeyframe*> copy = GetSelectedKeyframes();
|
||||
foreach (NodeKeyframe *key, copy) {
|
||||
rational key_time = key->time();
|
||||
|
||||
QVector<NodeKeyframe*> keys = key->parent()->GetKeyframesAtTime(key->input(), key_time, key->element());
|
||||
|
||||
foreach (NodeKeyframe* k, keys) {
|
||||
if (k != key) {
|
||||
SelectKeyframe(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currently_autoselecting_ = false;
|
||||
}
|
||||
|
||||
void KeyframeViewBase::Redraw()
|
||||
{
|
||||
viewport()->update();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef KEYFRAMEVIEWBASE_H
|
||||
#define KEYFRAMEVIEWBASE_H
|
||||
|
||||
#include "keyframeviewinputconnection.h"
|
||||
#include "node/keyframe.h"
|
||||
#include "widget/curvewidget/beziercontrolpointitem.h"
|
||||
#include "widget/menu/menu.h"
|
||||
#include "widget/timebased/timebasedview.h"
|
||||
#include "widget/timebased/timebasedviewselectionmanager.h"
|
||||
#include "widget/timetarget/timetarget.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class KeyframeViewBase : public TimeBasedView, public TimeTargetObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
KeyframeViewBase(QWidget* parent = nullptr);
|
||||
|
||||
void DeleteSelected();
|
||||
|
||||
using ElementConnections = QVector<KeyframeViewInputConnection *>;
|
||||
using InputConnections = QVector<ElementConnections>;
|
||||
using NodeConnections = QMap<QString, InputConnections>;
|
||||
|
||||
NodeConnections AddKeyframesOfNode(Node* n);
|
||||
|
||||
InputConnections AddKeyframesOfInput(Node *n, const QString &input);
|
||||
|
||||
ElementConnections AddKeyframesOfElement(const NodeInput &input);
|
||||
|
||||
KeyframeViewInputConnection *AddKeyframesOfTrack(const NodeKeyframeTrackReference &ref);
|
||||
|
||||
void RemoveKeyframesOfTrack(KeyframeViewInputConnection *connection);
|
||||
|
||||
void SelectAll();
|
||||
|
||||
void DeselectAll();
|
||||
|
||||
void Clear();
|
||||
|
||||
const QVector<NodeKeyframe*> &GetSelectedKeyframes() const
|
||||
{
|
||||
return selection_manager_.GetSelectedObjects();
|
||||
}
|
||||
|
||||
signals:
|
||||
void Dragged(int current_x, int current_y);
|
||||
|
||||
protected:
|
||||
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||
virtual void mouseMoveEvent(QMouseEvent *event) override;
|
||||
virtual void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
|
||||
virtual void drawForeground(QPainter *painter, const QRectF &rect) override;
|
||||
|
||||
virtual void ScaleChangedEvent(const double& scale) override;
|
||||
|
||||
virtual void TimeTargetChangedEvent(Node*) override;
|
||||
|
||||
virtual void TimebaseChangedEvent(const rational &timebase) override;
|
||||
|
||||
virtual void ContextMenuEvent(Menu &m);
|
||||
|
||||
bool IsDragging() const
|
||||
{
|
||||
return dragging_;
|
||||
}
|
||||
|
||||
void SelectKeyframe(NodeKeyframe *key);
|
||||
|
||||
void DeselectKeyframe(NodeKeyframe *key);
|
||||
|
||||
bool IsKeyframeSelected(NodeKeyframe *key) const
|
||||
{
|
||||
return selection_manager_.IsSelected(key);
|
||||
}
|
||||
|
||||
rational GetAdjustedKeyframeTime(NodeKeyframe *key);
|
||||
|
||||
double GetKeyframeSceneX(NodeKeyframe *key);
|
||||
|
||||
private:
|
||||
rational CalculateNewTimeFromScreen(const rational& old_time, double cursor_diff);
|
||||
|
||||
static QPointF GenerateBezierControlPosition(const NodeKeyframe::BezierType mode,
|
||||
const QPointF& start_point,
|
||||
const QPointF& scaled_cursor_diff);
|
||||
|
||||
QPointF GetScaledCursorPos(const QPointF &cursor_pos);
|
||||
|
||||
QPointF drag_start_;
|
||||
|
||||
BezierControlPointItem* dragging_bezier_point_;
|
||||
QPointF dragging_bezier_point_start_;
|
||||
QPointF dragging_bezier_point_opposing_start_;
|
||||
|
||||
QVector<KeyframeViewInputConnection*> tracks_;
|
||||
|
||||
bool currently_autoselecting_;
|
||||
|
||||
bool dragging_;
|
||||
|
||||
TimeBasedViewSelectionManager<NodeKeyframe> selection_manager_;
|
||||
|
||||
private slots:
|
||||
void ShowContextMenu();
|
||||
|
||||
void ShowKeyframePropertiesDialog();
|
||||
|
||||
void AutoSelectKeyTimeNeighbors();
|
||||
|
||||
void Redraw();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // KEYFRAMEVIEWBASE_H
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
KeyframeViewInputConnection::KeyframeViewInputConnection(const NodeKeyframeTrackReference &input, KeyframeViewBase *parent) :
|
||||
KeyframeViewInputConnection::KeyframeViewInputConnection(const NodeKeyframeTrackReference &input, KeyframeView *parent) :
|
||||
QObject(parent),
|
||||
keyframe_view_(parent),
|
||||
input_(input),
|
||||
@@ -36,7 +36,9 @@ KeyframeViewInputConnection::KeyframeViewInputConnection(const NodeKeyframeTrack
|
||||
|
||||
connect(n, &Node::KeyframeAdded, this, &KeyframeViewInputConnection::AddKeyframe);
|
||||
connect(n, &Node::KeyframeRemoved, this, &KeyframeViewInputConnection::RemoveKeyframe);
|
||||
connect(n, &Node::KeyframeTimeChanged, this, &KeyframeViewInputConnection::RequireUpdate);
|
||||
connect(n, &Node::KeyframeTimeChanged, this, &KeyframeViewInputConnection::KeyframeChanged);
|
||||
connect(n, &Node::KeyframeTypeChanged, this, &KeyframeViewInputConnection::KeyframeChanged);
|
||||
connect(n, &Node::KeyframeValueChanged, this, &KeyframeViewInputConnection::KeyframeChanged);
|
||||
}
|
||||
|
||||
void KeyframeViewInputConnection::SetKeyframeY(int y)
|
||||
@@ -80,4 +82,11 @@ void KeyframeViewInputConnection::RemoveKeyframe(NodeKeyframe *key)
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeViewInputConnection::KeyframeChanged(NodeKeyframe *key)
|
||||
{
|
||||
if (key->key_track_ref() == input_) {
|
||||
emit RequireUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,13 +28,13 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
class KeyframeViewBase;
|
||||
class KeyframeView;
|
||||
|
||||
class KeyframeViewInputConnection : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
KeyframeViewInputConnection(const NodeKeyframeTrackReference &input, KeyframeViewBase *parent);
|
||||
KeyframeViewInputConnection(const NodeKeyframeTrackReference &input, KeyframeView *parent);
|
||||
|
||||
const int &GetKeyframeY() const
|
||||
{
|
||||
@@ -66,7 +66,7 @@ signals:
|
||||
void RequireUpdate();
|
||||
|
||||
private:
|
||||
KeyframeViewBase *keyframe_view_;
|
||||
KeyframeView *keyframe_view_;
|
||||
|
||||
NodeKeyframeTrackReference input_;
|
||||
|
||||
@@ -81,6 +81,8 @@ private slots:
|
||||
|
||||
void RemoveKeyframe(NodeKeyframe *key);
|
||||
|
||||
void KeyframeChanged(NodeKeyframe *key);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -373,11 +373,9 @@ void NodeParamView::AddNode(Node *n, NodeParamViewContext *context)
|
||||
}
|
||||
}
|
||||
|
||||
// Set time target
|
||||
item->SetTimeTarget(GetTimeTarget());
|
||||
|
||||
// Set the timebase
|
||||
item->SetTimebase(timebase());
|
||||
item->SetTime(GetTime());
|
||||
|
||||
context->AddNode(item);
|
||||
|
||||
@@ -526,7 +524,7 @@ void NodeParamView::UpdateElementY()
|
||||
{
|
||||
foreach (NodeParamViewContext *ctx, context_items_) {
|
||||
for (auto it=ctx->GetItems().cbegin(); it!=ctx->GetItems().cend(); it++) {
|
||||
const KeyframeViewBase::NodeConnections &connections = it.value()->GetKeyframeConnections();
|
||||
const KeyframeView::NodeConnections &connections = it.value()->GetKeyframeConnections();
|
||||
|
||||
if (!connections.isEmpty()) {
|
||||
foreach (const QString& input, it.key()->inputs()) {
|
||||
@@ -538,10 +536,10 @@ void NodeParamView::UpdateElementY()
|
||||
|
||||
int y = it.value()->GetElementY(ic);
|
||||
|
||||
const KeyframeViewBase::InputConnections &input_con = connections.value(input);
|
||||
const KeyframeView::InputConnections &input_con = connections.value(input);
|
||||
int use_index = i + 1;
|
||||
if (use_index < input_con.size()) {
|
||||
const KeyframeViewBase::ElementConnections &ele_con = input_con.at(ic.element()+1);
|
||||
const KeyframeView::ElementConnections &ele_con = input_con.at(ic.element()+1);
|
||||
foreach (KeyframeViewInputConnection *track, ele_con) {
|
||||
track->SetKeyframeY(y);
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
#include "nodeparamviewwidgetbridge.h"
|
||||
#include "widget/clickablelabel/clickablelabel.h"
|
||||
#include "widget/collapsebutton/collapsebutton.h"
|
||||
#include "widget/keyframeview/keyframeviewbase.h"
|
||||
#include "widget/keyframeview/keyframeview.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -188,12 +188,12 @@ public:
|
||||
|
||||
void SetInputChecked(const NodeInput &input, bool e);
|
||||
|
||||
const KeyframeViewBase::NodeConnections &GetKeyframeConnections() const
|
||||
const KeyframeView::NodeConnections &GetKeyframeConnections() const
|
||||
{
|
||||
return keyframe_connections_;
|
||||
}
|
||||
|
||||
void SetKeyframeConnections(const KeyframeViewBase::NodeConnections &c)
|
||||
void SetKeyframeConnections(const KeyframeView::NodeConnections &c)
|
||||
{
|
||||
keyframe_connections_ = c;
|
||||
}
|
||||
@@ -217,7 +217,7 @@ private:
|
||||
|
||||
rational time_;
|
||||
|
||||
KeyframeViewBase::NodeConnections keyframe_connections_;
|
||||
KeyframeView::NodeConnections keyframe_connections_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
#include "widget/slider/floatslider.h"
|
||||
#include "widget/slider/integerslider.h"
|
||||
#include "widget/slider/rationalslider.h"
|
||||
#include "widget/videoparamedit/videoparamedit.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -89,6 +88,8 @@ void NodeParamViewWidgetBridge::CreateWidgets()
|
||||
case NodeValue::kShaderJob:
|
||||
case NodeValue::kSampleJob:
|
||||
case NodeValue::kGenerateJob:
|
||||
case NodeValue::kVideoParams:
|
||||
case NodeValue::kAudioParams:
|
||||
break;
|
||||
case NodeValue::kInt:
|
||||
{
|
||||
@@ -156,19 +157,6 @@ void NodeParamViewWidgetBridge::CreateWidgets()
|
||||
connect(font_combobox, &QFontComboBox::currentFontChanged, this, &NodeParamViewWidgetBridge::WidgetCallback);
|
||||
break;
|
||||
}
|
||||
case NodeValue::kVideoParams:
|
||||
{
|
||||
VideoParamEdit* edit = new VideoParamEdit();
|
||||
edit->SetColorManager(input_.node()->project()->color_manager());
|
||||
widgets_.append(edit);
|
||||
connect(edit, &VideoParamEdit::Changed, this, &NodeParamViewWidgetBridge::WidgetCallback);
|
||||
break;
|
||||
}
|
||||
case NodeValue::kAudioParams:
|
||||
{
|
||||
// FIXME: Create audio param widget
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check all properties
|
||||
@@ -278,6 +266,8 @@ void NodeParamViewWidgetBridge::WidgetCallback()
|
||||
case NodeValue::kShaderJob:
|
||||
case NodeValue::kSampleJob:
|
||||
case NodeValue::kGenerateJob:
|
||||
case NodeValue::kVideoParams:
|
||||
case NodeValue::kAudioParams:
|
||||
break;
|
||||
case NodeValue::kInt:
|
||||
{
|
||||
@@ -388,15 +378,6 @@ void NodeParamViewWidgetBridge::WidgetCallback()
|
||||
SetInputValue(index, 0);
|
||||
break;
|
||||
}
|
||||
case NodeValue::kVideoParams:
|
||||
{
|
||||
VideoParamEdit* edit = static_cast<VideoParamEdit*>(sender());
|
||||
SetInputValue(QVariant::fromValue(edit->GetVideoParams()), 0);
|
||||
break;
|
||||
}
|
||||
case NodeValue::kAudioParams:
|
||||
// FIXME: No audio param widget yet
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -434,6 +415,8 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues()
|
||||
case NodeValue::kShaderJob:
|
||||
case NodeValue::kSampleJob:
|
||||
case NodeValue::kGenerateJob:
|
||||
case NodeValue::kVideoParams:
|
||||
case NodeValue::kAudioParams:
|
||||
break;
|
||||
case NodeValue::kInt:
|
||||
{
|
||||
@@ -533,15 +516,6 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues()
|
||||
cb->blockSignals(false);
|
||||
break;
|
||||
}
|
||||
case NodeValue::kVideoParams:
|
||||
{
|
||||
VideoParamEdit* edit = static_cast<VideoParamEdit*>(widgets_.first());
|
||||
edit->SetVideoParams(input_.GetValueAtTime(node_time).value<VideoParams>());
|
||||
break;
|
||||
}
|
||||
case NodeValue::kAudioParams:
|
||||
// FIXME: No audio param widget
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -796,15 +770,6 @@ void NodeParamViewWidgetBridge::PropertyChanged(const QString& input, const QStr
|
||||
ff->SetDirectoryMode(value.toBool());
|
||||
}
|
||||
}
|
||||
|
||||
// Parameters for video param objects
|
||||
if (data_type == NodeValue::kVideoParams) {
|
||||
VideoParamEdit* edit = static_cast<VideoParamEdit*>(widgets_.first());
|
||||
|
||||
if (key == QStringLiteral("mask")) {
|
||||
edit->SetParameterMask(value.toULongLong());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeParamViewWidgetBridge::InputDataTypeChanged(const QString &input, NodeValue::Type type)
|
||||
|
||||
@@ -114,6 +114,8 @@ void NodeTreeView::SetNodes(const QVector<Node *> &nodes)
|
||||
} else {
|
||||
delete node_item;
|
||||
}
|
||||
|
||||
node_item->setExpanded(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,10 @@ public:
|
||||
return dragging_playhead_;
|
||||
}
|
||||
|
||||
// To be called only by selection managers
|
||||
virtual void SelectionManagerSelectEvent(void *obj){}
|
||||
virtual void SelectionManagerDeselectEvent(void *obj){}
|
||||
|
||||
public slots:
|
||||
void SetTime(const rational &time);
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include <QGraphicsView>
|
||||
#include <QMouseEvent>
|
||||
#include <QRubberBand>
|
||||
#include <QToolTip>
|
||||
|
||||
#include "common/rational.h"
|
||||
@@ -36,7 +37,8 @@ class TimeBasedViewSelectionManager
|
||||
{
|
||||
public:
|
||||
TimeBasedViewSelectionManager(TimeBasedView *view) :
|
||||
view_(view)
|
||||
view_(view),
|
||||
rubberband_(nullptr)
|
||||
{}
|
||||
|
||||
void ClearDrawnObjects()
|
||||
@@ -51,6 +53,8 @@ public:
|
||||
|
||||
bool Select(T *key)
|
||||
{
|
||||
Q_ASSERT(key);
|
||||
|
||||
if (!IsSelected(key)) {
|
||||
selected_.append(key);
|
||||
return true;
|
||||
@@ -61,6 +65,8 @@ public:
|
||||
|
||||
bool Deselect(T *key)
|
||||
{
|
||||
Q_ASSERT(key);
|
||||
|
||||
return selected_.removeOne(key);
|
||||
}
|
||||
|
||||
@@ -84,35 +90,48 @@ public:
|
||||
timebase_ = tb;
|
||||
}
|
||||
|
||||
T *GetObjectAtPoint(const QPointF &scene_pt)
|
||||
{
|
||||
foreach (const DrawnObject &kp, drawn_objects_) {
|
||||
if (kp.second.contains(scene_pt)) {
|
||||
return kp.first;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
T *GetObjectAtPoint(const QPoint &pt)
|
||||
{
|
||||
return GetObjectAtPoint(view_->mapToScene(pt));
|
||||
}
|
||||
|
||||
T *MousePress(QMouseEvent *event)
|
||||
{
|
||||
T *key_under_cursor = nullptr;
|
||||
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
if (event->button() == Qt::LeftButton || event->button() == Qt::RightButton) {
|
||||
// See if there's a keyframe in this position
|
||||
QPointF scene_pos = view_->mapToScene(event->pos());
|
||||
foreach (const DrawnObject &kp, drawn_objects_) {
|
||||
if (kp.second.contains(scene_pos)) {
|
||||
key_under_cursor = kp.first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
key_under_cursor = GetObjectAtPoint(event->pos());
|
||||
|
||||
bool holding_shift = event->modifiers() & Qt::ShiftModifier;
|
||||
|
||||
if (IsSelected(key_under_cursor)) {
|
||||
if (holding_shift) {
|
||||
// If selected and holding shift, de-select this item but do nothing else
|
||||
Deselect(key_under_cursor);
|
||||
}
|
||||
} else {
|
||||
if (!key_under_cursor || !IsSelected(key_under_cursor)) {
|
||||
if (!holding_shift) {
|
||||
// If not already selecting and not holding shift, clear the current selection
|
||||
ClearSelection();
|
||||
}
|
||||
|
||||
// Add item to selection, either nothing if shift wasn't held, or the existing selection
|
||||
Select(key_under_cursor);
|
||||
if (key_under_cursor) {
|
||||
Select(key_under_cursor);
|
||||
view_->SelectionManagerSelectEvent(key_under_cursor);
|
||||
}
|
||||
} else if (holding_shift) {
|
||||
// If selected and holding shift, de-select this item but do nothing else
|
||||
Deselect(key_under_cursor);
|
||||
view_->SelectionManagerDeselectEvent(key_under_cursor);
|
||||
key_under_cursor = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,6 +204,51 @@ public:
|
||||
for (int i=0; i<selected_.size(); i++) {
|
||||
command->add_child(new SetTimeCommand(selected_.at(i), selected_.at(i)->time(), dragging_.at(i).time));
|
||||
}
|
||||
|
||||
dragging_.clear();
|
||||
}
|
||||
|
||||
void RubberBandStart(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton || event->button() == Qt::RightButton) {
|
||||
rubberband_start_ = event->pos();
|
||||
|
||||
rubberband_ = new QRubberBand(QRubberBand::Rectangle, view_);
|
||||
rubberband_->setGeometry(QRect(rubberband_start_.x(), rubberband_start_.y(), 0, 0));
|
||||
rubberband_->show();
|
||||
|
||||
rubberband_preselected_ = selected_;
|
||||
}
|
||||
}
|
||||
|
||||
void RubberBandMove(QMouseEvent *event)
|
||||
{
|
||||
if (IsRubberBanding()) {
|
||||
QRect band_rect = QRect(rubberband_start_, event->pos()).normalized();
|
||||
rubberband_->setGeometry(band_rect);
|
||||
|
||||
QRectF scene_rect = view_->mapToScene(band_rect).boundingRect();
|
||||
|
||||
selected_ = rubberband_preselected_;
|
||||
foreach (const DrawnObject &kp, drawn_objects_) {
|
||||
if (scene_rect.intersects(kp.second)) {
|
||||
Select(kp.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RubberBandStop()
|
||||
{
|
||||
if (IsRubberBanding()) {
|
||||
delete rubberband_;
|
||||
rubberband_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsRubberBanding() const
|
||||
{
|
||||
return rubberband_;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -249,6 +313,10 @@ private:
|
||||
|
||||
rational timebase_;
|
||||
|
||||
QRubberBand *rubberband_;
|
||||
QPoint rubberband_start_;
|
||||
QVector<T*> rubberband_preselected_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2021 Olive Team
|
||||
#
|
||||
# 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 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
widget/videoparamedit/videoparamedit.cpp
|
||||
widget/videoparamedit/videoparamedit.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -1,391 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "videoparamedit.h"
|
||||
|
||||
#include <QGridLayout>
|
||||
|
||||
namespace olive {
|
||||
|
||||
VideoParamEdit::VideoParamEdit(QWidget* parent) :
|
||||
QWidget(parent),
|
||||
color_manager_(nullptr),
|
||||
mask_(0)
|
||||
{
|
||||
QGridLayout* layout = new QGridLayout(this);
|
||||
|
||||
layout->setMargin(0);
|
||||
|
||||
int row = 0;
|
||||
|
||||
// Enabled
|
||||
enabled_lbl_ = new QLabel(tr("Enabled:"));
|
||||
layout->addWidget(enabled_lbl_, row, 0);
|
||||
enabled_box_ = new QCheckBox();
|
||||
connect(enabled_box_, &QCheckBox::clicked, this, &VideoParamEdit::Changed);
|
||||
layout->addWidget(enabled_box_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
// Width
|
||||
width_lbl_ = new QLabel(tr("Width:"));
|
||||
layout->addWidget(width_lbl_, row, 0);
|
||||
|
||||
width_slider_ = new IntegerSlider();
|
||||
width_slider_->SetMinimum(1);
|
||||
width_slider_->SetMaximum(32768);
|
||||
connect(width_slider_, &IntegerSlider::ValueChanged, this, &VideoParamEdit::Changed);
|
||||
layout->addWidget(width_slider_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
// Height
|
||||
height_lbl_ = new QLabel(tr("Height:"));
|
||||
layout->addWidget(height_lbl_, row, 0);
|
||||
|
||||
height_slider_ = new IntegerSlider();
|
||||
height_slider_->SetMinimum(1);
|
||||
height_slider_->SetMaximum(32768);
|
||||
connect(height_slider_, &IntegerSlider::ValueChanged, this, &VideoParamEdit::Changed);
|
||||
layout->addWidget(height_slider_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
// Depth
|
||||
depth_lbl_ = new QLabel(tr("Depth:"));
|
||||
layout->addWidget(depth_lbl_, row, 0);
|
||||
|
||||
depth_slider_ = new IntegerSlider();
|
||||
depth_slider_->SetMinimum(1);
|
||||
depth_slider_->SetMaximum(32768);
|
||||
connect(depth_slider_, &IntegerSlider::ValueChanged, this, &VideoParamEdit::Changed);
|
||||
layout->addWidget(depth_slider_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
// Pixel Format
|
||||
format_lbl_ = new QLabel(tr("Format:"));
|
||||
layout->addWidget(format_lbl_, row, 0);
|
||||
format_combobox_ = new PixelFormatComboBox(true);
|
||||
connect(format_combobox_, static_cast<void (PixelFormatComboBox::*)(int)>(&PixelFormatComboBox::currentIndexChanged), this, &VideoParamEdit::Changed);
|
||||
layout->addWidget(format_combobox_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
// Frame Rate
|
||||
frame_rate_lbl_ = new QLabel(tr("Frame Rate:"));
|
||||
layout->addWidget(frame_rate_lbl_, row, 0);
|
||||
|
||||
frame_rate_combobox_ = new FrameRateComboBox();
|
||||
connect(frame_rate_combobox_, &FrameRateComboBox::FrameRateChanged, this, &VideoParamEdit::Changed);
|
||||
layout->addWidget(frame_rate_combobox_, row, 1);
|
||||
|
||||
frame_rate_slider_ = new RationalSlider();
|
||||
frame_rate_slider_->SetMinimum(0);
|
||||
frame_rate_slider_->SetDecimalPlaces(3);
|
||||
frame_rate_slider_->SetAutoTrimDecimalPlaces(true);
|
||||
frame_rate_slider_->SetTimebase(rational(1, 1000)); // Drag interval
|
||||
frame_rate_slider_->DisableDisplayType(RationalSlider::kTime);
|
||||
connect(frame_rate_slider_, &RationalSlider::ValueChanged, this, &VideoParamEdit::Changed);
|
||||
layout->addWidget(frame_rate_slider_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
// Pixel Aspect Ratio
|
||||
pixel_aspect_lbl_ = new QLabel(tr("Pixel Aspect Ratio:"));
|
||||
layout->addWidget(pixel_aspect_lbl_, row, 0);
|
||||
|
||||
pixel_aspect_combobox_ = new PixelAspectRatioComboBox();
|
||||
connect(pixel_aspect_combobox_, static_cast<void (PixelAspectRatioComboBox::*)(int)>(&PixelAspectRatioComboBox::currentIndexChanged), this, &VideoParamEdit::Changed);
|
||||
layout->addWidget(pixel_aspect_combobox_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
// Interlacing
|
||||
interlaced_lbl_ = new QLabel(tr("Interlacing:"));
|
||||
layout->addWidget(interlaced_lbl_, row, 0);
|
||||
|
||||
interlaced_combobox_ = new InterlacedComboBox();
|
||||
connect(interlaced_combobox_, static_cast<void (InterlacedComboBox::*)(int)>(&InterlacedComboBox::currentIndexChanged), this, &VideoParamEdit::Changed);
|
||||
layout->addWidget(interlaced_combobox_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
// Channel Count
|
||||
channel_count_lbl_ = new QLabel(tr("Channel Count:"));
|
||||
layout->addWidget(channel_count_lbl_, row, 0);
|
||||
|
||||
channel_count_combobox_ = new QComboBox();
|
||||
channel_count_combobox_->addItem(tr("RGB"), VideoParams::kRGBChannelCount);
|
||||
channel_count_combobox_->addItem(tr("RGBA"), VideoParams::kRGBAChannelCount);
|
||||
connect(channel_count_combobox_, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &VideoParamEdit::Changed);
|
||||
layout->addWidget(channel_count_combobox_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
// Divider
|
||||
divider_lbl_ = new QLabel(tr("Divider:"));
|
||||
layout->addWidget(divider_lbl_, row, 0);
|
||||
|
||||
divider_combobox_ = new VideoDividerComboBox();
|
||||
connect(divider_combobox_, static_cast<void (VideoDividerComboBox::*)(int)>(&VideoDividerComboBox::currentIndexChanged), this, &VideoParamEdit::Changed);
|
||||
layout->addWidget(divider_combobox_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
// Stream Index
|
||||
stream_index_lbl_ = new QLabel(tr("Stream Index:"));
|
||||
layout->addWidget(stream_index_lbl_, row, 0);
|
||||
stream_index_slider_ = new IntegerSlider();
|
||||
stream_index_slider_->SetMinimum(0);
|
||||
connect(stream_index_slider_, &IntegerSlider::ValueChanged, this, &VideoParamEdit::Changed);
|
||||
layout->addWidget(stream_index_slider_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
// Video type
|
||||
video_type_lbl_ = new QLabel(tr("Video Type:"));
|
||||
layout->addWidget(video_type_lbl_, row, 0);
|
||||
video_type_combobox_ = new QComboBox();
|
||||
video_type_combobox_->addItem(tr("Video"), VideoParams::kVideoTypeVideo);
|
||||
video_type_combobox_->addItem(tr("Still"), VideoParams::kVideoTypeStill);
|
||||
video_type_combobox_->addItem(tr("Image Sequence"), VideoParams::kVideoTypeImageSequence);
|
||||
connect(video_type_combobox_, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &VideoParamEdit::Changed);
|
||||
layout->addWidget(video_type_combobox_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
// Start time (for image sequences)
|
||||
start_time_lbl_ = new QLabel(tr("Start Time"));
|
||||
layout->addWidget(start_time_lbl_, row, 0);
|
||||
|
||||
start_time_slider_ = new IntegerSlider();
|
||||
start_time_slider_->SetMinimum(0);
|
||||
connect(start_time_slider_, &IntegerSlider::ValueChanged, this, &VideoParamEdit::Changed);
|
||||
layout->addWidget(start_time_slider_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
// End time (for image sequences)
|
||||
end_time_lbl_ = new QLabel(tr("End Time"));
|
||||
layout->addWidget(end_time_lbl_, row, 0);
|
||||
|
||||
end_time_slider_ = new IntegerSlider();
|
||||
end_time_slider_->SetMinimum(0);
|
||||
connect(end_time_slider_, &IntegerSlider::ValueChanged, this, &VideoParamEdit::Changed);
|
||||
layout->addWidget(end_time_slider_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
// Premultiplied alpha
|
||||
premultiplied_alpha_lbl_ = new QLabel(tr("Premultiplied Alpha"));
|
||||
layout->addWidget(premultiplied_alpha_lbl_, row, 0);
|
||||
|
||||
premultiplied_alpha_box_ = new QCheckBox();
|
||||
connect(premultiplied_alpha_box_, &QCheckBox::clicked, this, &VideoParamEdit::Changed);
|
||||
layout->addWidget(premultiplied_alpha_box_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
// Colorspace
|
||||
colorspace_lbl_ = new QLabel(tr("Colorspace"));
|
||||
layout->addWidget(colorspace_lbl_, row, 0);
|
||||
|
||||
colorspace_combobox_ = new QComboBox();
|
||||
connect(colorspace_combobox_, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &VideoParamEdit::Changed);
|
||||
layout->addWidget(colorspace_combobox_, row, 1);
|
||||
}
|
||||
|
||||
void VideoParamEdit::SetParameterMask(uint64_t mask)
|
||||
{
|
||||
mask_ = mask;
|
||||
|
||||
width_lbl_->setVisible(mask & kWidthHeight);
|
||||
width_slider_->setVisible(mask & kWidthHeight);
|
||||
height_lbl_->setVisible(mask & kWidthHeight);
|
||||
height_slider_->setVisible(mask & kWidthHeight);
|
||||
|
||||
depth_lbl_->setVisible(mask & kDepth);
|
||||
depth_slider_->setVisible(mask & kDepth);
|
||||
|
||||
frame_rate_lbl_->setVisible(mask & kFrameRate);
|
||||
frame_rate_combobox_->setVisible((mask & kFrameRate) && !(mask & kFrameRateIsArbitrary));
|
||||
frame_rate_slider_->setVisible((mask & kFrameRate) && (mask & kFrameRateIsArbitrary));
|
||||
|
||||
pixel_aspect_lbl_->setVisible(mask & kPixelAspect);
|
||||
pixel_aspect_combobox_->setVisible(mask & kPixelAspect);
|
||||
|
||||
interlaced_lbl_->setVisible(mask & kInterlacing);
|
||||
interlaced_combobox_->setVisible(mask & kInterlacing);
|
||||
|
||||
enabled_lbl_->setVisible(mask & kEnabled);
|
||||
enabled_box_->setVisible(mask & kEnabled);
|
||||
|
||||
format_lbl_->setVisible(mask & kFormat);
|
||||
format_combobox_->setVisible(mask & kFormat);
|
||||
|
||||
channel_count_lbl_->setVisible(mask & kChannelCount);
|
||||
channel_count_combobox_->setVisible(mask & kChannelCount);
|
||||
|
||||
divider_lbl_->setVisible(mask & kDivider);
|
||||
divider_combobox_->setVisible(mask & kDivider);
|
||||
|
||||
stream_index_lbl_->setVisible(mask & kStreamIndex);
|
||||
stream_index_slider_->setVisible(mask & kStreamIndex);
|
||||
|
||||
video_type_lbl_->setVisible(mask & kIsImageSequence);
|
||||
video_type_combobox_->setVisible(mask & kIsImageSequence);
|
||||
|
||||
start_time_lbl_->setVisible(mask & kStartTime);
|
||||
start_time_slider_->setVisible(mask & kStartTime);
|
||||
|
||||
end_time_lbl_->setVisible(mask & kEndTime);
|
||||
end_time_slider_->setVisible(mask & kEndTime);
|
||||
|
||||
premultiplied_alpha_lbl_->setVisible(mask & kPremultipliedAlpha);
|
||||
premultiplied_alpha_box_->setVisible(mask & kPremultipliedAlpha);
|
||||
|
||||
colorspace_lbl_->setVisible(mask & kColorspace);
|
||||
colorspace_combobox_->setVisible(mask & kColorspace);
|
||||
}
|
||||
|
||||
VideoParams VideoParamEdit::GetVideoParams() const
|
||||
{
|
||||
VideoParams p;
|
||||
|
||||
p.set_enabled(enabled_box_->isChecked());
|
||||
p.set_width(width_slider_->GetValue());
|
||||
p.set_height(height_slider_->GetValue());
|
||||
p.set_depth(depth_slider_->GetValue());
|
||||
|
||||
{
|
||||
rational using_frame_rate;
|
||||
|
||||
if (mask_ & kFrameRateIsArbitrary) {
|
||||
using_frame_rate = frame_rate_slider_->GetValue();
|
||||
} else {
|
||||
using_frame_rate = frame_rate_combobox_->GetFrameRate();
|
||||
}
|
||||
|
||||
p.set_frame_rate(using_frame_rate);
|
||||
if (mask_ & kFrameRateIsNotTimebase) {
|
||||
// Frame rate editor will only edit the frame rate
|
||||
p.set_time_base(timebase_temp_);
|
||||
} else {
|
||||
p.set_time_base(using_frame_rate.flipped());
|
||||
}
|
||||
}
|
||||
|
||||
p.set_pixel_aspect_ratio(pixel_aspect_combobox_->GetPixelAspectRatio());
|
||||
p.set_interlacing(interlaced_combobox_->GetInterlaceMode());
|
||||
p.set_format(format_combobox_->GetPixelFormat());
|
||||
p.set_channel_count(channel_count_combobox_->currentData().toInt());
|
||||
p.set_divider(divider_combobox_->GetDivider());
|
||||
p.set_stream_index(stream_index_slider_->GetValue());
|
||||
p.set_video_type(static_cast<VideoParams::Type>(video_type_combobox_->currentData().toInt()));
|
||||
p.set_start_time(start_time_slider_->GetValue());
|
||||
p.set_duration(end_time_slider_->GetValue() - start_time_slider_->GetValue() + 1);
|
||||
p.set_premultiplied_alpha(premultiplied_alpha_box_->isChecked());
|
||||
p.set_colorspace(colorspace_combobox_->currentData().toString());
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void VideoParamEdit::SetVideoParams(const VideoParams &p)
|
||||
{
|
||||
blockSignals(true);
|
||||
|
||||
enabled_box_->setChecked(p.enabled());
|
||||
width_slider_->SetValue(p.width());
|
||||
height_slider_->SetValue(p.height());
|
||||
depth_slider_->SetValue(p.depth());
|
||||
|
||||
frame_rate_combobox_->SetFrameRate(p.frame_rate());
|
||||
frame_rate_slider_->SetValue(p.frame_rate());
|
||||
timebase_temp_ = p.time_base();
|
||||
|
||||
pixel_aspect_combobox_->SetPixelAspectRatio(p.pixel_aspect_ratio());
|
||||
interlaced_combobox_->SetInterlaceMode(p.interlacing());
|
||||
format_combobox_->SetPixelFormat(p.format());
|
||||
SetChannelCount(p.channel_count());
|
||||
divider_combobox_->SetDivider(p.divider());
|
||||
stream_index_slider_->SetValue(p.stream_index());
|
||||
SetVideoTypeComboBox(p.video_type());
|
||||
start_time_slider_->SetValue(p.start_time());
|
||||
end_time_slider_->SetValue(p.start_time() + p.duration() - 1);
|
||||
premultiplied_alpha_box_->setChecked(p.premultiplied_alpha());
|
||||
|
||||
if (color_manager_) {
|
||||
// Assume colorspace box has been populated correctly
|
||||
for (int i=0; i<colorspace_combobox_->count(); i++) {
|
||||
if (colorspace_combobox_->itemData(i).toString() == p.colorspace()) {
|
||||
colorspace_combobox_->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Box is empty, fill with single option so that it gets preserved in GetVideoParams()
|
||||
colorspace_combobox_->clear();
|
||||
colorspace_combobox_->addItem(p.colorspace(), p.colorspace());
|
||||
}
|
||||
|
||||
blockSignals(false);
|
||||
}
|
||||
|
||||
void VideoParamEdit::SetColorManager(ColorManager *cm)
|
||||
{
|
||||
color_manager_ = cm;
|
||||
|
||||
// Re-populate colorspace combobox
|
||||
colorspace_combobox_->clear();
|
||||
|
||||
if (color_manager_) {
|
||||
// Add default colorspace
|
||||
colorspace_combobox_->addItem(tr("Default (%1)").arg(color_manager_->GetDefaultInputColorSpace()), QString());
|
||||
|
||||
// Add remaining
|
||||
QStringList spaces = color_manager_->ListAvailableColorspaces();
|
||||
foreach (const QString& s, spaces) {
|
||||
colorspace_combobox_->addItem(s, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VideoParamEdit::SetChannelCount(int count)
|
||||
{
|
||||
for (int i=0; i<channel_count_combobox_->count(); i++) {
|
||||
if (channel_count_combobox_->itemData(i).toInt() == count) {
|
||||
channel_count_combobox_->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VideoParamEdit::SetVideoTypeComboBox(VideoParams::Type type)
|
||||
{
|
||||
for (int i=0; i<video_type_combobox_->count(); i++) {
|
||||
if (video_type_combobox_->itemData(i).toInt() == type) {
|
||||
video_type_combobox_->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef VIDEOPARAMEDIT_H
|
||||
#define VIDEOPARAMEDIT_H
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QLabel>
|
||||
#include <QWidget>
|
||||
|
||||
#include "node/color/colormanager/colormanager.h"
|
||||
#include "render/videoparams.h"
|
||||
#include "widget/slider/integerslider.h"
|
||||
#include "widget/slider/rationalslider.h"
|
||||
#include "widget/standardcombos/frameratecombobox.h"
|
||||
#include "widget/standardcombos/interlacedcombobox.h"
|
||||
#include "widget/standardcombos/pixelaspectratiocombobox.h"
|
||||
#include "widget/standardcombos/pixelformatcombobox.h"
|
||||
#include "widget/standardcombos/videodividercombobox.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class VideoParamEdit : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
VideoParamEdit(QWidget* parent = nullptr);
|
||||
|
||||
enum ParamMask {
|
||||
kNone = 0x0,
|
||||
kEnabled = 0x1,
|
||||
kWidthHeight = 0x2,
|
||||
kDepth = 0x4,
|
||||
kFrameRate = 0x8,
|
||||
kFormat = 0x10,
|
||||
kChannelCount = 0x20,
|
||||
kPixelAspect = 0x40,
|
||||
kInterlacing = 0x80,
|
||||
kDivider = 0x100,
|
||||
kStreamIndex = 0x200,
|
||||
kIsImageSequence = 0x400,
|
||||
kStartTime = 0x800,
|
||||
kEndTime = 0x1000,
|
||||
kPremultipliedAlpha = 0x2000,
|
||||
kColorspace = 0x4000,
|
||||
kFrameRateIsNotTimebase = 0x8000,
|
||||
kFrameRateIsArbitrary = 0x10000
|
||||
};
|
||||
|
||||
void SetParameterMask(uint64_t mask);
|
||||
|
||||
VideoParams GetVideoParams() const;
|
||||
void SetVideoParams(const VideoParams& p);
|
||||
|
||||
/**
|
||||
* @brief Set pointer to ColorManager
|
||||
*
|
||||
* Call this before calling SetVideoParams because it'll populate the colorspace list so it
|
||||
* can correctly be chosen from in the UI.
|
||||
*/
|
||||
void SetColorManager(ColorManager* cm);
|
||||
|
||||
int GetWidth() const
|
||||
{
|
||||
return width_slider_->GetValue();
|
||||
}
|
||||
|
||||
void SetWidth(int w)
|
||||
{
|
||||
width_slider_->SetValue(w);
|
||||
}
|
||||
|
||||
int GetHeight() const
|
||||
{
|
||||
return height_slider_->GetValue();
|
||||
}
|
||||
|
||||
void SetHeight(int h)
|
||||
{
|
||||
height_slider_->SetValue(h);
|
||||
}
|
||||
|
||||
rational GetFrameRate() const
|
||||
{
|
||||
return frame_rate_combobox_->GetFrameRate();
|
||||
}
|
||||
|
||||
void SetFrameRate(const rational& r)
|
||||
{
|
||||
frame_rate_combobox_->SetFrameRate(r);
|
||||
}
|
||||
|
||||
rational GetPixelAspectRatio() const
|
||||
{
|
||||
return pixel_aspect_combobox_->GetPixelAspectRatio();
|
||||
}
|
||||
|
||||
void SetPixelAspectRatio(const rational& r)
|
||||
{
|
||||
pixel_aspect_combobox_->SetPixelAspectRatio(r);
|
||||
}
|
||||
|
||||
VideoParams::Interlacing GetInterlaceMode() const
|
||||
{
|
||||
return interlaced_combobox_->GetInterlaceMode();
|
||||
}
|
||||
|
||||
void SetInterlaceMode(VideoParams::Interlacing i)
|
||||
{
|
||||
interlaced_combobox_->SetInterlaceMode(i);
|
||||
}
|
||||
|
||||
signals:
|
||||
void Changed();
|
||||
|
||||
private:
|
||||
void SetChannelCount(int count);
|
||||
|
||||
void SetVideoTypeComboBox(VideoParams::Type type);
|
||||
|
||||
QLabel* enabled_lbl_;
|
||||
QCheckBox* enabled_box_;
|
||||
QLabel* width_lbl_;
|
||||
IntegerSlider* width_slider_;
|
||||
QLabel* height_lbl_;
|
||||
IntegerSlider* height_slider_;
|
||||
QLabel* depth_lbl_;
|
||||
IntegerSlider* depth_slider_;
|
||||
QLabel* frame_rate_lbl_;
|
||||
FrameRateComboBox* frame_rate_combobox_;
|
||||
RationalSlider* frame_rate_slider_;
|
||||
QLabel* pixel_aspect_lbl_;
|
||||
PixelAspectRatioComboBox* pixel_aspect_combobox_;
|
||||
QLabel* interlaced_lbl_;
|
||||
InterlacedComboBox* interlaced_combobox_;
|
||||
QLabel* format_lbl_;
|
||||
PixelFormatComboBox* format_combobox_;
|
||||
QLabel* channel_count_lbl_;
|
||||
QComboBox* channel_count_combobox_;
|
||||
QLabel* divider_lbl_;
|
||||
VideoDividerComboBox* divider_combobox_;
|
||||
QLabel* stream_index_lbl_;
|
||||
IntegerSlider* stream_index_slider_;
|
||||
QLabel* video_type_lbl_;
|
||||
QComboBox* video_type_combobox_;
|
||||
QLabel* start_time_lbl_;
|
||||
IntegerSlider* start_time_slider_;
|
||||
QLabel* end_time_lbl_;
|
||||
IntegerSlider* end_time_slider_;
|
||||
QLabel* premultiplied_alpha_lbl_;
|
||||
QCheckBox* premultiplied_alpha_box_;
|
||||
QLabel* colorspace_lbl_;
|
||||
QComboBox* colorspace_combobox_;
|
||||
|
||||
ColorManager* color_manager_;
|
||||
|
||||
rational timebase_temp_;
|
||||
|
||||
uint64_t mask_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // VIDEOPARAMEDIT_H
|
||||
@@ -97,6 +97,7 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
node_panel_->Select(target, true);
|
||||
});
|
||||
connect(param_panel_, &ParamPanel::FocusedNodeChanged, sequence_viewer_panel_, &ViewerPanel::SetGizmos);
|
||||
connect(param_panel_, &ParamPanel::FocusedNodeChanged, curve_panel_, &CurvePanel::SetNode);
|
||||
|
||||
// Connect time signals together
|
||||
connect(sequence_viewer_panel_, &SequenceViewerPanel::TimeChanged, param_panel_, &ParamPanel::SetTime);
|
||||
|
||||
Reference in New Issue
Block a user