mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-14 19:27:05 -04:00
Merge pull request #1029 from vbasky/fix/956-scale-unlink
This commit is contained in:
commit
c069a72928
2 files changed
+52
-11
No files matched your search
@@ -277,16 +277,18 @@ fn transform_row(app: &mut EffectcraftApp, ui: &mut egui::Ui, p: &egui::Painter,
|
||||
let c = value.components();
|
||||
let n = if pr.shown_dims > 0 && !(layer.is_3d() && c.len() == 3) { pr.shown_dims as usize } else { c.len() };
|
||||
let mut x = vx;
|
||||
let link_id = egui::Id::new(("props-scale-link", lid));
|
||||
let linked = is_scale && ui.data(|d| d.get_temp::<bool>(link_id)).unwrap_or(true);
|
||||
if is_scale {
|
||||
// Scale: the chain link (Constrain Proportions, on by default), sharing the
|
||||
// Timeline's per-property state so both panels agree (#956).
|
||||
let linkable = pr.match_id == "scale";
|
||||
let linked = linkable && !app.ui.timeline.unlinked.contains(&uid);
|
||||
if linkable {
|
||||
// Constrain Proportions toggle, left of the values.
|
||||
let lr = Rect::from_center_size(pos2(x - 12.0, cy), vec2(12.0, 12.0));
|
||||
let resp = ui.interact(lr, link_id.with("btn"), Sense::click());
|
||||
let resp = ui.interact(lr, egui::Id::new(("props-link", uid)), Sense::click()).on_hover_text(tr("Constrain Proportions"));
|
||||
icons::paint(p, lr, Icon::Link, if linked { t.accent } else { t.text_faint });
|
||||
app.auto.add(&format!("{base}.constrain"), lr, "Constrain Proportions");
|
||||
if resp.clicked() {
|
||||
ui.data_mut(|d| d.insert_temp(link_id, !linked));
|
||||
if resp.clicked() && !app.ui.timeline.unlinked.remove(&uid) {
|
||||
app.ui.timeline.unlinked.insert(uid);
|
||||
}
|
||||
}
|
||||
for d in 0..n.min(c.len()) {
|
||||
@@ -295,13 +297,14 @@ fn transform_row(app: &mut EffectcraftApp, ui: &mut egui::Ui, p: &egui::Painter,
|
||||
app.auto.add(&format!("{base}.value.{d}"), vr, &pr.name);
|
||||
if let Some(nv) = nv {
|
||||
let mut nc = c.clone();
|
||||
if linked && c[d].abs() > 1e-9 {
|
||||
let k = nv / c[d];
|
||||
for v in nc.iter_mut().take(n) {
|
||||
*v *= k;
|
||||
// Alt edits one value of a linked pair, as in the Timeline.
|
||||
let shown = if linked && !ui.input(|i| i.modifiers.alt && !widgets::proportional_scrub(i.modifiers)) { n.min(c.len()) } else { 1 };
|
||||
let from = if shown == 1 { d } else { 0 };
|
||||
for (e, v) in widgets::constrained(c.get(from..from + shown).unwrap_or_default(), d - from, nv).into_iter().enumerate() {
|
||||
if let Some(slot) = nc.get_mut(from + e) {
|
||||
*slot = v;
|
||||
}
|
||||
}
|
||||
nc[d] = nv;
|
||||
set(actions, json!(nc));
|
||||
}
|
||||
x = vr.max.x + 10.0;
|
||||
|
||||
@@ -466,6 +466,44 @@ fn essential_graphics_scale_keeps_constrain_proportions() {
|
||||
assert!(unlinked[0] > linked[0] && unlinked[1] == linked[1], "unlinked, X moves alone: {unlinked:?}");
|
||||
}
|
||||
|
||||
/// #956: the Properties panel's Scale chain link shares the Timeline's Constrain Proportions
|
||||
/// state: unlinking in Properties unlinks the Timeline row too, and X/Y scale together only
|
||||
/// while linked.
|
||||
#[test]
|
||||
fn properties_scale_link_shares_the_timeline_state() {
|
||||
let mut h = harness();
|
||||
let layer = h.state().session.active_comp().unwrap().layers[0].id.0;
|
||||
h.state_mut().session.execute("layer.select", json!({"layers": [layer]})).unwrap();
|
||||
h.state_mut().session.execute("prop.set", json!({"layer": layer, "path": "transform/scale", "value": [100.0, 50.0]})).unwrap();
|
||||
open(&mut h, "properties");
|
||||
let uid = {
|
||||
let comp = h.state().session.active_comp().unwrap();
|
||||
let l = comp.layers.iter().find(|l| l.id.0 == layer).unwrap();
|
||||
l.props.prop("transform/scale").unwrap().uid
|
||||
};
|
||||
let link = format!("properties.prop.{uid}.constrain");
|
||||
assert!(h.state().auto.find(&link).is_some(), "the chain link is drawn");
|
||||
let scale = |h: &Harness<'_, EffectcraftApp>| {
|
||||
let comp = h.state().session.active_comp().unwrap();
|
||||
let l = comp.layers.iter().find(|l| l.id.0 == layer).unwrap();
|
||||
l.props.prop("transform/scale").unwrap().value.components()
|
||||
};
|
||||
// Linked: dragging X scales Y with it.
|
||||
drag_x(&mut h, &format!("properties.prop.{uid}.value.0"), 40.0);
|
||||
let linked = scale(&h);
|
||||
assert!(linked[0] > 100.0, "{linked:?}");
|
||||
assert!((linked[1] / linked[0] - 0.5).abs() < 1e-6, "Y follows X in proportion: {linked:?}");
|
||||
// Unlinking in Properties writes the shared state, so the Timeline row unlinks too.
|
||||
click(&mut h, &link);
|
||||
assert!(h.state().ui.timeline.unlinked.contains(&uid), "unlinking writes the shared state");
|
||||
drag_x(&mut h, &format!("properties.prop.{uid}.value.0"), 40.0);
|
||||
let unlinked = scale(&h);
|
||||
assert!(unlinked[0] > linked[0] && unlinked[1] == linked[1], "unlinked, X moves alone: {unlinked:?}");
|
||||
// Re-linking clears it again.
|
||||
click(&mut h, &link);
|
||||
assert!(!h.state().ui.timeline.unlinked.contains(&uid), "re-linking clears the shared state");
|
||||
}
|
||||
|
||||
/// #757: Effects & Presets lists the built-in Transitions presets under * Animation Presets;
|
||||
/// double-clicking one applies it to the selected layer (effect plus keyframes).
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Block a user