UI: add an Effects submenu to the layer context menu
The layer context menu gains an Effects submenu between Blending Mode and Arrange, mirroring the After Effects layer menu: the effect registry grouped by category (sorted), synced with the Effect menu; picking one applies it to the selected layers.
This commit is contained in:
parent
470ea7a3af
commit
4101cef8b4
|
|
@ -3579,6 +3579,7 @@ pub(crate) fn layer_menu(ui: &mut egui::Ui, layers: &[u64], vector: bool, action
|
|||
}
|
||||
})
|
||||
});
|
||||
ui.menu_button(crate::i18n::tr("Effects"), |ui| effects_submenu(ui, layers, &mut item));
|
||||
ui.menu_button(crate::i18n::tr("Arrange"), |ui| {
|
||||
item(ui, "Bring Layer to Front", "layer.arrange", json!({"layers": layers, "to": "front"}));
|
||||
item(ui, "Bring Layer Forward", "layer.arrange", json!({"layers": layers, "to": "forward"}));
|
||||
|
|
@ -3592,6 +3593,31 @@ pub(crate) fn layer_menu(ui: &mut egui::Ui, layers: &[u64], vector: bool, action
|
|||
item(ui, "Delete", "edit.clear", json!({"layers": layers}));
|
||||
}
|
||||
|
||||
/// The layer context menu's Effects submenu: every effect from the registry grouped by category,
|
||||
/// in the same order as the Effect menu's palette, so the two stay in sync automatically.
|
||||
fn effects_submenu(ui: &mut egui::Ui, layers: &[u64], item: &mut dyn FnMut(&mut egui::Ui, &str, &str, serde_json::Value)) {
|
||||
let mut cats: Vec<(&str, Vec<&'static effectcraft_engine::effects::EffectSpec>)> = Vec::new();
|
||||
for e in effectcraft_engine::effects::all() {
|
||||
match cats.iter_mut().find(|(c, _)| *c == e.category) {
|
||||
Some((_, v)) => v.push(e),
|
||||
None => cats.push((e.category, vec![e])),
|
||||
}
|
||||
}
|
||||
cats.sort_by_key(|(c, _)| c.to_ascii_lowercase());
|
||||
widgets::menu_scroll(ui, |ui| {
|
||||
for (cat, mut fx) in cats {
|
||||
fx.sort_by_key(|e| e.name.to_ascii_lowercase());
|
||||
ui.menu_button(cat, |ui| {
|
||||
widgets::menu_scroll(ui, |ui| {
|
||||
for e in fx {
|
||||
item(ui, e.name, "effect.apply", json!({"effect": e.id, "layers": layers}));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn fmt_num(v: f64, d: usize) -> String {
|
||||
format!("{v:.d$}")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -226,3 +226,36 @@ fn context_menu_rows_leave_no_gap_between_them() {
|
|||
}
|
||||
snapshot(&mut h, "project-rows");
|
||||
}
|
||||
|
||||
/// The layer context menu offers the whole effect palette as an "Effects" submenu (synced with
|
||||
/// the Effect menu), between Blending Mode and Arrange; picking an effect applies it.
|
||||
#[test]
|
||||
fn timeline_layer_context_menu_has_the_effects_submenu_and_applies_an_effect() {
|
||||
let mut h = harness();
|
||||
let layer = h.state_mut().session.execute("layer.newNull", json!({"name": "Null"})).unwrap()["layer"].as_u64().unwrap();
|
||||
h.run_steps(3);
|
||||
let r = rect(&h, &format!("timeline.layer.{layer}.row"));
|
||||
right_click(&mut h, pos2(r.min.x + 210.0, r.center().y));
|
||||
// The palette sits between Blending Mode and Arrange, as After Effects' layer menu does.
|
||||
let blending = h.get_by_label("Blending Mode ⏵").rect();
|
||||
let effects = h.get_by_label("Effects ⏵").rect();
|
||||
let arrange = h.get_by_label("Arrange ⏵").rect();
|
||||
assert!(blending.bottom() <= effects.top() && effects.bottom() <= arrange.top(), "Effects between Blending Mode and Arrange");
|
||||
h.get_by_label("Effects ⏵").click();
|
||||
h.run_steps(3);
|
||||
h.get_by_label("Blur & Sharpen ⏵").click();
|
||||
h.run_steps(3);
|
||||
h.get_by_label("Gaussian Blur").click();
|
||||
h.run_steps(3);
|
||||
let names: Vec<String> = h
|
||||
.state()
|
||||
.session
|
||||
.active_comp()
|
||||
.unwrap()
|
||||
.layer(effectcraft_engine::project::LayerId(layer))
|
||||
.unwrap()
|
||||
.effects()
|
||||
.map(|f| f.groups().map(|g| g.name.clone()).collect())
|
||||
.unwrap_or_default();
|
||||
assert_eq!(names, ["Gaussian Blur"]);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue