aboutsummaryrefslogtreecommitdiff
path: root/src/gui/form
diff options
context:
space:
mode:
authorJoris2022-02-26 18:57:55 +0100
committerJoris2022-02-26 18:57:55 +0100
commitf9f49285c5ecc76d3edfb0a54ffab53c2e296d7f (patch)
treef77f9b625446de7f0b9de1553fc52d702c4cbc69 /src/gui/form
parent2d80413609130f1c121dcae39a150a27dd9f02ea (diff)
Apply linter advices
Diffstat (limited to 'src/gui/form')
-rw-r--r--src/gui/form/mod.rs126
-rw-r--r--src/gui/form/repetition.rs151
2 files changed, 277 insertions, 0 deletions
diff --git a/src/gui/form/mod.rs b/src/gui/form/mod.rs
new file mode 100644
index 0000000..4bf6f90
--- /dev/null
+++ b/src/gui/form/mod.rs
@@ -0,0 +1,126 @@
+mod repetition;
+
+use gtk4 as gtk;
+
+use gtk::glib;
+use gtk::prelude::*;
+
+use crate::{
+ db,
+ gui::{update, update::Msg, App},
+ model::{event, event::Event},
+};
+
+pub async fn show(app: &App, event: Event, is_new: bool) {
+ let dialog = gtk::Dialog::builder()
+ .transient_for(&*app.window)
+ .modal(true)
+ .title(if is_new { "Ajouter" } else { "Modifier" })
+ .css_classes(vec!["g-Form".to_string()])
+ .build();
+
+ let content_area = dialog.content_area();
+
+ let lines = gtk::Box::builder()
+ .orientation(gtk::Orientation::Vertical)
+ .build();
+ content_area.append(&lines);
+
+ let columns = gtk::Box::builder()
+ .orientation(gtk::Orientation::Horizontal)
+ .build();
+ columns.add_css_class("g-Form__Columns");
+ lines.append(&columns);
+
+ // First column
+
+ let column1 = gtk::Box::builder()
+ .orientation(gtk::Orientation::Vertical)
+ .build();
+ column1.add_css_class("g-Form__Inputs");
+ columns.append(&column1);
+
+ let name = entry(&event.name);
+ column1.append(&label("Événement"));
+ column1.append(&name);
+
+ let date = entry(&event.date.format(event::DATE_FORMAT).to_string());
+ column1.append(&label("Jour"));
+ column1.append(&date);
+
+ let start = entry(
+ &event
+ .start
+ .map(event::pprint_time)
+ .unwrap_or_else(|| "".to_string()),
+ );
+ column1.append(&label("Début"));
+ column1.append(&start);
+
+ let end = entry(
+ &event
+ .end
+ .map(event::pprint_time)
+ .unwrap_or_else(|| "".to_string()),
+ );
+ column1.append(&label("Fin"));
+ column1.append(&end);
+
+ // Second column
+
+ let repetition_model = repetition::view(&event);
+ columns.append(&repetition_model.view);
+
+ // Buttons
+
+ let button = gtk::Button::builder()
+ .label(if is_new { "Créer" } else { "Modifier" })
+ .margin_bottom(10)
+ .build();
+ lines.append(&button);
+ let conn = app.conn.clone();
+ let tx = app.tx.clone();
+ button.connect_clicked(glib::clone!(@weak dialog, @strong event => move |_| {
+ let repetition = repetition::validate(&repetition_model);
+ match event::validate(event.id, date.buffer().text(), name.buffer().text(), start.buffer().text(), end.buffer().text(), repetition) {
+ Some(new) => {
+ match if is_new { db::insert(&conn, &new) } else { db::update(&conn, &new) } {
+ Ok(_) => {
+ let msg = if is_new { Msg::AddEvent { new } } else { Msg::UpdateEvent { old: event.clone(), new } };
+ update::send(tx.clone(), msg);
+ dialog.close()
+ },
+ Err(err) => eprintln!("Error when upserting event: {err}")
+ }
+ },
+ None => eprintln!("Event is not valid: {event:?}")
+ }
+ }));
+
+ if !is_new {
+ let button = gtk::Button::builder().label("Supprimer").build();
+ lines.append(&button);
+ let conn = app.conn.clone();
+ let tx = app.tx.clone();
+ button.connect_clicked(glib::clone!(@weak dialog => move |_| {
+ if db::delete(&conn, &event.id).is_ok() {
+ update::send(tx.clone(), Msg::DeleteEvent { event: event.clone() });
+ dialog.close()
+ }
+ }));
+ }
+
+ dialog.run_future().await;
+}
+
+fn entry(text: &str) -> gtk::Entry {
+ gtk::Entry::builder().text(text).margin_bottom(10).build()
+}
+
+fn label(text: &str) -> gtk::Label {
+ gtk::Label::builder()
+ .label(text)
+ .halign(gtk::Align::Start)
+ .margin_bottom(5)
+ .build()
+}
diff --git a/src/gui/form/repetition.rs b/src/gui/form/repetition.rs
new file mode 100644
index 0000000..87c8d84
--- /dev/null
+++ b/src/gui/form/repetition.rs
@@ -0,0 +1,151 @@
+use gtk4 as gtk;
+
+use chrono::{Weekday, Weekday::*};
+use gtk::prelude::*;
+
+use crate::{
+ model::event::Event,
+ model::{
+ repetition,
+ repetition::{DayOfMonth, Repetition},
+ },
+};
+
+static WEEKDAYS_STR: [&str; 7] = [
+ "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche",
+];
+
+static WEEKDAYS: [Weekday; 7] = [Mon, Tue, Wed, Thu, Fri, Sat, Sun];
+
+pub struct Model {
+ pub view: gtk::Box,
+ no_radio: gtk::CheckButton,
+ day_interval_radio: gtk::CheckButton,
+ day_interval_entry: gtk::Entry,
+ monthly_radio: gtk::CheckButton,
+ monthly_entry: gtk::Entry,
+ first_day_radio: gtk::CheckButton,
+ first_day_dropdown: gtk::DropDown,
+ yearly_radio: gtk::CheckButton,
+}
+
+pub fn view(event: &Event) -> Model {
+ let view = gtk::Box::builder()
+ .orientation(gtk::Orientation::Vertical)
+ .build();
+ view.add_css_class("g-Form__Inputs");
+
+ view.append(&label("Répétition"));
+
+ let no_radio = gtk::CheckButton::builder()
+ .label("Non")
+ .active(event.repetition.is_none())
+ .build();
+ view.append(&no_radio);
+
+ let default = match event.repetition {
+ Some(Repetition::Daily { period }) => period.to_string(),
+ _ => "".to_string(),
+ };
+ let day_interval_entry = gtk::Entry::builder().text(&default).build();
+ let (day_interval_box, day_interval_radio) = radio_input(
+ &no_radio,
+ !default.is_empty(),
+ &day_interval_entry,
+ "Interval de jours",
+ );
+ view.append(&day_interval_box);
+
+ let default = match event.repetition {
+ Some(Repetition::Monthly {
+ day: DayOfMonth::Day { day },
+ }) => day.to_string(),
+ _ => "".to_string(),
+ };
+ let monthly_entry = gtk::Entry::builder().text(&default).build();
+ let (monthly_box, monthly_radio) =
+ radio_input(&no_radio, !default.is_empty(), &monthly_entry, "Mensuel");
+ view.append(&monthly_box);
+
+ let (active, default) = match event.repetition {
+ Some(Repetition::Monthly {
+ day: DayOfMonth::Weekday { weekday },
+ }) => (true, weekday),
+ _ => (false, 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 yearly_radio = gtk::CheckButton::builder()
+ .group(&no_radio)
+ .label("Annuel")
+ .active(event.repetition == Some(Repetition::Yearly))
+ .build();
+ view.append(&yearly_radio);
+
+ Model {
+ view,
+ no_radio,
+ day_interval_radio,
+ day_interval_entry,
+ monthly_radio,
+ monthly_entry,
+ first_day_radio,
+ first_day_dropdown,
+ yearly_radio,
+ }
+}
+
+fn radio_input(
+ radio_group: &impl IsA<gtk::CheckButton>,
+ active: bool,
+ input: &impl IsA<gtk::Widget>,
+ text: &str,
+) -> (gtk::Box, gtk::CheckButton) {
+ let radio_box = gtk::Box::builder().build();
+ let radio = gtk::CheckButton::builder()
+ .group(radio_group)
+ .label(text)
+ .active(active)
+ .build();
+ radio_box.append(&radio);
+ input.add_css_class("g-Form__RadioInput");
+ radio_box.append(input);
+ (radio_box, radio)
+}
+
+fn label(text: &str) -> gtk::Label {
+ gtk::Label::builder()
+ .label(text)
+ .halign(gtk::Align::Start)
+ .margin_bottom(5)
+ .build()
+}
+
+pub fn validate(model: &Model) -> Option<Repetition> {
+ if model.no_radio.is_active() {
+ None
+ } else if model.day_interval_radio.is_active() {
+ repetition::validate_day(&model.day_interval_entry.buffer().text())
+ .map(|d| Repetition::Daily { period: d })
+ } else if model.monthly_radio.is_active() {
+ repetition::validate_day(&model.monthly_entry.buffer().text()).map(|d| {
+ Repetition::Monthly {
+ day: DayOfMonth::Day { day: d },
+ }
+ })
+ } else if model.first_day_radio.is_active() {
+ let weekday = WEEKDAYS[model.first_day_dropdown.selected() as usize];
+ Some(Repetition::Monthly {
+ day: DayOfMonth::Weekday { weekday },
+ })
+ } else if model.yearly_radio.is_active() {
+ Some(Repetition::Yearly)
+ } else {
+ None
+ }
+}