From 5562b57ea5dd93716aecba2b31797614a79da0c6 Mon Sep 17 00:00:00 2001 From: Ilya Date: Wed, 13 Apr 2022 12:44:53 +0200 Subject: [PATCH] Add item list --- examples/test.rs | 45 +++++++++++------- src/calendar.rs | 68 ++++++++++----------------- src/lib.rs | 2 + src/list_item.rs | 116 ++++++++++++++++++++++++++++++++++++++++------- src/style.rs | 14 ++++++ 5 files changed, 168 insertions(+), 77 deletions(-) diff --git a/examples/test.rs b/examples/test.rs index 156534d..3e4d8ac 100644 --- a/examples/test.rs +++ b/examples/test.rs @@ -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 for TriColor { fn main() -> Result<(), core::convert::Infallible> { //let mut display = SimulatorDisplay::::new(Size::new(212, 104)); //let mut display = SimulatorDisplay::::new(Size::new(800, 480)); - let mut display = SimulatorDisplay::::new(Size::new(800, 480)); + let mut display = SimulatorDisplay::::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)) diff --git a/src/calendar.rs b/src/calendar.rs index 0ccb8cf..8a702ef 100644 --- a/src/calendar.rs +++ b/src/calendar.rs @@ -88,9 +88,7 @@ where pub struct CalendarMonth { pub top_left: Point, pub size: Size, - pub border: u32, - pub fg_color: C, - pub bg_color: C, + pub style: ComponentStyle, 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 { 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, + pub style: ComponentStyle, } impl Drawable for Calendar @@ -144,7 +141,7 @@ where where D: DrawTarget, { - 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)) diff --git a/src/lib.rs b/src/lib.rs index f61eb47..e08a390 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; diff --git a/src/list_item.rs b/src/list_item.rs index 9bdcd0f..8c796d2 100644 --- a/src/list_item.rs +++ b/src/list_item.rs @@ -1,26 +1,45 @@ +use crate::ComponentStyle; use embedded_graphics::{ + mono_font::{ + ascii::{FONT_10X20, FONT_7X14}, + 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, +} + +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<'a, C> { +pub struct List { + pub style: ComponentStyle, 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, } -impl Drawable for ListItem<'_, C> +impl Drawable for List where C: PixelColor + From, { @@ -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, 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 Drawable for ListItem<'_, C> @@ -65,14 +99,62 @@ where where D: DrawTarget, { - 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_10X20, 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(()) } } diff --git a/src/style.rs b/src/style.rs index d2edfd9..6f61ba5 100644 --- a/src/style.rs +++ b/src/style.rs @@ -7,3 +7,17 @@ pub struct ComponentStyle { pub border: u32, pub bezel: u32, } + +impl ComponentStyle { + 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 } + } +}