feat(app): OpenFX UI wiring - effect library, inspector params, startup glue
- src/oakui/ofx.rs: startup sequence (host scan, register_plugin_nodes, progress reporter factory -> app progress dialog channel, active viewer time provider, project extent sync); all failures degrade to logs. oak-worker runtime also scans and registers plugins. - Effect library groups OpenFX entries by sub-category (Filter/ Generator/Transition/General); effect insertion goes through Factory::create_any so dynamic plugin nodes resolve. - Inspector renders OFX parameters from node inputs (sliders, combo boxes from repeated combo_option/combo_value properties, vec/color spinboxes, text with explicit commit, push buttons), edits are undoable; persistent plugin messages surface as a card badge. - oakplugin: push_button_clicked and per-instance persistent message counting (thin public layers).
This commit is contained in:
@@ -65,9 +65,27 @@ impl<E: AppEngine> Render for EffectLibraryPanel<E> {
|
||||
.gap_1()
|
||||
.p_2()
|
||||
.overflow_y_scroll();
|
||||
for (type_id, name) in effects {
|
||||
|
||||
// Built-in effects render flat; OpenFX plugin entries are grouped
|
||||
// under their sub-category header (Filter / Generator / Transition /
|
||||
// General — the C++ `factorymenu` OpenFX branch).
|
||||
let mut last_group: Option<String> = None;
|
||||
for entry in &effects {
|
||||
match &entry.group {
|
||||
Some(group) => {
|
||||
if last_group.as_deref() != Some(group.as_str()) {
|
||||
last_group = Some(group.clone());
|
||||
list = list.child(group_header(&colors, group));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
last_group = None;
|
||||
}
|
||||
}
|
||||
let engine = self.engine.clone();
|
||||
let row_id = type_id.clone();
|
||||
let row_id = entry.type_id.clone();
|
||||
let name = entry.name.clone();
|
||||
let type_id = entry.type_id.clone();
|
||||
list = list.child(
|
||||
div()
|
||||
.id(SharedString::from(format!("effect-library-{type_id}")))
|
||||
@@ -123,6 +141,20 @@ impl<E: AppEngine> Render for EffectLibraryPanel<E> {
|
||||
}
|
||||
}
|
||||
|
||||
/// The sub-category header row of the OpenFX group (a muted, all-caps
|
||||
/// line above the plugin entries).
|
||||
fn group_header(colors: &gpui::colors::Colors, group: &str) -> impl IntoElement {
|
||||
div()
|
||||
.id(SharedString::from(format!("effect-library-group-{group}")))
|
||||
.pt_2()
|
||||
.pb_1()
|
||||
.px_2()
|
||||
.text_xs()
|
||||
.font_weight(gpui::FontWeight(600.0))
|
||||
.text_color(colors.disabled)
|
||||
.child(group.to_string())
|
||||
}
|
||||
|
||||
impl<E: AppEngine> EventEmitter<PanelEvent> for EffectLibraryPanel<E> {}
|
||||
|
||||
impl<E: AppEngine> DockPanel for EffectLibraryPanel<E> {
|
||||
@@ -161,7 +193,8 @@ mod tests {
|
||||
|
||||
let expected = crate::oakui::effectchain::addable_effects();
|
||||
assert!(!expected.is_empty());
|
||||
for (type_id, _) in &expected {
|
||||
for entry in &expected {
|
||||
let type_id = &entry.type_id;
|
||||
// `debug_bounds` takes a &'static selector; the per-row selector is
|
||||
// dynamic, so the test leaks it (process-lifetime, test-only).
|
||||
let selector: &'static str =
|
||||
|
||||
Reference in New Issue
Block a user