refined curve view

This commit is contained in:
itsmattkc
2021-12-17 17:07:59 -08:00
parent bf196e790b
commit 619de01db4
16 changed files with 309 additions and 253 deletions
+129 -69
View File
@@ -30,7 +30,6 @@
#include "common/qtutils.h"
#include "widget/keyframeview/keyframeviewundo.h"
#include "widget/nodeparamview/nodeparamviewundo.h"
#include "widget/slider/floatslider.h"
namespace olive {
@@ -61,6 +60,9 @@ void CurveView::ConnectInput(const NodeKeyframeTrackReference& ref)
track_con->SetBrush(keyframe_colors_.value(ref));
track_connections_.insert(ref, track_con);
// Signal to CurveWidget to update its bezier/linear/hold buttons if a key type changes
connect(track_con, &KeyframeViewInputConnection::TypeChanged, this, &CurveView::SelectionChanged);
// Append to the list
connected_inputs_.append(ref);
}
@@ -90,13 +92,6 @@ void CurveView::SelectKeyframesOfInput(const NodeKeyframeTrackReference& ref)
}
}
void CurveView::ZoomToFitInput(const NodeKeyframeTrackReference& ref)
{
if (KeyframeViewInputConnection *con = track_connections_.value(ref)) {
ZoomToFitInternal(con->GetKeyframes());
}
}
void CurveView::SetKeyframeTrackColor(const NodeKeyframeTrackReference &ref, const QColor &color)
{
// Insert color into hashmap
@@ -265,8 +260,28 @@ void CurveView::ContextMenuEvent(Menu &m)
void CurveView::SceneRectUpdateEvent(QRectF &r)
{
r.setTop(r.top() - this->height());
r.setBottom(r.bottom() + this->height());
double min_val, max_val;
bool got_val = false;
foreach (KeyframeViewInputConnection *con, track_connections_) {
foreach (NodeKeyframe *key, con->GetKeyframes()) {
qreal key_y = GetItemYFromKeyframeValue(key);
if (got_val) {
min_val = qMin(key_y, min_val);
max_val = qMax(key_y, max_val);
} else {
min_val = key_y;
max_val = key_y;
got_val = true;
}
}
}
if (got_val) {
r.setTop(min_val - this->height());
r.setBottom(max_val + this->height());
}
}
qreal CurveView::GetKeyframeSceneY(KeyframeViewInputConnection *track, NodeKeyframe *key)
@@ -392,7 +407,8 @@ void CurveView::KeyframeDragStart(QMouseEvent *event)
{
drag_keyframe_values_.resize(GetSelectedKeyframes().size());
for (int i=0; i<GetSelectedKeyframes().size(); i++) {
drag_keyframe_values_[i] = GetSelectedKeyframes().at(i)->value();
NodeKeyframe *key = GetSelectedKeyframes().at(i);
drag_keyframe_values_[i] = key->value();
}
drag_start_ = mapToScene(event->pos());
@@ -401,7 +417,11 @@ void CurveView::KeyframeDragStart(QMouseEvent *event)
void CurveView::KeyframeDragMove(QMouseEvent *event, QString &tip)
{
if (event->modifiers() & Qt::ShiftModifier) {
// Lock to X axis only
// Lock to X axis only and set original values on all keys
for (int i=0; i<GetSelectedKeyframes().size(); i++) {
NodeKeyframe *key = GetSelectedKeyframes().at(i);
key->set_value(drag_keyframe_values_.at(i));
}
return;
}
@@ -412,10 +432,11 @@ void CurveView::KeyframeDragMove(QMouseEvent *event, QString &tip)
for (int i=0; i<GetSelectedKeyframes().size(); i++) {
NodeKeyframe *key = GetSelectedKeyframes().at(i);
FloatSlider::DisplayType display = GetFloatDisplayTypeFromKeyframe(key);
Node* node = key->parent();
double original_val = drag_keyframe_values_.at(i).toDouble();
double original_val = FloatSlider::TransformValueToDisplay(drag_keyframe_values_.at(i).toDouble(), display);
const QString& input = key->input();
double new_val = original_val - scaled_diff;
double new_val = FloatSlider::TransformDisplayToValue(original_val - scaled_diff, display);
double limited = new_val;
if (node->HasInputProperty(input, QStringLiteral("min"))) {
@@ -434,23 +455,18 @@ void CurveView::KeyframeDragMove(QMouseEvent *event, QString &tip)
// Set values
for (int i=0; i<GetSelectedKeyframes().size(); i++) {
NodeKeyframe *key = GetSelectedKeyframes().at(i);
key->set_value(drag_keyframe_values_.at(i).toDouble() - scaled_diff);
FloatSlider::DisplayType display = GetFloatDisplayTypeFromKeyframe(key);
key->set_value(FloatSlider::TransformDisplayToValue(FloatSlider::TransformValueToDisplay(drag_keyframe_values_.at(i).toDouble(), display) - scaled_diff, display));
}
NodeKeyframe *tip_item = GetSelectedKeyframes().first();
FloatSlider::DisplayType display_type = FloatSlider::kNormal;
Node* initial_drag_input = tip_item->parent();
const QString& initial_drag_input_id = tip_item->input();
if (initial_drag_input->HasInputProperty(initial_drag_input_id, QStringLiteral("view"))) {
display_type = static_cast<FloatSlider::DisplayType>(initial_drag_input->GetInputProperty(initial_drag_input_id, QStringLiteral("view")).toInt());
}
bool ok;
double num_value = tip_item->value().toDouble(&ok);
if (ok) {
tip = QStringLiteral("%1\n");
tip.append(FloatSlider::ValueToString(num_value, display_type, 2, true));
tip.append(FloatSlider::ValueToString(num_value + GetOffsetFromKeyframe(tip_item), GetFloatDisplayTypeFromKeyframe(tip_item), 2, true));
}
}
@@ -458,8 +474,10 @@ void CurveView::KeyframeDragRelease(QMouseEvent *event, MultiUndoCommand *comman
{
for (int i=0; i<GetSelectedKeyframes().size(); i++) {
NodeKeyframe *k = GetSelectedKeyframes().at(i);
if (!qFuzzyCompare(k->value().toDouble(), drag_keyframe_values_.at(i).toDouble())) {
command->add_child(new NodeParamSetKeyframeValueCommand(k, k->value(), drag_keyframe_values_.at(i)));
}
}
}
QPointF CurveView::GenerateBezierControlPosition(const NodeKeyframe::BezierType mode, const QPointF &start_point, const QPointF &scaled_cursor_diff)
@@ -488,53 +506,88 @@ QPointF CurveView::GetScaledCursorPos(const QPointF &cursor_pos)
cursor_pos.y() / GetYScale());
}
void CurveView::ZoomToFitInternal(const QVector<NodeKeyframe *> &keys)
void CurveView::ZoomToFitInternal(bool selected_only)
{
if (keys.isEmpty()) {
// Prevent scaling to DBL_MIN/DBL_MAX
return;
}
bool got_val = false;
rational min_time = RATIONAL_MAX;
rational max_time = RATIONAL_MIN;
rational min_time, max_time;
double min_val, max_val;
double min_val = DBL_MAX;
double max_val = DBL_MIN;
foreach (NodeKeyframe* key, keys) {
foreach (KeyframeViewInputConnection *con, track_connections_) {
foreach (NodeKeyframe *key, con->GetKeyframes()) {
if (!selected_only || IsKeyframeSelected(key)) {
rational transformed_time = GetAdjustedTime(key->parent(),
GetTimeTarget(),
key->time(),
false);
qreal key_y = GetUnscaledItemYFromKeyframeValue(key);
if (got_val) {
min_time = qMin(transformed_time, min_time);
max_time = qMax(transformed_time, max_time);
min_val = qMin(key->value().toDouble(), min_val);
max_val = qMax(key->value().toDouble(), max_val);
min_val = qMin(key_y, min_val);
max_val = qMax(key_y, max_val);
} else {
min_time = transformed_time;
max_time = transformed_time;
min_val = key_y;
max_val = key_y;
got_val = true;
}
}
}
}
double time_range = max_time.toDouble() - min_time.toDouble();
double new_x_scale = CalculateScaleFromDimensions(this->width(), time_range);
double new_y_scale = CalculateScaleFromDimensions(this->height(), max_val - min_val);
// Prevent scaling if no keyframes were found
if (got_val) {
QRectF desired(QPointF(min_time.toDouble(), min_val), QPointF(max_time.toDouble(), max_val));
const double scale_divider = 0.5;
double scale_half_divider = scale_divider*0.5;
double new_x_scale = viewport()->width() / desired.width() * scale_divider;
double new_y_scale;
if (qFuzzyIsNull(desired.height())) {
// Catch divide by zero
new_y_scale = 1.0;
scale_half_divider = 0.5;
} else {
// Use height as normal
new_y_scale = viewport()->height() / desired.height() * scale_divider;
}
emit ScaleChanged(new_x_scale);
SetYScale(new_y_scale);
QMetaObject::invokeMethod(horizontalScrollBar(), "setValue", Qt::QueuedConnection,
Q_ARG(int, TimeToScene(min_time) - CalculatePaddingFromDimensionScale(this->width())));
QMetaObject::invokeMethod(verticalScrollBar(), "setValue", Qt::QueuedConnection,
Q_ARG(int, GetItemYFromKeyframeValue(max_val) - CalculatePaddingFromDimensionScale(this->height())));
UpdateSceneRect();
int sb_x = desired.left() * new_x_scale - viewport()->width() * scale_half_divider;
QMetaObject::invokeMethod(horizontalScrollBar(), "setValue", Qt::QueuedConnection, Q_ARG(int, sb_x));
int sb_y = desired.top() * new_y_scale - viewport()->height() * scale_half_divider;
QMetaObject::invokeMethod(verticalScrollBar(), "setValue", Qt::QueuedConnection, Q_ARG(int, sb_y));
}
}
qreal CurveView::GetItemYFromKeyframeValue(NodeKeyframe *key)
{
return GetItemYFromKeyframeValue(key->value().toDouble());
return GetUnscaledItemYFromKeyframeValue(key) * GetYScale();
}
qreal CurveView::GetItemYFromKeyframeValue(double value)
qreal CurveView::GetUnscaledItemYFromKeyframeValue(NodeKeyframe *key)
{
return -value * GetYScale();
double val = key->value().toDouble();
val = FloatSlider::TransformValueToDisplay(val, GetFloatDisplayTypeFromKeyframe(key));
val += GetOffsetFromKeyframe(key);
return -val;
}
QPointF CurveView::ScalePoint(const QPointF &point)
@@ -543,41 +596,48 @@ QPointF CurveView::ScalePoint(const QPointF &point)
return QPointF(point.x() * GetScale(), - point.y() * GetYScale());
}
FloatSlider::DisplayType CurveView::GetFloatDisplayTypeFromKeyframe(NodeKeyframe *key)
{
Node* node = key->parent();
const QString& input = key->input();
if (node->HasInputProperty(input, QStringLiteral("view"))) {
// Try to get view from input (which will be normal if unset)
return static_cast<FloatSlider::DisplayType>(node->GetInputProperty(input, QStringLiteral("view")).toInt());
}
// Fallback to normal
return FloatSlider::kNormal;
}
double CurveView::GetOffsetFromKeyframe(NodeKeyframe *key)
{
Node *node = key->parent();
const QString &input = key->input();
if (node->HasInputProperty(input, QStringLiteral("offset"))) {
QVariant v = node->GetInputProperty(input, QStringLiteral("offset"));
// NOTE: Implement getting correct offset for the track based on the data type
QVector<QVariant> track_vals = NodeValue::split_normal_value_into_track_values(node->GetInputDataType(input), v);
return track_vals.at(key->track()).toDouble();
}
return 0;
}
QPointF CurveView::GetKeyframePosition(NodeKeyframe *key)
{
return QPointF(GetKeyframeSceneX(key), GetItemYFromKeyframeValue(key));
}
void CurveView::KeyframeTypeChanged()
{
qDebug() << "STUB!";
/*NodeKeyframe* key = static_cast<NodeKeyframe*>(sender());
KeyframeViewItem* item = item_map().value(key);
if (item->isSelected()) {
item->setSelected(false);
item->setSelected(true);
}*/
}
void CurveView::ZoomToFit()
{
QVector<NodeKeyframe*> keys;
foreach (KeyframeViewInputConnection *con, track_connections_) {
foreach (NodeKeyframe *k, con->GetKeyframes()) {
if (!keys.contains(k)) {
keys.append(k);
}
}
}
ZoomToFitInternal(keys);
ZoomToFitInternal(false);
}
void CurveView::ZoomToFitSelected()
{
ZoomToFitInternal(GetSelectedKeyframes());
ZoomToFitInternal(true);
}
void CurveView::ResetZoom()
+7 -7
View File
@@ -23,6 +23,7 @@
#include "node/keyframe.h"
#include "widget/keyframeview/keyframeview.h"
#include "widget/slider/floatslider.h"
namespace olive {
@@ -38,8 +39,6 @@ public:
void SelectKeyframesOfInput(const NodeKeyframeTrackReference &ref);
void ZoomToFitInput(const NodeKeyframeTrackReference &ref);
void SetKeyframeTrackColor(const NodeKeyframeTrackReference& ref, const QColor& color);
public slots:
@@ -70,13 +69,17 @@ protected:
virtual void KeyframeDragRelease(QMouseEvent *event, MultiUndoCommand *command) override;
private:
void ZoomToFitInternal(const QVector<NodeKeyframe *> &keys);
void ZoomToFitInternal(bool selected_only);
qreal GetItemYFromKeyframeValue(NodeKeyframe* key);
qreal GetItemYFromKeyframeValue(double value);
qreal GetUnscaledItemYFromKeyframeValue(NodeKeyframe* key);
QPointF ScalePoint(const QPointF& point);
static FloatSlider::DisplayType GetFloatDisplayTypeFromKeyframe(NodeKeyframe *key);
static double GetOffsetFromKeyframe(NodeKeyframe *key);
void AdjustLines();
QPointF GetKeyframePosition(NodeKeyframe *key);
@@ -112,9 +115,6 @@ private:
QVector<QVariant> drag_keyframe_values_;
private slots:
void KeyframeTypeChanged();
};
}
+29 -50
View File
@@ -47,10 +47,7 @@ CurveWidget::CurveWidget(QWidget *parent) :
tree_view_ = new NodeTreeView();
tree_view_->SetOnlyShowKeyframable(true);
tree_view_->SetShowKeyframeTracksAsRows(true);
connect(tree_view_, &NodeTreeView::NodeEnableChanged, this, &CurveWidget::NodeEnabledChanged);
connect(tree_view_, &NodeTreeView::InputEnableChanged, this, &CurveWidget::InputEnabledChanged);
connect(tree_view_, &NodeTreeView::InputSelectionChanged, this, &CurveWidget::InputSelectionChanged);
connect(tree_view_, &NodeTreeView::InputDoubleClicked, this, &CurveWidget::InputDoubleClicked);
splitter->addWidget(tree_view_);
QWidget* workarea = new QWidget();
@@ -101,7 +98,7 @@ CurveWidget::CurveWidget(QWidget *parent) :
// Connect ruler and view together
connect(view_, &CurveView::TimeChanged, this, &CurveWidget::SetTimeAndSignal);
connect(view_->scene(), &QGraphicsScene::selectionChanged, this, &CurveWidget::SelectionChanged);
connect(view_, &CurveView::SelectionChanged, this, &CurveWidget::SelectionChanged);
connect(view_, &CurveView::ScaleChanged, this, &CurveWidget::SetScale);
connect(view_, &CurveView::Dragged, this, &CurveWidget::KeyframeViewDragged);
@@ -176,6 +173,8 @@ void CurveWidget::ScaleChangedEvent(const double &scale)
void CurveWidget::TimeTargetChangedEvent(Node *target)
{
TimeTargetObject::TimeTargetChangedEvent(target);
key_control_->SetTimeTarget(target);
view_->SetTimeTarget(target);
@@ -183,6 +182,8 @@ void CurveWidget::TimeTargetChangedEvent(Node *target)
void CurveWidget::ConnectedNodeChangeEvent(ViewerOutput *n)
{
super::ConnectedNodeChangeEvent(n);
SetTimeTarget(n);
}
@@ -215,7 +216,7 @@ void CurveWidget::UpdateBridgeTime(const rational &time)
void CurveWidget::ConnectNode(Node *node, bool connect)
{
foreach (const QString& input, node->inputs()) {
if (node->IsInputKeyframable(input)) {
if (node->IsInputKeyframable(input) && !node->IsInputHidden(input)) {
ConnectInput(node, input, connect);
}
}
@@ -228,9 +229,6 @@ void CurveWidget::ConnectInput(Node *node, const QString &input, bool connect)
return;
}
int track_count = NodeValue::get_number_of_keyframe_tracks(node->GetInputDataType(input));
bool multiple_tracks = track_count > 1;
int arr_sz = node->InputArraySize(input);
for (int i=-1; i<arr_sz; i++) {
// Generate a random color for this input
@@ -247,28 +245,6 @@ void CurveWidget::ConnectInput(Node *node, const QString &input, bool connect)
view_->SetKeyframeTrackColor(ref, c);
}
}
if (tree_view_->IsInputEnabled(NodeKeyframeTrackReference(NodeInput(node, input, i), multiple_tracks ? -1 : 0))) {
if (multiple_tracks) {
for (int j=0; j<track_count; j++) {
NodeKeyframeTrackReference ref(NodeInput(node, input, i), j);
if (tree_view_->IsInputEnabled(ref)) {
if (connect) {
view_->ConnectInput(ref);
} else {
view_->DisconnectInput(ref);
}
}
}
} else {
NodeKeyframeTrackReference ref(NodeInput(node, input, i), 0);
if (connect) {
view_->ConnectInput(ref);
} else {
view_->DisconnectInput(ref);
}
}
}
}
}
@@ -339,32 +315,35 @@ void CurveWidget::KeyframeTypeButtonTriggered(bool checked)
Core::instance()->undo_stack()->push(command);
}
void CurveWidget::NodeEnabledChanged(Node* n, bool e)
{
ConnectNode(n, e);
}
void CurveWidget::InputEnabledChanged(const NodeKeyframeTrackReference& ref, bool e)
{
if (e) {
view_->ConnectInput(ref);
} else {
view_->DisconnectInput(ref);
}
}
void CurveWidget::InputSelectionChanged(const NodeKeyframeTrackReference& ref)
{
key_control_->SetInput(ref.input());
if (ref.IsValid()) {
view_->SelectKeyframesOfInput(ref);
foreach (const NodeKeyframeTrackReference &c, selected_tracks_) {
view_->DisconnectInput(c);
}
}
void CurveWidget::InputDoubleClicked(const NodeKeyframeTrackReference& ref)
{
view_->ZoomToFitInput(ref);
selected_tracks_.clear();
if (ref.IsValid()) {
view_->ConnectInput(ref);
selected_tracks_.append(ref);
} else if (ref.input().IsValid()) {
int track_count = NodeValue::get_number_of_keyframe_tracks(ref.input().GetDataType());
for (int i=0; i<track_count; i++) {
NodeKeyframeTrackReference track_ref(ref.input(), i);
view_->ConnectInput(track_ref);
selected_tracks_.append(track_ref);
}
} else if (Node *node = ref.input().node()) {
foreach (const QString &input, node->inputs()) {
if (!node->IsInputKeyframable(input) || node->IsInputHidden(input)) {
}
}
}
view_->ZoomToFit();
}
void CurveWidget::KeyframeViewDragged(int x, int y)
+2 -6
View File
@@ -96,19 +96,15 @@ private:
QVector<Node*> nodes_;
QVector<NodeKeyframeTrackReference> selected_tracks_;
private slots:
void SelectionChanged();
void KeyframeTypeButtonTriggered(bool checked);
void NodeEnabledChanged(Node* n, bool e);
void InputEnabledChanged(const NodeKeyframeTrackReference &ref, bool e);
void InputSelectionChanged(const NodeKeyframeTrackReference& ref);
void InputDoubleClicked(const NodeKeyframeTrackReference& ref);
void KeyframeViewDragged(int x, int y);
void CatchUpYScrollToPoint(int point);
+10
View File
@@ -116,6 +116,7 @@ void KeyframeView::RemoveKeyframesOfTrack(KeyframeViewInputConnection *connectio
}
delete connection;
Redraw();
emit SelectionChanged();
}
}
@@ -157,6 +158,8 @@ void KeyframeView::SelectionManagerSelectEvent(void *obj)
}
}
}
emit SelectionChanged();
}
void KeyframeView::SelectionManagerDeselectEvent(void *obj)
@@ -170,6 +173,8 @@ void KeyframeView::SelectionManagerDeselectEvent(void *obj)
}
}
}
emit SelectionChanged();
}
void KeyframeView::mousePressEvent(QMouseEvent *event)
@@ -235,6 +240,7 @@ void KeyframeView::mouseReleaseEvent(QMouseEvent *event)
} else if (selection_manager_.IsRubberBanding()) {
selection_manager_.RubberBandStop();
Redraw();
emit SelectionChanged();
}
}
@@ -325,6 +331,8 @@ void KeyframeView::SelectKeyframe(NodeKeyframe *key)
{
if (selection_manager_.Select(key)) {
Redraw();
emit SelectionChanged();
}
}
@@ -332,6 +340,8 @@ void KeyframeView::DeselectKeyframe(NodeKeyframe *key)
{
if (selection_manager_.Deselect(key)) {
Redraw();
emit SelectionChanged();
}
}
+2
View File
@@ -75,6 +75,8 @@ public:
signals:
void Dragged(int current_x, int current_y);
void SelectionChanged();
protected:
virtual void mousePressEvent(QMouseEvent *event) override;
virtual void mouseMoveEvent(QMouseEvent *event) override;
@@ -38,6 +38,7 @@ KeyframeViewInputConnection::KeyframeViewInputConnection(const NodeKeyframeTrack
connect(n, &Node::KeyframeRemoved, this, &KeyframeViewInputConnection::RemoveKeyframe);
connect(n, &Node::KeyframeTimeChanged, this, &KeyframeViewInputConnection::KeyframeChanged);
connect(n, &Node::KeyframeTypeChanged, this, &KeyframeViewInputConnection::KeyframeChanged);
connect(n, &Node::KeyframeTypeChanged, this, &KeyframeViewInputConnection::KeyframeTypeChanged);
connect(n, &Node::KeyframeValueChanged, this, &KeyframeViewInputConnection::KeyframeChanged);
}
@@ -89,4 +90,11 @@ void KeyframeViewInputConnection::KeyframeChanged(NodeKeyframe *key)
}
}
void KeyframeViewInputConnection::KeyframeTypeChanged(NodeKeyframe *key)
{
if (key->key_track_ref() == input_) {
emit TypeChanged();
}
}
}
@@ -65,6 +65,8 @@ public:
signals:
void RequireUpdate();
void TypeChanged();
private:
KeyframeView *keyframe_view_;
@@ -83,6 +85,8 @@ private slots:
void KeyframeChanged(NodeKeyframe *key);
void KeyframeTypeChanged(NodeKeyframe *key);
};
}
+6 -1
View File
@@ -417,11 +417,15 @@ void NodeParamView::SortItemsInContext(NodeParamViewContext *context_item)
QVector<QPair<NodeParamViewItem*, int> > distances;
for (auto it=context_item->GetItems().cbegin(); it!=context_item->GetItems().cend(); it++) {
int distance = 0;
int distance = -1;
foreach (Node *ctx, context_item->GetContexts()) {
distance = qMax(distance, GetDistanceBetweenNodes(ctx, it.key()));
}
if (distance == -1) {
distance = INT_MAX;
}
bool inserted = false;
QPair<NodeParamViewItem*, int> dist(it.value(), distance);
@@ -439,6 +443,7 @@ void NodeParamView::SortItemsInContext(NodeParamViewContext *context_item)
}
foreach (auto info, distances) {
qDebug() << "Inserting" << info.first->GetNode() << "with distance" << info.second;
context_item->GetDockArea()->AddItem(info.first);
}
}
@@ -648,42 +648,12 @@ void NodeParamViewWidgetBridge::PropertyChanged(const QString& input, const QStr
break;
}
} else if (key == QStringLiteral("offset")) {
switch (data_type) {
case NodeValue::kInt:
static_cast<IntegerSlider*>(widgets_.first())->SetOffset(value);
break;
case NodeValue::kFloat:
static_cast<FloatSlider*>(widgets_.first())->SetOffset(value);
break;
case NodeValue::kRational:
static_cast<RationalSlider*>(widgets_.first())->SetOffset(value);
break;
case NodeValue::kVec2:
{
QVector2D offs = value.value<QVector2D>();
static_cast<FloatSlider*>(widgets_.at(0))->SetOffset(offs.x());
static_cast<FloatSlider*>(widgets_.at(1))->SetOffset(offs.y());
break;
}
case NodeValue::kVec3:
{
QVector3D offs = value.value<QVector3D>();
static_cast<FloatSlider*>(widgets_.at(0))->SetOffset(offs.x());
static_cast<FloatSlider*>(widgets_.at(1))->SetOffset(offs.y());
static_cast<FloatSlider*>(widgets_.at(2))->SetOffset(offs.z());
break;
}
case NodeValue::kVec4:
{
QVector4D offs = value.value<QVector4D>();
static_cast<FloatSlider*>(widgets_.at(0))->SetOffset(offs.x());
static_cast<FloatSlider*>(widgets_.at(1))->SetOffset(offs.y());
static_cast<FloatSlider*>(widgets_.at(2))->SetOffset(offs.z());
static_cast<FloatSlider*>(widgets_.at(3))->SetOffset(offs.w());
break;
}
default:
break;
int tracks = NodeValue::get_number_of_keyframe_tracks(data_type);
QVector<QVariant> offsets = NodeValue::split_normal_value_into_track_values(data_type, value);
for (int i=0; i<tracks; i++) {
static_cast<NumericSliderBase*>(widgets_.at(i))->SetOffset(offsets.at(i));
}
UpdateWidgetValues();
+27 -10
View File
@@ -27,7 +27,8 @@ namespace olive {
NodeTreeView::NodeTreeView(QWidget *parent) :
QTreeWidget(parent),
only_show_keyframable_(false),
show_keyframe_tracks_as_rows_(false)
show_keyframe_tracks_as_rows_(false),
checkboxes_enabled_(false)
{
connect(this, &NodeTreeView::itemChanged, this, &NodeTreeView::ItemCheckStateChanged);
connect(this, &NodeTreeView::itemSelectionChanged, this, &NodeTreeView::SelectionChanged);
@@ -67,12 +68,14 @@ void NodeTreeView::SetNodes(const QVector<Node *> &nodes)
foreach (Node* n, nodes_) {
QTreeWidgetItem* node_item = new QTreeWidgetItem();
node_item->setText(0, n->Name());
if (checkboxes_enabled_) {
node_item->setCheckState(0, disabled_nodes_.contains(n) ? Qt::Unchecked : Qt::Checked);
}
node_item->setData(0, kItemType, kItemTypeNode);
node_item->setData(0, kItemNodePointer, Node::PtrToValue(n));
foreach (const QString& input, n->inputs()) {
if (only_show_keyframable_ && !n->IsInputKeyframable(input)) {
if (n->IsInputHidden(input) || (only_show_keyframable_ && !n->IsInputKeyframable(input))) {
continue;
}
@@ -105,7 +108,6 @@ void NodeTreeView::SetNodes(const QVector<Node *> &nodes)
CreateItemsForTracks(element_item, input_ref, key_tracks.size());
}
}
}
// Add at the end to prevent unnecessary signalling while we're setting these objects up
@@ -114,9 +116,9 @@ void NodeTreeView::SetNodes(const QVector<Node *> &nodes)
} else {
delete node_item;
}
node_item->setExpanded(true);
}
expandAll();
}
void NodeTreeView::changeEvent(QEvent *e)
@@ -155,6 +157,8 @@ NodeKeyframeTrackReference NodeTreeView::GetSelectedInput()
if (item->data(0, kItemType).toInt() == kItemTypeInput) {
selected_ref = item->data(0, kItemInputReference).value<NodeKeyframeTrackReference>();
} else {
selected_ref = NodeKeyframeTrackReference(NodeInput(Node::ValueToPtr<Node>(item->data(0, kItemNodePointer)), QString()));
}
}
@@ -166,21 +170,27 @@ QTreeWidgetItem* NodeTreeView::CreateItem(QTreeWidgetItem *parent, const NodeKey
QTreeWidgetItem* input_item = new QTreeWidgetItem(parent);
QString item_name;
if (ref.track() == -1 || NodeValue::get_number_of_keyframe_tracks(ref.input().GetDataType()) == 1) {
if (ref.track() == -1
|| NodeValue::get_number_of_keyframe_tracks(ref.input().GetDataType()) == 1
|| (ref.input().IsArray() && ref.input().element() == -1)) {
if (ref.input().element() == -1) {
item_name = ref.input().name();
} else {
item_name = QString::number(ref.input().element());
}
} else {
switch (ref.track()) {
case 0:
item_name = tr("X");
item_name = UseRGBAOverXYZW(ref) ? tr("R") : tr("X");
break;
case 1:
item_name = tr("Y");
item_name = UseRGBAOverXYZW(ref) ? tr("G") : tr("Y");
break;
case 2:
item_name = tr("Z");
item_name = UseRGBAOverXYZW(ref) ? tr("B") : tr("Z");
break;
case 3:
item_name = tr("W");
item_name = UseRGBAOverXYZW(ref) ? tr("A") : tr("W");
break;
default:
item_name = QString::number(ref.track());
@@ -188,7 +198,9 @@ QTreeWidgetItem* NodeTreeView::CreateItem(QTreeWidgetItem *parent, const NodeKey
}
input_item->setText(0, item_name);
if (checkboxes_enabled_) {
input_item->setCheckState(0, disabled_inputs_.contains(ref) ? Qt::Unchecked : Qt::Checked);
}
input_item->setData(0, kItemType, kItemTypeInput);
input_item->setData(0, kItemInputReference, QVariant::fromValue(ref));
@@ -208,6 +220,11 @@ void NodeTreeView::CreateItemsForTracks(QTreeWidgetItem *parent, const NodeInput
}
}
bool NodeTreeView::UseRGBAOverXYZW(const NodeKeyframeTrackReference &ref)
{
return ref.input().GetDataType() == NodeValue::kColor;
}
void NodeTreeView::ItemCheckStateChanged(QTreeWidgetItem *item, int column)
{
Q_UNUSED(column)
+9
View File
@@ -37,6 +37,11 @@ public:
bool IsInputEnabled(const NodeKeyframeTrackReference& ref) const;
void SetCheckBoxesEnabled(bool e)
{
checkboxes_enabled_ = e;
}
void SetKeyframeTrackColor(const NodeKeyframeTrackReference& ref, const QColor& color);
void SetOnlyShowKeyframable(bool e)
@@ -75,6 +80,8 @@ private:
void CreateItemsForTracks(QTreeWidgetItem* parent, const NodeInput& input, int track_count);
static bool UseRGBAOverXYZW(const NodeKeyframeTrackReference &ref);
enum ItemType {
kItemTypeNode,
kItemTypeInput
@@ -98,6 +105,8 @@ private:
QHash<NodeKeyframeTrackReference, QColor> keyframe_colors_;
bool checkboxes_enabled_;
private slots:
void ItemCheckStateChanged(QTreeWidgetItem* item, int column);
+36 -44
View File
@@ -78,29 +78,46 @@ void FloatSlider::SetDisplayType(const FloatSlider::DisplayType &type)
}
}
QString FloatSlider::ValueToString(double val, FloatSlider::DisplayType display, int decimal_places, bool autotrim_decimal_places)
double FloatSlider::TransformValueToDisplay(double val, DisplayType display)
{
switch (display) {
case kNormal:
// Do nothing, skip to the return string at the end
break;
case kDecibel:
// Convert to decibels and return dB formatted string
// Return negative infinity for zero volume
if (qIsNull(val)) {
return tr("\xE2\x88\x9E");
}
val = Decibel::fromLinear(val);
break;
case kPercentage:
// Multiply value by 100 for user-friendly percentage
val *= 100.0;
break;
}
return FloatToString(val, decimal_places, autotrim_decimal_places);
return val;
}
double FloatSlider::TransformDisplayToValue(double val, DisplayType display)
{
switch (display) {
case kNormal:
break;
case kDecibel:
val = Decibel::toLinear(val);
break;
case kPercentage:
val *= 0.01;
break;
}
return val;
}
QString FloatSlider::ValueToString(double val, FloatSlider::DisplayType display, int decimal_places, bool autotrim_decimal_places)
{
// Return negative infinity for zero volume
if (display == kDecibel && qIsNull(val)) {
return tr("\xE2\x88\x9E");
}
return FloatToString(TransformValueToDisplay(val, display), decimal_places, autotrim_decimal_places);
}
QString FloatSlider::ValueToString(const QVariant &v) const
@@ -110,46 +127,21 @@ QString FloatSlider::ValueToString(const QVariant &v) const
QVariant FloatSlider::StringToValue(const QString &s, bool *ok) const
{
switch (display_type_) {
case kNormal:
// Do nothing, skip to the return string at the end
break;
case kDecibel:
{
bool valid;
// See if we can get a decimal number out of this
qreal decibels = s.toDouble(&valid);
if (ok) *ok = valid;
if (valid) {
// Convert from decibel scale to linear decimal
return Decibel::toLinear(decibels);
}
break;
}
case kPercentage:
{
bool valid;
// Try to get double value
double val = s.toDouble(&valid);
if (ok) *ok = valid;
// If we were given an `ok` pointer, set it to `valid`
if (ok) {
*ok = valid;
}
// If we could get it, convert back to a 0.0 - 1.0 value and return
// If valid, transform it from display
if (valid) {
return val * 0.01;
val = TransformDisplayToValue(val, display_type_);
}
break;
}
}
// Just try to convert the string to a double
return s.toDouble(ok) - GetOffset().toDouble();
// Return un-offset value
return val - GetOffset().toDouble();
}
QVariant FloatSlider::AdjustDragDistanceInternal(const QVariant &start, const double &drag) const
+4
View File
@@ -49,6 +49,10 @@ public:
void SetDisplayType(const DisplayType& type);
static double TransformValueToDisplay(double val, DisplayType display);
static double TransformDisplayToValue(double val, DisplayType display);
static QString ValueToString(double val, DisplayType display, int decimal_places, bool autotrim_decimal_places);
protected:
+5 -6
View File
@@ -62,6 +62,11 @@ public slots:
void SetEndTime(const rational& length);
/**
* @brief Slot called whenever the view resizes or the scene contents change to enforce minimum scene sizes
*/
void UpdateSceneRect();
signals:
void TimeChanged(const rational& time);
@@ -101,12 +106,6 @@ protected:
y_axis_enabled_ = e;
}
protected slots:
/**
* @brief Slot called whenever the view resizes or the scene contents change to enforce minimum scene sizes
*/
void UpdateSceneRect();
private:
qreal GetPlayheadX();
@@ -92,7 +92,9 @@ public:
T *GetObjectAtPoint(const QPointF &scene_pt)
{
foreach (const DrawnObject &kp, drawn_objects_) {
// Iterate in reverse order because the objects drawn later will appear on top to the user
for (auto it=drawn_objects_.crbegin(); it!=drawn_objects_.crend(); it++) {
const DrawnObject &kp = *it;
if (kp.second.contains(scene_pt)) {
return kp.first;
}
@@ -147,13 +149,11 @@ public:
{
initial_drag_item_ = initial_item;
dragging_.clear();
dragging_.resize(selected_.size());
for (int i=0; i<selected_.size(); i++) {
T *obj = selected_.at(i);
dragging_[i] = {obj->time()};
dragging_[i] = {obj->time(), view_->TimeToScene(obj->time())};
}
drag_mouse_start_ = view_->mapToScene(event->pos());
@@ -164,15 +164,15 @@ public:
QPointF diff = view_->mapToScene(event->pos()) - drag_mouse_start_;
for (int i=0; i<selected_.size(); i++) {
const rational &old_time = dragging_.at(i).time;
rational proposed_time = old_time + view_->SceneToTimeNoGrid(diff.x());
rational proposed_time = view_->SceneToTimeNoGrid(dragging_.at(i).x + diff.x());
T *sel = selected_.at(i);
// Magic number: use interval of 1ms to avoid collisions
rational adj(1, 1000);
if (old_time < proposed_time) {
if (dragging_.at(i).time < proposed_time) {
adj = -adj;
}
while (true) {
NodeKeyframe *key_at_time = sel->parent()->GetKeyframeAtTimeOnTrack(sel->input(), proposed_time, sel->track(), sel->element());
if (!key_at_time || key_at_time == sel) {
@@ -303,6 +303,7 @@ private:
struct DragObject
{
rational time;
double x;
};
QVector<DragObject> dragging_;