diff --git a/crates/ui-egui/src/menus.rs b/crates/ui-egui/src/menus.rs index 175a1b8f..d94586bf 100644 --- a/crates/ui-egui/src/menus.rs +++ b/crates/ui-egui/src/menus.rs @@ -175,6 +175,20 @@ pub fn reveal(app: &mut EffectcraftApp, kind: &str, now: f64, add: bool) { tl.apply_reveal(&targets, kinds); } +/// Show Time Remap on the layers of a `layer.enableTimeRemap` (`layers` by id, else the +/// selected ones) that have it now. +fn reveal_time_remap(app: &mut EffectcraftApp, params: &Value) { + let asked: Vec = match params.get("layers").and_then(Value::as_array) { + Some(a) => a.iter().filter_map(Value::as_u64).collect(), + None => app.session.state.selected_layers.iter().map(|l| l.0).collect(), + }; + let Some(comp) = app.session.active_comp() else { return }; + let layers: Vec = comp.layers.iter().filter(|l| asked.contains(&l.id.0) && l.props.get("timeRemap").is_some()).map(|l| l.id.0).collect(); + if !layers.is_empty() { + app.ui.timeline.apply_reveal(&layers, vec!["timeRemap".into()]); + } +} + /// The layers a reveal shortcut acts on: the selected ones, else every layer of the active comp. fn reveal_targets(app: &EffectcraftApp) -> Vec { if app.session.state.selected_layers.is_empty() { @@ -541,6 +555,14 @@ pub fn invoke(app: &mut EffectcraftApp, ctx: &egui::Context, id: &str, params: V } return r; } + // Enabling time remapping twirls its layers open on Time Remap and its two keys. + if id == "layer.enableTimeRemap" { + let r = run_engine(app, ctx, id, params.clone()); + if r.as_ref().is_ok_and(|on| on.as_bool() == Some(true)) { + reveal_time_remap(app, ¶ms); + } + return r; + } if id == "layer.rename" && params.get("name").is_none() { crate::panels::timeline::begin_rename(app, ctx); return Ok(Value::Null); diff --git a/crates/ui-egui/src/panels/timeline.rs b/crates/ui-egui/src/panels/timeline.rs index 99d336d6..48fc72ec 100644 --- a/crates/ui-egui/src/panels/timeline.rs +++ b/crates/ui-egui/src/panels/timeline.rs @@ -3015,6 +3015,29 @@ mod tests { assert!(rows.iter().any(|r| r.trim() == ">Transform"), "{rows:?}"); } + /// Enabling time remapping twirls the layer open on Time Remap (#157); disabling it reveals + /// nothing. + #[test] + fn enabling_time_remapping_reveals_time_remap() { + let mut s = effectcraft_engine::Session::default(); + let inner = s.execute("comp.new", json!({"name": "Inner", "width": 32, "height": 32, "duration": 2})).unwrap()["comp"].as_u64().unwrap(); + s.execute("comp.new", json!({"name": "Main", "width": 64, "height": 64, "duration": 4})).unwrap(); + s.execute("layer.newSolid", json!({"name": "Other"})).unwrap(); + let nested = s.execute("layer.addItem", json!({"item": inner})).unwrap()["layer"].as_u64().unwrap(); + let mut app = EffectcraftApp::new(s); + let ctx = egui::Context::default(); + crate::menus::invoke(&mut app, &ctx, "layer.enableTimeRemap", json!({"layers": [nested]})).unwrap(); + assert_eq!(app.ui.timeline.layer_reveal.get(&nested), Some(&vec!["timeRemap".to_string()])); + let rows = labels(&app); + let at = rows.iter().position(|r| r == "Inner").unwrap(); + assert_eq!(rows[at + 1].trim(), "Time Remap", "{rows:?}"); + // Through the selection (the Layer menu), turning it off again: nothing new is revealed. + app.session.execute("layer.select", json!({"layers": [nested]})).unwrap(); + app.ui.timeline.layer_reveal.clear(); + crate::menus::invoke(&mut app, &ctx, "layer.enableTimeRemap", json!({})).unwrap(); + assert!(app.ui.timeline.layer_reveal.is_empty()); + } + #[test] fn cti_snaps_to_the_nearest_candidate() { assert_eq!(snap_time(&[0.0, 1.0, 2.0], 1.04, 0.1), 1.0);