use crate::{get_global_color, MaterialButton};
use egui::{Response, RichText, Ui, Widget};
#[must_use = "You should put this widget in a ui with `ui.add(widget);`"]
pub struct MaterialBreadcrumbs {
items: Vec<BreadcrumbItem>,
separator: String,
font_size: f32,
spacing: f32,
show_separator: bool,
}
struct BreadcrumbItem {
text: String,
is_active: bool,
clickable: bool,
callback: Option<Box<dyn FnMut()>>,
}
impl Default for MaterialBreadcrumbs {
fn default() -> Self {
Self {
items: Vec::new(),
separator: "/".to_string(),
font_size: 14.0,
spacing: 8.0,
show_separator: true,
}
}
}
impl MaterialBreadcrumbs {
pub fn new() -> Self {
Self::default()
}
pub fn item(mut self, text: impl Into<String>) -> Self {
self.items.push(BreadcrumbItem {
text: text.into(),
is_active: false,
clickable: true,
callback: None,
});
self
}
pub fn item_with_callback<F>(mut self, text: impl Into<String>, callback: F) -> Self
where
F: FnMut() + 'static,
{
self.items.push(BreadcrumbItem {
text: text.into(),
is_active: false,
clickable: true,
callback: Some(Box::new(callback)),
});
self
}
pub fn active_item(mut self, text: impl Into<String>) -> Self {
self.items.push(BreadcrumbItem {
text: text.into(),
is_active: true,
clickable: false,
callback: None,
});
self
}
pub fn separator(mut self, separator: impl Into<String>) -> Self {
self.separator = separator.into();
self
}
pub fn font_size(mut self, size: f32) -> Self {
self.font_size = size;
self
}
pub fn spacing(mut self, spacing: f32) -> Self {
self.spacing = spacing;
self
}
pub fn hide_separator(mut self) -> Self {
self.show_separator = false;
self
}
pub fn items<I, S>(mut self, items: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
for text in items {
self.items.push(BreadcrumbItem {
text: text.into(),
is_active: false,
clickable: true,
callback: None,
});
}
self
}
}
impl Widget for MaterialBreadcrumbs {
fn ui(mut self, ui: &mut Ui) -> Response {
let on_surface_variant = get_global_color("onSurfaceVariant");
ui.horizontal_wrapped(|ui| {
ui.spacing_mut().item_spacing.x = self.spacing;
let items_count = self.items.len();
for (index, item) in self.items.iter_mut().enumerate() {
let is_last = index == items_count - 1;
if item.clickable && !item.is_active {
let button = MaterialButton::text(&item.text).small();
let response = ui.add(button);
if response.clicked() {
if let Some(callback) = &mut item.callback {
callback();
}
}
} else {
let button = MaterialButton::filled_tonal(&item.text)
.small()
.enabled(false);
ui.add(button);
}
if !is_last && self.show_separator {
let separator_text = RichText::new(&self.separator)
.color(on_surface_variant)
.size(self.font_size);
ui.label(separator_text);
}
}
})
.response
}
}
pub fn breadcrumbs() -> MaterialBreadcrumbs {
MaterialBreadcrumbs::new()
}