Skip to content

Commit

Permalink
use Buffer::set_rich_text from cosmic_text instead of own impl
Browse files Browse the repository at this point in the history
  • Loading branch information
TotalKrill committed Nov 12, 2023
1 parent be71200 commit eac113a
Showing 1 changed file with 28 additions and 128 deletions.
156 changes: 28 additions & 128 deletions crates/bevy_text/src/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use bevy_utils::{
HashMap,
};

use cosmic_text::{Attrs, AttrsList, Buffer, BufferLine, Family, Metrics, Wrap};
use cosmic_text::{Attrs, Buffer, Metrics, Shaping, Wrap};

use crate::{
error::TextError, BreakLineOn, Font, FontAtlasSets, FontAtlasWarning, PositionedGlyph,
Expand All @@ -26,7 +26,6 @@ use crate::{
// TODO: (future work) split text entities into section entities
// TODO: (future work) text editing
// TODO: font validation

// TODO: the only reason we need a mutex is due to TextMeasure
// - is there a way to do this without it?
/// A wrapper around a [`cosmic_text::FontSystem`]
Expand Down Expand Up @@ -104,117 +103,27 @@ impl TextPipeline {
let (font_size, line_height) = (font_size as f32, line_height as f32);
let metrics = Metrics::new(font_size, line_height);

let font_system = &mut acquire_font_system(&mut self.font_system)?;
let mut font_system = &mut acquire_font_system(&mut self.font_system)?;

// TODO: cache buffers (see Iced / glyphon)
let mut buffer = Buffer::new(font_system, metrics);

buffer.lines.clear();
let mut attrs_list = AttrsList::new(Attrs::new());
let mut line_string = String::new();
// all sections need to be combined and broken up into lines
// e.g.
// style0"Lorem ipsum\ndolor sit amet,"
// style1" consectetur adipiscing\nelit,"
// style2" sed do eiusmod tempor\nincididunt"
// style3" ut labore et dolore\nmagna aliqua."
// becomes:
// line0: style0"Lorem ipsum"
// line1: style0"dolor sit amet,"
// style1" consectetur adipiscing,"
// line2: style1"elit,"
// style2" sed do eiusmod tempor"
// line3: style2"incididunt"
// style3"ut labore et dolore"
// line4: style3"magna aliqua."

// combine all sections into a string
// as well as metadata that links those sections to that string
let mut end = 0;
let (string, sections_data): (String, Vec<_>) = sections
let spans: Vec<(&str, Attrs)> = sections
.iter()
.enumerate()
.map(|(section_index, section)| {
let start = end;
end += section.value.len();
(section.value.as_str(), (section, section_index, start..end))
(
&section.value[..],
get_attrs(
&section,
section_index,
&mut font_system,
&mut self.map_handle_to_font_id,
fonts,
),
)
})
.unzip();

let mut sections_iter = sections_data.into_iter();
let mut maybe_section = sections_iter.next();

// split the string into lines, as ranges
let string_start = string.as_ptr() as usize;
let mut lines_iter = BidiParagraphs::new(&string).map(|line: &str| {
let start = line.as_ptr() as usize - string_start;
let end = start + line.len();
start..end
});
let mut maybe_line = lines_iter.next();

loop {
let (Some(line_range), Some((section, section_index, section_range))) =
(&maybe_line, &maybe_section)
else {
// this is reached only if this text is empty
break;
};

// start..end is the intersection of this line and this section
let start = line_range.start.max(section_range.start);
let end = line_range.end.min(section_range.end);
if start < end {
let text = &string[start..end];
add_span(
&mut line_string,
&mut attrs_list,
section,
*section_index,
text,
font_system,
&mut self.map_handle_to_font_id,
fonts,
);
}
.collect();

// we know that at the end of a line,
// section text's end index is always >= line text's end index
// so if this section ends before this line ends,
// there is another section in this line.
// otherwise, we move on to the next line.
if section_range.end < line_range.end {
maybe_section = sections_iter.next();
} else {
maybe_line = lines_iter.next();
if maybe_line.is_some() {
// finalize this line and start a new line
let prev_attrs_list =
std::mem::replace(&mut attrs_list, AttrsList::new(Attrs::new()));
let prev_line_string = std::mem::take(&mut line_string);
//TODO: is basic the correct way?
buffer.lines.push(BufferLine::new(
prev_line_string,
prev_attrs_list,
cosmic_text::Shaping::Basic,
));
} else {
// finalize the final line
//TODO: is basic the correct way?
buffer.lines.push(BufferLine::new(
line_string,
attrs_list,
cosmic_text::Shaping::Basic,
));
break;
}
}
}

// node size (bounds) is already scaled by the systems that call queue_text
// TODO: cosmic text does not shape/layout text outside the buffer height
// consider a better way to do this
// let buffer_height = bounds.y;
// TODO: cache buffers (see Iced / glyphon)
let mut buffer = Buffer::new_empty(metrics);
let buffer_height = f32::INFINITY;
buffer.set_size(font_system, bounds.x.ceil(), buffer_height);

Expand All @@ -228,7 +137,7 @@ impl TextPipeline {
);

// TODO: other shaping methods?
buffer.shape_until_scroll(font_system);
buffer.set_rich_text(font_system, spans, Shaping::Basic);

if buffer.visible_lines() == 0 {
// Presumably the font(s) are not available yet
Expand Down Expand Up @@ -445,8 +354,8 @@ pub struct TextMeasureInfo {
impl std::fmt::Debug for TextMeasureInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TextMeasureInfo")
.field("min_width_content_size", &self.min)
.field("max_width_content_size", &self.max)
.field("min", &self.min)
.field("max", &self.max)
.field("buffer", &"_")
.field("font_system", &"_")
.finish()
Expand All @@ -462,24 +371,15 @@ impl TextMeasureInfo {
}
}

/// For the current line,
/// adds a span to the attributes list and pushes the text into the line string,
/// loading fonts into the [`Database`](cosmic_text::fontdb::Database) if required.
#[allow(clippy::too_many_arguments)]
fn add_span(
line_string: &mut String,
attrs_list: &mut AttrsList,
section: &TextSection,
// /// get attr for from textstyle
// /// loading fonts into the [`Database`](cosmic_text::fontdb::Database) if required.
fn get_attrs<'a>(
section: &'a TextSection,
section_index: usize,
text: &str,
font_system: &mut cosmic_text::FontSystem,
map_handle_to_font_id: &mut HashMap<AssetId<Font>, cosmic_text::fontdb::ID>,
fonts: &Assets<Font>,
) {
let start = line_string.len();
line_string.push_str(text);
let end = line_string.len();

) -> Attrs<'a> {
let font_handle = section.style.font.clone();
let font_handle_id = font_handle.id();
let face_id = map_handle_to_font_id
Expand All @@ -505,17 +405,17 @@ fn add_span(
let face = font_system.db().face(*face_id).unwrap();

// TODO: validate this is the correct string to extract
let family_name = &face.families[0].0;
// let family_name = &face.families[0].0;
let attrs = Attrs::new()
// TODO: validate that we can use metadata
.metadata(section_index)
.family(Family::Name(family_name))
// TODO: this reference, becomes owned by the font system, which is not really wanted...
// .family(Family::Name(family_name))
.stretch(face.stretch)
.style(face.style)
.weight(face.weight)
.color(cosmic_text::Color(section.style.color.as_linear_rgba_u32()));

attrs_list.add_span(start..end, attrs);
attrs
}

/// Calculate the size of the text area for the given buffer.
Expand Down

0 comments on commit eac113a

Please sign in to comment.