rat_widget/
slider.rs

1//!
2//! Slider widget.
3//!
4//! ```rust no_run
5//! use rat_widget::slider::{Slider, SliderState};
6//! # use ratatui::layout::Rect;
7//! # use ratatui::prelude::*;
8//! #
9//! # let slider_area = Rect::ZERO;
10//! # let mut buf = Buffer::default();
11//!
12//! let mut state = SliderState::<u8>::new_range((0,255), 1);
13//! state.set_value(42);
14//!
15//! Slider::new().render(slider_area, &mut buf, &mut state);
16//!
17//! ```
18//!
19
20use crate::_private::NonExhaustive;
21use crate::range_op::RangeOp;
22use crate::slider::event::SliderOutcome;
23use crate::text::HasScreenCursor;
24use crate::util::revert_style;
25use map_range_int::MapRange;
26use rat_event::util::MouseFlags;
27use rat_event::{HandleEvent, MouseOnly, Regular, ct_event};
28use rat_focus::{FocusBuilder, FocusFlag, HasFocus};
29use rat_reloc::{RelocatableState, relocate_area};
30use ratatui::buffer::Buffer;
31use ratatui::layout::{Alignment, Direction, Position, Rect};
32use ratatui::prelude::BlockExt;
33use ratatui::style::{Style, Stylize};
34use ratatui::text::{Line, Text};
35use ratatui::widgets::StatefulWidget;
36use ratatui::widgets::{Block, Widget};
37use std::borrow::Cow;
38use std::fmt::{Debug, Formatter};
39use std::marker::PhantomData;
40use unicode_display_width::width as unicode_width;
41
42/// Slider widget for a type T.
43///
44/// T has to implement [RangeOp] and [MapRange] to and from u16.
45///
46#[derive(Debug, Clone)]
47pub struct Slider<'a, T = usize>
48where
49    T: RangeOp<Step: Copy + Debug> + MapRange<u16> + Debug + Default + Copy + PartialEq,
50    u16: MapRange<T>,
51{
52    style: Style,
53    bounds_style: Option<Style>,
54    knob_style: Option<Style>,
55    focus_style: Option<Style>,
56
57    direction: Direction,
58
59    range: Option<(T, T)>,
60    step: Option<<T as RangeOp>::Step>,
61    long_step: Option<<T as RangeOp>::Step>,
62
63    text_align: Alignment,
64    lower_bound: Option<Cow<'a, str>>,
65    upper_bound: Option<Cow<'a, str>>,
66
67    track_char: Option<Cow<'a, str>>,
68
69    horizontal_knob: Option<Cow<'a, str>>,
70    vertical_knob: Option<Cow<'a, str>>,
71
72    block: Option<Block<'a>>,
73
74    _phantom: PhantomData<T>,
75}
76
77#[derive(Debug, Clone)]
78pub struct SliderStyle {
79    /// Base style.
80    pub style: Style,
81    /// Style for the upper/lower bounds text.
82    pub bounds: Option<Style>,
83    /// Style for the knob.
84    pub knob: Option<Style>,
85    /// Style when focused.
86    pub focus: Option<Style>,
87
88    /// Alignment for all text.
89    pub text_align: Option<Alignment>,
90    /// Text string for the lower bound. Can contain newlines.
91    pub lower_bound: Option<&'static str>,
92    /// Text string for the upper bound. Can contain newlines.
93    pub upper_bound: Option<&'static str>,
94
95    /// Fill char for the track.
96    pub track_char: Option<&'static str>,
97
98    /// Text for the knob in vertical mode.
99    pub vertical_knob: Option<&'static str>,
100    /// Text for the knob in horizontal mode.
101    pub horizontal_knob: Option<&'static str>,
102
103    /// Border
104    pub block: Option<Block<'static>>,
105
106    pub non_exhaustive: NonExhaustive,
107}
108
109/// State.
110pub struct SliderState<T = usize>
111where
112    T: RangeOp<Step: Copy + Debug> + MapRange<u16> + Debug + Default + Copy + PartialEq,
113    u16: MapRange<T>,
114{
115    /// Complete area
116    /// __read only__. renewed for each render.
117    pub area: Rect,
118    /// Area inside the block without padding due to alignment.
119    /// __read only__. renewed for each render.
120    pub inner: Rect,
121    /// Lower bounds area.
122    /// __read only__. renewed for each render.
123    pub lower_bound: Rect,
124    /// Upper bounds area.
125    /// __read only__. renewed for each render.
126    pub upper_bound: Rect,
127    /// Track char.
128    /// __read only__. renewed for each render.
129    pub track: Rect,
130    /// Knob text
131    /// __read only__. renewed for each render.
132    pub knob: Rect,
133    /// Length of the track without the knob
134    pub scale_len: u16,
135    /// Direction
136    /// __read only__. renewed for each render.
137    pub direction: Direction,
138
139    /// Value range
140    /// __read+write__. might be overwritten by render.
141    pub range: (T, T),
142    /// Minor step.
143    /// __read+write__. might be overwritten by render.
144    pub step: <T as RangeOp>::Step,
145    /// Major step.
146    /// __read+write__. might be overwritten by render.
147    pub long_step: Option<<T as RangeOp>::Step>,
148
149    /// Value
150    pub value: T,
151
152    /// Current focus state.
153    /// __read+write__
154    pub focus: FocusFlag,
155
156    /// Mouse helper
157    /// __read+write__
158    pub mouse: MouseFlags,
159
160    pub non_exhaustive: NonExhaustive,
161}
162
163pub(crate) mod event {
164    use rat_event::{ConsumedEvent, Outcome};
165
166    /// Result value for event-handling.
167    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
168    pub enum SliderOutcome {
169        /// The given event was not handled at all.
170        Continue,
171        /// The event was handled, no repaint necessary.
172        Unchanged,
173        /// The event was handled, repaint necessary.
174        Changed,
175        /// The value has changed.
176        Value,
177    }
178
179    impl ConsumedEvent for SliderOutcome {
180        fn is_consumed(&self) -> bool {
181            *self != SliderOutcome::Continue
182        }
183    }
184
185    impl From<SliderOutcome> for Outcome {
186        fn from(value: SliderOutcome) -> Self {
187            match value {
188                SliderOutcome::Continue => Outcome::Continue,
189                SliderOutcome::Unchanged => Outcome::Unchanged,
190                SliderOutcome::Changed => Outcome::Changed,
191                SliderOutcome::Value => Outcome::Changed,
192            }
193        }
194    }
195}
196
197impl Default for SliderStyle {
198    fn default() -> Self {
199        Self {
200            style: Default::default(),
201            bounds: None,
202            knob: None,
203            focus: None,
204            text_align: None,
205            lower_bound: None,
206            upper_bound: None,
207            track_char: None,
208            vertical_knob: None,
209            horizontal_knob: None,
210            block: None,
211            non_exhaustive: NonExhaustive,
212        }
213    }
214}
215
216impl<T> Default for Slider<'_, T>
217where
218    T: RangeOp<Step: Copy + Debug> + MapRange<u16> + Debug + Default + Copy + PartialEq,
219    u16: MapRange<T>,
220{
221    fn default() -> Self {
222        Self {
223            style: Default::default(),
224            bounds_style: None,
225            knob_style: None,
226            focus_style: None,
227            direction: Direction::Horizontal,
228            range: None,
229            step: None,
230            long_step: None,
231            text_align: Alignment::Left,
232            lower_bound: None,
233            upper_bound: None,
234            track_char: None,
235            horizontal_knob: None,
236            vertical_knob: None,
237            block: None,
238            _phantom: Default::default(),
239        }
240    }
241}
242
243impl<'a, T> Slider<'a, T>
244where
245    T: RangeOp<Step: Copy + Debug> + MapRange<u16> + Debug + Default + Copy + PartialEq,
246    u16: MapRange<T>,
247{
248    /// New
249    pub fn new() -> Self {
250        Default::default()
251    }
252
253    /// Direction for the slider.
254    pub fn direction(mut self, direction: Direction) -> Self {
255        self.direction = direction;
256        self
257    }
258
259    /// Overrides the range of the slider.
260    pub fn range(mut self, range: (T, T)) -> Self {
261        self.range = Some(range);
262        self
263    }
264
265    /// First step size.
266    pub fn step(mut self, step: <T as RangeOp>::Step) -> Self {
267        self.step = Some(step);
268        self
269    }
270
271    /// Second step size.
272    pub fn long_step(mut self, step: <T as RangeOp>::Step) -> Self {
273        self.long_step = Some(step);
274        self
275    }
276
277    /// Set all styles.
278    pub fn styles(mut self, styles: SliderStyle) -> Self {
279        self.style = styles.style;
280        if styles.bounds.is_some() {
281            self.bounds_style = styles.bounds;
282        }
283        if styles.knob.is_some() {
284            self.knob_style = styles.knob;
285        }
286        if styles.focus.is_some() {
287            self.focus_style = styles.focus;
288        }
289        if let Some(align) = styles.text_align {
290            self.text_align = align;
291        }
292        if styles.lower_bound.is_some() {
293            self.lower_bound = styles.lower_bound.map(Cow::Borrowed);
294        }
295        if styles.upper_bound.is_some() {
296            self.upper_bound = styles.upper_bound.map(Cow::Borrowed);
297        }
298        if styles.track_char.is_some() {
299            self.track_char = styles.track_char.map(Cow::Borrowed);
300        }
301        if styles.vertical_knob.is_some() {
302            self.vertical_knob = styles.vertical_knob.map(Cow::Borrowed);
303        }
304        if styles.horizontal_knob.is_some() {
305            self.horizontal_knob = styles.horizontal_knob.map(Cow::Borrowed);
306        }
307        if styles.block.is_some() {
308            self.block = styles.block;
309        }
310        self.block = self.block.map(|v| v.style(self.style));
311        self
312    }
313
314    /// Base style.
315    pub fn style(mut self, style: Style) -> Self {
316        self.style = style;
317        self.block = self.block.map(|v| v.style(style));
318        self
319    }
320
321    /// Style for focus.
322    pub fn focus_style(mut self, style: Style) -> Self {
323        self.focus_style = Some(style);
324        self
325    }
326
327    /// Style for the bounds text.
328    pub fn bounds_style(mut self, style: Style) -> Self {
329        self.bounds_style = Some(style);
330        self
331    }
332
333    /// Style for the knob.
334    pub fn knob_style(mut self, style: Style) -> Self {
335        self.knob_style = Some(style);
336        self
337    }
338
339    /// Text alignment. Used for the bounds and the knob.
340    pub fn text_align(mut self, align: Alignment) -> Self {
341        self.text_align = align;
342        self
343    }
344
345    /// Text for the lower bound. Can contain newlines.
346    pub fn lower_bound(mut self, bound: impl Into<Cow<'a, str>>) -> Self {
347        self.lower_bound = Some(bound.into());
348        self
349    }
350
351    /// Text for the upper bound. Can contain newlines.
352    pub fn upper_bound(mut self, bound: impl Into<Cow<'a, str>>) -> Self {
353        self.upper_bound = Some(bound.into());
354        self
355    }
356
357    /// Fill char for the track.
358    pub fn track_char(mut self, bound: impl Into<Cow<'a, str>>) -> Self {
359        self.track_char = Some(bound.into());
360        self
361    }
362
363    /// Text for the horizontal knob. Can contain newlines for
364    /// multiline sliders.
365    pub fn horizontal_knob(mut self, knob: impl Into<Cow<'a, str>>) -> Self {
366        self.horizontal_knob = Some(knob.into());
367        self
368    }
369
370    /// Text for the vertical knob. Can contain newlines for a
371    /// multiline knob.
372    pub fn vertical_knob(mut self, knob: impl Into<Cow<'a, str>>) -> Self {
373        self.vertical_knob = Some(knob.into());
374        self
375    }
376
377    /// Block for borders.
378    pub fn block(mut self, block: Block<'a>) -> Self {
379        self.block = Some(block);
380        self.block = self.block.map(|v| v.style(self.style));
381        self
382    }
383
384    pub fn width(&self) -> u16 {
385        match self.direction {
386            Direction::Horizontal => {
387                let lower_width = self
388                    .lower_bound
389                    .as_ref()
390                    .map(|v| unicode_width(v) as u16)
391                    .unwrap_or_default();
392                let upper_width = self
393                    .upper_bound
394                    .as_ref()
395                    .map(|v| unicode_width(v) as u16)
396                    .unwrap_or_default();
397
398                let knob_width = unicode_width(
399                    self.render_knob_str(1, false)
400                        .split('\n')
401                        .next()
402                        .expect("one knob"),
403                ) as u16;
404
405                lower_width + upper_width + knob_width + 4
406            }
407            Direction::Vertical => 1,
408        }
409    }
410
411    pub fn height(&self) -> u16 {
412        match self.direction {
413            Direction::Horizontal => 1,
414            Direction::Vertical => {
415                let lower_height = self
416                    .lower_bound
417                    .as_ref()
418                    .map(|v| v.split('\n').count() as u16)
419                    .unwrap_or_default();
420                let upper_height = self
421                    .upper_bound
422                    .as_ref()
423                    .map(|v| v.split('\n').count() as u16)
424                    .unwrap_or_default();
425
426                let knob_height = 1;
427
428                lower_height + upper_height + knob_height + 4
429            }
430        }
431    }
432}
433
434impl<'a, T> Slider<'a, T>
435where
436    T: RangeOp<Step: Copy + Debug> + MapRange<u16> + Debug + Default + Copy + PartialEq,
437    u16: MapRange<T>,
438{
439    // Creates the default knob text.
440    // knob_repeat is either rows/columns, the off direction.
441    fn render_knob_str(&'a self, knob_repeat: u16, is_focused: bool) -> Cow<'a, str> {
442        fn map_ref<'b>(s0: &'b Option<Cow<'b, str>>, d: Cow<'b, str>) -> Cow<'b, str> {
443            s0.as_ref().map(|v| Cow::Borrowed(v.as_ref())).unwrap_or(d)
444        }
445
446        if is_focused {
447            match (self.direction, knob_repeat) {
448                (Direction::Horizontal, 1) => map_ref(&self.horizontal_knob, Cow::Borrowed(" | ")),
449                (Direction::Horizontal, 2) => {
450                    map_ref(&self.horizontal_knob, Cow::Borrowed(" ╷ \n ╵ "))
451                }
452                (Direction::Horizontal, 3) => {
453                    map_ref(&self.horizontal_knob, Cow::Borrowed(" ╷ \n │ \n ╵ "))
454                }
455                (Direction::Horizontal, 4) => {
456                    map_ref(&self.horizontal_knob, Cow::Borrowed(" ╷ \n │ \n │ \n ╵ "))
457                }
458                (Direction::Horizontal, 5) => map_ref(
459                    &self.horizontal_knob,
460                    Cow::Borrowed(" ╷ \n │ \n │ \n │ \n ╵ "),
461                ),
462                (Direction::Horizontal, n) => {
463                    let mut tmp = String::new();
464                    tmp.push_str(" â•· \n");
465                    for _ in 0..n - 2 {
466                        tmp.push_str(" │ \n");
467                    }
468                    tmp.push_str(" ╵ ");
469                    map_ref(&self.horizontal_knob, Cow::Owned(tmp))
470                }
471
472                (Direction::Vertical, 1) => map_ref(&self.vertical_knob, Cow::Borrowed("─")),
473                (Direction::Vertical, 2) => map_ref(&self.vertical_knob, Cow::Borrowed("â•¶â•´")),
474                (Direction::Vertical, 3) => map_ref(&self.vertical_knob, Cow::Borrowed("╶─╴")),
475                (Direction::Vertical, 4) => map_ref(&self.vertical_knob, Cow::Borrowed("╶──╴")),
476                (Direction::Vertical, 5) => map_ref(&self.vertical_knob, Cow::Borrowed("╶───╴")),
477                (Direction::Vertical, n) => {
478                    let mut tmp = String::new();
479                    tmp.push('â•¶');
480                    for _ in 0..n - 2 {
481                        tmp.push('─');
482                    }
483                    tmp.push('â•´');
484                    map_ref(&self.vertical_knob, Cow::Owned(tmp))
485                }
486            }
487        } else {
488            match (self.direction, knob_repeat) {
489                (Direction::Horizontal, 1) => map_ref(&self.horizontal_knob, Cow::Borrowed("   ")),
490                (Direction::Horizontal, 2) => {
491                    map_ref(&self.horizontal_knob, Cow::Borrowed("   \n   "))
492                }
493                (Direction::Horizontal, 3) => {
494                    map_ref(&self.horizontal_knob, Cow::Borrowed("   \n   \n   "))
495                }
496                (Direction::Horizontal, 4) => {
497                    map_ref(&self.horizontal_knob, Cow::Borrowed("   \n   \n   \n   "))
498                }
499                (Direction::Horizontal, 5) => map_ref(
500                    &self.horizontal_knob,
501                    Cow::Borrowed("   \n   \n   \n   \n   "),
502                ),
503                (Direction::Horizontal, n) => {
504                    let mut tmp = String::new();
505                    tmp.push_str("   \n");
506                    for _ in 0..n.saturating_sub(2) {
507                        tmp.push_str("   \n");
508                    }
509                    tmp.push_str("   ");
510                    map_ref(&self.horizontal_knob, Cow::Owned(tmp))
511                }
512
513                (Direction::Vertical, 1) => map_ref(&self.vertical_knob, Cow::Borrowed(" ")),
514                (Direction::Vertical, 2) => map_ref(&self.vertical_knob, Cow::Borrowed("  ")),
515                (Direction::Vertical, 3) => map_ref(&self.vertical_knob, Cow::Borrowed("   ")),
516                (Direction::Vertical, 4) => map_ref(&self.vertical_knob, Cow::Borrowed("    ")),
517                (Direction::Vertical, 5) => map_ref(&self.vertical_knob, Cow::Borrowed("     ")),
518                (Direction::Vertical, n) => {
519                    map_ref(&self.vertical_knob, Cow::Owned(" ".repeat(n as usize)))
520                }
521            }
522        }
523    }
524
525    // layout
526    fn layout(&self, area: Rect, state: &mut SliderState<T>) {
527        state.area = area;
528        state.inner = self.block.inner_if_some(area);
529        state.direction = self.direction;
530
531        if let Some(range) = self.range {
532            state.range = range;
533        }
534        if let Some(step) = self.step {
535            state.step = step;
536        }
537        if let Some(long_step) = self.long_step {
538            state.long_step = Some(long_step);
539        }
540
541        let inner = state.inner;
542
543        match self.direction {
544            Direction::Horizontal => {
545                let lower_width = self
546                    .lower_bound
547                    .as_ref()
548                    .map(|v| unicode_width(v) as u16)
549                    .unwrap_or_default();
550                let upper_width = self
551                    .upper_bound
552                    .as_ref()
553                    .map(|v| unicode_width(v) as u16)
554                    .unwrap_or_default();
555
556                state.lower_bound = Rect::new(inner.x, inner.y, lower_width, inner.height);
557                state.upper_bound = Rect::new(
558                    (inner.x + inner.width).saturating_sub(upper_width),
559                    inner.y,
560                    upper_width,
561                    inner.height,
562                );
563
564                let track_len = state
565                    .upper_bound
566                    .x
567                    .saturating_sub(state.lower_bound.right());
568                state.track =
569                    Rect::new(state.lower_bound.right(), inner.y, track_len, inner.height);
570
571                let knob_width = unicode_width(
572                    self.render_knob_str(inner.height, false)
573                        .split('\n')
574                        .next()
575                        .expect("one knob"),
576                ) as u16;
577                state.scale_len = track_len.saturating_sub(knob_width);
578
579                if let Some(knob_pos) = state.value.map_range(state.range, (0, state.scale_len)) {
580                    state.knob =
581                        Rect::new(state.track.x + knob_pos, inner.y, knob_width, inner.height)
582                } else {
583                    state.knob = Rect::new(state.track.x, inner.y, 0, inner.height);
584                }
585            }
586            Direction::Vertical => {
587                let lower_height = self
588                    .lower_bound
589                    .as_ref()
590                    .map(|v| v.split('\n').count() as u16)
591                    .unwrap_or_default();
592                let upper_height = self
593                    .upper_bound
594                    .as_ref()
595                    .map(|v| v.split('\n').count() as u16)
596                    .unwrap_or_default();
597
598                state.lower_bound = Rect::new(inner.x, inner.y, inner.width, lower_height);
599                state.upper_bound = Rect::new(
600                    inner.x,
601                    inner.bottom().saturating_sub(upper_height),
602                    inner.width,
603                    upper_height,
604                );
605
606                let track_len = inner.height.saturating_sub(lower_height + upper_height);
607                state.track = Rect::new(inner.x, inner.y + lower_height, inner.width, track_len);
608
609                state.scale_len = track_len.saturating_sub(1);
610
611                if let Some(knob_pos) = state.value.map_range(state.range, (0, state.scale_len)) {
612                    state.knob = Rect::new(inner.x, state.track.y + knob_pos, inner.width, 1)
613                } else {
614                    state.knob = Rect::new(inner.x, state.track.y, inner.width, 0)
615                }
616            }
617        }
618    }
619}
620
621impl<'a, T> StatefulWidget for &Slider<'a, T>
622where
623    T: RangeOp<Step: Copy + Debug> + MapRange<u16> + Debug + Default + Copy + PartialEq,
624    u16: MapRange<T>,
625{
626    type State = SliderState<T>;
627
628    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
629        render_slider(self, area, buf, state);
630    }
631}
632
633impl<T> StatefulWidget for Slider<'_, T>
634where
635    T: RangeOp<Step: Copy + Debug> + MapRange<u16> + Debug + Default + Copy + PartialEq,
636    u16: MapRange<T>,
637{
638    type State = SliderState<T>;
639
640    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
641        render_slider(&self, area, buf, state);
642    }
643}
644
645fn render_slider<T>(
646    widget: &Slider<'_, T>,
647    area: Rect,
648    buf: &mut Buffer,
649    state: &mut SliderState<T>,
650) where
651    T: RangeOp<Step: Copy + Debug> + MapRange<u16> + Debug + Default + Copy + PartialEq,
652    u16: MapRange<T>,
653{
654    widget.layout(area, state);
655
656    if let Some(block) = widget.block.as_ref() {
657        block.render(area, buf);
658    } else {
659        buf.set_style(area, widget.style);
660    }
661
662    let style = if widget.style == Default::default() {
663        Style::default().black().on_gray()
664    } else {
665        widget.style
666    };
667    let bounds_style = if let Some(bounds_style) = widget.bounds_style {
668        style.patch(bounds_style)
669    } else {
670        style
671    };
672    let knob_style = if state.is_focused() {
673        if let Some(focus_style) = widget.focus_style {
674            style.patch(focus_style)
675        } else {
676            revert_style(style)
677        }
678    } else if let Some(knob_style) = widget.knob_style {
679        style.patch(knob_style)
680    } else {
681        revert_style(style)
682    };
683
684    if let Some(lower_bound_str) = widget.lower_bound.as_ref() {
685        match widget.direction {
686            Direction::Horizontal => {
687                buf.set_style(state.lower_bound, bounds_style);
688
689                // need to vertically align manually.
690                let lower_height = lower_bound_str.split('\n').count() as u16;
691                let y_offset = match widget.text_align {
692                    Alignment::Left => 0,
693                    Alignment::Center => state.lower_bound.height.saturating_sub(lower_height) / 2,
694                    Alignment::Right => state.lower_bound.height.saturating_sub(lower_height),
695                };
696                let txt_area = Rect::new(
697                    state.lower_bound.x,
698                    state.lower_bound.y + y_offset,
699                    state.lower_bound.width,
700                    state.lower_bound.height,
701                );
702
703                Text::from(lower_bound_str.as_ref())
704                    .alignment(widget.text_align)
705                    .render(txt_area, buf);
706            }
707            Direction::Vertical => {
708                Text::from(lower_bound_str.as_ref())
709                    .style(bounds_style)
710                    .alignment(widget.text_align)
711                    .render(state.lower_bound, buf);
712            }
713        }
714    }
715    if let Some(upper_bound_str) = widget.upper_bound.as_ref() {
716        match widget.direction {
717            Direction::Horizontal => {
718                buf.set_style(state.upper_bound, bounds_style);
719
720                // need to vertically align manually.
721                let upper_height = upper_bound_str.split('\n').count() as u16;
722                let y_offset = match widget.text_align {
723                    Alignment::Left => 0,
724                    Alignment::Center => state.upper_bound.height.saturating_sub(upper_height) / 2,
725                    Alignment::Right => state.upper_bound.height.saturating_sub(upper_height),
726                };
727
728                let txt_area = Rect::new(
729                    state.upper_bound.x,
730                    state.upper_bound.y + y_offset,
731                    state.upper_bound.width,
732                    state.upper_bound.height,
733                );
734
735                Text::from(upper_bound_str.as_ref())
736                    .alignment(widget.text_align)
737                    .render(txt_area, buf);
738            }
739            Direction::Vertical => {
740                Text::from(upper_bound_str.as_ref())
741                    .style(bounds_style)
742                    .alignment(widget.text_align)
743                    .render(state.upper_bound, buf);
744            }
745        }
746    }
747
748    let track_str = widget.track_char.as_ref().unwrap_or(&Cow::Borrowed(" "));
749    if " " != track_str.as_ref() {
750        for y in state.track.top()..state.track.bottom() {
751            for x in state.track.left()..state.track.right() {
752                if let Some(cell) = buf.cell_mut((x, y)) {
753                    cell.set_symbol(track_str.as_ref());
754                }
755            }
756        }
757    }
758
759    match widget.direction {
760        Direction::Horizontal => {
761            let knob_str = widget.render_knob_str(state.knob.height, state.is_focused());
762            Text::from(knob_str.as_ref())
763                .style(knob_style)
764                .render(state.knob, buf);
765        }
766        Direction::Vertical => {
767            let knob_str = widget.render_knob_str(state.knob.width, state.is_focused());
768            Line::from(knob_str)
769                .alignment(widget.text_align)
770                .style(knob_style)
771                .render(state.knob, buf);
772        }
773    }
774}
775
776impl<T> Debug for SliderState<T>
777where
778    T: RangeOp<Step: Copy + Debug> + MapRange<u16> + Debug + Default + Copy + PartialEq,
779    u16: MapRange<T>,
780{
781    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
782        f.debug_struct("SliderState")
783            .field("area", &self.area)
784            .field("inner", &self.inner)
785            .field("lower_bound", &self.lower_bound)
786            .field("upper_bound", &self.upper_bound)
787            .field("track", &self.track)
788            .field("knob", &self.knob)
789            .field("scale_len", &self.scale_len)
790            .field("direction", &self.direction)
791            .field("range", &self.range)
792            .field("step", &self.step)
793            .field("long_step", &self.long_step)
794            .field("value", &self.value)
795            .field("focus", &self.focus)
796            .field("mouse", &self.mouse)
797            .finish()
798    }
799}
800
801impl<T> HasFocus for SliderState<T>
802where
803    T: RangeOp<Step: Copy + Debug> + MapRange<u16> + Debug + Default + Copy + PartialEq,
804    u16: MapRange<T>,
805{
806    fn build(&self, builder: &mut FocusBuilder) {
807        builder.leaf_widget(self);
808    }
809
810    fn focus(&self) -> FocusFlag {
811        self.focus.clone()
812    }
813
814    fn area(&self) -> Rect {
815        self.area
816    }
817}
818
819impl<T> HasScreenCursor for SliderState<T>
820where
821    T: RangeOp<Step: Copy + Debug> + MapRange<u16> + Debug + Default + Copy + PartialEq,
822    u16: MapRange<T>,
823{
824    fn screen_cursor(&self) -> Option<(u16, u16)> {
825        None
826    }
827}
828
829impl<T> RelocatableState for SliderState<T>
830where
831    T: RangeOp<Step: Copy + Debug> + MapRange<u16> + Debug + Default + Copy + PartialEq,
832    u16: MapRange<T>,
833{
834    fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
835        self.area = relocate_area(self.area, shift, clip);
836        self.inner = relocate_area(self.inner, shift, clip);
837        self.lower_bound = relocate_area(self.lower_bound, shift, clip);
838        self.upper_bound = relocate_area(self.upper_bound, shift, clip);
839        self.track = relocate_area(self.track, shift, clip);
840        self.knob = relocate_area(self.knob, shift, clip);
841    }
842}
843
844macro_rules! slider_new {
845    ($tt:ty) => {
846        impl Default for SliderState<$tt> {
847            fn default() -> Self {
848                Self {
849                    area: Default::default(),
850                    inner: Default::default(),
851                    lower_bound: Default::default(),
852                    upper_bound: Default::default(),
853                    track: Default::default(),
854                    knob: Default::default(),
855                    scale_len: 0,
856                    direction: Default::default(),
857                    range: (<$tt>::MIN, <$tt>::MAX),
858                    step: 1,
859                    long_step: None,
860                    value: Default::default(),
861                    focus: Default::default(),
862                    mouse: Default::default(),
863                    non_exhaustive: NonExhaustive,
864                }
865            }
866        }
867
868        impl SliderState<$tt> {
869            pub fn new() -> Self {
870                Self::new_range((<$tt>::MIN, <$tt>::MAX), 1)
871            }
872
873            pub fn named(name: &str) -> Self {
874                let mut z = Self::new_range((<$tt>::MIN, <$tt>::MAX), 1);
875                z.focus = z.focus.with_name(name);
876                z
877            }
878        }
879    };
880}
881macro_rules! slider_new_f {
882    ($tt:ty) => {
883        impl Default for SliderState<$tt> {
884            fn default() -> Self {
885                Self {
886                    area: Default::default(),
887                    inner: Default::default(),
888                    lower_bound: Default::default(),
889                    upper_bound: Default::default(),
890                    track: Default::default(),
891                    knob: Default::default(),
892                    scale_len: 0,
893                    direction: Default::default(),
894                    range: (<$tt>::MIN, <$tt>::MAX),
895                    step: 1.,
896                    long_step: None,
897                    value: Default::default(),
898                    focus: Default::default(),
899                    mouse: Default::default(),
900                    non_exhaustive: NonExhaustive,
901                }
902            }
903        }
904
905        impl SliderState<$tt> {
906            pub fn new() -> Self {
907                Self::new_range((<$tt>::MIN, <$tt>::MAX), 1.)
908            }
909        }
910    };
911}
912
913impl<T> Clone for SliderState<T>
914where
915    T: RangeOp<Step: Copy + Debug> + MapRange<u16> + Debug + Default + Copy + PartialEq,
916    u16: MapRange<T>,
917{
918    fn clone(&self) -> Self {
919        Self {
920            area: self.area,
921            inner: self.inner,
922            lower_bound: self.lower_bound,
923            upper_bound: self.upper_bound,
924            track: self.track,
925            knob: self.knob,
926            scale_len: self.scale_len,
927            direction: self.direction,
928            range: self.range,
929            step: self.step,
930            long_step: self.long_step,
931            value: self.value,
932            focus: self.focus.new_instance(),
933            mouse: Default::default(),
934            non_exhaustive: NonExhaustive,
935        }
936    }
937}
938
939slider_new!(u8);
940slider_new!(u16);
941slider_new!(u32);
942slider_new!(u64);
943slider_new!(usize);
944slider_new!(i8);
945slider_new!(i16);
946slider_new!(i32);
947slider_new!(i64);
948slider_new!(isize);
949slider_new_f!(f32);
950slider_new_f!(f64);
951
952impl<T> SliderState<T>
953where
954    T: RangeOp<Step: Copy + Debug> + MapRange<u16> + Debug + Default + Copy + PartialEq,
955    u16: MapRange<T>,
956{
957    /// New state with a given range and step.
958    ///
959    /// The range will still be overridden when set with the Widget.
960    pub fn new_range(range: (T, T), step: T::Step) -> Self {
961        Self {
962            area: Default::default(),
963            inner: Default::default(),
964            lower_bound: Default::default(),
965            upper_bound: Default::default(),
966            track: Default::default(),
967            knob: Default::default(),
968            scale_len: 0,
969            direction: Default::default(),
970            range,
971            step,
972            long_step: None,
973            value: Default::default(),
974            focus: Default::default(),
975            mouse: Default::default(),
976            non_exhaustive: NonExhaustive,
977        }
978    }
979
980    /// Set the value.
981    ///
982    /// Any value you set is good, there will be no bounds check.
983    /// Without user interaction the same value will be returned
984    /// by value().
985    pub fn set_value(&mut self, value: T) -> bool {
986        let old_value = self.value;
987        self.value = value;
988        old_value != value
989    }
990
991    /// Current value.
992    pub fn value(&self) -> T {
993        self.value
994    }
995
996    /// Set to lower bound.
997    pub fn clear(&mut self) {
998        self.value = self.range.0;
999    }
1000
1001    /// Set the range.
1002    pub fn set_range(&mut self, range: (T, T)) {
1003        self.range = range;
1004    }
1005
1006    /// Range.
1007    pub fn range(&self) -> (T, T) {
1008        self.range
1009    }
1010
1011    /// Minor step size.
1012    pub fn set_step(&mut self, step: T::Step) {
1013        self.step = step;
1014    }
1015
1016    /// Minor step size.
1017    pub fn step(&self) -> T::Step {
1018        self.step
1019    }
1020
1021    /// Major step size.
1022    pub fn set_long_step(&mut self, step: T::Step) {
1023        self.long_step = Some(step);
1024    }
1025
1026    /// Major step size.
1027    pub fn long_step(&self) -> Option<T::Step> {
1028        self.long_step
1029    }
1030
1031    /// Next value by one step.
1032    #[allow(clippy::should_implement_trait)]
1033    pub fn next(&mut self) -> bool {
1034        let old_value = self.value;
1035        self.value = self.value.add_clamp(self.step, self.range);
1036        old_value != self.value
1037    }
1038
1039    /// Previous value by one step.
1040    pub fn prev(&mut self) -> bool {
1041        let old_value = self.value;
1042        self.value = self.value.sub_clamp(self.step, self.range);
1043        old_value != self.value
1044    }
1045
1046    /// Next value by one major step.
1047    pub fn next_major(&mut self) -> bool {
1048        let old_value = self.value;
1049        if let Some(long_step) = self.long_step {
1050            self.value = self.value.add_clamp(long_step, self.range);
1051        }
1052        old_value != self.value
1053    }
1054
1055    /// Previous value by one major step.
1056    pub fn prev_major(&mut self) -> bool {
1057        let old_value = self.value;
1058        if let Some(long_step) = self.long_step {
1059            self.value = self.value.sub_clamp(long_step, self.range);
1060        }
1061        old_value != self.value
1062    }
1063
1064    /// Clicked in the range or at the boundary.
1065    /// Transforms the relative screen position to a value.
1066    pub fn clicked_at(&mut self, x: u16, y: u16) -> bool {
1067        match self.direction {
1068            Direction::Horizontal => {
1069                let x_pos = x.saturating_sub(self.track.x);
1070                if x_pos >= self.track.width {
1071                    self.value = self.range.1;
1072                    true
1073                } else if let Some(value) = x_pos.map_range((0, self.scale_len), self.range) {
1074                    self.value = value;
1075                    true
1076                } else {
1077                    false
1078                }
1079            }
1080            Direction::Vertical => {
1081                let y_pos = y.saturating_sub(self.track.y);
1082                if y_pos >= self.track.height {
1083                    self.value = self.range.1;
1084                    true
1085                } else if let Some(value) = y_pos.map_range((0, self.scale_len), self.range) {
1086                    self.value = value;
1087                    true
1088                } else {
1089                    false
1090                }
1091            }
1092        }
1093    }
1094}
1095
1096impl<T> HandleEvent<crossterm::event::Event, Regular, SliderOutcome> for SliderState<T>
1097where
1098    T: RangeOp<Step: Copy + Debug> + MapRange<u16> + Debug + Default + Copy + PartialEq,
1099    u16: MapRange<T>,
1100{
1101    fn handle(&mut self, event: &crossterm::event::Event, _qualifier: Regular) -> SliderOutcome {
1102        let r = if self.is_focused() {
1103            match event {
1104                ct_event!(keycode press CONTROL-Left)
1105                | ct_event!(keycode press CONTROL-Up)
1106                | ct_event!(keycode press Home) => {
1107                    if self.set_value(self.range.0) {
1108                        SliderOutcome::Value
1109                    } else {
1110                        SliderOutcome::Unchanged
1111                    }
1112                }
1113
1114                ct_event!(keycode press CONTROL-Right)
1115                | ct_event!(keycode press CONTROL-Down)
1116                | ct_event!(keycode press End) => {
1117                    if self.set_value(self.range.1) {
1118                        SliderOutcome::Value
1119                    } else {
1120                        SliderOutcome::Unchanged
1121                    }
1122                }
1123
1124                ct_event!(keycode press Up)
1125                | ct_event!(keycode press Left)
1126                | ct_event!(key press '-') => {
1127                    if self.prev() {
1128                        SliderOutcome::Value
1129                    } else {
1130                        SliderOutcome::Unchanged
1131                    }
1132                }
1133                ct_event!(keycode press Down)
1134                | ct_event!(keycode press Right)
1135                | ct_event!(key press '+') => {
1136                    if self.next() {
1137                        SliderOutcome::Value
1138                    } else {
1139                        SliderOutcome::Unchanged
1140                    }
1141                }
1142
1143                ct_event!(keycode press PageUp)
1144                | ct_event!(keycode press ALT-Up)
1145                | ct_event!(keycode press ALT-Left)
1146                | ct_event!(key press ALT-'-') => {
1147                    if self.prev_major() {
1148                        SliderOutcome::Value
1149                    } else {
1150                        SliderOutcome::Unchanged
1151                    }
1152                }
1153                ct_event!(keycode press PageDown)
1154                | ct_event!(keycode press ALT-Down)
1155                | ct_event!(keycode press ALT-Right)
1156                | ct_event!(key press ALT-'+') => {
1157                    if self.next_major() {
1158                        SliderOutcome::Value
1159                    } else {
1160                        SliderOutcome::Unchanged
1161                    }
1162                }
1163                _ => SliderOutcome::Continue,
1164            }
1165        } else {
1166            SliderOutcome::Continue
1167        };
1168
1169        if r == SliderOutcome::Continue {
1170            HandleEvent::handle(self, event, MouseOnly)
1171        } else {
1172            r
1173        }
1174    }
1175}
1176
1177impl<T> HandleEvent<crossterm::event::Event, MouseOnly, SliderOutcome> for SliderState<T>
1178where
1179    T: RangeOp<Step: Copy + Debug> + MapRange<u16> + Debug + Default + Copy + PartialEq,
1180    u16: MapRange<T>,
1181{
1182    fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> SliderOutcome {
1183        match event {
1184            ct_event!(mouse any for m) if self.mouse.drag(self.inner, m) => {
1185                if self.inner.contains(Position::new(m.column, m.row)) {
1186                    if self.clicked_at(m.column, m.row) {
1187                        SliderOutcome::Value
1188                    } else {
1189                        SliderOutcome::Unchanged
1190                    }
1191                } else {
1192                    SliderOutcome::Continue
1193                }
1194            }
1195            ct_event!(mouse down Left for x,y) => {
1196                if !self.gained_focus() {
1197                    if self.inner.contains(Position::new(*x, *y)) {
1198                        if self.clicked_at(*x, *y) {
1199                            SliderOutcome::Value
1200                        } else {
1201                            SliderOutcome::Unchanged
1202                        }
1203                    } else {
1204                        SliderOutcome::Continue
1205                    }
1206                } else {
1207                    SliderOutcome::Continue
1208                }
1209            }
1210            ct_event!(scroll down for x,y) => {
1211                if self.track.contains(Position::new(*x, *y)) {
1212                    if self.next() {
1213                        SliderOutcome::Value
1214                    } else {
1215                        SliderOutcome::Unchanged
1216                    }
1217                } else {
1218                    SliderOutcome::Continue
1219                }
1220            }
1221            ct_event!(scroll up for x,y) => {
1222                if self.track.contains(Position::new(*x, *y)) {
1223                    if self.prev() {
1224                        SliderOutcome::Value
1225                    } else {
1226                        SliderOutcome::Unchanged
1227                    }
1228                } else {
1229                    SliderOutcome::Continue
1230                }
1231            }
1232            ct_event!(scroll ALT down for x,y) => {
1233                if self.track.contains(Position::new(*x, *y)) {
1234                    if self.next_major() {
1235                        SliderOutcome::Value
1236                    } else {
1237                        SliderOutcome::Unchanged
1238                    }
1239                } else {
1240                    SliderOutcome::Continue
1241                }
1242            }
1243            ct_event!(scroll ALT up for x,y) => {
1244                if self.track.contains(Position::new(*x, *y)) {
1245                    if self.prev_major() {
1246                        SliderOutcome::Value
1247                    } else {
1248                        SliderOutcome::Unchanged
1249                    }
1250                } else {
1251                    SliderOutcome::Continue
1252                }
1253            }
1254            _ => SliderOutcome::Continue,
1255        }
1256    }
1257}
1258
1259/// Handle all events.
1260/// Text events are only processed if focus is true.
1261/// Mouse events are processed if they are in range.
1262pub fn handle_events<T>(
1263    state: &mut SliderState<T>,
1264    focus: bool,
1265    event: &crossterm::event::Event,
1266) -> SliderOutcome
1267where
1268    T: RangeOp<Step: Copy + Debug> + MapRange<u16> + Debug + Default + Copy + PartialEq,
1269    u16: MapRange<T>,
1270{
1271    state.focus.set(focus);
1272    HandleEvent::handle(state, event, Regular)
1273}
1274
1275/// Handle only mouse-events.
1276pub fn handle_mouse_events<T>(
1277    state: &mut SliderState<T>,
1278    event: &crossterm::event::Event,
1279) -> SliderOutcome
1280where
1281    T: RangeOp<Step: Copy + Debug> + MapRange<u16> + Debug + Default + Copy + PartialEq,
1282    u16: MapRange<T>,
1283{
1284    HandleEvent::handle(state, event, MouseOnly)
1285}