Layer: reveal layer sources in the Project panel (#1809)

This commit is contained in:
Camille Moine
2026-10-20 13:59:01 +02:00
parent ef74d82902
commit c7eb48dc3a
8 changed files with 168 additions and 13 deletions
+23 -7
View File
@@ -754,17 +754,33 @@ fn reveal_in_finder(s: &mut Session, p: &Value) -> Result<Value> {
}
fn reveal_source(s: &mut Session, p: &Value) -> Result<Value> {
let item = source_item(s, p, "layer.revealSource")?;
s.state.project_selection = vec![item];
frontend(s, "window.panel", &json!({"panel": "project"}))?;
Ok(json!({"item": item.0}))
let (cid, ids) = layers_p(s, p)?;
let mut items: Vec<ItemId> = Vec::new();
{
let comp = s.project.comp(cid).ok_or(EngineError::NoComp)?;
for layer in ids.iter().filter_map(|id| comp.layer(*id)) {
if let Some(item) = layer.source.item()
&& s.project.item(item).is_some()
&& !items.contains(&item)
{
items.push(item);
}
}
}
if items.is_empty() {
return Err(bad("layer.revealSource", "the selected layers have no source item"));
}
s.state.project_selection = items.clone();
let raw: Vec<u64> = items.iter().map(|item| item.0).collect();
frontend(s, "project.reveal", &json!({"items": raw}))?;
Ok(json!({"item": items[0].0, "items": raw}))
}
fn reveal_comp(s: &mut Session, p: &Value) -> Result<Value> {
let cid = comp_id(s, p)?;
s.state.project_selection = vec![cid];
frontend(s, "window.panel", &json!({"panel": "project"}))?;
Ok(json!({"item": cid.0}))
frontend(s, "project.reveal", &json!({"items": [cid.0]}))?;
Ok(json!({"item": cid.0, "items": [cid.0]}))
}
impl Session {
@@ -909,7 +925,7 @@ pub fn specs() -> Vec<CommandSpec> {
cmd!("layer.trackMatte", "Track Matte", [], None, "{layers?, op: none|alpha|alphaInverted|luma|lumaInverted|above|below}", has_layers, track_matte_op),
cmd!("layer.openSource", "Open Layer Source", ["Layer"], None, "{layer?}", has_source_item, open_source),
cmd!("layer.revealInFinder", "Reveal in Finder", ["Layer"], None, "{layer?}", has_footage_layer, reveal_in_finder),
cmd!("layer.revealSource", "Reveal Layer Source in Project", ["Layer", "Reveal"], None, "{layer?}", has_source_item, reveal_source),
cmd!("layer.revealSource", "Reveal Layer Source in Project", ["Layer", "Reveal"], None, "{layers?}", has_source_item, reveal_source),
cmd!("comp.revealInProject", "Reveal Composition in Project", ["Layer", "Reveal"], None, "{comp?}", has_comp, reveal_comp),
cmd!("layer.revealExpressionErrors", "Reveal Expression Errors", ["Layer", "Reveal"], None, "{comp?}", has_comp, reveal_expression_errors),
cmd!(
+31
View File
@@ -725,6 +725,37 @@ fn reveal_properties() {
assert!(frontend_events(&mut s).iter().all(|(c, _)| c == "timeline.revealProps"));
}
#[test]
fn reveal_layer_source_selects_each_layers_source_item() {
let mut s = comp();
let a = solid(&mut s, "#ff0000");
let b = solid(&mut s, "#00ff00");
let ia = layer(&s, a).source.item().unwrap();
let ib = layer(&s, b).source.item().unwrap();
assert_ne!(ia, ib);
s.execute("layer.select", json!({"layers": [a, b]})).unwrap();
let r = s.execute("layer.revealSource", json!({})).unwrap();
assert_eq!(r["item"], ia.0);
assert_eq!(r["items"], json!([ia.0, ib.0]));
assert_eq!(s.state.project_selection, vec![ia, ib]);
assert_eq!(frontend_events(&mut s), vec![("project.reveal".to_string(), json!({"items": [ia.0, ib.0]}))]);
// A null has no source. Alone it is refused; in a mixed selection only the sources are revealed.
let n = s.execute("layer.newNull", json!({})).unwrap()["layer"].as_u64().unwrap();
s.execute("layer.select", json!({"layers": [n]})).unwrap();
assert!(s.execute("layer.revealSource", json!({})).is_err());
s.execute("layer.select", json!({"layers": [n, a]})).unwrap();
let r = s.execute("layer.revealSource", json!({})).unwrap();
assert_eq!(r["items"], json!([ia.0]));
assert_eq!(s.state.project_selection, vec![ia]);
// Reveal Composition in Project opens the same panel and folders.
let cid = s.active_comp_id().unwrap();
s.drain_events();
let r = s.execute("comp.revealInProject", json!({"comp": cid.0})).unwrap();
assert_eq!(r["item"], cid.0);
assert_eq!(s.state.project_selection, vec![cid]);
assert_eq!(frontend_events(&mut s), vec![("project.reveal".to_string(), json!({"items": [cid.0]}))]);
}
#[test]
fn sequence_layers() {
let mut s = comp();