mirror of
https://github.com/storytold/effectcraft.git
synced 2026-11-10 02:11:41 -06:00
Graph Editor: protect selection tools and restore queue-close focus
This commit is contained in:
@@ -55,7 +55,7 @@ fn compatible(a: &Property, b: &effectcraft_keyframe::Value) -> bool {
|
||||
a.value.kind_name() == b.kind_name() && a.value.components().len() == b.components().len()
|
||||
}
|
||||
|
||||
fn locked_selection(comp: &effectcraft_project::Comp, selected: &[KeyRef]) -> Vec<KeyRef> {
|
||||
pub(crate) fn locked_selection(comp: &effectcraft_project::Comp, selected: &[KeyRef]) -> Vec<KeyRef> {
|
||||
selected.iter().filter(|k| comp.layer(k.layer).is_some_and(|l| l.switches.locked)).copied().collect()
|
||||
}
|
||||
|
||||
|
||||
@@ -213,7 +213,8 @@ fn sample_curve(
|
||||
|
||||
fn grouped_selection(s: &Session) -> std::collections::BTreeMap<(LayerId, Uid), Vec<Tick>> {
|
||||
let mut g: std::collections::BTreeMap<(LayerId, Uid), Vec<Tick>> = Default::default();
|
||||
for k in &s.state.selected_keys {
|
||||
let Some(comp) = s.active_comp() else { return g };
|
||||
for k in super::prop::editable_keys(comp, &s.state.selected_keys) {
|
||||
g.entry((k.layer, k.prop)).or_default().push(k.time);
|
||||
}
|
||||
for v in g.values_mut() {
|
||||
@@ -273,7 +274,7 @@ fn run_on_selection(
|
||||
s.edit(label, None, |proj, st| {
|
||||
let comp = proj.comp_mut(cid).ok_or(EngineError::NoComp)?;
|
||||
let fr = comp.frame_rate;
|
||||
let mut sel = vec![];
|
||||
let mut sel = super::anim::locked_selection(comp, &st.selected_keys);
|
||||
for ((lid, uid), times) in groups {
|
||||
let Some(l) = comp.layer_mut(lid) else { continue };
|
||||
let lc = l.clone();
|
||||
|
||||
@@ -209,7 +209,8 @@ fn add_keyframe(s: &mut Session, _: &Value) -> Result<Value> {
|
||||
/// Selected keys grouped by (layer, property).
|
||||
fn keys_by_prop(s: &Session) -> BTreeMap<(LayerId, Uid), Vec<Tick>> {
|
||||
let mut m: BTreeMap<(LayerId, Uid), Vec<Tick>> = BTreeMap::new();
|
||||
for k in &s.state.selected_keys {
|
||||
let Some(comp) = s.active_comp() else { return m };
|
||||
for k in super::prop::editable_keys(comp, &s.state.selected_keys) {
|
||||
m.entry((k.layer, k.prop)).or_default().push(k.time);
|
||||
}
|
||||
m
|
||||
|
||||
@@ -37,6 +37,8 @@ pub(crate) fn label_keys(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
return Err(bad("keys.label", "select keyframes first"));
|
||||
}
|
||||
let cid = comp_id(s, p)?;
|
||||
let comp = s.project.comp(cid).ok_or(EngineError::NoComp)?;
|
||||
let keys = super::prop::editable_keys(comp, &keys);
|
||||
let n = s.edit("Keyframe Label", None, |proj, _| {
|
||||
let mut n = 0;
|
||||
for k in &keys {
|
||||
|
||||
@@ -437,20 +437,22 @@ fn interpolate(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
// The layer: given, else the layer of selected Mask Path keys, else the selection.
|
||||
let cid = super::comp_id(s, p)?;
|
||||
let comp = s.project.comp(cid).ok_or(EngineError::NoComp)?;
|
||||
let selected = super::prop::editable_keys(comp, &s.state.selected_keys);
|
||||
let lid = match p.get("layer") {
|
||||
Some(v) => super::resolve_layer(comp, v).ok_or_else(|| bad(CMD, format!("no layer {v}")))?,
|
||||
None => match s.state.selected_keys.iter().find(|k| comp.layer(k.layer).is_some_and(|l| l.masks().is_some_and(|m| m.find(k.prop).is_some()))) {
|
||||
None => match selected.iter().find(|k| comp.layer(k.layer).is_some_and(|l| l.masks().is_some_and(|m| m.find(k.prop).is_some()))) {
|
||||
Some(k) => k.layer,
|
||||
None => layer_p(s, p, CMD)?.1,
|
||||
},
|
||||
};
|
||||
let layer = comp.layer(lid).ok_or(EngineError::NoComp)?;
|
||||
if !super::prop::key_layer_editable(comp, lid) {
|
||||
return Err(bad(CMD, "the layer is locked"));
|
||||
}
|
||||
let masks = layer.masks().ok_or_else(|| bad(CMD, "the layer has no masks"))?;
|
||||
let uid = match p.get("mask") {
|
||||
Some(_) => mask_uid(s, layer, p),
|
||||
None => s
|
||||
.state
|
||||
.selected_keys
|
||||
None => selected
|
||||
.iter()
|
||||
.filter(|k| k.layer == lid)
|
||||
.find_map(|k| masks.groups().find(|g| g.get("path").is_some_and(|pr| pr.uid == k.prop)).map(|g| g.uid))
|
||||
@@ -465,7 +467,7 @@ fn interpolate(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let ts: Vec<Tick> = a.iter().filter_map(Value::as_f64).map(|t| layer.layer_time(comp.frame_rate.snap_nearest(Tick::from_seconds_f64(t)))).collect();
|
||||
ts.into_iter().filter_map(|t| path_prop.keys.iter().find(|k| (k.time.0 - t.0).abs() <= comp.frame_duration().0 / 2).map(|k| k.time)).collect()
|
||||
}
|
||||
None => s.state.selected_keys.iter().filter(|k| k.layer == lid && k.prop == path_prop.uid).map(|k| k.time).collect(),
|
||||
None => selected.iter().filter(|k| k.layer == lid && k.prop == path_prop.uid).map(|k| k.time).collect(),
|
||||
};
|
||||
times.sort();
|
||||
times.dedup();
|
||||
|
||||
@@ -883,6 +883,15 @@ fn select_keys(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
/// References that can be edited, including explicit key lists supplied by commands.
|
||||
pub(crate) fn editable_keys(comp: &effectcraft_project::Comp, selected: &[KeyRef]) -> Vec<KeyRef> {
|
||||
selected.iter().filter(|key| key_layer_editable(comp, key.layer)).copied().collect()
|
||||
}
|
||||
|
||||
pub(crate) fn key_layer_editable(comp: &effectcraft_project::Comp, layer: LayerId) -> bool {
|
||||
comp.layer(layer).is_some_and(|layer| !layer.switches.locked)
|
||||
}
|
||||
|
||||
/// Apply `f` to every selected key; keeps the selection pointing at moved keys.
|
||||
pub(crate) fn edit_keys(s: &mut Session, label: &str, merge: Option<&str>, f: impl Fn(&mut Vec<Keyframe>, usize, &mut Tick) -> bool) -> Result<Value> {
|
||||
let cid = s.active_comp_id().ok_or(EngineError::NoComp)?;
|
||||
|
||||
Reference in New Issue
Block a user