Rename recurring component to re-use it for savings

This commit is contained in:
Quentin Boyer 2023-11-12 19:38:52 +01:00
parent 4261ab76ca
commit fd57d4644f

View file

@ -30,41 +30,41 @@ const TEXT_EMPH2: u16 = 20;
const LIST_RULE: u16 = 5; const LIST_RULE: u16 = 5;
const SECTION_RULE: u16 = 15; const SECTION_RULE: u16 = 15;
struct AddRecurring<F> { struct AddFixed<F> {
on_submit: F, on_submit: F,
} }
#[derive(Default)] #[derive(Default)]
struct AddRecurringState { struct AddFixedState {
value: String, value: String,
item: String, item: String,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
enum AddRecurringEvent { enum AddFixedEvent {
SetItem(String), SetItem(String),
SetValue(String), SetValue(String),
SubmitValue, SubmitValue,
} }
impl<F> AddRecurring<F> { impl<F> AddFixed<F> {
fn new(on_submit: F) -> Self { fn new(on_submit: F) -> Self {
Self { on_submit } Self { on_submit }
} }
} }
impl<M, F> iced::widget::Component<M, Renderer> for AddRecurring<F> impl<M, F> iced::widget::Component<M, Renderer> for AddFixed<F>
where where
F: FnMut(String, f64) -> M, F: FnMut(String, f64) -> M,
{ {
type State = AddRecurringState; type State = AddFixedState;
type Event = AddRecurringEvent; type Event = AddFixedEvent;
fn update(&mut self, state: &mut Self::State, event: Self::Event) -> Option<M> { fn update(&mut self, state: &mut Self::State, event: Self::Event) -> Option<M> {
match event { match event {
AddRecurringEvent::SetItem(i) => state.item = i, AddFixedEvent::SetItem(i) => state.item = i,
AddRecurringEvent::SetValue(v) => state.value = v, AddFixedEvent::SetValue(v) => state.value = v,
AddRecurringEvent::SubmitValue => { AddFixedEvent::SubmitValue => {
if let Ok(v) = state.value.parse() { if let Ok(v) = state.value.parse() {
state.value.clear(); state.value.clear();
return Some((self.on_submit)(std::mem::take(&mut state.item), v)); return Some((self.on_submit)(std::mem::take(&mut state.item), v));
@ -77,36 +77,36 @@ where
fn view(&self, state: &Self::State) -> iced::Element<'_, Self::Event, Renderer> { fn view(&self, state: &Self::State) -> iced::Element<'_, Self::Event, Renderer> {
column![ column![
text_input("item", &state.item).on_input(AddRecurringEvent::SetItem), text_input("item", &state.item).on_input(AddFixedEvent::SetItem),
text_input("value", &state.value) text_input("value", &state.value)
.on_input(AddRecurringEvent::SetValue) .on_input(AddFixedEvent::SetValue)
.on_submit(AddRecurringEvent::SubmitValue) .on_submit(AddFixedEvent::SubmitValue)
] ]
.into() .into()
} }
} }
struct EditRecurring<'a, F> { struct EditFixed<'a, F> {
value: String, value: String,
name: &'a str, name: &'a str,
on_submit: F, on_submit: F,
} }
#[derive(Default)] #[derive(Default)]
struct EditRecurringState { struct EditFixedState {
edit: Option<String>, edit: Option<String>,
modal: bool, modal: bool,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
enum EditRecurringEvent { enum EditFixedgEvent {
Edit(String), Edit(String),
Open, Open,
Close, Close,
Submit, Submit,
} }
impl<'a, F> EditRecurring<'a, F> { impl<'a, F> EditFixed<'a, F> {
fn new(value: f64, name: &'a str, on_submit: F) -> Self { fn new(value: f64, name: &'a str, on_submit: F) -> Self {
Self { Self {
value: value.to_string(), value: value.to_string(),
@ -116,17 +116,17 @@ impl<'a, F> EditRecurring<'a, F> {
} }
} }
impl<M, F> iced::widget::Component<M, Renderer> for EditRecurring<'_, F> impl<M, F> iced::widget::Component<M, Renderer> for EditFixed<'_, F>
where where
F: FnMut(f64) -> M, F: FnMut(f64) -> M,
{ {
type State = EditRecurringState; type State = EditFixedState;
type Event = EditRecurringEvent; type Event = EditFixedgEvent;
fn update(&mut self, state: &mut Self::State, event: Self::Event) -> Option<M> { fn update(&mut self, state: &mut Self::State, event: Self::Event) -> Option<M> {
match event { match event {
EditRecurringEvent::Edit(v) => state.edit = Some(v), EditFixedgEvent::Edit(v) => state.edit = Some(v),
EditRecurringEvent::Submit => { EditFixedgEvent::Submit => {
if let Some(e) = &state.edit { if let Some(e) = &state.edit {
if let Ok(v) = e.parse() { if let Ok(v) = e.parse() {
state.edit = None; state.edit = None;
@ -135,10 +135,10 @@ where
} }
} }
} }
EditRecurringEvent::Open => { EditFixedgEvent::Open => {
state.modal = true; state.modal = true;
} }
EditRecurringEvent::Close => { EditFixedgEvent::Close => {
state.modal = false; state.modal = false;
state.edit = None; state.edit = None;
} }
@ -148,60 +148,62 @@ where
} }
fn view(&self, state: &Self::State) -> iced_aw::Element<'_, Self::Event, Renderer> { fn view(&self, state: &Self::State) -> iced_aw::Element<'_, Self::Event, Renderer> {
let underlay = button(text("Edit")).on_press(EditRecurringEvent::Open); let underlay = button(text("Edit")).on_press(EditFixedgEvent::Open);
let overlay = match state.modal { let overlay = match state.modal {
true => Some( true => Some(
card( card(
text(&format!("Edit {}", self.name)), text(&format!("Edit {}", self.name)),
text_input("new value", state.edit.as_ref().unwrap_or(&self.value)) text_input("new value", state.edit.as_ref().unwrap_or(&self.value))
.on_input(EditRecurringEvent::Edit) .on_input(EditFixedgEvent::Edit)
.on_submit(EditRecurringEvent::Submit), .on_submit(EditFixedgEvent::Submit),
) )
.max_width(300.0) .max_width(300.0)
.on_close(EditRecurringEvent::Close), .on_close(EditFixedgEvent::Close),
), ),
false => None, false => None,
}; };
modal(underlay, overlay) modal(underlay, overlay)
.backdrop(EditRecurringEvent::Close) .backdrop(EditFixedgEvent::Close)
.on_esc(EditRecurringEvent::Close) .on_esc(EditFixedgEvent::Close)
.into() .into()
} }
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
enum RecurringMessage { enum FixedAmountsMessage {
CloseAdd, CloseAdd,
Add, Add,
DoAdd(String, f64), DoAdd(String, f64),
} }
#[derive(Default)] #[derive(Default)]
struct RecurringState { struct FixedAmountsState {
add_recurring: bool, add: bool,
} }
struct Recurring<'a, F> { struct FixedAmounts<'a, F> {
items: &'a BTreeMap<String, f64>, items: &'a BTreeMap<String, f64>,
add_label: &'a str,
title: &'a str,
on_add: F, on_add: F,
} }
impl<M, F> iced::widget::Component<M, Renderer> for Recurring<'_, F> impl<M, F> iced::widget::Component<M, Renderer> for FixedAmounts<'_, F>
where where
F: FnMut(String, f64) -> M, F: FnMut(String, f64) -> M,
{ {
type State = RecurringState; type State = FixedAmountsState;
type Event = RecurringMessage; type Event = FixedAmountsMessage;
fn update(&mut self, state: &mut Self::State, event: Self::Event) -> Option<M> { fn update(&mut self, state: &mut Self::State, event: Self::Event) -> Option<M> {
match event { match event {
RecurringMessage::Add => state.add_recurring = true, FixedAmountsMessage::Add => state.add = true,
RecurringMessage::CloseAdd => { FixedAmountsMessage::CloseAdd => {
state.add_recurring = false; state.add = false;
} }
RecurringMessage::DoAdd(item, value) => { FixedAmountsMessage::DoAdd(item, value) => {
state.add_recurring = false; state.add = false;
return Some((self.on_add)(item, value)); return Some((self.on_add)(item, value));
} }
} }
@ -212,8 +214,8 @@ where
#[allow(unstable_name_collisions)] #[allow(unstable_name_collisions)]
fn view(&self, state: &Self::State) -> iced_aw::Element<'_, Self::Event, Renderer> { fn view(&self, state: &Self::State) -> iced_aw::Element<'_, Self::Event, Renderer> {
let underlay = column![ let underlay = column![
text("Recurring").size(TEXT_H2), text(self.title).size(TEXT_H2),
button(text("Add")).on_press(RecurringMessage::Add), button(text("Add")).on_press(FixedAmountsMessage::Add),
horizontal_rule(LIST_RULE), horizontal_rule(LIST_RULE),
column( column(
self.items self.items
@ -222,11 +224,10 @@ where
text(name).size(TEXT_EMPH1), text(name).size(TEXT_EMPH1),
text(&format!("{value}")), text(&format!("{value}")),
horizontal_space(Length::Fill), horizontal_space(Length::Fill),
component(EditRecurring::new( component(EditFixed::new(value, name, |v| FixedAmountsMessage::DoAdd(
value, name.to_string(),
name, v
|v| RecurringMessage::DoAdd(name.to_string(), v) )))
))
] ]
.spacing(5) .spacing(5)
.align_items(iced::Alignment::Center) .align_items(iced::Alignment::Center)
@ -238,21 +239,21 @@ where
text(&format!("Total: {}", self.items.values().sum::<f64>())).size(TEXT_EMPH2) text(&format!("Total: {}", self.items.values().sum::<f64>())).size(TEXT_EMPH2)
]; ];
let overlay = match state.add_recurring { let overlay = match state.add {
true => Some( true => Some(
card( card(
text("Add a recurring spending"), text(self.add_label),
component(AddRecurring::new(RecurringMessage::DoAdd)), component(AddFixed::new(FixedAmountsMessage::DoAdd)),
) )
.on_close(RecurringMessage::CloseAdd) .on_close(FixedAmountsMessage::CloseAdd)
.max_width(300.0), .max_width(300.0),
), ),
false => None, false => None,
}; };
modal(underlay, overlay) modal(underlay, overlay)
.backdrop(RecurringMessage::CloseAdd) .backdrop(FixedAmountsMessage::CloseAdd)
.on_esc(RecurringMessage::CloseAdd) .on_esc(FixedAmountsMessage::CloseAdd)
.into() .into()
} }
} }
@ -653,8 +654,10 @@ impl Application for Glaurung {
fn view(&self) -> Element { fn view(&self) -> Element {
column![ column![
text("Spendings").size(TEXT_H1), text("Spendings").size(TEXT_H1),
component(Recurring { component(FixedAmounts {
items: &self.recurring, items: &self.recurring,
title: "Recurring",
add_label: "Add a recurring spending",
on_add: Message::AddRecurring, on_add: Message::AddRecurring,
}), }),
horizontal_rule(SECTION_RULE).style(|theme: &Theme| { horizontal_rule(SECTION_RULE).style(|theme: &Theme| {