tui_lipan/widgets/
breadcrumb.rs1use std::sync::Arc;
4
5use crate::callback::Callback;
6use crate::core::element::Element;
7use crate::core::event::MouseEvent;
8use crate::style::{Align, Justify, Length, Padding, Style, StyleSlot};
9use crate::widgets::{Button, HStack, Text};
10
11#[derive(Clone)]
13pub struct Breadcrumb {
14 segments: Vec<Arc<str>>,
15 separator: Arc<str>,
16 gap: u16,
17 width: Length,
18 height: Length,
19 padding: Padding,
20 align: Align,
21 justify: Justify,
22 style: Style,
23 active: Option<usize>,
24 on_select: Option<Callback<usize>>,
25 active_style: Style,
26 inactive_style: Style,
27 hover_style: StyleSlot,
28 separator_style: Style,
29}
30
31impl Default for Breadcrumb {
32 fn default() -> Self {
33 Self {
34 segments: Vec::new(),
35 separator: "/".into(),
36 gap: 1,
37 width: Length::Flex(1),
38 height: Length::Auto,
39 padding: Padding::default(),
40 align: Align::Center,
41 justify: Justify::Start,
42 style: Style::default(),
43 active: None,
44 on_select: None,
45 active_style: Style::default(),
46 inactive_style: Style::default(),
47 hover_style: StyleSlot::Inherit,
48 separator_style: Style::default(),
49 }
50 }
51}
52
53impl Breadcrumb {
54 pub fn new() -> Self {
56 Self::default()
57 }
58
59 pub fn segments(mut self, segments: impl IntoIterator<Item = impl Into<Arc<str>>>) -> Self {
61 self.segments = segments.into_iter().map(Into::into).collect();
62 self
63 }
64
65 pub fn separator(mut self, separator: impl Into<Arc<str>>) -> Self {
67 self.separator = separator.into();
68 self
69 }
70
71 pub fn gap(mut self, gap: u16) -> Self {
73 self.gap = gap;
74 self
75 }
76
77 pub fn width(mut self, width: Length) -> Self {
79 self.width = width;
80 self
81 }
82
83 pub fn height(mut self, height: Length) -> Self {
85 self.height = height;
86 self
87 }
88
89 pub fn padding(mut self, padding: impl Into<Padding>) -> Self {
91 self.padding = padding.into();
92 self
93 }
94
95 pub fn align(mut self, align: Align) -> Self {
97 self.align = align;
98 self
99 }
100
101 pub fn justify(mut self, justify: Justify) -> Self {
103 self.justify = justify;
104 self
105 }
106
107 pub fn style(mut self, style: Style) -> Self {
109 self.style = style;
110 self
111 }
112
113 pub fn active(mut self, active: Option<usize>) -> Self {
115 self.active = active;
116 self
117 }
118
119 pub fn on_select(mut self, cb: Callback<usize>) -> Self {
121 self.on_select = Some(cb);
122 self
123 }
124
125 pub fn active_style(mut self, style: Style) -> Self {
127 self.active_style = style;
128 self
129 }
130
131 pub fn inactive_style(mut self, style: Style) -> Self {
133 self.inactive_style = style;
134 self
135 }
136
137 pub fn hover_style(mut self, style: Style) -> Self {
139 self.hover_style = StyleSlot::Replace(style);
140 self
141 }
142
143 pub fn extend_hover_style(mut self, style: Style) -> Self {
145 self.hover_style = StyleSlot::Extend(style);
146 self
147 }
148
149 pub fn inherit_hover_style(mut self) -> Self {
151 self.hover_style = StyleSlot::Inherit;
152 self
153 }
154
155 pub fn hover_style_slot(mut self, slot: StyleSlot) -> Self {
157 self.hover_style = slot;
158 self
159 }
160
161 pub fn separator_style(mut self, style: Style) -> Self {
163 self.separator_style = style;
164 self
165 }
166}
167
168impl From<Breadcrumb> for Element {
169 fn from(breadcrumb: Breadcrumb) -> Self {
170 let mut stack = HStack::new()
171 .gap(breadcrumb.gap)
172 .width(breadcrumb.width)
173 .height(breadcrumb.height)
174 .padding(breadcrumb.padding)
175 .align(breadcrumb.align)
176 .justify(breadcrumb.justify)
177 .style(breadcrumb.style);
178 let active_index = breadcrumb
179 .active
180 .unwrap_or_else(|| breadcrumb.segments.len().saturating_sub(1));
181
182 for (i, segment) in breadcrumb.segments.into_iter().enumerate() {
183 if i > 0 {
184 stack = stack.child(
185 Text::new(breadcrumb.separator.clone()).style(breadcrumb.separator_style),
186 );
187 }
188
189 let is_active = i == active_index;
190 let style = if is_active {
191 breadcrumb.active_style
192 } else {
193 breadcrumb.inactive_style
194 };
195
196 if let Some(cb) = breadcrumb.on_select.clone() {
197 let mut button = Button::filled(segment)
198 .padding(0)
199 .style(style)
200 .hover_style_slot(breadcrumb.hover_style)
201 .width(Length::Auto);
202 button = button.on_click(Callback::new(move |_: MouseEvent| cb.emit(i)));
203 stack = stack.child(button);
204 } else {
205 stack = stack.child(Text::new(segment).style(style));
206 }
207 }
208
209 stack.into()
210 }
211}