use slate_framework::elements::{TextArea, TextAreaStyle};
use slate_framework::text_system::TextSystem;
use slate_reactive::{Runtime, Signal};
macro_rules! ensure_eq {
($left:expr, $right:expr, $($arg:tt)*) => {{
let left = $left;
let right = $right;
if left != right {
return Err(format!(
"{} (left: {:?}, right: {:?})",
format!($($arg)*),
left,
right
));
}
}};
}
macro_rules! ensure {
($cond:expr, $($arg:tt)*) => {
match $cond {
true => {}
false => return Err(format!($($arg)*)),
}
};
}
type Case = (&'static str, fn() -> Result<(), String>);
fn check_text_area_module_smoke() -> Result<(), String> {
let rt = Runtime::new();
let signal = Signal::new(rt, String::new());
let style = TextAreaStyle {
width: 320.0,
min_lines: 3,
..Default::default()
};
let _ta = TextArea::new(signal).style(style);
Ok(())
}
fn check_three_line_document_wraps_to_three_lines_with_caret_on_line_zero() -> Result<(), String> {
let mut text_system = TextSystem::new().expect("create TextSystem");
let font = text_system
.load_font_from_bytes(slate_text::TEST_FONT, 14.0, 1.0)
.expect("load bundled font");
let doc = text_system
.shape_document(&font, "alpha\nbeta\ngamma")
.expect("shape document");
let layout = slate_text::wrap_document(&doc, 1000.0);
ensure_eq!(
layout.lines.len(),
3,
"two hard newlines must yield three visual lines"
);
ensure_eq!(layout.lines[0].byte_start, 0, "first line starts at byte 0");
for pair in layout.lines.windows(2) {
ensure_eq!(
pair[0].byte_end,
pair[1].byte_start,
"visual-line byte ranges must be contiguous"
);
}
ensure_eq!(
layout.lines.last().unwrap().byte_end,
"alpha\nbeta\ngamma".len(),
"final line must cover to text end"
);
let (line_idx, x_lpx, y_lpx) = layout.caret_position(0);
ensure_eq!(line_idx, 0, "caret at byte 0 resolves to the first line");
ensure_eq!(x_lpx, 0.0, "caret at byte 0 sits at the line's left edge");
ensure_eq!(y_lpx, 0.0, "first line's top is at y = 0");
ensure!(layout.line_height_lpx > 0.0, "line height is positive");
ensure_eq!(
layout.lines[1].line.y_offset_lpx,
layout.line_height_lpx,
"line1 sits one line-height down"
);
ensure_eq!(
layout.lines[2].line.y_offset_lpx,
2.0 * layout.line_height_lpx,
"line2 sits two line-heights down"
);
Ok(())
}
fn check_multi_space_run_preserves_every_space_byte() -> Result<(), String> {
let mut text_system = TextSystem::new().expect("create TextSystem");
let font = text_system
.load_font_from_bytes(slate_text::TEST_FONT, 14.0, 1.0)
.expect("load bundled font");
let one = text_system.shape_document(&font, "a b").expect("shape one");
let five = text_system
.shape_document(&font, "a b")
.expect("shape five");
let one_w = slate_text::wrap_document(&one, 1000.0).lines[0]
.line
.width_lpx;
let five_w = slate_text::wrap_document(&five, 1000.0).lines[0]
.line
.width_lpx;
ensure!(
five_w > one_w + 1.0,
"5-space run must be wider than 1-space ({five_w} vs {one_w})"
);
let layout = slate_text::wrap_document(&five, 1000.0);
let mut prev_x = layout.caret_position(1).1; for byte in 2..=6 {
let x = layout.caret_position(byte).1;
ensure!(
x > prev_x,
"caret-x must advance at space byte {byte}: {x} !> {prev_x}"
);
prev_x = x;
}
Ok(())
}
fn check_text_field_shape_line_preserves_spaces() -> Result<(), String> {
let mut text_system = TextSystem::new().expect("create TextSystem");
let font = text_system
.load_font_from_bytes(slate_text::TEST_FONT, 14.0, 1.0)
.expect("load bundled font");
let one = text_system.shape_line(&font, "a b").expect("shape one");
let five = text_system
.shape_line(&font, "a b")
.expect("shape five");
ensure!(
five.width_lpx > one.width_lpx + 1.0,
"shape_line must keep all 5 spaces ({} vs {})",
five.width_lpx,
one.width_lpx
);
Ok(())
}
fn main() {
let cases: &[Case] = &[
("text_area_module_smoke", check_text_area_module_smoke),
(
"three_line_document_wraps_to_three_lines_with_caret_on_line_zero",
check_three_line_document_wraps_to_three_lines_with_caret_on_line_zero,
),
(
"multi_space_run_preserves_every_space_byte",
check_multi_space_run_preserves_every_space_byte,
),
(
"text_field_shape_line_preserves_spaces",
check_text_field_shape_line_preserves_spaces,
),
];
let mut failed = 0;
for (name, f) in cases {
match f() {
Ok(()) => println!("ok - {name}"),
Err(e) => {
eprintln!("FAIL - {name}: {e}");
failed += 1;
}
}
}
if failed > 0 {
eprintln!("\n{failed} case(s) failed");
std::process::exit(1);
}
println!("\nall {} case(s) passed", cases.len());
}