Completely switch daydetail to navigation and remove the modal

This commit is contained in:
Savanni D'Gerinel 2023-12-25 10:02:24 -05:00
parent 7b851ba051
commit e00d0bdf51
5 changed files with 27 additions and 244 deletions

View File

@ -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;

View File

@ -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)

View File

@ -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(
&gtk::Label::builder()
.halign(gtk::Align::Start)
.label("15km of biking in 60 minutes")
.build(),
);
}
}
/*
pub struct DayDetailPrivate {
date: gtk::Label,
weight: RefCell<Option<gtk::Label>>,
@ -122,24 +127,9 @@ glib::wrapper! {
}
impl DayDetail {
pub fn new() -> Self {
pub fn new(date: chrono::NaiveDate, records: Vec<TraxRecord>) -> Self {
let s: Self = Object::builder().build();
s.set_orientation(gtk::Orientation::Vertical);
s.set_css_classes(&["modal"]);
s.append(&gtk::Label::new(Some("abcdefg")));
s
}
}
*/
pub fn day_detail(date: chrono::NaiveDate, records: Vec<TraxRecord>) -> 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 {
@ -155,11 +145,9 @@ pub fn day_detail(date: chrono::NaiveDate, records: Vec<TraxRecord>) -> gtk::Wid
record_view.add_css_class("day-detail");
record_view.set_halign(gtk::Align::Start);
content.append(&record_view);
s.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()
s
}
}

View File

@ -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::*};

View File

@ -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<gtk::Widget>,
primary_action: RefCell<gtk::Button>,
secondary_action: RefCell<Option<gtk::Button>>,
// tertiary_action: RefCell<Option<gtk::Button>>,
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<ModalPrivate>) @extends gtk::Box, gtk::Widget, @implements gtk::Orientable;
}
pub trait ModalImpl: BoxImpl {}
unsafe impl<T: ModalImpl> IsSubclassable<T> for Modal {}
impl Modal {
pub fn new<OnCancel>(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<Box<dyn FnOnce()>>,
}
#[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<ModalHeaderPrivate>) @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(&center);
s.append(&right);
s
}
pub fn set_title(&self, title: &str) {
self.imp().title.set_text(title);
}
pub fn set_on_cancel<OnCancel>(&self, on_cancel: OnCancel)
where
OnCancel: FnOnce() + 'static,
{
*self.imp().on_cancel.borrow_mut() = Box::new(on_cancel);
}
}