176 lines
5.5 KiB
Rust
176 lines
5.5 KiB
Rust
/*
|
|
Copyright 2024, Savanni D'Gerinel <savanni@luminescent-dreams.com>
|
|
|
|
This file is part of FitnessTrax.
|
|
|
|
FitnessTrax is free software: you can redistribute it and/or modify it under the terms of the GNU
|
|
General Public License as published by the Free Software Foundation, either version 3 of the
|
|
License, or (at your option) any later version.
|
|
|
|
FitnessTrax is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
|
even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License along with FitnessTrax. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
use crate::{components::{i32_field_builder, TextEntry, month_field_builder}, types::ParseError};
|
|
use chrono::{Datelike, Local};
|
|
use glib::Object;
|
|
use gtk::{prelude::*, subclass::prelude::*};
|
|
use std::{cell::RefCell, rc::Rc};
|
|
|
|
pub struct DateFieldPrivate {
|
|
date: Rc<RefCell<chrono::NaiveDate>>,
|
|
year: TextEntry<i32>,
|
|
month: TextEntry<u32>,
|
|
day: TextEntry<u32>,
|
|
}
|
|
|
|
#[glib::object_subclass]
|
|
impl ObjectSubclass for DateFieldPrivate {
|
|
const NAME: &'static str = "DateField";
|
|
type Type = DateField;
|
|
type ParentType = gtk::Box;
|
|
|
|
fn new() -> Self {
|
|
let date = Rc::new(RefCell::new(Local::now().date_naive()));
|
|
|
|
let year = i32_field_builder()
|
|
.with_value(date.borrow().year())
|
|
.with_on_update(
|
|
{
|
|
let date = date.clone();
|
|
move |value| {
|
|
if let Some(year) = value {
|
|
let mut date = date.borrow_mut();
|
|
if let Some(new_date) = date.with_year(year) {
|
|
*date = new_date;
|
|
}
|
|
}
|
|
}
|
|
})
|
|
.with_length(4)
|
|
.with_css_classes(vec!["date-field__year".to_owned()]).build();
|
|
|
|
let month = month_field_builder()
|
|
.with_value(date.borrow().month())
|
|
.with_on_update(
|
|
{
|
|
let date = date.clone();
|
|
move |value| {
|
|
if let Some(month) = value {
|
|
let mut date = date.borrow_mut();
|
|
if let Some(new_date) = date.with_month(month) {
|
|
*date = new_date;
|
|
}
|
|
}
|
|
}
|
|
})
|
|
.with_css_classes(vec!["date-field__month".to_owned()])
|
|
.build();
|
|
|
|
/* Modify this so that it enforces the number of days per month */
|
|
let day = TextEntry::builder()
|
|
.with_placeholder("day".to_owned())
|
|
.with_value(date.borrow().day())
|
|
.with_renderer(|v| format!("{}", v))
|
|
.with_parser(|v| v.parse::<u32>().map_err(|_| ParseError))
|
|
.with_on_update({
|
|
let date = date.clone();
|
|
move |value| {
|
|
if let Some(day) = value {
|
|
let mut date = date.borrow_mut();
|
|
if let Some(new_date) = date.with_day(day) {
|
|
*date = new_date;
|
|
}
|
|
}
|
|
}
|
|
})
|
|
.with_css_classes(vec!["date-field__day".to_owned()])
|
|
.build();
|
|
|
|
Self {
|
|
date,
|
|
year,
|
|
month,
|
|
day,
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
impl ObjectImpl for DateFieldPrivate {}
|
|
impl WidgetImpl for DateFieldPrivate {}
|
|
impl BoxImpl for DateFieldPrivate {}
|
|
|
|
glib::wrapper! {
|
|
pub struct DateField(ObjectSubclass<DateFieldPrivate>) @extends gtk::Box, gtk::Widget;
|
|
}
|
|
|
|
/* Render a date in the format 2006 Jan 01. The entire date is editable. When the user moves to one part of the date, it will be erased and replaced with a grey placeholder.
|
|
*/
|
|
impl DateField {
|
|
pub fn new(date: chrono::NaiveDate) -> Self {
|
|
let s: Self = Object::builder().build();
|
|
s.add_css_class("date-field");
|
|
|
|
s.append(&s.imp().year.widget());
|
|
s.append(>k::Label::new(Some("-")));
|
|
s.append(&s.imp().month.widget());
|
|
s.append(>k::Label::new(Some("-")));
|
|
s.append(&s.imp().day.widget());
|
|
|
|
s.set_date(date);
|
|
|
|
s
|
|
}
|
|
|
|
pub fn set_date(&self, date: chrono::NaiveDate) {
|
|
self.imp().year.set_value(Some(date.year()));
|
|
self.imp().month.set_value(Some(date.month()));
|
|
self.imp().day.set_value(Some(date.day()));
|
|
|
|
*self.imp().date.borrow_mut() = date;
|
|
}
|
|
|
|
pub fn date(&self) -> chrono::NaiveDate {
|
|
*self.imp().date.borrow()
|
|
}
|
|
/*
|
|
pub fn is_valid(&self) -> bool {
|
|
false
|
|
}
|
|
*/
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
// use crate::gtk_init::gtk_init;
|
|
|
|
// Enabling this test pushes tests on the TextField into an infinite loop. That likely indicates a bad interaction within the TextField itself, and that is going to need to be fixed.
|
|
#[test]
|
|
#[ignore]
|
|
fn it_allows_valid_dates() {
|
|
let reference = chrono::NaiveDate::from_ymd_opt(2006, 01, 02).unwrap();
|
|
let field = DateField::new(reference);
|
|
field.imp().year.set_value(Some(2023));
|
|
field.imp().month.set_value(Some(10));
|
|
field.imp().day.set_value(Some(13));
|
|
// assert!(field.is_valid());
|
|
}
|
|
|
|
#[test]
|
|
#[ignore]
|
|
fn it_disallows_out_of_range_months() {
|
|
unimplemented!()
|
|
}
|
|
|
|
#[test]
|
|
#[ignore]
|
|
fn it_allows_days_within_range_for_month() {
|
|
unimplemented!()
|
|
}
|
|
}
|