Start setting up date range widgets
This commit is contained in:
parent
e87b332705
commit
3d6e5470ed
|
@ -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/>.
|
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 crate::{components::TextEntry, types::ParseError};
|
||||||
use chrono::Datelike;
|
use chrono::{Datelike, Local};
|
||||||
use gtk::prelude::*;
|
use glib::Object;
|
||||||
|
use gtk::{prelude::*, subclass::prelude::*};
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct DateField {
|
enum Part {
|
||||||
value: Rc<RefCell<chrono::NaiveDate>>,
|
Year,
|
||||||
buffer: gtk::TextBuffer,
|
Month,
|
||||||
widget: gtk::TextView,
|
Day,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DateField {
|
pub struct DateFieldPrivate {
|
||||||
pub fn new(date: chrono::NaiveDate) -> Self {
|
value: Rc<RefCell<chrono::NaiveDate>>,
|
||||||
let buffer = gtk::TextBuffer::new(None);
|
year: TextEntry<i32>,
|
||||||
let tag = buffer.create_tag(
|
month: TextEntry<u32>,
|
||||||
Some("placeholder"),
|
day: TextEntry<u32>,
|
||||||
&[("foreground", &String::from("grey"))],
|
}
|
||||||
|
|
||||||
|
#[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");
|
Self {
|
||||||
let (start, end) = buffer.bounds();
|
value: Rc::new(RefCell::new(date.clone())),
|
||||||
let mut placeholder_start = start.clone();
|
year,
|
||||||
placeholder_start.forward_chars(13);
|
month,
|
||||||
|
day,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let placeholder_markup = buffer.tag_table().lookup("placeholder").unwrap();
|
impl ObjectImpl for DateFieldPrivate {}
|
||||||
buffer.apply_tag(&placeholder_markup, &placeholder_start, &end);
|
impl WidgetImpl for DateFieldPrivate {}
|
||||||
|
impl BoxImpl for DateFieldPrivate {}
|
||||||
|
|
||||||
let widget = gtk::TextView::builder()
|
glib::wrapper! {
|
||||||
.buffer(&buffer)
|
pub struct DateField(ObjectSubclass<DateFieldPrivate>) @extends gtk::Box, gtk::Widget;
|
||||||
.editable(true)
|
}
|
||||||
.can_focus(true)
|
|
||||||
.height_request(50)
|
|
||||||
.width_request(200)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let s = Self {
|
/* 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.
|
||||||
value: Rc::new(RefCell::new(date)),
|
*/
|
||||||
buffer,
|
impl DateField {
|
||||||
widget,
|
pub fn new(date: chrono::NaiveDate) -> Self {
|
||||||
};
|
let s: Self = Object::builder().build();
|
||||||
|
|
||||||
s.widget.buffer().connect_text_notify({
|
println!("{}", date);
|
||||||
let s = s.clone();
|
|
||||||
move |buffer| s.on_update(buffer)
|
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
|
s
|
||||||
}
|
}
|
||||||
|
}
|
||||||
pub fn widget(&self) -> gtk::Widget {
|
|
||||||
self.widget.clone().upcast::<gtk::Widget>()
|
/* As soon as the field gets focus, highlight the first element
|
||||||
}
|
*/
|
||||||
|
|
||||||
fn on_update(&self, buffer: >k::TextBuffer) {
|
#[cfg(test)]
|
||||||
let (start, end) = buffer.bounds();
|
mod test {
|
||||||
println!("[on_update] {}", buffer.text(&start, &end, true));
|
use super::*;
|
||||||
}
|
use crate::gtk_init::gtk_init;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ pub struct TextEntry<T: Clone + std::fmt::Debug> {
|
||||||
value: Rc<RefCell<Option<T>>>,
|
value: Rc<RefCell<Option<T>>>,
|
||||||
|
|
||||||
widget: gtk::Entry,
|
widget: gtk::Entry,
|
||||||
|
renderer: Rc<dyn Fn(&T) -> String>,
|
||||||
parser: Rc<Parser<T>>,
|
parser: Rc<Parser<T>>,
|
||||||
on_update: Rc<OnUpdate<T>>,
|
on_update: Rc<OnUpdate<T>>,
|
||||||
}
|
}
|
||||||
|
@ -64,6 +65,7 @@ impl<T: Clone + std::fmt::Debug + 'static> TextEntry<T> {
|
||||||
let s = Self {
|
let s = Self {
|
||||||
value: Rc::new(RefCell::new(value)),
|
value: Rc::new(RefCell::new(value)),
|
||||||
widget,
|
widget,
|
||||||
|
renderer: Rc::new(renderer),
|
||||||
parser: Rc::new(parser),
|
parser: Rc::new(parser),
|
||||||
on_update: Rc::new(on_update),
|
on_update: Rc::new(on_update),
|
||||||
};
|
};
|
||||||
|
@ -76,6 +78,13 @@ impl<T: Clone + std::fmt::Debug + 'static> TextEntry<T> {
|
||||||
s
|
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: >k::EntryBuffer) {
|
fn handle_text_change(&self, buffer: >k::EntryBuffer) {
|
||||||
if buffer.text().is_empty() {
|
if buffer.text().is_empty() {
|
||||||
*self.value.borrow_mut() = None;
|
*self.value.borrow_mut() = None;
|
||||||
|
|
|
@ -112,9 +112,9 @@ impl HistoricalView {
|
||||||
let date_row = gtk::Box::builder()
|
let date_row = gtk::Box::builder()
|
||||||
.orientation(gtk::Orientation::Horizontal)
|
.orientation(gtk::Orientation::Horizontal)
|
||||||
.build();
|
.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(>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()
|
let quick_picker = gtk::Box::builder()
|
||||||
.orientation(gtk::Orientation::Horizontal)
|
.orientation(gtk::Orientation::Horizontal)
|
||||||
|
|
Loading…
Reference in New Issue