Rename recurring component to re-use it for savings
This commit is contained in:
parent
4261ab76ca
commit
fd57d4644f
1 changed files with 59 additions and 56 deletions
115
src/main.rs
115
src/main.rs
|
|
@ -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| {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue