use iced::{
Border, Color, Element, Length, Theme, border,
widget::{Container, container},
};
#[derive(Clone, Copy)]
enum ContentPosition {
Top,
Bottom,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct EdgeRadii {
pub left: f32,
pub right: f32,
}
impl From<f32> for EdgeRadii {
fn from(value: f32) -> Self {
Self {
left: value,
right: value,
}
}
}
impl From<(f32, f32)> for EdgeRadii {
fn from((left, right): (f32, f32)) -> Self {
Self { left, right }
}
}
fn section_border_radius(radii: EdgeRadii, position: ContentPosition) -> border::Radius {
let (l, r) = (radii.left, radii.right);
match position {
ContentPosition::Top => border::Radius {
top_left: l,
top_right: r,
bottom_right: 0.0,
bottom_left: 0.0,
},
ContentPosition::Bottom => border::Radius {
top_left: 0.0,
top_right: 0.0,
bottom_right: r,
bottom_left: l,
},
}
}
pub fn node_header<'a, Message>(
content: impl Into<Element<'a, Message, Theme, iced::Renderer>>,
background: Color,
radii: impl Into<EdgeRadii>,
) -> Container<'a, Message, Theme, iced::Renderer>
where
Message: Clone + 'a,
{
node_section(content, background, radii.into(), ContentPosition::Top)
}
pub fn node_footer<'a, Message>(
content: impl Into<Element<'a, Message, Theme, iced::Renderer>>,
background: Color,
radii: impl Into<EdgeRadii>,
) -> Container<'a, Message, Theme, iced::Renderer>
where
Message: Clone + 'a,
{
node_section(content, background, radii.into(), ContentPosition::Bottom)
}
fn node_section<'a, Message>(
content: impl Into<Element<'a, Message, Theme, iced::Renderer>>,
background: Color,
radii: EdgeRadii,
position: ContentPosition,
) -> Container<'a, Message, Theme, iced::Renderer>
where
Message: Clone + 'a,
{
let radius = section_border_radius(radii, position);
container(content)
.width(Length::Fill)
.style(move |_theme: &Theme| container::Style {
background: Some(background.into()),
border: Border {
radius,
..Default::default()
},
..Default::default()
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn single_radius_rounds_both_corners() {
let r = section_border_radius(6.0.into(), ContentPosition::Top);
assert_eq!((r.top_left, r.top_right), (6.0, 6.0));
assert_eq!((r.bottom_left, r.bottom_right), (0.0, 0.0));
}
#[test]
fn tuple_radius_rounds_corners_independently() {
let top = section_border_radius((4.0, 8.0).into(), ContentPosition::Top);
assert_eq!((top.top_left, top.top_right), (4.0, 8.0));
let bottom = section_border_radius((4.0, 8.0).into(), ContentPosition::Bottom);
assert_eq!((bottom.bottom_left, bottom.bottom_right), (4.0, 8.0));
}
#[test]
fn header_and_footer_round_complementary_corners() {
let cr: EdgeRadii = 6.0.into();
let header = section_border_radius(cr, ContentPosition::Top);
let footer = section_border_radius(cr, ContentPosition::Bottom);
assert_eq!((header.top_left, header.top_right), (6.0, 6.0));
assert_eq!((header.bottom_left, header.bottom_right), (0.0, 0.0));
assert_eq!((footer.bottom_left, footer.bottom_right), (6.0, 6.0));
assert_eq!((footer.top_left, footer.top_right), (0.0, 0.0));
}
}