Refactor some ui while upgrading tui to latest version.
This commit is contained in:
parent
d9a296a34e
commit
823f83d5c6
|
@ -248,6 +248,31 @@ version = "0.8.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"crossterm_winapi",
|
||||
"libc",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"signal-hook",
|
||||
"signal-hook-mio",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm_winapi"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crptls"
|
||||
version = "0.2.2"
|
||||
|
@ -263,7 +288,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cryptools"
|
||||
version = "0.12.4"
|
||||
version = "0.12.5"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"crptls",
|
||||
|
@ -313,12 +338,6 @@ version = "0.15.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
|
||||
|
||||
[[package]]
|
||||
name = "endian-type"
|
||||
version = "0.1.2"
|
||||
|
@ -441,15 +460,6 @@ dependencies = [
|
|||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.9"
|
||||
|
@ -477,6 +487,16 @@ version = "0.4.10"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.11"
|
||||
|
@ -492,6 +512,18 @@ version = "2.3.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nibble_vec"
|
||||
version = "0.1.0"
|
||||
|
@ -533,6 +565,29 @@ version = "1.18.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"redox_syscall 0.4.1",
|
||||
"smallvec",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
version = "0.2.0"
|
||||
|
@ -671,6 +726,15 @@ dependencies = [
|
|||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_termios"
|
||||
version = "0.1.1"
|
||||
|
@ -828,6 +892,36 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"signal-hook-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-mio"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mio",
|
||||
"signal-hook",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simdutf8"
|
||||
version = "0.1.4"
|
||||
|
@ -990,15 +1084,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tui"
|
||||
version = "0.8.0"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b871b61f4c4b81e630215cd12e0ec29953d4545898e21a9e023b7520a74a9f9"
|
||||
checksum = "ccdd26cbd674007e649a272da4475fb666d3aa0ad0531da7136db6fab0e5bad1"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"cassowary",
|
||||
"either",
|
||||
"itertools",
|
||||
"log",
|
||||
"crossterm",
|
||||
"termion 1.5.5",
|
||||
"unicode-segmentation",
|
||||
"unicode-width",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "cryptools"
|
||||
version = "0.12.4"
|
||||
version = "0.12.5"
|
||||
authors = ["scoobybejesus <scoobybejesus@users.noreply.github.com>"]
|
||||
edition = "2021"
|
||||
description = "Command-line utility for processing cryptocurrency transactions into 'lots' and 'movements'."
|
||||
|
@ -26,7 +26,7 @@ rust_decimal_macros = "1.32.0"
|
|||
chrono = { version = "0.4.31", features = ["serde"] }
|
||||
structopt = "0.2.10"
|
||||
rustyline = "12.0.0"
|
||||
tui = { version = "0.8", optional = true, features = ['termion'] }
|
||||
tui = { version = "0.19", optional = true, features = ['termion'] }
|
||||
termion = { version = "2.0.1", optional = true }
|
||||
dotenv = "0.15.0"
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ use std::collections::HashMap;
|
|||
use crptls::transaction::{Transaction, ActionRecord};
|
||||
use crptls::account::{Account, RawAccount};
|
||||
use crptls::core_functions::ImportProcessParameters;
|
||||
use tui::widgets::ListState;
|
||||
|
||||
use crate::export::{export_csv, export_je, export_txt};
|
||||
|
||||
|
@ -24,36 +25,51 @@ pub (crate) const REPORTS: [&'static str; 11] = [
|
|||
"11. TXT: Bookkeeping journal entries",
|
||||
];
|
||||
|
||||
pub struct ListState<I> {
|
||||
pub struct StatefulList<I> {
|
||||
pub items: Vec<I>,
|
||||
pub selected: usize,
|
||||
pub state: ListState,
|
||||
}
|
||||
|
||||
impl<I> ListState<I> {
|
||||
impl<T> StatefulList<T> {
|
||||
|
||||
fn new(items: Vec<I>) -> ListState<I> {
|
||||
ListState { items, selected: 0 }
|
||||
fn new(items: Vec<T>) -> StatefulList<T> {
|
||||
StatefulList { items, state: ListState::default() }
|
||||
}
|
||||
|
||||
fn select_previous(&mut self) {
|
||||
|
||||
if self.selected > 0 {
|
||||
self.selected -= 1;
|
||||
let i = match self.state.selected() {
|
||||
Some(i) => {
|
||||
if i == 0 {
|
||||
self.items.len() - 1
|
||||
} else {
|
||||
i - 1
|
||||
}
|
||||
}
|
||||
None => 0,
|
||||
};
|
||||
self.state.select(Some(i));
|
||||
}
|
||||
|
||||
fn select_next(&mut self) {
|
||||
let i = match self.state.selected() {
|
||||
Some(i) => {
|
||||
if i >= self.items.len() - 1 {
|
||||
0
|
||||
} else {
|
||||
i + 1
|
||||
}
|
||||
}
|
||||
None => 0,
|
||||
};
|
||||
self.state.select(Some(i));
|
||||
}
|
||||
|
||||
if self.selected < self.items.len() - 1 {
|
||||
self.selected += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PrintWindow<'a> {
|
||||
pub title: &'a str,
|
||||
pub should_quit: bool,
|
||||
pub tasks: ListState<&'a str>,
|
||||
pub tasks: StatefulList<&'a str>,
|
||||
pub to_print_by_idx: Vec<usize>,
|
||||
pub to_print_by_title: Vec<&'a str>,
|
||||
}
|
||||
|
@ -61,10 +77,13 @@ pub struct PrintWindow<'a> {
|
|||
impl<'a> PrintWindow<'a> {
|
||||
|
||||
pub fn new(title: &'a str) -> PrintWindow<'a> {
|
||||
let mut tasks = StatefulList::new(REPORTS.to_vec());
|
||||
tasks.state.select(Some(0));
|
||||
|
||||
PrintWindow {
|
||||
title,
|
||||
should_quit: false,
|
||||
tasks: ListState::new(REPORTS.to_vec()),
|
||||
tasks,
|
||||
to_print_by_idx: Vec::with_capacity(REPORTS.len()),
|
||||
to_print_by_title: Vec::with_capacity(REPORTS.len()),
|
||||
}
|
||||
|
@ -78,7 +97,7 @@ impl<'a> PrintWindow<'a> {
|
|||
self.tasks.select_next();
|
||||
}
|
||||
|
||||
pub fn on_key(&mut self, c: char) {
|
||||
pub fn on_key(&mut self, c: char) -> Result<(), Box<dyn Error>> {
|
||||
|
||||
match c {
|
||||
|
||||
|
@ -90,7 +109,7 @@ impl<'a> PrintWindow<'a> {
|
|||
self.should_quit = true;
|
||||
}
|
||||
'x' => {
|
||||
let selected = self.tasks.selected;
|
||||
let selected = self.tasks.state.selected().unwrap();
|
||||
if self.to_print_by_idx.contains(&selected) {} else {
|
||||
self.to_print_by_idx.push(selected);
|
||||
self.to_print_by_title.push(self.tasks.items[selected])
|
||||
|
@ -99,7 +118,7 @@ impl<'a> PrintWindow<'a> {
|
|||
self.tasks.select_next();
|
||||
}
|
||||
'd' => {
|
||||
let selected_idx = self.tasks.selected;
|
||||
let selected_idx = self.tasks.state.selected().unwrap();
|
||||
self.to_print_by_idx.retain(|&x| x != selected_idx );
|
||||
let selected_str = self.tasks.items[selected_idx];
|
||||
self.to_print_by_title.retain(|&x| x != selected_str );
|
||||
|
@ -107,6 +126,7 @@ impl<'a> PrintWindow<'a> {
|
|||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn change_vecs_to_chrono_order(vec: &mut Vec<usize>, strvec: &mut Vec<&str>) {
|
||||
|
|
|
@ -46,14 +46,14 @@ pub (crate) fn print_menu_tui(
|
|||
|
||||
loop {
|
||||
|
||||
ui::draw(&mut terminal, &app)?;
|
||||
ui::draw(&mut terminal, &mut app)?;
|
||||
|
||||
if let Event::Input(key) = events.next()? {
|
||||
|
||||
match key {
|
||||
|
||||
Key::Char(c) => {
|
||||
app.on_key(c);
|
||||
app.on_key(c)?;
|
||||
}
|
||||
Key::Up => {
|
||||
app.on_up();
|
||||
|
|
115
src/mytui/ui.rs
115
src/mytui/ui.rs
|
@ -1,35 +1,57 @@
|
|||
// Copyright (c) 2017-2023, scoobybejesus
|
||||
// Redistributions must include the license: https://github.com/scoobybejesus/cryptools/blob/master/LEGAL.txt
|
||||
|
||||
use std::io;
|
||||
use std::error::Error;
|
||||
|
||||
use ::tui::Terminal;
|
||||
use ::tui::style::{Color, Modifier, Style};
|
||||
use ::tui::widgets::{Widget, Block, Borders, SelectableList, Text, Paragraph, List};
|
||||
use ::tui::text::{Text, Span, Spans};
|
||||
use tui::widgets::{Wrap, ListItem};
|
||||
use ::tui::widgets::{Block, Borders, Paragraph, List};
|
||||
use ::tui::layout::{Layout, Constraint, Direction};
|
||||
use ::tui::backend::Backend;
|
||||
|
||||
use crate::mytui::app::{PrintWindow, REPORTS};
|
||||
|
||||
|
||||
pub fn draw<B: Backend>(terminal: &mut Terminal<B>, app: &PrintWindow) -> Result<(), io::Error> {
|
||||
pub fn draw<B: Backend>(terminal: &mut Terminal<B>, app: &mut PrintWindow) -> Result<(), Box<dyn Error>> {
|
||||
|
||||
terminal.draw(|mut f| {
|
||||
terminal.draw(|f| {
|
||||
|
||||
let instructions = [
|
||||
Text::raw("\nPress '"),
|
||||
Text::styled("x", Style::default().fg(Color::Cyan).modifier(Modifier::BOLD)),
|
||||
Text::raw("' to add the selected report to the list of reports to print/export.\n"),
|
||||
Text::raw("\nPress '"),
|
||||
Text::styled("d", Style::default().fg(Color::Yellow).modifier(Modifier::BOLD)),
|
||||
Text::raw("' to delete the selected report from the list of reports to print/export.\n"),
|
||||
Text::raw("\nPress '"),
|
||||
Text::styled("p", Style::default().fg(Color::Green).modifier(Modifier::BOLD)),
|
||||
Text::raw("' to print/export the selected reports.\n"),
|
||||
Text::raw("\nPress '"),
|
||||
Text::styled("q", Style::default().fg(Color::Red).modifier(Modifier::BOLD)),
|
||||
Text::raw("' to quit without printing.\n\n"),
|
||||
let instructions = vec![
|
||||
Spans::from(vec![Span::raw("")]),
|
||||
|
||||
Spans::from(vec![
|
||||
Span::raw(" Press '"),
|
||||
Span::styled("x", Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD)),
|
||||
Span::raw("' to add the selected report to the list of reports to print/export."),
|
||||
]),
|
||||
|
||||
Spans::from(vec![Span::raw("")]),
|
||||
|
||||
Spans::from(vec![
|
||||
Span::raw(" Press '"),
|
||||
Span::styled("d", Style::default().fg(Color::Yellow).add_modifier(Modifier::BOLD)),
|
||||
Span::raw("' to delete the selected report from the list of reports to print/export."),
|
||||
]),
|
||||
|
||||
Spans::from(vec![Span::raw("")]),
|
||||
|
||||
Spans::from(vec![
|
||||
Span::raw(" Press '"),
|
||||
Span::styled("p", Style::default().fg(Color::Green).add_modifier(Modifier::BOLD)),
|
||||
Span::raw("' to print/export the selected reports."),
|
||||
]),
|
||||
|
||||
Spans::from(vec![Span::raw("")]),
|
||||
|
||||
Spans::from(vec![
|
||||
Span::raw(" Press '"),
|
||||
Span::styled("q", Style::default().fg(Color::Red).add_modifier(Modifier::BOLD)),
|
||||
Span::raw("' to quit without printing."),
|
||||
]),
|
||||
];
|
||||
|
||||
let rpts_to_prnt = app.to_print_by_title.iter().map(|&rpt_to_prnt| {
|
||||
Text::styled(
|
||||
format!("{}", rpt_to_prnt),
|
||||
|
@ -40,53 +62,60 @@ pub fn draw<B: Backend>(terminal: &mut Terminal<B>, app: &PrintWindow) -> Result
|
|||
let top_level_chunks = Layout::default()
|
||||
.constraints([
|
||||
Constraint::Length(1),
|
||||
Constraint::Length(2 + (instructions.len() as u16 / 3 * 2)), // 2 for title "Instructions", plus 3 TEXT array elements/instruction
|
||||
Constraint::Length(instructions.len() as u16 + 2),
|
||||
Constraint::Length(REPORTS.len() as u16 + 2),
|
||||
Constraint::Length(rpts_to_prnt.len() as u16 + 2),
|
||||
Constraint::Length(1),
|
||||
].as_ref())
|
||||
.split(f.size());
|
||||
|
||||
Paragraph::new(instructions.iter())
|
||||
.block(
|
||||
Block::default()
|
||||
let pg1 = Paragraph::new(instructions)
|
||||
.block(Block::default()
|
||||
.borders(Borders::NONE)
|
||||
.title("Instructions")
|
||||
.title_style(Style::default().fg(Color::Blue).modifier(Modifier::BOLD).modifier(Modifier::UNDERLINED)),
|
||||
.title(Span::styled(
|
||||
"Instructions",
|
||||
Style::default().fg(Color::LightMagenta).add_modifier(Modifier::BOLD).add_modifier(Modifier::UNDERLINED)
|
||||
))
|
||||
)
|
||||
.wrap(true)
|
||||
.render(&mut f, top_level_chunks[1]);
|
||||
.wrap(Wrap {trim: false});
|
||||
f.render_widget(pg1, top_level_chunks[1]);
|
||||
|
||||
let level_2_chunks = Layout::default()
|
||||
.constraints([Constraint::Percentage(10), Constraint::Percentage(80),Constraint::Percentage(10),].as_ref())
|
||||
.direction(Direction::Horizontal)
|
||||
.split(top_level_chunks[2]);
|
||||
|
||||
SelectableList::default()
|
||||
.block(
|
||||
Block::default()
|
||||
let report_list_items: Vec<_> = app.tasks.items.iter().map(|i| ListItem::new(*i)).collect();
|
||||
|
||||
let items = List::new(report_list_items)
|
||||
.block(Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.title("Reports available for exporting")
|
||||
.title_style(Style::default().fg(Color::White).modifier(Modifier::BOLD).modifier(Modifier::UNDERLINED))
|
||||
.title(Span::styled(
|
||||
"Reports available for exporting",
|
||||
Style::default().fg(Color::White).add_modifier(Modifier::BOLD).add_modifier(Modifier::UNDERLINED)
|
||||
))
|
||||
)
|
||||
.items(&app.tasks.items)
|
||||
.select(Some(app.tasks.selected))
|
||||
.highlight_style(Style::default().fg(Color::Yellow).modifier(Modifier::BOLD))
|
||||
.highlight_symbol(">")
|
||||
.render(&mut f, level_2_chunks[1]);
|
||||
.highlight_style(Style::default().fg(Color::Yellow).add_modifier(Modifier::BOLD))
|
||||
.highlight_symbol(">");
|
||||
f.render_stateful_widget(items, level_2_chunks[1], &mut app.tasks.state);
|
||||
|
||||
let level_2_chunks = Layout::default()
|
||||
.constraints([Constraint::Percentage(10), Constraint::Percentage(80),Constraint::Percentage(10),].as_ref())
|
||||
.direction(Direction::Horizontal)
|
||||
.split(top_level_chunks[3]);
|
||||
|
||||
List::new(rpts_to_prnt)
|
||||
.block(
|
||||
Block::default()
|
||||
let rpts_to_prnt: Vec<_> = app.to_print_by_title.iter().map(|i| ListItem::new(*i)).collect();
|
||||
|
||||
let to_print = List::new(rpts_to_prnt)
|
||||
.block(Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.title("Reports to be exported")
|
||||
.title_style(Style::default().fg(Color::LightYellow).modifier(Modifier::BOLD).modifier(Modifier::UNDERLINED))
|
||||
)
|
||||
.render(&mut f, level_2_chunks[1]);
|
||||
})
|
||||
.title(Span::styled(
|
||||
"Reports to be exported",
|
||||
Style::default().fg(Color::LightYellow).add_modifier(Modifier::BOLD).add_modifier(Modifier::UNDERLINED)
|
||||
))
|
||||
);
|
||||
f.render_widget(to_print, level_2_chunks[1]);
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in New Issue