aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoris2021-11-21 18:12:11 +0100
committerJoris2021-11-21 18:12:11 +0100
commit3c5be0300f281d809cbe2ecdf52a4ef34598f084 (patch)
tree5de6256d0d20c8bb0c3e4d88e1f84e17f3f6a7fd
parent54bcca5c48b4586433a92fe4aad9cddd2e5500dc (diff)
Show static events on the calendar
-rw-r--r--Cargo.lock1
-rw-r--r--Cargo.toml3
-rw-r--r--README.md17
-rw-r--r--src/main.rs171
-rw-r--r--src/model/event.rs39
-rw-r--r--src/model/mod.rs1
-rw-r--r--src/style.css25
7 files changed, 202 insertions, 55 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 7dabba4..c25214f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -50,6 +50,7 @@ version = "0.1.0"
dependencies = [
"chrono",
"gtk4",
+ "pango",
]
[[package]]
diff --git a/Cargo.toml b/Cargo.toml
index 0f6e92e..f240486 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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"
diff --git a/README.md b/README.md
index c27e3f7..b6bfd91 100644
--- a/README.md
+++ b/README.md
@@ -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: &gtk::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;
}