/* Copyright 2023, Savanni D'Gerinel 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 . */ use crate::{ app::App, components::DaySummary, types::DayInterval, view_models::DayDetailViewModel, }; use emseries::Record; use ft_core::TraxRecord; use glib::Object; use gtk::{prelude::*, subclass::prelude::*}; use std::{cell::RefCell, rc::Rc}; /// The historical view will show a window into the main database. It will show some version of /// daily summaries, daily details, and will provide all functions the user may need for editing /// records. pub struct HistoricalViewPrivate { app: Rc>>, time_window: Rc>, list_view: gtk::ListView, } #[glib::object_subclass] impl ObjectSubclass for HistoricalViewPrivate { const NAME: &'static str = "HistoricalView"; type Type = HistoricalView; type ParentType = gtk::Box; fn new() -> Self { let factory = gtk::SignalListItemFactory::new(); factory.connect_setup(move |_, list_item| { list_item .downcast_ref::() .expect("should be a ListItem") .set_child(Some(&DaySummary::new())); }); let s = Self { app: Rc::new(RefCell::new(None)), time_window: Rc::new(RefCell::new(DayInterval::default())), list_view: gtk::ListView::builder() .factory(&factory) .single_click_activate(true) .build(), }; factory.connect_bind({ let app = s.app.clone(); move |_, list_item| { let date = list_item .downcast_ref::() .expect("should be a ListItem") .item() .and_downcast::() .expect("should be a Date"); let summary = list_item .downcast_ref::() .expect("should be a ListItem") .child() .and_downcast::() .expect("should be a DaySummary"); if let Some(app) = app.borrow().clone() { glib::spawn_future_local(async move { let view_model = DayDetailViewModel::new(date.date(), app.clone()).await; summary.set_data(view_model); }); } } }); s } } impl ObjectImpl for HistoricalViewPrivate {} impl WidgetImpl for HistoricalViewPrivate {} impl BoxImpl for HistoricalViewPrivate {} glib::wrapper! { pub struct HistoricalView(ObjectSubclass) @extends gtk::Box, gtk::Widget, @implements gtk::Orientable; } impl HistoricalView { pub fn new(app: App, interval: DayInterval, on_select_day: Rc) -> Self where SelectFn: Fn(chrono::NaiveDate, Vec>) + 'static, { let s: Self = Object::builder().build(); s.set_orientation(gtk::Orientation::Vertical); s.set_css_classes(&["historical"]); *s.imp().app.borrow_mut() = Some(app); let mut model = gio::ListStore::new::(); model.extend(interval.days().map(Date::new)); s.imp() .list_view .set_model(Some(>k::NoSelection::new(Some(model)))); s.imp().list_view.connect_activate({ let on_select_day = on_select_day.clone(); move |s, idx| { // This gets triggered whenever the user clicks on an item on the list. let item = s.model().unwrap().item(idx).unwrap(); let date = item.downcast_ref::().unwrap(); on_select_day(date.date(), vec![]); } }); s.append(&s.imp().list_view); s } pub fn time_window(&self) -> DayInterval { self.imp().time_window.borrow().clone() } } #[derive(Default)] pub struct DatePrivate { date: RefCell, } #[glib::object_subclass] impl ObjectSubclass for DatePrivate { const NAME: &'static str = "Date"; type Type = Date; } impl ObjectImpl for DatePrivate {} glib::wrapper! { pub struct Date(ObjectSubclass); } impl Date { pub fn new(date: chrono::NaiveDate) -> Self { let s: Self = Object::builder().build(); *s.imp().date.borrow_mut() = date; s } pub fn date(&self) -> chrono::NaiveDate { *self.imp().date.borrow() } }