cosmic-text/tests/ellipsize_rendering.rs

199 lines
6.9 KiB
Rust
Raw Normal View History

use std::path::PathBuf;
Ellipsize (#467) * feat: add Ellipsize enum * chore: API changes needed for ellipsize Decided not to change "layout()" function for now to avoid breaking the interface. For now. * chore: shape ellipsis * feat: Ellipsize::Start Since it can only have 1 line, it's easier to implement. * DROPME: temporarily change rich-text for testing * test(ellipsize): Testing Ellipsize::Start Long text in small buffer should produce ellipsis glyphs * fix: do not need font_system anymore We moved ellipsis shaping elsewhere so no need to pass font_system to layout function (which also was recreating a new one in the tests every time making them take forever). * feat: Ellipsize::End * improv(ellipsize): use a single ellipsis shape * improv: Ellipsie::End && Wrap::None There is no need to layout the whole line if it's not going to fit. * fix: mixed bidi text when Ellipsize::End && Wrap::None * chore: clean up and simplify when line.RTL==span.RTL * fix(ellipsize): last word is not (word_count -1) if iter().rev() * refactor(layout): extract the layout algorithm to make it more readable * improv(ellipsize): Ellipsize::Start && Wrap::None we iterate in reverse and only layout what's going to be visible * Revert: delete the previous approach of post processing ellipsis * doc: explain the interaction between Ellipsize and Wrap * chore: lower the scope * feat: Ellipsize the last line of a paragraph For now only the number of lines is supported * fix: clear ellipsized field on visual lines This was causing ellipsis to show on random lines * chore: remove old tests will add better tests soon * chore: clean up changes from previous attempt * fix: consider the ellipsis width when doing alignment * feat(ellipsize): add `Height` limit to `Ellipsize` * fix: ellipsize the start of the last line correctly * fix: ellipsize at the start of mixed bidi lines * feat: Ellipsize::Middle * fix: consider ellipsize::middle when calculating alignment correction * refactor: improve readability * refactor: deduplicate "fit_glyphs" * refactor: combine backward and forward layout into one (wip) * fix: Backward works in the unified layout_spans * chore: clean up * fix: Ellipsize::Middle * fix: handle large words in bidi boundaries * chore: clean up and some refactoring * fix: ellipsis is now the same level as the surrounding text * fix: try to fit more when ellipsizing::middle * improv: ellipsis now have the same level as the neighbors This makes ellipsized RTL text inside a LTR line more readable. before: Hello سلام...خوبی؟ Hello خولی؟...سلام * fix: some extra words were being rendered in Ellipsize::Middle This was causing the last word (if it's not the same level as the rest) to be rendered outside the buffer. * test: a few test cases for ellipsize * fix: assign the correct byte range to ellipsis this should fix the panic when selecting or clicking on or near the ellipsis in the editor.
2026-02-19 09:11:22 -07:00
use common::DrawTestCfg;
use cosmic_text::{
fontdb::Database, Align, Attrs, Buffer, Ellipsize, EllipsizeHeightLimit, Family, FontSystem,
Metrics, Shaping, Wrap,
};
Ellipsize (#467) * feat: add Ellipsize enum * chore: API changes needed for ellipsize Decided not to change "layout()" function for now to avoid breaking the interface. For now. * chore: shape ellipsis * feat: Ellipsize::Start Since it can only have 1 line, it's easier to implement. * DROPME: temporarily change rich-text for testing * test(ellipsize): Testing Ellipsize::Start Long text in small buffer should produce ellipsis glyphs * fix: do not need font_system anymore We moved ellipsis shaping elsewhere so no need to pass font_system to layout function (which also was recreating a new one in the tests every time making them take forever). * feat: Ellipsize::End * improv(ellipsize): use a single ellipsis shape * improv: Ellipsie::End && Wrap::None There is no need to layout the whole line if it's not going to fit. * fix: mixed bidi text when Ellipsize::End && Wrap::None * chore: clean up and simplify when line.RTL==span.RTL * fix(ellipsize): last word is not (word_count -1) if iter().rev() * refactor(layout): extract the layout algorithm to make it more readable * improv(ellipsize): Ellipsize::Start && Wrap::None we iterate in reverse and only layout what's going to be visible * Revert: delete the previous approach of post processing ellipsis * doc: explain the interaction between Ellipsize and Wrap * chore: lower the scope * feat: Ellipsize the last line of a paragraph For now only the number of lines is supported * fix: clear ellipsized field on visual lines This was causing ellipsis to show on random lines * chore: remove old tests will add better tests soon * chore: clean up changes from previous attempt * fix: consider the ellipsis width when doing alignment * feat(ellipsize): add `Height` limit to `Ellipsize` * fix: ellipsize the start of the last line correctly * fix: ellipsize at the start of mixed bidi lines * feat: Ellipsize::Middle * fix: consider ellipsize::middle when calculating alignment correction * refactor: improve readability * refactor: deduplicate "fit_glyphs" * refactor: combine backward and forward layout into one (wip) * fix: Backward works in the unified layout_spans * chore: clean up * fix: Ellipsize::Middle * fix: handle large words in bidi boundaries * chore: clean up and some refactoring * fix: ellipsis is now the same level as the surrounding text * fix: try to fit more when ellipsizing::middle * improv: ellipsis now have the same level as the neighbors This makes ellipsized RTL text inside a LTR line more readable. before: Hello سلام...خوبی؟ Hello خولی؟...سلام * fix: some extra words were being rendered in Ellipsize::Middle This was causing the last word (if it's not the same level as the rest) to be rendered outside the buffer. * test: a few test cases for ellipsize * fix: assign the correct byte range to ellipsis this should fix the panic when selecting or clicking on or near the ellipsis in the editor.
2026-02-19 09:11:22 -07:00
mod common;
#[test]
fn test_ellipsize_ltr_end_single_line() {
let attrs = Attrs::new().family(Family::Name("Inter"));
DrawTestCfg::new("ellipsize_ltr_end_single_line")
.font_size(20., 26.)
.font_attrs(attrs)
.text("The quick brown fox jumps over the lazy dog.")
.wrap(Wrap::None)
.ellipsize(Ellipsize::End(EllipsizeHeightLimit::Lines(1)))
.canvas(180, 50)
.validate_text_rendering();
}
#[test]
fn test_ellipsize_ltr_end_single_line_aligned_right() {
let attrs = Attrs::new().family(Family::Name("Inter"));
DrawTestCfg::new("ellipsize_ltr_end_single_line_aligned_right")
.font_size(20., 26.)
.font_attrs(attrs)
.text("The quick brown fox jumps over the lazy dog.")
.wrap(Wrap::None)
.ellipsize(Ellipsize::End(EllipsizeHeightLimit::Lines(1)))
.alignment(Some(Align::Right))
.canvas(180, 50)
.validate_text_rendering();
}
#[test]
fn test_ellipsize_rtl_end_single_line() {
let attrs = Attrs::new().family(Family::Name("Noto Sans"));
DrawTestCfg::new("ellipsize_rtl_end_single_line")
.font_size(22., 28.)
.font_attrs(attrs)
.text("توانا بود هرکه دانا بود.")
.wrap(Wrap::None)
.ellipsize(Ellipsize::End(EllipsizeHeightLimit::Lines(1)))
.canvas(180, 55)
.validate_text_rendering();
}
#[test]
fn test_ellipsize_mixed_end_single_line() {
let attrs = Attrs::new().family(Family::Name("Noto Sans"));
DrawTestCfg::new("ellipsize_mixed_end_single_line")
.font_size(20., 26.)
.font_attrs(attrs)
.text("Hello سلام mixed RTL/LTR world with extra words")
.wrap(Wrap::None)
.ellipsize(Ellipsize::End(EllipsizeHeightLimit::Lines(1)))
.canvas(190, 50)
.validate_text_rendering();
}
#[test]
fn test_ellipsize_ltr_start_single_line() {
let attrs = Attrs::new().family(Family::Name("Inter"));
DrawTestCfg::new("ellipsize_ltr_start_single_line")
.font_size(20., 26.)
.font_attrs(attrs)
.text("The quick brown fox jumps over the lazy dog.")
.wrap(Wrap::None)
.ellipsize(Ellipsize::Start(EllipsizeHeightLimit::Lines(1)))
.canvas(180, 50)
.validate_text_rendering();
}
#[test]
fn test_ellipsize_ltr_middle_single_line() {
let attrs = Attrs::new().family(Family::Name("Inter"));
DrawTestCfg::new("ellipsize_ltr_middle_single_line")
.font_size(20., 26.)
.font_attrs(attrs)
.text("The quick brown fox jumps over the lazy dog.")
.wrap(Wrap::None)
.ellipsize(Ellipsize::Middle(EllipsizeHeightLimit::Lines(1)))
.canvas(180, 50)
.validate_text_rendering();
}
#[test]
fn test_ellipsize_ltr_end_two_lines() {
let attrs = Attrs::new().family(Family::Name("Inter"));
DrawTestCfg::new("ellipsize_ltr_end_two_lines")
.font_size(18., 24.)
.font_attrs(attrs)
.text("Pack my box with five dozen liquor jugs. Sphinx of black quartz, judge my vow.")
.wrap(Wrap::Word)
.ellipsize(Ellipsize::End(EllipsizeHeightLimit::Lines(2)))
.canvas(200, 80)
.validate_text_rendering();
}
#[test]
fn test_ellipsize_mixed_middle_single_line() {
let attrs = Attrs::new().family(Family::Name("Inter"));
DrawTestCfg::new("ellipsize_mixed_middle_single_line")
.font_size(20., 26.)
.font_attrs(attrs)
.text("Hello سلام mixed RTL/LTR world with extra words")
.wrap(Wrap::None)
.ellipsize(Ellipsize::Middle(EllipsizeHeightLimit::Lines(1)))
.canvas(180, 50)
.validate_text_rendering();
}
#[test]
fn test_ellipsize_mixed_ltr_rtl_middle_two_lines() {
let attrs = Attrs::new().family(Family::Name("Inter"));
DrawTestCfg::new("ellipsize_mixed_ltr_rtl_middle_two_lines")
.font_size(20., 26.)
.font_attrs(attrs)
.text("First line is LTR خط دوم از راست به چپ")
.wrap(Wrap::WordOrGlyph)
.ellipsize(Ellipsize::Middle(EllipsizeHeightLimit::Lines(2)))
.canvas(180, 80)
.validate_text_rendering();
}
#[test]
fn test_ellipsize_mixed_rtl_ltr_middle_two_lines() {
let attrs = Attrs::new().family(Family::Name("Inter"));
DrawTestCfg::new("ellipsize_mixed_rtl_ltr_middle_two_lines")
.font_size(20., 26.)
.font_attrs(attrs)
.text("خط اول از راست به چپ Second line is LTR and has more words")
.wrap(Wrap::WordOrGlyph)
.ellipsize(Ellipsize::Middle(EllipsizeHeightLimit::Lines(2)))
.canvas(210, 80)
.validate_text_rendering();
}
#[test]
fn test_ellipsize_ltr_single_word_middle_two_lines() {
let attrs = Attrs::new().family(Family::Name("Inter"));
DrawTestCfg::new("ellipsize_ltr_single_word_middle_two_lines")
.font_size(20., 26.)
.font_attrs(attrs)
.text("AVeryLongWordThatExceedsTheWidth")
.wrap(Wrap::WordOrGlyph)
.ellipsize(Ellipsize::Middle(EllipsizeHeightLimit::Lines(2)))
.canvas(180, 80)
.validate_text_rendering();
}
#[test]
fn test_ellipsize_mixed_ltr_rtl_ltr_middle_three_lines() {
let attrs = Attrs::new().family(Family::Name("Inter"));
DrawTestCfg::new("ellipsize_mixed_ltr_rtl_ltr_middle_three_lines")
.font_size(20., 26.)
.font_attrs(attrs)
.text("This is some LTR text that keeps و یه مشت متن فارسیی.zippy")
.wrap(Wrap::WordOrGlyph)
.ellipsize(Ellipsize::Middle(EllipsizeHeightLimit::Lines(3)))
.canvas(200, 100)
.validate_text_rendering();
}
/// Regression rendering test: Fluent's fl!() wraps interpolated values with BiDi
/// isolation characters. With the bug, this rendered as "Workspace… 2"
/// After the fix it must render the full "Workspace 2" without ellipsis.
#[test]
fn test_ellipsize_bidi_isolates_middle_bug() {
let attrs = Attrs::new().family(Family::Name("Inter"));
DrawTestCfg::new("ellipsize_bidi_isolates_middle_bug")
.font_size(20., 26.)
.font_attrs(attrs)
.rich_text([(
"\u{2068}Workspace\u{2069}\u{2068} \u{2069}\u{2068}2\u{2069}",
Attrs::new().family(Family::Name("Inter")),
)])
.wrap(Wrap::WordOrGlyph)
.ellipsize(Ellipsize::Middle(EllipsizeHeightLimit::Lines(1)))
.canvas(220, 50)
.validate_text_rendering();
}
#[test]
fn test_ellipsize_ltr_middle_small_buffer() {
let attrs = Attrs::new().family(Family::Name("Inter"));
DrawTestCfg::new("ellipsize_ltr_middle_small_buffer")
.font_size(20., 26.)
.font_attrs(attrs)
.text("A/B Tester x8 Mono")
.wrap(Wrap::None)
.ellipsize(Ellipsize::Middle(EllipsizeHeightLimit::Lines(1)))
.canvas(30, 100)
.validate_text_rendering();
}