diff --git a/crates/engine/src/cmd/comp.rs b/crates/engine/src/cmd/comp.rs index 8bc0931f8..8f44ebd3d 100644 --- a/crates/engine/src/cmd/comp.rs +++ b/crates/engine/src/cmd/comp.rs @@ -272,6 +272,20 @@ fn poster(s: &mut Session, p: &Value) -> Result { Ok(Value::Null) } +fn delete_all_markers(s: &mut Session, p: &Value) -> Result { + let cid = comp_id(s, p)?; + s.edit("Delete All Markers", None, |proj, _| { + proj.comp_mut(cid).ok_or(EngineError::NoComp)?.markers.clear(); + Ok(()) + })?; + Ok(Value::Null) +} + +fn has_comp_markers(s: &Session) -> std::result::Result<(), String> { + let c = s.active_comp().ok_or("no composition")?; + if !c.markers.is_empty() { Ok(()) } else { Err("the composition has no markers".into()) } +} + pub fn specs() -> Vec { vec![ cmd!( @@ -299,5 +313,6 @@ pub fn specs() -> Vec { cmd!("comp.workArea", "Set Work Area", [], None, "{start?, end?, set?: begin|end (at CTI)}", has_comp, work_area), cmd!("comp.setSwitch", "Composition Switch", [], None, "{switch: hideShy|motionBlur|frameBlending|draft3d, value?}", has_comp, comp_switch), cmd!("comp.addMarker", "Add Marker", [], Some("Num*"), "{time?, comment?}", has_comp, add_marker), + cmd!("comp.deleteAllMarkers", "Delete All Markers", [], None, "{comp?}", has_comp_markers, delete_all_markers), ] } diff --git a/crates/engine/src/cmd/layer_menu.rs b/crates/engine/src/cmd/layer_menu.rs index e0c4efc72..455873e0b 100644 --- a/crates/engine/src/cmd/layer_menu.rs +++ b/crates/engine/src/cmd/layer_menu.rs @@ -19,9 +19,10 @@ fn has_masks(s: &Session) -> std::result::Result<(), String> { } fn has_markers(s: &Session) -> std::result::Result<(), String> { - has_layers(s)?; let c = s.active_comp().ok_or("no composition")?; - if s.state.selected_layers.iter().filter_map(|l| c.layer(*l)).any(|l| !l.markers.is_empty()) { + if s.state.selected_layers.is_empty() { + if !c.markers.is_empty() { Ok(()) } else { Err("the composition has no markers".into()) } + } else if s.state.selected_layers.iter().filter_map(|l| c.layer(*l)).any(|l| !l.markers.is_empty()) { Ok(()) } else { Err("the selected layers have no markers".into()) @@ -596,6 +597,13 @@ fn markers_lock(s: &mut Session, p: &Value) -> Result { fn delete_all_markers(s: &mut Session, p: &Value) -> Result { let (cid, ids) = layers_p(s, p)?; + if ids.is_empty() { + s.edit("Delete All Markers", None, |proj, _| { + proj.comp_mut(cid).ok_or(EngineError::NoComp)?.markers.clear(); + Ok(()) + })?; + return Ok(Value::Null); + } s.edit("Delete All Markers", None, |proj, _| { for l in proj.comp_mut(cid).ok_or(EngineError::NoComp)?.layers.iter_mut_where(|l| ids.contains(&l.id) && !l.markers_locked) { l.markers.clear(); diff --git a/crates/engine/src/tests/markers.rs b/crates/engine/src/tests/markers.rs index 66aac5694..87792390f 100644 --- a/crates/engine/src/tests/markers.rs +++ b/crates/engine/src/tests/markers.rs @@ -194,3 +194,30 @@ fn layer_add_marker_without_selection_adds_comp_marker() { assert!((comp.markers[0].time.seconds() - 1.5).abs() < 1e-9); assert_eq!(comp.markers[0].comment, "timeline marker"); } + +#[test] +fn comp_delete_all_markers_with_undo() { + let (mut s, _) = setup(); + s.execute("comp.addMarker", json!({"time": 1.0, "comment": "m1"})).unwrap(); + s.execute("comp.addMarker", json!({"time": 2.0, "comment": "m2"})).unwrap(); + assert_eq!(s.active_comp().unwrap().markers.len(), 2); + s.execute("comp.deleteAllMarkers", json!({})).unwrap(); + assert!(s.active_comp().unwrap().markers.is_empty()); + s.execute("edit.undo", json!({})).unwrap(); + assert_eq!(s.active_comp().unwrap().markers.len(), 2); + s.execute("edit.redo", json!({})).unwrap(); + assert!(s.active_comp().unwrap().markers.is_empty()); +} + +#[test] +fn layer_delete_all_markers_without_selection_clears_comp_markers() { + let (mut s, _) = setup(); + s.execute("comp.addMarker", json!({"time": 1.0, "comment": "m1"})).unwrap(); + s.execute("comp.addMarker", json!({"time": 2.0, "comment": "m2"})).unwrap(); + s.execute("edit.deselectAll", json!({})).unwrap(); + assert!(s.state.selected_layers.is_empty()); + s.execute("layer.deleteAllMarkers", json!({})).unwrap(); + assert!(s.active_comp().unwrap().markers.is_empty()); + s.execute("edit.undo", json!({})).unwrap(); + assert_eq!(s.active_comp().unwrap().markers.len(), 2); +} diff --git a/crates/ui-egui/src/panels/markers_ui.rs b/crates/ui-egui/src/panels/markers_ui.rs index 2c21a293b..54f06d6b6 100644 --- a/crates/ui-egui/src/panels/markers_ui.rs +++ b/crates/ui-egui/src/panels/markers_ui.rs @@ -373,6 +373,8 @@ fn marker(app: &mut EffectcraftApp, ui: &mut egui::Ui, p: &egui::Painter, comp: run(ui, "Delete This Marker", "markers.delete", r.params(), app); if let Some(l) = r.layer { run(ui, "Delete All Markers", "layer.deleteAllMarkers", json!({"layers": [l]}), app); + } else { + run(ui, "Delete All Markers", "comp.deleteAllMarkers", json!({}), app); } }); }