summaryrefslogtreecommitdiff
path: root/schist_desktop_gui/src/gui
diff options
context:
space:
mode:
Diffstat (limited to 'schist_desktop_gui/src/gui')
-rw-r--r--schist_desktop_gui/src/gui/components.rs2
-rw-r--r--schist_desktop_gui/src/gui/components/table.rs42
-rw-r--r--schist_desktop_gui/src/gui/components/table/view_table.rs48
-rw-r--r--schist_desktop_gui/src/gui/components/text.rs31
-rw-r--r--schist_desktop_gui/src/gui/components/transactions_view.rs87
-rw-r--r--schist_desktop_gui/src/gui/components/transactions_view/view_transactions_view.rs27
-rw-r--r--schist_desktop_gui/src/gui/screens/main_screen/main_screen.rs2
7 files changed, 199 insertions, 40 deletions
diff --git a/schist_desktop_gui/src/gui/components.rs b/schist_desktop_gui/src/gui/components.rs
index 606c418..ec91951 100644
--- a/schist_desktop_gui/src/gui/components.rs
+++ b/schist_desktop_gui/src/gui/components.rs
@@ -5,6 +5,7 @@ pub mod buckets_view;
pub mod button;
pub mod navigation;
pub mod panel;
+pub mod table;
pub mod text;
pub mod transactions_view;
@@ -15,5 +16,6 @@ pub use buckets_view::BucketsView;
pub use button::{button, panel_button};
pub use navigation::Navigation;
pub use panel::Panel;
+pub use table::Table;
pub use text::Text;
pub use transactions_view::TransactionsView;
diff --git a/schist_desktop_gui/src/gui/components/table.rs b/schist_desktop_gui/src/gui/components/table.rs
new file mode 100644
index 0000000..4c6d810
--- /dev/null
+++ b/schist_desktop_gui/src/gui/components/table.rs
@@ -0,0 +1,42 @@
+use std::collections::HashMap;
+
+mod view_table;
+
+#[derive(Clone, Debug, Default)]
+pub struct Table<Ix>
+where
+ Ix: Clone + std::fmt::Debug,
+{
+ pub cols: Vec<Column<Ix>>,
+ pub rows: Vec<HashMap<Ix, Value>>,
+}
+
+#[derive(Clone, Debug)]
+pub enum Value {
+ String(String),
+ Currency(i32),
+}
+
+#[derive(Clone, Debug)]
+pub struct Column<Ix>
+where
+ Ix: Clone + std::fmt::Debug,
+{
+ pub index: Ix,
+ pub name: String,
+}
+
+impl<Ix> Column<Ix>
+where
+ Ix: Clone + std::fmt::Debug,
+{
+ pub fn new(index: Ix, name: &str) -> Self {
+ Self {
+ index,
+ name: name.to_string(),
+ }
+ }
+}
+
+#[derive(Clone, Debug)]
+pub enum Message {}
diff --git a/schist_desktop_gui/src/gui/components/table/view_table.rs b/schist_desktop_gui/src/gui/components/table/view_table.rs
new file mode 100644
index 0000000..1d5f10b
--- /dev/null
+++ b/schist_desktop_gui/src/gui/components/table/view_table.rs
@@ -0,0 +1,48 @@
+use std::{fmt::Debug, hash::Hash};
+
+use iced::widget::{column, container, row};
+
+use crate::{gui::components::Text, style::SPACING_MD, traits::Viewable};
+
+use super::{Message, Table, Value};
+
+impl<'a, Ix> Viewable<'a, Message> for Table<Ix>
+where
+ Ix: Clone + Debug + Eq + Hash,
+{
+ fn view(&'a self) -> iced::Element<'a, Message> {
+ let headers = row(self.cols.iter().map(|col| {
+ container(Text::default(&col.name))
+ .width(iced::Length::FillPortion(1))
+ .into()
+ }))
+ .padding(SPACING_MD);
+ let rows = column(self.rows.iter().map(|row_data| {
+ row(self.cols.iter().map(|col| {
+ container(
+ row_data
+ .get(&col.index)
+ .map(|datum| match datum {
+ Value::String(text) => Text::default(text).weak(),
+ Value::Currency(amount) => format_currency(*amount),
+ })
+ .unwrap_or_else(|| Text::default("")),
+ )
+ .width(iced::Length::FillPortion(1))
+ .into()
+ }))
+ .spacing(SPACING_MD)
+ .into()
+ }));
+ column![headers, rows].into()
+ }
+}
+
+fn format_currency(amount: i32) -> Text {
+ let text = if amount >= 0 {
+ format!(" {} · {} ", amount / 100, amount % 100)
+ } else {
+ format!("({} · {})", -amount / 100, -amount % 100)
+ };
+ Text::default(&text).align_right().width(iced::Length::Fill)
+}
diff --git a/schist_desktop_gui/src/gui/components/text.rs b/schist_desktop_gui/src/gui/components/text.rs
index d171043..0d0c8c4 100644
--- a/schist_desktop_gui/src/gui/components/text.rs
+++ b/schist_desktop_gui/src/gui/components/text.rs
@@ -5,13 +5,21 @@ use crate::{impl_focusable, style::*};
#[derive(Clone, Debug, PartialEq)]
pub struct Text {
pub content: String,
+ align: Alignment,
colour: Option<Colour>,
size: Option<Size>,
strength: Option<Strength>,
+ width: iced::Length,
is_focused: bool,
}
#[derive(Clone, Debug, PartialEq)]
+enum Alignment {
+ Left,
+ Right,
+}
+
+#[derive(Clone, Debug, PartialEq)]
enum Colour {
Danger,
Primary,
@@ -33,20 +41,24 @@ enum Size {
impl Text {
pub fn default(content: &str) -> Self {
Self {
+ align: Alignment::Left,
content: String::from(content),
colour: Some(Colour::Primary),
size: Some(Size::Base),
strength: Some(Strength::Base),
is_focused: false,
+ width: iced::Length::Shrink,
}
}
pub fn new(content: &str) -> Self {
Self {
+ align: Alignment::Left,
content: String::from(content),
colour: None,
size: None,
strength: None,
+ width: iced::Length::Shrink,
is_focused: false,
}
}
@@ -86,6 +98,20 @@ impl Text {
self.clone()
}
}
+
+ pub fn align_right(&self) -> Self {
+ Self {
+ align: Alignment::Right,
+ ..self.clone()
+ }
+ }
+
+ pub fn width(&self, width: iced::Length) -> Self {
+ Self {
+ width,
+ ..self.clone()
+ }
+ }
}
impl<'a, Message> From<Text> for iced::Element<'a, Message> {
@@ -109,6 +135,11 @@ where
Some(Size::Small) => TEXT_SIZE_SM,
Some(Size::Base) | None => TEXT_SIZE_BASE,
})
+ .align_x(match &text.borrow().align {
+ Alignment::Left => iced::alignment::Horizontal::Left,
+ Alignment::Right => iced::alignment::Horizontal::Right,
+ })
+ .width(text.borrow().width)
.style(move |theme: &iced::Theme| iced::widget::text::Style {
color: match (
&text.borrow().borrow().colour,
diff --git a/schist_desktop_gui/src/gui/components/transactions_view.rs b/schist_desktop_gui/src/gui/components/transactions_view.rs
index f39b522..8e076f1 100644
--- a/schist_desktop_gui/src/gui/components/transactions_view.rs
+++ b/schist_desktop_gui/src/gui/components/transactions_view.rs
@@ -6,13 +6,14 @@ use itertools::Itertools;
use schist_models::{Account, Transaction};
use crate::{
- gui::components::{navigation, AccountNavigationEntry, Navigation},
+ gui::components::{navigation, table, AccountNavigationEntry, Navigation, Table},
traits::Component,
};
#[derive(Clone, Debug)]
pub struct TransactionsView {
navigation: Navigation<AccountNavigationEntry>,
+ table: Table<i32>,
transactions_by_account_id: HashMap<i32, Vec<Transaction>>,
}
@@ -23,6 +24,7 @@ pub enum Message {
SelectPrevAccount,
SetAccounts(Vec<Account>),
SetTransactions(Vec<Transaction>),
+ TableMessage(table::Message),
}
pub enum Action {
@@ -30,57 +32,106 @@ pub enum Action {
}
impl TransactionsView {
- pub fn new(accounts: Vec<Account>, transactions: Vec<Transaction>) -> Self {
+ pub fn new(accounts: &[Account], transactions: &[Transaction]) -> Self {
let account_navigation_entries: Vec<AccountNavigationEntry> = accounts
- .into_iter()
+ .iter()
+ .cloned()
.map(AccountNavigationEntry::new)
.collect();
let navigation = Navigation::new(
account_navigation_entries.first().cloned(),
account_navigation_entries,
);
- Self {
+ let mut transactions_view = Self {
navigation: navigation,
+ table: Table::default(),
transactions_by_account_id: transactions
.into_iter()
+ .cloned()
.into_group_map_by(|t| t.account_id),
- }
+ };
+ transactions_view.refresh_table();
+ transactions_view
+ }
+
+ fn refresh_table(&mut self) {
+ self.table =
+ Table {
+ cols: vec![
+ table::Column::new(0, "Date"),
+ table::Column::new(1, "Bucket"),
+ table::Column::new(2, "Payee"),
+ table::Column::new(3, "Quantity"),
+ table::Column::new(4, "Balance"),
+ ],
+ rows: self.navigation.active_option.clone().map_or_else(
+ Vec::new,
+ |AccountNavigationEntry { account, .. }| {
+ self.transactions_by_account_id
+ .get(&account.id)
+ .cloned()
+ .unwrap_or_else(Vec::new)
+ .iter()
+ .map(|t| {
+ HashMap::from([
+ (0, table::Value::String(t.date.to_string())),
+ (
+ 1,
+ table::Value::String(t.bucket_id.map_or_else(
+ || String::from("None"),
+ |id| id.to_string(),
+ )),
+ ),
+ (2, table::Value::String(t.counterparty.clone())),
+ (3, table::Value::Currency(t.amount)),
+ (4, table::Value::String(String::from("TODO"))),
+ ])
+ })
+ .collect()
+ },
+ ),
+ };
}
}
impl<'a> Component<'a, Message, Action> for TransactionsView {
fn update(&mut self, message: Message) -> Action {
match message {
- Message::NavigationMessage(message) => {
- self.navigation.update(message);
- Action::None
- }
+ Message::NavigationMessage(message) => self.update_navigation(message),
Message::SetAccounts(accounts) => {
let account_navigation_entries: Vec<AccountNavigationEntry> = accounts
.into_iter()
.map(AccountNavigationEntry::new)
.collect();
- self.navigation
- .update(navigation::Message::SetOptions(account_navigation_entries));
- Action::None
+ self.update_navigation(navigation::Message::SetOptions(account_navigation_entries))
}
Message::SetTransactions(transactions) => {
self.transactions_by_account_id =
transactions.into_iter().into_group_map_by(|t| t.account_id);
+ self.refresh_table();
Action::None
}
- Message::SelectNextAccount => {
- self.navigation.update(navigation::Message::SelectNext);
- Action::None
- }
+ Message::SelectNextAccount => self.update_navigation(navigation::Message::SelectNext),
+
+ Message::SelectPrevAccount => self.update_navigation(navigation::Message::SelectPrev),
+ }
+ }
+}
- Message::SelectPrevAccount => {
- self.navigation.update(navigation::Message::SelectPrev);
+impl TransactionsView {
+ fn update_navigation(
+ &mut self,
+ message: navigation::Message<AccountNavigationEntry>,
+ ) -> Action {
+ match self.navigation.update(message) {
+ navigation::Action::ActivateOption(_) | navigation::Action::SelectOption(_) => {
+ self.refresh_table();
Action::None
}
+ navigation::Action::None => Action::None,
}
}
}
diff --git a/schist_desktop_gui/src/gui/components/transactions_view/view_transactions_view.rs b/schist_desktop_gui/src/gui/components/transactions_view/view_transactions_view.rs
index 37b6655..d1f6af2 100644
--- a/schist_desktop_gui/src/gui/components/transactions_view/view_transactions_view.rs
+++ b/schist_desktop_gui/src/gui/components/transactions_view/view_transactions_view.rs
@@ -1,37 +1,22 @@
use iced::{
alignment,
- widget::{column, row, Container},
+ widget::{row, Container},
Element, Length,
};
-use crate::{
- gui::components::{AccountNavigationEntry, Text},
- style::SPACING_LG,
- traits::Viewable,
-};
+use crate::{style::SPACING_LG, traits::Viewable};
use super::{Message, TransactionsView};
impl<'a> Viewable<'a, Message> for TransactionsView {
fn view(&'a self) -> Element<'a, Message> {
let navigation = self.navigation.view().map(Message::NavigationMessage);
- let main_content = column![
- Text::default("Hello, transactions!").as_element(),
- Text::default(&format!(
- "{} transactions in this account.",
- self.navigation.active_option.clone().map_or(
- 0,
- |AccountNavigationEntry { account, .. }| self
- .transactions_by_account_id
- .get(&account.id)
- .map_or(0, |transactions| transactions.len())
- )
- ))
- .as_element(),
- ];
+
+ let table = self.table.view().map(Message::TableMessage);
+
row![
Container::new(navigation).width(Length::FillPortion(1)),
- Container::new(main_content).width(Length::FillPortion(3)),
+ Container::new(table).width(Length::FillPortion(3)),
]
.align_y(alignment::Vertical::Center)
.height(Length::Fill)
diff --git a/schist_desktop_gui/src/gui/screens/main_screen/main_screen.rs b/schist_desktop_gui/src/gui/screens/main_screen/main_screen.rs
index 0a2c355..9852b53 100644
--- a/schist_desktop_gui/src/gui/screens/main_screen/main_screen.rs
+++ b/schist_desktop_gui/src/gui/screens/main_screen/main_screen.rs
@@ -204,7 +204,7 @@ impl MainScreen {
balances_view: BalancesView::new(Vec::new()),
buckets: Vec::new(),
buckets_view: BucketsView::new(Vec::new(), "Hello, buckets!"),
- transactions_view: TransactionsView::new(Vec::new(), Vec::new()),
+ transactions_view: TransactionsView::new(&[], &[]),
active_view: View::Balances,
view_navigation: Navigation::new(Some(View::Balances), View::views()),
}