use crate::{helper::weekday_to_str, Globals}; use chrono::{Datelike, Duration, NaiveDateTime, Timelike}; use egui::{Color32, Label, Rect, ScrollArea, Sense, Stroke, Ui, Vec2}; use egui_extras::{Size, StripBuilder}; use std::{collections::BTreeMap, ops::Range}; #[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 task_ids = g.tasks.keys().map(|e| e.to_owned()).collect::>(); let instances = BTreeMap::from_iter(task_ids.iter().filter_map(|id| { Some((id, g.get_instances_range(*id, start_dt, end_dt).to_owned()?)) })); let height = 2000.0; ScrollArea::vertical().show(ui, |ui| { StripBuilder::new(ui) .size(Size::exact(50.0)) .sizes(Size::remainder(), 7) .horizontal(|mut ui| { ui.cell(|ui| { for h in 0..24 { ui.add_sized( Vec2::new(50.0, height / 24.0), Label::new(&format!("{h:02}:00")), ); } }); for d in 0..7 { ui.cell(|ui| { let time = start_dt + Duration::days(d as i64); let time_end = time + Duration::days(1) - Duration::seconds(1); let instances_here = instances .iter() .map(|(id, rs)| { ( id, rs.iter() .filter(|r| r.overlaps(time..time_end)) .collect::>(), ) }) .filter(|(_, l)| l.len() != 0); ui.vertical(|ui| { ui.heading(weekday_to_str( time.date().weekday().num_days_from_monday().into(), )); ui.horizontal(|ui| { for (id, rs) in instances_here { let task = g.tasks.get(id).unwrap(); let (rect, response) = ui.allocate_exact_size( Vec2::new(10.0, height), Sense::hover(), ); for r in &rs { let r = r.start.unwrap_or(time)..r.end.unwrap_or(time_end); let rect_start = (r.start.hour() as f32 + (r.start.minute() as f32 / 60.0)) / 24.0 * height; let rect_end = (r.end.hour() as f32 + (r.end.minute() as f32 / 60.0)) / 24.0 * height; ui.painter().rect( Rect::from_two_pos( rect.min + Vec2::new(0.0, rect_start), rect.min + Vec2::new(10.0, rect_end), ), 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); } }); } }) }); // }); // }); }); } }); }); // 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, } } }