commit
1b5c4c1795
10 changed files with 707 additions and 0 deletions
@ -0,0 +1,13 @@ |
|||||||
|
[package] |
||||||
|
name = "embedded-components" |
||||||
|
version = "0.1.0" |
||||||
|
edition = "2021" |
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
||||||
|
|
||||||
|
[dependencies] |
||||||
|
embedded-graphics = "*" |
||||||
|
embedded-graphics-simulator = "*" |
||||||
|
chrono = "*" |
||||||
|
embedded-layout = "*" |
||||||
|
num-traits = "*" |
||||||
@ -0,0 +1,106 @@ |
|||||||
|
use embedded_components::{Calendar, ComponentStyle, Gauge, ScrollingCalendar}; |
||||||
|
use embedded_graphics::{ |
||||||
|
pixelcolor::{raw::RawU2, BinaryColor, Rgb888}, |
||||||
|
prelude::*, |
||||||
|
}; |
||||||
|
use embedded_graphics_simulator::{ |
||||||
|
BinaryColorTheme, OutputSettingsBuilder, SimulatorDisplay, Window, |
||||||
|
}; |
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq)] |
||||||
|
enum TriColor { |
||||||
|
White, |
||||||
|
Black, |
||||||
|
Red, |
||||||
|
} |
||||||
|
|
||||||
|
impl PixelColor for TriColor { |
||||||
|
type Raw = RawU2; |
||||||
|
} |
||||||
|
|
||||||
|
impl From<BinaryColor> for TriColor { |
||||||
|
fn from(value: BinaryColor) -> TriColor { |
||||||
|
match value { |
||||||
|
BinaryColor::On => TriColor::Black, |
||||||
|
BinaryColor::Off => TriColor::White, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Into<Rgb888> for TriColor { |
||||||
|
fn into(self) -> Rgb888 { |
||||||
|
match self { |
||||||
|
TriColor::Black => Rgb888::new(0, 0, 0), |
||||||
|
TriColor::White => Rgb888::new(255, 255, 255), |
||||||
|
TriColor::Red => Rgb888::new(180, 0, 0), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<Rgb888> for TriColor { |
||||||
|
fn from(value: Rgb888) -> TriColor { |
||||||
|
match value { |
||||||
|
_ => TriColor::White, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
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 fg_color = TriColor::Black; |
||||||
|
let bg_color = TriColor::White; |
||||||
|
let hi_color = Some(TriColor::Red); |
||||||
|
|
||||||
|
let gauge = Gauge { |
||||||
|
top_left: Point::new(300, 300), |
||||||
|
bezel: 1, |
||||||
|
border: 1, |
||||||
|
size: Size::new(200, 20), |
||||||
|
ratio: 0.5, |
||||||
|
fg_color, |
||||||
|
bg_color, |
||||||
|
text: "test test test test test", |
||||||
|
}; |
||||||
|
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, |
||||||
|
}; |
||||||
|
calendar.draw(&mut display)?; |
||||||
|
|
||||||
|
let calendar_style = ComponentStyle { |
||||||
|
border: 1, |
||||||
|
bezel: 1, |
||||||
|
fg_color, |
||||||
|
bg_color, |
||||||
|
hi_color: TriColor::Red, |
||||||
|
}; |
||||||
|
|
||||||
|
let calendar_week = ScrollingCalendar { |
||||||
|
top_left: Point::new(400, 10), |
||||||
|
size: Size::new(396, 396), |
||||||
|
style: calendar_style, |
||||||
|
day: chrono::Local::now() |
||||||
|
.naive_local() |
||||||
|
.date() |
||||||
|
.checked_add_signed(chrono::Duration::days(28 * 4 * 0 - 14)) |
||||||
|
.unwrap(), |
||||||
|
weeks: 5, |
||||||
|
}; |
||||||
|
calendar_week.draw(&mut display)?; |
||||||
|
|
||||||
|
let output_settings = OutputSettingsBuilder::new() |
||||||
|
.theme(BinaryColorTheme::Default) |
||||||
|
.build(); |
||||||
|
Window::new("Hello World", &output_settings).show_static(&display); |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
@ -0,0 +1,27 @@ |
|||||||
|
use embedded_graphics::{ |
||||||
|
pixelcolor::{BinaryColor, PixelColor}, |
||||||
|
prelude::*, |
||||||
|
primitives::{Circle, Rectangle}, |
||||||
|
}; |
||||||
|
use embedded_layout::prelude::*; |
||||||
|
|
||||||
|
pub struct BulletCounter<C> { |
||||||
|
pub top_left: Point, |
||||||
|
pub size: Size, // Available space
|
||||||
|
pub color: C, |
||||||
|
} |
||||||
|
|
||||||
|
impl<C> Drawable for BulletCounter<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>, |
||||||
|
{ |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,386 @@ |
|||||||
|
use crate::ComponentStyle; |
||||||
|
|
||||||
|
use chrono::{Datelike, Month}; |
||||||
|
use embedded_graphics::{ |
||||||
|
mono_font::{ |
||||||
|
ascii::{FONT_10X20, FONT_4X6, FONT_6X9, FONT_8X13}, |
||||||
|
MonoFont, MonoTextStyle, |
||||||
|
}, |
||||||
|
pixelcolor::{BinaryColor, PixelColor}, |
||||||
|
prelude::*, |
||||||
|
primitives::{Line, PrimitiveStyle, Rectangle}, |
||||||
|
text::Text, |
||||||
|
}; |
||||||
|
use embedded_layout::prelude::*; |
||||||
|
use num_traits::cast::FromPrimitive; |
||||||
|
|
||||||
|
fn font(height: u32) -> MonoFont<'static> { |
||||||
|
if height < 8 { |
||||||
|
return FONT_4X6; |
||||||
|
} else if height < 10 { |
||||||
|
return FONT_6X9; |
||||||
|
} else if height < 14 { |
||||||
|
return FONT_8X13; |
||||||
|
} else { |
||||||
|
return FONT_10X20; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub struct CalendarDay<C> { |
||||||
|
pub top_left: Point, |
||||||
|
pub size: Size, |
||||||
|
pub fg_color: C, |
||||||
|
pub bg_color: C, |
||||||
|
pub border: u32, |
||||||
|
pub day_of_month: u32, |
||||||
|
pub has_event: bool, |
||||||
|
} |
||||||
|
|
||||||
|
impl<C> Drawable for CalendarDay<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>, |
||||||
|
{ |
||||||
|
// Adding based on border so that the borders overlap
|
||||||
|
let date_rect = Rectangle::new( |
||||||
|
self.top_left, |
||||||
|
self.size + Size::new(self.border / 2 + 1, self.border / 2 + 1), |
||||||
|
); |
||||||
|
date_rect |
||||||
|
.into_styled(PrimitiveStyle::with_fill(self.bg_color)) |
||||||
|
.draw(target)?; |
||||||
|
date_rect |
||||||
|
.into_styled(PrimitiveStyle::with_stroke(self.fg_color, self.border)) |
||||||
|
.draw(target)?; |
||||||
|
|
||||||
|
let text = format!("{}", self.day_of_month); |
||||||
|
let date_font = font(self.size.height / 2); |
||||||
|
let text_style = MonoTextStyle::new(&date_font, self.fg_color); |
||||||
|
Text::new(&text, self.top_left, text_style) |
||||||
|
.align_to(&date_rect, horizontal::Center, vertical::Center) |
||||||
|
.draw(target)?; |
||||||
|
|
||||||
|
if self.has_event { |
||||||
|
let event_padding: i32 = 2 + self.size.height as i32 / 10; |
||||||
|
Line::new( |
||||||
|
self.top_left + Point::new(event_padding, self.size.height as i32 - event_padding), |
||||||
|
self.top_left |
||||||
|
+ Point::new( |
||||||
|
self.size.width as i32 - event_padding, |
||||||
|
self.size.height as i32 - event_padding, |
||||||
|
), |
||||||
|
) |
||||||
|
.into_styled(PrimitiveStyle::with_stroke(self.fg_color, 2)) |
||||||
|
.draw(target)?; |
||||||
|
} |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub struct CalendarMonth<C> { |
||||||
|
pub top_left: Point, |
||||||
|
pub size: Size, |
||||||
|
pub border: u32, |
||||||
|
pub fg_color: C, |
||||||
|
pub bg_color: C, |
||||||
|
pub month: chrono::Month, |
||||||
|
} |
||||||
|
|
||||||
|
impl<C> Drawable for CalendarMonth<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.border / 2 + 1, self.border / 2 + 1), |
||||||
|
); |
||||||
|
rect |
||||||
|
.into_styled(PrimitiveStyle::with_stroke(self.fg_color, self.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); |
||||||
|
Text::new(self.month.name(), self.top_left, text_style) |
||||||
|
.align_to(&rect, horizontal::Center, vertical::Center) |
||||||
|
.draw(target)?; |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
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>, |
||||||
|
} |
||||||
|
|
||||||
|
impl<C> Drawable for Calendar<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 border = self.border; |
||||||
|
|
||||||
|
let date_now = chrono::offset::Local::now().date(); |
||||||
|
let first_day = date_now.with_day(1).unwrap(); |
||||||
|
let last_day = date_now |
||||||
|
.with_day(1) |
||||||
|
.unwrap() |
||||||
|
.with_month(date_now.month() % 11 + 1) |
||||||
|
.unwrap() |
||||||
|
.pred(); |
||||||
|
|
||||||
|
let empty_squares = first_day.weekday().num_days_from_monday(); |
||||||
|
let squares_needed = empty_squares + last_day.day(); |
||||||
|
let month_needs_separate_row = empty_squares < 4; |
||||||
|
let mut rows_needed = squares_needed / 7 + 1; |
||||||
|
if month_needs_separate_row { |
||||||
|
rows_needed += 1; |
||||||
|
} |
||||||
|
|
||||||
|
let width = self.size.width / 7; |
||||||
|
let height = self.size.height / rows_needed; |
||||||
|
|
||||||
|
let month_width = if month_needs_separate_row { |
||||||
|
width * 7 |
||||||
|
} else { |
||||||
|
width * empty_squares |
||||||
|
}; |
||||||
|
let calendar_month = CalendarMonth { |
||||||
|
border, |
||||||
|
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)?; |
||||||
|
|
||||||
|
for i in empty_squares..squares_needed { |
||||||
|
let x = i % 7; |
||||||
|
let mut y = i / 7; |
||||||
|
if month_needs_separate_row { |
||||||
|
y = y + 1; |
||||||
|
} |
||||||
|
let day_of_month = i - empty_squares + 1; |
||||||
|
let is_weekend = date_now |
||||||
|
.with_day(day_of_month) |
||||||
|
.unwrap() |
||||||
|
.weekday() |
||||||
|
.num_days_from_monday() |
||||||
|
>= 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 calendar_day = CalendarDay { |
||||||
|
border, |
||||||
|
top_left: self.top_left + Point::new((width * x) as i32, (height * y) as i32), |
||||||
|
size: Size::new(width, height), |
||||||
|
fg_color, |
||||||
|
bg_color, |
||||||
|
day_of_month: i - empty_squares + 1, |
||||||
|
has_event: i - empty_squares + 1 == date_now.day(), |
||||||
|
}; |
||||||
|
calendar_day.draw(target)?; |
||||||
|
} |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub struct ScrollingWeek { |
||||||
|
day: chrono::NaiveDate, |
||||||
|
} |
||||||
|
|
||||||
|
impl ScrollingWeek { |
||||||
|
pub fn succ(&self) -> ScrollingWeek { |
||||||
|
let start_of_next_month = self |
||||||
|
.day |
||||||
|
.with_month(self.day.month() % 11 + 1) |
||||||
|
.unwrap() |
||||||
|
.with_day(1) |
||||||
|
.unwrap(); |
||||||
|
|
||||||
|
let days_until_next_month = |
||||||
|
start_of_next_month.num_days_from_ce() - self.day.num_days_from_ce(); |
||||||
|
let day_of_week = self.day.weekday().number_from_monday() as i32; |
||||||
|
|
||||||
|
let days_to_skip = if days_until_next_month < 7 { |
||||||
|
days_until_next_month |
||||||
|
} else { |
||||||
|
8 - day_of_week |
||||||
|
}; |
||||||
|
|
||||||
|
ScrollingWeek { |
||||||
|
day: self |
||||||
|
.day |
||||||
|
.checked_add_signed(chrono::Duration::days(days_to_skip as i64)) |
||||||
|
.unwrap(), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn month(&self) -> chrono::Month { |
||||||
|
chrono::Month::from_u32(self.day.month()).unwrap() |
||||||
|
} |
||||||
|
|
||||||
|
pub fn week(&self) -> u32 { |
||||||
|
self.day.iso_week().week() |
||||||
|
} |
||||||
|
|
||||||
|
pub fn year(&self) -> i32 { |
||||||
|
self.day.year() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub struct ScrollingCalendar<C> { |
||||||
|
pub top_left: Point, |
||||||
|
pub size: Size, |
||||||
|
pub style: ComponentStyle<C>, |
||||||
|
pub day: chrono::NaiveDate, |
||||||
|
pub weeks: u32, |
||||||
|
} |
||||||
|
|
||||||
|
impl<C> Drawable for ScrollingCalendar<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 height = self.size.height / (1 + self.weeks * 2); |
||||||
|
let mut week = ScrollingWeek { |
||||||
|
day: chrono::NaiveDate::from_isoywd( |
||||||
|
self.day.year(), |
||||||
|
self.day.iso_week().week(), |
||||||
|
chrono::Weekday::Mon, |
||||||
|
) |
||||||
|
.checked_add_signed(chrono::Duration::days(-7 * self.weeks as i64)) |
||||||
|
.unwrap(), |
||||||
|
}; |
||||||
|
|
||||||
|
for i in 0..self.weeks * 2 { |
||||||
|
week = week.succ(); |
||||||
|
|
||||||
|
let calendar_week = CalendarWeek { |
||||||
|
top_left: Point::new(self.top_left.x, self.top_left.y + (height * i) as i32), |
||||||
|
size: Size::new(self.size.width, height), |
||||||
|
style: self.style.clone(), |
||||||
|
year: week.year(), |
||||||
|
month: week.month(), |
||||||
|
week: week.week(), |
||||||
|
}; |
||||||
|
calendar_week.draw(target)?; |
||||||
|
} |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub struct CalendarWeek<C> { |
||||||
|
pub top_left: Point, |
||||||
|
pub size: Size, |
||||||
|
pub style: ComponentStyle<C>, |
||||||
|
pub year: i32, |
||||||
|
pub month: chrono::Month, |
||||||
|
pub week: u32, |
||||||
|
} |
||||||
|
|
||||||
|
impl<C> Drawable for CalendarWeek<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 today = chrono::Utc::now().date(); |
||||||
|
let monday = chrono::NaiveDate::from_isoywd(self.year, self.week, chrono::Weekday::Mon); |
||||||
|
let sunday = chrono::NaiveDate::from_isoywd(self.year, self.week, chrono::Weekday::Sun); |
||||||
|
let week_starts_in_prev_month = monday.month() != self.month.number_from_month(); |
||||||
|
let week_ends_in_next_month = sunday.month() != self.month.number_from_month(); |
||||||
|
let first_day = match week_starts_in_prev_month { |
||||||
|
true => sunday.with_day(1).unwrap(), |
||||||
|
false => monday, |
||||||
|
}; |
||||||
|
let last_day = match week_ends_in_next_month { |
||||||
|
true => sunday.with_day(1).unwrap().pred(), |
||||||
|
false => sunday, |
||||||
|
}; |
||||||
|
|
||||||
|
let width = self.size.width / 7; |
||||||
|
|
||||||
|
let mut weekday = first_day.weekday(); |
||||||
|
for i in first_day.weekday().number_from_monday()..(last_day.weekday().number_from_monday() + 1) |
||||||
|
{ |
||||||
|
let day = chrono::NaiveDate::from_isoywd(self.year, self.week, weekday); |
||||||
|
|
||||||
|
let (fg_color, bg_color) = match ( |
||||||
|
weekday.num_days_from_monday() < 5, |
||||||
|
day.num_days_from_ce() == today.num_days_from_ce(), |
||||||
|
) { |
||||||
|
(_, true) => (self.style.bg_color, self.style.hi_color), |
||||||
|
(true, _) => (self.style.fg_color, self.style.bg_color), |
||||||
|
(false, _) => (self.style.bg_color, self.style.fg_color), |
||||||
|
}; |
||||||
|
|
||||||
|
let style = ComponentStyle { |
||||||
|
fg_color, |
||||||
|
bg_color, |
||||||
|
..self.style |
||||||
|
}; |
||||||
|
|
||||||
|
let calendar_day = CalendarDay { |
||||||
|
top_left: self.top_left + Point::new((width * weekday.num_days_from_monday()) as i32, 0), |
||||||
|
size: Size::new(width, self.size.height), |
||||||
|
border: self.style.border, |
||||||
|
fg_color, |
||||||
|
bg_color, |
||||||
|
day_of_month: day.day(), |
||||||
|
has_event: day.num_days_from_ce() == today.num_days_from_ce(), |
||||||
|
}; |
||||||
|
calendar_day.draw(target)?; |
||||||
|
|
||||||
|
weekday = weekday.succ(); |
||||||
|
} |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,77 @@ |
|||||||
|
use embedded_graphics::{ |
||||||
|
mono_font::{ascii::FONT_6X9, MonoTextStyle}, |
||||||
|
pixelcolor::{BinaryColor, PixelColor}, |
||||||
|
prelude::*, |
||||||
|
primitives::{PrimitiveStyle, Rectangle}, |
||||||
|
text::{Baseline, Text}, |
||||||
|
}; |
||||||
|
|
||||||
|
pub struct Gauge<'a, C> { |
||||||
|
pub top_left: Point, |
||||||
|
pub bezel: u32, |
||||||
|
pub border: u32, |
||||||
|
pub size: Size, |
||||||
|
pub ratio: f32, |
||||||
|
pub fg_color: C, |
||||||
|
pub bg_color: C, |
||||||
|
pub text: &'a str, |
||||||
|
} |
||||||
|
|
||||||
|
impl<C> Drawable for Gauge<'_, 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 margin = self.border + self.bezel; |
||||||
|
|
||||||
|
Rectangle::new(self.top_left, self.size) |
||||||
|
.into_styled(PrimitiveStyle::with_stroke(self.fg_color, self.border)) |
||||||
|
.draw(target)?; |
||||||
|
|
||||||
|
let inner_size = self.size - Size::new(margin * 2, margin * 2); |
||||||
|
let inner_filled = Size::new( |
||||||
|
(inner_size.width as f32 * self.ratio).ceil() as u32, |
||||||
|
inner_size.height, |
||||||
|
); |
||||||
|
let inner_unfilled = Size::new(inner_size.width - inner_filled.width, inner_size.height); |
||||||
|
|
||||||
|
let filled_rect = Rectangle::new( |
||||||
|
self.top_left + Point::new(margin as i32, margin as i32), |
||||||
|
inner_filled, |
||||||
|
); |
||||||
|
let unfilled_rect = Rectangle::new( |
||||||
|
self.top_left + Point::new(margin as i32 + inner_filled.width as i32, margin as i32), |
||||||
|
inner_unfilled, |
||||||
|
); |
||||||
|
|
||||||
|
// Added stroke as well because sometimes it showed a 1px bezel otherwise
|
||||||
|
// This is probably because the outer border stroke extends border/2 out/inwards
|
||||||
|
filled_rect |
||||||
|
.into_styled(PrimitiveStyle::with_stroke(self.fg_color, self.border)) |
||||||
|
.draw(target)?; |
||||||
|
filled_rect |
||||||
|
.into_styled(PrimitiveStyle::with_fill(self.fg_color)) |
||||||
|
.draw(target)?; |
||||||
|
|
||||||
|
let filled_style = MonoTextStyle::new(&FONT_6X9, self.bg_color); |
||||||
|
let unfilled_style = MonoTextStyle::new(&FONT_6X9, self.fg_color); |
||||||
|
|
||||||
|
let mut text = |
||||||
|
Text::with_baseline(self.text, self.top_left, filled_style, Baseline::Middle); |
||||||
|
let text_box = text.bounding_box(); |
||||||
|
text.position = self.top_left + Point::new(margin as i32, margin as i32) + inner_size / 2 |
||||||
|
- Point::new((text_box.size.width as f32 / 2.) as i32, 0); |
||||||
|
|
||||||
|
text.draw(&mut target.clipped(&filled_rect))?; |
||||||
|
let text = Text::with_baseline(self.text, text.position, unfilled_style, Baseline::Middle); |
||||||
|
text.draw(&mut target.clipped(&unfilled_rect))?; |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,9 @@ |
|||||||
|
mod bullet_counter; |
||||||
|
mod calendar; |
||||||
|
mod gauge; |
||||||
|
mod style; |
||||||
|
|
||||||
|
pub use bullet_counter::BulletCounter; |
||||||
|
pub use calendar::{Calendar, ScrollingCalendar}; |
||||||
|
pub use gauge::Gauge; |
||||||
|
pub use style::ComponentStyle; |
||||||
@ -0,0 +1,78 @@ |
|||||||
|
use embedded_graphics::{ |
||||||
|
pixelcolor::{BinaryColor, PixelColor}, |
||||||
|
prelude::*, |
||||||
|
primitives::{Line, PrimitiveStyle, Rectangle}, |
||||||
|
text::Text, |
||||||
|
}; |
||||||
|
use embedded_layout::prelude::*; |
||||||
|
|
||||||
|
pub struct ListItemData { |
||||||
|
pub text: &'a str, |
||||||
|
pub icon: &'a str, |
||||||
|
} |
||||||
|
|
||||||
|
pub struct List<'a, C> { |
||||||
|
pub top_left: Point, |
||||||
|
pub border: u32, |
||||||
|
pub size: Size, |
||||||
|
pub fg_color: C, |
||||||
|
pub bg_color: C, |
||||||
|
pub list_items: Vec<ListItemData>, |
||||||
|
} |
||||||
|
|
||||||
|
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); |
||||||
|
rect |
||||||
|
.into_styled(PrimitiveStyle::with_stroke(self.fg_color, self.border)) |
||||||
|
.draw(target)?; |
||||||
|
rect |
||||||
|
.into_styled(PrimitiveStyle::with_fill(self.fg_color)) |
||||||
|
.draw(target)?; |
||||||
|
|
||||||
|
// TODO: draw list items
|
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub struct ListItem<'a, C> { |
||||||
|
pub top_left: Point, |
||||||
|
pub border: u32, |
||||||
|
pub size: Size, |
||||||
|
pub fg_color: C, |
||||||
|
pub bg_color: C, |
||||||
|
pub text: &'a str, |
||||||
|
} |
||||||
|
|
||||||
|
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); |
||||||
|
rect |
||||||
|
.into_styled(PrimitiveStyle::with_stroke(self.fg_color, self.border)) |
||||||
|
.draw(target)?; |
||||||
|
rect |
||||||
|
.into_styled(PrimitiveStyle::with_fill(self.fg_color)) |
||||||
|
.draw(target)?; |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue