Add recurring spendings
This commit is contained in:
parent
90b987c7b0
commit
e9058c6b27
4 changed files with 349 additions and 13 deletions
81
Cargo.lock
generated
81
Cargo.lock
generated
|
|
@ -449,6 +449,12 @@ version = "1.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
|
|
@ -674,6 +680,8 @@ name = "glaurung"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"iced",
|
||||
"iced_aw",
|
||||
"itertools",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -803,6 +811,12 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.3"
|
||||
|
|
@ -829,6 +843,15 @@ dependencies = [
|
|||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iced_aw"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46c51860ce7be5be6f6104c4e13b14e56662ebbd7c96c50e10069d59f8c3d892"
|
||||
dependencies = [
|
||||
"iced_widget",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iced_core"
|
||||
version = "0.10.0"
|
||||
|
|
@ -957,6 +980,7 @@ dependencies = [
|
|||
"iced_runtime",
|
||||
"iced_style",
|
||||
"num-traits",
|
||||
"ouroboros",
|
||||
"thiserror",
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
|
@ -1022,6 +1046,15 @@ dependencies = [
|
|||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jni-sys"
|
||||
version = "0.3.0"
|
||||
|
|
@ -1493,6 +1526,30 @@ dependencies = [
|
|||
"libredox",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ouroboros"
|
||||
version = "0.17.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2ba07320d39dfea882faa70554b4bd342a5f273ed59ba7c1c6b4c840492c954"
|
||||
dependencies = [
|
||||
"aliasable",
|
||||
"ouroboros_macro",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ouroboros_macro"
|
||||
version = "0.17.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec4c6225c69b4ca778c0aea097321a64c421cf4577b331c61b229267edabb6f8"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "owned_ttf_parser"
|
||||
version = "0.20.0"
|
||||
|
|
@ -1668,6 +1725,30 @@ dependencies = [
|
|||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.69"
|
||||
|
|
|
|||
|
|
@ -5,4 +5,9 @@ authors = ["traxys <quentin@familleboyer.net>"]
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
iced = "0.10.0"
|
||||
iced = { version = "0.10.0", features = ["lazy"] }
|
||||
iced_aw = { version = "0.7.0", default-features = false, features = [
|
||||
"modal",
|
||||
"card",
|
||||
] }
|
||||
itertools = "0.11.0"
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
libPath = with pkgs; lib.makeLibraryPath [libxkbcommon wayland];
|
||||
in {
|
||||
devShell = pkgs.mkShell {
|
||||
nativeBuildInputs = [rust];
|
||||
nativeBuildInputs = [rust pkgs.cargo-watch];
|
||||
RUST_PATH = "${rust}";
|
||||
RUST_DOC_PATH = "${rust}/share/doc/rust/html/std/index.html";
|
||||
LD_LIBRARY_PATH = libPath;
|
||||
|
|
|
|||
272
src/main.rs
272
src/main.rs
|
|
@ -1,27 +1,277 @@
|
|||
use iced::{widget::column, Sandbox, Settings};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum Message {}
|
||||
use iced::{
|
||||
font,
|
||||
widget::{button, column, component, horizontal_rule, row, text, text_input},
|
||||
Application, Command, Renderer, Settings, Theme,
|
||||
};
|
||||
use iced_aw::{card, modal};
|
||||
use itertools::Itertools;
|
||||
|
||||
struct Glaurung {}
|
||||
type Element<'a> = iced::Element<'a, Message>;
|
||||
|
||||
impl Sandbox for Glaurung {
|
||||
#[derive(Clone, Debug)]
|
||||
enum Message {
|
||||
AddRecurring(String, f64),
|
||||
FontLoaded(Result<(), font::Error>),
|
||||
}
|
||||
|
||||
struct Glaurung {
|
||||
recurring: BTreeMap<String, f64>,
|
||||
}
|
||||
|
||||
struct AddRecurring<F> {
|
||||
on_submit: F,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct AddRecurringState {
|
||||
value: String,
|
||||
item: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum AddRecurringEvent {
|
||||
SetItem(String),
|
||||
SetValue(String),
|
||||
SubmitValue,
|
||||
}
|
||||
|
||||
impl<F> AddRecurring<F> {
|
||||
fn new(on_submit: F) -> Self {
|
||||
Self { on_submit }
|
||||
}
|
||||
}
|
||||
|
||||
impl<M, F> iced::widget::Component<M, Renderer> for AddRecurring<F>
|
||||
where
|
||||
F: FnMut(String, f64) -> M,
|
||||
{
|
||||
type State = AddRecurringState;
|
||||
type Event = AddRecurringEvent;
|
||||
|
||||
fn update(&mut self, state: &mut Self::State, event: Self::Event) -> Option<M> {
|
||||
match event {
|
||||
AddRecurringEvent::SetItem(i) => state.item = i,
|
||||
AddRecurringEvent::SetValue(v) => state.value = v,
|
||||
AddRecurringEvent::SubmitValue => {
|
||||
if let Ok(v) = state.value.parse() {
|
||||
state.value.clear();
|
||||
return Some((self.on_submit)(std::mem::take(&mut state.item), v));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn view(&self, state: &Self::State) -> iced::Element<'_, Self::Event, Renderer> {
|
||||
column![
|
||||
text_input("item", &state.item).on_input(AddRecurringEvent::SetItem),
|
||||
text_input("value", &state.value)
|
||||
.on_input(AddRecurringEvent::SetValue)
|
||||
.on_submit(AddRecurringEvent::SubmitValue)
|
||||
]
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
struct EditRecurring<'a, F> {
|
||||
value: f64,
|
||||
name: &'a str,
|
||||
on_submit: F,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct EditRecurringState {
|
||||
edit: String,
|
||||
modal: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum EditRecurringEvent {
|
||||
Edit(String),
|
||||
Open,
|
||||
Close,
|
||||
Submit,
|
||||
}
|
||||
|
||||
impl<M, F> iced::widget::Component<M, Renderer> for EditRecurring<'_, F>
|
||||
where
|
||||
F: FnMut(f64) -> M,
|
||||
{
|
||||
type State = EditRecurringState;
|
||||
type Event = EditRecurringEvent;
|
||||
|
||||
fn update(&mut self, state: &mut Self::State, event: Self::Event) -> Option<M> {
|
||||
match event {
|
||||
EditRecurringEvent::Edit(_) => todo!(),
|
||||
EditRecurringEvent::Submit => {
|
||||
if let Ok(v) = state.edit.parse() {
|
||||
state.edit.clear();
|
||||
state.modal = false;
|
||||
return Some((self.on_submit)(v));
|
||||
}
|
||||
}
|
||||
EditRecurringEvent::Open => {
|
||||
state.modal = true;
|
||||
}
|
||||
EditRecurringEvent::Close => {
|
||||
state.modal = false;
|
||||
state.edit.clear();
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn view(&self, state: &Self::State) -> iced_aw::Element<'_, Self::Event, Renderer> {
|
||||
let underlay = button(text("Edit")).on_press(EditRecurringEvent::Open);
|
||||
let overlay = match state.modal {
|
||||
true => Some(
|
||||
card(text(&format!("Edit {}", self.name)), text("todo"))
|
||||
.max_width(300.0)
|
||||
.on_close(EditRecurringEvent::Close),
|
||||
),
|
||||
false => None,
|
||||
};
|
||||
|
||||
modal(underlay, overlay)
|
||||
.backdrop(EditRecurringEvent::Close)
|
||||
.on_esc(EditRecurringEvent::Close)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum RecurringMessage {
|
||||
CloseAdd,
|
||||
Add,
|
||||
DoAdd(String, f64),
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct RecurringState {
|
||||
add_recurring: bool,
|
||||
}
|
||||
|
||||
struct Recurring<'a, F> {
|
||||
items: &'a BTreeMap<String, f64>,
|
||||
on_add: F,
|
||||
}
|
||||
|
||||
impl<M, F> iced::widget::Component<M, Renderer> for Recurring<'_, F>
|
||||
where
|
||||
F: FnMut(String, f64) -> M,
|
||||
{
|
||||
type State = RecurringState;
|
||||
type Event = RecurringMessage;
|
||||
|
||||
fn update(&mut self, state: &mut Self::State, event: Self::Event) -> Option<M> {
|
||||
match event {
|
||||
RecurringMessage::Add => state.add_recurring = true,
|
||||
RecurringMessage::CloseAdd => {
|
||||
state.add_recurring = false;
|
||||
}
|
||||
RecurringMessage::DoAdd(item, value) => {
|
||||
state.add_recurring = false;
|
||||
return Some((self.on_add)(item, value));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn view(&self, state: &Self::State) -> iced_aw::Element<'_, Self::Event, Renderer> {
|
||||
let underlay = column![
|
||||
text("Recurring").size(20),
|
||||
button(text("Add")).on_press(RecurringMessage::Add),
|
||||
horizontal_rule(5),
|
||||
column(
|
||||
self.items
|
||||
.iter()
|
||||
.map(|(name, &value)| row![
|
||||
text(name).size(17),
|
||||
text(&format!("{value} €")),
|
||||
component(EditRecurring {
|
||||
value,
|
||||
name,
|
||||
on_submit: |v| RecurringMessage::DoAdd(name.to_string(), v)
|
||||
})
|
||||
]
|
||||
.spacing(5)
|
||||
.align_items(iced::Alignment::Center)
|
||||
.into())
|
||||
.intersperse_with(|| horizontal_rule(5).into())
|
||||
.collect()
|
||||
),
|
||||
];
|
||||
|
||||
let overlay = match state.add_recurring {
|
||||
true => Some(
|
||||
card(
|
||||
text("Add a recurring spending"),
|
||||
component(AddRecurring::new(RecurringMessage::DoAdd)),
|
||||
)
|
||||
.on_close(RecurringMessage::CloseAdd)
|
||||
.max_width(300.0),
|
||||
),
|
||||
false => None,
|
||||
};
|
||||
|
||||
modal(underlay, overlay)
|
||||
.backdrop(RecurringMessage::CloseAdd)
|
||||
.on_esc(RecurringMessage::CloseAdd)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Application for Glaurung {
|
||||
type Message = Message;
|
||||
type Theme = Theme;
|
||||
type Executor = iced::executor::Default;
|
||||
type Flags = ();
|
||||
|
||||
fn new() -> Self {
|
||||
Self {}
|
||||
fn new(_flags: Self::Flags) -> (Self, Command<Message>) {
|
||||
(
|
||||
Self {
|
||||
recurring: BTreeMap::new(),
|
||||
},
|
||||
Command::batch(vec![
|
||||
font::load(iced_aw::graphics::icons::ICON_FONT_BYTES).map(Message::FontLoaded)
|
||||
]),
|
||||
)
|
||||
}
|
||||
|
||||
fn title(&self) -> String {
|
||||
"Glaurung - Account Manager".into()
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Self::Message) {
|
||||
match message {}
|
||||
fn update(&mut self, message: Self::Message) -> Command<Message> {
|
||||
match message {
|
||||
Message::AddRecurring(name, value) => {
|
||||
self.recurring.insert(name, value);
|
||||
}
|
||||
Message::FontLoaded(r) => r.expect("could not load font"),
|
||||
}
|
||||
|
||||
Command::none()
|
||||
}
|
||||
|
||||
fn view(&self) -> iced::Element<'_, Self::Message> {
|
||||
column![].into()
|
||||
fn view(&self) -> Element {
|
||||
column![
|
||||
text("Spendings").size(30),
|
||||
component(Recurring {
|
||||
items: &self.recurring,
|
||||
on_add: Message::AddRecurring,
|
||||
})
|
||||
]
|
||||
.padding(5)
|
||||
.into()
|
||||
}
|
||||
|
||||
fn theme(&self) -> iced::Theme {
|
||||
iced::Theme::Dark
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue