feat: built-in effect params, clip click-select, effect drag-and-drop, project load with plugins
- serializer resolves node types through the factory's dynamic (runtime-registered OpenFX) entries, so a project carrying plugin nodes loads again (was: "unknown node type"); covered by a new CI-gated round-trip test driving the real fixture plugin - built-in effect nodes expose their inputs as inspector parameters like the C++ parameter editor: localized input names from the behavior, combo option tables via the new NodeBehavior::input_combo_strings (16 nodes, string-for-string from the C++ set_combo_box_strings), connection/data inputs excluded - effect library: live drag-and-drop — onto the inspector's effect stack (lands at the indicator position) and onto the node editor canvas (creates the node at the drop point); double-click still appends to the selected clip - inspector parameter controls are no longer recreated per render (gpui stack view caches them per effect), so sliders drag and checkboxes click; the view observes the engine and silently re-syncs values (undo/redo land on the widgets) - timeline: left-press selects clips (plain/keep-multi/Ctrl-Cmd toggle); clip moves clamp the shared delta so no clip of a linked group lands before frame 0 instead of failing with "invalid move target" - oakplugin: createInstance-rejected instances skip the destroyInstance notification (the plugin never owned them); vendor-suite fetchSuite misses moved behind OAK_OFX_TRACE; the worker logs the discovered/ registered plugin counts - CI: the OFX probe step also runs the serialization round-trip test - gpui submodule: params view caching, clip click-select, library drag payload, graph_position_at
This commit is contained in:
@@ -501,6 +501,14 @@ pub trait NodeBehavior: Send {
|
||||
}
|
||||
}
|
||||
|
||||
/// The option labels of a combo input (C++ `set_combo_box_strings`,
|
||||
/// called from each node's `retranslate`). Empty for non-combo inputs;
|
||||
/// the default is no options.
|
||||
fn input_combo_strings(&self, id: &str) -> Vec<&'static str> {
|
||||
let _ = id;
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
/// Inputs excluded from rendering (C++ `ignore_inputs_for_rendering()`).
|
||||
fn ignore_inputs_for_rendering(&self) -> &[String] {
|
||||
&[]
|
||||
|
||||
@@ -305,6 +305,16 @@ impl NodeBehavior for BlurFilterNode {
|
||||
}
|
||||
}
|
||||
|
||||
/// Combo input option labels (C++ `retranslate()` /
|
||||
/// `set_combo_box_strings`): `method_in` -> "Box", "Gaussian",
|
||||
/// "Directional", "Radial".
|
||||
fn input_combo_strings(&self, id: &str) -> Vec<&'static str> {
|
||||
match id {
|
||||
METHOD_INPUT => vec!["Box", "Gaussian", "Directional", "Radial"],
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluate outputs (C++ `value()`): no texture -> push nothing;
|
||||
/// radius <= 0.0, or box/gaussian with both horiz and vert unchecked
|
||||
/// -> pass-through push of the input texture; otherwise push a shader
|
||||
|
||||
@@ -174,6 +174,15 @@ impl NodeBehavior for ColorDifferenceKeyNode {
|
||||
}
|
||||
}
|
||||
|
||||
/// Combo input option labels (C++ `retranslate()` /
|
||||
/// `set_combo_box_strings`): `color_in` -> "Green", "Blue".
|
||||
fn input_combo_strings(&self, id: &str) -> Vec<&'static str> {
|
||||
match id {
|
||||
COLOR_INPUT => vec!["Green", "Blue"],
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluate outputs (C++ `value()`): no texture on `tex_in` ->
|
||||
/// push nothing; texture present -> push a `ShaderJob` with the
|
||||
/// whole input row inserted.
|
||||
|
||||
@@ -158,6 +158,18 @@ impl NodeBehavior for DespillNode {
|
||||
}
|
||||
}
|
||||
|
||||
/// Combo input option labels (C++ `retranslate()` /
|
||||
/// `set_combo_box_strings`): `color_in` -> "Green", "Blue";
|
||||
/// `method_in` -> "Average", "Double Red Average", "Double Average",
|
||||
/// "Limit".
|
||||
fn input_combo_strings(&self, id: &str) -> Vec<&'static str> {
|
||||
match id {
|
||||
COLOR_INPUT => vec!["Green", "Blue"],
|
||||
METHOD_INPUT => vec!["Average", "Double Red Average", "Double Average", "Limit"],
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluate outputs (C++ `value()`): builds a `ShaderJob` from
|
||||
/// the whole input row, then inserts a `luma_coeffs` vec3 taken
|
||||
/// from the project's color manager default luma coefficients
|
||||
|
||||
@@ -165,6 +165,18 @@ impl NodeBehavior for DisplayTransformNode {
|
||||
}
|
||||
}
|
||||
|
||||
/// Combo input option labels (C++ `retranslate()` /
|
||||
/// `set_combo_box_strings`): `dir_in` -> "Forward", "Inverse". The
|
||||
/// `display_in`/`view_in` strings come from the attached color
|
||||
/// manager at runtime (`update_displays`/`update_views`) and cannot
|
||||
/// be expressed statically.
|
||||
fn input_combo_strings(&self, id: &str) -> Vec<&'static str> {
|
||||
match id {
|
||||
DIRECTION_INPUT => vec!["Forward", "Inverse"],
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Input value changed (C++ `InputValueChangedEvent`): for
|
||||
/// `display_in`, `view_in` or `dir_in` regenerates the processor;
|
||||
/// a `display_in` change additionally refreshes the view combo.
|
||||
|
||||
@@ -100,6 +100,16 @@ impl NodeBehavior for MathNode {
|
||||
}
|
||||
}
|
||||
|
||||
/// Combo input option labels (C++ `retranslate()` /
|
||||
/// `set_combo_box_strings`): `method_in` -> the five operation names
|
||||
/// "Add", "Subtract", "Multiply", "Divide", "Power".
|
||||
fn input_combo_strings(&self, id: &str) -> Vec<&'static str> {
|
||||
match id {
|
||||
METHOD_INPUT => vec!["Add", "Subtract", "Multiply", "Divide", "Power"],
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluate outputs (C++ `value()`): pushes both operands into
|
||||
/// single-value tables, runs the [`super::mathbase::PairingCalculator`]
|
||||
/// heuristic, and if a pairing was found delegates to
|
||||
|
||||
@@ -190,6 +190,17 @@ impl NodeBehavior for MultiCamNode {
|
||||
}
|
||||
}
|
||||
|
||||
/// Combo input option labels (C++ `retranslate()` /
|
||||
/// `set_combo_box_strings`): `sequence_type_in` -> "Video", "Audio".
|
||||
/// `current_in`'s strings are built dynamically per connected source
|
||||
/// (`"<i + 1>: <name>"`) and cannot be expressed statically.
|
||||
fn input_combo_strings(&self, id: &str) -> Vec<&'static str> {
|
||||
match id {
|
||||
SEQUENCE_TYPE_INPUT => vec!["Video", "Audio"],
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Inputs excluded from rendering (C++
|
||||
/// `ignore_inputs_for_rendering()`): always
|
||||
/// `{ k_sequence_input }`.
|
||||
|
||||
@@ -269,6 +269,15 @@ impl NodeBehavior for OCIOLutNode {
|
||||
}
|
||||
}
|
||||
|
||||
/// Combo input option labels (C++ `retranslate()` /
|
||||
/// `set_combo_box_strings`): `lut_dir_in` -> "Forward", "Inverse".
|
||||
fn input_combo_strings(&self, id: &str) -> Vec<&'static str> {
|
||||
match id {
|
||||
DIRECTION_INPUT => vec!["Forward", "Inverse"],
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Input value changed (C++ `InputValueChangedEvent`): for
|
||||
/// `lut_file_in` or `lut_dir_in`, regenerates the processor
|
||||
/// immediately in the main process; in the render worker (where
|
||||
|
||||
@@ -175,6 +175,16 @@ impl NodeBehavior for ShapeNode {
|
||||
}
|
||||
}
|
||||
|
||||
/// Combo input option labels (C++ `retranslate()` /
|
||||
/// `set_combo_box_strings`): `type_in` -> "Rectangle", "Ellipse",
|
||||
/// "Rounded Rectangle".
|
||||
fn input_combo_strings(&self, id: &str) -> Vec<&'static str> {
|
||||
match id {
|
||||
TYPE_INPUT => vec!["Rectangle", "Ellipse", "Rounded Rectangle"],
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluate outputs (C++ `value()`): builds a `"shape"` shader job
|
||||
/// from the input row, inserting `resolution_in` (the base
|
||||
/// texture's virtual resolution when connected, else the sequence
|
||||
|
||||
@@ -202,6 +202,16 @@ impl NodeBehavior for TextGeneratorV1 {
|
||||
}
|
||||
}
|
||||
|
||||
/// Combo input option labels (C++ `retranslate()` /
|
||||
/// `set_combo_box_strings`): `valign_in` -> "Top", "Center",
|
||||
/// "Bottom".
|
||||
fn input_combo_strings(&self, id: &str) -> Vec<&'static str> {
|
||||
match id {
|
||||
V_ALIGN_INPUT => vec!["Top", "Center", "Bottom"],
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluate outputs (C++ `value()`): if the text input is
|
||||
/// non-empty, push a texture generate job at the global video
|
||||
/// params; otherwise push nothing.
|
||||
|
||||
@@ -258,6 +258,16 @@ impl NodeBehavior for TextGeneratorV2 {
|
||||
}
|
||||
}
|
||||
|
||||
/// Combo input option labels (C++ `retranslate()` /
|
||||
/// `set_combo_box_strings`): `valign_in` -> "Top", "Center",
|
||||
/// "Bottom".
|
||||
fn input_combo_strings(&self, id: &str) -> Vec<&'static str> {
|
||||
match id {
|
||||
V_ALIGN_INPUT => vec!["Top", "Center", "Bottom"],
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluate outputs (C++ `value()`): if the text input is
|
||||
/// non-empty, push a texture generate job at the global video
|
||||
/// params forced to `PixelFormat::f32`; otherwise push nothing.
|
||||
|
||||
@@ -269,6 +269,16 @@ impl NodeBehavior for TextGeneratorV3 {
|
||||
}
|
||||
}
|
||||
|
||||
/// Combo input option labels (C++ `retranslate()` /
|
||||
/// `set_combo_box_strings`): `valign_in` -> "Top", "Middle",
|
||||
/// "Bottom".
|
||||
fn input_combo_strings(&self, id: &str) -> Vec<&'static str> {
|
||||
match id {
|
||||
VERTICAL_ALIGNMENT_INPUT => vec!["Top", "Middle", "Bottom"],
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluate outputs (C++ `value()`): if `use_args_in` is set and
|
||||
/// the args array is non-empty, expand `%N` placeholders in the
|
||||
/// text via [`Self::format_string`]; if the resulting text is
|
||||
|
||||
@@ -194,6 +194,27 @@ impl NodeBehavior for TileDistortNode {
|
||||
}
|
||||
}
|
||||
|
||||
/// Combo input option labels (C++ `retranslate()` /
|
||||
/// `set_combo_box_strings`): `anchor_in` -> "Top-Left", "Top-Center",
|
||||
/// "Top-Right", "Middle-Left", "Middle-Center", "Middle-Right",
|
||||
/// "Bottom-Left", "Bottom-Center", "Bottom-Right".
|
||||
fn input_combo_strings(&self, id: &str) -> Vec<&'static str> {
|
||||
match id {
|
||||
ANCHOR_INPUT => vec![
|
||||
"Top-Left",
|
||||
"Top-Center",
|
||||
"Top-Right",
|
||||
"Middle-Left",
|
||||
"Middle-Center",
|
||||
"Middle-Right",
|
||||
"Bottom-Left",
|
||||
"Bottom-Center",
|
||||
"Bottom-Right",
|
||||
],
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluate outputs (C++ `value()`): no texture -> push nothing;
|
||||
/// scale differs from 1.0 (an approximate-equality epsilon test:
|
||||
/// `abs(scale-1)*1e12 > min(abs(scale), 1)`) -> shader job over the
|
||||
|
||||
@@ -307,6 +307,18 @@ impl NodeBehavior for TransformDistortNode {
|
||||
}
|
||||
}
|
||||
|
||||
/// Combo input option labels (C++ `retranslate()` /
|
||||
/// `set_combo_box_strings`): `autoscale_in` -> "None", "Fit", "Fill",
|
||||
/// "Stretch"; `interpolation_in` -> "Nearest Neighbor", "Bilinear",
|
||||
/// "Mipmapped Bilinear".
|
||||
fn input_combo_strings(&self, id: &str) -> Vec<&'static str> {
|
||||
match id {
|
||||
AUTOSCALE_INPUT => vec!["None", "Fit", "Fill", "Stretch"],
|
||||
INTERPOLATION_INPUT => vec!["Nearest Neighbor", "Bilinear", "Mipmapped Bilinear"],
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluate outputs (C++ `value()`): generates the matrix from the
|
||||
/// inherited transform inputs (position/rotation/scale/anchor,
|
||||
/// folded with `parent_in`) and always pushes it as a `k_matrix`
|
||||
|
||||
@@ -92,6 +92,27 @@ impl NodeBehavior for TrigonometryNode {
|
||||
}
|
||||
}
|
||||
|
||||
/// Combo input option labels (C++ `retranslate()` /
|
||||
/// `set_combo_box_strings`): `method_in` -> "Sine", "Cosine",
|
||||
/// "Tangent", "Inverse Sine", "Inverse Cosine", "Inverse Tangent",
|
||||
/// "Hyperbolic Sine", "Hyperbolic Cosine", "Hyperbolic Tangent".
|
||||
fn input_combo_strings(&self, id: &str) -> Vec<&'static str> {
|
||||
match id {
|
||||
METHOD_INPUT => vec![
|
||||
"Sine",
|
||||
"Cosine",
|
||||
"Tangent",
|
||||
"Inverse Sine",
|
||||
"Inverse Cosine",
|
||||
"Inverse Tangent",
|
||||
"Hyperbolic Sine",
|
||||
"Hyperbolic Cosine",
|
||||
"Hyperbolic Tangent",
|
||||
],
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluate outputs (C++ `value()`): reads `x_in` as a double,
|
||||
/// applies the [`Operation`] selected by `method_in`
|
||||
/// (sin/cos/tan/asin/acos/atan/sinh/cosh/tanh), and pushes the
|
||||
|
||||
@@ -88,6 +88,29 @@ impl NodeBehavior for ValueNode {
|
||||
}
|
||||
}
|
||||
|
||||
/// Combo input option labels (C++ `retranslate()` /
|
||||
/// `set_combo_box_strings`): `type_in` -> the pretty data-type names
|
||||
/// of [`SUPPORTED_TYPES`] in order — "Float", "Integer", "Rational",
|
||||
/// "Vector 2D", "Vector 3D", "Vector 4D", "Color", "Text", "Boolean"
|
||||
/// (matching the Rust list, which omits the C++ `k_matrix`/`k_font`
|
||||
/// entries).
|
||||
fn input_combo_strings(&self, id: &str) -> Vec<&'static str> {
|
||||
match id {
|
||||
TYPE_INPUT => vec![
|
||||
"Float",
|
||||
"Integer",
|
||||
"Rational",
|
||||
"Vector 2D",
|
||||
"Vector 3D",
|
||||
"Vector 4D",
|
||||
"Color",
|
||||
"Text",
|
||||
"Boolean",
|
||||
],
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluate outputs (C++ `value()`): pushes the `value_in` value
|
||||
/// onto the table unchanged.
|
||||
fn value(
|
||||
|
||||
@@ -122,6 +122,16 @@ impl NodeBehavior for WaveDistortNode {
|
||||
}
|
||||
}
|
||||
|
||||
/// Combo input option labels (C++ `retranslate()` /
|
||||
/// `set_combo_box_strings`): `vertical_in` -> "Horizontal",
|
||||
/// "Vertical".
|
||||
fn input_combo_strings(&self, id: &str) -> Vec<&'static str> {
|
||||
match id {
|
||||
VERTICAL_INPUT => vec!["Horizontal", "Vertical"],
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluate outputs (C++ `value()`): no texture -> push nothing;
|
||||
/// intensity != 0.0 -> shader job over the whole value row rendered
|
||||
/// at the texture's own params; intensity == 0.0 -> pass-through
|
||||
|
||||
@@ -632,12 +632,14 @@ fn load_node(
|
||||
|
||||
// Instantiate the node type; timeline structural types (which are
|
||||
// not in the factory menu) are reconstructed directly, unknown
|
||||
// types fall back to an error.
|
||||
// types fall back to an error. `create_any` also covers the dynamic
|
||||
// (runtime-registered OpenFX plugin) entries — `find` alone would
|
||||
// reject every project that carries a plugin node.
|
||||
let (mut core, behavior): (NodeCore, Box<dyn crate::node::NodeBehavior>) =
|
||||
match create_timeline_type(&type_id) {
|
||||
Some(x) => x,
|
||||
None => match crate::factory::Factory::global().find(&type_id) {
|
||||
Some(meta) => (meta.create)(),
|
||||
None => match crate::factory::Factory::global().create_any(&type_id) {
|
||||
Some(x) => x,
|
||||
None => {
|
||||
// Unknown type: skip the element body.
|
||||
reader.skip_current_element();
|
||||
|
||||
Reference in New Issue
Block a user