diff options
author | Joris | 2021-11-21 18:12:11 +0100 |
---|---|---|
committer | Joris | 2021-11-21 18:12:11 +0100 |
commit | 3c5be0300f281d809cbe2ecdf52a4ef34598f084 (patch) | |
tree | 5de6256d0d20c8bb0c3e4d88e1f84e17f3f6a7fd | |
parent | 54bcca5c48b4586433a92fe4aad9cddd2e5500dc (diff) |
Show static events on the calendar
-rw-r--r-- | Cargo.lock | 1 | ||||
-rw-r--r-- | Cargo.toml | 3 | ||||
-rw-r--r-- | README.md | 17 | ||||
-rw-r--r-- | src/main.rs | 171 | ||||
-rw-r--r-- | src/model/event.rs | 39 | ||||
-rw-r--r-- | src/model/mod.rs | 1 | ||||
-rw-r--r-- | src/style.css | 25 |
7 files changed, 202 insertions, 55 deletions
@@ -50,6 +50,7 @@ version = "0.1.0" dependencies = [ "chrono", "gtk4", + "pango", ] [[package]] @@ -5,5 +5,6 @@ authors = ["Joris Guyonvarch"] edition = "2018" [dependencies] -gtk4 = { version = "0.3", features = ["v4_2"] } chrono = "0.4" +gtk4 = { version = "0.3", features = ["v4_2"] } +pango = "0.14" @@ -4,16 +4,11 @@ nix develop --command cargo run ``` -# TODO +# Links -## Show events at specific days +- gtk4 documentation: https://gtk-rs.org/gtk4-rs/stable/latest/docs/gtk4/ -1. Modelize an event as Day + Option<Time>. -2. Define a simple test list of events. -3. Show it on the calendar (Order events by time). -4. Add ellipsis in case an event description is too long, and show the complete - message when hovering the mouse. -5. Allow to scroll vertically through events if there are a lot of events one day. +# TODO ## CRUD @@ -34,7 +29,7 @@ Be able to specify repetition. ## API -1. Get list of events today. +1. Get list of today events. ## Calendar focus @@ -49,4 +44,6 @@ Be able to specify repetition. ## Nice to have -1. Drag & drop events. +- Drag & drop events. +- Show an indicator when a day can be scrolled vertically. +- Multi day events diff --git a/src/main.rs b/src/main.rs index 072fbd5..557f8ee 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,30 +1,78 @@ -use chrono::{NaiveDate, Datelike, Weekday}; -use gtk4::gdk::Display; -use gtk4::prelude::*; -use gtk4::{ - Align, Application, ApplicationWindow, CssProvider, Grid, Label, Orientation, StyleContext, - STYLE_PROVIDER_PRIORITY_APPLICATION, Box as GtkBox -}; +mod model; + +use gtk4 as gtk; + +use chrono::{Datelike, NaiveDate, NaiveTime, Weekday}; +use gtk::gdk::Display; +use gtk::prelude::*; + +use crate::model::event::{Event, Time}; static DAYS: [&str; 7] = ["LUN", "MAR", "MER", "JEU", "VEN", "SAM", "DIM"]; -static MONTHES: [&str; 12] = ["Jan", "Fév", "Mar", "Avr", "Mai", "Juin", "Juil", "Aoû", "Sep", "Oct", "Nov", "Déc"]; +static MONTHES: [&str; 12] = [ + "Jan", "Fév", "Mar", "Avr", "Mai", "Juin", "Juil", "Aoû", "Sep", "Oct", "Nov", "Déc", +]; fn main() { - let application = Application::new(Some("me.guyonvarch.calendar"), Default::default()); - application.connect_startup(build_ui); + let mut events = [ + Event { + date: NaiveDate::from_ymd(2021, 11, 22), + time: Time::AllDay, + name: "Début de la semaine".to_string(), + }, + Event { + date: NaiveDate::from_ymd(2021, 11, 26), + time: Time::AllDay, + name: "Fin de la semaine".to_string(), + }, + Event { + date: NaiveDate::from_ymd(2021, 11, 26), + time: Time::Time { + start: NaiveTime::from_hms(15, 0, 0), + end: Some(NaiveTime::from_hms(15, 30, 0)), + }, + name: "Appel".to_string(), + }, + Event { + date: NaiveDate::from_ymd(2021, 11, 26), + time: Time::Time { + start: NaiveTime::from_hms(12, 0, 0), + end: Some(NaiveTime::from_hms(14, 0, 0)), + }, + name: "Repas".to_string(), + }, + Event { + date: NaiveDate::from_ymd(2021, 11, 26), + time: Time::Time { + start: NaiveTime::from_hms(8, 0, 0), + end: None, + }, + name: "Promener le chien".to_string(), + }, + Event { + date: NaiveDate::from_ymd(2021, 11, 26), + time: Time::Time { + start: NaiveTime::from_hms(9, 0, 0), + end: None, + }, + name: "Thé".to_string(), + }, + ] + .to_vec(); + events.sort_by_key(|e| e.time); + let application = gtk::Application::new(Some("me.guyonvarch.calendar"), Default::default()); + application.connect_startup(move |app| build_ui(app, &events)); application.run(); } -fn build_ui(application: &Application) { - let window = ApplicationWindow::new(application); +fn build_ui(app: >k::Application, events: &Vec<Event>) { + let window = gtk::ApplicationWindow::new(app); window.set_title(Some("Calendar")); window.set_default_size(800, 600); - window.set_hexpand(true); - window.set_vexpand(true); load_style(); - let grid = Grid::builder().build(); + let grid = gtk::Grid::builder().build(); window.set_child(Some(&grid)); @@ -38,7 +86,7 @@ fn build_ui(application: &Application) { let mut d = last_monday; for row in 1..5 { for col in 0..7 { - grid.attach(&day_entry(&today, &d), col, row, 1, 1); + grid.attach(&day_entry(&d, &today, &events), col, row, 1, 1); d = d.succ(); } } @@ -47,55 +95,100 @@ fn build_ui(application: &Application) { } fn load_style() { - let provider = CssProvider::new(); + let provider = gtk::CssProvider::new(); provider.load_from_data(include_bytes!("style.css")); - StyleContext::add_provider_for_display( + gtk::StyleContext::add_provider_for_display( &Display::default().expect("Error initializing gtk css provider."), &provider, - STYLE_PROVIDER_PRIORITY_APPLICATION, + gtk::STYLE_PROVIDER_PRIORITY_APPLICATION, ); } -fn day_title(col: i32) -> GtkBox { - let vbox = GtkBox::builder() - .orientation(Orientation::Vertical) - .hexpand(true) +fn day_title(col: i32) -> gtk::Box { + let vbox = gtk::Box::builder() + .orientation(gtk::Orientation::Vertical) .build(); vbox.add_css_class("g-Calendar__DayTitle"); - let label = Label::builder() - .label(DAYS[col as usize]) - .halign(Align::Center) - .valign(Align::Center) - .build(); + let label = gtk::Label::builder().label(DAYS[col as usize]).build(); + vbox.append(&label); vbox } -fn day_entry(today: &NaiveDate, d: &NaiveDate) -> GtkBox { - let vbox = GtkBox::builder() - .orientation(Orientation::Vertical) - .hexpand(true) - .vexpand(true) +fn day_entry(date: &NaiveDate, today: &NaiveDate, events: &Vec<Event>) -> gtk::ScrolledWindow { + let vbox = gtk::Box::builder() + .orientation(gtk::Orientation::Vertical) .build(); vbox.add_css_class("g-Calendar__Day"); - if d == today { + if date == today { vbox.add_css_class("g-Calendar__Day--Today"); } - let label = Label::builder() - .label(&format!("{} {}", d.day(), MONTHES[d.month0() as usize])) - .halign(Align::Start) - .valign(Align::Center) + vbox.append(&day_label(date)); + + let events = events + .iter() + .filter(|e| e.date == *date) + .collect::<Vec<&Event>>(); + + if !events.is_empty() { + vbox.append(&day_events(events)); + } + + let scrolled_window = gtk::ScrolledWindow::builder() + .hscrollbar_policy(gtk::PolicyType::Never) + .hexpand(true) + .vexpand(true) + .child(&vbox) + .build(); + + scrolled_window +} + +fn day_label(date: &NaiveDate) -> gtk::Label { + let label = gtk::Label::builder() + .label(&format!( + "{} {}", + date.day(), + MONTHES[date.month0() as usize] + )) + .halign(gtk::Align::Start) .build(); label.add_css_class("g-Calendar__DayNumber"); - vbox.append(&label); + label +} + +fn day_events(events: Vec<&Event>) -> gtk::Box { + let vbox = gtk::Box::builder() + .orientation(gtk::Orientation::Vertical) + .build(); + + for event in events { + let hbox = gtk::Box::builder() + .orientation(gtk::Orientation::Horizontal) + .hexpand(true) + .build(); + + hbox.add_css_class("g-Calendar__DayEvent"); + + let event_txt = &event.pprint(); + let label = gtk::Label::builder() + .label(&event_txt) + .ellipsize(pango::EllipsizeMode::End) + .tooltip_text(&event_txt) + .halign(gtk::Align::Start) + .build(); + + hbox.append(&label); + vbox.append(&hbox); + } vbox } diff --git a/src/model/event.rs b/src/model/event.rs new file mode 100644 index 0000000..d1d9775 --- /dev/null +++ b/src/model/event.rs @@ -0,0 +1,39 @@ +use chrono::Timelike; +use chrono::{NaiveDate, NaiveTime}; + +#[derive(Debug, Clone)] +pub struct Event { + pub date: NaiveDate, + pub time: Time, + pub name: String, +} + +impl Event { + pub fn pprint(&self) -> String { + match self.time { + Time::AllDay => self.name.clone(), + Time::Time { start, end: None } => format!("{} {}", pprint_time(start), self.name), + Time::Time { + start, + end: Some(e), + } => format!("{}-{} {}", pprint_time(start), pprint_time(e), self.name), + } + } +} + +#[derive(Debug, Clone, Copy, PartialOrd, PartialEq, Eq, Ord)] +pub enum Time { + AllDay, + Time { + start: NaiveTime, + end: Option<NaiveTime>, + }, +} + +fn pprint_time(t: NaiveTime) -> String { + if t.minute() == 0 { + format!("{}h", t.hour()) + } else { + format!("{}h{}", t.hour(), t.minute()) + } +} diff --git a/src/model/mod.rs b/src/model/mod.rs new file mode 100644 index 0000000..53f1126 --- /dev/null +++ b/src/model/mod.rs @@ -0,0 +1 @@ +pub mod event; diff --git a/src/style.css b/src/style.css index bd58799..0a8292f 100644 --- a/src/style.css +++ b/src/style.css @@ -1,20 +1,35 @@ .g-Calendar__DayTitle { - border: 1px solid #CCCCCC; - font-weight: bold; + border-right: 1px solid #D2D2D2; + border-bottom: 1px solid #D2D2D2; padding: 4px; + background-color: white; + font-weight: bold; } .g-Calendar__Day { background-color: white; - border: 1px solid #CCCCCC; + border-right: 1px solid #D2D2D2; + border-bottom: 1px solid #D2D2D2; padding: 4px; } .g-Calendar__Day--Today { - background-color: #FFEC40; + background-color: #FFFCD8; } .g-Calendar__DayNumber { - color: #333333; font-size: 90%; + margin-bottom: 4px; +} + +.g-Calendar__DayEvent { + background-color: #A8C2E0; + color: white; + border-radius: 4px; + padding: 4px; + margin: 4px; +} + +.g-Calendar__DayEvent:hover { + background-color: pink; } |