embedded-components/src/list_item.rs

161 lines
3.9 KiB
Rust

use crate::ComponentStyle;
use embedded_graphics::{
mono_font::{
ascii::{FONT_10X20, FONT_6X13},
MonoTextStyle,
},
pixelcolor::{BinaryColor, PixelColor},
prelude::*,
primitives::{Line, PrimitiveStyle, Rectangle},
text::{Baseline, Text},
};
use embedded_layout::prelude::*;
pub struct ListItemData<'a> {
pub text: &'a str,
pub icon: &'a str,
pub progress: f32,
}
impl<'a> ListItemData<'a> {
pub fn new(icon: &'a str, text: &'a str) -> Self {
Self {
icon,
text,
progress: 0f32,
}
}
pub fn with_progress(self, progress: f32) -> Self {
Self { progress, ..self }
}
}
pub struct List<'a, C> {
pub style: ComponentStyle<C>,
pub top_left: Point,
pub size: Size,
pub items_shown: usize,
pub list_items: Vec<ListItemData<'a>>,
}
impl<C> Drawable for List<'_, C>
where
C: PixelColor + From<BinaryColor>,
{
type Color = C;
type Output = ();
fn draw<D>(&self, target: &mut D) -> Result<Self::Output, D::Error>
where
D: DrawTarget<Color = C>,
{
let rect = Rectangle::new(self.top_left, self.size);
rect
.into_styled(PrimitiveStyle::with_stroke(
self.style.bg_color,
self.style.border,
))
.draw(target)?;
rect
.into_styled(PrimitiveStyle::with_fill(self.style.bg_color))
.draw(target)?;
let height = self.size.height / self.items_shown as u32;
for (i, item) in self.list_items.iter().enumerate().take(self.items_shown) {
let list_item = ListItem {
style: self.style.clone(),
top_left: self.top_left + Point::new(0, (height as usize * i) as i32),
size: Size::new(self.size.width, height),
text: item.text,
icon: item.icon,
progress: item.progress,
};
list_item.draw(target)?;
}
Ok(())
}
}
pub struct ListItem<'a, C> {
pub style: ComponentStyle<C>,
pub top_left: Point,
pub size: Size,
pub text: &'a str,
pub icon: &'a str,
pub progress: f32,
}
impl<C> Drawable for ListItem<'_, C>
where
C: PixelColor + From<BinaryColor>,
{
type Color = C;
type Output = ();
fn draw<D>(&self, target: &mut D) -> Result<Self::Output, D::Error>
where
D: DrawTarget<Color = C>,
{
let rect = Rectangle::new(
self.top_left,
self.size + Size::new(self.style.border / 2 + 1, self.style.border / 2 + 1),
);
rect
.into_styled(PrimitiveStyle::with_fill(self.style.bg_color))
.draw(target)?;
rect
.into_styled(PrimitiveStyle::with_stroke(
self.style.border_color,
self.style.border,
))
.draw(target)?;
let icon_square = Rectangle::new(self.top_left, Size::new(rect.size.height, rect.size.height));
icon_square
.into_styled(PrimitiveStyle::with_stroke(
self.style.border_color,
self.style.border,
))
.draw(target)?;
let icon_style = MonoTextStyle::new(&FONT_10X20, self.style.hi_color);
let mut icon = Text::with_baseline(self.icon, self.top_left, icon_style, Baseline::Middle);
let icon_box = icon.bounding_box();
icon.position = self.top_left
+ Point::new(
(self.size.height / 2 - icon_box.size.width / 2) as i32,
(self.size.height / 2) as i32,
);
icon.draw(&mut target.clipped(&rect))?;
let text_style = MonoTextStyle::new(&FONT_6X13, self.style.fg_color);
let mut text = Text::with_baseline(self.text, self.top_left, text_style, Baseline::Middle);
let text_box = text.bounding_box();
text.position = self.top_left
+ Point::new(
(self.size.height as f32 * 1.2) as i32,
self.size.height as i32 / 2,
);
text.draw(&mut target.clipped(&rect))?;
if self.progress >= 100f32 {
let strikethrough = Line::new(
text.position + Point::new(0, 1),
text.position + Point::new(text_box.size.width as i32, 1),
);
strikethrough
.into_styled(PrimitiveStyle::with_stroke(self.style.fg_color, 2))
.draw(target)?;
}
Ok(())
}
}