implemented bulk of curve editor's functionality
While certainly not entirely complete yet, this implements most of the UI functionality of the graph editor.
This commit is contained in:
+3
-3
@@ -373,6 +373,9 @@ void Core::StartGUI(bool full_screen)
|
||||
// Since we're starting GUI mode, create a PanelFocusManager (auto-deletes with QObject)
|
||||
PanelManager::CreateInstance();
|
||||
|
||||
// Initialize audio service
|
||||
AudioManager::CreateInstance();
|
||||
|
||||
// Connect the PanelFocusManager to the application's focus change signal
|
||||
connect(qApp,
|
||||
SIGNAL(focusChanged(QWidget*, QWidget*)),
|
||||
@@ -390,9 +393,6 @@ void Core::StartGUI(bool full_screen)
|
||||
// When a new project is opened, update the mainwindow
|
||||
connect(this, SIGNAL(ProjectOpened(Project*)), main_window_, SLOT(ProjectOpen(Project*)));
|
||||
|
||||
// Initialize audio service
|
||||
AudioManager::CreateInstance();
|
||||
|
||||
// Start autorecovery timer using the config value as its interval
|
||||
SetAutorecoveryInterval(Config::Current()["AutorecoveryInterval"].toInt());
|
||||
autorecovery_timer_.start();
|
||||
|
||||
@@ -10,10 +10,37 @@ CurvePanel::CurvePanel(QWidget *parent) :
|
||||
widget_ = new CurveWidget();
|
||||
setWidget(widget_);
|
||||
|
||||
connect(widget_, &CurveWidget::TimeChanged, this, &CurvePanel::TimeChanged);
|
||||
|
||||
// Set strings
|
||||
Retranslate();
|
||||
}
|
||||
|
||||
void CurvePanel::SetInput(NodeInput *input)
|
||||
{
|
||||
widget_->SetInput(input);
|
||||
}
|
||||
|
||||
void CurvePanel::SetTimebase(const rational &timebase)
|
||||
{
|
||||
widget_->SetTimebase(timebase);
|
||||
}
|
||||
|
||||
void CurvePanel::SetTime(const int64_t ×tamp)
|
||||
{
|
||||
widget_->SetTime(timestamp);
|
||||
}
|
||||
|
||||
void CurvePanel::ZoomIn()
|
||||
{
|
||||
widget_->SetScale(widget_->GetScale() * 2);
|
||||
}
|
||||
|
||||
void CurvePanel::ZoomOut()
|
||||
{
|
||||
widget_->SetScale(widget_->GetScale() * 0.5);
|
||||
}
|
||||
|
||||
void CurvePanel::changeEvent(QEvent *e)
|
||||
{
|
||||
if (e->type() == QEvent::LanguageChange) {
|
||||
|
||||
@@ -6,9 +6,24 @@
|
||||
|
||||
class CurvePanel : public PanelWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CurvePanel(QWidget* parent);
|
||||
|
||||
public slots:
|
||||
void SetInput(NodeInput* input);
|
||||
|
||||
void SetTimebase(const rational& timebase);
|
||||
|
||||
void SetTime(const int64_t& timestamp);
|
||||
|
||||
virtual void ZoomIn() override;
|
||||
|
||||
virtual void ZoomOut() override;
|
||||
|
||||
signals:
|
||||
void TimeChanged(const int64_t& timestamp);
|
||||
|
||||
protected:
|
||||
virtual void changeEvent(QEvent* e) override;
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ ParamPanel::ParamPanel(QWidget* parent) :
|
||||
|
||||
view_ = new NodeParamView(this);
|
||||
connect(view_, &NodeParamView::TimeChanged, this, &ParamPanel::TimeChanged);
|
||||
connect(view_, &NodeParamView::SelectedInputChanged, this, &ParamPanel::SelectedInputChanged);
|
||||
connect(view_, &NodeParamView::TimebaseChanged, this, &ParamPanel::TimebaseChanged);
|
||||
|
||||
setWidget(view_);
|
||||
|
||||
|
||||
@@ -42,6 +42,10 @@ public slots:
|
||||
signals:
|
||||
void TimeChanged(const int64_t& timestamp);
|
||||
|
||||
void SelectedInputChanged(NodeInput* input);
|
||||
|
||||
void TimebaseChanged(const rational& timebase);
|
||||
|
||||
protected:
|
||||
virtual void changeEvent(QEvent* e) override;
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "common/timecodefunctions.h"
|
||||
#include "panel/panelmanager.h"
|
||||
#include "panel/node/node.h"
|
||||
#include "panel/curve/curve.h"
|
||||
#include "panel/param/param.h"
|
||||
#include "panel/timeline/timeline.h"
|
||||
#include "panel/viewer/viewer.h"
|
||||
@@ -45,6 +46,7 @@ void Sequence::Open(SequencePtr sequence)
|
||||
TimelinePanel* timeline_panel = PanelManager::instance()->MostRecentlyFocused<TimelinePanel>();
|
||||
NodePanel* node_panel = PanelManager::instance()->MostRecentlyFocused<NodePanel>();
|
||||
ParamPanel* param_panel = PanelManager::instance()->MostRecentlyFocused<ParamPanel>();
|
||||
CurvePanel* curve_panel = PanelManager::instance()->MostRecentlyFocused<CurvePanel>();
|
||||
|
||||
viewer_panel->ConnectViewerNode(sequence->viewer_output_);
|
||||
timeline_panel->ConnectTimelineNode(sequence->timeline_output_);
|
||||
@@ -52,10 +54,16 @@ void Sequence::Open(SequencePtr sequence)
|
||||
|
||||
connect(timeline_panel, &TimelinePanel::TimeChanged, param_panel, &ParamPanel::SetTime);
|
||||
connect(timeline_panel, &TimelinePanel::TimeChanged, viewer_panel, &ViewerPanel::SetTime);
|
||||
connect(timeline_panel, &TimelinePanel::TimeChanged, curve_panel, &CurvePanel::SetTime);
|
||||
connect(viewer_panel, &ViewerPanel::TimeChanged, param_panel, &ParamPanel::SetTime);
|
||||
connect(viewer_panel, &ViewerPanel::TimeChanged, timeline_panel, &TimelinePanel::SetTime);
|
||||
connect(viewer_panel, &ViewerPanel::TimeChanged, curve_panel, &CurvePanel::SetTime);
|
||||
connect(param_panel, &ParamPanel::TimeChanged, viewer_panel, &ViewerPanel::SetTime);
|
||||
connect(param_panel, &ParamPanel::TimeChanged, timeline_panel, &TimelinePanel::SetTime);
|
||||
connect(param_panel, &ParamPanel::TimeChanged, curve_panel, &CurvePanel::SetTime);
|
||||
connect(curve_panel, &CurvePanel::TimeChanged, viewer_panel, &ViewerPanel::SetTime);
|
||||
connect(curve_panel, &CurvePanel::TimeChanged, timeline_panel, &TimelinePanel::SetTime);
|
||||
connect(curve_panel, &CurvePanel::TimeChanged, param_panel, &ParamPanel::SetTime);
|
||||
}
|
||||
|
||||
void Sequence::add_default_nodes()
|
||||
|
||||
@@ -16,9 +16,9 @@ AudioMonitor::AudioMonitor(QWidget *parent) :
|
||||
{
|
||||
clear_timer_.setInterval(kClearTimerInterval);
|
||||
|
||||
connect(AudioManager::instance(), SIGNAL(SentSamples(QVector<double>)), this, SLOT(SetValues(QVector<double>)));
|
||||
connect(&clear_timer_, SIGNAL(timeout()), this, SLOT(Clear()));
|
||||
connect(&clear_timer_, SIGNAL(timeout()), &clear_timer_, SLOT(stop()));
|
||||
connect(AudioManager::instance(), &AudioManager::SentSamples, this, &AudioMonitor::SetValues);
|
||||
connect(&clear_timer_, &QTimer::timeout, this, &AudioMonitor::Clear);
|
||||
connect(&clear_timer_, &QTimer::timeout, &clear_timer_, &QTimer::stop);
|
||||
}
|
||||
|
||||
void AudioMonitor::SetValues(QVector<double> values)
|
||||
|
||||
@@ -5,41 +5,197 @@
|
||||
#include "common/qtversionabstraction.h"
|
||||
|
||||
CurveView::CurveView(QWidget *parent) :
|
||||
TimelineViewBase(parent)
|
||||
TimelineViewBase(parent),
|
||||
y_scale_(1.0)
|
||||
{
|
||||
setAlignment(Qt::AlignLeft | Qt::AlignBottom);
|
||||
setDragMode(RubberBandDrag);
|
||||
setViewportUpdateMode(FullViewportUpdate);
|
||||
|
||||
text_padding_ = QFontMetricsWidth(fontMetrics(), QStringLiteral("i"));
|
||||
|
||||
minimum_grid_space_ = QFontMetricsWidth(fontMetrics(), QStringLiteral("00000"));
|
||||
}
|
||||
|
||||
void CurveView::Clear()
|
||||
{
|
||||
QMap<NodeKeyframe*, KeyframeViewItem*>::const_iterator iterator;
|
||||
|
||||
for (iterator=key_item_map_.begin();iterator!=key_item_map_.end();iterator++) {
|
||||
delete iterator.value();
|
||||
}
|
||||
|
||||
foreach (QGraphicsLineItem* line, lines_) {
|
||||
delete line;
|
||||
}
|
||||
|
||||
key_item_map_.clear();
|
||||
lines_.clear();
|
||||
}
|
||||
|
||||
void CurveView::SetXScale(const double& x_scale)
|
||||
{
|
||||
SetScale(x_scale);
|
||||
}
|
||||
|
||||
void CurveView::SetYScale(const double &y_scale)
|
||||
{
|
||||
y_scale_ = y_scale;
|
||||
viewport()->update();
|
||||
}
|
||||
|
||||
void CurveView::drawBackground(QPainter *painter, const QRectF &rect)
|
||||
{
|
||||
if (timebase().isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QVector<QLine> lines;
|
||||
|
||||
//painter->setPen(palette().window().color());
|
||||
double x_interval = timebase().flipped().toDouble();
|
||||
double y_interval = 100.0;
|
||||
|
||||
int grid_interval = 100;
|
||||
int x_grid_interval, y_grid_interval;
|
||||
|
||||
int x_start = qCeil(rect.left() / grid_interval) * grid_interval;
|
||||
int y_start = qCeil(rect.top() / grid_interval) * grid_interval;
|
||||
painter->setPen(QPen(palette().window().color(), 1));
|
||||
|
||||
QPoint viewport_edges(0, qRound(rect.height()));
|
||||
QPointF scene_edges = mapToScene(viewport_edges);
|
||||
//QRectF scene_edges = mapToScene(viewport()->geometry()).boundingRect();
|
||||
do {
|
||||
x_grid_interval = qRound(x_interval * scale_ * timebase_dbl());
|
||||
x_interval *= 2.0;
|
||||
} while (x_grid_interval < minimum_grid_space_);
|
||||
|
||||
do {
|
||||
y_grid_interval = qRound(y_interval * y_scale_);
|
||||
y_interval *= 2.0;
|
||||
} while (y_grid_interval < minimum_grid_space_);
|
||||
|
||||
int x_start = qCeil(rect.left() / x_grid_interval) * x_grid_interval;
|
||||
int y_start = qCeil(rect.top() / y_grid_interval) * y_grid_interval;
|
||||
|
||||
QPointF scene_bottom_left = mapToScene(QPoint(0, qRound(rect.height())));
|
||||
QPointF scene_top_right = mapToScene(QPoint(qRound(rect.width()), 0));
|
||||
|
||||
// Add vertical lines
|
||||
for (int i=x_start;i<rect.right();i+=grid_interval) {
|
||||
painter->drawText(i + text_padding_, qRound(scene_edges.y()) - text_padding_, QString::number(i));
|
||||
for (int i=x_start;i<rect.right();i+=x_grid_interval) {
|
||||
int value = qRound(static_cast<double>(i) / scale_ / timebase_dbl());
|
||||
painter->drawText(i + text_padding_, qRound(scene_bottom_left.y()) - text_padding_, QString::number(value));
|
||||
lines.append(QLine(i, qRound(rect.top()), i, qRound(rect.bottom())));
|
||||
}
|
||||
|
||||
// Add horizontal lines
|
||||
for (int i=y_start;i<rect.bottom();i+=grid_interval) {
|
||||
painter->drawText(qRound(scene_edges.x()) + text_padding_, i - text_padding_, QString::number(-i));
|
||||
for (int i=y_start;i<rect.bottom();i+=y_grid_interval) {
|
||||
int value = qRound(static_cast<double>(i) / y_scale_);
|
||||
painter->drawText(qRound(scene_bottom_left.x()) + text_padding_, i - text_padding_, QString::number(-value));
|
||||
lines.append(QLine(qRound(rect.left()), i, qRound(rect.right()), i));
|
||||
}
|
||||
|
||||
// Draw grid
|
||||
painter->drawLines(lines);
|
||||
|
||||
// Draw keyframe lines
|
||||
QList<NodeKeyframe*> keys = GetKeyframesSortedByTime();
|
||||
painter->setPen(QPen(palette().text().color(), 2));
|
||||
if (!keys.isEmpty()) {
|
||||
QVector<QLineF> keyframe_lines;
|
||||
|
||||
// Draw straight line leading to first keyframe
|
||||
QPointF first_key_pos = key_item_map_.value(keys.first())->center_pos();
|
||||
keyframe_lines.append(QLineF(QPointF(scene_bottom_left.x(), first_key_pos.y()), first_key_pos));
|
||||
|
||||
// Draw lines between each keyframe
|
||||
for (int i=1;i<keys.size();i++) {
|
||||
NodeKeyframe* before = keys.at(i-1);
|
||||
NodeKeyframe* after = keys.at(i);
|
||||
|
||||
KeyframeViewItem* before_item = key_item_map_.value(before);
|
||||
KeyframeViewItem* after_item = key_item_map_.value(after);
|
||||
|
||||
if (before->type() == NodeKeyframe::kHold) {
|
||||
// Draw a hold keyframe (basically a right angle)
|
||||
keyframe_lines.append(QLineF(before_item->center_pos().x(),
|
||||
before_item->center_pos().y(),
|
||||
after_item->center_pos().x(),
|
||||
before_item->center_pos().y()));
|
||||
keyframe_lines.append(QLineF(after_item->center_pos().x(),
|
||||
before_item->center_pos().y(),
|
||||
after_item->center_pos().x(),
|
||||
after_item->center_pos().y()));
|
||||
} else if (before->type() == NodeKeyframe::kBezier && after->type() == NodeKeyframe::kBezier) {
|
||||
// Draw a cubic bezier
|
||||
} else if (before->type() == NodeKeyframe::kBezier || after->type() == NodeKeyframe::kBezier) {
|
||||
// Draw a quadratic bezier
|
||||
} else {
|
||||
// Linear to linear
|
||||
keyframe_lines.append(QLineF(before_item->center_pos(), after_item->center_pos()));
|
||||
}
|
||||
}
|
||||
|
||||
// Draw straight line leading from end keyframe
|
||||
QPointF last_key_pos = key_item_map_.value(keys.last())->center_pos();
|
||||
keyframe_lines.append(QLineF(last_key_pos, QPointF(scene_top_right.x(), last_key_pos.y())));
|
||||
|
||||
painter->drawLines(keyframe_lines);
|
||||
}
|
||||
}
|
||||
|
||||
QList<NodeKeyframe *> CurveView::GetKeyframesSortedByTime()
|
||||
{
|
||||
QList<NodeKeyframe *> sorted;
|
||||
|
||||
QMap<NodeKeyframe*, KeyframeViewItem*>::const_iterator iterator;
|
||||
|
||||
for (iterator=key_item_map_.begin();iterator!=key_item_map_.end();iterator++) {
|
||||
NodeKeyframe* key = iterator.key();
|
||||
bool inserted = false;
|
||||
|
||||
for (int i=0;i<sorted.size();i++) {
|
||||
if (sorted.at(i)->time() > key->time()) {
|
||||
sorted.insert(i, key);
|
||||
inserted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!inserted) {
|
||||
sorted.append(key);
|
||||
}
|
||||
}
|
||||
|
||||
return sorted;
|
||||
}
|
||||
|
||||
qreal CurveView::GetItemYFromKeyframeValue(NodeKeyframe *key)
|
||||
{
|
||||
return -key->value().toDouble() * y_scale_;
|
||||
}
|
||||
|
||||
void CurveView::SetItemYFromKeyframeValue(NodeKeyframe *key, KeyframeViewItem *item)
|
||||
{
|
||||
item->SetOverrideY(GetItemYFromKeyframeValue(key));
|
||||
}
|
||||
|
||||
void CurveView::KeyframeValueChanged()
|
||||
{
|
||||
NodeKeyframe* key = static_cast<NodeKeyframe*>(sender());
|
||||
KeyframeViewItem* item = key_item_map_.value(key);
|
||||
|
||||
SetItemYFromKeyframeValue(key, item);
|
||||
}
|
||||
|
||||
void CurveView::AddKeyframe(NodeKeyframePtr key)
|
||||
{
|
||||
KeyframeViewItem* item = new KeyframeViewItem(key);
|
||||
SetItemYFromKeyframeValue(key.get(), item);
|
||||
item->SetScale(scale_);
|
||||
scene()->addItem(item);
|
||||
key_item_map_.insert(key.get(), item);
|
||||
|
||||
connect(key.get(), &NodeKeyframe::ValueChanged, this, &CurveView::KeyframeValueChanged);
|
||||
}
|
||||
|
||||
void CurveView::RemoveKeyframe(NodeKeyframePtr key)
|
||||
{
|
||||
disconnect(key.get(), &NodeKeyframe::ValueChanged, this, &CurveView::KeyframeValueChanged);
|
||||
|
||||
delete key_item_map_.take(key.get());
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef CURVEVIEW_H
|
||||
#define CURVEVIEW_H
|
||||
|
||||
#include "node/keyframe.h"
|
||||
#include "widget/keyframeview/keyframeviewitem.h"
|
||||
#include "widget/timelinewidget/view/timelineviewbase.h"
|
||||
|
||||
class CurveView : public TimelineViewBase
|
||||
@@ -8,12 +10,42 @@ class CurveView : public TimelineViewBase
|
||||
public:
|
||||
CurveView(QWidget* parent = nullptr);
|
||||
|
||||
void Clear();
|
||||
|
||||
void SetXScale(const double& x_scale);
|
||||
|
||||
void SetYScale(const double& y_scale);
|
||||
|
||||
public slots:
|
||||
void AddKeyframe(NodeKeyframePtr key);
|
||||
|
||||
void RemoveKeyframe(NodeKeyframePtr key);
|
||||
|
||||
protected:
|
||||
virtual void drawBackground(QPainter* painter, const QRectF& rect) override;
|
||||
|
||||
private:
|
||||
QList<NodeKeyframe*> GetKeyframesSortedByTime();
|
||||
|
||||
qreal GetItemYFromKeyframeValue(NodeKeyframe* key);
|
||||
|
||||
void SetItemYFromKeyframeValue(NodeKeyframe* key, KeyframeViewItem* item);
|
||||
|
||||
void AdjustLines();
|
||||
|
||||
int text_padding_;
|
||||
|
||||
double y_scale_;
|
||||
|
||||
int minimum_grid_space_;
|
||||
|
||||
QMap<NodeKeyframe*, KeyframeViewItem*> key_item_map_;
|
||||
|
||||
QList<QGraphicsLineItem*> lines_;
|
||||
|
||||
private slots:
|
||||
void KeyframeValueChanged();
|
||||
|
||||
};
|
||||
|
||||
#endif // CURVEVIEW_H
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
#include "curvewidget.h"
|
||||
|
||||
#include <QEvent>
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "core.h"
|
||||
#include "common/qtversionabstraction.h"
|
||||
#include "common/timecodefunctions.h"
|
||||
#include "node/node.h"
|
||||
#include "widget/keyframeview/keyframeviewundo.h"
|
||||
#include "widget/nodeparamview/nodeparamviewkeyframecontrol.h"
|
||||
|
||||
CurveWidget::CurveWidget(QWidget *parent) :
|
||||
QWidget(parent)
|
||||
QWidget(parent),
|
||||
input_(nullptr),
|
||||
bridge_(nullptr)
|
||||
{
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
|
||||
@@ -25,21 +33,235 @@ CurveWidget::CurveWidget(QWidget *parent) :
|
||||
linear_button_->setCheckable(true);
|
||||
linear_button_->setEnabled(false);
|
||||
top_controls->addWidget(linear_button_);
|
||||
connect(linear_button_, &QPushButton::clicked, this, &CurveWidget::KeyframeTypeButtonTriggered);
|
||||
|
||||
bezier_button_ = new QPushButton(tr("Bezier"));
|
||||
bezier_button_->setCheckable(true);
|
||||
bezier_button_->setEnabled(false);
|
||||
top_controls->addWidget(bezier_button_);
|
||||
connect(bezier_button_, &QPushButton::clicked, this, &CurveWidget::KeyframeTypeButtonTriggered);
|
||||
|
||||
hold_button_ = new QPushButton(tr("Hold"));
|
||||
hold_button_->setCheckable(true);
|
||||
hold_button_->setEnabled(false);
|
||||
top_controls->addWidget(hold_button_);
|
||||
connect(hold_button_, &QPushButton::clicked, this, &CurveWidget::KeyframeTypeButtonTriggered);
|
||||
|
||||
layout->addLayout(top_controls);
|
||||
|
||||
view_ = new CurveView();
|
||||
layout->addWidget(view_);
|
||||
// We use a separate layout for the ruler+view combination so that there's no spacing between them
|
||||
QVBoxLayout* ruler_view_layout = new QVBoxLayout();
|
||||
ruler_view_layout->setMargin(0);
|
||||
ruler_view_layout->setSpacing(0);
|
||||
|
||||
layout->addWidget(new QLabel("Values"));
|
||||
ruler_ = new TimeRuler();
|
||||
ruler_view_layout->addWidget(ruler_);
|
||||
|
||||
view_ = new CurveView();
|
||||
ruler_view_layout->addWidget(view_);
|
||||
|
||||
layout->addLayout(ruler_view_layout);
|
||||
|
||||
// Connect ruler and view together
|
||||
connect(ruler_, &TimeRuler::TimeChanged, view_, &CurveView::SetTime);
|
||||
connect(view_, &CurveView::TimeChanged, ruler_, &TimeRuler::SetTime);
|
||||
connect(ruler_, &TimeRuler::TimeChanged, this, &CurveWidget::TimeChanged);
|
||||
connect(view_, &CurveView::TimeChanged, this, &CurveWidget::TimeChanged);
|
||||
connect(ruler_, &TimeRuler::TimeChanged, this, &CurveWidget::UpdateBridgeTime);
|
||||
connect(view_, &CurveView::TimeChanged, this, &CurveWidget::UpdateBridgeTime);
|
||||
connect(view_->scene(), &QGraphicsScene::selectionChanged, this, &CurveWidget::SelectionChanged);
|
||||
|
||||
widget_bridge_layout_ = new QHBoxLayout();
|
||||
widget_bridge_layout_->addStretch();
|
||||
input_label_ = new QLabel();
|
||||
widget_bridge_layout_->addWidget(input_label_);
|
||||
widget_bridge_layout_->addStretch();
|
||||
layout->addLayout(widget_bridge_layout_);
|
||||
|
||||
SetScale(120.0);
|
||||
}
|
||||
|
||||
CurveWidget::~CurveWidget()
|
||||
{
|
||||
// Quick way to avoid segfault when QGraphicsScene::selectionChanged is emitted after the buttons have been destroyed
|
||||
view_->Clear();
|
||||
}
|
||||
|
||||
void CurveWidget::SetInput(NodeInput *input)
|
||||
{
|
||||
if (bridge_) {
|
||||
foreach (QWidget* bridge_widget, bridge_->widgets()) {
|
||||
delete bridge_widget;
|
||||
}
|
||||
delete bridge_;
|
||||
bridge_ = nullptr;
|
||||
}
|
||||
|
||||
if (input_) {
|
||||
disconnect(input_, &NodeInput::KeyframeAdded, view_, &CurveView::AddKeyframe);
|
||||
disconnect(input_, &NodeInput::KeyframeRemoved, view_, &CurveView::RemoveKeyframe);
|
||||
}
|
||||
|
||||
view_->Clear();
|
||||
|
||||
input_ = input;
|
||||
|
||||
if (input_) {
|
||||
bridge_ = new NodeParamViewWidgetBridge(input_, this);
|
||||
|
||||
for (int i=0;i<bridge_->widgets().size();i++) {
|
||||
// Insert between two stretches to center the widget
|
||||
widget_bridge_layout_->insertWidget(2 + i, bridge_->widgets().at(i));
|
||||
}
|
||||
|
||||
connect(input_, &NodeInput::KeyframeAdded, view_, &CurveView::AddKeyframe);
|
||||
connect(input_, &NodeInput::KeyframeRemoved, view_, &CurveView::RemoveKeyframe);
|
||||
|
||||
foreach (NodeKeyframePtr key, input_->keyframes()) {
|
||||
view_->AddKeyframe(key);
|
||||
}
|
||||
}
|
||||
|
||||
UpdateInputLabel();
|
||||
}
|
||||
|
||||
void CurveWidget::SetTimebase(const rational &timebase)
|
||||
{
|
||||
ruler_->SetTimebase(timebase);
|
||||
view_->SetTimebase(timebase);
|
||||
}
|
||||
|
||||
void CurveWidget::SetTime(const int64_t ×tamp)
|
||||
{
|
||||
ruler_->SetTime(timestamp);
|
||||
view_->SetTime(timestamp);
|
||||
UpdateBridgeTime(timestamp);
|
||||
}
|
||||
|
||||
const double &CurveWidget::GetScale()
|
||||
{
|
||||
return ruler_->scale();
|
||||
}
|
||||
|
||||
void CurveWidget::SetScale(const double &scale)
|
||||
{
|
||||
ruler_->SetScale(scale);
|
||||
view_->SetXScale(scale);
|
||||
}
|
||||
|
||||
void CurveWidget::changeEvent(QEvent *e)
|
||||
{
|
||||
if (e->type() == QEvent::LanguageChange) {
|
||||
UpdateInputLabel();
|
||||
}
|
||||
QWidget::changeEvent(e);
|
||||
}
|
||||
|
||||
void CurveWidget::UpdateInputLabel()
|
||||
{
|
||||
if (input_) {
|
||||
input_label_->setText(QStringLiteral("%1 :: %2:").arg(input_->parentNode()->Name(), input_->name()));
|
||||
} else {
|
||||
input_label_->clear();
|
||||
}
|
||||
}
|
||||
|
||||
void CurveWidget::SetKeyframeButtonEnabled(bool enable)
|
||||
{
|
||||
linear_button_->setEnabled(enable);
|
||||
bezier_button_->setEnabled(enable);
|
||||
hold_button_->setEnabled(enable);
|
||||
}
|
||||
|
||||
void CurveWidget::SetKeyframeButtonChecked(bool checked)
|
||||
{
|
||||
linear_button_->setChecked(checked);
|
||||
bezier_button_->setChecked(checked);
|
||||
hold_button_->setChecked(checked);
|
||||
}
|
||||
|
||||
void CurveWidget::SetKeyframeButtonCheckedFromType(NodeKeyframe::Type type)
|
||||
{
|
||||
linear_button_->setChecked(type == NodeKeyframe::kLinear);
|
||||
bezier_button_->setChecked(type == NodeKeyframe::kBezier);
|
||||
hold_button_->setChecked(type == NodeKeyframe::kHold);
|
||||
}
|
||||
|
||||
void CurveWidget::UpdateBridgeTime(const int64_t ×tamp)
|
||||
{
|
||||
if (!input_) {
|
||||
return;
|
||||
}
|
||||
|
||||
rational time = Timecode::timestamp_to_time(timestamp, view_->timebase());
|
||||
bridge_->SetTime(time);
|
||||
}
|
||||
|
||||
void CurveWidget::SelectionChanged()
|
||||
{
|
||||
QList<QGraphicsItem*> selected = view_->scene()->selectedItems();
|
||||
|
||||
SetKeyframeButtonChecked(false);
|
||||
SetKeyframeButtonEnabled(!selected.isEmpty());
|
||||
|
||||
if (!selected.isEmpty()) {
|
||||
bool all_same_type = true;
|
||||
NodeKeyframe::Type type = static_cast<KeyframeViewItem*>(selected.first())->key()->type();
|
||||
|
||||
for (int i=1;i<selected.size();i++) {
|
||||
KeyframeViewItem* prev_item = static_cast<KeyframeViewItem*>(selected.at(i-1));
|
||||
KeyframeViewItem* this_item = static_cast<KeyframeViewItem*>(selected.at(i));
|
||||
|
||||
if (prev_item->key()->type() != this_item->key()->type()) {
|
||||
all_same_type = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (all_same_type) {
|
||||
SetKeyframeButtonCheckedFromType(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CurveWidget::KeyframeTypeButtonTriggered(bool checked)
|
||||
{
|
||||
QPushButton* key_btn = static_cast<QPushButton*>(sender());
|
||||
|
||||
if (!checked) {
|
||||
// Keyframe buttons cannot be checked off, we undo this action here
|
||||
key_btn->setChecked(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get selected items and do nothing if there are none
|
||||
QList<QGraphicsItem*> selected = view_->scene()->selectedItems();
|
||||
if (selected.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set all selected keyframes to this type
|
||||
NodeKeyframe::Type new_type;
|
||||
|
||||
// Determine which type to set
|
||||
if (key_btn == bezier_button_) {
|
||||
new_type = NodeKeyframe::kBezier;
|
||||
} else if (key_btn == hold_button_) {
|
||||
new_type = NodeKeyframe::kHold;
|
||||
} else {
|
||||
new_type = NodeKeyframe::kLinear;
|
||||
}
|
||||
|
||||
// Ensure only the appropriate button is checked
|
||||
SetKeyframeButtonCheckedFromType(new_type);
|
||||
|
||||
QUndoCommand* command = new QUndoCommand();
|
||||
|
||||
foreach (QGraphicsItem* item, selected) {
|
||||
KeyframeViewItem* key_item = static_cast<KeyframeViewItem*>(item);
|
||||
|
||||
new KeyframeSetTypeCommand(key_item->key(), new_type, command);
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->push(command);
|
||||
}
|
||||
|
||||
@@ -1,25 +1,72 @@
|
||||
#ifndef CURVEWIDGET_H
|
||||
#define CURVEWIDGET_H
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QWidget>
|
||||
|
||||
#include "curveview.h"
|
||||
#include "node/input.h"
|
||||
#include "widget/nodeparamview/nodeparamviewwidgetbridge.h"
|
||||
#include "widget/timeruler/timeruler.h"
|
||||
|
||||
class CurveWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CurveWidget(QWidget* parent = nullptr);
|
||||
|
||||
virtual ~CurveWidget() override;
|
||||
|
||||
void SetInput(NodeInput* input);
|
||||
|
||||
void SetTimebase(const rational& timebase);
|
||||
|
||||
void SetTime(const int64_t& timestamp);
|
||||
|
||||
const double& GetScale();
|
||||
void SetScale(const double& scale);
|
||||
|
||||
signals:
|
||||
void TimeChanged(const int64_t& timestamp);
|
||||
|
||||
protected:
|
||||
virtual void changeEvent(QEvent *) override;
|
||||
|
||||
private:
|
||||
void UpdateInputLabel();
|
||||
|
||||
void SetKeyframeButtonEnabled(bool enable);
|
||||
|
||||
void SetKeyframeButtonChecked(bool checked);
|
||||
|
||||
void SetKeyframeButtonCheckedFromType(NodeKeyframe::Type type);
|
||||
|
||||
QPushButton* linear_button_;
|
||||
|
||||
QPushButton* bezier_button_;
|
||||
|
||||
QPushButton* hold_button_;
|
||||
|
||||
TimeRuler* ruler_;
|
||||
|
||||
CurveView* view_;
|
||||
|
||||
NodeInput* input_;
|
||||
|
||||
QLabel* input_label_;
|
||||
|
||||
QHBoxLayout* widget_bridge_layout_;
|
||||
|
||||
NodeParamViewWidgetBridge* bridge_;
|
||||
|
||||
private slots:
|
||||
void UpdateBridgeTime(const int64_t& timestamp);
|
||||
|
||||
void SelectionChanged();
|
||||
|
||||
void KeyframeTypeButtonTriggered(bool checked);
|
||||
|
||||
};
|
||||
|
||||
#endif // CURVEWIDGET_H
|
||||
|
||||
@@ -37,7 +37,8 @@ void KeyframeView::AddKeyframe(NodeKeyframePtr key, int y)
|
||||
QPoint local_pt = mapFromGlobal(global_pt);
|
||||
QPointF scene_pt = mapToScene(local_pt);
|
||||
|
||||
KeyframeViewItem* item = new KeyframeViewItem(key, scene_pt.y());
|
||||
KeyframeViewItem* item = new KeyframeViewItem(key);
|
||||
item->SetOverrideY(scene_pt.y());
|
||||
item->SetScale(scale_);
|
||||
item_map_.insert(key.get(), item);
|
||||
scene()->addItem(item);
|
||||
@@ -45,7 +46,7 @@ void KeyframeView::AddKeyframe(NodeKeyframePtr key, int y)
|
||||
|
||||
void KeyframeView::RemoveKeyframe(NodeKeyframePtr key)
|
||||
{
|
||||
delete item_map_.value(key.get());
|
||||
delete item_map_.take(key.get());
|
||||
}
|
||||
|
||||
void KeyframeView::mousePressEvent(QMouseEvent *event)
|
||||
|
||||
@@ -7,11 +7,11 @@
|
||||
|
||||
#include "common/qtversionabstraction.h"
|
||||
|
||||
KeyframeViewItem::KeyframeViewItem(NodeKeyframePtr key, qreal vcenter, QGraphicsItem *parent) :
|
||||
KeyframeViewItem::KeyframeViewItem(NodeKeyframePtr key, QGraphicsItem *parent) :
|
||||
QGraphicsRectItem(parent),
|
||||
key_(key),
|
||||
scale_(1.0),
|
||||
middle_(vcenter)
|
||||
vert_center_(0)
|
||||
{
|
||||
keyframe_size_ = QFontMetricsWidth(qApp->fontMetrics(), "Oi");
|
||||
setFlag(QGraphicsItem::ItemIsSelectable);
|
||||
@@ -24,6 +24,12 @@ KeyframeViewItem::KeyframeViewItem(NodeKeyframePtr key, qreal vcenter, QGraphics
|
||||
UpdatePos();
|
||||
}
|
||||
|
||||
void KeyframeViewItem::SetOverrideY(qreal vertical_center)
|
||||
{
|
||||
vert_center_ = vertical_center;
|
||||
UpdatePos();
|
||||
}
|
||||
|
||||
void KeyframeViewItem::SetScale(double scale)
|
||||
{
|
||||
scale_ = scale;
|
||||
@@ -35,6 +41,11 @@ NodeKeyframePtr KeyframeViewItem::key() const
|
||||
return key_;
|
||||
}
|
||||
|
||||
QPointF KeyframeViewItem::center_pos() const
|
||||
{
|
||||
return pos() + rect().center();
|
||||
}
|
||||
|
||||
void KeyframeViewItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
|
||||
{
|
||||
painter->setPen(Qt::black);
|
||||
@@ -71,7 +82,7 @@ void KeyframeViewItem::UpdatePos()
|
||||
{
|
||||
double x_center = key_->time().toDouble() * scale_;
|
||||
|
||||
setPos(x_center - keyframe_size_/2, middle_ - keyframe_size_/2);
|
||||
setPos(x_center - keyframe_size_/2, vert_center_ - keyframe_size_/2);
|
||||
}
|
||||
|
||||
void KeyframeViewItem::Redraw()
|
||||
|
||||
@@ -9,12 +9,16 @@ class KeyframeViewItem : public QObject, public QGraphicsRectItem
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
KeyframeViewItem(NodeKeyframePtr key, qreal vcenter, QGraphicsItem *parent = nullptr);
|
||||
KeyframeViewItem(NodeKeyframePtr key, QGraphicsItem *parent = nullptr);
|
||||
|
||||
void SetOverrideY(qreal vertical_center);
|
||||
|
||||
void SetScale(double scale);
|
||||
|
||||
NodeKeyframePtr key() const;
|
||||
|
||||
QPointF center_pos() const;
|
||||
|
||||
protected:
|
||||
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
|
||||
|
||||
@@ -23,7 +27,7 @@ private:
|
||||
|
||||
double scale_;
|
||||
|
||||
qreal middle_;
|
||||
qreal vert_center_;
|
||||
|
||||
int keyframe_size_;
|
||||
|
||||
|
||||
@@ -132,6 +132,7 @@ void NodeParamView::SetNodes(QList<Node *> nodes)
|
||||
connect(item, &NodeParamViewItem::KeyframeAdded, keyframe_view_, &KeyframeView::AddKeyframe);
|
||||
connect(item, &NodeParamViewItem::KeyframeRemoved, keyframe_view_, &KeyframeView::RemoveKeyframe);
|
||||
connect(item, &NodeParamViewItem::RequestSetTime, this, &NodeParamView::ItemRequestedTimeChanged);
|
||||
connect(item, &NodeParamViewItem::InputClicked, this, &NodeParamView::SelectedInputChanged);
|
||||
|
||||
items_.append(item);
|
||||
|
||||
@@ -147,6 +148,13 @@ void NodeParamView::SetNodes(QList<Node *> nodes)
|
||||
}
|
||||
|
||||
SetTime(0);
|
||||
|
||||
// FIXME: Test code only!
|
||||
if (nodes_.isEmpty()) {
|
||||
emit SelectedInputChanged(nullptr);
|
||||
} else {
|
||||
emit SelectedInputChanged(static_cast<NodeInput*>(nodes_.first()->parameters().first()));
|
||||
}
|
||||
}
|
||||
|
||||
void NodeParamView::resizeEvent(QResizeEvent *event)
|
||||
@@ -184,6 +192,8 @@ void NodeParamView::SetTimebase(const rational &timebase)
|
||||
{
|
||||
ruler_->SetTimebase(timebase);
|
||||
keyframe_view_->SetTimebase(timebase);
|
||||
|
||||
emit TimebaseChanged(timebase);
|
||||
}
|
||||
|
||||
void NodeParamView::UpdateItemTime(const int64_t ×tamp)
|
||||
|
||||
@@ -47,6 +47,10 @@ public slots:
|
||||
signals:
|
||||
void TimeChanged(const int64_t& timestamp);
|
||||
|
||||
void SelectedInputChanged(NodeInput* input);
|
||||
|
||||
void TimebaseChanged(const rational& timebase);
|
||||
|
||||
protected:
|
||||
virtual void resizeEvent(QResizeEvent *event) override;
|
||||
|
||||
|
||||
@@ -137,7 +137,8 @@ void NodeParamViewItem::SetupUI()
|
||||
NodeInput* input = static_cast<NodeInput*>(param);
|
||||
|
||||
// Add descriptor label
|
||||
QLabel* param_label = new QLabel();
|
||||
ClickableLabel* param_label = new ClickableLabel();
|
||||
connect(param_label, &ClickableLabel::MouseClicked, this, &NodeParamViewItem::LabelClicked);
|
||||
param_lbls_.append(param_label);
|
||||
|
||||
label_map_.insert(input, param_label);
|
||||
@@ -364,6 +365,17 @@ void NodeParamViewItem::GoToNextKey()
|
||||
}
|
||||
}
|
||||
|
||||
void NodeParamViewItem::LabelClicked()
|
||||
{
|
||||
ClickableLabel* lbl = static_cast<ClickableLabel*>(sender());
|
||||
|
||||
NodeInput* input = label_map_.key(lbl, nullptr);
|
||||
|
||||
if (input) {
|
||||
emit InputClicked(input);
|
||||
}
|
||||
}
|
||||
|
||||
NodeParamViewItemTitleBar::NodeParamViewItemTitleBar(QWidget *parent) :
|
||||
QWidget(parent)
|
||||
{
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "node/node.h"
|
||||
#include "nodeparamviewkeyframecontrol.h"
|
||||
#include "nodeparamviewwidgetbridge.h"
|
||||
#include "widget/clickablelabel/clickablelabel.h"
|
||||
|
||||
class NodeParamViewItemTitleBar : public QWidget {
|
||||
public:
|
||||
@@ -57,6 +58,8 @@ signals:
|
||||
|
||||
void RequestSetTime(const rational& time);
|
||||
|
||||
void InputClicked(NodeInput* input);
|
||||
|
||||
protected:
|
||||
virtual void changeEvent(QEvent *e) override;
|
||||
|
||||
@@ -77,7 +80,7 @@ private:
|
||||
|
||||
QLabel* title_bar_lbl_;
|
||||
|
||||
QVector<QLabel*> param_lbls_;
|
||||
QVector<ClickableLabel*> param_lbls_;
|
||||
|
||||
QPushButton* title_bar_collapse_btn_;
|
||||
|
||||
@@ -110,6 +113,8 @@ private slots:
|
||||
|
||||
void GoToNextKey();
|
||||
|
||||
void LabelClicked();
|
||||
|
||||
};
|
||||
|
||||
#endif // NODEPARAMVIEWITEM_H
|
||||
|
||||
@@ -31,6 +31,10 @@ void NodeParamViewWidgetBridge::SetTime(const rational &time)
|
||||
{
|
||||
time_ = time;
|
||||
|
||||
if (!input_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We assume the first data type is the "primary" type
|
||||
switch (input_->data_type()) {
|
||||
// None of these inputs have applicable UI widgets
|
||||
|
||||
@@ -248,13 +248,14 @@ void TimelineWidget::PointerTool::InitiateDrag(const TimelineCoordinate &mouse_p
|
||||
Timeline::MovementMode trim_mode = Timeline::kNone;
|
||||
|
||||
// FIXME: Hardcoded number
|
||||
const int kTrimHandle = 10;
|
||||
double kTrimHandle = qMax(10.0, parent()->TimeToScene(parent()->timebase()));
|
||||
|
||||
qreal mouse_x = parent()->TimeToScene(mouse_pos.GetFrame());
|
||||
|
||||
if (trimming_allowed_ && mouse_x < clicked_item->x() + kTrimHandle) {
|
||||
|
||||
if (trimming_allowed_ && mouse_x <= clicked_item->x() + kTrimHandle) {
|
||||
trim_mode = Timeline::kTrimIn;
|
||||
} else if (trimming_allowed_ && mouse_x > clicked_item->x() + clicked_item->rect().right() - kTrimHandle) {
|
||||
} else if (trimming_allowed_ && mouse_x >= clicked_item->x() + clicked_item->rect().right() - kTrimHandle) {
|
||||
trim_mode = Timeline::kTrimOut;
|
||||
} else if (movement_allowed_) {
|
||||
// Some derived classes don't allow movement
|
||||
|
||||
@@ -71,7 +71,9 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
curve_panel_ = PanelManager::instance()->CreatePanel<CurvePanel>(this);
|
||||
addDockWidget(Qt::BottomDockWidgetArea, curve_panel_);
|
||||
|
||||
connect(node_panel_, SIGNAL(SelectionChanged(QList<Node*>)), param_panel_, SLOT(SetNodes(QList<Node*>)));
|
||||
connect(node_panel_, &NodePanel::SelectionChanged, param_panel_, &ParamPanel::SetNodes);
|
||||
connect(param_panel_, &ParamPanel::SelectedInputChanged, curve_panel_, &CurvePanel::SetInput);
|
||||
connect(param_panel_, &ParamPanel::TimebaseChanged, curve_panel_, &CurvePanel::SetTimebase);
|
||||
}
|
||||
|
||||
void MainWindow::SetFullscreen(bool fullscreen)
|
||||
@@ -154,6 +156,6 @@ void MainWindow::SetDefaultLayout()
|
||||
Qt::Horizontal);
|
||||
|
||||
resizeDocks({project_panel_, tool_panel_, timeline_panel_, audio_monitor_panel_},
|
||||
{width()/4, 0, width(), 0},
|
||||
{width()/4, 1, width(), 1},
|
||||
Qt::Horizontal);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user