Files
wldash/src/widgets/calendar.rs
T

346 lines
10 KiB
Rust
Raw Normal View History

2023-01-16 00:25:34 +01:00
use std::default::Default;
use crate::{
buffer::BufferView,
color::Color,
2023-02-02 23:52:59 +01:00
event::Event,
2023-02-08 22:53:41 +01:00
fonts::FontMap,
2023-01-16 00:25:34 +01:00
widgets::{Geometry, Widget},
};
use chrono::{Datelike, Local, NaiveDate};
pub struct Calendar {
font: &'static str,
2023-02-08 22:53:41 +01:00
month_font: &'static str,
2023-01-16 00:25:34 +01:00
sections_x: i32,
sections_y: i32,
2023-02-02 23:52:59 +01:00
shown_sections_x: u32,
shown_sections_y: u32,
2023-01-16 00:25:34 +01:00
size: f32,
dirty: bool,
geometry: Geometry,
2023-02-08 22:53:41 +01:00
year_width: u32,
date_width: u32,
day_width: u32,
2023-01-16 00:25:34 +01:00
}
2023-02-08 22:53:41 +01:00
const DAY_FACTOR: f32 = 0.6;
const YEAR_FACTOR: f32 = 0.75;
const MONTH_FACTOR: f32 = 1.25;
const DATE_FACTOR: f32 = 1.;
const LINE_HEIGHT: f32 = 0.85;
2023-02-02 23:52:59 +01:00
2023-01-16 00:25:34 +01:00
impl Calendar {
2023-02-08 22:53:41 +01:00
pub fn new(
fm: &mut FontMap,
font: &'static str,
month_font: &'static str,
size: f32,
sections_x: i32,
sections_y: i32,
) -> Calendar {
fm.queue_font(font, size * DAY_FACTOR, "ADEFHIMNORSTUW");
fm.queue_font(font, size * YEAR_FACTOR, "0123456789");
fm.queue_font(font, size * DATE_FACTOR, "0123456789");
2025-01-12 23:10:21 +01:00
fm.queue_font(
month_font,
size * MONTH_FACTOR,
"ADFJMNOSabcehgilmnoprstuvy",
);
2023-02-02 23:52:59 +01:00
2023-01-16 00:25:34 +01:00
Calendar {
font,
2023-02-08 22:53:41 +01:00
month_font,
2023-01-16 00:25:34 +01:00
size,
sections_x,
sections_y,
2023-02-02 23:52:59 +01:00
shown_sections_x: 0,
shown_sections_y: 0,
2023-01-16 00:25:34 +01:00
dirty: true,
geometry: Default::default(),
2023-02-08 22:53:41 +01:00
date_width: 0,
day_width: 0,
year_width: 0,
2023-01-16 00:25:34 +01:00
}
}
fn draw_month(
&mut self,
fonts: &mut FontMap,
2026-10-03 09:20:25 +00:00
view: &mut BufferView,
2023-01-16 00:25:34 +01:00
orig: NaiveDate,
mut time: NaiveDate,
) {
2023-02-08 22:53:41 +01:00
let cal_width = 8 * self.date_width + 7 * (self.date_width / 2);
2023-02-02 23:52:59 +01:00
//
// Draw the week day
//
2023-02-08 22:53:41 +01:00
let day_offset = (self.date_width - self.day_width) / 2;
let date_offset = self.date_width + self.date_width / 2;
2023-02-02 23:52:59 +01:00
for idx in 1..8 {
let day_font = fonts.get_font(self.font, self.size * DAY_FACTOR);
2023-02-08 22:53:41 +01:00
let (wk_chr, c) = match idx {
1 => ("MON", Color::WHITE),
2 => ("TUE", Color::WHITE),
3 => ("WED", Color::WHITE),
4 => ("THU", Color::WHITE),
5 => ("FRI", Color::WHITE),
6 => ("SAT", Color::GREY80),
7 => ("SUN", Color::GREY80),
2023-02-02 23:52:59 +01:00
_ => panic!("impossible value"),
};
day_font
.draw_text(
2023-02-08 22:53:41 +01:00
&mut view.offset((
idx * date_offset + day_offset,
(self.size * MONTH_FACTOR).ceil() as u32
+ (self.size * DAY_FACTOR * 0.2).ceil() as u32,
)),
c,
2026-10-03 09:20:25 +00:00
wk_chr,
2023-02-02 23:52:59 +01:00
)
.unwrap();
}
//
// Draw the month
//
2023-02-08 22:53:41 +01:00
let month_font = fonts.get_font(self.month_font, self.size * MONTH_FACTOR);
2023-01-16 00:25:34 +01:00
let month_str = match time.month() {
1 => "January",
2 => "February",
3 => "March",
4 => "April",
5 => "May",
6 => "June",
7 => "July",
8 => "August",
9 => "September",
10 => "October",
11 => "November",
12 => "December",
_ => panic!("impossible value"),
};
2026-10-03 09:20:25 +00:00
month_font.draw_text(view, Color::WHITE, month_str).unwrap();
2023-01-16 00:25:34 +01:00
2023-02-08 22:53:41 +01:00
//
// Draw the year
//
if time.year() != orig.year() {
let year_font = fonts.get_font(self.font, self.size * YEAR_FACTOR);
year_font
.draw_text(
&mut view.offset((cal_width - self.year_width, 0)),
Color::GREY80,
&format!("{:}", time.year()),
)
.unwrap();
}
2023-01-16 00:25:34 +01:00
2023-02-08 22:53:41 +01:00
let cal_font = fonts.get_font(self.font, self.size * DATE_FACTOR);
'row: for y_off in 1.. {
2023-01-16 00:25:34 +01:00
//
// Draw the week number
//
let wk = time.iso_week();
cal_font
2023-02-02 23:52:59 +01:00
.draw_text(
2023-02-08 22:53:41 +01:00
&mut view.offset((
0,
y_off * (self.size * LINE_HEIGHT).ceil() as u32
+ (self.size * MONTH_FACTOR).ceil() as u32,
)),
2023-02-14 10:23:20 +01:00
if wk == orig.iso_week() {
Color::WHITE
} else {
Color::GREY75
},
2023-01-16 00:25:34 +01:00
&format!("{:02}", wk.week()),
)
.unwrap();
//
// Draw the dates
//
2023-02-08 22:53:41 +01:00
let x_off = time.weekday().number_from_monday();
for x_pos in x_off..8 {
2023-01-16 00:25:34 +01:00
let c = if time.day() == orig.day() && time.month() == orig.month() {
2023-02-02 23:52:59 +01:00
Color::WHITE
2023-02-08 22:53:41 +01:00
} else if x_pos < 6 {
2023-02-02 23:52:59 +01:00
Color::GREY50
2023-02-08 22:53:41 +01:00
} else {
Color::GREY35
2023-01-16 00:25:34 +01:00
};
cal_font
2023-02-02 23:52:59 +01:00
.draw_text(
2023-02-08 22:53:41 +01:00
&mut view.offset((
x_pos * date_offset,
y_off * (self.size * LINE_HEIGHT).ceil() as u32
+ (self.size * MONTH_FACTOR).ceil() as u32,
)),
2023-02-02 23:52:59 +01:00
c,
2023-01-16 00:25:34 +01:00
&format!("{:02}", time.day()),
)
.unwrap();
let t = time.with_day(time.day() + 1);
if t.is_none() {
2023-02-08 22:53:41 +01:00
break 'row;
2023-01-16 00:25:34 +01:00
}
time = t.unwrap();
}
}
}
}
2026-10-03 09:20:25 +00:00
impl Widget for Calendar {
2023-01-16 00:25:34 +01:00
fn get_dirty(&self) -> bool {
self.dirty
}
fn geometry(&self) -> Geometry {
self.geometry
}
fn event(&mut self, event: &Event) {
2026-10-03 09:20:25 +00:00
if let Event::NewMinute = event {
self.dirty = true;
2023-01-16 00:25:34 +01:00
}
}
fn draw(&mut self, fonts: &mut FontMap, view: &mut BufferView) -> Geometry {
2023-02-02 23:52:59 +01:00
if self.shown_sections_y == 0 || self.shown_sections_x == 0 {
return Geometry {
x: self.geometry.x,
y: self.geometry.y,
width: 0,
height: 0,
};
}
2023-01-16 00:25:34 +01:00
let time = Local::now().naive_local().date();
2023-02-08 22:53:41 +01:00
let cal_height =
(self.size * MONTH_FACTOR).ceil() as u32 + (self.size * LINE_HEIGHT * 8.).ceil() as u32;
let cal_width = 8 * self.date_width + 7 * (self.date_width / 2);
let cal_pad = self.date_width * 2;
2023-01-16 00:25:34 +01:00
let mut t = time.with_day(1).unwrap();
2023-02-02 23:52:59 +01:00
let cals = self.shown_sections_y * self.shown_sections_x;
if cals >= 3 {
2023-01-16 00:25:34 +01:00
t = t.pred_opt().unwrap().with_day(1).unwrap();
}
2023-02-02 23:52:59 +01:00
for ydx in 0..self.shown_sections_y {
for idx in 0..self.shown_sections_x {
2023-01-16 00:25:34 +01:00
self.draw_month(
fonts,
2023-02-08 22:53:41 +01:00
&mut view.offset(((cal_width + cal_pad) * idx, cal_height * ydx)),
2023-01-16 00:25:34 +01:00
time,
t,
);
t = if t.month() == 12 {
t.with_year(t.year() + 1).unwrap().with_month(1).unwrap()
} else {
t.with_month(t.month() + 1).unwrap()
};
}
}
self.dirty = false;
self.geometry
}
2023-02-08 22:53:41 +01:00
fn geometry_update(&mut self, fonts: &mut FontMap, geometry: &Geometry) -> Geometry {
let year_font = fonts.get_font(self.font, self.size * YEAR_FACTOR);
self.year_width = year_font.auto_widest("0123456789").unwrap() * 4;
let cal_font = fonts.get_font(self.font, self.size * DATE_FACTOR);
self.date_width = cal_font.auto_widest("0123456789").unwrap() * 2;
let day_font = fonts.get_font(self.font, self.size * DAY_FACTOR);
self.day_width = day_font.auto_widest("ADEFHIMNORSTUW").unwrap() * 3;
2023-02-02 23:52:59 +01:00
2023-02-08 22:53:41 +01:00
let cal_width = 8 * self.date_width + 7 * (self.date_width / 2);
let cal_height =
(self.size * MONTH_FACTOR).ceil() as u32 + (self.size * LINE_HEIGHT * 8.).ceil() as u32;
let cal_pad = self.date_width * 2;
if geometry.width < cal_width || geometry.height < cal_height {
2023-02-08 22:56:52 +01:00
self.shown_sections_x = 0;
self.shown_sections_y = 0;
2023-02-08 22:53:41 +01:00
self.geometry = Default::default();
return self.geometry;
}
let possible_sections_x = 1 + (geometry.width - cal_width) / (cal_width + cal_pad);
let possible_sections_y = 1 + (geometry.height - cal_height) / cal_height;
self.shown_sections_x =
if self.sections_x > 0 && self.sections_x < possible_sections_x as i32 {
self.sections_x as u32
} else {
possible_sections_x
};
self.shown_sections_y =
if self.sections_y > 0 && self.sections_y < possible_sections_y as i32 {
self.sections_y as u32
} else {
possible_sections_y
};
2023-02-02 23:52:59 +01:00
2023-01-16 00:25:34 +01:00
self.geometry = Geometry {
x: geometry.x,
y: geometry.y,
2026-10-03 09:20:25 +00:00
width: cal_width * self.shown_sections_x + cal_pad * (self.shown_sections_x - 1),
height: cal_height * self.shown_sections_y,
2023-01-16 00:25:34 +01:00
};
self.geometry
}
2023-02-08 22:53:41 +01:00
fn minimum_size(&mut self, fonts: &mut FontMap) -> Geometry {
let cal_font = fonts.get_font(self.font, self.size * DATE_FACTOR);
let date_width = cal_font.auto_widest("0123456789").unwrap() * 2;
let cal_width = 8 * date_width + 7 * (date_width / 2);
let cal_height =
(self.size * MONTH_FACTOR).ceil() as u32 + (self.size * LINE_HEIGHT * 8.).ceil() as u32;
let cal_pad = date_width * 2;
let sections_x = match self.sections_x {
-1 => 1,
0 => {
return Geometry {
x: 0,
y: 0,
width: 0,
height: 0,
2026-10-03 09:16:15 +00:00
};
2023-02-08 22:53:41 +01:00
}
x => x,
};
let sections_y = match self.sections_y {
-1 => 1,
0 => {
return Geometry {
x: 0,
y: 0,
width: 0,
height: 0,
2026-10-03 09:16:15 +00:00
};
2023-02-08 22:53:41 +01:00
}
y => y,
};
let width = cal_width * sections_x as u32 + cal_pad * (sections_x as u32 - 1);
let height = cal_height * sections_y as u32;
Geometry {
x: 0,
y: 0,
2026-10-03 09:20:25 +00:00
width,
height,
2023-02-08 22:53:41 +01:00
}
}
2023-01-16 00:25:34 +01:00
}