refined keyframeview and curveview

This commit is contained in:
itsmattkc
2021-12-15 19:45:04 -08:00
parent 1644b9e4c1
commit bf196e790b
33 changed files with 1013 additions and 1777 deletions
+4
View File
@@ -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
View File
@@ -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
View File
@@ -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);
-4
View File
@@ -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));
-2
View File
@@ -200,8 +200,6 @@ public:
static const QString kVideoAutoCacheInput;
static const QString kAudioAutoCacheInput;
static const uint64_t kVideoParamEditMask;
signals:
void FrameRateChanged(const rational&);
-34
View File
@@ -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)));
}
+12
View File
@@ -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;
-1
View File
@@ -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
+2 -4
View File
@@ -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
+238 -92
View File
@@ -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;
+36 -18
View File
@@ -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();
};
}
+7 -5
View File
@@ -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);
-2
View File
@@ -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
+426 -3
View File
@@ -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();
}
}
+105 -5
View File
@@ -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();
}
}
-139
View File
@@ -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);
};
}
+4 -6
View File
@@ -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);
}
+4 -4
View File
@@ -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)
+2
View File
@@ -114,6 +114,8 @@ void NodeTreeView::SetNodes(const QVector<Node *> &nodes)
} else {
delete node_item;
}
node_item->setExpanded(true);
}
}
+4
View File
@@ -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_;
};
}
-22
View File
@@ -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;
}
}
}
}
-182
View File
@@ -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
+1
View File
@@ -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);