feat(engine,app): effect chain facade + app effect stack wiring

facade (oakengine/oaknode, API only extended):
- oaknode_node_get_effect_input / oaknode_node_get_flags module exports
- oakengine_clip_as_node, oakengine_node_effect_count/at/insert/remove/
  move/set_enabled, oakengine_node_identity/is_enabled/get_type_id,
  oakengine_node_factory_id_at — chain edits wrap disconnect/reconnect
  into single undo groups; insert/move/remove validated against the
  factory and the host's effect input
- it_node covers enumeration/insert/remove/move/toggle/undo-redo and
  the NULL/illegal-input matrix

app:
- RealEngine binds the selected clip's effect chain to the inspector's
  effect stack (cards, enable, reorder, remove, expansion state), all
  edits undoable through the facade
- AppEngine gains set_selected_clips / addable_effects / add_effect
  (default no-ops keep the mock untouched); the timeline selection
  drives the stack target; the inspector gets a small add-effect menu
- known module gap (documented): ClipBlockBehavior sets no effect_input
  yet, so timeline clips cannot host effects until oaknode grows tex_in
This commit is contained in:
2026-08-11 20:04:44 +08:00
parent f86a895c8a
commit cdda643d64
10 changed files with 1443 additions and 28 deletions
+34
View File
@@ -1016,6 +1016,40 @@ pub mod node {
})
}
/// `oaknode_node_get_effect_input` (two-stage; `Node::GetEffectInputID()`).
///
/// The id of the input the effect chain attaches to (empty when the node
/// cannot host effects — the C++ contract: "If this is empty, effects
/// cannot attach to this node").
#[no_mangle]
pub unsafe extern "C" fn oaknode_node_get_effect_input(
node: CHandle,
buf: *mut c_char,
buf_size: c_int,
) -> c_int {
let n = match unsafe { node_ref(&node) } {
Ok(n) => n,
Err(_) => return crate::error::OAKNODE_E_INVALID,
};
let effect_input = with_graph_read(&n.project, |g| {
g.get(n.id).map(|e| e.core.effect_input.clone())
});
match effect_input {
Some(id) => copy_string_out(&id, buf, buf_size),
None => crate::error::OAKNODE_E_NOT_FOUND,
}
}
/// `oaknode_node_get_flags` — the node's flags bitmask (`Node::flags_`).
#[no_mangle]
pub unsafe extern "C" fn oaknode_node_get_flags(node: CHandle) -> u64 {
let n = match unsafe { node_ref(&node) } {
Ok(n) => n,
Err(_) => return 0,
};
with_graph_read(&n.project, |g| g.get(n.id).map(|e| e.core.flags)).unwrap_or(0)
}
// ---- Input introspection ------------------------------------------
/// `oaknode_node_input_count`.