use std::ops::Range; use crate::{helper::weekday_to_str, Globals}; use chrono::{Duration, NaiveDateTime}; use egui::{Color32, Sense, Stroke, Ui, Vec2}; use egui_extras::TableBuilder; #[derive(Default)] pub struct Calendar { // offset: NaiveDate, } impl Calendar { pub fn ui(&mut self, ui: &mut Ui, g: &mut Globals) { let start_date = chrono::Utc::now().date_naive(); //self.offset; let end_date = start_date + chrono::Duration::days(7); let start_dt = start_date.and_hms(0, 0, 0); let end_dt = end_date.and_hms(0, 0, 0); let instances = if let Some(instances) = g.get_all_instances_range(start_dt, end_dt) { instances } else { ui.horizontal(|ui| { ui.spinner(); ui.label("Loading…"); }); return; }; let height = 50.0; TableBuilder::new(ui) .column(egui_extras::Size::exact(50.0)) .columns(egui_extras::Size::remainder(), 7) .header(25.0, |mut ui| { ui.col(|_| {}); for d in 0..7 { ui.col(|ui| { ui.heading(weekday_to_str(d)); }); } }) .body(|mut ui| { let mut cols: [Vec; 7] = [vec![], vec![], vec![], vec![], vec![], vec![], vec![]]; for h in 0..24 { ui.row(height, |mut ui| { ui.col(|ui| { ui.heading(&format!("{h:02}:00")); }); for d in 0..7 { let _col = &mut cols[d]; let time = start_dt + Duration::days(d as i64) + Duration::hours(h); let time_end = time + Duration::hours(1) - Duration::seconds(1); let instances_here = instances.iter().filter(|(_, r)| r.overlaps(time..time_end)); ui.col(|ui| { for (id, _) in instances_here { let task = g.tasks.get(id).unwrap(); let (rect, response) = ui.allocate_at_least(Vec2::new(10.0, 50.0), Sense::hover()); ui.painter().rect( rect, 0.0, Color32::KHAKI, Stroke::new(0.0, Color32::WHITE), ); response.on_hover_ui_at_pointer(|ui| { ui.heading(&task.name); if let Some(d) = &task.description { ui.label(d); } }); } }); } }) } }) } } pub trait Overlaps { fn overlaps(&self, v: T) -> bool; } impl Overlaps for Range { fn overlaps(&self, v: NaiveDateTime) -> bool { self.start <= v && v < self.end } } impl Overlaps for Range> { fn overlaps(&self, v: NaiveDateTime) -> bool { match (self.start, self.end) { (Some(s), Some(e)) => s <= v && v < e, (Some(s), None) => s <= v, (None, Some(e)) => v < e, (None, None) => false, } } } impl Overlaps> for Range> { fn overlaps(&self, v: Range) -> bool { match (self.start, self.end) { (None, None) => false, (None, Some(e)) => v.start < e, (Some(s), None) => v.end > s, (Some(s), Some(e)) => v.start < e && v.end > s, } } }