diff --git a/crates/engine/src/commands/nav_more.rs b/crates/engine/src/commands/nav_more.rs index 3617f9a0..ce7fc57e 100644 --- a/crates/engine/src/commands/nav_more.rs +++ b/crates/engine/src/commands/nav_more.rs @@ -6,7 +6,7 @@ use effectcraft_project::{ItemId, LayerSource, Project}; use effectcraft_time::Tick; use serde_json::{Value, json}; -use super::{CommandSpec, comp_id, has_comp, layers_p}; +use super::{CommandSpec, always, comp_id, has_comp, layers_p}; use crate::{EngineError, Result, Session, cmd}; /// Ctrl+Alt+B: the work area spans the selected layers (their earliest In to their latest Out @@ -30,6 +30,14 @@ fn work_area_to_selection(s: &mut Session, p: &Value) -> Result { Ok(json!({"start": c.work_area.0.seconds(), "end": c.work_area.1.seconds()})) } +/// Shift+F2: no keyframe, property or property group is selected; the layers stay selected. +fn deselect_keys(s: &mut Session, _: &Value) -> Result { + s.state.selected_keys.clear(); + s.state.selected_props.clear(); + s.state.selected_vertices.clear(); + Ok(Value::Null) +} + /// The comps connected to `from` by nesting, in either direction (its network of nested /// compositions), `from` included. fn hierarchy(proj: &Project, from: ItemId) -> BTreeSet { @@ -84,5 +92,6 @@ pub fn specs() -> Vec { work_area_to_selection ), cmd!("comp.activateRecent", "Activate Most Recent Composition in Hierarchy", [], Some("Shift+Escape"), "{comp?}", has_comp, activate_recent), + cmd!("keys.deselectAll", "Deselect All Keyframes and Properties", [], ["Shift+F2", "Cmd+Alt+Shift+A"], "{}", always, deselect_keys), ] } diff --git a/crates/engine/src/tests_shortcut_cmds.rs b/crates/engine/src/tests_shortcut_cmds.rs index 30d2daa2..64bcb744 100644 --- a/crates/engine/src/tests_shortcut_cmds.rs +++ b/crates/engine/src/tests_shortcut_cmds.rs @@ -95,3 +95,15 @@ fn the_most_recent_comp_of_the_hierarchy_comes_back() { s.execute("comp.activateRecent", json!({})).unwrap(); assert_eq!(s.state.active_comp.map(|c| c.0), Some(main)); } + +#[test] +fn keyframes_and_properties_deselect_but_layers_stay() { + let (mut s, ids) = session([(0.0, 10.0); 3]); + s.execute("layer.select", json!({"layers": [ids[0]]})).unwrap(); + s.execute("prop.addKey", json!({"layer": ids[0], "path": "transform/opacity", "time": 1.0, "value": 50.0})).unwrap(); + s.execute("keys.selectAll", json!({})).unwrap(); + assert!(!s.state.selected_keys.is_empty()); + s.execute("keys.deselectAll", json!({})).unwrap(); + assert!(s.state.selected_keys.is_empty() && s.state.selected_props.is_empty()); + assert_eq!(s.state.selected_layers.len(), 1); +} diff --git a/crates/ui-egui/src/menus.rs b/crates/ui-egui/src/menus.rs index 041a7779..cb2cb0b6 100644 --- a/crates/ui-egui/src/menus.rs +++ b/crates/ui-egui/src/menus.rs @@ -2129,6 +2129,8 @@ mod tests { ("time.nextInOut", "Cmd+Alt+Shift+ArrowRight"), ("comp.workAreaToSelection", "Cmd+Alt+B"), ("comp.activateRecent", "Shift+Escape"), + ("keys.deselectAll", "Shift+F2"), + ("keys.deselectAll", "Cmd+Alt+Shift+A"), ] { assert!(bindings(&app.session).into_iter().any(|x| x.2 == id && Some((x.0, x.1)) == parse_shortcut(keys)), "{id} on {keys}"); }