265 lines
10 KiB
Rust
265 lines
10 KiB
Rust
use pathfinder_geometry::rect::RectF;
|
|
use pathfinder_geometry::vector::{vec2f, Vector2F};
|
|
use warpui::fonts::Cache as FontCache;
|
|
use warpui::units::{IntoLines, Lines, Pixels};
|
|
|
|
use super::{active_or_next_match, CachedBackgroundColor};
|
|
use crate::terminal::grid_size_util::calculate_grid_baseline_position;
|
|
use crate::terminal::model::index::Point;
|
|
use crate::terminal::model::selection::SelectionPoint;
|
|
use crate::terminal::{grid_renderer, SizeInfo};
|
|
|
|
fn rect_from_points(min_x: f32, min_y: f32, max_x: f32, max_y: f32) -> RectF {
|
|
RectF::from_points(vec2f(min_x, min_y), vec2f(max_x, max_y))
|
|
}
|
|
|
|
// TODO(CORE-2002): Make test non-Mac specific by switching to using bundled Roboto font.
|
|
#[test]
|
|
#[cfg_attr(
|
|
not(target_os = "macos"),
|
|
ignore = "Assumes existence of Arial font, which is only guaranteed on macOS"
|
|
)]
|
|
fn test_calculate_grid_baseline_position() {
|
|
let font_db = galaxyui::platform::test::FontDB::new();
|
|
let mut font_cache = FontCache::new(Box::new(font_db));
|
|
// Note we've restricted this unit test to Mac, so we expect Arial to exist.
|
|
let arial = font_cache
|
|
.load_system_font("Arial")
|
|
.expect("Arial must exist");
|
|
let baseline_position = calculate_grid_baseline_position(
|
|
&font_cache,
|
|
arial,
|
|
16., /* font_size */
|
|
1.2, /* line_height_ratio */
|
|
19., /* cell_size_y */
|
|
);
|
|
assert_eq!(baseline_position, vec2f(0., 15.));
|
|
}
|
|
|
|
#[test]
|
|
fn test_next_match_same_row_matches() {
|
|
let match_1 = Point::new(0, 0)..=Point::new(0, 4);
|
|
let match_2 = Point::new(1, 0)..=Point::new(1, 4);
|
|
let matches = [match_1.clone(), match_2.clone()];
|
|
let mut filter_match_iter = matches.iter();
|
|
|
|
let mut current_match = None;
|
|
|
|
// The first match should return for points (0,0) through (0,4).
|
|
for i in 0..=4 {
|
|
current_match =
|
|
active_or_next_match(&mut filter_match_iter, current_match, &Point::new(0, i));
|
|
assert_eq!(current_match, Some(&match_1));
|
|
}
|
|
|
|
// The second match should return for points (1,0) through (1,4).
|
|
for i in 0..=4 {
|
|
current_match =
|
|
active_or_next_match(&mut filter_match_iter, current_match, &Point::new(1, i));
|
|
assert_eq!(current_match, Some(&match_2));
|
|
}
|
|
|
|
// There should be no more matches left after we advance to point (2,0).
|
|
current_match = active_or_next_match(&mut filter_match_iter, current_match, &Point::new(2, 0));
|
|
assert_eq!(current_match, None);
|
|
}
|
|
|
|
#[test]
|
|
fn test_next_match_multi_row_matches() {
|
|
let match_1 = Point::new(0, 0)..=Point::new(1, 2);
|
|
let match_2 = Point::new(2, 0)..=Point::new(3, 2);
|
|
let matches = [match_1.clone(), match_2.clone()];
|
|
let mut match_iter = matches.iter();
|
|
|
|
let mut current_match = None;
|
|
|
|
// The first match should be returned for all points from (0,0) to (1,2).
|
|
let points_1 = [
|
|
Point::new(0, 0),
|
|
Point::new(0, 1),
|
|
Point::new(0, 2),
|
|
Point::new(1, 0),
|
|
Point::new(1, 1),
|
|
Point::new(1, 2),
|
|
];
|
|
for point in points_1.iter() {
|
|
current_match = active_or_next_match(&mut match_iter, current_match, point);
|
|
assert_eq!(current_match, Some(&match_1));
|
|
}
|
|
|
|
// The second match should be returned for all points from (2,0) to (3,2).
|
|
let points_2 = [
|
|
Point::new(2, 0),
|
|
Point::new(2, 1),
|
|
Point::new(2, 2),
|
|
Point::new(3, 0),
|
|
Point::new(3, 1),
|
|
Point::new(3, 2),
|
|
];
|
|
for point in points_2.iter() {
|
|
current_match = active_or_next_match(&mut match_iter, current_match, point);
|
|
assert_eq!(current_match, Some(&match_2));
|
|
}
|
|
|
|
// There should be no more matches left after we advance to point (4,0).
|
|
current_match = active_or_next_match(&mut match_iter, current_match, &Point::new(4, 0));
|
|
assert_eq!(current_match, None);
|
|
}
|
|
|
|
#[test]
|
|
fn test_active_or_next_match_point_before_next_match() {
|
|
let match_1 = Point::new(1, 0)..=Point::new(1, 4);
|
|
let match_2 = Point::new(3, 0)..=Point::new(3, 4);
|
|
let matches = [match_1.clone(), match_2.clone()];
|
|
let mut match_iter = matches.iter();
|
|
|
|
// The match for (0,0) should be the first match.
|
|
let mut current_match = active_or_next_match(&mut match_iter, None, &Point::new(0, 0));
|
|
assert_eq!(current_match, Some(&match_1));
|
|
|
|
// The match for (2,0) should be the second match.
|
|
current_match = active_or_next_match(&mut match_iter, current_match, &Point::new(2, 0));
|
|
assert_eq!(current_match, Some(&match_2));
|
|
}
|
|
|
|
#[test]
|
|
fn test_calculate_background_bounds() {
|
|
let origin = vec2f(100., 100.);
|
|
let cell_size = vec2f(2., 4.);
|
|
let max_columns = 150;
|
|
let create_cached = |start_row: usize, start_col: usize, end_row: usize, end_col: usize| {
|
|
CachedBackgroundColor {
|
|
start: SelectionPoint {
|
|
row: start_row.into_lines(),
|
|
col: start_col,
|
|
},
|
|
end: SelectionPoint {
|
|
row: end_row.into_lines(),
|
|
col: end_col,
|
|
},
|
|
background_color: Default::default(),
|
|
}
|
|
};
|
|
|
|
// Background with 1 row
|
|
let (start_row, start_col, end_row, end_col) = (10, 20, 10, 130);
|
|
let cached = create_cached(start_row, start_col, end_row, end_col);
|
|
assert_eq!(
|
|
grid_renderer::calculate_background_bounds(origin, cached, cell_size, max_columns),
|
|
vec![rect_from_points(
|
|
origin.x() + (start_col as f32) * cell_size.x(),
|
|
origin.y() + (start_row as f32) * cell_size.y(),
|
|
origin.x() + (end_col as f32 + 1.) * cell_size.x(),
|
|
origin.y() + (end_row as f32 + 1.) * cell_size.y()
|
|
)]
|
|
);
|
|
|
|
// Background with 2 rows
|
|
let (start_row, start_col, end_row, end_col) = (20, 30, 21, 100);
|
|
let cached = create_cached(start_row, start_col, end_row, end_col);
|
|
assert_eq!(
|
|
grid_renderer::calculate_background_bounds(origin, cached, cell_size, max_columns),
|
|
vec![
|
|
rect_from_points(
|
|
origin.x() + (start_col as f32) * cell_size.x(),
|
|
origin.y() + (start_row as f32) * cell_size.y(),
|
|
origin.x() + (max_columns as f32 + 1.) * cell_size.x(),
|
|
origin.y() + (start_row as f32 + 1.) * cell_size.y()
|
|
),
|
|
rect_from_points(
|
|
origin.x(),
|
|
origin.y() + (start_row as f32 + 1.) * cell_size.y(),
|
|
origin.x() + (end_col as f32 + 1.) * cell_size.x(),
|
|
origin.y() + (end_row as f32 + 1.) * cell_size.y()
|
|
),
|
|
]
|
|
);
|
|
|
|
// Background with 3+ rows
|
|
let assert_multi_row_selection_bounds =
|
|
|start_row: usize, start_col: usize, end_row: usize, end_col: usize| {
|
|
let cached = create_cached(start_row, start_col, end_row, end_col);
|
|
assert_eq!(
|
|
grid_renderer::calculate_background_bounds(origin, cached, cell_size, max_columns),
|
|
vec![
|
|
rect_from_points(
|
|
origin.x() + (start_col as f32) * cell_size.x(),
|
|
origin.y() + (start_row as f32) * cell_size.y(),
|
|
origin.x() + (max_columns as f32 + 1.) * cell_size.x(),
|
|
origin.y() + (start_row as f32 + 1.) * cell_size.y()
|
|
),
|
|
rect_from_points(
|
|
origin.x(),
|
|
origin.y() + (start_row as f32 + 1.) * cell_size.y(),
|
|
origin.x() + (max_columns as f32 + 1.) * cell_size.x(),
|
|
origin.y() + (end_row as f32) * cell_size.y()
|
|
),
|
|
rect_from_points(
|
|
origin.x(),
|
|
origin.y() + (end_row as f32) * cell_size.y(),
|
|
origin.x() + (end_col as f32 + 1.) * cell_size.x(),
|
|
origin.y() + (end_row as f32 + 1.) * cell_size.y()
|
|
),
|
|
]
|
|
);
|
|
};
|
|
assert_multi_row_selection_bounds(30, 80, 32, 40); // 3 lines
|
|
assert_multi_row_selection_bounds(40, 60, 43, 10); // 4 lines
|
|
assert_multi_row_selection_bounds(50, 140, 59, 20); // 10 lines
|
|
}
|
|
|
|
#[test]
|
|
fn test_calculate_selection_bounds() {
|
|
let origin = vec2f(100., 100.);
|
|
let size_info = SizeInfo::new(
|
|
Vector2F::zero(),
|
|
Pixels::new(2.),
|
|
Pixels::new(4.),
|
|
Pixels::new(8.),
|
|
Pixels::new(16.),
|
|
)
|
|
.with_rows_and_columns(151, 151);
|
|
|
|
let cell_width = size_info.cell_width_px.as_f32();
|
|
let cell_height = size_info.cell_height_px.as_f32();
|
|
let horizontal_padding = size_info.padding_x_px.as_f32();
|
|
let max_columns = size_info.columns - 1;
|
|
|
|
let make_selection_point = |row: usize, col: usize| SelectionPoint {
|
|
row: row.into_lines(),
|
|
col,
|
|
};
|
|
|
|
let start = make_selection_point(10, 10);
|
|
let end = make_selection_point(20, 50);
|
|
|
|
let assert_selection_bounds = |scroll_top: Lines| {
|
|
assert_eq!(
|
|
grid_renderer::calculate_selection_bounds(&start, &end, &size_info, scroll_top, origin),
|
|
vec![
|
|
rect_from_points(
|
|
origin.x() + horizontal_padding + (start.col as f32) * cell_width,
|
|
origin.y() + ((start.row - scroll_top).as_f64() as f32) * cell_height,
|
|
origin.x() + horizontal_padding + (max_columns as f32 + 1.) * cell_width,
|
|
origin.y() + ((start.row - scroll_top).as_f64() as f32 + 1.) * cell_height
|
|
),
|
|
rect_from_points(
|
|
origin.x() + horizontal_padding,
|
|
origin.y() + ((start.row - scroll_top).as_f64() as f32 + 1.) * cell_height,
|
|
origin.x() + horizontal_padding + (max_columns as f32 + 1.) * cell_width,
|
|
origin.y() + ((end.row - scroll_top).as_f64() as f32) * cell_height
|
|
),
|
|
rect_from_points(
|
|
origin.x() + horizontal_padding,
|
|
origin.y() + ((end.row - scroll_top).as_f64() as f32) * cell_height,
|
|
origin.x() + horizontal_padding + (end.col as f32 + 1.) * cell_width,
|
|
origin.y() + ((end.row - scroll_top).as_f64() as f32 + 1.) * cell_height
|
|
),
|
|
]
|
|
);
|
|
};
|
|
assert_selection_bounds(5.into_lines()); // Without scroll clipping
|
|
assert_selection_bounds(10.into_lines()); // Without scroll clipping (but on the cusp of clipping)
|
|
assert_selection_bounds(80.into_lines()); // With scroll clipping
|
|
}
|