altui_core/widgets/
tabs.rs1use crate::{
2 buffer::Buffer,
3 layout::Rect,
4 style::Style,
5 symbols,
6 text::{Span, Spans},
7 widgets::{Block, Widget},
8};
9
10#[derive(Debug, Clone)]
27pub struct Tabs<'a> {
28 block: Option<Block<'a>>,
30 titles: Vec<Spans<'a>>,
32 selected: usize,
34 style: Style,
36 highlight_style: Style,
38 divider: Span<'a>,
40}
41
42impl<'a> Tabs<'a> {
43 pub fn new(titles: Vec<Spans<'a>>) -> Tabs<'a> {
44 Tabs {
45 block: None,
46 titles,
47 selected: 0,
48 style: Default::default(),
49 highlight_style: Default::default(),
50 divider: Span::raw(symbols::line::VERTICAL),
51 }
52 }
53
54 pub fn block(&mut self, block: Block<'a>) {
55 self.block = Some(block);
56 }
57
58 pub fn select(&mut self, selected: usize) {
59 self.selected = selected;
60 }
61
62 pub fn style(&mut self, style: Style) {
63 self.style = style;
64 }
65
66 pub fn highlight_style(&mut self, style: Style) {
67 self.highlight_style = style;
68 }
69
70 pub fn divider<T>(&mut self, divider: T)
71 where
72 T: Into<Span<'a>>,
73 {
74 self.divider = divider.into();
75 }
76}
77
78impl<'a> Widget for Tabs<'a> {
79 fn render(&mut self, area: Rect, buf: &mut Buffer) {
80 buf.set_style(area, self.style);
81 let tabs_area = match self.block.as_mut() {
82 Some(b) => {
83 let inner_area = b.inner(area);
84 b.render(area, buf);
85 inner_area
86 }
87 None => area,
88 };
89
90 if tabs_area.height < 1 {
91 return;
92 }
93
94 let mut x = tabs_area.left();
95 let titles_length = self.titles.len();
96 for (i, title) in self.titles.clone().into_iter().enumerate() {
97 let last_title = titles_length - 1 == i;
98 x = x.saturating_add(1);
99 let remaining_width = tabs_area.right().saturating_sub(x);
100 if remaining_width == 0 {
101 break;
102 }
103 let pos = buf.set_spans(x, tabs_area.top(), &title, remaining_width);
104 if i == self.selected {
105 buf.set_style(
106 Rect {
107 x,
108 y: tabs_area.top(),
109 width: pos.0.saturating_sub(x),
110 height: 1,
111 },
112 self.highlight_style,
113 );
114 }
115 x = pos.0.saturating_add(1);
116 let remaining_width = tabs_area.right().saturating_sub(x);
117 if remaining_width == 0 || last_title {
118 break;
119 }
120 let pos = buf.set_span(x, tabs_area.top(), &self.divider, remaining_width);
121 x = pos.0;
122 }
123 }
124}