Add item list
parent
3b0a5836fa
commit
86c23c0e5c
|
|
@ -1,4 +1,6 @@
|
|||
use embedded_components::{Calendar, ComponentStyle, Gauge, ScrollingCalendar};
|
||||
use embedded_components::{
|
||||
Calendar, ComponentStyle, Gauge, List, ListItem, ListItemData, ScrollingCalendar,
|
||||
};
|
||||
use embedded_graphics::{
|
||||
pixelcolor::{raw::RawU2, BinaryColor, Rgb888},
|
||||
prelude::*,
|
||||
|
|
@ -48,12 +50,21 @@ impl From<Rgb888> for TriColor {
|
|||
fn main() -> Result<(), core::convert::Infallible> {
|
||||
//let mut display = SimulatorDisplay::<BinaryColor>::new(Size::new(212, 104));
|
||||
//let mut display = SimulatorDisplay::<BinaryColor>::new(Size::new(800, 480));
|
||||
let mut display = SimulatorDisplay::<TriColor>::new(Size::new(800, 480));
|
||||
let mut display = SimulatorDisplay::<TriColor>::new(Size::new(600, 448));
|
||||
|
||||
let fg_color = TriColor::Black;
|
||||
let bg_color = TriColor::White;
|
||||
let hi_color = Some(TriColor::Red);
|
||||
|
||||
let calendar_style = ComponentStyle {
|
||||
border: 1,
|
||||
bezel: 1,
|
||||
fg_color,
|
||||
bg_color,
|
||||
hi_color: TriColor::Red,
|
||||
border_color: TriColor::Black,
|
||||
};
|
||||
|
||||
let gauge = Gauge {
|
||||
top_left: Point::new(300, 300),
|
||||
bezel: 1,
|
||||
|
|
@ -64,30 +75,30 @@ fn main() -> Result<(), core::convert::Infallible> {
|
|||
bg_color,
|
||||
text: "test test test test test",
|
||||
};
|
||||
gauge.draw(&mut display)?;
|
||||
//gauge.draw(&mut display)?;
|
||||
|
||||
let calendar = Calendar {
|
||||
top_left: Point::new(10, 10),
|
||||
size: Size::new(196, 196),
|
||||
border: 1,
|
||||
fg_color,
|
||||
bg_color,
|
||||
hi_color,
|
||||
style: calendar_style.clone(),
|
||||
};
|
||||
calendar.draw(&mut display)?;
|
||||
//calendar.draw(&mut display)?;
|
||||
|
||||
let calendar_style = ComponentStyle {
|
||||
border: 1,
|
||||
bezel: 1,
|
||||
fg_color,
|
||||
bg_color,
|
||||
hi_color: TriColor::Red,
|
||||
border_color: TriColor::Black,
|
||||
let list = List {
|
||||
top_left: Point::new(320, 0),
|
||||
size: Size::new(280, 448),
|
||||
style: calendar_style.clone(),
|
||||
items_shown: 10,
|
||||
list_items: vec![
|
||||
ListItemData::new("I", "Test task").with_progress(100f32),
|
||||
ListItemData::new("O", "Other test task"),
|
||||
],
|
||||
};
|
||||
list.draw(&mut display)?;
|
||||
|
||||
let calendar_week = ScrollingCalendar {
|
||||
top_left: Point::new(400, 0),
|
||||
size: Size::new(396, 480),
|
||||
top_left: Point::new(0, 0),
|
||||
size: Size::new(320, 448),
|
||||
style: calendar_style,
|
||||
day: chrono::Local::now().naive_local().date(),
|
||||
//.checked_add_signed(chrono::Duration::days(28 * 4 * 0 - 14))
|
||||
|
|
|
|||
|
|
@ -88,9 +88,7 @@ where
|
|||
pub struct CalendarMonth<C> {
|
||||
pub top_left: Point,
|
||||
pub size: Size,
|
||||
pub border: u32,
|
||||
pub fg_color: C,
|
||||
pub bg_color: C,
|
||||
pub style: ComponentStyle<C>,
|
||||
pub month: chrono::Month,
|
||||
}
|
||||
|
||||
|
|
@ -107,14 +105,17 @@ where
|
|||
{
|
||||
let rect = Rectangle::new(
|
||||
self.top_left,
|
||||
self.size + Size::new(self.border / 2 + 1, self.border / 2 + 1),
|
||||
self.size + Size::new(self.style.border / 2 + 1, self.style.border / 2 + 1),
|
||||
);
|
||||
rect
|
||||
.into_styled(PrimitiveStyle::with_stroke(self.fg_color, self.border))
|
||||
.into_styled(PrimitiveStyle::with_stroke(
|
||||
self.style.fg_color,
|
||||
self.style.border,
|
||||
))
|
||||
.draw(target)?;
|
||||
|
||||
let month_font = font((self.size.height as f32 * 0.8 - 2.) as u32);
|
||||
let text_style = MonoTextStyle::new(&month_font, self.fg_color);
|
||||
let text_style = MonoTextStyle::new(&month_font, self.style.fg_color);
|
||||
Text::new(self.month.name(), self.top_left, text_style)
|
||||
.align_to(&rect, horizontal::Center, vertical::Center)
|
||||
.draw(target)?;
|
||||
|
|
@ -126,11 +127,7 @@ where
|
|||
pub struct Calendar<C> {
|
||||
pub top_left: Point,
|
||||
pub size: Size,
|
||||
pub border: u32,
|
||||
pub fg_color: C,
|
||||
pub bg_color: C,
|
||||
// If hi_color is None, the fg and bg colors are inverted instead.
|
||||
pub hi_color: Option<C>,
|
||||
pub style: ComponentStyle<C>,
|
||||
}
|
||||
|
||||
impl<C> Drawable for Calendar<C>
|
||||
|
|
@ -144,7 +141,7 @@ where
|
|||
where
|
||||
D: DrawTarget<Color = C>,
|
||||
{
|
||||
let border = self.border;
|
||||
let border = self.style.border;
|
||||
|
||||
let date_now = chrono::offset::Local::now().date();
|
||||
let first_day = date_now.with_day(1).unwrap();
|
||||
|
|
@ -171,12 +168,11 @@ where
|
|||
} else {
|
||||
width * empty_squares
|
||||
};
|
||||
|
||||
let calendar_month = CalendarMonth {
|
||||
border,
|
||||
style: self.style.clone(),
|
||||
top_left: self.top_left,
|
||||
size: Size::new(month_width, height),
|
||||
fg_color: self.fg_color,
|
||||
bg_color: self.bg_color,
|
||||
month: Month::from_u32(date_now.month()).unwrap(),
|
||||
};
|
||||
calendar_month.draw(target)?;
|
||||
|
|
@ -196,22 +192,19 @@ where
|
|||
>= 5;
|
||||
let is_today = day_of_month == date_now.day();
|
||||
|
||||
let (mut fg_color, mut bg_color, mut border) = match is_weekend ^ is_today {
|
||||
false => (self.fg_color, self.bg_color, border),
|
||||
true => (self.bg_color, self.fg_color, 0),
|
||||
};
|
||||
if is_today && self.hi_color.is_some() {
|
||||
(fg_color, bg_color, border) = (self.bg_color, self.hi_color.unwrap(), 0);
|
||||
}
|
||||
let style = self.style.clone();
|
||||
|
||||
let style = ComponentStyle {
|
||||
fg_color,
|
||||
bg_color,
|
||||
hi_color: fg_color,
|
||||
border_color: fg_color,
|
||||
border,
|
||||
bezel: border,
|
||||
let mut style = match is_weekend ^ is_today {
|
||||
false => style.with_border(border),
|
||||
true => style.invert().with_border(0),
|
||||
};
|
||||
if is_today && self.style.hi_color != self.style.bg_color {
|
||||
style = ComponentStyle {
|
||||
fg_color: style.hi_color,
|
||||
bg_color: style.fg_color,
|
||||
..style
|
||||
}
|
||||
}
|
||||
|
||||
let calendar_day = CalendarDay {
|
||||
style,
|
||||
|
|
@ -262,13 +255,6 @@ impl ScrollingWeek {
|
|||
.succ()
|
||||
.first_day_of_week()?
|
||||
.checked_sub_signed(chrono::Duration::days(1));
|
||||
|
||||
match self.succ().first_day_of_week() {
|
||||
None => self
|
||||
.first_day_of_week()?
|
||||
.checked_add_signed(chrono::Duration::days(6)),
|
||||
Some(date) => date.checked_sub_signed(chrono::Duration::days(1)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_date(date: chrono::NaiveDate) -> ScrollingWeek {
|
||||
|
|
@ -419,28 +405,24 @@ where
|
|||
|
||||
if first_day.weekday().number_from_monday() > 4 {
|
||||
let month = CalendarMonth {
|
||||
style: self.style.clone(),
|
||||
top_left: self.top_left,
|
||||
size: Size::new(
|
||||
width * first_day.weekday().number_from_monday(),
|
||||
width * (first_day.weekday().number_from_monday() - 1),
|
||||
self.size.height,
|
||||
),
|
||||
border: self.style.border,
|
||||
fg_color: self.style.fg_color,
|
||||
bg_color: self.style.bg_color,
|
||||
month: chrono::Month::from_u32(first_day.month()).unwrap(),
|
||||
};
|
||||
month.draw(target)?;
|
||||
} else if last_day.weekday().number_from_monday() < 4 {
|
||||
let month = CalendarMonth {
|
||||
style: self.style.clone(),
|
||||
top_left: self.top_left
|
||||
+ Point::new((width * last_day.weekday().number_from_monday()) as i32, 0),
|
||||
size: Size::new(
|
||||
width * (7 - last_day.weekday().number_from_monday()),
|
||||
self.size.height,
|
||||
),
|
||||
border: self.style.border,
|
||||
fg_color: self.style.fg_color,
|
||||
bg_color: self.style.bg_color,
|
||||
month: chrono::Month::from_u32(
|
||||
last_day
|
||||
.checked_add_signed(chrono::Duration::days(1))
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
mod bullet_counter;
|
||||
mod calendar;
|
||||
mod gauge;
|
||||
mod list_item;
|
||||
mod style;
|
||||
|
||||
pub use bullet_counter::BulletCounter;
|
||||
pub use calendar::{Calendar, ScrollingCalendar};
|
||||
pub use gauge::Gauge;
|
||||
pub use list_item::{List, ListItem, ListItemData};
|
||||
pub use style::ComponentStyle;
|
||||
|
|
|
|||
116
src/list_item.rs
116
src/list_item.rs
|
|
@ -1,26 +1,45 @@
|
|||
use crate::ComponentStyle;
|
||||
use embedded_graphics::{
|
||||
mono_font::{
|
||||
ascii::{FONT_10X20, FONT_6X13},
|
||||
MonoTextStyle,
|
||||
},
|
||||
pixelcolor::{BinaryColor, PixelColor},
|
||||
prelude::*,
|
||||
primitives::{Line, PrimitiveStyle, Rectangle},
|
||||
text::Text,
|
||||
text::{Baseline, Text},
|
||||
};
|
||||
use embedded_layout::prelude::*;
|
||||
|
||||
pub struct ListItemData {
|
||||
pub text: &'a str,
|
||||
pub icon: &'a str,
|
||||
pub text: String,
|
||||
pub icon: String,
|
||||
pub progress: f32,
|
||||
}
|
||||
|
||||
pub struct List<'a, C> {
|
||||
impl<'a> ListItemData {
|
||||
pub fn new(icon: &'a str, text: &'a str) -> Self {
|
||||
Self {
|
||||
icon: String::from(icon),
|
||||
text: String::from(text),
|
||||
progress: 0f32,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_progress(self, progress: f32) -> Self {
|
||||
Self { progress, ..self }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct List<C> {
|
||||
pub style: ComponentStyle<C>,
|
||||
pub top_left: Point,
|
||||
pub border: u32,
|
||||
pub size: Size,
|
||||
pub fg_color: C,
|
||||
pub bg_color: C,
|
||||
pub items_shown: usize,
|
||||
pub list_items: Vec<ListItemData>,
|
||||
}
|
||||
|
||||
impl<C> Drawable for ListItem<'_, C>
|
||||
impl<C> Drawable for List<C>
|
||||
where
|
||||
C: PixelColor + From<BinaryColor>,
|
||||
{
|
||||
|
|
@ -33,25 +52,40 @@ where
|
|||
{
|
||||
let rect = Rectangle::new(self.top_left, self.size);
|
||||
rect
|
||||
.into_styled(PrimitiveStyle::with_stroke(self.fg_color, self.border))
|
||||
.into_styled(PrimitiveStyle::with_stroke(
|
||||
self.style.bg_color,
|
||||
self.style.border,
|
||||
))
|
||||
.draw(target)?;
|
||||
rect
|
||||
.into_styled(PrimitiveStyle::with_fill(self.fg_color))
|
||||
.into_styled(PrimitiveStyle::with_fill(self.style.bg_color))
|
||||
.draw(target)?;
|
||||
|
||||
// TODO: draw list items
|
||||
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 border: u32,
|
||||
pub size: Size,
|
||||
pub fg_color: C,
|
||||
pub bg_color: C,
|
||||
pub text: &'a str,
|
||||
pub icon: &'a str,
|
||||
pub progress: f32,
|
||||
}
|
||||
|
||||
impl<C> Drawable for ListItem<'_, C>
|
||||
|
|
@ -65,14 +99,62 @@ where
|
|||
where
|
||||
D: DrawTarget<Color = C>,
|
||||
{
|
||||
let rect = Rectangle::new(self.top_left, self.size);
|
||||
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_stroke(self.fg_color, self.border))
|
||||
.into_styled(PrimitiveStyle::with_fill(self.style.bg_color))
|
||||
.draw(target)?;
|
||||
rect
|
||||
.into_styled(PrimitiveStyle::with_fill(self.fg_color))
|
||||
.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(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
14
src/style.rs
14
src/style.rs
|
|
@ -7,3 +7,17 @@ pub struct ComponentStyle<C> {
|
|||
pub border: u32,
|
||||
pub bezel: u32,
|
||||
}
|
||||
|
||||
impl<C> ComponentStyle<C> {
|
||||
pub fn invert(self) -> Self {
|
||||
ComponentStyle {
|
||||
fg_color: self.bg_color,
|
||||
bg_color: self.fg_color,
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_border(self, border: u32) -> Self {
|
||||
ComponentStyle { border, ..self }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue