Start setting up date range widgets

This commit is contained in:
Savanni D'Gerinel 2024-02-15 09:11:34 -05:00 committed by savanni
parent b33d17c256
commit 01776a534e
3 changed files with 94 additions and 47 deletions

View File

@ -14,62 +14,100 @@ 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::types::ParseError;
use chrono::Datelike;
use gtk::prelude::*;
use crate::{components::TextEntry, types::ParseError};
use chrono::{Datelike, Local};
use glib::Object;
use gtk::{prelude::*, subclass::prelude::*};
use std::{cell::RefCell, rc::Rc};
#[derive(Clone)]
pub struct DateField {
value: Rc<RefCell<chrono::NaiveDate>>,
buffer: gtk::TextBuffer,
widget: gtk::TextView,
#[derive(Clone, Debug)]
enum Part {
Year,
Month,
Day,
}
impl DateField {
pub fn new(date: chrono::NaiveDate) -> Self {
let buffer = gtk::TextBuffer::new(None);
let tag = buffer.create_tag(
Some("placeholder"),
&[("foreground", &String::from("grey"))],
pub struct DateFieldPrivate {
value: 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 = Local::now().date_naive();
let year = TextEntry::new(
"year",
Some(date.year()),
|v| format!("{}", v),
|v| v.parse::<i32>().map_err(|_| ParseError),
|_| {},
);
let month = TextEntry::new(
"month",
Some(date.month()),
|v| format!("{}", v),
|v| v.parse::<u32>().map_err(|_| ParseError),
|_| {},
);
let day = TextEntry::new(
"day",
Some(date.day()),
|v| format!("{}", v),
|v| v.parse::<u32>().map_err(|_| ParseError),
|_| {},
);
buffer.set_text("regular text placeholder text");
let (start, end) = buffer.bounds();
let mut placeholder_start = start.clone();
placeholder_start.forward_chars(13);
Self {
value: Rc::new(RefCell::new(date.clone())),
year,
month,
day,
}
}
}
let placeholder_markup = buffer.tag_table().lookup("placeholder").unwrap();
buffer.apply_tag(&placeholder_markup, &placeholder_start, &end);
impl ObjectImpl for DateFieldPrivate {}
impl WidgetImpl for DateFieldPrivate {}
impl BoxImpl for DateFieldPrivate {}
let widget = gtk::TextView::builder()
.buffer(&buffer)
.editable(true)
.can_focus(true)
.height_request(50)
.width_request(200)
.build();
glib::wrapper! {
pub struct DateField(ObjectSubclass<DateFieldPrivate>) @extends gtk::Box, gtk::Widget;
}
let s = Self {
value: Rc::new(RefCell::new(date)),
buffer,
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.widget.buffer().connect_text_notify({
let s = s.clone();
move |buffer| s.on_update(buffer)
});
println!("{}", date);
s.append(&s.imp().year.widget());
s.append(&s.imp().month.widget());
s.append(&s.imp().day.widget());
s.imp().year.set_value(Some(date.year()));
s.imp().month.set_value(Some(date.month()));
s.imp().day.set_value(Some(date.day()));
*s.imp().value.borrow_mut() = date;
s
}
pub fn widget(&self) -> gtk::Widget {
self.widget.clone().upcast::<gtk::Widget>()
}
fn on_update(&self, buffer: &gtk::TextBuffer) {
let (start, end) = buffer.bounds();
println!("[on_update] {}", buffer.text(&start, &end, true));
}
}
/* As soon as the field gets focus, highlight the first element
*/
#[cfg(test)]
mod test {
use super::*;
use crate::gtk_init::gtk_init;
}

View File

@ -28,6 +28,7 @@ pub struct TextEntry<T: Clone + std::fmt::Debug> {
value: Rc<RefCell<Option<T>>>,
widget: gtk::Entry,
renderer: Rc<dyn Fn(&T) -> String>,
parser: Rc<Parser<T>>,
on_update: Rc<OnUpdate<T>>,
}
@ -64,6 +65,7 @@ impl<T: Clone + std::fmt::Debug + 'static> TextEntry<T> {
let s = Self {
value: Rc::new(RefCell::new(value)),
widget,
renderer: Rc::new(renderer),
parser: Rc::new(parser),
on_update: Rc::new(on_update),
};
@ -76,6 +78,13 @@ impl<T: Clone + std::fmt::Debug + 'static> TextEntry<T> {
s
}
pub fn set_value(&self, val: Option<T>) {
if let Some(ref v) = val {
self.widget.set_text(&(self.renderer)(v));
}
*self.value.borrow_mut() = val;
}
fn handle_text_change(&self, buffer: &gtk::EntryBuffer) {
if buffer.text().is_empty() {
*self.value.borrow_mut() = None;

View File

@ -112,9 +112,9 @@ impl HistoricalView {
let date_row = gtk::Box::builder()
.orientation(gtk::Orientation::Horizontal)
.build();
date_row.append(&DateField::new(interval.start).widget());
date_row.append(&DateField::new(interval.start));
date_row.append(&gtk::Label::new(Some("to")));
date_row.append(&DateField::new(interval.end).widget());
date_row.append(&DateField::new(interval.end));
let quick_picker = gtk::Box::builder()
.orientation(gtk::Orientation::Horizontal)