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