aboutsummaryrefslogtreecommitdiff
path: root/karlgui/src/views/calendar.rs
blob: a861c73d5de5192ac73e6110bb8915c097c06874 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
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<u64>; 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<T> {
    fn overlaps(&self, v: T) -> bool;
}
impl Overlaps<NaiveDateTime> for Range<NaiveDateTime> {
    fn overlaps(&self, v: NaiveDateTime) -> bool {
        self.start <= v && v < self.end
    }
}
impl Overlaps<NaiveDateTime> for Range<Option<NaiveDateTime>> {
    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<Range<NaiveDateTime>> for Range<Option<NaiveDateTime>> {
    fn overlaps(&self, v: Range<NaiveDateTime>) -> 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,
        }
    }
}