engine: keyframe properties dialog migrates; keyframeviewundo deleted

- new primitives: keyframes_set_time_many (conflict-safe batch time
  move), keyframes_set_value_many (captured or explicit old values),
  keyframes_set_bezier_many (double precision), and
  keyframe_set_bezier_point (single handle with NaN-capture fallback)
- dialog and curveview drag finalization go through the facade; the
  tests exercise the same global undo stack via oakengine_project_undo
- keyframeview/keyframeviewundo.{h,cpp} removed with zero remaining
  references
This commit is contained in:
2026-07-20 15:41:36 +08:00
parent 8311128f4c
commit 37aa859cd0
10 changed files with 752 additions and 241 deletions
+138 -21
View File
@@ -30,14 +30,67 @@
#include "common/decibel.h"
#include "common/qtutils.h"
#include "node/nodeundo.h"
#include "widget/keyframeview/keyframeviewundo.h"
#include "oakengine/node.h"
namespace olive
{
#define super KeyframeView
namespace
{
// Map a keyframe track's scalar QVariant into the facade POD for the
// input's declared type (the curve view drags numeric tracks).
void track_value_to_c(NodeValue::Type declared, const QVariant &value,
oak_node_value *out)
{
memset(out, 0, sizeof(*out));
switch (declared) {
case NodeValue::k_int:
out->type = OAK_NODE_VALUE_INT;
out->num = value.toLongLong();
break;
case NodeValue::k_combo:
out->type = OAK_NODE_VALUE_COMBO;
out->num = value.toLongLong();
break;
case NodeValue::k_boolean:
out->type = OAK_NODE_VALUE_BOOL;
out->num = value.toBool() ? 1 : 0;
break;
case NodeValue::k_rational: {
const Rational r = value.value<Rational>();
out->type = OAK_NODE_VALUE_RATIONAL;
out->num = r.numerator();
out->den = r.denominator();
break;
}
case NodeValue::k_color:
out->type = OAK_NODE_VALUE_COLOR;
out->f[0] = value.toDouble();
break;
case NodeValue::k_vec2:
out->type = OAK_NODE_VALUE_VEC2;
out->f[0] = value.toDouble();
break;
case NodeValue::k_vec3:
out->type = OAK_NODE_VALUE_VEC3;
out->f[0] = value.toDouble();
break;
case NodeValue::k_vec4:
out->type = OAK_NODE_VALUE_VEC4;
out->f[0] = value.toDouble();
break;
default:
out->type = OAK_NODE_VALUE_FLOAT;
out->f[0] = value.toDouble();
break;
}
}
} // namespace
CurveView::CurveView(QWidget *parent)
: KeyframeView(parent)
, dragging_bezier_pt_(nullptr)
@@ -410,28 +463,40 @@ void CurveView::first_chance_mouse_move(QMouseEvent *event)
void CurveView::first_chance_mouse_release(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_));
// Through the liboakengine C ABI facade with the drag-start point(s)
// as the explicit old values (the drag already live-set the new
// ones); one undoable command per handle, same as the old
// KeyframeSetBezierControlPoint children.
NodeKeyframe *key = dragging_bezier_pt_->keyframe;
OakEngineNode *handle =
reinterpret_cast<OakEngineNode *>(key->parent());
int tbn = 0, tbd = 0;
oakengine_node_frame_time_base(handle, &tbn, &tbd);
const int64_t ts = Timecode::time_to_timestamp(
key->time(), Rational(tbn, tbd), Timecode::k_round);
const QPointF current =
key->bezier_control(dragging_bezier_pt_->type);
oakengine_node_keyframe_set_bezier_point(
handle, key->input().toUtf8().constData(), key->element(), ts,
key->track(),
(dragging_bezier_pt_->type == NodeKeyframe::k_in_handle) ? 0 : 1,
current.x(), current.y(), dragging_bezier_point_start_.x(),
dragging_bezier_point_start_.y());
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_));
const QPointF opposing_current = key->bezier_control(opposing_type);
oakengine_node_keyframe_set_bezier_point(
handle, key->input().toUtf8().constData(), key->element(), ts,
key->track(),
(opposing_type == NodeKeyframe::k_in_handle) ? 0 : 1,
opposing_current.x(), opposing_current.y(),
dragging_bezier_point_opposing_start_.x(),
dragging_bezier_point_opposing_start_.y());
}
dragging_bezier_pt_ = nullptr;
Core::instance()->undo_stack()->push(
command, tr("Moved Keyframe Bezier Control Point"));
}
void CurveView::keyframe_drag_start(QMouseEvent *event)
@@ -517,13 +582,65 @@ void CurveView::keyframe_drag_move(QMouseEvent *event, QString &tip)
void CurveView::keyframe_drag_release(QMouseEvent *event,
MultiUndoCommand *command)
{
Q_UNUSED(command) // the facade pushes its own single command below
// Group the changed keys by owning input and push ONE undoable
// command per group through the liboakengine C ABI facade, with the
// drag-start values as the explicit undo values (the drag already
// live-set the new ones).
struct ValueGroup {
Node *node;
QString input;
int element;
QVector<int64_t> times;
QVector<int> tracks;
std::vector<oak_node_value> values;
std::vector<oak_node_value> olds;
};
QVector<ValueGroup> groups;
for (size_t i = 0; i < get_selected_keyframes().size(); i++) {
NodeKeyframe *k = get_selected_keyframes().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)));
if (qFuzzyCompare(k->value().toDouble(),
drag_keyframe_values_.at(i).toDouble())) {
continue;
}
int g = 0;
for (; g < groups.size(); g++) {
if (groups.at(g).node == k->parent() &&
groups.at(g).input == k->input() &&
groups.at(g).element == k->element()) {
break;
}
}
if (g == groups.size()) {
groups.append(
{ k->parent(), k->input(), k->element(), {}, {}, {}, {} });
}
OakEngineNode *handle =
reinterpret_cast<OakEngineNode *>(k->parent());
int tbn = 0, tbd = 0;
oakengine_node_frame_time_base(handle, &tbn, &tbd);
groups[g].times.append(Timecode::time_to_timestamp(
k->time(), Rational(tbn, tbd), Timecode::k_round));
groups[g].tracks.append(k->track());
const NodeValue::Type declared =
k->parent()->get_input_data_type(k->input());
oak_node_value new_v, old_v;
track_value_to_c(declared, k->value(), &new_v);
track_value_to_c(declared, drag_keyframe_values_.at(i), &old_v);
groups[g].values.push_back(new_v);
groups[g].olds.push_back(old_v);
}
foreach (const ValueGroup &g, groups) {
oakengine_node_keyframes_set_value_many(
reinterpret_cast<OakEngineNode *>(g.node),
g.input.toUtf8().constData(), g.element, g.times.constData(),
g.tracks.data(), g.times.size(), g.values.data(),
g.olds.data());
}
}
-2
View File
@@ -20,7 +20,5 @@ set(OLIVE_SOURCES
widget/keyframeview/keyframeview.h
widget/keyframeview/keyframeviewinputconnection.cpp
widget/keyframeview/keyframeviewinputconnection.h
widget/keyframeview/keyframeviewundo.cpp
widget/keyframeview/keyframeviewundo.h
PARENT_SCOPE
)
@@ -1,87 +0,0 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2022 Olive Team
Modifications Copyright (C) 2025 mikesolar
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#include "keyframeviewundo.h"
#include "node/node.h"
#include "node/project/sequence/sequence.h"
namespace olive
{
KeyframeSetTypeCommand::KeyframeSetTypeCommand(NodeKeyframe *key,
NodeKeyframe::Type type)
: key_(key)
, old_type_(key->type())
, new_type_(type)
{
}
Project *KeyframeSetTypeCommand::get_relevant_project() const
{
return key_->parent()->project();
}
void KeyframeSetTypeCommand::redo()
{
key_->set_type(new_type_);
}
void KeyframeSetTypeCommand::undo()
{
key_->set_type(old_type_);
}
KeyframeSetBezierControlPoint::KeyframeSetBezierControlPoint(
NodeKeyframe *key, NodeKeyframe::BezierType mode, const QPointF &point)
: key_(key)
, mode_(mode)
, old_point_(key->bezier_control(mode_))
, new_point_(point)
{
}
KeyframeSetBezierControlPoint::KeyframeSetBezierControlPoint(
NodeKeyframe *key, NodeKeyframe::BezierType mode, const QPointF &new_point,
const QPointF &old_point)
: key_(key)
, mode_(mode)
, old_point_(old_point)
, new_point_(new_point)
{
}
Project *KeyframeSetBezierControlPoint::get_relevant_project() const
{
return key_->parent()->project();
}
void KeyframeSetBezierControlPoint::redo()
{
key_->set_bezier_control(mode_, new_point_);
}
void KeyframeSetBezierControlPoint::undo()
{
key_->set_bezier_control(mode_, old_point_);
}
}
@@ -1,77 +0,0 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2022 Olive Team
Modifications Copyright (C) 2025 mikesolar
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#ifndef OAK_KEYFRAMEVIEWUNDO_H
#define OAK_KEYFRAMEVIEWUNDO_H
#include "node/keyframe.h"
#include "undo/undocommand.h"
namespace olive
{
class KeyframeSetTypeCommand : public UndoCommand {
public:
KeyframeSetTypeCommand(NodeKeyframe *key, NodeKeyframe::Type type);
virtual Project *get_relevant_project() const override;
protected:
virtual void redo() override;
virtual void undo() override;
private:
NodeKeyframe *key_;
NodeKeyframe::Type old_type_;
NodeKeyframe::Type new_type_;
};
class KeyframeSetBezierControlPoint : public UndoCommand {
public:
KeyframeSetBezierControlPoint(NodeKeyframe *key,
NodeKeyframe::BezierType mode,
const QPointF &point);
KeyframeSetBezierControlPoint(NodeKeyframe *key,
NodeKeyframe::BezierType mode,
const QPointF &new_point,
const QPointF &old_point);
virtual Project *get_relevant_project() const override;
protected:
virtual void redo() override;
virtual void undo() override;
private:
NodeKeyframe *key_;
NodeKeyframe::BezierType mode_;
QPointF old_point_;
QPointF new_point_;
};
}
#endif // OAK_KEYFRAMEVIEWUNDO_H