Improve scrolling calendar
parent
1b5c4c1795
commit
28527109b6
|
|
@ -82,18 +82,19 @@ fn main() -> Result<(), core::convert::Infallible> {
|
||||||
fg_color,
|
fg_color,
|
||||||
bg_color,
|
bg_color,
|
||||||
hi_color: TriColor::Red,
|
hi_color: TriColor::Red,
|
||||||
|
border_color: TriColor::Black,
|
||||||
};
|
};
|
||||||
|
|
||||||
let calendar_week = ScrollingCalendar {
|
let calendar_week = ScrollingCalendar {
|
||||||
top_left: Point::new(400, 10),
|
top_left: Point::new(400, 10),
|
||||||
size: Size::new(396, 396),
|
size: Size::new(396, 496),
|
||||||
style: calendar_style,
|
style: calendar_style,
|
||||||
day: chrono::Local::now()
|
day: chrono::Local::now()
|
||||||
.naive_local()
|
.naive_local()
|
||||||
.date()
|
.date()
|
||||||
.checked_add_signed(chrono::Duration::days(28 * 4 * 0 - 14))
|
.checked_add_signed(chrono::Duration::days(28 * 4 * 0 - 14))
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
weeks: 5,
|
weeks: 7,
|
||||||
};
|
};
|
||||||
calendar_week.draw(&mut display)?;
|
calendar_week.draw(&mut display)?;
|
||||||
|
|
||||||
|
|
|
||||||
192
src/calendar.rs
192
src/calendar.rs
|
|
@ -27,11 +27,9 @@ fn font(height: u32) -> MonoFont<'static> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CalendarDay<C> {
|
pub struct CalendarDay<C> {
|
||||||
|
pub style: ComponentStyle<C>,
|
||||||
pub top_left: Point,
|
pub top_left: Point,
|
||||||
pub size: Size,
|
pub size: Size,
|
||||||
pub fg_color: C,
|
|
||||||
pub bg_color: C,
|
|
||||||
pub border: u32,
|
|
||||||
pub day_of_month: u32,
|
pub day_of_month: u32,
|
||||||
pub has_event: bool,
|
pub has_event: bool,
|
||||||
}
|
}
|
||||||
|
|
@ -50,18 +48,21 @@ where
|
||||||
// Adding based on border so that the borders overlap
|
// Adding based on border so that the borders overlap
|
||||||
let date_rect = Rectangle::new(
|
let date_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),
|
||||||
);
|
);
|
||||||
date_rect
|
date_rect
|
||||||
.into_styled(PrimitiveStyle::with_fill(self.bg_color))
|
.into_styled(PrimitiveStyle::with_fill(self.style.bg_color))
|
||||||
.draw(target)?;
|
.draw(target)?;
|
||||||
date_rect
|
date_rect
|
||||||
.into_styled(PrimitiveStyle::with_stroke(self.fg_color, self.border))
|
.into_styled(PrimitiveStyle::with_stroke(
|
||||||
|
self.style.border_color,
|
||||||
|
self.style.border,
|
||||||
|
))
|
||||||
.draw(target)?;
|
.draw(target)?;
|
||||||
|
|
||||||
let text = format!("{}", self.day_of_month);
|
let text = format!("{}", self.day_of_month);
|
||||||
let date_font = font(self.size.height / 2);
|
let date_font = font(self.size.height / 2);
|
||||||
let text_style = MonoTextStyle::new(&date_font, self.fg_color);
|
let text_style = MonoTextStyle::new(&date_font, self.style.fg_color);
|
||||||
Text::new(&text, self.top_left, text_style)
|
Text::new(&text, self.top_left, text_style)
|
||||||
.align_to(&date_rect, horizontal::Center, vertical::Center)
|
.align_to(&date_rect, horizontal::Center, vertical::Center)
|
||||||
.draw(target)?;
|
.draw(target)?;
|
||||||
|
|
@ -76,7 +77,7 @@ where
|
||||||
self.size.height as i32 - event_padding,
|
self.size.height as i32 - event_padding,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.into_styled(PrimitiveStyle::with_stroke(self.fg_color, 2))
|
.into_styled(PrimitiveStyle::with_stroke(self.style.fg_color, 2))
|
||||||
.draw(target)?;
|
.draw(target)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -203,12 +204,19 @@ where
|
||||||
(fg_color, bg_color, border) = (self.bg_color, self.hi_color.unwrap(), 0);
|
(fg_color, bg_color, border) = (self.bg_color, self.hi_color.unwrap(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let calendar_day = CalendarDay {
|
let style = ComponentStyle {
|
||||||
border,
|
|
||||||
top_left: self.top_left + Point::new((width * x) as i32, (height * y) as i32),
|
|
||||||
size: Size::new(width, height),
|
|
||||||
fg_color,
|
fg_color,
|
||||||
bg_color,
|
bg_color,
|
||||||
|
hi_color: fg_color,
|
||||||
|
border_color: fg_color,
|
||||||
|
border,
|
||||||
|
bezel: border,
|
||||||
|
};
|
||||||
|
|
||||||
|
let calendar_day = CalendarDay {
|
||||||
|
style,
|
||||||
|
top_left: self.top_left + Point::new((width * x) as i32, (height * y) as i32),
|
||||||
|
size: Size::new(width, height),
|
||||||
day_of_month: i - empty_squares + 1,
|
day_of_month: i - empty_squares + 1,
|
||||||
has_event: i - empty_squares + 1 == date_now.day(),
|
has_event: i - empty_squares + 1 == date_now.day(),
|
||||||
};
|
};
|
||||||
|
|
@ -219,47 +227,100 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct ScrollingWeek {
|
pub struct ScrollingWeek {
|
||||||
day: chrono::NaiveDate,
|
year: i32,
|
||||||
|
week: u32,
|
||||||
|
start_of_month: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScrollingWeek {
|
impl ScrollingWeek {
|
||||||
pub fn succ(&self) -> ScrollingWeek {
|
fn monday(&self) -> chrono::NaiveDate {
|
||||||
let start_of_next_month = self
|
chrono::NaiveDate::from_isoywd(self.year, self.week, chrono::Weekday::Mon)
|
||||||
.day
|
}
|
||||||
.with_month(self.day.month() % 11 + 1)
|
|
||||||
.unwrap()
|
fn first_day_of_week(&self) -> Option<chrono::NaiveDate> {
|
||||||
.with_day(1)
|
let monday = self.monday();
|
||||||
|
let next_monday = self
|
||||||
|
.monday()
|
||||||
|
.checked_add_signed(chrono::Duration::days(7))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let days_until_next_month =
|
if !self.start_of_month || monday.month() == next_monday.month() {
|
||||||
start_of_next_month.num_days_from_ce() - self.day.num_days_from_ce();
|
return Some(monday);
|
||||||
let day_of_week = self.day.weekday().number_from_monday() as i32;
|
}
|
||||||
|
|
||||||
let days_to_skip = if days_until_next_month < 7 {
|
if !self.start_of_month && next_monday.day() == 1 {
|
||||||
days_until_next_month
|
return None;
|
||||||
} else {
|
}
|
||||||
8 - day_of_week
|
|
||||||
};
|
return next_monday.with_day(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn last_day_of_week(&self) -> Option<chrono::NaiveDate> {
|
||||||
|
return self
|
||||||
|
.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 {
|
||||||
|
let week = date.iso_week().week();
|
||||||
|
|
||||||
ScrollingWeek {
|
ScrollingWeek {
|
||||||
day: self
|
year: date.year(),
|
||||||
.day
|
week,
|
||||||
.checked_add_signed(chrono::Duration::days(days_to_skip as i64))
|
start_of_month: date.day() < 7,
|
||||||
.unwrap(),
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_week(week: chrono::IsoWeek) -> ScrollingWeek {
|
||||||
|
let now = chrono::Local::today().naive_local();
|
||||||
|
let monday = chrono::NaiveDate::from_isoywd(now.year(), week.week(), chrono::Weekday::Mon);
|
||||||
|
|
||||||
|
ScrollingWeek {
|
||||||
|
year: now.year(),
|
||||||
|
week: week.week(),
|
||||||
|
start_of_month: monday.day() < 7,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn succ(&self) -> ScrollingWeek {
|
||||||
|
let monday = self.monday();
|
||||||
|
let next_monday = monday
|
||||||
|
.checked_add_signed(chrono::Duration::days(7))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match self.start_of_month || monday.month() == next_monday.month() {
|
||||||
|
false => ScrollingWeek {
|
||||||
|
start_of_month: true,
|
||||||
|
..*self
|
||||||
|
},
|
||||||
|
true => ScrollingWeek {
|
||||||
|
year: next_monday.year(),
|
||||||
|
week: next_monday.iso_week().week(),
|
||||||
|
start_of_month: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn month(&self) -> chrono::Month {
|
pub fn month(&self) -> chrono::Month {
|
||||||
chrono::Month::from_u32(self.day.month()).unwrap()
|
chrono::Month::from_u32(self.monday().month()).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn week(&self) -> u32 {
|
pub fn week(&self) -> u32 {
|
||||||
self.day.iso_week().week()
|
self.monday().iso_week().week()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn year(&self) -> i32 {
|
pub fn year(&self) -> i32 {
|
||||||
self.day.year()
|
self.monday().year()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -283,15 +344,7 @@ where
|
||||||
D: DrawTarget<Color = C>,
|
D: DrawTarget<Color = C>,
|
||||||
{
|
{
|
||||||
let height = self.size.height / (1 + self.weeks * 2);
|
let height = self.size.height / (1 + self.weeks * 2);
|
||||||
let mut week = ScrollingWeek {
|
let mut week = ScrollingWeek::from_date(self.day);
|
||||||
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 {
|
for i in 0..self.weeks * 2 {
|
||||||
week = week.succ();
|
week = week.succ();
|
||||||
|
|
@ -303,6 +356,7 @@ where
|
||||||
year: week.year(),
|
year: week.year(),
|
||||||
month: week.month(),
|
month: week.month(),
|
||||||
week: week.week(),
|
week: week.week(),
|
||||||
|
scroll_week: week.clone(),
|
||||||
};
|
};
|
||||||
calendar_week.draw(target)?;
|
calendar_week.draw(target)?;
|
||||||
}
|
}
|
||||||
|
|
@ -318,6 +372,7 @@ pub struct CalendarWeek<C> {
|
||||||
pub year: i32,
|
pub year: i32,
|
||||||
pub month: chrono::Month,
|
pub month: chrono::Month,
|
||||||
pub week: u32,
|
pub week: u32,
|
||||||
|
pub scroll_week: ScrollingWeek,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C> Drawable for CalendarWeek<C>
|
impl<C> Drawable for CalendarWeek<C>
|
||||||
|
|
@ -345,10 +400,55 @@ where
|
||||||
false => sunday,
|
false => sunday,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let first_day = self.scroll_week.first_day_of_week();
|
||||||
|
let last_day = self.scroll_week.last_day_of_week();
|
||||||
|
|
||||||
|
if first_day.is_none() || last_day.is_none() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let first_day = first_day.unwrap();
|
||||||
|
let last_day = last_day.unwrap();
|
||||||
|
|
||||||
let width = self.size.width / 7;
|
let width = self.size.width / 7;
|
||||||
|
|
||||||
|
if first_day.weekday().number_from_monday() > 4 {
|
||||||
|
let month = CalendarMonth {
|
||||||
|
top_left: self.top_left,
|
||||||
|
size: Size::new(
|
||||||
|
width * first_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(first_day.month()).unwrap(),
|
||||||
|
};
|
||||||
|
month.draw(target)?;
|
||||||
|
} else if last_day.weekday().number_from_monday() < 4 {
|
||||||
|
let month = CalendarMonth {
|
||||||
|
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))
|
||||||
|
.unwrap()
|
||||||
|
.month(),
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
};
|
||||||
|
month.draw(target)?;
|
||||||
|
}
|
||||||
|
|
||||||
let mut weekday = first_day.weekday();
|
let mut weekday = first_day.weekday();
|
||||||
for i in first_day.weekday().number_from_monday()..(last_day.weekday().number_from_monday() + 1)
|
for _ 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 day = chrono::NaiveDate::from_isoywd(self.year, self.week, weekday);
|
||||||
|
|
||||||
|
|
@ -368,11 +468,9 @@ where
|
||||||
};
|
};
|
||||||
|
|
||||||
let calendar_day = CalendarDay {
|
let calendar_day = CalendarDay {
|
||||||
|
style,
|
||||||
top_left: self.top_left + Point::new((width * weekday.num_days_from_monday()) as i32, 0),
|
top_left: self.top_left + Point::new((width * weekday.num_days_from_monday()) as i32, 0),
|
||||||
size: Size::new(width, self.size.height),
|
size: Size::new(width, self.size.height),
|
||||||
border: self.style.border,
|
|
||||||
fg_color,
|
|
||||||
bg_color,
|
|
||||||
day_of_month: day.day(),
|
day_of_month: day.day(),
|
||||||
has_event: day.num_days_from_ce() == today.num_days_from_ce(),
|
has_event: day.num_days_from_ce() == today.num_days_from_ce(),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ pub struct ComponentStyle<C> {
|
||||||
pub fg_color: C,
|
pub fg_color: C,
|
||||||
pub bg_color: C,
|
pub bg_color: C,
|
||||||
pub hi_color: C,
|
pub hi_color: C,
|
||||||
|
pub border_color: C,
|
||||||
pub border: u32,
|
pub border: u32,
|
||||||
pub bezel: u32,
|
pub bezel: u32,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue