Skip to content

Add slider::Scale trait and implementations#3370

Open
edwloef wants to merge 2 commits into
iced-rs:masterfrom
edwloef:slider-scale
Open

Add slider::Scale trait and implementations#3370
edwloef wants to merge 2 commits into
iced-rs:masterfrom
edwloef:slider-scale

Conversation

@edwloef

@edwloef edwloef commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

This pull request adds a new slider::Scale trait and two implementations of that trait through slider::discrete and slider::continuous.

slider::Scale generalizes over the previous step and shift_step methods of Slider and VerticalSlider, allowing for custom snapping and stepping behavior. slider::discrete implements the previous stepped behavior, and slider::continuous is a straight pass-through of the un-snapped value, which wasn't available before. The default, being slider::discrete(1), matches the old behavior.

Some more hopefully illustrative examples of why this is useful:

This is an implementation of a slider::Scale that snaps the value to a given "default" when it's within some radius of that value, and is continuous otherwise. A concrete use-case for this scale could be a volume slider snapping to full volume, while allowing continuous adjustment both above and below that value.

struct SnapNear {
    value: f64,
    radius: f64,
}

impl<T: FromPrimitive> Scale<T> for SnapNear {
    fn snap(&self, value: f64, _: RangeInclusive<T>) -> Option<T> {
        if (value - self.value).abs() < self.radius {
            T::from_f64(self.value)
        } else {
            T::from_f64(value)
        }
    }
}

This is an implementation of a slider::Scale that snaps to exponentially spaced steps (or linearly spaced steps on a logarithmic scale). A concrete use-case for this scale could be a slider setting the app scale factor, since app scale factor lies on a logarithmic scale (1.0 is halfway between 0.5 and 2.0).

struct LogScale {
    step: f64,
}

impl<T: AsPrimitive<f64> + FromPrimitive> Scale<T> for LogScale {
    fn snap(&self, value: f64, range: RangeInclusive<T>) -> Option<T> {
        let start = range.start().as_().log2();
        let value = ((value.log2() - start) / self.step).round() * self.step + start;
        T::from_f64(value.min(range.end().as_().log2()).exp2())
    }

    fn step_up(&self, value: T, range: RangeInclusive<T>, _: Modifiers) -> Option<T> {
        T::from_f64(
            (value.as_().log2() + self.step)
                .min(range.end().as_().log2())
                .exp2(),
        )
    }

    fn step_down(&self, value: T, range: RangeInclusive<T>, _: Modifiers) -> Option<T> {
        T::from_f64(
            (value.as_().log2() - self.step)
                .min(range.end().as_().log2())
                .exp2(),
        )
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants