mirror of
https://github.com/storytold/effectcraft.git
synced 2026-11-11 13:04:59 +00:00
Settings: fit tools move the target comp's playhead, not the active one
show_span now checks and moves the playhead of the comp the layer was made in, through a new Session::set_time_of that set_time uses for the active comp, so time sync and the text editor boundary stay the same.
This commit is contained in:
@@ -31,12 +31,12 @@ pub(crate) fn fit_span(l: &mut Layer, span: Option<(Tick, Tick)>) {
|
||||
}
|
||||
|
||||
/// Tools draw on the layer they create: when it was fitted to a span the playhead is outside,
|
||||
/// move the playhead to the span's start so the new layer shows. The layer's timing is kept.
|
||||
pub(crate) fn show_span(s: &mut Session, span: Option<(Tick, Tick)>) {
|
||||
/// move `comp`'s playhead to the span's start so the new layer shows. The layer's timing is kept.
|
||||
pub(crate) fn show_span(s: &mut Session, comp: ItemId, span: Option<(Tick, Tick)>) {
|
||||
if let Some((start, end)) = span
|
||||
&& !(start..end).contains(&s.time())
|
||||
&& !(start..end).contains(&s.time_of(comp))
|
||||
{
|
||||
s.set_time(start);
|
||||
s.set_time_of(comp, start);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,7 +244,7 @@ pub(super) fn new_text(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
Ok(id)
|
||||
})?;
|
||||
if edit {
|
||||
show_span(s, span);
|
||||
show_span(s, cid, span);
|
||||
}
|
||||
Ok(json!({"layer": id.0}))
|
||||
}
|
||||
|
||||
@@ -364,7 +364,7 @@ fn new_shape(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
Ok((lid, group))
|
||||
})?;
|
||||
if target.is_none() {
|
||||
super::layer::show_span(s, span);
|
||||
super::layer::show_span(s, cid, span);
|
||||
}
|
||||
Ok(json!({"layer": lid.0, "group": group}))
|
||||
}
|
||||
|
||||
@@ -302,7 +302,7 @@ fn new_path(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
Ok((lid, group, path_uid))
|
||||
})?;
|
||||
if target.is_none() {
|
||||
super::layer::show_span(s, span);
|
||||
super::layer::show_span(s, cid, span);
|
||||
}
|
||||
Ok(json!({"layer": lid.0, "group": group, "path": path_uid}))
|
||||
}
|
||||
|
||||
@@ -114,18 +114,23 @@ impl Session {
|
||||
/// editor is open, so playback with no text editor does no text copying.
|
||||
pub fn set_time(&mut self, t: Tick) {
|
||||
if let Some(c) = self.state.active_comp {
|
||||
let t = self.project.comp(c).map_or(t, |comp| comp_frame_at(comp, t));
|
||||
let edited = commands::text_edit::active_editor_snapshot(self);
|
||||
self.state.times.insert(c, t);
|
||||
if self.prefs.general.sync_time_related_items {
|
||||
// Direct call: this `set_time` owns the editor snapshot for both time changes,
|
||||
// so a synced move does not take a second snapshot or start a second epoch.
|
||||
self.sync_related_times_inner(c, t);
|
||||
}
|
||||
commands::text_edit::after_external_change(self, edited.as_ref());
|
||||
self.set_time_of(c, t);
|
||||
}
|
||||
}
|
||||
|
||||
/// Move `comp`'s current time, as [`Session::set_time`] does for the active comp.
|
||||
pub fn set_time_of(&mut self, c: ItemId, t: Tick) {
|
||||
let t = self.project.comp(c).map_or(t, |comp| comp_frame_at(comp, t));
|
||||
let edited = commands::text_edit::active_editor_snapshot(self);
|
||||
self.state.times.insert(c, t);
|
||||
if self.prefs.general.sync_time_related_items {
|
||||
// Direct call: this `set_time_of` owns the editor snapshot for both time changes,
|
||||
// so a synced move does not take a second snapshot or start a second epoch.
|
||||
self.sync_related_times_inner(c, t);
|
||||
}
|
||||
commands::text_edit::after_external_change(self, edited.as_ref());
|
||||
}
|
||||
|
||||
/// Settings ▸ General ▸ Synchronize Time of All Related Items: moving the current time of
|
||||
/// `comp` moves the current time of the comps nested in it (through their precomp layers'
|
||||
/// timing) and of the comps it is nested in, so every related viewer shows the same moment.
|
||||
|
||||
@@ -424,6 +424,31 @@ fn new_layers_fit_the_work_area_when_asked() {
|
||||
assert_eq!(span(&s), (7.0, 7.0, 10.0), "footage and comps keep their own timing");
|
||||
}
|
||||
|
||||
/// A tool command aimed at another comp moves that comp's playhead into its work area and
|
||||
/// leaves the active comp's playhead where it was.
|
||||
#[test]
|
||||
fn fitted_layers_in_another_comp_move_that_comps_playhead() {
|
||||
let mut s = session_with_comp();
|
||||
let active = s.active_comp_id().unwrap();
|
||||
s.execute("prefs.set", json!({"key": "general.fitNewLayersToWorkArea", "value": true})).unwrap();
|
||||
let other = effectcraft_project::ItemId(s.execute("comp.new", json!({"name": "Other", "duration": 10, "open": false})).unwrap()["comp"].as_u64().unwrap());
|
||||
s.execute("comp.workArea", json!({"comp": other.0, "start": 2, "end": 5})).unwrap();
|
||||
s.execute("comp.workArea", json!({"start": 8, "end": 9})).unwrap();
|
||||
s.set_time(effectcraft_time::Tick::from_seconds_f64(7.0));
|
||||
for (cmd, p) in [
|
||||
("layer.newText", json!({"comp": other.0, "text": "a", "edit": true})),
|
||||
("shape.newShape", json!({"comp": other.0, "kind": "ellipse"})),
|
||||
("shape.newPath", json!({"comp": other.0, "vertices": [[0, 0], [10, 0], [10, 10]]})),
|
||||
] {
|
||||
s.set_time_of(other, effectcraft_time::Tick::from_seconds_f64(7.0));
|
||||
s.state.selected_layers.clear();
|
||||
s.state.text_edit = None;
|
||||
s.execute(cmd, p).unwrap();
|
||||
assert_eq!(s.time_of(other).seconds().round(), 2.0, "{cmd}: the target comp's playhead");
|
||||
assert_eq!(s.time_of(active).seconds().round(), 7.0, "{cmd}: the active comp's playhead");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_spatial_interpolation_linear() {
|
||||
let mut s = session_with_comp();
|
||||
|
||||
Reference in New Issue
Block a user