refined keyframeview and curveview

This commit is contained in:
itsmattkc
2021-12-15 19:45:04 -08:00
parent 1644b9e4c1
commit bf196e790b
33 changed files with 1013 additions and 1777 deletions
+238 -92
View File
@@ -20,31 +20,33 @@
#include "curveview.h"
#include <cfloat>
#include <QHash>
#include <QMouseEvent>
#include <QPainterPath>
#include <QScrollBar>
#include <QtMath>
#include <cfloat>
#include "common/qtutils.h"
#include "widget/keyframeview/keyframeviewundo.h"
#include "widget/nodeparamview/nodeparamviewundo.h"
#include "widget/slider/floatslider.h"
namespace olive {
#define super KeyframeViewBase
#define super KeyframeView
CurveView::CurveView(QWidget *parent) :
KeyframeViewBase(parent)
KeyframeView(parent),
dragging_bezier_pt_(nullptr)
{
setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
setDragMode(RubberBandDrag);
setViewportUpdateMode(FullViewportUpdate);
SetYAxisEnabled(true);
SetAutoSelectSiblings(false);
text_padding_ = QtUtils::QFontMetricsWidth(fontMetrics(), QStringLiteral("i"));
minimum_grid_space_ = QtUtils::QFontMetricsWidth(fontMetrics(), QStringLiteral("00000"));
connect(scene(), &QGraphicsScene::selectionChanged, this, &CurveView::SelectionChanged);
}
void CurveView::ConnectInput(const NodeKeyframeTrackReference& ref)
@@ -90,7 +92,9 @@ void CurveView::SelectKeyframesOfInput(const NodeKeyframeTrackReference& ref)
void CurveView::ZoomToFitInput(const NodeKeyframeTrackReference& ref)
{
ZoomToFitInternal(track_connections_.value(ref)->GetKeyframes());
if (KeyframeViewInputConnection *con = track_connections_.value(ref)) {
ZoomToFitInternal(con->GetKeyframes());
}
}
void CurveView::SetKeyframeTrackColor(const NodeKeyframeTrackReference &ref, const QColor &color)
@@ -98,8 +102,10 @@ void CurveView::SetKeyframeTrackColor(const NodeKeyframeTrackReference &ref, con
// Insert color into hashmap
keyframe_colors_.insert(ref, color);
// Update all keyframes
track_connections_.value(ref)->SetBrush(color);
if (KeyframeViewInputConnection *con = track_connections_.value(ref)) {
// Update all keyframes
con->SetBrush(color);
}
}
void CurveView::drawBackground(QPainter *painter, const QRectF &rect)
@@ -235,40 +241,13 @@ void CurveView::drawBackground(QPainter *painter, const QRectF &rect)
}
}
}
// Draw bezier control point lines
/*if (!bezier_control_points_.isEmpty()) {
painter->setPen(QPen(palette().text().color(), 1));
QVector<QLineF> bezier_lines;
foreach (BezierControlPointItem* item, bezier_control_points_) {
// All BezierControlPointItems should be children of a KeyframeViewItem
KeyframeViewItem* par = static_cast<KeyframeViewItem*>(item->parentItem());
bezier_lines.append(QLineF(par->pos(), par->pos() + item->pos()));
}
painter->drawLines(bezier_lines);
}*/
}
void CurveView::ScaleChangedEvent(const double& scale)
void CurveView::drawForeground(QPainter *painter, const QRectF &rect)
{
KeyframeViewBase::ScaleChangedEvent(scale);
bezier_pts_.clear();
foreach (BezierControlPointItem* item, bezier_control_points_) {
item->SetXScale(scale);
}
}
void CurveView::VerticalScaleChangedEvent(double scale)
{
Q_UNUSED(scale)
foreach (BezierControlPointItem* item, bezier_control_points_) {
item->SetYScale(scale);
}
viewport()->update();
super::drawForeground(painter, rect);
}
void CurveView::ContextMenuEvent(Menu &m)
@@ -290,6 +269,225 @@ void CurveView::SceneRectUpdateEvent(QRectF &r)
r.setBottom(r.bottom() + this->height());
}
qreal CurveView::GetKeyframeSceneY(KeyframeViewInputConnection *track, NodeKeyframe *key)
{
return GetItemYFromKeyframeValue(key);
}
void CurveView::DrawKeyframe(QPainter *painter, NodeKeyframe *key, KeyframeViewInputConnection *track, const QRectF &key_rect)
{
if (IsKeyframeSelected(key) && key->type() == NodeKeyframe::kBezier) {
// Draw bezier control points if keyframe is selected
int control_point_size = QtUtils::QFontMetricsWidth(fontMetrics(), "o");
int half_sz = control_point_size / 2;
QRectF control_point_rect(-half_sz, -half_sz, control_point_size, control_point_size);
painter->setPen(palette().text().color());
painter->setBrush(Qt::NoBrush);
QRectF cp_in = control_point_rect.translated(key_rect.center() + ScalePoint(key->bezier_control_in()));
QRectF cp_out = control_point_rect.translated(key_rect.center() + ScalePoint(key->bezier_control_out()));
painter->drawLine(key_rect.center(), cp_in.center());
painter->drawLine(key_rect.center(), cp_out.center());
painter->drawEllipse(cp_in);
painter->drawEllipse(cp_out);
bezier_pts_.append({cp_in, key, NodeKeyframe::kInHandle});
bezier_pts_.append({cp_out, key, NodeKeyframe::kOutHandle});
}
super::DrawKeyframe(painter, key, track, key_rect);
}
bool CurveView::FirstChanceMousePress(QMouseEvent *event)
{
dragging_bezier_pt_ = nullptr;
QPointF scene_pt = mapToScene(event->pos());
foreach (const BezierPoint &b, bezier_pts_) {
if (b.rect.contains(scene_pt)) {
dragging_bezier_pt_ = &b;
break;
}
}
if (dragging_bezier_pt_) {
NodeKeyframe *key = dragging_bezier_pt_->keyframe;
dragging_bezier_point_start_ = (dragging_bezier_pt_->type == NodeKeyframe::kInHandle) ? key->bezier_control_in() : key->bezier_control_out();
dragging_bezier_point_opposing_start_ = (dragging_bezier_pt_->type == NodeKeyframe::kInHandle) ? key->bezier_control_out() : key->bezier_control_in();
drag_start_ = mapToScene(event->pos());
return true;
} else {
return false;
}
}
void CurveView::FirstChanceMouseMove(QMouseEvent *event)
{
// Calculate cursor difference and scale it
QPointF scene_pos = mapToScene(event->pos());
QPointF mouse_diff_scaled = GetScaledCursorPos(scene_pos - drag_start_);
if (event->modifiers() & Qt::ShiftModifier) {
// If holding shift, only move one axis
mouse_diff_scaled.setY(0);
}
// Flip the mouse Y because bezier control points are drawn bottom to top, not top to bottom
mouse_diff_scaled.setY(-mouse_diff_scaled.y());
QPointF new_bezier_pos = GenerateBezierControlPosition(dragging_bezier_pt_->type,
dragging_bezier_point_start_,
mouse_diff_scaled);
// If the user is NOT holding control, we set the other handle to the exact negative of this handle
QPointF new_opposing_pos;
NodeKeyframe::BezierType opposing_type = NodeKeyframe::get_opposing_bezier_type(dragging_bezier_pt_->type);
if (!(event->modifiers() & Qt::ControlModifier)) {
new_opposing_pos = GenerateBezierControlPosition(opposing_type,
dragging_bezier_point_opposing_start_,
-mouse_diff_scaled);
} else {
new_opposing_pos = dragging_bezier_point_opposing_start_;
}
dragging_bezier_pt_->keyframe->set_bezier_control(dragging_bezier_pt_->type,
new_bezier_pos);
dragging_bezier_pt_->keyframe->set_bezier_control(opposing_type,
new_opposing_pos);
Redraw();
}
void CurveView::FirstChanceMouseRelease(QMouseEvent *event)
{
MultiUndoCommand* command = new MultiUndoCommand();
// Create undo command with the current bezier point and the old one
command->add_child(new KeyframeSetBezierControlPoint(dragging_bezier_pt_->keyframe,
dragging_bezier_pt_->type,
dragging_bezier_pt_->keyframe->bezier_control(dragging_bezier_pt_->type),
dragging_bezier_point_start_));
if (!(event->modifiers() & Qt::ControlModifier)) {
auto opposing_type = NodeKeyframe::get_opposing_bezier_type(dragging_bezier_pt_->type);
command->add_child(new KeyframeSetBezierControlPoint(dragging_bezier_pt_->keyframe,
opposing_type,
dragging_bezier_pt_->keyframe->bezier_control(opposing_type),
dragging_bezier_point_opposing_start_));
}
dragging_bezier_pt_ = nullptr;
Core::instance()->undo_stack()->push(command);
}
void CurveView::KeyframeDragStart(QMouseEvent *event)
{
drag_keyframe_values_.resize(GetSelectedKeyframes().size());
for (int i=0; i<GetSelectedKeyframes().size(); i++) {
drag_keyframe_values_[i] = GetSelectedKeyframes().at(i)->value();
}
drag_start_ = mapToScene(event->pos());
}
void CurveView::KeyframeDragMove(QMouseEvent *event, QString &tip)
{
if (event->modifiers() & Qt::ShiftModifier) {
// Lock to X axis only
return;
}
// Calculate cursor difference
double scaled_diff = (mapToScene(event->pos()).y() - drag_start_.y()) / GetYScale();
// Validate movement - ensure no keyframe goes above its max point or below its min point
for (int i=0; i<GetSelectedKeyframes().size(); i++) {
NodeKeyframe *key = GetSelectedKeyframes().at(i);
Node* node = key->parent();
double original_val = drag_keyframe_values_.at(i).toDouble();
const QString& input = key->input();
double new_val = original_val - scaled_diff;
double limited = new_val;
if (node->HasInputProperty(input, QStringLiteral("min"))) {
limited = qMax(limited, node->GetInputProperty(input, QStringLiteral("min")).toDouble());
}
if (node->HasInputProperty(input, QStringLiteral("max"))) {
limited = qMin(limited, node->GetInputProperty(input, QStringLiteral("max")).toDouble());
}
if (limited != new_val) {
scaled_diff = original_val - limited;
}
}
// Set values
for (int i=0; i<GetSelectedKeyframes().size(); i++) {
NodeKeyframe *key = GetSelectedKeyframes().at(i);
key->set_value(drag_keyframe_values_.at(i).toDouble() - scaled_diff);
}
NodeKeyframe *tip_item = GetSelectedKeyframes().first();
FloatSlider::DisplayType display_type = FloatSlider::kNormal;
Node* initial_drag_input = tip_item->parent();
const QString& initial_drag_input_id = tip_item->input();
if (initial_drag_input->HasInputProperty(initial_drag_input_id, QStringLiteral("view"))) {
display_type = static_cast<FloatSlider::DisplayType>(initial_drag_input->GetInputProperty(initial_drag_input_id, QStringLiteral("view")).toInt());
}
bool ok;
double num_value = tip_item->value().toDouble(&ok);
if (ok) {
tip = QStringLiteral("%1\n");
tip.append(FloatSlider::ValueToString(num_value, display_type, 2, true));
}
}
void CurveView::KeyframeDragRelease(QMouseEvent *event, MultiUndoCommand *command)
{
for (int i=0; i<GetSelectedKeyframes().size(); i++) {
NodeKeyframe *k = GetSelectedKeyframes().at(i);
command->add_child(new NodeParamSetKeyframeValueCommand(k, k->value(), drag_keyframe_values_.at(i)));
}
}
QPointF CurveView::GenerateBezierControlPosition(const NodeKeyframe::BezierType mode, const QPointF &start_point, const QPointF &scaled_cursor_diff)
{
QPointF new_bezier_pos = start_point;
new_bezier_pos += scaled_cursor_diff;
// LIMIT bezier handles from overlapping each other
if (mode == NodeKeyframe::kInHandle) {
if (new_bezier_pos.x() > 0) {
new_bezier_pos.setX(0);
}
} else {
if (new_bezier_pos.x() < 0) {
new_bezier_pos.setX(0);
}
}
return new_bezier_pos;
}
QPointF CurveView::GetScaledCursorPos(const QPointF &cursor_pos)
{
return QPointF(cursor_pos.x() / GetScale(),
cursor_pos.y() / GetYScale());
}
void CurveView::ZoomToFitInternal(const QVector<NodeKeyframe *> &keys)
{
if (keys.isEmpty()) {
@@ -345,36 +543,11 @@ QPointF CurveView::ScalePoint(const QPointF &point)
return QPointF(point.x() * GetScale(), - point.y() * GetYScale());
}
void CurveView::CreateBezierControlPoints(NodeKeyframe* item)
{
qDebug() << "STUB!";
/*BezierControlPointItem* bezier_in_pt = new BezierControlPointItem(item, NodeKeyframe::kInHandle, item);
bezier_in_pt->SetXScale(GetScale());
bezier_in_pt->SetYScale(GetYScale());
bezier_control_points_.append(bezier_in_pt);
connect(bezier_in_pt, &QObject::destroyed, this, &CurveView::BezierControlPointDestroyed, Qt::DirectConnection);
BezierControlPointItem* bezier_out_pt = new BezierControlPointItem(item, NodeKeyframe::kOutHandle, item);
bezier_out_pt->SetXScale(GetScale());
bezier_out_pt->SetYScale(GetYScale());
bezier_control_points_.append(bezier_out_pt);
connect(bezier_out_pt, &QObject::destroyed, this, &CurveView::BezierControlPointDestroyed, Qt::DirectConnection);*/
}
QPointF CurveView::GetKeyframePosition(NodeKeyframe *key)
{
return QPointF(GetKeyframeSceneX(key), GetItemYFromKeyframeValue(key));
}
void CurveView::KeyframeValueChanged()
{
qDebug() << "STUB!";
/*NodeKeyframe* key = static_cast<NodeKeyframe*>(sender());
KeyframeViewItem* item = item_map().value(key);
SetItemYFromKeyframeValue(key, item);*/
}
void CurveView::KeyframeTypeChanged()
{
qDebug() << "STUB!";
@@ -387,33 +560,6 @@ void CurveView::KeyframeTypeChanged()
}*/
}
void CurveView::SelectionChanged()
{
qDebug() << "STUB!";
/*
// Clear current bezier handles
while (!bezier_control_points_.isEmpty()) {
delete bezier_control_points_.first();
}
QList<QGraphicsItem*> selected = scene()->selectedItems();
foreach (QGraphicsItem* item, selected) {
KeyframeViewItem* this_item = static_cast<KeyframeViewItem*>(item);
if (this_item->key()->type() == NodeKeyframe::kBezier) {
CreateBezierControlPoints(this_item);
}
}
*/
}
void CurveView::BezierControlPointDestroyed()
{
BezierControlPointItem* item = static_cast<BezierControlPointItem*>(sender());
bezier_control_points_.removeOne(item);
}
void CurveView::ZoomToFit()
{
QVector<NodeKeyframe*> keys;