diff --git a/fitnesstrax/app/src/components/date_field.rs b/fitnesstrax/app/src/components/date_field.rs
index 6cc5335..64bf7ab 100644
--- a/fitnesstrax/app/src/components/date_field.rs
+++ b/fitnesstrax/app/src/components/date_field.rs
@@ -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 .
*/
-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>,
- 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>,
+ year: TextEntry,
+ month: TextEntry,
+ day: TextEntry,
+}
+
+#[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::().map_err(|_| ParseError),
+ |_| {},
+ );
+ let month = TextEntry::new(
+ "month",
+ Some(date.month()),
+ |v| format!("{}", v),
+ |v| v.parse::().map_err(|_| ParseError),
+ |_| {},
+ );
+ let day = TextEntry::new(
+ "day",
+ Some(date.day()),
+ |v| format!("{}", v),
+ |v| v.parse::().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) @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::()
- }
-
- fn on_update(&self, buffer: >k::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;
}
diff --git a/fitnesstrax/app/src/components/text_entry.rs b/fitnesstrax/app/src/components/text_entry.rs
index a8f89cb..71a4ed4 100644
--- a/fitnesstrax/app/src/components/text_entry.rs
+++ b/fitnesstrax/app/src/components/text_entry.rs
@@ -28,6 +28,7 @@ pub struct TextEntry {
value: Rc>>,
widget: gtk::Entry,
+ renderer: Rc String>,
parser: Rc>,
on_update: Rc>,
}
@@ -64,6 +65,7 @@ impl TextEntry {
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 TextEntry {
s
}
+ pub fn set_value(&self, val: Option) {
+ if let Some(ref v) = val {
+ self.widget.set_text(&(self.renderer)(v));
+ }
+ *self.value.borrow_mut() = val;
+ }
+
fn handle_text_change(&self, buffer: >k::EntryBuffer) {
if buffer.text().is_empty() {
*self.value.borrow_mut() = None;
diff --git a/fitnesstrax/app/src/views/historical_view.rs b/fitnesstrax/app/src/views/historical_view.rs
index a7accc4..51ec3a5 100644
--- a/fitnesstrax/app/src/views/historical_view.rs
+++ b/fitnesstrax/app/src/views/historical_view.rs
@@ -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(>k::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)