use gpui::prelude::FluentBuilder;
use gpui::*;
use crate::theme::*;
#[derive(Clone, Debug)]
pub enum BreadcrumbEvent {
ItemClicked { index: usize },
}
impl EventEmitter<BreadcrumbEvent> for Breadcrumb {}
#[derive(Clone, Debug)]
pub struct BreadcrumbItem {
pub label: String,
pub value: Option<String>,
}
impl BreadcrumbItem {
pub fn new(label: impl Into<String>) -> Self {
Self {
label: label.into(),
value: None,
}
}
pub fn with_value(label: impl Into<String>, value: impl Into<String>) -> Self {
Self {
label: label.into(),
value: Some(value.into()),
}
}
}
pub struct Breadcrumb {
items: Vec<BreadcrumbItem>,
separator: BreadcrumbSeparator,
size: ComponentSize,
custom_text_color: Option<Rgba>,
custom_separator_color: Option<Rgba>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum BreadcrumbSeparator {
Slash,
Chevron,
Arrow,
Custom(char),
}
impl Default for BreadcrumbSeparator {
fn default() -> Self {
Self::Chevron
}
}
impl BreadcrumbSeparator {
fn display(&self) -> String {
match self {
Self::Slash => "/".to_string(),
Self::Chevron => "›".to_string(),
Self::Arrow => "→".to_string(),
Self::Custom(ch) => ch.to_string(),
}
}
}
impl Breadcrumb {
pub fn new(_cx: &mut Context<Self>) -> Self {
Self {
items: Vec::new(),
separator: BreadcrumbSeparator::Chevron,
size: ComponentSize::Medium,
custom_text_color: None,
custom_separator_color: None,
}
}
pub fn items(mut self, items: Vec<BreadcrumbItem>) -> Self {
self.items = items;
self
}
pub fn separator(mut self, separator: BreadcrumbSeparator) -> Self {
self.separator = separator;
self
}
pub fn size(mut self, size: ComponentSize) -> Self {
self.size = size;
self
}
pub fn text_color(mut self, color: Rgba) -> Self {
self.custom_text_color = Some(color);
self
}
pub fn separator_color(mut self, color: Rgba) -> Self {
self.custom_separator_color = Some(color);
self
}
fn handle_item_click(&mut self, index: usize, cx: &mut Context<Self>) {
cx.emit(BreadcrumbEvent::ItemClicked { index });
cx.notify();
}
}
impl Render for Breadcrumb {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let theme = Theme::default();
let items = self.items.clone();
let separator = self.separator;
let text_size = self.size.font_size();
let text_color = self.custom_text_color.unwrap_or(theme.colors.text);
let separator_color = self.custom_separator_color.unwrap_or(theme.colors.text_secondary);
div()
.id("breadcrumb")
.flex()
.flex_row()
.items_center()
.gap_2()
.children(items.iter().enumerate().flat_map(|(index, item)| {
let is_last = index == items.len() - 1;
let item_label = item.label.clone();
let text_color = text_color;
let separator_color = separator_color;
let mut elements: Vec<AnyElement> = Vec::new();
elements.push(
div()
.text_size(text_size)
.text_color(if is_last {
text_color
} else {
text_color
})
.when(!is_last, |this| {
this.cursor(CursorStyle::PointingHand)
.hover(|style| style.text_color(theme.colors.primary))
})
.when(is_last, |this| {
this.font_weight(FontWeight::MEDIUM)
})
.on_mouse_down(MouseButton::Left, cx.listener({
let index = index;
move |this, _event: &MouseDownEvent, _window, cx| {
this.handle_item_click(index, cx);
}
}))
.child(item_label)
.into_any_element()
);
if !is_last {
elements.push(
div()
.text_size(text_size)
.text_color(separator_color)
.px(px(4.))
.child(separator.display())
.into_any_element()
);
}
elements.into_iter()
}))
}
}