Skip to main content

ansiq_widgets/
scrollbar.rs

1use ansiq_core::{
2    Element, ElementKind, Layout, Length, ScrollHandler, ScrollbarOrientation, ScrollbarProps,
3    ScrollbarState, Style, symbols::scrollbar::Set as ScrollbarSymbolSet,
4};
5
6fn default_symbols(orientation: ScrollbarOrientation) -> ScrollbarSymbolSet {
7    if orientation.is_vertical() {
8        ansiq_core::symbols::scrollbar::DOUBLE_VERTICAL
9    } else {
10        ansiq_core::symbols::scrollbar::DOUBLE_HORIZONTAL
11    }
12}
13
14pub struct Scrollbar<Message = ()> {
15    orientation: ScrollbarOrientation,
16    state: ScrollbarState,
17    thumb_symbol: String,
18    thumb_style: Style,
19    track_symbol: Option<String>,
20    track_style: Style,
21    begin_symbol: Option<String>,
22    begin_style: Style,
23    end_symbol: Option<String>,
24    end_style: Style,
25    on_scroll: Option<ScrollHandler<Message>>,
26    layout: Layout,
27    style: Style,
28    focusable: bool,
29}
30
31impl<Message> Default for Scrollbar<Message> {
32    fn default() -> Self {
33        Self::new(ScrollbarOrientation::VerticalRight)
34    }
35}
36
37impl<Message> Scrollbar<Message> {
38    pub fn new(orientation: ScrollbarOrientation) -> Self {
39        let symbols = default_symbols(orientation);
40        Self {
41            orientation,
42            state: ScrollbarState::default(),
43            thumb_symbol: symbols.thumb.to_string(),
44            thumb_style: Style::default(),
45            track_symbol: Some(symbols.track.to_string()),
46            track_style: Style::default(),
47            begin_symbol: Some(symbols.begin.to_string()),
48            begin_style: Style::default(),
49            end_symbol: Some(symbols.end.to_string()),
50            end_style: Style::default(),
51            on_scroll: None,
52            layout: match orientation {
53                ScrollbarOrientation::VerticalRight | ScrollbarOrientation::VerticalLeft => {
54                    Layout {
55                        width: Length::Fixed(1),
56                        height: Length::Fill,
57                    }
58                }
59                ScrollbarOrientation::HorizontalBottom | ScrollbarOrientation::HorizontalTop => {
60                    Layout {
61                        width: Length::Fill,
62                        height: Length::Fixed(1),
63                    }
64                }
65            },
66            style: Style::default(),
67            focusable: false,
68        }
69    }
70
71    pub fn state(mut self, state: ScrollbarState) -> Self {
72        self.state = state;
73        self
74    }
75
76    pub fn position(mut self, position: usize) -> Self {
77        self.state = self.state.position(position);
78        self
79    }
80
81    pub fn content_length(mut self, content_length: usize) -> Self {
82        self.state = self.state.content_length(content_length);
83        self
84    }
85
86    pub fn viewport_length(self, viewport_length: usize) -> Self {
87        self.viewport_content_length(viewport_length)
88    }
89
90    pub fn viewport_content_length(mut self, viewport_length: usize) -> Self {
91        self.state = self.state.viewport_content_length(viewport_length);
92        self
93    }
94
95    pub fn orientation(mut self, orientation: ScrollbarOrientation) -> Self {
96        self.orientation = orientation;
97        let symbols = default_symbols(orientation);
98        self = self.symbols(symbols);
99        self.layout = match orientation {
100            ScrollbarOrientation::VerticalRight | ScrollbarOrientation::VerticalLeft => Layout {
101                width: Length::Fixed(1),
102                height: Length::Fill,
103            },
104            ScrollbarOrientation::HorizontalBottom | ScrollbarOrientation::HorizontalTop => {
105                Layout {
106                    width: Length::Fill,
107                    height: Length::Fixed(1),
108                }
109            }
110        };
111        self
112    }
113
114    pub fn orientation_and_symbol(
115        mut self,
116        orientation: ScrollbarOrientation,
117        symbols: ScrollbarSymbolSet,
118    ) -> Self {
119        self.orientation = orientation;
120        self = self.symbols(symbols);
121        self.layout = match orientation {
122            ScrollbarOrientation::VerticalRight | ScrollbarOrientation::VerticalLeft => Layout {
123                width: Length::Fixed(1),
124                height: Length::Fill,
125            },
126            ScrollbarOrientation::HorizontalBottom | ScrollbarOrientation::HorizontalTop => {
127                Layout {
128                    width: Length::Fill,
129                    height: Length::Fixed(1),
130                }
131            }
132        };
133        self
134    }
135
136    pub fn symbols(mut self, symbols: ScrollbarSymbolSet) -> Self {
137        self.thumb_symbol = symbols.thumb.to_string();
138        if self.track_symbol.is_some() {
139            self.track_symbol = Some(symbols.track.to_string());
140        }
141        if self.begin_symbol.is_some() {
142            self.begin_symbol = Some(symbols.begin.to_string());
143        }
144        if self.end_symbol.is_some() {
145            self.end_symbol = Some(symbols.end.to_string());
146        }
147        self
148    }
149
150    pub fn thumb_symbol(mut self, thumb_symbol: impl Into<String>) -> Self {
151        self.thumb_symbol = thumb_symbol.into();
152        self
153    }
154
155    pub fn thumb_style<S: Into<Style>>(mut self, style: S) -> Self {
156        self.thumb_style = style.into();
157        self
158    }
159
160    pub fn track_symbol<S: Into<String>>(mut self, track_symbol: Option<S>) -> Self {
161        self.track_symbol = track_symbol.map(Into::into);
162        self
163    }
164
165    pub fn track_style<S: Into<Style>>(mut self, style: S) -> Self {
166        self.track_style = style.into();
167        self
168    }
169
170    pub fn begin_symbol<S: Into<String>>(mut self, begin_symbol: Option<S>) -> Self {
171        self.begin_symbol = begin_symbol.map(Into::into);
172        self
173    }
174
175    pub fn begin_style<S: Into<Style>>(mut self, style: S) -> Self {
176        self.begin_style = style.into();
177        self
178    }
179
180    pub fn end_symbol<S: Into<String>>(mut self, end_symbol: Option<S>) -> Self {
181        self.end_symbol = end_symbol.map(Into::into);
182        self
183    }
184
185    pub fn end_style<S: Into<Style>>(mut self, style: S) -> Self {
186        self.end_style = style.into();
187        self
188    }
189
190    pub fn on_scroll<F>(mut self, handler: F) -> Self
191    where
192        F: FnMut(usize) -> Option<Message> + 'static,
193    {
194        self.on_scroll = Some(std::boxed::Box::new(handler));
195        self.focusable = true;
196        self
197    }
198
199    pub fn layout(mut self, layout: Layout) -> Self {
200        self.layout = layout;
201        self
202    }
203
204    pub fn style<S: Into<Style>>(mut self, style: S) -> Self {
205        let style = style.into();
206        self.style = style;
207        self.thumb_style = style;
208        self.track_style = style;
209        self.begin_style = style;
210        self.end_style = style;
211        self
212    }
213
214    pub fn build(self) -> Element<Message> {
215        Element::new(ElementKind::Scrollbar(ScrollbarProps {
216            orientation: self.orientation,
217            state: self.state,
218            thumb_symbol: self.thumb_symbol,
219            thumb_style: self.thumb_style,
220            track_symbol: self.track_symbol,
221            track_style: self.track_style,
222            begin_symbol: self.begin_symbol,
223            begin_style: self.begin_style,
224            end_symbol: self.end_symbol,
225            end_style: self.end_style,
226            on_scroll: self.on_scroll,
227        }))
228        .with_layout(self.layout)
229        .with_style(self.style)
230        .with_focusable(self.focusable)
231    }
232}