Browse Source

Improve scrolling calendar

master
Ilya 4 years ago
parent
commit
3b0a5836fa
  1. 13
      examples/test.rs
  2. 197
      src/calendar.rs
  3. 1
      src/style.rs

13
examples/test.rs

@ -82,17 +82,16 @@ 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, 0),
size: Size::new(396, 396), size: Size::new(396, 480),
style: calendar_style, style: calendar_style,
day: chrono::Local::now() day: chrono::Local::now().naive_local().date(),
.naive_local() //.checked_add_signed(chrono::Duration::days(28 * 4 * 0 - 14))
.date() //.unwrap(),
.checked_add_signed(chrono::Duration::days(28 * 4 * 0 - 14))
.unwrap(),
weeks: 5, weeks: 5,
}; };
calendar_week.draw(&mut display)?; calendar_week.draw(&mut display)?;

197
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 {
fg_color,
bg_color,
hi_color: fg_color,
border_color: fg_color,
border, border,
bezel: border,
};
let calendar_day = CalendarDay {
style,
top_left: self.top_left + Point::new((width * x) as i32, (height * y) as i32), top_left: self.top_left + Point::new((width * x) as i32, (height * y) as i32),
size: Size::new(width, height), size: Size::new(width, height),
fg_color,
bg_color,
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,17 +344,14 @@ 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(
day: chrono::NaiveDate::from_isoywd( self
self.day.year(), .day
self.day.iso_week().week(), .checked_sub_signed(chrono::Duration::days(self.weeks as i64 * 7))
chrono::Weekday::Mon, .unwrap(),
) );
.checked_add_signed(chrono::Duration::days(-7 * self.weeks as i64))
.unwrap(),
};
for i in 0..self.weeks * 2 { for i in 0..(1 + self.weeks * 2) {
week = week.succ(); week = week.succ();
let calendar_week = CalendarWeek { let calendar_week = CalendarWeek {
@ -303,6 +361,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 +377,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 +405,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 +473,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(),
}; };

1
src/style.rs

@ -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…
Cancel
Save