/* 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::components::{EditView, ParseError, Singleton, TextEntry}; use chrono::{Local, NaiveDate}; use dimensioned::si; use glib::{object::ObjectRef, Object}; use gtk::{prelude::*, subclass::prelude::*}; use std::{borrow::Borrow, cell::RefCell}; #[derive(Default)] pub struct WeightViewPrivate { /* date: RefCell, record: RefCell>, widget: RefCell>>>, on_edit_finished: RefCell)>>, */ } /* impl Default for WeightViewPrivate { fn default() -> Self { Self { date: RefCell::new(Local::now().date_naive()), record: RefCell::new(None), widget: RefCell::new(EditView::Unconfigured), container: Singleton::default(), on_edit_finished: RefCell::new(Box::new(|_| {})), } } } */ /* #[glib::object_subclass] impl ObjectSubclass for WeightViewPrivate { const NAME: &'static str = "WeightView"; type Type = WeightView; type ParentType = gtk::Label; } impl ObjectImpl for WeightViewPrivate {} impl WidgetImpl for WeightViewPrivate {} impl LabelImpl for WeightViewPrivate {} glib::wrapper! { pub struct WeightView(ObjectSubclass) @extends gtk::Label, gtk::Widget; } impl WeightView { pub fn new( date: NaiveDate, weight: Option, on_edit_finished: OnEditFinished, ) -> Self where OnEditFinished: Fn(si::Kilogram) + 'static, { let s: Self = Object::builder().build(); s.set_css_classes(&["card", "weight-view"]); s.set_can_focus(true); s.append(&s.imp().container); match weight { Some(weight) => { s.remove_css_class("dim_label"); s.set_label(&format!("{:?}", weight)); } None => { s.add_css_class("dim_label"); s.set_label("No weight recorded"); } } /* *s.imp().on_edit_finished.borrow_mut() = Box::new(on_edit_finished); *s.imp().date.borrow_mut() = date; *s.imp().record.borrow_mut() = weight; s.view(); */ s } /* fn view(&self) { let view = gtk::Label::builder() .css_classes(["card", "weight-view"]) .halign(gtk::Align::Start) .can_focus(true) .build(); let view_click_controller = gtk::GestureClick::new(); view_click_controller.connect_released({ let s = self.clone(); move |_, _, _, _| { s.edit(); } }); view.add_controller(view_click_controller); match *self.imp().record.borrow() { Some(ref record) => { view.remove_css_class("dim_label"); view.set_label(&format!("{:?}", record.weight)); } None => { view.add_css_class("dim_label"); view.set_label("No weight recorded"); } } // self.swap(EditView::View(view)); } fn edit(&self) { let edit = TextEntry::>::new( "weight", None, |val: &si::Kilogram| val.to_string(), |v: &str| v.parse::().map(|w| w * si::KG).map_err(|_| ParseError), ); match *self.imp().record.borrow() { Some(ref record) => edit.set_value(Some(record.weight)), None => edit.set_value(None), } // self.swap(EditView::Edit(edit.clone())); edit.grab_focus(); } */ /* fn swap(&self, new_view: EditView>>) { match new_view { EditView::Unconfigured => {} EditView::View(view) => self.imp().container.swap(&view.upcast()), EditView::Edit(editor) => self.imp().container.swap(&editor.widget()), } /* let mut widget = self.imp().widget.borrow_mut(); match *widget { EditView::Unconfigured => {} EditView::View(ref view) => self.remove(view), EditView::Edit(ref editor) => self.remove(&editor.widget()), } match new_view { EditView::Unconfigured => {} EditView::View(ref view) => self.append(view), EditView::Edit(ref editor) => self.append(&editor.widget()), } *widget = new_view; */ } */ /* pub fn blur(&self) { match *self.imp().widget.borrow() { EditView::Unconfigured => {} EditView::View(_) => {} EditView::Edit(ref editor) => { let weight = editor.value(); // This has really turned into rubbish // on_edit_finished needs to accept a full record now. // needs to be possible to delete a record if the value is None // it's hard to be sure whether I need the full record object or if I need to update // it. I probably don't. I think I need to borrow it and call on_edit_finished with an // updated version of it. // on_edit_finished still doesn't have a way to support a delete operation let record = match (self.imp().record.borrow().clone(), weight) { // update an existing record (Some(record), Some(weight)) => Some(Weight { date: record.date, weight, }), // create a new record (None, Some(weight)) => Some(Weight { date: self.imp().date.borrow().clone(), weight, }), // do nothing or delete an existing record (_, None) => None, }; match record { Some(record) => { self.imp().on_edit_finished.borrow()(record.weight); *self.imp().record.borrow_mut() = Some(record); } None => {} } } } self.view(); } */ } */ pub struct Weight { label: gtk::Label, } impl Weight { pub fn new(weight: Option) -> Self { let label = gtk::Label::builder() .css_classes(["card", "weight-view"]) .can_focus(true) .build(); match weight { Some(w) => label.set_text(&format!("{:?}", w.weight)), None => label.set_text("No weight recorded"), } Self { label } } pub fn widget(&self) -> gtk::Widget { self.label.clone().upcast() } } pub struct WeightEdit { entry: TextEntry>, } impl WeightEdit { pub fn new(weight: Option) -> Self { Self { entry: TextEntry::new( "0 kg", weight.map(|w| w.weight), |val: &si::Kilogram| val.to_string(), |v: &str| v.parse::().map(|w| w * si::KG).map_err(|_| ParseError), ), } } pub fn value(&self) -> Option> { self.entry.value() } pub fn widget(&self) -> gtk::Widget { self.entry.widget() } }