From bf196e790b5a28d50ad37b6317da45b34ad7bbf9 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Wed, 15 Dec 2021 19:45:04 -0800 Subject: [PATCH] refined keyframeview and curveview --- app/common/bezier.cpp | 4 + .../sequence/sequencedialogparametertab.cpp | 47 +- .../sequence/sequencedialogparametertab.h | 22 +- app/node/node.cpp | 12 +- app/node/node.h | 6 +- app/node/output/viewer/viewer.cpp | 4 - app/node/output/viewer/viewer.h | 2 - app/node/project/footage/footage.cpp | 34 - app/panel/curve/curve.h | 12 + app/widget/CMakeLists.txt | 1 - app/widget/curvewidget/CMakeLists.txt | 6 +- .../curvewidget/beziercontrolpointitem.cpp | 106 --- .../curvewidget/beziercontrolpointitem.h | 68 -- app/widget/curvewidget/curveview.cpp | 330 +++++++--- app/widget/curvewidget/curveview.h | 54 +- app/widget/curvewidget/curvewidget.cpp | 12 +- app/widget/keyframeview/CMakeLists.txt | 2 - app/widget/keyframeview/keyframeview.cpp | 429 ++++++++++++- app/widget/keyframeview/keyframeview.h | 110 +++- app/widget/keyframeview/keyframeviewbase.cpp | 602 ------------------ app/widget/keyframeview/keyframeviewbase.h | 139 ---- .../keyframeviewinputconnection.cpp | 13 +- .../keyframeviewinputconnection.h | 8 +- app/widget/nodeparamview/nodeparamview.cpp | 10 +- app/widget/nodeparamview/nodeparamviewitem.h | 8 +- .../nodeparamviewwidgetbridge.cpp | 47 +- app/widget/nodetreeview/nodetreeview.cpp | 2 + app/widget/timebased/timebasedview.h | 4 + .../timebased/timebasedviewselectionmanager.h | 100 ++- app/widget/videoparamedit/CMakeLists.txt | 22 - app/widget/videoparamedit/videoparamedit.cpp | 391 ------------ app/widget/videoparamedit/videoparamedit.h | 182 ------ app/window/mainwindow/mainwindow.cpp | 1 + 33 files changed, 1013 insertions(+), 1777 deletions(-) delete mode 100644 app/widget/curvewidget/beziercontrolpointitem.cpp delete mode 100644 app/widget/curvewidget/beziercontrolpointitem.h delete mode 100644 app/widget/keyframeview/keyframeviewbase.cpp delete mode 100644 app/widget/keyframeview/keyframeviewbase.h delete mode 100644 app/widget/videoparamedit/CMakeLists.txt delete mode 100644 app/widget/videoparamedit/videoparamedit.cpp delete mode 100644 app/widget/videoparamedit/videoparamedit.h diff --git a/app/common/bezier.cpp b/app/common/bezier.cpp index 10fd88a2a..a1d900d57 100644 --- a/app/common/bezier.cpp +++ b/app/common/bezier.cpp @@ -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); diff --git a/app/dialog/sequence/sequencedialogparametertab.cpp b/app/dialog/sequence/sequencedialogparametertab.cpp index 9d77d8bc7..00f89e002 100644 --- a/app/dialog/sequence/sequencedialogparametertab.cpp +++ b/app/dialog/sequence/sequencedialogparametertab.cpp @@ -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), diff --git a/app/dialog/sequence/sequencedialogparametertab.h b/app/dialog/sequence/sequencedialogparametertab.h index 6d4d3bf08..e11561b2f 100644 --- a/app/dialog/sequence/sequencedialogparametertab.h +++ b/app/dialog/sequence/sequencedialogparametertab.h @@ -1,6 +1,7 @@ #ifndef SEQUENCEDIALOGPARAMETERTAB_H #define SEQUENCEDIALOGPARAMETERTAB_H +#include #include #include #include @@ -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_; diff --git a/app/node/node.cpp b/app/node/node.cpp index 4e850e821..ad406a8bb 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -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(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 diff --git a/app/node/node.h b/app/node/node.h index f38835ce9..92d06e63f 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -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); diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index 87707e2c1..8e257f6e9 100644 --- a/app/node/output/viewer/viewer.cpp +++ b/app/node/output/viewer/viewer.cpp @@ -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)); diff --git a/app/node/output/viewer/viewer.h b/app/node/output/viewer/viewer.h index 5c516000c..2be79bf2f 100644 --- a/app/node/output/viewer/viewer.h +++ b/app/node/output/viewer/viewer.h @@ -200,8 +200,6 @@ public: static const QString kVideoAutoCacheInput; static const QString kAudioAutoCacheInput; - static const uint64_t kVideoParamEditMask; - signals: void FrameRateChanged(const rational&); diff --git a/app/node/project/footage/footage.cpp b/app/node/project/footage/footage.cpp index 4ab67ebb8..a9aa54705 100644 --- a/app/node/project/footage/footage.cpp +++ b/app/node/project/footage/footage.cpp @@ -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 nodes; + + if (node) { + nodes.append(node); + } + + SetNodes(nodes); + } + void SetNodes(const QVector &nodes); virtual void IncreaseTrackHeight() override; diff --git a/app/widget/CMakeLists.txt b/app/widget/CMakeLists.txt index 38fefccbd..915214d9e 100644 --- a/app/widget/CMakeLists.txt +++ b/app/widget/CMakeLists.txt @@ -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 diff --git a/app/widget/curvewidget/CMakeLists.txt b/app/widget/curvewidget/CMakeLists.txt index 1ceeadf85..baafedb04 100644 --- a/app/widget/curvewidget/CMakeLists.txt +++ b/app/widget/curvewidget/CMakeLists.txt @@ -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 ) diff --git a/app/widget/curvewidget/beziercontrolpointitem.cpp b/app/widget/curvewidget/beziercontrolpointitem.cpp deleted file mode 100644 index 26f062803..000000000 --- a/app/widget/curvewidget/beziercontrolpointitem.cpp +++ /dev/null @@ -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 . - -***/ - -#include "beziercontrolpointitem.h" - -#include -#include -#include -#include - -#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()); -} - -} diff --git a/app/widget/curvewidget/beziercontrolpointitem.h b/app/widget/curvewidget/beziercontrolpointitem.h deleted file mode 100644 index 26d87f8dc..000000000 --- a/app/widget/curvewidget/beziercontrolpointitem.h +++ /dev/null @@ -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 . - -***/ - -#ifndef BEZIERCONTROLPOINTITEM_H -#define BEZIERCONTROLPOINTITEM_H - -#include - -#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 diff --git a/app/widget/curvewidget/curveview.cpp b/app/widget/curvewidget/curveview.cpp index e83909702..8fee9d725 100644 --- a/app/widget/curvewidget/curveview.cpp +++ b/app/widget/curvewidget/curveview.cpp @@ -20,31 +20,33 @@ #include "curveview.h" +#include #include #include +#include #include #include -#include #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 bezier_lines; - foreach (BezierControlPointItem* item, bezier_control_points_) { - // All BezierControlPointItems should be children of a KeyframeViewItem - KeyframeViewItem* par = static_cast(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; ivalue(); + } + + 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; iparent(); + 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; iset_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(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; iadd_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 &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(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 selected = scene()->selectedItems(); - - foreach (QGraphicsItem* item, selected) { - KeyframeViewItem* this_item = static_cast(item); - - if (this_item->key()->type() == NodeKeyframe::kBezier) { - CreateBezierControlPoints(this_item); - } - } - */ -} - -void CurveView::BezierControlPointDestroyed() -{ - BezierControlPointItem* item = static_cast(sender()); - bezier_control_points_.removeOne(item); -} - void CurveView::ZoomToFit() { QVector keys; diff --git a/app/widget/curvewidget/curveview.h b/app/widget/curvewidget/curveview.h index e5b056830..3858368e8 100644 --- a/app/widget/curvewidget/curveview.h +++ b/app/widget/curvewidget/curveview.h @@ -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 &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 keyframe_colors_; QHash track_connections_; @@ -82,21 +94,27 @@ private: int minimum_grid_space_; - QVector lines_; - - QVector bezier_control_points_; - QVector connected_inputs_; + struct BezierPoint + { + QRectF rect; + NodeKeyframe *keyframe; + NodeKeyframe::BezierType type; + }; + + QVector bezier_pts_; + const BezierPoint *dragging_bezier_pt_; + + QPointF dragging_bezier_point_start_; + QPointF dragging_bezier_point_opposing_start_; + QPointF drag_start_; + + QVector drag_keyframe_values_; + private slots: - void KeyframeValueChanged(); - void KeyframeTypeChanged(); - void SelectionChanged(); - - void BezierControlPointDestroyed(); - }; } diff --git a/app/widget/curvewidget/curvewidget.cpp b/app/widget/curvewidget/curvewidget.cpp index c77a553aa..6dac944dd 100644 --- a/app/widget/curvewidget/curvewidget.cpp +++ b/app/widget/curvewidget/curvewidget.cpp @@ -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 &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); diff --git a/app/widget/keyframeview/CMakeLists.txt b/app/widget/keyframeview/CMakeLists.txt index 1ab460182..d61022fbe 100644 --- a/app/widget/keyframeview/CMakeLists.txt +++ b/app/widget/keyframeview/CMakeLists.txt @@ -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 diff --git a/app/widget/keyframeview/keyframeview.cpp b/app/widget/keyframeview/keyframeview.cpp index f8cf5413f..cec7aca99 100644 --- a/app/widget/keyframeview/keyframeview.cpp +++ b/app/widget/keyframeview/keyframeview.cpp @@ -20,15 +20,334 @@ #include "keyframeview.h" +#include +#include +#include + +#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& tracks = input.node()->GetKeyframeTracks(input); + ElementConnections vec(tracks.size()); + + for (int i=0; iGetKeyframes()) { + 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(obj); + QVector 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(obj); + QVector 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;itype() != 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(); +} + } diff --git a/app/widget/keyframeview/keyframeview.h b/app/widget/keyframeview/keyframeview.h index 56874232a..a0c1aa99a 100644 --- a/app/widget/keyframeview/keyframeview.h +++ b/app/widget/keyframeview/keyframeview.h @@ -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; + using InputConnections = QVector; + using NodeConnections = QMap; + + 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 &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 tracks_; + + TimeBasedViewSelectionManager 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 diff --git a/app/widget/keyframeview/keyframeviewbase.cpp b/app/widget/keyframeview/keyframeviewbase.cpp deleted file mode 100644 index c3be1e273..000000000 --- a/app/widget/keyframeview/keyframeviewbase.cpp +++ /dev/null @@ -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 . - -***/ - -#include "keyframeviewbase.h" - -#include -#include -#include - -#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& tracks = input.node()->GetKeyframeTracks(input); - ElementConnections vec(tracks.size()); - - for (int i=0; iGetKeyframes()) { - 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(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(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;itype() != 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 copy = GetSelectedKeyframes(); - foreach (NodeKeyframe *key, copy) { - rational key_time = key->time(); - - QVector 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(); -} - -} diff --git a/app/widget/keyframeview/keyframeviewbase.h b/app/widget/keyframeview/keyframeviewbase.h deleted file mode 100644 index 2ce0673f7..000000000 --- a/app/widget/keyframeview/keyframeviewbase.h +++ /dev/null @@ -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 . - -***/ - -#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; - using InputConnections = QVector; - using NodeConnections = QMap; - - 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 &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 tracks_; - - bool currently_autoselecting_; - - bool dragging_; - - TimeBasedViewSelectionManager selection_manager_; - -private slots: - void ShowContextMenu(); - - void ShowKeyframePropertiesDialog(); - - void AutoSelectKeyTimeNeighbors(); - - void Redraw(); - -}; - -} - -#endif // KEYFRAMEVIEWBASE_H diff --git a/app/widget/keyframeview/keyframeviewinputconnection.cpp b/app/widget/keyframeview/keyframeviewinputconnection.cpp index a8e9a35e5..b7b3c2dca 100644 --- a/app/widget/keyframeview/keyframeviewinputconnection.cpp +++ b/app/widget/keyframeview/keyframeviewinputconnection.cpp @@ -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(); + } +} + } diff --git a/app/widget/keyframeview/keyframeviewinputconnection.h b/app/widget/keyframeview/keyframeviewinputconnection.h index b8f951d22..f10f37d2b 100644 --- a/app/widget/keyframeview/keyframeviewinputconnection.h +++ b/app/widget/keyframeview/keyframeviewinputconnection.h @@ -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); + }; } diff --git a/app/widget/nodeparamview/nodeparamview.cpp b/app/widget/nodeparamview/nodeparamview.cpp index b2e219478..e21ead255 100644 --- a/app/widget/nodeparamview/nodeparamview.cpp +++ b/app/widget/nodeparamview/nodeparamview.cpp @@ -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); } diff --git a/app/widget/nodeparamview/nodeparamviewitem.h b/app/widget/nodeparamview/nodeparamviewitem.h index 1e0aae242..9e45f55f7 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.h +++ b/app/widget/nodeparamview/nodeparamviewitem.h @@ -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_; }; diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp index e54350ef4..dda919eee 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp @@ -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(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(widgets_.first()); - edit->SetVideoParams(input_.GetValueAtTime(node_time).value()); - 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(widgets_.first()); - - if (key == QStringLiteral("mask")) { - edit->SetParameterMask(value.toULongLong()); - } - } } void NodeParamViewWidgetBridge::InputDataTypeChanged(const QString &input, NodeValue::Type type) diff --git a/app/widget/nodetreeview/nodetreeview.cpp b/app/widget/nodetreeview/nodetreeview.cpp index a1ee354a3..a2f8ec9df 100644 --- a/app/widget/nodetreeview/nodetreeview.cpp +++ b/app/widget/nodetreeview/nodetreeview.cpp @@ -114,6 +114,8 @@ void NodeTreeView::SetNodes(const QVector &nodes) } else { delete node_item; } + + node_item->setExpanded(true); } } diff --git a/app/widget/timebased/timebasedview.h b/app/widget/timebased/timebasedview.h index 247948b61..f6f46d285 100644 --- a/app/widget/timebased/timebasedview.h +++ b/app/widget/timebased/timebasedview.h @@ -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); diff --git a/app/widget/timebased/timebasedviewselectionmanager.h b/app/widget/timebased/timebasedviewselectionmanager.h index d1f53d7d6..19d61e203 100644 --- a/app/widget/timebased/timebasedviewselectionmanager.h +++ b/app/widget/timebased/timebasedviewselectionmanager.h @@ -23,6 +23,7 @@ #include #include +#include #include #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; iadd_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 rubberband_preselected_; + }; } diff --git a/app/widget/videoparamedit/CMakeLists.txt b/app/widget/videoparamedit/CMakeLists.txt deleted file mode 100644 index bd799b34d..000000000 --- a/app/widget/videoparamedit/CMakeLists.txt +++ /dev/null @@ -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 . - -set(OLIVE_SOURCES - ${OLIVE_SOURCES} - widget/videoparamedit/videoparamedit.cpp - widget/videoparamedit/videoparamedit.h - PARENT_SCOPE -) diff --git a/app/widget/videoparamedit/videoparamedit.cpp b/app/widget/videoparamedit/videoparamedit.cpp deleted file mode 100644 index 13ddfc825..000000000 --- a/app/widget/videoparamedit/videoparamedit.cpp +++ /dev/null @@ -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 . - -***/ - -#include "videoparamedit.h" - -#include - -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(&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(&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(&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(&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(&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(&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(&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(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; icount(); 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; icount(); 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; icount(); i++) { - if (video_type_combobox_->itemData(i).toInt() == type) { - video_type_combobox_->setCurrentIndex(i); - break; - } - } -} - -} diff --git a/app/widget/videoparamedit/videoparamedit.h b/app/widget/videoparamedit/videoparamedit.h deleted file mode 100644 index 0aa372869..000000000 --- a/app/widget/videoparamedit/videoparamedit.h +++ /dev/null @@ -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 . - -***/ - -#ifndef VIDEOPARAMEDIT_H -#define VIDEOPARAMEDIT_H - -#include -#include -#include - -#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 diff --git a/app/window/mainwindow/mainwindow.cpp b/app/window/mainwindow/mainwindow.cpp index 2cbf094d0..bceb6a935 100644 --- a/app/window/mainwindow/mainwindow.cpp +++ b/app/window/mainwindow/mainwindow.cpp @@ -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);