diff --git a/src/calc.rs b/src/calc.rs index f9339d5..e951b8f 100644 --- a/src/calc.rs +++ b/src/calc.rs @@ -333,7 +333,7 @@ impl<'a> Calculator<'a> { push: OpArgs::Unary(f), }) } - fn pop(&mut self) -> CalculatorResult { + pub fn pop(&mut self) -> CalculatorResult { 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 { + 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()? { diff --git a/src/main.rs b/src/main.rs index 01143ec..60fef14 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,7 +27,18 @@ struct Dimensions { height: u16, } +enum DisplayMode { + Default(Option), + Scientific(usize), + Engineering, +} + +struct AppSettings { + display_mode: DisplayMode, +} + enum AppState { + AppSettings, Calculator, Help, } @@ -37,6 +48,7 @@ struct App<'a> { error_msg: Option, state: AppState, current_macro: Option, + 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> { 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("", Style::default().add_modifier(Modifier::BOLD)), @@ -106,7 +132,16 @@ fn main() -> Result<(), Box> { .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> { ); 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 { 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 {} }, + (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(c: ClippyRectangle, f: &mut Frame 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) +}