From d42001eedf861e7063c619851f16c98b5514c8fc Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Sun, 18 Aug 2019 15:56:41 -0400 Subject: move global event handling to txn_tables --- src/views/txn_table.rs | 317 ++++++++---------------------------------------- src/views/txn_tables.rs | 288 ++++++++++++++++++++++++++++++++++++++++++- src/views/util.rs | 2 +- 3 files changed, 334 insertions(+), 273 deletions(-) diff --git a/src/views/txn_table.rs b/src/views/txn_table.rs index 43bb2c4..b71a92e 100644 --- a/src/views/txn_table.rs +++ b/src/views/txn_table.rs @@ -10,7 +10,7 @@ pub enum TxnColumn { TotalAmount, } -type TxnTableView = +pub type TxnTableView = cursive_table_view::TableView; pub struct TxnTable { view: super::util::FullView, @@ -37,239 +37,67 @@ impl TxnTable { c.align(cursive::align::HAlign::Right).width(10) }) .default_column(TxnColumn::Date) - .on_submit(move |s, _, _| { - let outflows: Vec<_> = s - .call_on_id("outflows_table", |v: &mut TxnTableView| { - v.borrow_items() - .iter() - .filter(|t| t.selected) - .cloned() - .collect() - }) - .unwrap(); - let inflows: Vec<_> = s - .call_on_id("inflows_table", |v: &mut TxnTableView| { - v.borrow_items() - .iter() - .filter(|t| t.selected) - .cloned() - .collect() - }) - .unwrap(); - let total_outflow: i64 = - outflows.iter().map(|t| t.amount).sum(); - let total_inflow: i64 = - inflows.iter().map(|t| t.amount).sum(); - let total_amount = total_outflow + total_inflow; - if total_amount == 0 { - let budget: &mut crate::ynab::Budget = - s.user_data().unwrap(); - let txns: Vec<_> = - outflows.iter().chain(inflows.iter()).collect(); - let err = budget.reconcile_transactions(&txns); - if let Some(err) = err { - s.add_layer(super::util::dialog(&format!( - "Error: {}", - err - ))) - } else { - s.add_layer(super::util::dialog(&format!( - "Successfully updated {} transactions", - txns.len() - ))); - s.call_on_id( - "outflows_table", - |v: &mut TxnTableView| { - let all_txns = v.borrow_items_mut(); - for id in txns.iter().map(|t| t.id.clone()) { - if let Some(idx) = all_txns - .iter() - .position(|t| t.id == id) - { - all_txns.remove(idx); - } - } - if let Some(row) = v.row() { - if row >= v.len() { - v.set_selected_row(v.len() - 1); - } - } - }, - ) - .unwrap(); - s.call_on_id( - "inflows_table", - |v: &mut TxnTableView| { - let all_txns = v.borrow_items_mut(); - for id in txns.iter().map(|t| t.id.clone()) { - if let Some(idx) = all_txns - .iter() - .position(|t| t.id == id) - { - all_txns.remove(idx); - } - } - if let Some(row) = v.row() { - if row >= v.len() { - v.set_selected_row(v.len() - 1); - } - } - }, - ) - .unwrap(); - } - } else { - s.add_layer(super::util::dialog(&format!( - "Selected amount is {}, must be 0", - crate::ynab::format_amount(total_amount) - ))) + .on_submit(|s, _, _| { + s.on_event(cursive::event::Event::Key( + cursive::event::Key::F0, + )); + }); + table.set_items(txns); + let view = cursive::views::OnEventView::new(table) + .on_event_inner(' ', |v: &mut TxnTableView, _| { + if let Some(idx) = v.item() { + let txn = v.borrow_item_mut(idx).unwrap(); + txn.selected = !txn.selected; } + None }) - .with_id(id); - table.get_mut().set_items(txns); - let view = cursive::views::OnEventView::new(table) - .on_event(' ', move |s| { - s.call_on_id(&id, |v: &mut TxnTableView| { - if let Some(idx) = v.item() { - let txn = v.borrow_item_mut(idx).unwrap(); - txn.selected = !txn.selected; - } - }); - render_selected_total(s); + .on_event_inner('h', |v: &mut TxnTableView, _| { + v.on_event(cursive::event::Event::Key( + cursive::event::Key::Left, + )); + None }) - .on_event_inner( - 'h', - |v: &mut cursive::views::IdView, _| { - v.on_event(cursive::event::Event::Key( - cursive::event::Key::Left, - )); - None - }, - ) - .on_event_inner( - 'j', - |v: &mut cursive::views::IdView, _| { - v.on_event(cursive::event::Event::Key( - cursive::event::Key::Down, - )); - None - }, - ) - .on_event_inner( - 'k', - |v: &mut cursive::views::IdView, _| { - v.on_event(cursive::event::Event::Key( - cursive::event::Key::Up, - )); - None - }, - ) - .on_event_inner( - 'l', - |v: &mut cursive::views::IdView, _| { - v.on_event(cursive::event::Event::Key( - cursive::event::Key::Right, - )); - None - }, - ) - .on_event_inner( - 'g', - |v: &mut cursive::views::IdView, _| { - v.get_mut().set_selected_row(0); - None - }, - ) - .on_event_inner( - 'G', - |v: &mut cursive::views::IdView, _| { - let mut v = v.get_mut(); - let last_row = v.len() - 1; - v.set_selected_row(last_row); - None - }, - ) - .on_event('r', move |s| { - let budget: &mut crate::ynab::Budget = s.user_data().unwrap(); - budget.refresh(); - - let mut inflows: Vec<_> = budget - .reimbursables() - .iter() - .filter(|t| !t.reimbursed && t.amount > 0) - .cloned() - .collect(); - s.call_on_id("inflows_table", |v: &mut TxnTableView| { - let selected: std::collections::HashSet<_> = v - .borrow_items() - .iter() - .filter(|t| t.selected) - .map(|t| t.id.clone()) - .collect(); - let row = v.item().and_then(|idx| { - v.borrow_item(idx).map(|t| t.id.clone()) - }); - for mut t in inflows.iter_mut() { - if selected.contains(&t.id) { - t.selected = true; - } - } - let idx = row.and_then(|id| { - inflows.iter().position(|t| t.id == id) - }); - v.set_items(inflows); - if let Some(idx) = idx { - v.set_selected_item(idx); - } - }) - .unwrap(); - - let budget: &mut crate::ynab::Budget = s.user_data().unwrap(); - let mut outflows: Vec<_> = budget - .reimbursables() - .iter() - .filter(|t| !t.reimbursed && t.amount <= 0) - .cloned() - .collect(); - s.call_on_id("outflows_table", |v: &mut TxnTableView| { - let selected: std::collections::HashSet<_> = v - .borrow_items() - .iter() - .filter(|t| t.selected) - .map(|t| t.id.clone()) - .collect(); - let row = v.item().and_then(|idx| { - v.borrow_item(idx).map(|t| t.id.clone()) - }); - for mut t in outflows.iter_mut() { - if selected.contains(&t.id) { - t.selected = true; - } - } - let idx = row.and_then(|id| { - outflows.iter().position(|t| t.id == id) - }); - v.set_items(outflows); - if let Some(idx) = idx { - v.set_selected_item(idx); - } - }) - .unwrap(); - - render_selected_total(s); - }); + .on_event_inner('j', |v: &mut TxnTableView, _| { + v.on_event(cursive::event::Event::Key( + cursive::event::Key::Down, + )); + None + }) + .on_event_inner('k', |v: &mut TxnTableView, _| { + v.on_event(cursive::event::Event::Key( + cursive::event::Key::Up, + )); + None + }) + .on_event_inner('l', |v: &mut TxnTableView, _| { + v.on_event(cursive::event::Event::Key( + cursive::event::Key::Right, + )); + None + }) + .on_event_inner('g', |v: &mut TxnTableView, _| { + v.set_selected_row(0); + None + }) + .on_event_inner('G', |v: &mut TxnTableView, _| { + v.set_selected_row(v.len() - 1); + None + }) + .with_id(id); TxnTable { view } } pub fn len(&self) -> usize { - self.view.get_inner().with_view(|v| v.len()).unwrap() + self.view + .with_view(|v| v.with_view(|v| v.len()).unwrap()) + .unwrap() } // XXX why does borrow_items require &mut self? pub fn amount(&mut self) -> i64 { self.view - .get_inner_mut() .get_mut() + .get_inner_mut() .borrow_items() .iter() .map(|t| t.amount) @@ -321,50 +149,3 @@ impl cursive_table_view::TableViewItem } } } - -fn render_selected_total(s: &mut cursive::Cursive) { - let outflows: Vec<_> = s - .call_on_id("outflows_table", |v: &mut TxnTableView| { - v.borrow_items() - .iter() - .filter(|t| t.selected) - .map(|t| t.amount) - .collect() - }) - .unwrap(); - let inflows: Vec<_> = s - .call_on_id("inflows_table", |v: &mut TxnTableView| { - v.borrow_items() - .iter() - .filter(|t| t.selected) - .map(|t| t.amount) - .collect() - }) - .unwrap(); - let outflow: i64 = outflows.iter().sum(); - let inflow: i64 = inflows.iter().sum(); - let amount = outflow + inflow; - s.call_on_id("selected_total", |v: &mut cursive::views::TextView| { - let mut sstr = - cursive::utils::markup::StyledString::plain("Selected: "); - let color = if amount == 0 && outflows.len() + inflows.len() != 0 { - cursive::theme::Color::Dark(cursive::theme::BaseColor::Green) - } else { - cursive::theme::Color::TerminalDefault - }; - sstr.append(cursive::utils::markup::StyledString::styled( - crate::ynab::format_amount(amount), - color, - )); - sstr.append(format!( - " ({} transaction{}", - outflows.len() + inflows.len(), - if outflows.len() + inflows.len() == 1 { - ") " - } else { - "s)" - } - )); - v.set_content(sstr); - }); -} diff --git a/src/views/txn_tables.rs b/src/views/txn_tables.rs index 757e25a..8579bf6 100644 --- a/src/views/txn_tables.rs +++ b/src/views/txn_tables.rs @@ -1,4 +1,4 @@ -use cursive::view::Identifiable; +use cursive::view::{Identifiable, View}; const SELECTED_TOTAL_ID: &str = "selected_total"; const INFLOWS_TABLE_ID: &str = "inflows_table"; @@ -69,8 +69,288 @@ impl TxnTables { cursive::views::BoxView::with_full_screen(outflows_table), )); - TxnTables { - view: cursive::views::OnEventView::new(layout.with_id(id)), - } + let event_view = cursive::views::OnEventView::new(layout) + .on_event(cursive::event::Key::F0, move |s| { + let inflows: Vec<_> = s + .call_on_id( + INFLOWS_TABLE_ID, + |v: &mut cursive::views::OnEventView< + super::txn_table::TxnTableView, + >| { + v.get_inner_mut() + .borrow_items() + .iter() + .filter(|t| t.selected) + .cloned() + .collect() + }, + ) + .unwrap(); + let outflows: Vec<_> = s + .call_on_id( + OUTFLOWS_TABLE_ID, + |v: &mut cursive::views::OnEventView< + super::txn_table::TxnTableView, + >| { + v.get_inner_mut() + .borrow_items() + .iter() + .filter(|t| t.selected) + .cloned() + .collect() + }, + ) + .unwrap(); + let total_inflow: i64 = + inflows.iter().map(|t| t.amount).sum(); + let total_outflow: i64 = + outflows.iter().map(|t| t.amount).sum(); + let total_amount = total_inflow + total_outflow; + if total_amount == 0 { + let budget: &mut crate::ynab::Budget = + s.user_data().unwrap(); + let txns: Vec<_> = + inflows.iter().chain(outflows.iter()).collect(); + let err = budget.reconcile_transactions(&txns); + if let Some(err) = err { + s.add_layer(super::util::dialog(&format!( + "Error: {}", + err + ))) + } else { + s.add_layer(super::util::dialog(&format!( + "Successfully updated {} transactions", + txns.len() + ))); + s.call_on_id( + INFLOWS_TABLE_ID, + |v: &mut cursive::views::OnEventView< + super::txn_table::TxnTableView, + >| { + let v = v.get_inner_mut(); + let all_txns = v.borrow_items_mut(); + for id in txns.iter().map(|t| t.id.clone()) { + if let Some(idx) = all_txns + .iter() + .position(|t| t.id == id) + { + all_txns.remove(idx); + } + } + if let Some(row) = v.row() { + if row >= v.len() { + v.set_selected_row(v.len() - 1); + } + } + }, + ) + .unwrap(); + s.call_on_id( + OUTFLOWS_TABLE_ID, + |v: &mut cursive::views::OnEventView< + super::txn_table::TxnTableView, + >| { + let v = v.get_inner_mut(); + let all_txns = v.borrow_items_mut(); + for id in txns.iter().map(|t| t.id.clone()) { + if let Some(idx) = all_txns + .iter() + .position(|t| t.id == id) + { + all_txns.remove(idx); + } + } + if let Some(row) = v.row() { + if row >= v.len() { + v.set_selected_row(v.len() - 1); + } + } + }, + ) + .unwrap(); + } + } else { + s.add_layer(super::util::dialog(&format!( + "Selected amount is {}, must be 0", + crate::ynab::format_amount(total_amount) + ))) + } + }) + .on_event('r', move |s| { + let budget: &mut crate::ynab::Budget = s.user_data().unwrap(); + budget.refresh(); + + let mut inflows: Vec<_> = budget + .reimbursables() + .iter() + .filter(|t| !t.reimbursed && t.amount > 0) + .cloned() + .collect(); + s.call_on_id( + INFLOWS_TABLE_ID, + |v: &mut cursive::views::OnEventView< + super::txn_table::TxnTableView, + >| { + let v = v.get_inner_mut(); + let selected: std::collections::HashSet<_> = v + .borrow_items() + .iter() + .filter(|t| t.selected) + .map(|t| t.id.clone()) + .collect(); + let row = v.item().and_then(|idx| { + v.borrow_item(idx).map(|t| t.id.clone()) + }); + for mut t in inflows.iter_mut() { + if selected.contains(&t.id) { + t.selected = true; + } + } + let idx = row.and_then(|id| { + inflows.iter().position(|t| t.id == id) + }); + v.set_items(inflows); + if let Some(idx) = idx { + v.set_selected_item(idx); + } + }, + ) + .unwrap(); + + let budget: &mut crate::ynab::Budget = s.user_data().unwrap(); + let mut outflows: Vec<_> = budget + .reimbursables() + .iter() + .filter(|t| !t.reimbursed && t.amount <= 0) + .cloned() + .collect(); + s.call_on_id( + OUTFLOWS_TABLE_ID, + |v: &mut cursive::views::OnEventView< + super::txn_table::TxnTableView, + >| { + let v = v.get_inner_mut(); + let selected: std::collections::HashSet<_> = v + .borrow_items() + .iter() + .filter(|t| t.selected) + .map(|t| t.id.clone()) + .collect(); + let row = v.item().and_then(|idx| { + v.borrow_item(idx).map(|t| t.id.clone()) + }); + for mut t in outflows.iter_mut() { + if selected.contains(&t.id) { + t.selected = true; + } + } + let idx = row.and_then(|id| { + outflows.iter().position(|t| t.id == id) + }); + v.set_items(outflows); + if let Some(idx) = idx { + v.set_selected_item(idx); + } + }, + ) + .unwrap(); + + render_selected_total(s); + }) + .on_pre_event_inner(' ', |v, _| { + let idx = v.get_focus_index(); + let child = v.get_child_mut(idx).unwrap(); + child.call_on_any( + &cursive::view::Selector::Id(INFLOWS_TABLE_ID), + Box::new(|v| { + v.downcast_mut::, + >>() + .map(|v| { + v.on_event(cursive::event::Event::Char(' ')) + }); + }), + ); + child.call_on_any( + &cursive::view::Selector::Id(OUTFLOWS_TABLE_ID), + Box::new(|v| { + v.downcast_mut::, + >>() + .map(|v| { + v.on_event(cursive::event::Event::Char(' ')) + }); + }), + ); + Some(cursive::event::EventResult::with_cb(|s| { + render_selected_total(s); + })) + }) + .with_id(id); + + TxnTables { view: event_view } } } + +fn render_selected_total(s: &mut cursive::Cursive) { + let inflows: Vec<_> = s + .call_on_id( + INFLOWS_TABLE_ID, + |v: &mut cursive::views::OnEventView< + super::txn_table::TxnTableView, + >| { + v.get_inner_mut() + .borrow_items() + .iter() + .filter(|t| t.selected) + .map(|t| t.amount) + .collect() + }, + ) + .unwrap(); + let outflows: Vec<_> = s + .call_on_id( + OUTFLOWS_TABLE_ID, + |v: &mut cursive::views::OnEventView< + super::txn_table::TxnTableView, + >| { + v.get_inner_mut() + .borrow_items() + .iter() + .filter(|t| t.selected) + .map(|t| t.amount) + .collect() + }, + ) + .unwrap(); + let outflow: i64 = outflows.iter().sum(); + let inflow: i64 = inflows.iter().sum(); + let amount = outflow + inflow; + s.call_on_id(SELECTED_TOTAL_ID, |v: &mut cursive::views::TextView| { + let mut sstr = + cursive::utils::markup::StyledString::plain("Selected: "); + let color = if amount == 0 && outflows.len() + inflows.len() != 0 { + cursive::theme::Color::Dark(cursive::theme::BaseColor::Green) + } else { + cursive::theme::Color::TerminalDefault + }; + sstr.append(cursive::utils::markup::StyledString::styled( + crate::ynab::format_amount(amount), + color, + )); + sstr.append(format!( + " ({} transaction{}", + outflows.len() + inflows.len(), + if outflows.len() + inflows.len() == 1 { + ") " + } else { + "s)" + } + )); + v.set_content(sstr); + }); +} diff --git a/src/views/util.rs b/src/views/util.rs index 1dec69a..eb22976 100644 --- a/src/views/util.rs +++ b/src/views/util.rs @@ -1,4 +1,4 @@ -pub type FullView = cursive::views::OnEventView>; +pub type FullView = cursive::views::IdView>; pub fn dialog(s: &str) -> impl cursive::view::View { cursive::views::Panel::new(cursive::views::Dialog::info(s)) -- cgit v1.2.3-54-g00ecf