diff --git a/sugarloaf/src/components/rich_text/mod.rs b/sugarloaf/src/components/rich_text/mod.rs index 628dff2633..155201eac1 100644 --- a/sugarloaf/src/components/rich_text/mod.rs +++ b/sugarloaf/src/components/rich_text/mod.rs @@ -322,7 +322,7 @@ impl RichTextBrush { let dimensions = fetch_dimensions( &mut self.comp, - &state.compositors.advanced.render_data_sugar, + &state.compositors.advanced.mocked_render_data, state.compositors.advanced.font_library(), ); if dimensions.0 > 0. && dimensions.1 > 0. { @@ -704,7 +704,7 @@ impl RichTextBrush { #[inline] fn draw_layout( comp: &mut compositor::Compositor, - layout: &crate::layout::Paragraph, + render_data: &crate::layout::RenderData, x: f32, y: f32, font_library: &FontLibrary, @@ -712,7 +712,7 @@ fn draw_layout( ) { let depth = 0.0; let mut glyphs = Vec::new(); - for line in layout.lines() { + for line in render_data.lines() { let mut px = x + line.offset(); for run in line.runs() { let font = run.font(); @@ -763,7 +763,7 @@ fn draw_layout( #[inline] fn fetch_dimensions( comp: &mut compositor::Compositor, - layout: &crate::layout::Paragraph, + render_data: &crate::layout::RenderData, font_library: &FontLibrary, ) -> (f32, f32) { let x = 0.; @@ -771,12 +771,13 @@ fn fetch_dimensions( let mut glyphs = Vec::with_capacity(3); let mut sugarwidth = 0.0; let mut sugarheight = 0.0; - for line in layout.lines() { + for line in render_data.lines() { let mut px = x + line.offset(); for run in line.runs() { let font = run.font(); let py = line.baseline() + y; let run_x = px; + let line_height = line.ascent() + line.descent() + line.leading(); glyphs.clear(); for cluster in run.visual_clusters() { for glyph in cluster.glyphs() { @@ -797,13 +798,13 @@ fn fetch_dimensions( background_color: None, baseline: py, topline: py - line.ascent(), - line_height: line.ascent() + line.descent(), + line_height, advance: px - run_x, underline: None, }; sugarwidth = style.advance; - sugarheight = style.line_height; + sugarheight = line_height; comp.draw_glyphs( Rect::new(run_x, py, style.advance, 1.), 0.0, diff --git a/sugarloaf/src/layout/builder.rs b/sugarloaf/src/layout/builder.rs index 0f2de5f48b..9d0e7648ed 100644 --- a/sugarloaf/src/layout/builder.rs +++ b/sugarloaf/src/layout/builder.rs @@ -6,14 +6,14 @@ // builder.rs was originally retired from dfrg/swash_demo licensed under MIT // https://github.com/dfrg/swash_demo/blob/master/LICENSE -//! Paragraph builder. +//! Render data builder. use super::bidi::*; use super::builder_data::*; use super::span_style::*; -use super::Paragraph; use super::{SpanId, MAX_ID}; use crate::font::{FontContext, FontLibrary}; +use crate::layout::render_data::RenderData; use core::borrow::Borrow; use swash::shape::{self, ShapeContext}; use swash::text::cluster::{CharCluster, CharInfo, Parser, Token}; @@ -120,23 +120,32 @@ impl<'a> ParagraphBuilder<'a> { } } + #[inline] + pub fn new_line(&mut self) { + self.s.new_line(); + } + /// Adds a text fragment to the paragraph. pub fn add_text(&mut self, text: &str) -> Option<()> { - let id = self.s.fragments.len() as u32; + let current_line = self.s.current_line(); + let line = &mut self.s.lines[current_line]; + let id = line.text.frags.len(); if id > MAX_ID { return None; } let span_id = *self.s.span_stack.last()?; let span = self.s.spans.get(span_id.to_usize())?; let mut offset = self.last_offset; + macro_rules! push_char { ($ch: expr) => {{ - self.s.text.push($ch); - self.s.text_offsets.push(offset); + line.text.content.push($ch); + line.text.offsets.push(offset); offset += ($ch).len_utf8() as u32; }}; } - let start = self.s.text.len(); + + let start = line.text.content.len(); match span.text_transform { TextTransform::Uppercase => { if let Some(lang) = &span.lang { @@ -237,8 +246,8 @@ impl<'a> ParagraphBuilder<'a> { } } } - let end = self.s.text.len(); - let break_shaping = if let Some(prev_frag) = self.s.fragments.last() { + let end = line.text.content.len(); + let break_shaping = if let Some(prev_frag) = line.fragments.last() { if prev_frag.is_text { if prev_frag.span == span_id { false @@ -258,15 +267,15 @@ impl<'a> ParagraphBuilder<'a> { true }; let len = end - start; - self.s.text_frags.reserve(len); + line.text.frags.reserve(len); for _ in 0..len { - self.s.text_frags.push(id); + line.text.frags.push(id as u32); } - self.s.text_spans.reserve(len); + line.text.spans.reserve(len); for _ in 0..len { - self.s.text_spans.push(span_id.0); + line.text.spans.push(span_id.0 as u32); } - self.s.fragments.push(FragmentData { + line.fragments.push(FragmentData { span: span_id, is_text: true, break_shaping, @@ -281,68 +290,80 @@ impl<'a> ParagraphBuilder<'a> { } /// Consumes the builder and fills the specified paragraph with the result. - pub fn build_into(mut self, para: &mut Paragraph) { - self.resolve(para); - para.finish(); + pub fn build_into(mut self, render_data: &mut RenderData) { + self.resolve(render_data); + render_data.finish(); // self.fcx.reset_group_state(); } /// Consumes the builder and returns the resulting paragraph. - pub fn build(self) -> Paragraph { - let mut para = Paragraph::default(); - self.build_into(&mut para); - para + pub fn build(self) -> RenderData { + let mut render_data = RenderData::default(); + self.build_into(&mut render_data); + render_data } } impl<'a> ParagraphBuilder<'a> { - fn resolve(&mut self, layout: &mut Paragraph) { + fn resolve(&mut self, render_data: &mut RenderData) { // Bit of a hack: add a single trailing space fragment to account for // empty paragraphs and to force an extra break if the paragraph ends // in a newline. - self.s - .span_stack - .push(SpanId(self.s.spans.len() as u32 - 1)); + + self.s.span_stack.push(SpanId(self.s.spans.len() - 1)); self.add_text(" "); for _ in 0..self.dir_depth { const PDI: char = '\u{2069}'; self.push_char(PDI); } - let mut analysis = analyze(self.s.text.iter()); - for (props, boundary) in analysis.by_ref() { - self.s.text_info.push(CharInfo::new(props, boundary)); - } - if analysis.needs_bidi_resolution() || self.dir != Direction::LeftToRight { - let dir = match self.dir { - Direction::Auto => None, - Direction::LeftToRight => Some(BidiDirection::LeftToRight), - Direction::RightToLeft => Some(BidiDirection::RightToLeft), - }; - self.bidi.resolve_with_types( - &self.s.text, - self.s.text_info.iter().map(|i| i.bidi_class()), - dir, - ); - self.needs_bidi = true; + + // for (line_number, line) in self.s.lines.iter_mut().enumerate() { + for line_number in 0..self.s.lines.len() { + let line = &mut self.s.lines[line_number]; + let mut analysis = analyze(line.text.content.iter()); + for (props, boundary) in analysis.by_ref() { + line.text.info.push(CharInfo::new(props, boundary)); + } + if analysis.needs_bidi_resolution() || self.dir != Direction::LeftToRight { + let dir = match self.dir { + Direction::Auto => None, + Direction::LeftToRight => Some(BidiDirection::LeftToRight), + Direction::RightToLeft => Some(BidiDirection::RightToLeft), + }; + self.bidi.resolve_with_types( + &self.s.lines[line_number].text.content, + self.s.lines[line_number] + .text + .info + .iter() + .map(|i| i.bidi_class()), + dir, + ); + if !self.needs_bidi { + self.needs_bidi = true; + } + } + + self.itemize(line_number); + self.shape(render_data, line_number); } - self.itemize(); - self.shape(layout); } - fn itemize(&mut self) { - let limit = self.s.text.len(); - if self.s.fragments.is_empty() || limit == 0 { + fn itemize(&mut self, line_number: usize) { + let line = &mut self.s.lines[line_number]; + let limit = line.text.content.len(); + if line.text.frags.is_empty() || limit == 0 { return; } - let mut last_script = self - .s - .text_info + let mut last_script = line + .text + .info .iter() .map(|i| i.script()) .find(|s| real_script(*s)) .unwrap_or(Script::Latin); let levels = self.bidi.levels(); - let mut last_frag = self.s.fragments.first().unwrap(); + let mut last_frag = line.fragments.first().unwrap(); let mut last_level = if self.needs_bidi { levels[last_frag.start] } else { @@ -365,13 +386,13 @@ impl<'a> ParagraphBuilder<'a> { item.level = last_level; item.vars = last_vars; item.features = last_features; - self.s.items.push(item); + line.items.push(item); item.start = item.end; } }; } if self.needs_bidi { - for frag in &self.s.fragments { + for frag in &line.fragments { if frag.break_shaping || frag.start != last_frag.end { push_item!(); item.start = frag.start; @@ -382,7 +403,7 @@ impl<'a> ParagraphBuilder<'a> { last_vars = frag.vars; let range = frag.start..frag.end; for (&props, &level) in - self.s.text_info[range.clone()].iter().zip(&levels[range]) + line.text.info[range.clone()].iter().zip(&levels[range]) { let script = props.script(); let real = real_script(script); @@ -398,7 +419,7 @@ impl<'a> ParagraphBuilder<'a> { } } } else { - for frag in &self.s.fragments { + for frag in &line.fragments { if frag.break_shaping || frag.start != last_frag.end { push_item!(); item.start = frag.start; @@ -408,7 +429,7 @@ impl<'a> ParagraphBuilder<'a> { last_features = frag.features; last_vars = frag.vars; let range = frag.start..frag.end; - for &props in &self.s.text_info[range] { + for &props in &line.text.info[range] { let script = props.script(); let real = real_script(script); if script != last_script && real { @@ -426,10 +447,11 @@ impl<'a> ParagraphBuilder<'a> { push_item!(); } - fn shape(&mut self, layout: &mut Paragraph) { + fn shape(&mut self, render_data: &mut RenderData, line_number: usize) { // let start = std::time::Instant::now(); let mut char_cluster = CharCluster::new(); - for item in &self.s.items { + let line = &self.s.lines[line_number]; + for item in &line.items { shape_item( self.fcx, self.fonts, @@ -437,10 +459,11 @@ impl<'a> ParagraphBuilder<'a> { self.s, item, &mut char_cluster, - layout, + render_data, + line_number, ); } - layout.apply_spacing(&self.s.spans); + render_data.apply_spacing(&self.s.spans); // let duration = start.elapsed(); // println!("Time elapsed in shape is: {:?}", duration); } @@ -449,10 +472,11 @@ impl<'a> ParagraphBuilder<'a> { impl<'a> ParagraphBuilder<'a> { #[inline] fn push_char(&mut self, ch: char) { - self.s.text.push(ch); - self.s.text_frags.push(0); - self.s.text_spans.push(0); - self.s.text_offsets.push(0); + let current_line = self.s.current_line(); + self.s.lines[current_line].text.content.push(ch); + self.s.lines[current_line].text.frags.push(0); + self.s.lines[current_line].text.spans.push(0); + self.s.lines[current_line].text.offsets.push(0); } } @@ -475,6 +499,7 @@ struct ShapeState<'a> { } #[inline] +#[allow(clippy::too_many_arguments)] fn shape_item( fcx: &mut FontContext, fonts: &FontLibrary, @@ -482,7 +507,8 @@ fn shape_item( state: &BuilderState, item: &ItemData, cluster: &mut CharCluster, - layout: &mut Paragraph, + render_data: &mut RenderData, + current_line: usize, ) -> Option<()> { let dir = if item.level & 1 != 0 { shape::Direction::RightToLeft @@ -490,7 +516,7 @@ fn shape_item( shape::Direction::LeftToRight }; let range = item.start..item.end; - let span_index = state.text_spans[item.start]; + let span_index = state.lines[current_line].text.spans[item.start]; let span = state.spans.get(span_index as usize)?; let features = state.features.get(item.features); let vars = state.vars.get(item.vars); @@ -509,11 +535,11 @@ fn shape_item( // fcx.select_group(shape_state.font_id); // fcx.select_fallbacks(item.script, shape_state.span.lang.as_ref()); if item.level & 1 != 0 { - let chars = state.text[range.clone()] + let chars = state.lines[current_line].text.content[range.clone()] .iter() - .zip(&state.text_offsets[range.clone()]) - .zip(&state.text_spans[range.clone()]) - .zip(&state.text_info[range]) + .zip(&state.lines[current_line].text.offsets[range.clone()]) + .zip(&state.lines[current_line].text.spans[range.clone()]) + .zip(&state.lines[current_line].text.info[range]) .map(|z| { use swash::text::Codepoint; let (((&ch, &offset), &span_index), &info) = z; @@ -541,14 +567,15 @@ fn shape_item( &mut parser, cluster, dir, - layout, + render_data, + current_line, ) {} } else { - let chars = state.text[range.clone()] + let chars = state.lines[current_line].text.content[range.clone()] .iter() - .zip(&state.text_offsets[range.clone()]) - .zip(&state.text_spans[range.clone()]) - .zip(&state.text_info[range]) + .zip(&state.lines[current_line].text.offsets[range.clone()]) + .zip(&state.lines[current_line].text.spans[range.clone()]) + .zip(&state.lines[current_line].text.info[range]) .map(|z| { let (((&ch, &offset), &span_index), &info) = z; Token { @@ -574,7 +601,8 @@ fn shape_item( &mut parser, cluster, dir, - layout, + render_data, + current_line, ) {} } Some(()) @@ -590,7 +618,8 @@ fn shape_clusters( parser: &mut Parser, cluster: &mut CharCluster, dir: shape::Direction, - layout: &mut Paragraph, + render_data: &mut RenderData, + current_line: usize, ) -> bool where I: Iterator + Clone, @@ -615,13 +644,17 @@ where loop { shaper.add_cluster(cluster); if !parser.next(cluster) { - layout.push_run( + // let start = std::time::Instant::now(); + render_data.push_run( &state.state.spans, ¤t_font_id, state.size, state.level, + current_line as u32, shaper, ); + // let duration = start.elapsed(); + // println!("Time elapsed in push_run is: {:?}", duration); return false; } let cluster_span = cluster.user_data(); @@ -637,13 +670,17 @@ where let next_font = fcx.map_cluster(cluster, &mut synth, fonts); if next_font != state.font_id || synth != state.synth { - layout.push_run( + // let start = std::time::Instant::now(); + render_data.push_run( &state.state.spans, ¤t_font_id, state.size, state.level, + current_line as u32, shaper, ); + // let duration = start.elapsed(); + // println!("Time elapsed in push_run is: {:?}", duration); state.font_id = next_font; state.synth = synth; return true; diff --git a/sugarloaf/src/layout/builder_data.rs b/sugarloaf/src/layout/builder_data.rs index 544d955cf2..9d135eb3ff 100644 --- a/sugarloaf/src/layout/builder_data.rs +++ b/sugarloaf/src/layout/builder_data.rs @@ -107,19 +107,35 @@ pub struct SpanData { pub cursor: SugarCursor, } -/// Builder state. +/// Builder Line State #[derive(Default)] -pub struct BuilderState { +pub struct BuilderLineText { /// Combined text. - pub text: Vec, + pub content: Vec, /// Fragment index per character. - pub text_frags: Vec, + pub frags: Vec, /// Span index per character. - pub text_spans: Vec, + pub spans: Vec, /// Character info per character. - pub text_info: Vec, + pub info: Vec, /// Offset of each character relative to its fragment. - pub text_offsets: Vec, + pub offsets: Vec, +} + +#[derive(Default)] +pub struct BuilderLine { + pub text: BuilderLineText, + /// Collection of fragments. + pub fragments: Vec, + /// Collection of items. + pub items: Vec, +} + +/// Builder state. +#[derive(Default)] +pub struct BuilderState { + /// Lines State + pub lines: Vec, /// Collection of all spans, in order of span identifier. pub spans: Vec, /// Stack of spans. @@ -128,10 +144,6 @@ pub struct BuilderState { pub features: FontSettingCache, /// Font variation setting cache. pub vars: FontSettingCache, - /// Collection of fragments. - pub fragments: Vec, - /// Collection of items. - pub items: Vec, /// User specified scale. pub scale: f32, } @@ -139,23 +151,39 @@ pub struct BuilderState { impl BuilderState { /// Creates a new layout state. pub fn new() -> Self { - Self::default() + Self { + lines: vec![BuilderLine::default()], + ..BuilderState::default() + } + } + #[inline] + pub fn new_line(&mut self) { + self.lines.push(BuilderLine::default()); + } + #[inline] + pub fn current_line(&self) -> usize { + let size = self.lines.len(); + if size == 0 { + 0 + } else { + size - 1 + } } pub fn clear(&mut self) { - self.text.clear(); - self.text_frags.clear(); - self.text_spans.clear(); - self.text_info.clear(); - self.text_offsets.clear(); + self.lines.clear(); + // self.text.clear(); + // self.text_frags.clear(); + // self.text_spans.clear(); + // self.text_info.clear(); + // self.text_offsets.clear(); self.spans.clear(); self.span_stack.clear(); self.features.clear(); self.vars.clear(); - self.fragments.clear(); - self.items.clear(); } pub fn begin(&mut self, dir: Direction, lang: Option, scale: f32) { + self.lines.push(BuilderLine::default()); self.spans.push(SpanData { id: SpanId(0), parent: None, @@ -197,7 +225,7 @@ impl BuilderState { I: IntoIterator, I::Item: Borrow>, { - let next_id = SpanId(self.spans.len() as u32); + let next_id = SpanId(self.spans.len()); if next_id.0 > MAX_ID { return None; } diff --git a/sugarloaf/src/content/mod.rs b/sugarloaf/src/layout/content.rs similarity index 89% rename from sugarloaf/src/content/mod.rs rename to sugarloaf/src/layout/content.rs index 8e70019253..3c7cf74786 100644 --- a/sugarloaf/src/content/mod.rs +++ b/sugarloaf/src/layout/content.rs @@ -10,12 +10,12 @@ use crate::layout::*; use core::borrow::Borrow; use core::ops::Range; -#[derive(Clone, Default)] +#[derive(Default, Clone)] pub struct Content { pub spans: Vec, pub fragments: Vec<(u32, u32)>, pub text: String, - pub roots: Vec, + pub roots: Vec, } impl PartialEq for Content { @@ -30,6 +30,8 @@ impl Content { } pub fn layout(&self, lcx: &mut ParagraphBuilder) { + // println!("{:?}", self.roots); + // for root in 0..self.line { for root in &self.roots { self.layout_span(*root, lcx); } @@ -114,8 +116,8 @@ impl Content { None } - fn layout_span(&self, span: u32, lcx: &mut ParagraphBuilder) { - let span = &self.spans[span as usize]; + fn layout_span(&self, span: usize, lcx: &mut ParagraphBuilder) { + let span = &self.spans[span]; lcx.push_span(&span.properties); for e in &span.elements { match e { @@ -128,6 +130,9 @@ impl Content { } } } + SpanElement::BreakLine => { + lcx.new_line(); + } } } lcx.pop_span(); @@ -164,7 +169,8 @@ impl Span { #[derive(Copy, PartialEq, Clone)] pub enum SpanElement { Fragment(u32), - Span(u32), + Span(usize), + BreakLine, } #[derive(Default, Clone, PartialEq)] @@ -186,14 +192,15 @@ impl ContentBuilder { .collect(), elements: Vec::new(), }; - let index = self.content.spans.len() as u32; + let size = self.content.spans.len(); + let index = size as u32; self.content.spans.push(span); if let Some(parent) = self.spans.last() { self.content.spans[*parent as usize] .elements - .push(SpanElement::Span(index)); + .push(SpanElement::Span(size)); } else { - self.content.roots.push(index); + self.content.roots.push(size); } self.spans.push(index); index @@ -234,15 +241,16 @@ impl ContentBuilder { #[inline] pub fn break_line(&mut self) { + // Hacky: under the hood it will ignore this "\n" for break_line + // however whenever process styles from span like background color + // will apply the line width based on last char before \n and not + // the remaining space. + self.add_char('\n'); + if let Some(span) = self.spans.last() { - let index = self.content.fragments.len() as u32; - let start = self.content.text.len() as u32; - self.content.text.push('\n'); - let end = self.content.text.len() as u32; - self.content.fragments.push((start, end)); self.content.spans[*span as usize] .elements - .push(SpanElement::Fragment(index)); + .push(SpanElement::BreakLine); } } diff --git a/sugarloaf/src/layout/layout_data.rs b/sugarloaf/src/layout/layout_data.rs index 6f365c33e0..8a954a52af 100644 --- a/sugarloaf/src/layout/layout_data.rs +++ b/sugarloaf/src/layout/layout_data.rs @@ -169,7 +169,7 @@ impl GlyphData { } } -#[derive(Copy, Clone)] +#[derive(Copy, Debug, Clone)] pub struct RunData { pub span: SpanId, pub line: u32, @@ -210,7 +210,7 @@ pub struct LayoutData { /// Glyph runs. pub runs: Vec, /// Last shaped span. - pub last_span: u32, + pub last_span: usize, } impl LayoutData { @@ -235,7 +235,7 @@ pub struct LineData { pub trailing_whitespace: bool, pub explicit_break: bool, pub width: f32, - pub max_advance: f32, + pub max_advance: Option, pub runs: (u32, u32), pub clusters: (u32, u32), } diff --git a/sugarloaf/src/layout/line_breaker.rs b/sugarloaf/src/layout/line_breaker.rs index 410dcc70bb..aa29784cd0 100644 --- a/sugarloaf/src/layout/line_breaker.rs +++ b/sugarloaf/src/layout/line_breaker.rs @@ -37,6 +37,15 @@ impl<'a> BreakLines<'a> { } } + // pub fn from_data(layout: &'a mut LayoutData, lines: &'a mut LineLayoutData) -> Self { + // Self { + // layout, + // lines, + // state: BreakerState::default(), + // prev_state: None, + // } + // } + /// Computes the next line in the paragraph. Returns the advance and size /// (width and height for horizontal layouts) of the line. pub fn break_next( @@ -65,7 +74,7 @@ impl<'a> BreakLines<'a> { self.layout, self.lines, &mut self.state.line, - max_advance, + Some(max_advance), alignment, true, ) { @@ -99,7 +108,7 @@ impl<'a> BreakLines<'a> { self.layout, self.lines, &mut self.state.line, - max_advance, + Some(max_advance), alignment, false, ) { @@ -122,7 +131,7 @@ impl<'a> BreakLines<'a> { self.layout, self.lines, &mut self.state.line, - max_advance, + Some(max_advance), alignment, false, ) { @@ -140,7 +149,7 @@ impl<'a> BreakLines<'a> { self.layout, self.lines, &mut self.state.line, - max_advance, + Some(max_advance), alignment, false, ) { @@ -167,7 +176,7 @@ impl<'a> BreakLines<'a> { self.layout, self.lines, &mut self.state.line, - max_advance, + Some(max_advance), alignment, false, ) { @@ -194,7 +203,7 @@ impl<'a> BreakLines<'a> { self.layout, self.lines, &mut self.state.line, - max_advance, + Some(max_advance), alignment, true, ) { @@ -226,6 +235,61 @@ impl<'a> BreakLines<'a> { self.finish(); } + #[inline] + pub fn break_without_advance_or_alignment(mut self) { + let run_len = self.layout.runs.len(); + + for i in 0..self.layout.runs.len() { + let run = &self.layout.runs[i]; + let mut should_commit_line = false; + // self.state.prev_boundary = None; + + if i == run_len - 1 { + should_commit_line = true; + } else { + // If next run has a different line number then + // try to commit line + let next_run = &self.layout.runs[i + 1]; + if next_run.line != run.line { + should_commit_line = true; + } + } + + // If we would case about max_advance + // let cluster_end = run.clusters.1 as usize; + // while self.state.j < cluster_end { + // let cluster = + // Cluster::new(self.layout, self.layout.clusters[self.state.j]); + // let advance = cluster.advance(); + // self.state.line.x += advance; + // self.state.j += 1; + // } + + self.state.line.runs.1 = i as u32 + 1; + // self.state.line.clusters.1 = self.state.j as u32; + self.state.line.clusters.1 = run.clusters.1; + + if should_commit_line + && commit_line( + self.layout, + self.lines, + &mut self.state.line, + None, + Alignment::Start, + true, + ) + { + self.state.runs = self.lines.runs.len(); + self.state.lines = self.lines.lines.len(); + self.state.line.x = 0.; + // self.state.j += 1; + self.state.line.clusters.1 = run.clusters.1 + 1; + } + } + + self.finish(); + } + /// Consumes the line breaker and finalizes all line computations. pub fn finish(self) { for run in &mut self.lines.runs { @@ -344,17 +408,21 @@ impl<'a> BreakLines<'a> { } else { 0. }; - let extra = line.max_advance - total_advance + trailing_space_advance; - if extra > 0. { - let offset = if line.alignment == Alignment::Middle { - extra * 0.5 - } else { - extra - }; - for cluster in &mut self.lines.clusters[make_range(line.clusters)] { - cluster.1 += offset; + + if let Some(max_advance) = line.max_advance { + let extra = max_advance - total_advance + trailing_space_advance; + if extra > 0. { + let offset = if line.alignment == Alignment::Middle { + extra * 0.5 + } else { + extra + }; + for cluster in &mut self.lines.clusters[make_range(line.clusters)] + { + cluster.1 += offset; + } + line.x = offset; } - line.x = offset; } } if line.explicit_break { @@ -408,11 +476,12 @@ struct BreakerState { prev_boundary: Option, } +#[inline] fn commit_line( layout: &LayoutData, lines: &mut LineLayoutData, state: &mut LineState, - max_advance: f32, + max_advance: Option, alignment: Alignment, explicit: bool, ) -> bool { diff --git a/sugarloaf/src/layout/mod.rs b/sugarloaf/src/layout/mod.rs index eb51f4c8d1..69aeb839f5 100644 --- a/sugarloaf/src/layout/mod.rs +++ b/sugarloaf/src/layout/mod.rs @@ -12,13 +12,15 @@ mod bidi; mod builder; mod builder_data; +mod content; mod layout_data; mod line_breaker; mod nav; mod render_data; mod span_style; -pub use swash; +pub use content::{Content, ContentBuilder}; +pub use render_data::RenderData; #[doc(inline)] pub use swash::text::Language; @@ -34,26 +36,17 @@ pub use nav::{Erase, ExtendTo, Selection}; pub use render_data::{Cluster, Glyph, Line, Run}; pub use span_style::*; -use layout_data::{LayoutData, LineLayoutData}; - -/// Collection of text, organized into lines, runs and clusters. -#[derive(Clone, Default)] -pub struct Paragraph { - data: LayoutData, - line_data: LineLayoutData, -} - /// Largest allowable span or fragment identifier. -const MAX_ID: u32 = i32::MAX as u32; +const MAX_ID: usize = i32::MAX as usize; /// Index of a span in sequential order of submission to a paragraph builder. #[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Default, Debug)] -pub struct SpanId(pub u32); +pub struct SpanId(pub usize); impl SpanId { /// Converts the span identifier to an index. pub fn to_usize(self) -> usize { - self.0 as usize + self.0 } } diff --git a/sugarloaf/src/layout/nav.rs b/sugarloaf/src/layout/nav.rs index e7e5ebe6a9..a70a718f68 100644 --- a/sugarloaf/src/layout/nav.rs +++ b/sugarloaf/src/layout/nav.rs @@ -6,10 +6,9 @@ // Nav.rs was originally retired from dfrg/swash_demo licensed under MIT // https://github.com/dfrg/swash_demo/blob/master/LICENSE -//! Support for navigating a layout. +//! Support for navigating a render_data. -use super::render_data::{make_range, Line}; -use super::Paragraph; +use super::render_data::{make_range, Line, RenderData}; use core::ops::Range; /// Describes the text range for an erase operation. @@ -39,8 +38,8 @@ pub struct Selection { impl Selection { /// Creates a new selection with the focus at the specified point. - pub fn from_point(layout: &Paragraph, x: f32, y: f32) -> Self { - let focus = Node::from_point(layout, x, y); + pub fn from_point(render_data: &RenderData, x: f32, y: f32) -> Self { + let focus = Node::from_point(render_data, x, y); Self { anchor: focus, focus, @@ -49,66 +48,69 @@ impl Selection { } /// Creates a new selection bounding the word at the specified point. - pub fn word_from_point(layout: &Paragraph, x: f32, y: f32) -> Self { - let target = Node::from_point_direct(layout, x, y); - let line_data = &layout.line_data.lines[target.line as usize]; + pub fn word_from_point(render_data: &RenderData, x: f32, y: f32) -> Self { + let target = Node::from_point_direct(render_data, x, y); + let line_data = &render_data.line_data.lines[target.line as usize]; let limit = line_data.clusters.1; let lower_limit = line_data.clusters.0; - let mut logical_index = layout.line_data.visual_to_logical(target.cluster); - if logical_index as usize >= layout.data.clusters.len() { + let mut logical_index = render_data.line_data.visual_to_logical(target.cluster); + if logical_index as usize >= render_data.data.clusters.len() { logical_index -= 1; } let mut anchor_index = logical_index; for i in (lower_limit..=logical_index).rev() { anchor_index = i; - let c = &layout.data.clusters[i as usize]; + let c = &render_data.data.clusters[i as usize]; if c.info.is_boundary() { break; } } let mut focus_index = logical_index; for i in logical_index + 1..limit { - let c = &layout.data.clusters[i as usize]; + let c = &render_data.data.clusters[i as usize]; if c.info.is_boundary() { break; } focus_index = i; } - let anchor_visual = layout.line_data.logical_to_visual(anchor_index); - let focus_visual = layout.line_data.logical_to_visual(focus_index); - let anchor_rtl = layout.line_data.is_rtl(anchor_index); - let focus_rtl = layout.line_data.is_rtl(focus_index); + let anchor_visual = render_data.line_data.logical_to_visual(anchor_index); + let focus_visual = render_data.line_data.logical_to_visual(focus_index); + let anchor_rtl = render_data.line_data.is_rtl(anchor_index); + let focus_rtl = render_data.line_data.is_rtl(focus_index); Self { - anchor: Node::from_visual_cluster(layout, anchor_visual, anchor_rtl), - focus: Node::from_visual_cluster(layout, focus_visual, !focus_rtl), + anchor: Node::from_visual_cluster(render_data, anchor_visual, anchor_rtl), + focus: Node::from_visual_cluster(render_data, focus_visual, !focus_rtl), move_state: None, } } /// Creates a new selection bounding the line at the specified point. - pub fn line_from_point(layout: &Paragraph, x: f32, y: f32) -> Self { - let target = Node::from_point_direct(layout, x, y); + pub fn line_from_point(render_data: &RenderData, x: f32, y: f32) -> Self { + let target = Node::from_point_direct(render_data, x, y); Self::from_focus(target) - .home(layout, false) - .end(layout, true) + .home(render_data, false) + .end(render_data, true) } /// Creates a new selection with a focus nearest to the character with the /// specified byte offset. - pub fn from_offset(layout: &Paragraph, offset: usize) -> Self { - for (i, cluster) in layout.data.clusters.iter().enumerate() { + pub fn from_offset(render_data: &RenderData, offset: usize) -> Self { + for (i, cluster) in render_data.data.clusters.iter().enumerate() { if cluster.offset as usize >= offset { - let prev = i.saturating_sub(1).min(layout.data.clusters.len() - 1) as u32; - let after = offset != 0 && !layout.line_data.is_rtl(prev); - let visual = layout.line_data.logical_to_visual(prev); + let prev = + i.saturating_sub(1).min(render_data.data.clusters.len() - 1) as u32; + let after = offset != 0 && !render_data.line_data.is_rtl(prev); + let visual = render_data.line_data.logical_to_visual(prev); return Self::from_focus(Node::from_visual_cluster( - layout, visual, after, + render_data, + visual, + after, )); } } Self::from_focus(Node::from_visual_cluster( - layout, - layout.data.clusters.len().saturating_sub(1) as u32, + render_data, + render_data.data.clusters.len().saturating_sub(1) as u32, false, )) } @@ -119,39 +121,39 @@ impl Selection { } /// Returns the visual geometry of the focus. - pub fn cursor(&self, layout: &Paragraph) -> ([f32; 2], f32, bool) { + pub fn cursor(&self, render_data: &RenderData) -> ([f32; 2], f32, bool) { let node = - Node::from_visual_cluster(layout, self.focus.cluster, self.focus.after); - let line = Line::new(layout, node.line as usize); + Node::from_visual_cluster(render_data, self.focus.cluster, self.focus.after); + let line = Line::new(render_data, node.line as usize); ([node.edge, above(&line)], line.size(), node.rtl) } /// Returns the current source offset for the focus of the selection. This /// is where text should be inserted. - pub fn offset(&self, layout: &Paragraph) -> usize { - self.focus.text_offset(layout) + pub fn offset(&self, render_data: &RenderData) -> usize { + self.focus.text_offset(render_data) } /// Returns the current source offset for the anchor of the selection. - pub fn anchor_offset(&self, layout: &Paragraph) -> usize { - self.anchor.text_offset(layout) + pub fn anchor_offset(&self, render_data: &RenderData) -> usize { + self.anchor.text_offset(render_data) } /// Returns the source range for the currently selected text. Note that /// the ordering of this range is dependent on the direction in which /// the text was selected. Use [`normalized_range`](Selection::normalized_range) /// for a standard ordering. - pub fn range(&self, layout: &Paragraph) -> Range { - let start = self.anchor.text_offset(layout); - let end = self.focus.text_offset(layout); + pub fn range(&self, render_data: &RenderData) -> Range { + let start = self.anchor.text_offset(render_data); + let end = self.focus.text_offset(render_data); start..end } /// Returns the source range for the currently selected text. This /// function ensures that `start <= end`. - pub fn normalized_range(&self, layout: &Paragraph) -> Range { - let mut start = self.focus.text_offset(layout); - let mut end = self.anchor.text_offset(layout); + pub fn normalized_range(&self, render_data: &RenderData) -> Range { + let mut start = self.focus.text_offset(render_data); + let mut end = self.anchor.text_offset(render_data); if start > end { core::mem::swap(&mut start, &mut end); } @@ -161,14 +163,14 @@ impl Selection { /// Returns the range of text that should be erased based on the /// current selection. This operation implements the action of the /// `delete` key. - pub fn erase(&self, layout: &Paragraph) -> Option { + pub fn erase(&self, render_data: &RenderData) -> Option { if !self.is_collapsed() { - return Some(Erase::Full(self.normalized_range(layout))); + return Some(Erase::Full(self.normalized_range(render_data))); } - let cluster = layout + let cluster = render_data .data .clusters - .get(self.focus.logical_index(layout) as usize)?; + .get(self.focus.logical_index(render_data) as usize)?; let start = cluster.offset as usize; let end = start + cluster.len as usize; Some(Erase::Full(start..end)) @@ -177,19 +179,19 @@ impl Selection { /// Returns the range of text that should be erased based on the current /// selection. This operation implements the action of the `backspace` /// key. - pub fn erase_previous(&self, layout: &Paragraph) -> Option { + pub fn erase_previous(&self, render_data: &RenderData) -> Option { if !self.is_collapsed() { - return Some(Erase::Full(self.normalized_range(layout))); + return Some(Erase::Full(self.normalized_range(render_data))); } - let logical_index = self.focus.logical_index(layout) as usize; + let logical_index = self.focus.logical_index(render_data) as usize; if logical_index == 0 { return None; } let prev_logical = logical_index - 1; - let cluster = layout.data.clusters.get(prev_logical)?; + let cluster = render_data.data.clusters.get(prev_logical)?; let start = cluster.offset as usize; let end = start + cluster.len as usize; - let emoji = layout.data.clusters.get(prev_logical)?.info.is_emoji(); + let emoji = render_data.data.clusters.get(prev_logical)?.info.is_emoji(); Some(if emoji { Erase::Full(start..end) } else { @@ -198,35 +200,43 @@ impl Selection { } /// Returns a new selection, extending self to the specified point. - pub fn extend_to(&self, layout: &Paragraph, x: f32, y: f32, to: ExtendTo) -> Self { + pub fn extend_to( + &self, + render_data: &RenderData, + x: f32, + y: f32, + to: ExtendTo, + ) -> Self { match to { - ExtendTo::Point => self.extend(layout, Node::from_point(layout, x, y)), + ExtendTo::Point => { + self.extend(render_data, Node::from_point(render_data, x, y)) + } ExtendTo::Word => { - self.extend_word(layout, Self::word_from_point(layout, x, y)) + self.extend_word(render_data, Self::word_from_point(render_data, x, y)) } ExtendTo::Line => { - self.extend_full(layout, Self::line_from_point(layout, x, y)) + self.extend_full(render_data, Self::line_from_point(render_data, x, y)) } } } /// Returns a new, optionally extended, selection with the focus at /// the next visual character. - pub fn next(&self, layout: &Paragraph, extend: bool) -> Self { + pub fn next(&self, render_data: &RenderData, extend: bool) -> Self { if !extend && !self.is_collapsed() { - return self.collapse(layout, false); + return self.collapse(render_data, false); } let mut index = self.focus.cluster; let mut eol = false; - if let Some(eol_state) = self.focus.eol_state(layout) { + if let Some(eol_state) = self.focus.eol_state(render_data) { index += eol_state; eol = true; } else if self.focus.after { index += 1; } - let focus = Node::from_visual_cluster(layout, index, !eol || extend); + let focus = Node::from_visual_cluster(render_data, index, !eol || extend); if extend { - self.extend(layout, focus) + self.extend(render_data, focus) } else { Self::from_focus(focus) } @@ -234,17 +244,17 @@ impl Selection { /// Returns a new, optionally extended, selection with the focus at /// the previous visual character. - pub fn previous(&self, layout: &Paragraph, extend: bool) -> Self { + pub fn previous(&self, render_data: &RenderData, extend: bool) -> Self { if !extend && !self.is_collapsed() { - return self.collapse(layout, true); + return self.collapse(render_data, true); } let mut index = self.focus.cluster; if !self.focus.after || self.focus.nl { index = index.saturating_sub(1); } - let focus = Node::from_visual_cluster(layout, index, false); + let focus = Node::from_visual_cluster(render_data, index, false); if extend { - self.extend(layout, focus) + self.extend(render_data, focus) } else { Self::from_focus(focus) } @@ -252,34 +262,37 @@ impl Selection { /// Returns a new, optionally extended, selection with the focus at /// the beginning of the current line. - pub fn home(&self, layout: &Paragraph, extend: bool) -> Self { - let baseline = self.cursor(layout).0[1]; + pub fn home(&self, render_data: &RenderData, extend: bool) -> Self { + let baseline = self.cursor(render_data).0[1]; if extend { - self.extend_to(layout, 0., baseline + 0.001, ExtendTo::Point) + self.extend_to(render_data, 0., baseline + 0.001, ExtendTo::Point) } else { - Self::from_point(layout, 0., baseline + 0.001) + Self::from_point(render_data, 0., baseline + 0.001) } } /// Returns a new, optionally extended, selection with the focus at /// the end of the current line. - pub fn end(&self, layout: &Paragraph, extend: bool) -> Self { - let baseline = self.cursor(layout).0[1]; + pub fn end(&self, render_data: &RenderData, extend: bool) -> Self { + let baseline = self.cursor(render_data).0[1]; if extend { - self.extend_to(layout, f32::MAX, baseline + 0.001, ExtendTo::Point) + self.extend_to(render_data, f32::MAX, baseline + 0.001, ExtendTo::Point) } else { - Self::from_point(layout, f32::MAX, baseline + 0.001) + Self::from_point(render_data, f32::MAX, baseline + 0.001) } } /// Returns a new, optionally extended, selection with the focus at /// a position on the next line that matches the state of the current /// selection. - pub fn next_line(&self, layout: &Paragraph, extend: bool) -> Self { + pub fn next_line(&self, render_data: &RenderData, extend: bool) -> Self { let mut move_state = self.move_state; - if let Some(focus) = self.focus.adjacent_line(layout, false, &mut move_state) { + if let Some(focus) = self + .focus + .adjacent_line(render_data, false, &mut move_state) + { let mut res = if extend { - self.extend(layout, focus) + self.extend(render_data, focus) } else { Self::from_focus(focus) }; @@ -293,11 +306,12 @@ impl Selection { /// Returns a new, optionally extended, selection with the focus at /// a position on the previous line that matches the state of the current /// selection. - pub fn previous_line(&self, layout: &Paragraph, extend: bool) -> Self { + pub fn previous_line(&self, render_data: &RenderData, extend: bool) -> Self { let mut move_state = self.move_state; - if let Some(focus) = self.focus.adjacent_line(layout, true, &mut move_state) { + if let Some(focus) = self.focus.adjacent_line(render_data, true, &mut move_state) + { let mut res = if extend { - self.extend(layout, focus) + self.extend(render_data, focus) } else { Self::from_focus(focus) }; @@ -312,26 +326,27 @@ impl Selection { /// the visual state of the selection. pub fn regions_with( &self, - layout: &Paragraph, + render_data: &RenderData, mut f: impl FnMut([f32; 4]), ) -> Option<()> { if self.is_collapsed() { return Some(()); } - let mut start = self.focus.logical_index(layout); - let mut end = self.anchor.logical_index(layout); + let mut start = self.focus.logical_index(render_data); + let mut end = self.anchor.logical_index(render_data); if start > end { core::mem::swap(&mut start, &mut end); } let mut in_region = false; - let start_line = layout.line_data.line_index_for_cluster(start); - let end_line = layout.line_data.line_index_for_cluster(end); + let start_line = render_data.line_data.line_index_for_cluster(start); + let end_line = render_data.line_data.line_index_for_cluster(end); for line_index in start_line..=end_line { - let line = Line::new(layout, line_index); + let line = Line::new(render_data, line_index); let line_data = line.data(); let line_end = line.offset() + line.advance(); let mut rect = [line.offset(), above(&line), 0., line.size()]; - let clusters = &layout.line_data.clusters[make_range(line_data.clusters)]; + let clusters = + &render_data.line_data.clusters[make_range(line_data.clusters)]; for (i, &(logical_index, edge)) in clusters.iter().enumerate() { if logical_index >= start && logical_index < end { let far_edge = clusters.get(i + 1).map(|x| x.1).unwrap_or(line_end); @@ -360,11 +375,11 @@ impl Selection { } impl Selection { - pub fn dump(&self, layout: &Paragraph) { + pub fn dump(&self, render_data: &RenderData) { println!("anchor: {:?}", self.anchor); - println!(" -- logical: {}", self.anchor.logical_index(layout)); + println!(" -- logical: {}", self.anchor.logical_index(render_data)); println!("focus: {:?}", self.focus); - println!(" -- logical: {}", self.focus.logical_index(layout)); + println!(" -- logical: {}", self.focus.logical_index(render_data)); } fn from_focus(focus: Node) -> Self { @@ -375,7 +390,7 @@ impl Selection { } } - fn extend(&self, layout: &Paragraph, focus: Node) -> Self { + fn extend(&self, render_data: &RenderData, focus: Node) -> Self { let mut anchor = self.anchor; if anchor.line < focus.line || (anchor.line == focus.line && anchor.edge < focus.edge) @@ -384,14 +399,14 @@ impl Selection { // 'before' state. if anchor.after { let index = anchor.cluster + 1; - if index as usize <= layout.line_data.clusters.len() { - anchor = Node::from_visual_cluster(layout, index, false); + if index as usize <= render_data.line_data.clusters.len() { + anchor = Node::from_visual_cluster(render_data, index, false); } } } else if anchor.line > focus.line || anchor.edge > focus.edge { // Otherwise, set it to 'after' state. if !anchor.after && anchor.cluster > 0 { - anchor = Node::from_visual_cluster(layout, anchor.cluster - 1, true); + anchor = Node::from_visual_cluster(render_data, anchor.cluster - 1, true); } } Self { @@ -401,12 +416,12 @@ impl Selection { } } - fn extend_word(&self, layout: &Paragraph, other: Selection) -> Self { + fn extend_word(&self, render_data: &RenderData, other: Selection) -> Self { let fudge = if self.anchor.after { -0.01 } else { 0.01 }; let initial_word = Self::word_from_point( - layout, + render_data, self.anchor.edge + fudge, - layout.line_data.lines[self.anchor.line as usize].baseline - 0.001, + render_data.line_data.lines[self.anchor.line as usize].baseline - 0.001, ); let mut anchor = initial_word.anchor; let mut focus = other.focus; @@ -425,14 +440,14 @@ impl Selection { // 'before' state. if anchor.after { let index = anchor.cluster + 1; - if index as usize <= layout.line_data.clusters.len() { - anchor = Node::from_visual_cluster(layout, index, false); + if index as usize <= render_data.line_data.clusters.len() { + anchor = Node::from_visual_cluster(render_data, index, false); } } } else if anchor.line > focus.line || anchor.edge > focus.edge { // Otherwise, set it to 'after' state. if !anchor.after && anchor.cluster > 0 { - anchor = Node::from_visual_cluster(layout, anchor.cluster - 1, true); + anchor = Node::from_visual_cluster(render_data, anchor.cluster - 1, true); } } Self { @@ -442,7 +457,7 @@ impl Selection { } } - fn extend_full(&self, layout: &Paragraph, other: Selection) -> Self { + fn extend_full(&self, render_data: &RenderData, other: Selection) -> Self { let mut anchor = self.anchor; let mut focus = other.focus; if anchor > focus { @@ -460,14 +475,14 @@ impl Selection { // 'before' state. if anchor.after { let index = anchor.cluster + 1; - if index as usize <= layout.line_data.clusters.len() { - anchor = Node::from_visual_cluster(layout, index, false); + if index as usize <= render_data.line_data.clusters.len() { + anchor = Node::from_visual_cluster(render_data, index, false); } } } else if anchor.line > focus.line || anchor.edge > focus.edge { // Otherwise, set it to 'after' state. if !anchor.after && anchor.cluster > 0 { - anchor = Node::from_visual_cluster(layout, anchor.cluster - 1, true); + anchor = Node::from_visual_cluster(render_data, anchor.cluster - 1, true); } } Self { @@ -477,7 +492,7 @@ impl Selection { } } - fn collapse(&self, _layout: &Paragraph, prev: bool) -> Self { + fn collapse(&self, _layout: &RenderData, prev: bool) -> Self { let node = if prev { if self.focus < self.anchor { &self.focus @@ -504,14 +519,14 @@ struct Node { } impl Node { - fn from_point(layout: &Paragraph, mut x: f32, y: f32) -> Self { + fn from_point(render_data: &RenderData, mut x: f32, y: f32) -> Self { let mut this = Self::default(); - let line_count = layout.line_data.lines.len(); + let line_count = render_data.line_data.lines.len(); if line_count == 0 { return this; } let last_line_index = line_count - 1; - for (i, line) in layout.lines().enumerate() { + for (i, line) in render_data.lines().enumerate() { if y <= (line.baseline() + line.descent()) || i == last_line_index { if y > line.baseline() + line.descent() { x = f32::MAX; @@ -519,7 +534,8 @@ impl Node { let line_end = line.offset() + line.advance(); let line_data = line.data(); this.line = i as u32; - let clusters = &layout.line_data.clusters[make_range(line_data.clusters)]; + let clusters = + &render_data.line_data.clusters[make_range(line_data.clusters)]; let mut last_edge = f32::MIN; for (i, &(_, edge)) in clusters.iter().enumerate() { if x >= last_edge { @@ -536,7 +552,7 @@ impl Node { this.edge = far_edge; } this.setup_from_visual( - layout, + render_data, line_data.clusters.0 + i as u32, ); return this; @@ -548,21 +564,24 @@ impl Node { if !line_data.explicit_break { this.after = true; } - this.setup_from_visual(layout, line_data.clusters.1.saturating_sub(1)); + this.setup_from_visual( + render_data, + line_data.clusters.1.saturating_sub(1), + ); return this; } } this } - fn from_point_direct(layout: &Paragraph, mut x: f32, y: f32) -> Self { + fn from_point_direct(render_data: &RenderData, mut x: f32, y: f32) -> Self { let mut this = Self::default(); - let line_count = layout.line_data.lines.len(); + let line_count = render_data.line_data.lines.len(); if line_count == 0 { return this; } let last_line_index = line_count - 1; - for (i, line) in layout.lines().enumerate() { + for (i, line) in render_data.lines().enumerate() { if y <= (line.baseline() + line.descent()) || i == last_line_index { let line_start = line.offset(); let line_end = line_start + line.advance(); @@ -571,7 +590,8 @@ impl Node { x = f32::MAX; } this.line = i as u32; - let clusters = &layout.line_data.clusters[make_range(line_data.clusters)]; + let clusters = + &render_data.line_data.clusters[make_range(line_data.clusters)]; for (i, &(_, edge)) in clusters.iter().enumerate() { if x >= edge || x < line_start { let far_edge = @@ -579,7 +599,7 @@ impl Node { if x < far_edge { this.edge = edge; this.setup_from_visual( - layout, + render_data, line_data.clusters.0 + i as u32, ); return this; @@ -590,15 +610,22 @@ impl Node { if !line_data.explicit_break { this.after = true; } - this.setup_from_visual(layout, line_data.clusters.1.saturating_sub(1)); + this.setup_from_visual( + render_data, + line_data.clusters.1.saturating_sub(1), + ); return this; } } this } - fn from_visual_cluster(layout: &Paragraph, mut index: u32, mut after: bool) -> Self { - let limit = layout.line_data.clusters.len() as u32; + fn from_visual_cluster( + render_data: &RenderData, + mut index: u32, + mut after: bool, + ) -> Self { + let limit = render_data.line_data.clusters.len() as u32; let mut this = Self::default(); if limit == 0 { return this; @@ -607,13 +634,13 @@ impl Node { after = false; index = limit - 1; } - let line_index = layout.line_data.line_index_for_cluster(index); - let line = Line::new(layout, line_index); + let line_index = render_data.line_data.line_index_for_cluster(index); + let line = Line::new(render_data, line_index); this.line = line_index as u32; this.cluster = index; - let logical_index = layout.line_data.visual_to_logical(index); - this.nl = layout.data.clusters[logical_index as usize].is_newline(); - this.rtl = layout.line_data.is_rtl(logical_index); + let logical_index = render_data.line_data.visual_to_logical(index); + this.nl = render_data.data.clusters[logical_index as usize].is_newline(); + this.rtl = render_data.line_data.is_rtl(logical_index); if after { index += 1; } @@ -622,7 +649,7 @@ impl Node { this.line += 1; last_cluster = (last_cluster + 1).min(limit); } - let line_clusters = &layout.line_data.clusters[0..last_cluster as usize]; + let line_clusters = &render_data.line_data.clusters[0..last_cluster as usize]; if let Some(x) = line_clusters.get(index as usize) { this.edge = x.1; } else { @@ -634,37 +661,37 @@ impl Node { fn adjacent_line( &self, - layout: &Paragraph, + render_data: &RenderData, prev: bool, move_state: &mut Option, ) -> Option { let x = move_state.unwrap_or(self.edge); - let mut line_index = layout.line_data.line_index_for_cluster(self.cluster); + let mut line_index = render_data.line_data.line_index_for_cluster(self.cluster); if prev { line_index = line_index.checked_sub(1)?; } else { line_index = line_index.checked_add(1)?; } - let line = layout.line_data.lines.get(line_index)?; + let line = render_data.line_data.lines.get(line_index)?; let y = line.baseline - 0.001; *move_state = Some(x); - Some(Self::from_point(layout, x, y)) + Some(Self::from_point(render_data, x, y)) } - fn setup_from_visual(&mut self, layout: &Paragraph, mut index: u32) { - let limit = layout.data.clusters.len() as u32; + fn setup_from_visual(&mut self, render_data: &RenderData, mut index: u32) { + let limit = render_data.data.clusters.len() as u32; index = index.min(limit.saturating_sub(1)); self.cluster = index; - let logical_index = layout.line_data.visual_to_logical(index); - self.rtl = layout.line_data.is_rtl(logical_index); - self.nl = layout.data.clusters[logical_index as usize].is_newline(); + let logical_index = render_data.line_data.visual_to_logical(index); + self.rtl = render_data.line_data.is_rtl(logical_index); + self.nl = render_data.data.clusters[logical_index as usize].is_newline(); if index == limit { self.after = false; } } - fn eol_state(&self, layout: &Paragraph) -> Option { - if let Some(line) = layout.line_data.lines.get(self.line as usize) { + fn eol_state(&self, render_data: &RenderData) -> Option { + if let Some(line) = render_data.line_data.lines.get(self.line as usize) { let tw = line.trailing_whitespace; if self.cluster + 1 == line.clusters.1 && tw { return Some(1); @@ -675,50 +702,50 @@ impl Node { None } - // fn previous_text_location(&self, layout: &Paragraph) -> (FragmentId, usize) { - // let data = &layout.data; + // fn previous_text_location(&self, render_data: &RenderData) -> (FragmentId, usize) { + // let data = &render_data.data; // let limit = data.clusters.len() as u32; // if limit == 0 { // return (FragmentId(0), 0); // } // if self.after { // if self.rtl { - // let logical_index = layout + // let logical_index = render_data // .line_data // .visual_to_logical(self.cluster) // .min(limit - 1) // .saturating_sub(1); // return Self::from_visual_cluster( - // layout, - // layout.line_data.logical_to_visual(logical_index), + // render_data, + // render_data.line_data.logical_to_visual(logical_index), // true, // ) - // .text_location(layout); + // .text_location(render_data); // } else { - // return Self::from_visual_cluster(layout, self.cluster, false) - // .text_location(layout); + // return Self::from_visual_cluster(render_data, self.cluster, false) + // .text_location(render_data); // } // } - // let logical_index = layout + // let logical_index = render_data // .line_data // .visual_to_logical(self.cluster) // .min(limit - 1) // .saturating_sub(1); // Self::from_visual_cluster( - // layout, - // layout.line_data.logical_to_visual(logical_index), + // render_data, + // render_data.line_data.logical_to_visual(logical_index), // false, // ) - // .text_location(layout) + // .text_location(render_data) // } - fn text_offset(&self, layout: &Paragraph) -> usize { - let data = &layout.data; + fn text_offset(&self, render_data: &RenderData) -> usize { + let data = &render_data.data; let limit = data.clusters.len() as u32; if limit == 0 { return 0; } - let index = layout + let index = render_data .line_data .visual_to_logical(self.cluster) .min(limit - 1); @@ -736,8 +763,8 @@ impl Node { 0 } - fn logical_index(&self, layout: &Paragraph) -> u32 { - let mut index = layout.line_data.visual_to_logical(self.cluster); + fn logical_index(&self, render_data: &RenderData) -> u32 { + let mut index = render_data.line_data.visual_to_logical(self.cluster); if self.rtl { if !self.after { index += 1; diff --git a/sugarloaf/src/layout/render_data.rs b/sugarloaf/src/layout/render_data.rs index 92eb10bf72..b49207e9ae 100644 --- a/sugarloaf/src/layout/render_data.rs +++ b/sugarloaf/src/layout/render_data.rs @@ -9,12 +9,12 @@ // This file however suffered updates made by Raphael Amorim to support // underline_color, background_color, text color and other functionalities -//! Paragraph. +//! RenderData. use super::layout_data::*; use super::line_breaker::BreakLines; use super::Direction; -use super::{builder_data::SpanData, Paragraph, SpanId}; +use super::{builder_data::SpanData, SpanId}; use crate::sugarloaf::primitives::SugarCursor; use core::iter::DoubleEndedIterator; use core::ops::Range; @@ -22,7 +22,14 @@ use swash::shape::{cluster::Glyph as ShapedGlyph, Shaper}; use swash::text::cluster::{Boundary, ClusterInfo}; use swash::{GlyphId, NormalizedCoord}; -impl Paragraph { +/// Collection of text, organized into lines, runs and clusters. +#[derive(Clone, Default)] +pub struct RenderData { + pub data: LayoutData, + pub line_data: LineLayoutData, +} + +impl RenderData { pub fn dump_clusters(&self) { for (i, cluster) in self.line_data.clusters.iter().enumerate() { println!("[{}] {} @ {}", i, cluster.0, cluster.1); @@ -56,13 +63,14 @@ impl Paragraph { } } -impl Paragraph { +impl RenderData { pub(super) fn push_run( &mut self, spans: &[SpanData], font: &usize, size: f32, level: u8, + line: u32, shaper: Shaper<'_>, ) { let coords_start = self.data.coords.len() as u32; @@ -74,7 +82,7 @@ impl Paragraph { let metrics = shaper.metrics(); let mut advance = 0.; let mut last_span = self.data.last_span; - let mut span_data = &spans[last_span as usize]; + let mut span_data = &spans[self.data.last_span]; shaper.shape_with(|c| { if c.info.boundary() == Boundary::Mandatory { if let Some(c) = self.data.clusters.last_mut() { @@ -82,14 +90,14 @@ impl Paragraph { } } let span = c.data; - if span != last_span { - span_data = &spans[last_span as usize]; + if span as usize != last_span { + span_data = &spans[last_span]; // Ensure that every run belongs to a single span. let clusters_end = self.data.clusters.len() as u32; if clusters_end != clusters_start { self.data.runs.push(RunData { span: SpanId(last_span), - line: 0, + line, font: *font, coords: (coords_start, coords_end), color: span_data.color, @@ -119,7 +127,7 @@ impl Paragraph { }); clusters_start = clusters_end; } - last_span = span; + last_span = span as usize; } let mut glyphs_start = self.data.glyphs.len() as u32; let mut cluster_advance = 0.; @@ -190,7 +198,7 @@ impl Paragraph { self.data.last_span = last_span; self.data.runs.push(RunData { span: SpanId(last_span), - line: 0, + line, font: *font, coords: (coords_start, coords_end), size, @@ -225,7 +233,7 @@ impl Paragraph { // Simple glyph self.data.glyphs.push(GlyphData { data: glyph.id as u32 | (packed_advance << 16), - span: SpanId(glyph.data), + span: SpanId(glyph.data as usize), }); return glyph_index; } @@ -235,7 +243,7 @@ impl Paragraph { self.data.detailed_glyphs.push(Glyph::new(glyph)); self.data.glyphs.push(GlyphData { data: GLYPH_DETAILED | detail_index, - span: SpanId(glyph.data), + span: SpanId(glyph.data as usize), }); glyph_index } @@ -442,7 +450,7 @@ impl Glyph { x: g.x, y: g.y, advance: g.advance, - span: SpanId(g.data), + span: SpanId(g.data as usize), } } } @@ -616,7 +624,7 @@ pub struct Line<'a> { } impl<'a> Line<'a> { - pub(super) fn new(layout: &'a Paragraph, line_index: usize) -> Self { + pub(super) fn new(layout: &'a RenderData, line_index: usize) -> Self { Self { layout: &layout.data, line_layout: &layout.line_data, diff --git a/sugarloaf/src/lib.rs b/sugarloaf/src/lib.rs index 8188ca18fe..277d79bdfb 100644 --- a/sugarloaf/src/lib.rs +++ b/sugarloaf/src/lib.rs @@ -1,11 +1,9 @@ pub mod components; -pub mod content; pub mod context; pub mod font; pub mod layout; mod sugarloaf; -pub use crate::content::{Content, ContentBuilder}; pub use crate::sugarloaf::{ compositors::SugarCompositorLevel, graphics::{ diff --git a/sugarloaf/src/sugarloaf/compositors/advanced.rs b/sugarloaf/src/sugarloaf/compositors/advanced.rs index d8f8b2ae9d..c52ef6e3b2 100644 --- a/sugarloaf/src/sugarloaf/compositors/advanced.rs +++ b/sugarloaf/src/sugarloaf/compositors/advanced.rs @@ -8,13 +8,13 @@ use crate::font::FontLibrary; use crate::font::{Style, Weight}; -use crate::layout::{Alignment, Direction, LayoutContext, Paragraph}; +use crate::layout::{Content, ContentBuilder, Direction, LayoutContext, RenderData}; use crate::sugarloaf::{tree::SugarTree, SpanStyle}; -use crate::{Content, ContentBuilder, SugarCursor, SugarDecoration}; +use crate::{SugarCursor, SugarDecoration}; pub struct Advanced { - pub render_data: Paragraph, - pub render_data_sugar: Paragraph, + pub render_data: RenderData, + pub mocked_render_data: RenderData, content_builder: ContentBuilder, layout_context: LayoutContext, } @@ -26,8 +26,8 @@ impl Default for Advanced { Self { layout_context, content_builder: ContentBuilder::default(), - render_data: Paragraph::new(), - render_data_sugar: Paragraph::new(), + render_data: RenderData::new(), + mocked_render_data: RenderData::new(), } } } @@ -53,7 +53,7 @@ impl Advanced { // self.content = build_complex_content(); // self.content = build_terminal_content(); // self.content = self.content_builder.clone().build(); - self.render_data = Paragraph::default(); + self.render_data = RenderData::default(); } #[inline] @@ -77,7 +77,7 @@ impl Advanced { #[inline] pub fn calculate_dimensions(&mut self, tree: &SugarTree) { - let mut content_builder = crate::content::Content::builder(); + let mut content_builder = Content::builder(); content_builder.enter_span(&[ SpanStyle::FontId(0), SpanStyle::Size(tree.layout.font_size), @@ -93,22 +93,31 @@ impl Advanced { ); let content = content_builder.build_ref(); content.layout(&mut lb); - self.render_data_sugar.clear(); - lb.build_into(&mut self.render_data_sugar); + self.mocked_render_data.clear(); + lb.build_into(&mut self.mocked_render_data); - self.render_data_sugar.break_lines().break_remaining( - tree.layout.width - tree.layout.style.screen_position.0, - Alignment::Start, - ); + // self.mocked_render_data.break_lines().break_remaining( + // tree.layout.width - tree.layout.style.screen_position.0, + // Alignment::Start, + // ); + self.mocked_render_data + .break_lines() + .break_without_advance_or_alignment() } #[inline] - pub fn update_size(&mut self, tree: &SugarTree) { + pub fn update_size(&mut self, _tree: &SugarTree) { // let start = std::time::Instant::now(); - self.render_data.break_lines().break_remaining( - tree.layout.width - tree.layout.style.screen_position.0, - Alignment::Start, - ); + // self.render_data.break_lines().break_remaining( + // tree.layout.width - tree.layout.style.screen_position.0, + // Alignment::Start, + // ); + + // TODO: break_lines and break_remaining + self.render_data + .break_lines() + .break_without_advance_or_alignment(); + // let duration = start.elapsed(); // println!( // "Time elapsed in rich_text_brush.prepare() break_lines and break_remaining is: {:?}", @@ -117,8 +126,8 @@ impl Advanced { } #[inline] - pub fn update_tree_with_new_line(&mut self, line: usize, tree: &SugarTree) { - if line == 0 { + pub fn update_tree_with_new_line(&mut self, line_number: usize, tree: &SugarTree) { + if line_number == 0 { self.content_builder = Content::builder(); self.content_builder.enter_span(&[ SpanStyle::Size(tree.layout.font_size), @@ -126,7 +135,7 @@ impl Advanced { ]); } - let line = &tree.lines[line]; + let line = &tree.lines[line_number]; let underline = &[ SpanStyle::Underline(true), @@ -141,13 +150,7 @@ impl Advanced { SpanStyle::UnderlineSize(Some(2.)), ]; - // let mut content = String::from(""); for i in 0..line.len() { - // let mut font_id = 0; - // if line[i].content == '🥶' { - // font_id = 7; - // } - let mut span_counter = 0; if line[i].style.is_bold_italic { self.content_builder.enter_span(&[ @@ -208,7 +211,6 @@ impl Advanced { } self.content_builder.enter_span(&[ - // SpanStyle::FontId(font_id), SpanStyle::Color(line[i].foreground_color), SpanStyle::BackgroundColor(line[i].background_color), ]); @@ -228,14 +230,19 @@ impl Advanced { span_counter -= 1; } } - self.content_builder.add_char('\n'); + + // if line is the last one skip break line + // if line_number < tree.lines.len() - 1 { + // self.content_builder.add_char('\n'); + self.content_builder.break_line(); + // } } } #[allow(unused)] -fn build_simple_content() -> crate::content::Content { +fn build_simple_content() -> Content { use crate::layout::*; - let mut db = crate::content::Content::builder(); + let mut db = Content::builder(); use SpanStyle as S; @@ -247,9 +254,9 @@ fn build_simple_content() -> crate::content::Content { } #[allow(unused)] -fn build_complex_content() -> crate::content::Content { +fn build_complex_content() -> Content { use crate::layout::*; - let mut db = crate::content::Content::builder(); + let mut db = Content::builder(); use SpanStyle as S; @@ -320,9 +327,9 @@ fn build_complex_content() -> crate::content::Content { } #[allow(unused)] -fn build_terminal_content() -> crate::content::Content { +fn build_terminal_content() -> Content { use crate::layout::*; - let mut db = crate::content::Content::builder(); + let mut db = Content::builder(); use SpanStyle as S;