Start work on formatting options

This commit is contained in:
Austen Adler 2021-05-03 00:48:25 -04:00
parent 062cd97609
commit 85ea8ca776
2 changed files with 116 additions and 9 deletions

View File

@ -333,7 +333,7 @@ impl<'a> Calculator<'a> {
push: OpArgs::Unary(f),
})
}
fn pop(&mut self) -> CalculatorResult<f64> {
pub fn pop(&mut self) -> CalculatorResult<f64> {
let f = self.checked_get(0)?;
self.direct_state_change(CalculatorStateChange {
pop: OpArgs::Unary(f),
@ -341,6 +341,15 @@ impl<'a> Calculator<'a> {
})?;
Ok(f)
}
pub fn pop_usize(&mut self) -> CalculatorResult<usize> {
let f = self.checked_get(0)?;
let ret = usize::from(f as usize);
self.direct_state_change(CalculatorStateChange {
pop: OpArgs::Unary(f),
push: OpArgs::None,
})?;
Ok(ret)
}
pub fn op(&mut self, op: CalculatorOperation) -> CalculatorResult<()> {
// Dup is special -- don't actually run it if l needs to be flushed
if self.flush_l()? {

View File

@ -27,7 +27,18 @@ struct Dimensions {
height: u16,
}
enum DisplayMode {
Default(Option<char>),
Scientific(usize),
Engineering,
}
struct AppSettings {
display_mode: DisplayMode,
}
enum AppState {
AppSettings,
Calculator,
Help,
}
@ -37,6 +48,7 @@ struct App<'a> {
error_msg: Option<String>,
state: AppState,
current_macro: Option<char>,
app_settings: AppSettings,
}
impl<'a> Default for App<'a> {
@ -46,6 +58,9 @@ impl<'a> Default for App<'a> {
error_msg: None,
state: AppState::Calculator,
current_macro: None,
app_settings: AppSettings {
display_mode: DisplayMode::Default(None),
},
}
}
}
@ -80,13 +95,24 @@ fn main() -> Result<(), Box<dyn Error>> {
Span::raw("Error: "),
Span::styled(e, Style::default().add_modifier(Modifier::RAPID_BLINK)),
],
(None, AppState::Calculator) => vec![
Span::raw("Press "),
Span::styled("q", Style::default().add_modifier(Modifier::BOLD)),
Span::raw(" to exit, "),
Span::styled("h", Style::default().add_modifier(Modifier::BOLD)),
Span::raw(" for help"),
],
(None, AppState::Calculator) => {
// TODO: There has to be a better way than making strings each time
let display_mode_str = match &app.app_settings.display_mode {
DisplayMode::Default(None) => String::from("[d]"),
DisplayMode::Default(Some(c)) => format!("[d({})]", c),
DisplayMode::Scientific(p) => format!("[s({})]", p),
DisplayMode::Engineering => String::from("[e]"),
};
vec![
Span::raw("Press "),
Span::styled("q", Style::default().add_modifier(Modifier::BOLD)),
Span::raw(" to exit, "),
Span::styled("h", Style::default().add_modifier(Modifier::BOLD)),
Span::raw(" for help - "),
Span::raw(display_mode_str),
]
}
(None, _) => vec![
Span::raw("Press "),
Span::styled("<esc>", Style::default().add_modifier(Modifier::BOLD)),
@ -106,7 +132,16 @@ fn main() -> Result<(), Box<dyn Error>> {
.enumerate()
.rev()
.map(|(i, m)| {
let content = vec![Spans::from(Span::raw(format!("{}: {}", i, m)))];
let content = vec![Spans::from(Span::raw(
match &app.app_settings.display_mode {
DisplayMode::Default(None) => format!("{:>2}: {}", i, *m),
DisplayMode::Default(Some(c)) => fmt_separated(i, *m, *c),
DisplayMode::Scientific(precision) => fmt_scientific(i, *m, *precision),
DisplayMode::Engineering => {
format!("{:>2}: {}", i, m)
}
},
))];
ListItem::new(content)
})
.collect();
@ -134,6 +169,22 @@ fn main() -> Result<(), Box<dyn Error>> {
);
match (&app.state, app.calculator.get_state()) {
(AppState::AppSettings, _) => {
draw_clippy_rect(
ClippyRectangle {
title: "App Settings",
msg: "\
d => Default\n\
, => Default (comma separated)\n\
s => Scientific\n\
S => Scientific (stack precision)\n\
e => Engineering\n\
E => Engineering (stack precision)\n\
",
},
f,
);
}
(AppState::Help, _) => {
draw_clippy_rect(
ClippyRectangle {
@ -248,6 +299,9 @@ fn handle_key(app: &mut App, events: &Events, key: Key) -> CalculatorResult<bool
Key::Char('h') => {
app.state = AppState::Help;
}
Key::Ctrl('s') => {
app.state = AppState::AppSettings;
}
Key::Char('\n') | Key::Char(' ') => {
app.calculator.take_input(' ')?;
}
@ -265,6 +319,20 @@ fn handle_key(app: &mut App, events: &Events, key: Key) -> CalculatorResult<bool
}
_ => {}
},
(AppState::AppSettings, _) => match key {
Key::Esc | Key::Char('q') => {
app.state = AppState::Calculator;
app.calculator.cancel()?;
}
Key::Char('d') => app.app_settings.display_mode = DisplayMode::Default(None),
Key::Char(',') => app.app_settings.display_mode = DisplayMode::Default(Some(',')),
Key::Char('s') => app.app_settings.display_mode = DisplayMode::Scientific(3),
Key::Char('S') => {
app.app_settings.display_mode = DisplayMode::Scientific(app.calculator.pop_usize()?)
}
Key::Char('e') => app.app_settings.display_mode = DisplayMode::Engineering,
_ => {}
},
(AppState::Help, _) => match key {
Key::Esc | Key::Char('q') => {
app.state = AppState::Calculator;
@ -336,3 +404,33 @@ fn draw_clippy_rect<T: std::io::Write>(c: ClippyRectangle, f: &mut Frame<Termion
.block(Block::default().borders(Borders::ALL).title(c.title));
f.render_widget(help_message, area);
}
fn fmt_scientific(i: usize, f: f64, precision: usize) -> String {
let mut ret = format!("{:.precision$e}", f, precision = precision);
let exp = ret.split_off(ret.find('e').unwrap_or(0));
let (pow_sign, exp) = if exp.starts_with("e-") {
('-', &exp[2..])
} else {
('+', &exp[1..])
};
let sign = if !ret.starts_with('-') { " " } else { "" };
format!(
"{:>2}: {}{}e{}{:0>pad$}",
i,
sign,
ret,
pow_sign,
exp,
pad = 2
)
}
fn fmt_separated(i: usize, f: f64, sep: char) -> String {
let mut ret = f.to_string();
let start = if ret.starts_with('-') { 1 } else { 0 };
let end = ret.find('.').unwrap_or(ret.len());
for i in 0..((end - start).div_euclid(3)) {
ret.insert(end - start - (i + 1) * 3, sep);
}
format!("{:>2}: {}", i, ret)
}