Skip to main content

tui_slider/
state.rs

1//! Slider state management module
2//!
3//! This module provides the state management for sliders, including value tracking
4//! and bounds checking.
5//!
6//! # Overview
7//!
8//! The `SliderState` struct manages the value and bounds of a slider. It ensures
9//! that values are always clamped within the specified min/max range.
10//!
11//! # Examples
12//!
13//! ## Basic Usage
14//!
15//! ```
16//! use tui_slider::SliderState;
17//!
18//! let mut state = SliderState::new(50.0, 0.0, 100.0);
19//! assert_eq!(state.value(), 50.0);
20//!
21//! state.set_value(75.0);
22//! assert_eq!(state.value(), 75.0);
23//! ```
24//!
25//! ## Value Clamping
26//!
27//! ```
28//! use tui_slider::SliderState;
29//!
30//! let mut state = SliderState::new(50.0, 0.0, 100.0);
31//!
32//! // Values are automatically clamped
33//! state.set_value(150.0);
34//! assert_eq!(state.value(), 100.0);
35//!
36//! state.set_value(-10.0);
37//! assert_eq!(state.value(), 0.0);
38//! ```
39//!
40//! ## Percentage Operations
41//!
42//! ```
43//! use tui_slider::SliderState;
44//!
45//! let mut state = SliderState::new(50.0, 0.0, 100.0);
46//!
47//! // Get value as percentage (0.0 to 1.0)
48//! assert_eq!(state.percentage(), 0.5);
49//!
50//! // Set value from percentage
51//! state.set_percentage(0.75);
52//! assert_eq!(state.value(), 75.0);
53//! ```
54
55/// State management for a slider widget
56///
57/// Manages the current value and min/max bounds. All values are automatically
58/// clamped to stay within the specified range.
59///
60/// # Examples
61///
62/// ```
63/// use tui_slider::SliderState;
64///
65/// let mut state = SliderState::new(50.0, 0.0, 100.0);
66///
67/// // Direct value manipulation
68/// state.increase(10.0);
69/// assert_eq!(state.value(), 60.0);
70///
71/// state.decrease(5.0);
72/// assert_eq!(state.value(), 55.0);
73/// ```
74#[derive(Debug, Clone)]
75pub struct SliderState {
76    /// Current value of the slider
77    value: f64,
78    /// Minimum value
79    min: f64,
80    /// Maximum value
81    max: f64,
82    /// Step size for increment/decrement operations
83    step: f64,
84}
85
86impl SliderState {
87    /// Creates a new slider state with the given value and bounds
88    ///
89    /// # Arguments
90    ///
91    /// * `value` - Initial value
92    /// * `min` - Minimum value
93    /// * `max` - Maximum value
94    ///
95    /// # Panics
96    ///
97    /// Panics if min >= max
98    ///
99    /// # Examples
100    ///
101    /// ```
102    /// use tui_slider::SliderState;
103    ///
104    /// let state = SliderState::new(50.0, 0.0, 100.0);
105    /// assert_eq!(state.value(), 50.0);
106    /// assert_eq!(state.min(), 0.0);
107    /// assert_eq!(state.max(), 100.0);
108    /// ```
109    ///
110    /// ```should_panic
111    /// use tui_slider::SliderState;
112    ///
113    /// // This will panic because min >= max
114    /// let state = SliderState::new(50.0, 100.0, 0.0);
115    /// ```
116    pub fn new(value: f64, min: f64, max: f64) -> Self {
117        assert!(min < max, "min must be less than max");
118        let clamped_value = value.clamp(min, max);
119        Self {
120            value: clamped_value,
121            min,
122            max,
123            step: 1.0, // Default step size
124        }
125    }
126
127    /// Gets the current value
128    ///
129    /// # Examples
130    ///
131    /// ```
132    /// use tui_slider::SliderState;
133    ///
134    /// let state = SliderState::new(75.0, 0.0, 100.0);
135    /// assert_eq!(state.value(), 75.0);
136    /// ```
137    pub fn value(&self) -> f64 {
138        self.value
139    }
140
141    /// Sets the value (automatically clamped to min..max range)
142    ///
143    /// # Examples
144    ///
145    /// ```
146    /// use tui_slider::SliderState;
147    ///
148    /// let mut state = SliderState::new(50.0, 0.0, 100.0);
149    /// state.set_value(75.0);
150    /// assert_eq!(state.value(), 75.0);
151    ///
152    /// // Values are clamped to the valid range
153    /// state.set_value(150.0);
154    /// assert_eq!(state.value(), 100.0);
155    /// ```
156    pub fn set_value(&mut self, value: f64) {
157        self.value = value.clamp(self.min, self.max);
158    }
159
160    /// Gets the minimum value
161    ///
162    /// # Examples
163    ///
164    /// ```
165    /// use tui_slider::SliderState;
166    ///
167    /// let state = SliderState::new(50.0, 0.0, 100.0);
168    /// assert_eq!(state.min(), 0.0);
169    /// ```
170    pub fn min(&self) -> f64 {
171        self.min
172    }
173
174    /// Gets the maximum value
175    ///
176    /// # Examples
177    ///
178    /// ```
179    /// use tui_slider::SliderState;
180    ///
181    /// let state = SliderState::new(50.0, 0.0, 100.0);
182    /// assert_eq!(state.max(), 100.0);
183    /// ```
184    pub fn max(&self) -> f64 {
185        self.max
186    }
187
188    /// Sets the minimum value
189    ///
190    /// # Panics
191    ///
192    /// Panics if the new min is >= max
193    ///
194    /// # Examples
195    ///
196    /// ```
197    /// use tui_slider::SliderState;
198    ///
199    /// let mut state = SliderState::new(50.0, 0.0, 100.0);
200    /// state.set_min(-10.0);
201    /// assert_eq!(state.min(), -10.0);
202    /// ```
203    pub fn set_min(&mut self, min: f64) {
204        assert!(min < self.max, "min must be less than max");
205        self.min = min;
206        self.value = self.value.clamp(self.min, self.max);
207    }
208
209    /// Sets the maximum value
210    ///
211    /// # Panics
212    ///
213    /// Panics if the new max is <= min
214    ///
215    /// # Examples
216    ///
217    /// ```
218    /// use tui_slider::SliderState;
219    ///
220    /// let mut state = SliderState::new(50.0, 0.0, 100.0);
221    /// state.set_max(200.0);
222    /// assert_eq!(state.max(), 200.0);
223    /// ```
224    pub fn set_max(&mut self, max: f64) {
225        assert!(max > self.min, "max must be greater than min");
226        self.max = max;
227        self.value = self.value.clamp(self.min, self.max);
228    }
229
230    /// Gets the value as a percentage (0.0 to 1.0)
231    ///
232    /// # Examples
233    ///
234    /// ```
235    /// use tui_slider::SliderState;
236    ///
237    /// let state = SliderState::new(50.0, 0.0, 100.0);
238    /// assert_eq!(state.percentage(), 0.5);
239    ///
240    /// let state = SliderState::new(25.0, 0.0, 100.0);
241    /// assert_eq!(state.percentage(), 0.25);
242    /// ```
243    pub fn percentage(&self) -> f64 {
244        if (self.max - self.min).abs() < f64::EPSILON {
245            return 0.0;
246        }
247        (self.value - self.min) / (self.max - self.min)
248    }
249
250    /// Sets the value from a percentage (0.0 to 1.0)
251    ///
252    /// The percentage is automatically clamped to the 0.0-1.0 range.
253    ///
254    /// # Examples
255    ///
256    /// ```
257    /// use tui_slider::SliderState;
258    ///
259    /// let mut state = SliderState::new(0.0, 0.0, 100.0);
260    /// state.set_percentage(0.75);
261    /// assert_eq!(state.value(), 75.0);
262    ///
263    /// state.set_percentage(0.5);
264    /// assert_eq!(state.value(), 50.0);
265    /// ```
266    pub fn set_percentage(&mut self, percentage: f64) {
267        let clamped_percentage = percentage.clamp(0.0, 1.0);
268        self.set_value(self.min + (self.max - self.min) * clamped_percentage);
269    }
270
271    /// Increases the value by a step
272    ///
273    /// The result is automatically clamped to the maximum value.
274    ///
275    /// # Examples
276    ///
277    /// ```
278    /// use tui_slider::SliderState;
279    ///
280    /// let mut state = SliderState::new(50.0, 0.0, 100.0);
281    /// state.increase(10.0);
282    /// assert_eq!(state.value(), 60.0);
283    ///
284    /// // Won't exceed max
285    /// state.increase(100.0);
286    /// assert_eq!(state.value(), 100.0);
287    /// ```
288    pub fn increase(&mut self, step: f64) {
289        self.set_value(self.value + step);
290    }
291
292    /// Increases the value by the configured step size
293    ///
294    /// This is a convenience method that uses the step size set via `set_step()`.
295    ///
296    /// # Examples
297    ///
298    /// ```
299    /// use tui_slider::SliderState;
300    ///
301    /// let mut state = SliderState::new(50.0, 0.0, 100.0);
302    /// state.set_step(5.0);
303    /// state.step_up();
304    /// assert_eq!(state.value(), 55.0);
305    /// ```
306    pub fn step_up(&mut self) {
307        self.increase(self.step);
308    }
309
310    /// Decreases the value by a step
311    ///
312    /// The result is automatically clamped to the minimum value.
313    ///
314    /// # Examples
315    ///
316    /// ```
317    /// use tui_slider::SliderState;
318    ///
319    /// let mut state = SliderState::new(50.0, 0.0, 100.0);
320    /// state.decrease(10.0);
321    /// assert_eq!(state.value(), 40.0);
322    ///
323    /// // Won't go below min
324    /// state.decrease(100.0);
325    /// assert_eq!(state.value(), 0.0);
326    /// ```
327    pub fn decrease(&mut self, step: f64) {
328        self.set_value(self.value - step);
329    }
330
331    /// Decreases the value by the configured step size
332    ///
333    /// This is a convenience method that uses the step size set via `set_step()`.
334    ///
335    /// # Examples
336    ///
337    /// ```
338    /// use tui_slider::SliderState;
339    ///
340    /// let mut state = SliderState::new(50.0, 0.0, 100.0);
341    /// state.set_step(5.0);
342    /// state.step_down();
343    /// assert_eq!(state.value(), 45.0);
344    /// ```
345    pub fn step_down(&mut self) {
346        self.decrease(self.step);
347    }
348
349    /// Gets the current step size
350    ///
351    /// # Examples
352    ///
353    /// ```
354    /// use tui_slider::SliderState;
355    ///
356    /// let state = SliderState::new(50.0, 0.0, 100.0);
357    /// assert_eq!(state.step(), 1.0);
358    /// ```
359    pub fn step(&self) -> f64 {
360        self.step
361    }
362
363    /// Sets the step size for increment/decrement operations
364    ///
365    /// The step size determines how much the value changes when using
366    /// `step_up()` and `step_down()` methods.
367    ///
368    /// # Arguments
369    ///
370    /// * `step` - The step size (must be positive)
371    ///
372    /// # Panics
373    ///
374    /// Panics if step is not positive (step <= 0.0)
375    ///
376    /// # Examples
377    ///
378    /// ```
379    /// use tui_slider::SliderState;
380    ///
381    /// let mut state = SliderState::new(50.0, 0.0, 100.0);
382    /// state.set_step(5.0);
383    /// state.step_up();
384    /// assert_eq!(state.value(), 55.0);
385    ///
386    /// state.set_step(10.0);
387    /// state.step_up();
388    /// assert_eq!(state.value(), 65.0);
389    /// ```
390    ///
391    /// ```should_panic
392    /// use tui_slider::SliderState;
393    ///
394    /// let mut state = SliderState::new(50.0, 0.0, 100.0);
395    /// state.set_step(-1.0); // Panics!
396    /// ```
397    pub fn set_step(&mut self, step: f64) {
398        assert!(step > 0.0, "step must be positive");
399        self.step = step;
400    }
401
402    /// Creates a new slider state with a custom step size
403    ///
404    /// # Arguments
405    ///
406    /// * `value` - Initial value
407    /// * `min` - Minimum value
408    /// * `max` - Maximum value
409    /// * `step` - Step size for increment/decrement operations
410    ///
411    /// # Panics
412    ///
413    /// Panics if min >= max or if step <= 0.0
414    ///
415    /// # Examples
416    ///
417    /// ```
418    /// use tui_slider::SliderState;
419    ///
420    /// let mut state = SliderState::with_step(50.0, 0.0, 100.0, 5.0);
421    /// assert_eq!(state.step(), 5.0);
422    /// state.step_up();
423    /// assert_eq!(state.value(), 55.0);
424    /// ```
425    pub fn with_step(value: f64, min: f64, max: f64, step: f64) -> Self {
426        assert!(min < max, "min must be less than max");
427        assert!(step > 0.0, "step must be positive");
428        let clamped_value = value.clamp(min, max);
429        Self {
430            value: clamped_value,
431            min,
432            max,
433            step,
434        }
435    }
436
437    /// Sets the value from a position within a given length
438    ///
439    /// # Arguments
440    ///
441    /// * `position` - Position in the slider (0 to length)
442    /// * `length` - Total length of the slider
443    ///
444    /// # Examples
445    ///
446    /// ```
447    /// use tui_slider::SliderState;
448    ///
449    /// let mut state = SliderState::new(0.0, 0.0, 100.0);
450    /// state.set_from_position(50, 100);
451    /// assert_eq!(state.value(), 50.0);
452    /// ```
453    pub fn set_from_position(&mut self, position: u16, length: u16) {
454        if length == 0 {
455            return;
456        }
457        let percentage = position as f64 / length as f64;
458        self.set_percentage(percentage);
459    }
460
461    /// Gets the position within a given length
462    ///
463    /// # Examples
464    ///
465    /// ```
466    /// use tui_slider::SliderState;
467    ///
468    /// let state = SliderState::new(50.0, 0.0, 100.0);
469    /// assert_eq!(state.position(100), 50);
470    ///
471    /// let state = SliderState::new(25.0, 0.0, 100.0);
472    /// assert_eq!(state.position(100), 25);
473    /// ```
474    pub fn position(&self, length: u16) -> u16 {
475        (self.percentage() * length as f64).round() as u16
476    }
477
478    /// Returns the range (max - min)
479    ///
480    /// # Examples
481    ///
482    /// ```
483    /// use tui_slider::SliderState;
484    ///
485    /// let state = SliderState::new(50.0, 0.0, 100.0);
486    /// assert_eq!(state.range(), 100.0);
487    ///
488    /// let state = SliderState::new(50.0, 25.0, 75.0);
489    /// assert_eq!(state.range(), 50.0);
490    /// ```
491    pub fn range(&self) -> f64 {
492        self.max - self.min
493    }
494
495    /// Returns true if the slider is at its minimum value
496    ///
497    /// # Examples
498    ///
499    /// ```
500    /// use tui_slider::SliderState;
501    ///
502    /// let state = SliderState::new(0.0, 0.0, 100.0);
503    /// assert!(state.is_at_min());
504    ///
505    /// let state = SliderState::new(50.0, 0.0, 100.0);
506    /// assert!(!state.is_at_min());
507    /// ```
508    pub fn is_at_min(&self) -> bool {
509        (self.value - self.min).abs() < f64::EPSILON
510    }
511
512    /// Returns true if the slider is at its maximum value
513    ///
514    /// # Examples
515    ///
516    /// ```
517    /// use tui_slider::SliderState;
518    ///
519    /// let state = SliderState::new(100.0, 0.0, 100.0);
520    /// assert!(state.is_at_max());
521    ///
522    /// let state = SliderState::new(50.0, 0.0, 100.0);
523    /// assert!(!state.is_at_max());
524    /// ```
525    pub fn is_at_max(&self) -> bool {
526        (self.value - self.max).abs() < f64::EPSILON
527    }
528
529    /// Returns true if the slider is at or near the middle of its range
530    ///
531    /// # Examples
532    ///
533    /// ```
534    /// use tui_slider::SliderState;
535    ///
536    /// let state = SliderState::new(50.0, 0.0, 100.0);
537    /// assert!(state.is_at_middle());
538    ///
539    /// let state = SliderState::new(0.0, 0.0, 100.0);
540    /// assert!(!state.is_at_middle());
541    /// ```
542    pub fn is_at_middle(&self) -> bool {
543        let middle = (self.min + self.max) / 2.0;
544        (self.value - middle).abs() < self.range() * 0.1
545    }
546
547    /// Returns true if the slider value is in the lower third of its range
548    ///
549    /// # Examples
550    ///
551    /// ```
552    /// use tui_slider::SliderState;
553    ///
554    /// let state = SliderState::new(20.0, 0.0, 100.0);
555    /// assert!(state.is_low());
556    ///
557    /// let state = SliderState::new(80.0, 0.0, 100.0);
558    /// assert!(!state.is_low());
559    /// ```
560    pub fn is_low(&self) -> bool {
561        self.percentage() < 0.33
562    }
563
564    /// Returns true if the slider value is in the middle third of its range
565    ///
566    /// # Examples
567    ///
568    /// ```
569    /// use tui_slider::SliderState;
570    ///
571    /// let state = SliderState::new(50.0, 0.0, 100.0);
572    /// assert!(state.is_medium());
573    ///
574    /// let state = SliderState::new(10.0, 0.0, 100.0);
575    /// assert!(!state.is_medium());
576    /// ```
577    pub fn is_medium(&self) -> bool {
578        let pct = self.percentage();
579        (0.33..0.67).contains(&pct)
580    }
581
582    /// Returns true if the slider value is in the upper third of its range
583    ///
584    /// # Examples
585    ///
586    /// ```
587    /// use tui_slider::SliderState;
588    ///
589    /// let state = SliderState::new(80.0, 0.0, 100.0);
590    /// assert!(state.is_high());
591    ///
592    /// let state = SliderState::new(20.0, 0.0, 100.0);
593    /// assert!(!state.is_high());
594    /// ```
595    pub fn is_high(&self) -> bool {
596        self.percentage() >= 0.67
597    }
598
599    /// Returns the distance from the minimum value
600    ///
601    /// # Examples
602    ///
603    /// ```
604    /// use tui_slider::SliderState;
605    ///
606    /// let state = SliderState::new(75.0, 0.0, 100.0);
607    /// assert_eq!(state.distance_from_min(), 75.0);
608    ///
609    /// let state = SliderState::new(75.0, 25.0, 100.0);
610    /// assert_eq!(state.distance_from_min(), 50.0);
611    /// ```
612    pub fn distance_from_min(&self) -> f64 {
613        self.value - self.min
614    }
615
616    /// Returns the distance from the maximum value
617    ///
618    /// # Examples
619    ///
620    /// ```
621    /// use tui_slider::SliderState;
622    ///
623    /// let state = SliderState::new(75.0, 0.0, 100.0);
624    /// assert_eq!(state.distance_from_max(), 25.0);
625    ///
626    /// let state = SliderState::new(75.0, 25.0, 100.0);
627    /// assert_eq!(state.distance_from_max(), 25.0);
628    /// ```
629    pub fn distance_from_max(&self) -> f64 {
630        self.max - self.value
631    }
632
633    /// Returns a formatted string representation of the current value
634    ///
635    /// # Examples
636    ///
637    /// ```
638    /// use tui_slider::SliderState;
639    ///
640    /// let state = SliderState::new(75.5, 0.0, 100.0);
641    /// assert_eq!(state.value_string(1), "75.5");
642    /// assert_eq!(state.value_string(0), "76");
643    /// ```
644    pub fn value_string(&self, decimals: usize) -> String {
645        format!("{:.decimals$}", self.value, decimals = decimals)
646    }
647
648    /// Returns a formatted percentage string (e.g., "75%")
649    ///
650    /// # Examples
651    ///
652    /// ```
653    /// use tui_slider::SliderState;
654    ///
655    /// let state = SliderState::new(75.0, 0.0, 100.0);
656    /// assert_eq!(state.percentage_string(), "75%");
657    ///
658    /// let state = SliderState::new(50.0, 0.0, 100.0);
659    /// assert_eq!(state.percentage_string(), "50%");
660    /// ```
661    pub fn percentage_string(&self) -> String {
662        format!("{:.0}%", self.percentage() * 100.0)
663    }
664}
665
666impl Default for SliderState {
667    fn default() -> Self {
668        Self::new(0.0, 0.0, 100.0)
669    }
670}
671
672#[cfg(test)]
673mod tests {
674    use super::*;
675
676    #[test]
677    fn test_new_state() {
678        let state = SliderState::new(50.0, 0.0, 100.0);
679        assert_eq!(state.value(), 50.0);
680        assert_eq!(state.min(), 0.0);
681        assert_eq!(state.max(), 100.0);
682    }
683
684    #[test]
685    fn test_clamping() {
686        let mut state = SliderState::new(50.0, 0.0, 100.0);
687
688        state.set_value(150.0);
689        assert_eq!(state.value(), 100.0);
690
691        state.set_value(-50.0);
692        assert_eq!(state.value(), 0.0);
693    }
694
695    #[test]
696    fn test_percentage() {
697        let state = SliderState::new(50.0, 0.0, 100.0);
698        assert_eq!(state.percentage(), 0.5);
699
700        let state = SliderState::new(25.0, 0.0, 100.0);
701        assert_eq!(state.percentage(), 0.25);
702
703        let state = SliderState::new(0.0, 0.0, 100.0);
704        assert_eq!(state.percentage(), 0.0);
705
706        let state = SliderState::new(100.0, 0.0, 100.0);
707        assert_eq!(state.percentage(), 1.0);
708    }
709
710    #[test]
711    fn test_set_percentage() {
712        let mut state = SliderState::new(0.0, 0.0, 100.0);
713        state.set_percentage(0.5);
714        assert_eq!(state.value(), 50.0);
715
716        state.set_percentage(0.25);
717        assert_eq!(state.value(), 25.0);
718    }
719
720    #[test]
721    fn test_increase_decrease() {
722        let mut state = SliderState::new(50.0, 0.0, 100.0);
723
724        state.increase(10.0);
725        assert_eq!(state.value(), 60.0);
726
727        state.set_value(50.0); // Reset
728        state.decrease(10.0);
729        assert_eq!(state.value(), 40.0);
730    }
731
732    #[test]
733    fn test_position() {
734        let state = SliderState::new(50.0, 0.0, 100.0);
735        assert_eq!(state.position(100), 50);
736
737        let state = SliderState::new(25.0, 0.0, 100.0);
738        assert_eq!(state.position(100), 25);
739    }
740
741    #[test]
742    fn test_set_from_position() {
743        let mut state = SliderState::new(0.0, 0.0, 100.0);
744        state.set_from_position(50, 100);
745        assert_eq!(state.value(), 50.0);
746    }
747
748    #[test]
749    #[should_panic(expected = "min must be less than max")]
750    fn test_invalid_bounds() {
751        SliderState::new(50.0, 100.0, 0.0);
752    }
753
754    #[test]
755    fn test_default_step() {
756        let state = SliderState::new(50.0, 0.0, 100.0);
757        assert_eq!(state.step(), 1.0);
758    }
759
760    #[test]
761    fn test_set_step() {
762        let mut state = SliderState::new(50.0, 0.0, 100.0);
763        state.set_step(5.0);
764        assert_eq!(state.step(), 5.0);
765
766        state.set_step(10.0);
767        assert_eq!(state.step(), 10.0);
768    }
769
770    #[test]
771    #[should_panic(expected = "step must be positive")]
772    fn test_invalid_step_negative() {
773        let mut state = SliderState::new(50.0, 0.0, 100.0);
774        state.set_step(-1.0);
775    }
776
777    #[test]
778    #[should_panic(expected = "step must be positive")]
779    fn test_invalid_step_zero() {
780        let mut state = SliderState::new(50.0, 0.0, 100.0);
781        state.set_step(0.0);
782    }
783
784    #[test]
785    fn test_step_up() {
786        let mut state = SliderState::new(50.0, 0.0, 100.0);
787        state.set_step(5.0);
788
789        state.step_up();
790        assert_eq!(state.value(), 55.0);
791
792        state.step_up();
793        assert_eq!(state.value(), 60.0);
794    }
795
796    #[test]
797    fn test_step_down() {
798        let mut state = SliderState::new(50.0, 0.0, 100.0);
799        state.set_step(5.0);
800
801        state.step_down();
802        assert_eq!(state.value(), 45.0);
803
804        state.step_down();
805        assert_eq!(state.value(), 40.0);
806    }
807
808    #[test]
809    fn test_step_up_clamping() {
810        let mut state = SliderState::new(95.0, 0.0, 100.0);
811        state.set_step(10.0);
812
813        state.step_up();
814        assert_eq!(state.value(), 100.0); // Clamped to max
815    }
816
817    #[test]
818    fn test_step_down_clamping() {
819        let mut state = SliderState::new(5.0, 0.0, 100.0);
820        state.set_step(10.0);
821
822        state.step_down();
823        assert_eq!(state.value(), 0.0); // Clamped to min
824    }
825
826    #[test]
827    fn test_with_step() {
828        let state = SliderState::with_step(50.0, 0.0, 100.0, 5.0);
829        assert_eq!(state.value(), 50.0);
830        assert_eq!(state.min(), 0.0);
831        assert_eq!(state.max(), 100.0);
832        assert_eq!(state.step(), 5.0);
833    }
834
835    #[test]
836    fn test_with_step_operations() {
837        let mut state = SliderState::with_step(50.0, 0.0, 100.0, 2.5);
838
839        state.step_up();
840        assert_eq!(state.value(), 52.5);
841
842        state.step_down();
843        assert_eq!(state.value(), 50.0);
844    }
845
846    #[test]
847    #[should_panic(expected = "step must be positive")]
848    fn test_with_step_invalid() {
849        SliderState::with_step(50.0, 0.0, 100.0, -1.0);
850    }
851
852    #[test]
853    fn test_different_step_sizes() {
854        let mut state = SliderState::new(50.0, 0.0, 100.0);
855
856        // Step by 0.1
857        state.set_step(0.1);
858        state.step_up();
859        assert!((state.value() - 50.1).abs() < 0.0001);
860
861        // Step by 25
862        state.set_value(50.0);
863        state.set_step(25.0);
864        state.step_up();
865        assert_eq!(state.value(), 75.0);
866    }
867}