diff --git a/fitnesstrax/app/resources/style.css b/fitnesstrax/app/resources/style.css index 7ea5d9e..4028c61 100644 --- a/fitnesstrax/app/resources/style.css +++ b/fitnesstrax/app/resources/style.css @@ -14,30 +14,6 @@ .welcome__footer { } -.modal { - background-color: @dialog_bg_color; - border-radius: 8px; - margin: 64px; -} - -.modal__header { -} - -.modal__title { - font-size: larger; - padding: 8px; -} - -.modal__content { - background-color: @view_bg_color; - margin: 16px; -} - -.modal__row { - margin: 8px 0px 8px 0px; - padding: 8px; -} - .historical { margin: 32px; border-radius: 8px; diff --git a/fitnesstrax/app/src/app_window.rs b/fitnesstrax/app/src/app_window.rs index 5990dde..5d3b182 100644 --- a/fitnesstrax/app/src/app_window.rs +++ b/fitnesstrax/app/src/app_window.rs @@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License along with Fit use crate::{ app::{AppInvocation, AppResponse}, - components::day_detail, + components::DayDetail, views::{HistoricalView, PlaceholderView, View, ViewName, WelcomeView}, }; use adw::prelude::*; @@ -208,7 +208,7 @@ impl AppWindow { Rc::new(move |date: chrono::NaiveDate, records| { let layout = gtk::Box::new(gtk::Orientation::Vertical, 0); layout.append(&adw::HeaderBar::new()); - layout.append(&day_detail(date, records)); + layout.append(&DayDetail::new(date, records)); let page = &adw::NavigationPage::builder() .title(date.format("%Y-%m-%d").to_string()) .child(&layout) diff --git a/fitnesstrax/app/src/components/day.rs b/fitnesstrax/app/src/components/day.rs index db6f08e..4c1c3db 100644 --- a/fitnesstrax/app/src/components/day.rs +++ b/fitnesstrax/app/src/components/day.rs @@ -16,7 +16,6 @@ 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::Modal; use ft_core::TraxRecord; use glib::Object; use gtk::{prelude::*, subclass::prelude::*}; @@ -86,10 +85,16 @@ impl DaySummary { self.append(&label); *self.imp().weight.borrow_mut() = Some(label); } + + self.append( + >k::Label::builder() + .halign(gtk::Align::Start) + .label("15km of biking in 60 minutes") + .build(), + ); } } -/* pub struct DayDetailPrivate { date: gtk::Label, weight: RefCell>, @@ -122,44 +127,27 @@ glib::wrapper! { } impl DayDetail { - pub fn new() -> Self { + pub fn new(date: chrono::NaiveDate, records: Vec) -> Self { let s: Self = Object::builder().build(); s.set_orientation(gtk::Orientation::Vertical); - s.set_css_classes(&["modal"]); - s.append(>k::Label::new(Some("abcdefg"))); + records.into_iter().for_each(|record| { + let record_view = match record { + TraxRecord::BikeRide(_) => gtk::Label::new(Some("BikeRide")), + TraxRecord::Row(_) => gtk::Label::new(Some("Row")), + TraxRecord::Run(_) => gtk::Label::new(Some("Run")), + TraxRecord::Steps(_) => gtk::Label::new(Some("Steps")), + TraxRecord::Swim(_) => gtk::Label::new(Some("Swim")), + TraxRecord::Walk(_) => gtk::Label::new(Some("Walk")), + TraxRecord::Weight(_) => gtk::Label::new(Some("Weight")), + }; + + record_view.add_css_class("day-detail"); + record_view.set_halign(gtk::Align::Start); + + s.append(&record_view); + }); s } } -*/ - -pub fn day_detail(date: chrono::NaiveDate, records: Vec) -> gtk::Widget { - let modal = Modal::new(&date.format("%Y-%m-%d").to_string(), || {}); - - let content = gtk::Box::builder() - .orientation(gtk::Orientation::Vertical) - .build(); - - records.into_iter().for_each(|record| { - let record_view = match record { - TraxRecord::BikeRide(_) => gtk::Label::new(Some("BikeRide")), - TraxRecord::Row(_) => gtk::Label::new(Some("Row")), - TraxRecord::Run(_) => gtk::Label::new(Some("Run")), - TraxRecord::Steps(_) => gtk::Label::new(Some("Steps")), - TraxRecord::Swim(_) => gtk::Label::new(Some("Swim")), - TraxRecord::Walk(_) => gtk::Label::new(Some("Walk")), - TraxRecord::Weight(_) => gtk::Label::new(Some("Weight")), - }; - - record_view.add_css_class("day-detail"); - record_view.set_halign(gtk::Align::Start); - - content.append(&record_view); - }); - - modal.set_content(content.upcast()); - modal.set_primary_action(gtk::Button::builder().label("Save").build()); - modal.set_secondary_action(gtk::Button::builder().label("Cancel").build()); - modal.upcast() -} diff --git a/fitnesstrax/app/src/components/mod.rs b/fitnesstrax/app/src/components/mod.rs index 528b427..c6bce7d 100644 --- a/fitnesstrax/app/src/components/mod.rs +++ b/fitnesstrax/app/src/components/mod.rs @@ -15,10 +15,7 @@ You should have received a copy of the GNU General Public License along with Fit */ mod day; -pub use day::{day_detail, DaySummary}; - -mod modal; -pub use modal::{Modal, ModalImpl}; +pub use day::{DayDetail, DaySummary}; use glib::Object; use gtk::{prelude::*, subclass::prelude::*}; diff --git a/fitnesstrax/app/src/components/modal.rs b/fitnesstrax/app/src/components/modal.rs deleted file mode 100644 index c53d750..0000000 --- a/fitnesstrax/app/src/components/modal.rs +++ /dev/null @@ -1,178 +0,0 @@ -//! The Modal is a reusable component with a title, arbitrary content, and up to three action -//! buttons. It does not itself enforce being a modal, but is meant to become a child of an Overlay -//! component. -use glib::Object; -use gtk::{prelude::*, subclass::prelude::*}; -use std::cell::RefCell; - -pub struct ModalPrivate { - header: ModalHeader, - content: RefCell, - primary_action: RefCell, - secondary_action: RefCell>, - // tertiary_action: RefCell>, - footer: gtk::Box, -} - -#[glib::object_subclass] -impl ObjectSubclass for ModalPrivate { - const NAME: &'static str = "Modal"; - type Type = Modal; - type ParentType = gtk::Box; - - fn new() -> Self { - let header = ModalHeader::new("Modal"); - let content = gtk::Box::builder() - .orientation(gtk::Orientation::Vertical) - .vexpand(true) - .css_classes(["modal__content"]) - .build(); - let primary_action = gtk::Button::builder().label("Primary").build(); - let footer = gtk::Box::builder() - .orientation(gtk::Orientation::Horizontal) - .hexpand(true) - .halign(gtk::Align::End) - .build(); - footer.append(&primary_action); - - Self { - header, - content: RefCell::new(content.upcast()), - primary_action: RefCell::new(primary_action), - secondary_action: RefCell::new(None), - // tertiary_action: RefCell::new(None), - footer, - } - } -} - -impl ObjectImpl for ModalPrivate {} -impl WidgetImpl for ModalPrivate {} -impl BoxImpl for ModalPrivate {} - -glib::wrapper! { - pub struct Modal(ObjectSubclass) @extends gtk::Box, gtk::Widget, @implements gtk::Orientable; -} - -pub trait ModalImpl: BoxImpl {} - -unsafe impl IsSubclassable for Modal {} - -impl Modal { - pub fn new(title: &str, on_cancel: OnCancel) -> Self - where - OnCancel: FnOnce() + 'static, - { - let s: Self = Object::builder().build(); - s.set_css_classes(&["modal"]); - s.set_orientation(gtk::Orientation::Vertical); - s.set_hexpand(true); - - s.imp().header.set_title(title); - s.imp().header.set_on_cancel(on_cancel); - - s.append(&s.imp().header); - s.append(&*s.imp().content.borrow()); - s.append(&s.imp().footer); - - let gesture = gtk::GestureClick::new(); - gesture.connect_released(|s, _, _, _| println!("released on {:?}", s)); - s.add_controller(gesture.clone()); - - s - } - - pub fn set_title(&self, text: &str) { - self.imp().header.set_title(text); - } - - pub fn set_content(&self, content: gtk::Widget) { - self.remove(&*self.imp().content.borrow()); - content.set_halign(gtk::Align::Fill); - content.set_vexpand(true); - content.add_css_class("modal__content"); - self.insert_child_after(&content, Some(&self.imp().header)); - *self.imp().content.borrow_mut() = content; - } - - pub fn set_primary_action(&self, action: gtk::Button) { - *self.imp().primary_action.borrow_mut() = action; - self.render_footer(); - } - - pub fn set_secondary_action(&self, action: gtk::Button) { - *self.imp().secondary_action.borrow_mut() = Some(action); - self.render_footer(); - } - - fn render_footer(&self) { - while let Some(ref child) = self.imp().footer.first_child() { - self.imp().footer.remove(child); - } - - if let Some(ref button) = *self.imp().secondary_action.borrow() { - self.imp().footer.append(button); - } - self.imp() - .footer - .append(&*self.imp().primary_action.borrow()); - } -} - -pub struct ModalHeaderPrivate { - title: gtk::Label, - on_cancel: RefCell>, -} - -#[glib::object_subclass] -impl ObjectSubclass for ModalHeaderPrivate { - const NAME: &'static str = "ModalHeader"; - type Type = ModalHeader; - type ParentType = gtk::Box; - - fn new() -> Self { - Self { - title: gtk::Label::builder().css_classes(["modal__title"]).build(), - on_cancel: RefCell::new(Box::new(|| {})), - } - } -} - -impl ObjectImpl for ModalHeaderPrivate {} -impl WidgetImpl for ModalHeaderPrivate {} -impl BoxImpl for ModalHeaderPrivate {} - -glib::wrapper! { - pub struct ModalHeader(ObjectSubclass) @extends gtk::Box, gtk::Widget, @implements gtk::Orientable; -} - -impl ModalHeader { - pub fn new(title: &str) -> Self { - let s: Self = Object::builder().build(); - s.add_css_class("modal__header"); - s.set_homogeneous(true); - s.imp().title.set_text(title); - - let left = gtk::Box::builder().build(); - let center = gtk::Box::builder().halign(gtk::Align::Center).build(); - let right = gtk::Box::builder().halign(gtk::Align::End).build(); - - center.append(&s.imp().title); - s.append(&left); - s.append(¢er); - s.append(&right); - - s - } - - pub fn set_title(&self, title: &str) { - self.imp().title.set_text(title); - } - - pub fn set_on_cancel(&self, on_cancel: OnCancel) - where - OnCancel: FnOnce() + 'static, - { - *self.imp().on_cancel.borrow_mut() = Box::new(on_cancel); - } -}