From e7a396856def9b69ab89d813d440f3e8eb82240e Mon Sep 17 00:00:00 2001 From: arnav-hiwarkar Date: Thu, 12 Nov 2026 13:54:03 +0530 Subject: [PATCH] #3087: a placeholder's float slider keeps its range It arrived as a plain number, so Effect Controls neither dragged it over its slider range nor clamped an edit to its valid range, as After Effects does. --- crates/aep/src/import/placeholder.rs | 13 ++++++++++++- crates/aep/tests/it/import/placeholders.rs | 18 ++++++++++++++++++ docs/aep-import.md | 2 +- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/crates/aep/src/import/placeholder.rs b/crates/aep/src/import/placeholder.rs index 4186c9e28..2fc40b71b 100644 --- a/crates/aep/src/import/placeholder.rs +++ b/crates/aep/src/import/placeholder.rs @@ -111,7 +111,7 @@ pub(crate) fn placeholder( /// parameter), its control and its value until one is read. `None` for a type that is not kept. fn shape(p: &EffectParam<'_, '_>, size: (u32, u32)) -> Option<(Option, ParamUi, Value)> { let (kind, ui, default) = match p.kind { - ParamKind::IntSlider | ParamKind::FixedSlider | ParamKind::FloatSlider => (Kind::EffectNumber(1.0), ParamUi::Number, Value::Scalar(0.0)), + ParamKind::IntSlider | ParamKind::FixedSlider | ParamKind::FloatSlider => (Kind::EffectNumber(1.0), slider_ui(p), Value::Scalar(0.0)), ParamKind::Angle => (Kind::EffectNumber(1.0), ParamUi::Angle, Value::Scalar(0.0)), ParamKind::Checkbox => (Kind::EffectBool, ParamUi::Checkbox, Value::Bool(false)), ParamKind::Color => (Kind::EffectColor, ParamUi::Color, Value::Color([0.0, 0.0, 0.0, 1.0])), @@ -131,6 +131,17 @@ fn shape(p: &EffectParam<'_, '_>, size: (u32, u32)) -> Option<(Option, Par Some((Some(kind), ui, default)) } +/// A slider's control: a float slider's valid range and drag range when they are ranges (only a +/// float slider's are decoded), else a plain number. +fn slider_ui(p: &EffectParam<'_, '_>) -> ParamUi { + match (p.valid_min(), p.valid_max(), p.slider_range()) { + (Some(min), Some(max), Some((smin, smax))) if [min, max, smin, smax].iter().all(|v| v.is_finite()) && min <= max && smin <= smax => { + ParamUi::Slider { min, max, slider_min: smin.clamp(min, max), slider_max: smax.clamp(min, max), decimals: 2 } + } + _ => ParamUi::Number, + } +} + /// How many items a popup has: the file's names, else the count in its definition. fn popup_len(p: &EffectParam<'_, '_>) -> Option { p.items.as_ref().map(Vec::len).or_else(|| p.popup_item_count().map(usize::from)) diff --git a/crates/aep/tests/it/import/placeholders.rs b/crates/aep/tests/it/import/placeholders.rs index 01f1cbc43..1a8ec697e 100644 --- a/crates/aep/tests/it/import/placeholders.rs +++ b/crates/aep/tests/it/import/placeholders.rs @@ -109,6 +109,24 @@ fn a_pseudo_effect_is_kept_with_its_parameter_names() { assert_eq!(path.as_deref().and_then(|p| layer.prop(p)).map(|p| p.name.as_str()), Some("Speed"), "{path:?}"); } +/// A float slider keeps its valid range and slider range, so Effect Controls drags and clamps it +/// as After Effects does; a range that is not a range stays a plain number. +#[test] +fn a_float_slider_keeps_its_range() { + let fx = Fx { + match_name: "Pseudo/r", + name: "Rig", + defs: vec![slider(K::FloatSlider, "Speed", 1.0, (-10.0, 100.0)), slider(K::FloatSlider, "Odd", 1.0, (5.0, 1.0))], + values: vec![], + data: vec![], + keys: vec![], + }; + let (g, _) = placeholder_of(&fx); + let ui = |id: &str| g.prop(id).map(|p| p.ui.clone()); + assert_eq!(ui("param1"), Some(ParamUi::Slider { min: -10.0, max: 100.0, slider_min: -10.0, slider_max: 100.0, decimals: 2 })); + assert_eq!(ui("param2"), Some(ParamUi::Number)); +} + /// A popup whose definition is borrowed (the project copy) and has no value record: its default /// slot holds another copy's selection, so the first item is used and that is reported. #[test] diff --git a/docs/aep-import.md b/docs/aep-import.md index ffaa3f733..8fc01d5ca 100644 --- a/docs/aep-import.md +++ b/docs/aep-import.md @@ -324,7 +324,7 @@ parameter gets the file's value: is that name as one path segment, `Pseudo_0_25`). Like a missing plug-in it renders nothing and is listed by Find Missing Effects. It holds the effect's parameters flat (without the twirl-down group markers), named as in After Effects, each with its value, keys and expression, in the units After Effects' - expressions read (sliders and angles as stored, points in layer pixels, colours, checkboxes, + expressions read (sliders and angles as stored, a float slider with its range, points in layer pixels, colours, checkboxes, popups over the file's items with separators, read 1-based; layer parameters name their layer, without a record the layer itself as for Layer Control, and their keys and expression are reported),