Skip to main content

ansiq_widgets/
tabs.rs

1use ansiq_core::{
2    BlockFrame, Element, ElementKind, Layout, Length, Line, SelectHandler, Span, Style, TabsProps,
3};
4
5use crate::Block;
6
7pub struct Tabs<Message = ()> {
8    block: Option<BlockFrame>,
9    titles: Vec<Line>,
10    selected: Option<usize>,
11    selection_explicit: bool,
12    highlight_style: Style,
13    divider: Span,
14    padding_left: Line,
15    padding_right: Line,
16    on_select: Option<SelectHandler<Message>>,
17    layout: Layout,
18    style: Style,
19    focusable: bool,
20}
21
22impl<Message> Default for Tabs<Message> {
23    fn default() -> Self {
24        Self {
25            block: None,
26            titles: Vec::new(),
27            selected: None,
28            selection_explicit: false,
29            highlight_style: Style::default().reversed(true),
30            divider: Span::raw(ansiq_core::symbols::line::VERTICAL),
31            padding_left: Line::from(" "),
32            padding_right: Line::from(" "),
33            on_select: None,
34            layout: Layout {
35                width: Length::Fill,
36                height: Length::Fixed(1),
37            },
38            style: Style::default(),
39            focusable: false,
40        }
41    }
42}
43
44impl Tabs<()> {
45    pub fn new<I, T>(titles: I) -> Self
46    where
47        I: IntoIterator<Item = T>,
48        T: Into<Line>,
49    {
50        Self::default().titles(titles)
51    }
52}
53
54impl<Message> Tabs<Message> {
55    pub fn block(mut self, block: Block<Message>) -> Self {
56        self.block = Some(block.into_frame());
57        self
58    }
59
60    pub fn tab<T>(mut self, title: T) -> Self
61    where
62        T: Into<Line>,
63    {
64        self.titles.push(title.into());
65        if self.titles.len() == 1 && self.selected.is_none() {
66            self.selected = Some(0);
67        }
68        self
69    }
70
71    pub fn titles<I, T>(mut self, titles: I) -> Self
72    where
73        I: IntoIterator<Item = T>,
74        T: Into<Line>,
75    {
76        self.titles = titles.into_iter().map(Into::into).collect();
77        self.selected = if self.titles.is_empty() {
78            None
79        } else {
80            self.selected
81                .map(|selected| selected.min(self.titles.len() - 1))
82                .or(Some(0))
83        };
84        self
85    }
86
87    pub fn selected(mut self, selected: Option<usize>) -> Self {
88        self.selected = selected;
89        self.selection_explicit = true;
90        self
91    }
92
93    pub fn select<T>(mut self, selected: T) -> Self
94    where
95        T: Into<Option<usize>>,
96    {
97        self.selected = selected.into();
98        self.selection_explicit = true;
99        self
100    }
101
102    pub fn highlight_style<S: Into<Style>>(mut self, style: S) -> Self {
103        self.highlight_style = style.into();
104        self
105    }
106
107    pub fn divider<D: Into<Span>>(mut self, divider: D) -> Self {
108        self.divider = divider.into();
109        self
110    }
111
112    pub fn padding<L, R>(mut self, left: L, right: R) -> Self
113    where
114        L: Into<Line>,
115        R: Into<Line>,
116    {
117        self.padding_left = left.into();
118        self.padding_right = right.into();
119        self
120    }
121
122    pub fn padding_left<P: Into<Line>>(mut self, left: P) -> Self {
123        self.padding_left = left.into();
124        self
125    }
126
127    pub fn padding_right<P: Into<Line>>(mut self, right: P) -> Self {
128        self.padding_right = right.into();
129        self
130    }
131
132    pub fn on_select<NextMessage, F>(self, handler: F) -> Tabs<NextMessage>
133    where
134        F: FnMut(usize) -> Option<NextMessage> + 'static,
135    {
136        Tabs {
137            block: self.block,
138            titles: self.titles,
139            selected: self.selected,
140            selection_explicit: self.selection_explicit,
141            highlight_style: self.highlight_style,
142            divider: self.divider,
143            padding_left: self.padding_left,
144            padding_right: self.padding_right,
145            on_select: Some(std::boxed::Box::new(handler)),
146            layout: self.layout,
147            style: self.style,
148            focusable: true,
149        }
150    }
151
152    pub fn layout(mut self, layout: Layout) -> Self {
153        self.layout = layout;
154        self
155    }
156
157    pub fn style<S: Into<Style>>(mut self, style: S) -> Self {
158        self.style = style.into();
159        self
160    }
161
162    pub fn build(self) -> Element<Message> {
163        Element::new(ElementKind::Tabs(TabsProps {
164            block: self.block,
165            titles: self.titles,
166            selected: self.selected,
167            selection_explicit: self.selection_explicit,
168            highlight_style: self.highlight_style,
169            divider: self.divider,
170            padding_left: self.padding_left,
171            padding_right: self.padding_right,
172            on_select: self.on_select,
173        }))
174        .with_layout(self.layout)
175        .with_style(self.style)
176        .with_focusable(self.focusable)
177    }
178}
179
180impl<Item> FromIterator<Item> for Tabs<()>
181where
182    Item: Into<Line>,
183{
184    fn from_iter<Iter: IntoIterator<Item = Item>>(iter: Iter) -> Self {
185        Self::new(iter)
186    }
187}