From 36cd29b459bb41bff1cf6d079fd30100bd89cec1 Mon Sep 17 00:00:00 2001 From: Joris Date: Sat, 20 Jan 2024 22:08:44 +0100 Subject: Allow to specify week of month for repetition --- src/gui/form/repetition.rs | 67 +++++++++++++++++++++++++++++++++------------- src/model/repetition.rs | 18 +++++++------ 2 files changed, 59 insertions(+), 26 deletions(-) diff --git a/src/gui/form/repetition.rs b/src/gui/form/repetition.rs index 4da65ac..a83aea5 100644 --- a/src/gui/form/repetition.rs +++ b/src/gui/form/repetition.rs @@ -12,11 +12,17 @@ use crate::model::{ use crate::validation; static WEEKDAYS_STR: [&str; 7] = [ - "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche", + "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi", "dimanche", ]; static WEEKDAYS: [Weekday; 7] = [Mon, Tue, Wed, Thu, Fri, Sat, Sun]; +static WEEKS_STR: [&str; 4] = [ + "1er", "2ème", "3ème", "4ème" +]; + +static WEEKS: [u8; 4] = [1, 2, 3, 4]; + pub struct Model { pub view: gtk::Box, no_radio: gtk::CheckButton, @@ -24,8 +30,9 @@ pub struct Model { day_interval_entry: gtk::Entry, monthly_radio: gtk::CheckButton, monthly_entry: gtk::Entry, - first_day_radio: gtk::CheckButton, - first_day_dropdown: gtk::DropDown, + day_of_month_radio: gtk::CheckButton, + week_of_month: gtk::DropDown, + day_of_week: gtk::DropDown, yearly_radio: gtk::CheckButton, until: gtk::Entry, } @@ -70,18 +77,19 @@ pub fn view(repetition: Option<&Repetition>) -> Model { radio_input(&no_radio, !default.is_empty(), &monthly_entry, "Mensuel"); view.append(&monthly_box); - let (active, default) = match frequency { + let (active, default_week, default_day) = match frequency { Some(Frequency::Monthly { - day: DayOfMonth::Weekday { weekday }, - }) => (true, weekday), - _ => (false, Mon), + day: DayOfMonth::Weekday { week, day }, + }) => (true, week, day), + _ => (false, 1, Mon), }; - let first_day_dropdown = gtk::DropDown::from_strings(&WEEKDAYS_STR); - first_day_dropdown - .set_selected(WEEKDAYS.iter().position(|d| d == &default).unwrap_or(0) as u32); - let (first_day_of_month_box, first_day_radio) = - radio_input(&no_radio, active, &first_day_dropdown, "1er jour du mois"); - view.append(&first_day_of_month_box); + let week_of_month = gtk::DropDown::from_strings(&WEEKS_STR); + week_of_month.set_selected(WEEKS.iter().position(|d| d == &default_week).unwrap_or(0) as u32); + let day_of_week = gtk::DropDown::from_strings(&WEEKDAYS_STR); + day_of_week.set_selected(WEEKDAYS.iter().position(|d| d == &default_day).unwrap_or(0) as u32); + let (day_of_month_box, day_of_month_radio) = + radio_day_of_month(&no_radio, active, &week_of_month, &day_of_week); + view.append(&day_of_month_box); let yearly_radio = gtk::CheckButton::builder() .group(&no_radio) @@ -106,8 +114,9 @@ pub fn view(repetition: Option<&Repetition>) -> Model { day_interval_entry, monthly_radio, monthly_entry, - first_day_radio, - first_day_dropdown, + day_of_month_radio, + week_of_month, + day_of_week, yearly_radio, until, } @@ -131,6 +140,27 @@ fn radio_input( (radio_box, radio) } +fn radio_day_of_month( + radio_group: &impl IsA, + active: bool, + number: &impl IsA, + day: &impl IsA, +) -> (gtk::Box, gtk::CheckButton) { + let radio_box = gtk::Box::builder().build(); + let radio = gtk::CheckButton::builder() + .group(radio_group) + .active(active) + .build(); + radio_box.append(&radio); + number.add_css_class("g-Form__RadioInput"); + radio_box.append(number); + day.add_css_class("g-Form__RadioInput"); + radio_box.append(day); + let text = gtk::Text::builder().text("du mois.").build(); + radio_box.append(&text); + (radio_box, radio) +} + fn label(text: &str) -> gtk::Label { gtk::Label::builder() .label(text) @@ -153,10 +183,11 @@ pub fn validate( Ok(Some(Frequency::Monthly { day: DayOfMonth::Day { day }, })) - } else if model.first_day_radio.is_active() { - let weekday = WEEKDAYS[model.first_day_dropdown.selected() as usize]; + } else if model.day_of_month_radio.is_active() { + let week = WEEKS[model.week_of_month.selected() as usize]; + let day = WEEKDAYS[model.day_of_week.selected() as usize]; Ok(Some(Frequency::Monthly { - day: DayOfMonth::Weekday { weekday }, + day: DayOfMonth::Weekday { week, day }, })) } else if model.yearly_radio.is_active() { Ok(Some(Frequency::Yearly)) diff --git a/src/model/repetition.rs b/src/model/repetition.rs index 360cf5f..07fc4d4 100644 --- a/src/model/repetition.rs +++ b/src/model/repetition.rs @@ -19,7 +19,7 @@ pub enum Frequency { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum DayOfMonth { Day { day: u8 }, - Weekday { weekday: Weekday }, + Weekday { week: u8, day: Weekday }, } pub fn validate_period(str: &str) -> Result { @@ -74,10 +74,10 @@ impl Repetition { None => vec![], }, Frequency::Monthly { - day: DayOfMonth::Weekday { weekday }, + day: DayOfMonth::Weekday { week, day }, } => repeat( - first_weekday_of_month(event, weekday), - Box::new(|d| first_weekday_of_month(next_month(d), weekday)), + day_of_month(event, week, day), + Box::new(|d| day_of_month(next_month(d), week, day)), ), Frequency::Yearly => repeat( // TODO: error handling @@ -97,9 +97,9 @@ impl Repetition { } } -fn first_weekday_of_month(date: NaiveDate, weekday: Weekday) -> NaiveDate { +fn day_of_month(date: NaiveDate, week: u8, day: Weekday) -> NaiveDate { // TODO: error handling - NaiveDate::from_weekday_of_month_opt(date.year(), date.month(), weekday, 1).unwrap() + NaiveDate::from_weekday_of_month_opt(date.year(), date.month(), day, week).unwrap() } fn next_month(date: NaiveDate) -> NaiveDate { @@ -177,7 +177,8 @@ mod tests { fn weekday_of_month() { let repetition = from_freq(Frequency::Monthly { day: DayOfMonth::Weekday { - weekday: Weekday::Tue, + week: 1, + day: Weekday::Tue, }, }); assert_eq!( @@ -228,7 +229,8 @@ mod tests { let repetition = Repetition { frequency: Frequency::Monthly { day: DayOfMonth::Weekday { - weekday: Weekday::Fri, + week: 1, + day: Weekday::Fri, }, }, removed_occurences: HashSet::from([1, 2, 3]), -- cgit v1.2.3