app: migrate all engine access to the C ABI facade (nm U _ZN5olive = 0)

Every app module now reaches liboakengine exclusively through
oakengine_* C calls, EngineEventBridge subscriptions and app-side
handle headers (cliphandle/keyframehandle/nodevaluehandle/oakvaluehelper).
Direct C++ command construction, engine signal connect()s, and engine
type usage in MOC-visible signatures are gone: 557 -> 0 undefined
olive:: symbols in oak-editor.
This commit is contained in:
2026-07-26 22:43:21 +08:00
parent f95590e924
commit 0aa5879f35
256 changed files with 12357 additions and 4044 deletions
+403 -191
View File
@@ -31,9 +31,10 @@
#include "node/audio/volume/volume.h"
#include "node/distort/transform/transformdistortnode.h"
#include "node/group/group.h"
#include "node/nodeundo.h"
#include "node/project/serializer/serializer.h"
#include "oakengine/node.h"
#include "oakengine/project.h"
#include "oakengine/serializer.h"
#include "oakengine/undo.h"
#include "panel/panelmanager.h"
#include "node/traverser.h"
#include "ui/icons/icons.h"
@@ -46,6 +47,33 @@
namespace olive
{
namespace
{
QVector<OakEngineNode *> node_vector_to_engine(const QVector<Node *> &nodes)
{
QVector<OakEngineNode *> result;
result.reserve(nodes.size());
foreach (Node *n, nodes) {
result.append(reinterpret_cast<OakEngineNode *>(n));
}
return result;
}
QVector<QPair<OakEngineNode *, OakEngineNode *>>
context_pair_vector_to_engine(const QVector<Node::ContextPair> &pairs)
{
QVector<QPair<OakEngineNode *, OakEngineNode *>> result;
result.reserve(pairs.size());
foreach (const Node::ContextPair &p, pairs) {
result.append(qMakePair(reinterpret_cast<OakEngineNode *>(p.node),
reinterpret_cast<OakEngineNode *>(p.context)));
}
return result;
}
} // namespace
const double NodeView::k_minimum_scale = 0.1;
const int NodeView::k_maximum_contexts = 10;
@@ -59,6 +87,7 @@ NodeView::NodeView(QWidget *parent)
, scale_(1.0)
, dont_emit_selection_signals_(false)
, show_in_param_editor_action_(nullptr)
, bridge_(new EngineEventBridge(this))
{
setScene(&scene_);
set_default_drag_mode(RubberBandDrag);
@@ -70,6 +99,9 @@ NodeView::NodeView(QWidget *parent)
connect(this, &NodeView::customContextMenuRequested, this,
&NodeView::show_context_menu);
connect(bridge_, &EngineEventBridge::node_removed_from_graph, this,
&NodeView::node_removed_from_graph);
connect_selection_changed_signal();
set_flow_direction(NodeViewCommon::k_left_to_right);
@@ -159,16 +191,49 @@ void NodeView::clear_graph()
void NodeView::delete_selected()
{
NodeViewDeleteCommand *command = new NodeViewDeleteCommand();
int count = 0;
QVector<Node *> nodes;
QVector<Node *> contexts;
QVector<NodeViewEdge *> edges;
foreach (NodeViewContext *ctx, scene_.context_map()) {
count += ctx->delete_selected(command);
ctx->get_selected_for_deletion(nodes, contexts, edges);
}
Core::instance()->undo_stack()->push(command,
tr("Deleted %1 Node(s)").arg(count));
if (nodes.isEmpty() && edges.isEmpty()) {
return;
}
// Build C arrays for the facade
QVector<OakEngineNode *> oak_nodes(nodes.size());
QVector<OakEngineNode *> oak_contexts(nodes.size());
for (int i = 0; i < nodes.size(); i++) {
oak_nodes[i] = reinterpret_cast<OakEngineNode *>(nodes[i]);
oak_contexts[i] = reinterpret_cast<OakEngineNode *>(contexts[i]);
}
QVector<OakEngineNode *> edge_outputs(edges.size());
QVector<OakEngineNode *> edge_input_nodes(edges.size());
QVector<QByteArray> edge_input_ids_storage(edges.size());
QVector<const char *> edge_input_ids(edges.size());
QVector<int> edge_input_elements(edges.size());
for (int i = 0; i < edges.size(); i++) {
edge_outputs[i] = reinterpret_cast<OakEngineNode *>(edges[i]->output());
edge_input_nodes[i] = reinterpret_cast<OakEngineNode *>(edges[i]->input().node());
edge_input_ids_storage[i] = edges[i]->input().input().toUtf8();
edge_input_ids[i] = edge_input_ids_storage[i].constData();
edge_input_elements[i] = edges[i]->input().element();
}
// ONE undoable command whether this deletes nodes, edges, or both
oakengine_nodes_delete_many(
oak_nodes.isEmpty() ? nullptr : oak_nodes.constData(),
oak_contexts.isEmpty() ? nullptr : oak_contexts.constData(),
nodes.size(),
edge_outputs.isEmpty() ? nullptr : edge_outputs.constData(),
edge_input_nodes.isEmpty() ? nullptr : edge_input_nodes.constData(),
edge_input_ids.isEmpty() ? nullptr : edge_input_ids.constData(),
edge_input_elements.isEmpty() ? nullptr : edge_input_elements.constData(),
edges.size());
}
void NodeView::select_all()
@@ -199,14 +264,16 @@ void NodeView::deselect_all()
connect_selection_changed_signal();
// Just emit all the nodes that are currently selected as no longer selected
emit nodes_deselected(selected_nodes_);
emit nodes_deselected(node_vector_to_engine(selected_nodes_));
selected_nodes_.clear();
emit node_selection_changed(selected_nodes_);
emit node_selection_changed_with_contexts(QVector<Node::ContextPair>());
emit node_selection_changed(node_vector_to_engine(selected_nodes_));
emit node_selection_changed_with_contexts(
QVector<QPair<OakEngineNode *, OakEngineNode *>>());
}
void NodeView::select(const QVector<Node::ContextPair> &nodes,
bool center_view_on_item)
void NodeView::select(
const QVector<QPair<OakEngineNode *, OakEngineNode *>> &nodes,
bool center_view_on_item)
{
// Optimization: rather than respond to every single item being selected, ignore the signal and
// then handle them all at the end.
@@ -217,10 +284,12 @@ void NodeView::select(const QVector<Node::ContextPair> &nodes,
scene_.deselect_all();
foreach (const Node::ContextPair &p, nodes) {
NodeViewContext *ctx = scene_.context_map().value(p.context);
foreach (const auto &p, nodes) {
Node *node = reinterpret_cast<Node *>(p.first);
Node *context = reinterpret_cast<Node *>(p.second);
NodeViewContext *ctx = scene_.context_map().value(context);
if (ctx) {
NodeViewItem *item = ctx->get_item_from_map(p.node);
NodeViewItem *item = ctx->get_item_from_map(node);
if (item) {
item->setSelected(true);
}
@@ -230,7 +299,7 @@ void NodeView::select(const QVector<Node::ContextPair> &nodes,
// Center on something
if (center_view_on_item && !nodes.isEmpty()) {
QMetaObject::invokeMethod(this, "center_on_node", Qt::QueuedConnection,
OLIVE_NS_ARG(Node *, nodes.first().node));
Q_ARG(OakEngineNode *, nodes.first().first));
}
connect_selection_changed_signal();
@@ -247,34 +316,42 @@ void NodeView::copy_selected(bool cut)
return;
}
QString copy_str;
QXmlStreamWriter writer(&copy_str);
OakEngineClipboard *cb = oakengine_clipboard_create(
OAKENGINE_CLIPBOARD_NODES, nullptr, nullptr);
ProjectSerializer::SaveData sdata(ProjectSerializer::k_only_nodes);
sdata.set_only_serialize_nodes_and_resolve_groups(selected_nodes_);
ProjectSerializer::SerializedProperties properties;
oakengine_clipboard_set_nodes(
cb,
reinterpret_cast<const OakEngineNode *const *>(
selected_nodes_.constData()),
selected_nodes_.size());
for (Node *n : selected_nodes_) {
NodeViewItem *item = get_assumed_item_for_selected_node(n);
if (item) {
Node::Position pos = item->get_node_position_data();
properties[n][QStringLiteral("x")] =
QString::number(pos.position.x());
properties[n][QStringLiteral("y")] =
QString::number(pos.position.y());
properties[n][QStringLiteral("expanded")] =
QString::number(pos.expanded);
oakengine_clipboard_set_property(
cb, reinterpret_cast<OakEngineNode *>(n), "x",
QByteArray::number(pos.position.x()).constData());
oakengine_clipboard_set_property(
cb, reinterpret_cast<OakEngineNode *>(n), "y",
QByteArray::number(pos.position.y()).constData());
oakengine_clipboard_set_property(
cb, reinterpret_cast<OakEngineNode *>(n), "expanded",
QByteArray::number(pos.expanded).constData());
}
}
sdata.set_properties(properties);
// Two-phase buf/size: query the needed length first — serialized node
// graphs have no size bound, a fixed buffer would truncate large copies.
const int needed = oakengine_clipboard_save_to_xml(cb, nullptr, 0);
if (needed >= 0) {
QByteArray buf(needed + 1, '\0');
oakengine_clipboard_save_to_xml(cb, buf.data(), buf.size());
Core::copy_string_to_clipboard(QString::fromUtf8(buf.constData()));
}
ProjectSerializer::save(&writer, sdata);
Core::copy_string_to_clipboard(copy_str);
oakengine_clipboard_free(cb);
if (cut) {
delete_selected();
@@ -287,27 +364,48 @@ void NodeView::paste()
return;
}
ProjectSerializer::Result res =
ProjectSerializer::paste(ProjectSerializer::k_only_nodes);
if (res.get_load_data().nodes.isEmpty()) {
OakEngineClipboard *cb = oakengine_clipboard_create(
OAKENGINE_CLIPBOARD_NODES, nullptr, nullptr);
int result_code = OAKENGINE_SERIALIZER_NO_DATA;
oakengine_clipboard_paste(cb, OAKENGINE_CLIPBOARD_NODES, nullptr,
&result_code, nullptr, 0);
if (result_code != OAKENGINE_SERIALIZER_OK) {
oakengine_clipboard_free(cb);
return;
}
QVector<Node *> nodes;
const int node_count = oakengine_clipboard_get_loaded_node_count(cb);
nodes.reserve(node_count);
for (int i = 0; i < node_count; i++) {
nodes.append(reinterpret_cast<Node *>(
oakengine_clipboard_get_loaded_node_at(cb, i)));
}
Node::PositionMap map;
for (auto it = res.get_load_data().properties.cbegin();
it != res.get_load_data().properties.cend(); it++) {
Node::Position pos;
oakengine_clipboard_foreach_property(
cb,
[](OakEngineNode *node, const char *key, const char *value,
void *userdata) -> int {
auto *m = static_cast<Node::PositionMap *>(userdata);
Node *n = reinterpret_cast<Node *>(node);
Node::Position &pos = (*m)[n];
if (std::strcmp(key, "x") == 0) {
pos.position.setX(QString::fromUtf8(value).toDouble());
} else if (std::strcmp(key, "y") == 0) {
pos.position.setY(QString::fromUtf8(value).toDouble());
} else if (std::strcmp(key, "expanded") == 0) {
pos.expanded = QString::fromUtf8(value).toDouble();
}
return 0;
},
&map);
const QMap<QString, QString> &node_props = it.value();
pos.position.setX(node_props.value(QStringLiteral("x")).toDouble());
pos.position.setY(node_props.value(QStringLiteral("y")).toDouble());
pos.expanded = node_props.value(QStringLiteral("expanded")).toDouble();
oakengine_clipboard_free(cb);
map.insert(it.key(), pos);
}
post_paste(res.get_load_data().nodes, map);
post_paste(nodes, map);
}
void NodeView::duplicate()
@@ -323,9 +421,10 @@ void NodeView::duplicate()
for (int i = 0; i < selected.size(); i++) {
new_nodes[i] = selected.at(i)->copy();
if (NodeGroup *g = dynamic_cast<NodeGroup *>(selected.at(i))) {
for (auto it = g->get_context_positions().cbegin();
it != g->get_context_positions().cend(); it++) {
if (oakengine_node_is_group(
reinterpret_cast<OakEngineNode *>(selected.at(i)))) {
for (auto it = selected.at(i)->get_context_positions().cbegin();
it != selected.at(i)->get_context_positions().cend(); it++) {
if (!selected.contains(it.key())) {
// This should automatically recurse if this is a group inside a group
selected.append(it.key());
@@ -353,30 +452,67 @@ void NodeView::duplicate()
if (child_index != -1) {
Node *child_copy = new_nodes.at(child_index);
copy->set_node_position_in_context(child_copy, it.value());
oakengine_node_set_context_position(
reinterpret_cast<OakEngineNode*>(copy),
reinterpret_cast<OakEngineNode*>(child_copy),
it.value().position.x(), it.value().position.y());
}
}
if (NodeGroup *src_group = dynamic_cast<NodeGroup *>(og)) {
NodeGroup *dst_group = static_cast<NodeGroup *>(copy);
for (auto it = src_group->get_input_passthroughs().cbegin();
it != src_group->get_input_passthroughs().cend(); it++) {
NodeInput input = it->second;
input.set_node(
new_nodes.at(selected.indexOf(input.node())));
dst_group->add_input_passthrough(input, it->first);
if (oakengine_node_is_group(reinterpret_cast<OakEngineNode *>(og))) {
const int pt_count = oakengine_group_input_passthrough_count(
reinterpret_cast<OakEngineNode *>(og));
for (int pt = 0; pt < pt_count; pt++) {
OakEngineNode *inner_node = nullptr;
char inner_input[256];
int inner_element = 0;
char id[256];
if (oakengine_group_input_passthrough_at(
reinterpret_cast<OakEngineNode *>(og), pt,
id, sizeof(id), &inner_node, inner_input,
sizeof(inner_input), &inner_element) !=
OAKENGINE_OK) {
continue;
}
Node *inner = reinterpret_cast<Node *>(inner_node);
int src_index = selected.indexOf(inner);
if (src_index == -1) {
continue;
}
Node *copy_inner = new_nodes.at(src_index);
char out_id[256];
oakengine_group_add_input_passthrough(
reinterpret_cast<OakEngineNode *>(copy),
reinterpret_cast<OakEngineNode *>(copy_inner),
inner_input, inner_element, id,
out_id, sizeof(out_id));
}
dst_group->set_output_passthrough(new_nodes.at(
selected.indexOf(src_group->get_output_passthrough())));
if (OakEngineNode *output = oakengine_group_get_output_passthrough(
reinterpret_cast<OakEngineNode *>(og))) {
int idx = selected.indexOf(reinterpret_cast<Node *>(output));
if (idx >= 0 && idx < new_nodes.size()) {
oakengine_group_set_output_passthrough(
reinterpret_cast<OakEngineNode *>(copy),
reinterpret_cast<OakEngineNode *>(
new_nodes.at(idx)));
}
}
}
Node::copy_inputs(selected.at(i), new_nodes.at(i), false);
oakengine_node_copy_inputs(
reinterpret_cast<OakEngineNode*>(new_nodes.at(i)),
reinterpret_cast<const OakEngineNode*>(selected.at(i)));
}
// Copy connections
Node::copy_dependency_graph(selected, new_nodes, nullptr);
{
QVector<OakEngineNode*> sel_arr, new_arr;
for (Node *n : selected) sel_arr.append(reinterpret_cast<OakEngineNode*>(n));
for (Node *n : new_nodes) new_arr.append(reinterpret_cast<OakEngineNode*>(n));
oakengine_node_copy_dependency_graph(
sel_arr.data(), new_arr.data(), sel_arr.size(), nullptr);
}
// Set root level context positions and attach to
post_paste(new_nodes, map);
@@ -385,14 +521,17 @@ void NodeView::duplicate()
void NodeView::set_color_label(int index)
{
MultiUndoCommand *command = new MultiUndoCommand();
void *command = oakengine_undo_command_create_multi();
for (Node *node : qAsConst(selected_nodes_)) {
command->add_child(new NodeOverrideColorCommand(node, index));
oakengine_undo_command_multi_add_child(
command,
oakengine_node_set_color_label_command(
reinterpret_cast<OakEngineNode *>(node), index));
}
Core::instance()->undo_stack()->push(
command, tr("Set Color of %1 Node(s)").arg(selected_nodes_.size()));
oakengine_undo_push(
command, tr("Set Color of %1 Node(s)").arg(selected_nodes_.size()).toUtf8().constData());
}
void NodeView::zoom_in()
@@ -412,7 +551,7 @@ void NodeView::keyPressEvent(QKeyEvent *event)
case Qt::Key_Right:
case Qt::Key_Up:
case Qt::Key_Down: {
MultiUndoCommand *pos_command = new MultiUndoCommand();
void *pos_command = oakengine_undo_command_create_multi();
for (Node *n : qAsConst(selected_nodes_)) {
for (Node *context : qAsConst(contexts_)) {
if (context->context_contains_node(n)) {
@@ -444,13 +583,20 @@ void NodeView::keyPressEvent(QKeyEvent *event)
node_movement, scene_.get_flow_direction());
// Move command
pos_command->add_child(new NodeSetPositionCommand(
n, context, old_pos + node_movement));
Node::Position new_pos = old_pos;
new_pos.position += node_movement;
oakengine_undo_command_multi_add_child(
pos_command,
oakengine_node_set_position_command(
reinterpret_cast<void *>(n),
reinterpret_cast<void *>(context),
new_pos.position.x(), new_pos.position.y(),
new_pos.expanded ? 1 : 0));
}
}
}
Core::instance()->undo_stack()->push(
pos_command, tr("Moved %1 Node(s)").arg(selected_nodes_.size()));
oakengine_undo_push(
pos_command, tr("Moved %1 Node(s)").arg(selected_nodes_.size()).toUtf8().constData());
break;
}
case Qt::Key_Escape:
@@ -601,7 +747,7 @@ void NodeView::mouseReleaseEvent(QMouseEvent *event)
end_edge_drag();
}
MultiUndoCommand *command = new MultiUndoCommand();
void *command = oakengine_undo_command_create_multi();
Node *select_context = nullptr;
QVector<Node *> select_nodes;
@@ -632,13 +778,15 @@ void NodeView::mouseReleaseEvent(QMouseEvent *event)
}
QPointF current_pos = i->get_node_position();
if (dragging_items_.value(i) != current_pos) {
command->add_child(new NodeSetPositionCommand(
i->get_node(), i->get_context(), current_pos));
oakengine_undo_command_multi_add_child(command, oakengine_node_set_position_command(
reinterpret_cast<void *>(i->get_node()),
reinterpret_cast<void *>(i->get_context()), current_pos.x(),
current_pos.y(), 0));
}
}
Core::instance()->undo_stack()->push(
command, tr("Moved %1 Node(s)").arg(dragging_items_.size()));
oakengine_undo_push(
command, tr("Moved %1 Node(s)").arg(dragging_items_.size()).toUtf8().constData());
dragging_items_.clear();
@@ -674,8 +822,8 @@ void NodeView::dragEnterEvent(QDragEnterEvent *event)
QStringList mime_fmts = event->mimeData()->formats();
if (mime_fmts.contains(Project::k_item_mime_type)) {
QByteArray model_data = event->mimeData()->data(Project::k_item_mime_type);
if (mime_fmts.contains(QString::fromUtf8(oakengine_project_item_mime_type()))) {
QByteArray model_data = event->mimeData()->data(QString::fromUtf8(oakengine_project_item_mime_type()));
QDataStream stream(&model_data, QIODevice::ReadOnly);
// Variables to deserialize into
@@ -732,11 +880,11 @@ void NodeView::dragMoveEvent(QDragMoveEvent *event)
void NodeView::dropEvent(QDropEvent *event)
{
if (Node *drop_ctx = get_context_at_mouse_pos(event->pos())) {
MultiUndoCommand *command = new MultiUndoCommand();
void *command = oakengine_undo_command_create_multi();
QVector<Node *> select_nodes =
process_dropping_attached_nodes(command, drop_ctx, event->pos());
Core::instance()->undo_stack()->push(
command, tr("Dropped %1 Node(s)").arg(select_nodes.size()));
oakengine_undo_push(
command, tr("Dropped %1 Node(s)").arg(select_nodes.size()).toUtf8().constData());
deselect_all();
scene_.context_map().value(drop_ctx)->select(select_nodes);
@@ -815,16 +963,17 @@ void NodeView::update_selection_cache()
}
if (!deselected.isEmpty()) {
emit nodes_deselected(deselected);
emit nodes_deselected(node_vector_to_engine(deselected));
}
if (!selected.isEmpty()) {
emit nodes_selected(selected);
emit nodes_selected(node_vector_to_engine(selected));
}
if (!dont_emit_selection_signals_) {
emit node_selection_changed(selected_nodes_);
emit node_selection_changed_with_contexts(sel_with_ctx);
emit node_selection_changed(node_vector_to_engine(selected_nodes_));
emit node_selection_changed_with_contexts(
context_pair_vector_to_engine(sel_with_ctx));
}
}
@@ -856,7 +1005,8 @@ void NodeView::show_context_menu(const QPoint &pos)
if (item_under_cursor && !selected.isEmpty()) {
// Grouping
if (selected.size() == 1 &&
dynamic_cast<NodeGroup *>(selected.first()->get_node())) {
oakengine_node_is_group(reinterpret_cast<OakEngineNode *>(
selected.first()->get_node()))) {
QAction *ungroup_action = m.addAction(tr("Ungroup"));
connect(ungroup_action, &QAction::triggered, this,
&NodeView::ungroup_nodes);
@@ -947,9 +1097,10 @@ void NodeView::create_node_slot(QAction *action)
new_attached.append({ new_item, new_node, QPointF(0, 0) });
if (NodeGroup *new_group = dynamic_cast<NodeGroup *>(new_node)) {
for (auto it = new_group->get_context_positions().cbegin();
it != new_group->get_context_positions().cend(); it++) {
if (oakengine_node_is_group(
reinterpret_cast<OakEngineNode *>(new_node))) {
for (auto it = new_node->get_context_positions().cbegin();
it != new_node->get_context_positions().cend(); it++) {
new_attached.append({ nullptr, it.key(), QPointF(0, 0) });
}
}
@@ -992,10 +1143,11 @@ void NodeView::center_on_items_bounding_rect()
centerOn(scene_.itemsBoundingRect().center());
}
void NodeView::center_on_node(Node *n)
void NodeView::center_on_node(OakEngineNode *n)
{
foreach (NodeViewContext *ctx, scene_.context_map()) {
if (NodeViewItem *item = ctx->get_item_from_map(n)) {
if (NodeViewItem *item = ctx->get_item_from_map(
reinterpret_cast<Node *>(n))) {
centerOn(item);
break;
}
@@ -1036,9 +1188,9 @@ void NodeView::move_to_scene_point(const QPointF &pos)
centerOn(pos);
}
void NodeView::node_removed_from_graph()
void NodeView::node_removed_from_graph(OakEngineNode *source)
{
Node *context = static_cast<Node *>(sender());
Node *context = reinterpret_cast<Node *>(source);
remove_context(context);
@@ -1111,7 +1263,7 @@ void NodeView::process_moving_attached_nodes(const QPoint &pos)
} else {
// Otherwise, we may have to iterate to find a valid one
for (const QString &input : attached_node->inputs()) {
if (input == Node::k_enabled_input) {
if (input == QLatin1String(oakengine_node_enabled_input_id())) {
// Ignore enabled input
continue;
}
@@ -1160,7 +1312,7 @@ void NodeView::process_moving_attached_nodes(const QPoint &pos)
}
QVector<Node *>
NodeView::process_dropping_attached_nodes(MultiUndoCommand *command,
NodeView::process_dropping_attached_nodes(void *command,
Node *select_context, const QPoint &pos)
{
QVector<Node *> select_nodes;
@@ -1180,41 +1332,44 @@ NodeView::process_dropping_attached_nodes(MultiUndoCommand *command,
}
{
MultiUndoCommand *add_command = new MultiUndoCommand();
void *add_command = oakengine_undo_command_create_multi();
foreach (const AttachedItem &ai, attached) {
// Add node to the same graph that the context is in
if (ai.node->parent() != select_context->parent()) {
add_command->add_child(
new NodeAddCommand(select_context->parent(), ai.node));
oakengine_undo_command_multi_add_child(add_command,
oakengine_node_add_to_project_command(
reinterpret_cast<OakEngineProject *>(select_context->parent()),
reinterpret_cast<OakEngineNode *>(ai.node)));
if (ai.node->is_item() && !ai.node->folder()) {
add_command->add_child(new FolderAddChild(
select_context->parent()->root(), ai.node));
oakengine_folder_add_child(
reinterpret_cast<OakEngineNode *>(select_context->parent()->root()),
reinterpret_cast<OakEngineNode *>(ai.node));
}
}
// Add node to the context
if (ai.item) {
select_nodes.append(ai.node);
add_command->add_child(new NodeSetPositionCommand(
ai.node, select_context,
scene_.context_map()
oakengine_undo_command_multi_add_child(add_command, oakengine_node_set_position_command(reinterpret_cast<void *>(ai.node), reinterpret_cast<void *>(select_context), scene_.context_map()
.value(select_context)
->map_scene_pos_to_node_pos_in_context(ai.item->pos())));
->map_scene_pos_to_node_pos_in_context(ai.item->pos()).x(), scene_.context_map()
.value(select_context)
->map_scene_pos_to_node_pos_in_context(ai.item->pos()).y(), 0));
}
}
if (add_command->child_count()) {
add_command->redo_now();
command->add_child(add_command);
if (oakengine_undo_command_multi_child_count(add_command) > 0) {
oakengine_undo_command_redo_now(add_command);
oakengine_undo_command_multi_add_child(command, add_command);
} else {
delete add_command;
oakengine_undo_command_free(add_command);
}
}
{
// Dropped attached item onto an edge, connect it between them
MultiUndoCommand *drop_edge_command = new MultiUndoCommand();
// Dropped attached item onto an edge, connect it between them as one
// undoable child of the parent command.
if (attached.size() == 1) {
Node *dropping_node = nullptr;
@@ -1226,25 +1381,35 @@ NodeView::process_dropping_attached_nodes(MultiUndoCommand *command,
}
if (dropping_node && drop_edge_) {
// Remove old edge
drop_edge_command->add_child(new NodeEdgeRemoveCommand(
drop_edge_->output(), drop_edge_->input()));
void *edge_command = oakengine_undo_command_create_multi();
// Place new edges
drop_edge_command->add_child(
new NodeEdgeAddCommand(drop_edge_->output(), drop_input_));
drop_edge_command->add_child(
new NodeEdgeAddCommand(dropping_node, drop_edge_->input()));
oakengine_undo_command_multi_add_child(
edge_command,
oakengine_node_disconnect_command(
reinterpret_cast<OakEngineNode *>(drop_edge_->input().node()),
drop_edge_->input().input().toUtf8().constData(),
drop_edge_->input().element()));
oakengine_undo_command_multi_add_child(
edge_command,
oakengine_node_connect_command(
reinterpret_cast<OakEngineNode *>(drop_edge_->output()),
reinterpret_cast<OakEngineNode *>(drop_input_.node()),
drop_input_.input().toUtf8().constData(),
drop_input_.element()));
oakengine_undo_command_multi_add_child(
edge_command,
oakengine_node_connect_command(
reinterpret_cast<OakEngineNode *>(dropping_node),
reinterpret_cast<OakEngineNode *>(drop_edge_->input().node()),
drop_edge_->input().input().toUtf8().constData(),
drop_edge_->input().element()));
oakengine_undo_command_redo_now(edge_command);
oakengine_undo_command_multi_add_child(command, edge_command);
}
drop_edge_ = nullptr;
}
if (drop_edge_command->child_count()) {
drop_edge_command->redo_now();
command->add_child(drop_edge_command);
} else {
delete drop_edge_command;
}
}
detach_items_from_cursor(false);
@@ -1518,28 +1683,29 @@ void NodeView::group_nodes()
avg_pos /= items.size();
// Create group
NodeGroup *group = new NodeGroup();
Node *group = reinterpret_cast<Node *>(oakengine_node_group_create());
// Add group to graph and context
MultiUndoCommand *command = new MultiUndoCommand();
void *command = oakengine_undo_command_create_multi();
// Add nodes to group
Node *output_passthrough = nullptr;
QVector<Node *> nodes_to_group = selected_nodes_;
deselect_all();
foreach (Node *n, nodes_to_group) {
command->add_child(
new NodeRemovePositionFromContextCommand(n, context));
command->add_child(new NodeSetPositionCommand(
n, group, context->get_node_position_data_in_context(n)));
oakengine_undo_command_multi_add_child(command, oakengine_node_remove_position_command(reinterpret_cast<void *>(n), reinterpret_cast<void *>(context)));
oakengine_undo_command_multi_add_child(command, oakengine_node_set_position_command(reinterpret_cast<void *>(n), reinterpret_cast<void *>(group), context->get_node_position_data_in_context(n).position.x(), context->get_node_position_data_in_context(n).position.y(), context->get_node_position_data_in_context(n).expanded ? 1 : 0));
for (auto it = n->inputs().cbegin(); it != n->inputs().cend(); it++) {
NodeInput input(n, *it, -1);
if (!input.is_connected() ||
!nodes_to_group.contains(input.get_connected_output())) {
command->add_child(
new NodeGroupAddInputPassthrough(group, input));
oakengine_undo_command_multi_add_child(command, (void *)(oakengine_group_add_input_passthrough_command(
reinterpret_cast<OakEngineNode *>(group),
reinterpret_cast<OakEngineNode *>(input.node()),
input.input().toUtf8().constData(), input.element(),
nullptr)));
}
}
@@ -1556,17 +1722,21 @@ void NodeView::group_nodes()
}
// Set output passthrough
command->add_child(
new NodeGroupSetOutputPassthrough(group, output_passthrough));
oakengine_undo_command_multi_add_child(command, (void *)(oakengine_group_set_output_passthrough_command(
reinterpret_cast<OakEngineNode *>(group),
reinterpret_cast<OakEngineNode *>(output_passthrough))));
// Add group to graph
command->add_child(new NodeAddCommand(context->parent(), group));
command->add_child(new NodeSetPositionCommand(group, context, avg_pos));
oakengine_undo_command_multi_add_child(command,
oakengine_node_add_to_project_command(
reinterpret_cast<OakEngineProject *>(context->parent()),
reinterpret_cast<OakEngineNode *>(group)));
oakengine_undo_command_multi_add_child(command, oakengine_node_set_position_command(reinterpret_cast<void *>(group), reinterpret_cast<void *>(context), avg_pos.x(), avg_pos.y(), 0));
// Do command
Core::instance()->label_nodes({ group }, command);
Core::instance()->undo_stack()->push(command, tr("Grouped Nodes"));
oakengine_undo_push(command, tr("Grouped Nodes").toUtf8().constData());
}
void NodeView::ungroup_nodes()
@@ -1577,9 +1747,11 @@ void NodeView::ungroup_nodes()
return;
}
NodeGroup *group = nullptr;
Node *group = nullptr;
foreach (NodeViewItem *i, items) {
if ((group = dynamic_cast<NodeGroup *>(i->get_node()))) {
if (oakengine_node_is_group(reinterpret_cast<OakEngineNode *>(
i->get_node()))) {
group = i->get_node();
group_item = i;
break;
}
@@ -1589,30 +1761,27 @@ void NodeView::ungroup_nodes()
return;
}
MultiUndoCommand *command = new MultiUndoCommand();
void *command = oakengine_undo_command_create_multi();
Node *context = group_item->get_context();
command->add_child(
new NodeRemovePositionFromContextCommand(group, context));
command->add_child(new NodeRemoveAndDisconnectCommand(group));
oakengine_undo_command_multi_add_child(command, oakengine_node_remove_position_command(reinterpret_cast<void *>(group), reinterpret_cast<void *>(context)));
oakengine_undo_command_multi_add_child(command, oakengine_node_remove_and_disconnect_command(reinterpret_cast<void *>(group)));
for (auto it = group->get_context_positions().cbegin();
it != group->get_context_positions().cend(); it++) {
command->add_child(
new NodeRemovePositionFromContextCommand(it.key(), group));
command->add_child(new NodeSetPositionCommand(
it.key(), context, group->get_node_position_data_in_context(it.key())));
oakengine_undo_command_multi_add_child(command, oakengine_node_remove_position_command(reinterpret_cast<void *>(it.key()), reinterpret_cast<void *>(group)));
oakengine_undo_command_multi_add_child(command, oakengine_node_set_position_command(reinterpret_cast<void *>(it.key()), reinterpret_cast<void *>(context), group->get_node_position_data_in_context(it.key()).position.x(), group->get_node_position_data_in_context(it.key()).position.y(), group->get_node_position_data_in_context(it.key()).expanded ? 1 : 0));
}
Core::instance()->undo_stack()->push(command, tr("Ungrouped Nodes"));
oakengine_undo_push(command, tr("Ungrouped Nodes").toUtf8().constData());
}
void NodeView::show_node_properties()
{
Node *first_node = selected_nodes_.first();
if (NodeGroup *group = dynamic_cast<NodeGroup *>(first_node)) {
if (oakengine_node_is_group(reinterpret_cast<OakEngineNode *>(first_node))) {
if (!overlay_view_) {
overlay_view_ = new NodeView(this);
overlay_view_->show();
@@ -1647,19 +1816,20 @@ void NodeView::show_node_properties()
overlay_close_btn->setStyleSheet(
QStringLiteral("background: transparent; border: none;"));
}
overlay_view_->set_contexts({ group });
overlay_view_->set_contexts({ first_node });
resize_overlay();
QMetaObject::invokeMethod(overlay_view_,
&NodeView::center_on_items_bounding_rect,
Qt::QueuedConnection);
overlay_view_->setFocus();
emit nodes_deselected(selected_nodes_);
emit node_selection_changed(QVector<Node *>());
emit node_selection_changed_with_contexts(QVector<Node::ContextPair>());
emit nodes_deselected(node_vector_to_engine(selected_nodes_));
emit node_selection_changed(QVector<OakEngineNode *>());
emit node_selection_changed_with_contexts(
QVector<QPair<OakEngineNode *, OakEngineNode *>>());
overlay_view_->select_all();
emit node_group_opened(group);
emit node_group_opened(reinterpret_cast<OakEngineNode *>(first_node));
} else {
label_selected_nodes();
}
@@ -1701,7 +1871,8 @@ void NodeView::show_selected_node_in_param_editor()
}
}
emit node_selection_changed_with_contexts(selection_with_contexts);
emit node_selection_changed_with_contexts(
context_pair_vector_to_engine(selection_with_contexts));
}
void NodeView::label_selected_nodes()
@@ -1752,14 +1923,14 @@ void NodeView::add_context(Node *n)
connect(ctx, &NodeViewContext::item_about_to_be_deleted, this,
&NodeView::item_about_to_be_deleted);
connect(n, &Node::removed_from_graph, this, &NodeView::node_removed_from_graph);
removed_from_graph_subs_[n] = bridge_->subscribe(
reinterpret_cast<void *>(n), OAKENGINE_EVENT_NODE_REMOVED_FROM_GRAPH);
}
void NodeView::remove_context(Node *n)
{
scene_.remove_context(n);
disconnect(n, &Node::removed_from_graph, this,
&NodeView::node_removed_from_graph);
bridge_->unsubscribe(removed_from_graph_subs_.take(n));
}
bool NodeView::is_item_attached_to_cursor(NodeViewItem *item) const
@@ -1788,7 +1959,7 @@ void NodeView::collapse_item(NodeViewItem *item)
void NodeView::end_edge_drag(bool cancel)
{
// Check if the edge was reconnected to the same place as before
MultiUndoCommand *command = new MultiUndoCommand();
void *command = oakengine_undo_command_create_multi();
bool reconnected_to_itself = false;
@@ -1799,8 +1970,12 @@ void NodeView::end_edge_drag(bool cancel)
reconnected_to_itself = true;
} else {
// We are moving (or removing) an existing edge
command->add_child(new NodeEdgeRemoveCommand(
create_edge_->output(), create_edge_->input()));
oakengine_undo_command_multi_add_child(
command,
oakengine_node_disconnect_command(
reinterpret_cast<OakEngineNode *>(create_edge_->input().node()),
create_edge_->input().input().toUtf8().constData(),
create_edge_->input().element()));
}
}
} else {
@@ -1827,15 +2002,32 @@ void NodeView::end_edge_drag(bool cancel)
if (!reconnected_to_itself) {
Node *creating_output = create_edge_output_item_->get_node();
while (NodeGroup *output_group =
dynamic_cast<NodeGroup *>(creating_output)) {
creating_output = output_group->get_output_passthrough();
while (oakengine_node_is_group(
reinterpret_cast<OakEngineNode *>(creating_output))) {
OakEngineNode *out = oakengine_group_get_output_passthrough(
reinterpret_cast<OakEngineNode *>(creating_output));
if (!out) {
break;
}
creating_output = reinterpret_cast<Node *>(out);
}
while (NodeGroup *input_group =
dynamic_cast<NodeGroup *>(creating_input.node())) {
creating_input =
input_group->get_input_from_id(creating_input.input());
while (oakengine_node_is_group(reinterpret_cast<OakEngineNode *>(
creating_input.node()))) {
OakEngineNode *inner_node = nullptr;
char inner_input[256];
int inner_element = 0;
if (oakengine_group_get_passthrough_from_id(
reinterpret_cast<OakEngineNode *>(
creating_input.node()),
creating_input.input().toUtf8().constData(),
&inner_node, inner_input, sizeof(inner_input),
&inner_element) != OAKENGINE_OK) {
break;
}
creating_input = NodeInput(
reinterpret_cast<Node *>(inner_node),
QString::fromUtf8(inner_input), inner_element);
}
if (creating_input.is_connected()) {
@@ -1862,33 +2054,52 @@ void NodeView::end_edge_drag(bool cancel)
}
if (!cancel) {
command->add_child(new NodeEdgeRemoveCommand(
existing_edge_to_remove.first,
existing_edge_to_remove.second));
oakengine_undo_command_multi_add_child(
command,
oakengine_node_disconnect_command(
reinterpret_cast<OakEngineNode *>(existing_edge_to_remove.second.node()),
existing_edge_to_remove.second.input().toUtf8().constData(),
existing_edge_to_remove.second.element()));
}
}
if (!cancel) {
command->add_child(new NodeEdgeAddCommand(creating_output,
creating_input));
oakengine_undo_command_multi_add_child(
command,
oakengine_node_connect_command(
reinterpret_cast<OakEngineNode *>(creating_output),
reinterpret_cast<OakEngineNode *>(creating_input.node()),
creating_input.input().toUtf8().constData(),
creating_input.element()));
command_name = Node::get_connect_command_string(
creating_output, creating_input);
{
char cmd_buf[256];
oakengine_node_connect_command_string(
reinterpret_cast<OakEngineNode*>(creating_output),
reinterpret_cast<OakEngineNode*>(creating_input.node()),
creating_input.input().toUtf8().constData(),
creating_input.element(), cmd_buf, sizeof(cmd_buf));
command_name = QString::fromUtf8(cmd_buf);
}
// If the output is not in the input's context, add it now. We check the item rather than
// the node itself, because sometimes a node may not be in the context but another node
// representing it will be (e.g. groups)
if (!scene_.context_map()
.value(create_edge_input_item_->get_context())
->get_item_from_map(creating_output)) {
command->add_child(new NodeSetPositionCommand(
creating_output,
create_edge_input_item_->get_context(),
scene_.context_map()
.value(create_edge_input_item_->get_context())
->map_scene_pos_to_node_pos_in_context(
create_edge_output_item_->scenePos())));
}
if (!scene_.context_map()
.value(create_edge_input_item_->get_context())
->get_item_from_map(creating_output)) {
QPointF new_pos = scene_.context_map()
.value(create_edge_input_item_->get_context())
->map_scene_pos_to_node_pos_in_context(
create_edge_output_item_->scenePos());
oakengine_undo_command_multi_add_child(
command,
oakengine_node_set_position_command(
reinterpret_cast<void *>(creating_output),
reinterpret_cast<void *>(
create_edge_input_item_->get_context()),
new_pos.x(), new_pos.y(), 0));
}
}
}
}
@@ -1905,9 +2116,10 @@ void NodeView::end_edge_drag(bool cancel)
}
create_edge_expanded_items_.clear();
Core::instance()->undo_stack()->push(command, command_name);
oakengine_undo_push(command, command_name.toUtf8().constData());
}
void NodeView::post_paste(const QVector<Node *> &new_nodes,
const Node::PositionMap &map)
{