use std::fmt::Debug;
use iced::{
Alignment::Center,
Color, Element, Length, Padding, Theme,
widget::{button, container, row, text},
};
use snora_core::{BreadcrumbAction, Crumb, LayoutDirection};
#[derive(Debug, PartialEq)]
pub(crate) struct CrumbGeometry {
pub(crate) gap: f32,
pub(crate) row_pad_x: f32,
pub(crate) row_pad_y: f32,
pub(crate) btn_pad_x: f32,
pub(crate) btn_pad_y: f32,
pub(crate) btn_radius: f32,
}
impl CrumbGeometry {
pub(crate) const fn unstyled() -> Self {
Self {
gap: 6.0,
row_pad_x: 12.0,
row_pad_y: 4.0,
btn_pad_x: 4.0,
btn_pad_y: 2.0,
btn_radius: 3.0,
}
}
}
pub fn app_breadcrumb<'a, Message, CrumbId, F>(
crumbs: Vec<Crumb<CrumbId>>,
on_action: &'a F,
direction: LayoutDirection,
) -> Element<'a, Message>
where
Message: Clone + 'a,
CrumbId: Clone + Debug + 'a,
F: Fn(BreadcrumbAction<CrumbId>) -> Message + 'a,
{
build_breadcrumb(crumbs, on_action, direction, CrumbGeometry::unstyled())
}
pub(crate) fn build_breadcrumb<'a, Message, CrumbId, F>(
crumbs: Vec<Crumb<CrumbId>>,
on_action: &'a F,
direction: LayoutDirection,
geometry: CrumbGeometry,
) -> Element<'a, Message>
where
Message: Clone + 'a,
CrumbId: Clone + Debug + 'a,
F: Fn(BreadcrumbAction<CrumbId>) -> Message + 'a,
{
let separator = match direction {
LayoutDirection::Ltr => "›",
LayoutDirection::Rtl => "‹",
};
let crumbs: Vec<_> = match direction {
LayoutDirection::Ltr => crumbs,
LayoutDirection::Rtl => crumbs.into_iter().rev().collect(),
};
let mut trail = row![].spacing(geometry.gap).align_y(Center);
let last = crumbs.len().saturating_sub(1);
for (i, crumb) in crumbs.into_iter().enumerate() {
trail = trail.push(render_crumb(
crumb,
on_action,
geometry.btn_pad_x,
geometry.btn_pad_y,
geometry.btn_radius,
));
if i < last {
trail = trail.push(text(separator).size(13).style(|theme: &Theme| {
iced::widget::text::Style {
color: Some(separator_color(theme)),
}
}));
}
}
container(trail)
.width(Length::Fill)
.padding(Padding::from([geometry.row_pad_y, geometry.row_pad_x]))
.into()
}
fn render_crumb<'a, Message, CrumbId, F>(
crumb: Crumb<CrumbId>,
on_action: &'a F,
btn_pad_x: f32,
btn_pad_y: f32,
btn_radius: f32,
) -> Element<'a, Message>
where
Message: Clone + 'a,
CrumbId: Clone + Debug + 'a,
F: Fn(BreadcrumbAction<CrumbId>) -> Message + 'a,
{
if crumb.is_leaf {
text(crumb.label).size(13).into()
} else {
let id_for_msg = crumb.id.clone();
button(text(crumb.label).size(13))
.on_press_with(move || on_action(BreadcrumbAction::Pressed(id_for_msg.clone())))
.padding(Padding::from([btn_pad_y, btn_pad_x]))
.style(move |theme, status| crumb_button_style(theme, status, btn_radius))
.into()
}
}
fn crumb_button_style(theme: &Theme, status: button::Status, radius: f32) -> button::Style {
use iced::{Background, Border};
let palette = theme.extended_palette();
let (background, text_color) = match status {
button::Status::Hovered | button::Status::Pressed => (
Some(Background::Color(palette.background.weak.color)),
palette.primary.base.color,
),
_ => (None, palette.primary.base.color),
};
button::Style {
background,
text_color,
border: Border {
color: Color::TRANSPARENT,
width: 0.0,
radius: radius.into(),
},
..button::Style::default()
}
}
fn separator_color(theme: &Theme) -> Color {
let p = theme.extended_palette();
let fg = p.background.base.text;
let bg = p.background.base.color;
Color {
r: fg.r * 0.5 + bg.r * 0.5,
g: fg.g * 0.5 + bg.g * 0.5,
b: fg.b * 0.5 + bg.b * 0.5,
a: 1.0,
}
}