Compare commits
6 Commits
4239844e0e
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 9de22b9680 | |||
| 3f336578f5 | |||
| 5fbff13428 | |||
| 7a23ee5bc0 | |||
| ea2e633d7d | |||
| d9f29434e9 |
@@ -19,7 +19,7 @@ use crate::app::{
|
|||||||
clipboard::Clipboard,
|
clipboard::Clipboard,
|
||||||
error_msg::StatusMessage,
|
error_msg::StatusMessage,
|
||||||
logic::{
|
logic::{
|
||||||
calc::{Grid, get_header_size},
|
calc::{Grid, LEN, get_header_size},
|
||||||
cell::CellType,
|
cell::CellType,
|
||||||
},
|
},
|
||||||
mode::Mode,
|
mode::Mode,
|
||||||
@@ -42,9 +42,11 @@ pub struct App {
|
|||||||
|
|
||||||
impl Widget for &App {
|
impl Widget for &App {
|
||||||
fn render(self, area: prelude::Rect, buf: &mut prelude::Buffer) {
|
fn render(self, area: prelude::Rect, buf: &mut prelude::Buffer) {
|
||||||
|
// let now = std::time::Instant::now();
|
||||||
|
|
||||||
let (x_max, y_max) = self.screen.how_many_cells_fit_in(&area, &self.vars);
|
let (x_max, y_max) = self.screen.how_many_cells_fit_in(&area, &self.vars);
|
||||||
|
|
||||||
let is_selected = |x: usize, y: usize| -> bool {
|
let is_visually_selected = |x: usize, y: usize| -> bool {
|
||||||
if let Mode::Visual((mut x1, mut y1)) | Mode::VisualCmd((mut x1, mut y1), _) = self.mode {
|
if let Mode::Visual((mut x1, mut y1)) | Mode::VisualCmd((mut x1, mut y1), _) = self.mode {
|
||||||
let (mut x2, mut y2) = self.grid.cursor();
|
let (mut x2, mut y2) = self.grid.cursor();
|
||||||
x1 += 1;
|
x1 += 1;
|
||||||
@@ -63,10 +65,40 @@ impl Widget for &App {
|
|||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
for x in 0..x_max {
|
// cells that are related by reference to the cursor's cell
|
||||||
for y in 0..y_max {
|
// (inputs to formulas and such)
|
||||||
let mut display = String::new();
|
let cells_of_interest: Vec<(usize, usize)> = {
|
||||||
let mut style = Style::new();
|
let ctx = crate::app::logic::ctx::ExtractionContext::new();
|
||||||
|
let (x, y) = self.grid.cursor();
|
||||||
|
if let Some(cell) = self.grid.get_cell_raw(x, y) {
|
||||||
|
if let CellType::Equation(eq) = cell {
|
||||||
|
let _ = evalexpr::eval_with_context(&eq[1..], &ctx);
|
||||||
|
let vars = ctx.dump_vars();
|
||||||
|
|
||||||
|
let mut interest = Vec::new();
|
||||||
|
for var in vars {
|
||||||
|
if let Some(a) = Grid::parse_to_idx(&var) {
|
||||||
|
interest.push(a);
|
||||||
|
} else if let Some((start, end)) = Grid::range_as_indices(&var) {
|
||||||
|
// insert coords:
|
||||||
|
// (start, 0..len)
|
||||||
|
// ..
|
||||||
|
// (end, 0..len)
|
||||||
|
for x in start..=end {
|
||||||
|
for y in 0..=super::logic::calc::LEN {
|
||||||
|
interest.push((x,y))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
interest
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Custom width for the header of each row
|
// Custom width for the header of each row
|
||||||
let row_header_width = get_header_size() as u16;
|
let row_header_width = get_header_size() as u16;
|
||||||
@@ -75,6 +107,11 @@ impl Widget for &App {
|
|||||||
let cell_width = self.screen.get_cell_width(&self.vars) as u16;
|
let cell_width = self.screen.get_cell_width(&self.vars) as u16;
|
||||||
let cell_height = self.screen.get_cell_height(&self.vars) as u16;
|
let cell_height = self.screen.get_cell_height(&self.vars) as u16;
|
||||||
|
|
||||||
|
for x in 0..x_max {
|
||||||
|
for y in 0..y_max {
|
||||||
|
let mut display = String::new();
|
||||||
|
let mut style = Style::new();
|
||||||
|
|
||||||
// Minus 1 because of header cells,
|
// Minus 1 because of header cells,
|
||||||
// the grid is shifted over (1,1), so if you need
|
// the grid is shifted over (1,1), so if you need
|
||||||
// to index the grid, these are you values.
|
// to index the grid, these are you values.
|
||||||
@@ -87,6 +124,11 @@ impl Widget for &App {
|
|||||||
y_idx = y as usize - 1 + self.screen.scroll_y();
|
y_idx = y as usize - 1 + self.screen.scroll_y();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// don't render non-accessible cells
|
||||||
|
if x_idx > LEN-1 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const ORANGE1: Color = Color::Rgb(200, 160, 0);
|
const ORANGE1: Color = Color::Rgb(200, 160, 0);
|
||||||
const ORANGE2: Color = Color::Rgb(180, 130, 0);
|
const ORANGE2: Color = Color::Rgb(180, 130, 0);
|
||||||
|
|
||||||
@@ -169,6 +211,7 @@ impl Widget for &App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===================================================
|
||||||
// Allow for text in one cell to visually overflow into empty cells
|
// Allow for text in one cell to visually overflow into empty cells
|
||||||
suggest_upper_bound = Some(display.len() as u16);
|
suggest_upper_bound = Some(display.len() as u16);
|
||||||
// check for cells to the right, see if we should truncate the cell width
|
// check for cells to the right, see if we should truncate the cell width
|
||||||
@@ -185,18 +228,25 @@ impl Widget for &App {
|
|||||||
display.push('…');
|
display.push('…');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ===================================================
|
||||||
}
|
}
|
||||||
// Don't render blank cells
|
// Don't render blank cells
|
||||||
None => should_render = false,
|
None => should_render = false,
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_selected(x.into(), y.into()) {
|
if cells_of_interest.contains(&(x_idx, y_idx)) {
|
||||||
|
style = style.fg(Color::Yellow);
|
||||||
|
should_render = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_visually_selected(x.into(), y.into()) {
|
||||||
style = style.bg(Color::Blue);
|
style = style.bg(Color::Blue);
|
||||||
// Make it so that cells render when selected. This fixes issue #32
|
// Make it so that cells render when selected. This fixes issue #32
|
||||||
should_render = true;
|
should_render = true;
|
||||||
}
|
}
|
||||||
if (x_idx, y_idx) == self.grid.cursor() {
|
if (x_idx, y_idx) == self.grid.cursor() {
|
||||||
should_render = true;
|
should_render = true;
|
||||||
|
|
||||||
style = Style::new().fg(Color::Black).bg(Color::White);
|
style = Style::new().fg(Color::Black).bg(Color::White);
|
||||||
// modify the style of the cell you are editing
|
// modify the style of the cell you are editing
|
||||||
if let Mode::Insert(_) = self.mode {
|
if let Mode::Insert(_) = self.mode {
|
||||||
@@ -235,6 +285,9 @@ impl Widget for &App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// let ns = now.elapsed().as_nanos() as f64;
|
||||||
|
// eprintln!("Rendered in {}ms", ns/1_000_000.);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -489,6 +489,8 @@ impl Grid {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the indices of the range labels.
|
||||||
|
/// A:B -> (0,1)
|
||||||
pub fn range_as_indices(range: &str) -> Option<(usize, usize)> {
|
pub fn range_as_indices(range: &str) -> Option<(usize, usize)> {
|
||||||
let v = range.split(':').collect::<Vec<&str>>();
|
let v = range.split(':').collect::<Vec<&str>>();
|
||||||
if v.len() == 2 {
|
if v.len() == 2 {
|
||||||
@@ -761,14 +763,16 @@ fn fn_of_fn() {
|
|||||||
grid.set_cell("C0", "=A0+B0".to_string());
|
grid.set_cell("C0", "=A0+B0".to_string());
|
||||||
grid.set_cell("D0", "=C0*2".to_string());
|
grid.set_cell("D0", "=C0*2".to_string());
|
||||||
|
|
||||||
if let Some(cell) = grid.get_cell("D0") {
|
let cell = grid.get_cell("D0");
|
||||||
|
assert!(cell.is_some());
|
||||||
|
|
||||||
|
if let Some(cell) = cell {
|
||||||
let res = grid.evaluate(&cell.to_string());
|
let res = grid.evaluate(&cell.to_string());
|
||||||
|
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
assert_eq!(res.unwrap(), (6.).into());
|
assert_eq!(res.unwrap(), (6.).into());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
panic!("Cell not found");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Two cells that have a circular dependency to solve for a value
|
// Two cells that have a circular dependency to solve for a value
|
||||||
@@ -778,12 +782,14 @@ fn circular_reference_cells() {
|
|||||||
grid.set_cell("A0", "=B0".to_string());
|
grid.set_cell("A0", "=B0".to_string());
|
||||||
grid.set_cell("B0", "=A0".to_string());
|
grid.set_cell("B0", "=A0".to_string());
|
||||||
|
|
||||||
if let Some(cell) = grid.get_cell("A0") {
|
let cell = grid.get_cell("A0");
|
||||||
|
assert!(cell.is_some());
|
||||||
|
|
||||||
|
if let Some(cell) = cell {
|
||||||
let res = grid.evaluate(&cell.to_string());
|
let res = grid.evaluate(&cell.to_string());
|
||||||
assert!(res.is_err());
|
assert!(res.is_err());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
panic!("Cell not found");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -188,7 +188,14 @@ impl<'a> Context for CallbackContext<'a> {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
e => panic!("> Error {e}\n> Equation: '{eq}'"),
|
e => {
|
||||||
|
let msg = format!("> Error {e}\n> Equation: '{eq}'");
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
panic!("{msg}");
|
||||||
|
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
eprintln!("{msg}");
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -273,13 +273,13 @@ impl Mode {
|
|||||||
// Go to end of row
|
// Go to end of row
|
||||||
'$' => {
|
'$' => {
|
||||||
let (_, y) = app.grid.cursor();
|
let (_, y) = app.grid.cursor();
|
||||||
app.grid.mv_cursor_to(super::logic::calc::LEN, y);
|
app.grid.mv_cursor_to(super::logic::calc::LEN-1, y);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Go to bottom of column
|
// Go to bottom of column
|
||||||
'G' => {
|
'G' => {
|
||||||
let (x, _) = app.grid.cursor();
|
let (x, _) = app.grid.cursor();
|
||||||
app.grid.mv_cursor_to(x, super::logic::calc::LEN);
|
app.grid.mv_cursor_to(x, super::logic::calc::LEN-1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// edit cell
|
// edit cell
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use std::{collections::HashMap, sync::RwLock};
|
use std::{cmp::min, collections::HashMap, sync::RwLock};
|
||||||
|
|
||||||
use ratatui::prelude;
|
use ratatui::prelude;
|
||||||
|
|
||||||
@@ -123,22 +123,12 @@ impl ScreenSpace {
|
|||||||
l.1 = area.height as usize;
|
l.1 = area.height as usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
// let width = (area.width as usize + calc::get_header_size() -1) / self.get_cell_width(vars);
|
let width = (area.width as usize / self.get_cell_width(vars)) + 1;
|
||||||
let width = area.width as usize / self.get_cell_width(vars);
|
|
||||||
let height = area.height as usize / self.get_cell_height(vars);
|
let height = area.height as usize / self.get_cell_height(vars);
|
||||||
|
|
||||||
let x_max =
|
let x_max = min(LEN-1, width);
|
||||||
if width > LEN {
|
let y_max = min(LEN-1, height);
|
||||||
LEN - 1
|
|
||||||
} else {
|
|
||||||
width
|
|
||||||
};
|
|
||||||
let y_max =
|
|
||||||
if height > LEN {
|
|
||||||
LEN - 1
|
|
||||||
} else {
|
|
||||||
height
|
|
||||||
};
|
|
||||||
(x_max as u16, y_max as u16)
|
(x_max as u16, y_max as u16)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,7 +142,7 @@ fn fit_cells() {
|
|||||||
app.vars.insert("height".to_string(), 1.to_string());
|
app.vars.insert("height".to_string(), 1.to_string());
|
||||||
|
|
||||||
let (x,y) = app.screen.how_many_cells_fit_in(&prelude::Rect::new(0, 0, 181, 14), &app.vars);
|
let (x,y) = app.screen.how_many_cells_fit_in(&prelude::Rect::new(0, 0, 181, 14), &app.vars);
|
||||||
assert_eq!(x, 18);
|
assert_eq!(x, 19);
|
||||||
assert_eq!(y, 14);
|
assert_eq!(y, 14);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,7 +156,7 @@ fn scroll() {
|
|||||||
// We have to check how many cells fit, because screen learns the width
|
// We have to check how many cells fit, because screen learns the width
|
||||||
// of the area by rumour here.
|
// of the area by rumour here.
|
||||||
let (x,y) = app.screen.how_many_cells_fit_in(&prelude::Rect::new(0, 0, 181, 14), &app.vars);
|
let (x,y) = app.screen.how_many_cells_fit_in(&prelude::Rect::new(0, 0, 181, 14), &app.vars);
|
||||||
assert_eq!(x, 18);
|
assert_eq!(x, 19);
|
||||||
assert_eq!(y, 14);
|
assert_eq!(y, 14);
|
||||||
|
|
||||||
// we aren't scrolled at all yet
|
// we aren't scrolled at all yet
|
||||||
|
|||||||
Reference in New Issue
Block a user