diff --git a/fitnesstrax/app/src/components/day.rs b/fitnesstrax/app/src/components/day.rs index 9e5a28b..b16980d 100644 --- a/fitnesstrax/app/src/components/day.rs +++ b/fitnesstrax/app/src/components/day.rs @@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License along with Fit // use chrono::NaiveDate; // use ft_core::TraxRecord; -use crate::components::{ParseError, TextEntry}; +use crate::components::{EditView, ParseError, TextEntry}; use chrono::{Local, NaiveDate}; use dimensioned::si; use emseries::Record; @@ -221,49 +221,21 @@ impl DayDetail { } } -#[derive(Clone, Copy, PartialEq)] -enum WeightViewMode { - View, - Edit, -} - pub struct WeightViewPrivate { date: RefCell, record: RefCell>, - view: RefCell, - edit: RefCell>>, - current: RefCell, - mode: RefCell, - // If I create a swappable component and don't make it a true GTK component, the way - // TextEntry ended up not a true GTK component, on_edit_finished will not need to be a - // RefCell. + widget: RefCell>>>, + on_edit_finished: RefCell)>>, } impl Default for WeightViewPrivate { fn default() -> Self { - let view = gtk::Label::builder() - .css_classes(["card", "weight-view"]) - .halign(gtk::Align::Start) - .can_focus(true) - .build(); - let edit = TextEntry::>::new( - "weight", - None, - |w: &si::Kilogram| w.to_string(), - |w: &str| w.parse::().map(|w| w * si::KG).map_err(|_| ParseError), - ); - - let current = view.clone(); - Self { date: RefCell::new(Local::now().date_naive()), record: RefCell::new(None), - view: RefCell::new(view), - edit: RefCell::new(edit), - current: RefCell::new(current.upcast()), - mode: RefCell::new(WeightViewMode::View), + widget: RefCell::new(EditView::Unconfigured), on_edit_finished: RefCell::new(Box::new(|_| {})), } } @@ -301,20 +273,26 @@ impl WeightView { *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 = s.clone(); + let s = self.clone(); move |_, _, _, _| { s.edit(); } }); - s.imp().view.borrow().add_controller(view_click_controller); - s - } + view.add_controller(view_click_controller); - fn view(&self) { - let view = self.imp().view.borrow(); match *self.imp().record.borrow() { Some(ref record) => { view.remove_css_class("dim_label"); @@ -325,69 +303,83 @@ impl WeightView { view.set_label("No weight recorded"); } } - *self.imp().mode.borrow_mut() = WeightViewMode::View; - self.swap(view.clone().upcast()); + + self.swap(EditView::View(view)); } fn edit(&self) { - let edit = self.imp().edit.borrow(); + 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.imp().mode.borrow_mut() = WeightViewMode::Edit; - self.swap(edit.widget()); + + self.swap(EditView::Edit(edit.clone())); edit.grab_focus(); } - fn swap(&self, new_view: gtk::Widget) { - let mut current = self.imp().current.borrow_mut(); - self.remove(&*current); - self.append(&new_view); - *current = new_view; + fn swap(&self, new_view: EditView>>) { + 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; } fn blur(&self) { - if *self.imp().mode.borrow() == WeightViewMode::Edit { - println!("on_blur"); - let weight = self.imp().edit.borrow().value(); + 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, + }), - println!("new weight: {:?}", weight); + // create a new record + (None, Some(weight)) => Some(Weight { + date: self.imp().date.borrow().clone(), + weight, + }), - // 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, - }), + // do nothing or delete an existing record + (_, None) => None, + }; - // 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, - }; - - println!("updated record: {:?}", record); - - match record { - Some(record) => { - self.imp().on_edit_finished.borrow()(record.weight); - *self.imp().record.borrow_mut() = Some(record); + match record { + Some(record) => { + self.imp().on_edit_finished.borrow()(record.weight); + *self.imp().record.borrow_mut() = Some(record); + } + None => {} } - None => {} } } + self.view(); } } diff --git a/fitnesstrax/app/src/components/edit_view.rs b/fitnesstrax/app/src/components/edit_view.rs new file mode 100644 index 0000000..f6f96a9 --- /dev/null +++ b/fitnesstrax/app/src/components/edit_view.rs @@ -0,0 +1,22 @@ +/* +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 . +*/ + +#[derive(Clone)] +pub enum EditView { + Unconfigured, + View(View), + Edit(Edit), +} diff --git a/fitnesstrax/app/src/components/mod.rs b/fitnesstrax/app/src/components/mod.rs index 450fad8..30e7908 100644 --- a/fitnesstrax/app/src/components/mod.rs +++ b/fitnesstrax/app/src/components/mod.rs @@ -17,6 +17,9 @@ You should have received a copy of the GNU General Public License along with Fit mod day; pub use day::{DayDetail, DaySummary}; +mod edit_view; +pub use edit_view::EditView; + mod text_entry; pub use text_entry::{ParseError, TextEntry};