From 66c83c31ce59e85969b74c9f65c842a25268aea3 Mon Sep 17 00:00:00 2001 From: Fishhh Date: Sat, 30 Nov 2024 15:38:58 +0100 Subject: [PATCH] Move `ends_with_newline` back into `Row` --- crates/epaint/src/text/fonts.rs | 93 +++++++++++---------- crates/epaint/src/text/text_layout.rs | 14 ++-- crates/epaint/src/text/text_layout_types.rs | 14 ++-- 3 files changed, 64 insertions(+), 57 deletions(-) diff --git a/crates/epaint/src/text/fonts.rs b/crates/epaint/src/text/fonts.rs index a26442991..dbd2fa3c0 100644 --- a/crates/epaint/src/text/fonts.rs +++ b/crates/epaint/src/text/fonts.rs @@ -739,7 +739,9 @@ impl GalleyCache { let mut text_left = job.text.as_str(); let mut first_row_min_height = job.first_row_min_height; loop { - let end = text_left.find('\n').map_or(job.text.len(), |i| i + current); + let end = text_left + .find('\n') + .map_or(job.text.len(), |i| i + current + 1); let start = current; let mut line_job = LayoutJob { @@ -749,8 +751,7 @@ impl GalleyCache { ..job.wrap }, sections: Vec::new(), - // Prevent an infinite recursion - break_on_newline: false, + break_on_newline: true, halign: job.halign, justify: job.justify, first_row_min_height, @@ -782,28 +783,14 @@ impl GalleyCache { current = section_end; } - // If the current line is empty, add an extra offset to make sure it's not omitted - // because the resulting galley will have a height of zero. - let extra_y_offset = if start == end && end != job.text.len() { - while job.sections[current_section].byte_range.end == end { - current_section += 1; - } - let format = &job.sections[current_section].format; - format - .line_height - .unwrap_or(fonts.row_height(&format.font_id)) - } else { - 0.0 - }; - - let galley = self.layout(fonts, line_job); + let galley = self.layout_component_line(fonts, line_job); // This will prevent us from invalidating cache entries unnecessarily if left_max_rows != usize::MAX { left_max_rows -= galley.rows.len(); } - galleys.push((galley, extra_y_offset)); + galleys.push(galley); - current = end + 1; + current = end; if current >= job.text.len() { break; } else { @@ -822,29 +809,30 @@ impl GalleyCache { pixels_per_point: fonts.pixels_per_point, }; - for (galley, extra_y_offset) in galleys { + for (i, galley) in galleys.iter().enumerate() { let current_offset = emath::vec2(0.0, merged_galley.rect.height()); - merged_galley - .rows - .extend(galley.rows.iter().map(|placed_row| { - let new_pos = placed_row.pos + current_offset; - merged_galley.mesh_bounds = merged_galley - .mesh_bounds - .union(placed_row.visuals.mesh_bounds.translate(new_pos.to_vec2())); - super::PlacedRow { - row: placed_row.row.clone(), - pos: round_to_pixel(new_pos), - ends_with_newline: placed_row.ends_with_newline, - } - })); - if let Some(last) = merged_galley.rows.last_mut() { - last.ends_with_newline = true; + let mut rows = galley.rows.iter(); + if i != galleys.len() - 1 && !galley.elided { + let popped = rows.next_back(); + debug_assert_eq!(popped.unwrap().row.glyphs.len(), 0); } - merged_galley.rect = merged_galley - .rect - .union(galley.rect.translate(current_offset)); - merged_galley.rect.max.y += extra_y_offset; + + merged_galley.rows.extend(rows.map(|placed_row| { + let new_pos = round_to_pixel(placed_row.pos + current_offset); + merged_galley.mesh_bounds = merged_galley + .mesh_bounds + .union(placed_row.visuals.mesh_bounds.translate(new_pos.to_vec2())); + merged_galley.rect = merged_galley + .rect + .union(emath::Rect::from_min_size(new_pos, placed_row.size)); + + super::PlacedRow { + row: placed_row.row.clone(), + pos: new_pos, + } + })); + merged_galley.num_vertices += galley.num_vertices; merged_galley.num_indices += galley.num_indices; if galley.elided { @@ -853,13 +841,30 @@ impl GalleyCache { } } - if let Some(last) = merged_galley.rows.last_mut() { - last.ends_with_newline = false; - } - merged_galley } + fn layout_component_line(&mut self, fonts: &mut FontsImpl, job: LayoutJob) -> Arc { + let hash = crate::util::hash(&job); + + match self.cache.entry(hash) { + std::collections::hash_map::Entry::Occupied(entry) => { + let cached = entry.into_mut(); + cached.last_used = self.generation; + cached.galley.clone() + } + std::collections::hash_map::Entry::Vacant(entry) => { + let galley = super::layout(fonts, job.into()); + let galley = Arc::new(galley); + entry.insert(CachedGalley { + last_used: self.generation, + galley: galley.clone(), + }); + galley + } + } + } + fn layout(&mut self, fonts: &mut FontsImpl, mut job: LayoutJob) -> Arc { if job.wrap.max_width.is_finite() { // Protect against rounding errors in egui layout code. diff --git a/crates/epaint/src/text/text_layout.rs b/crates/epaint/src/text/text_layout.rs index 9203dfd05..798fa1d3b 100644 --- a/crates/epaint/src/text/text_layout.rs +++ b/crates/epaint/src/text/text_layout.rs @@ -220,9 +220,9 @@ fn rows_from_paragraphs( glyphs: vec![], visuals: Default::default(), size: vec2(0.0, paragraph.empty_paragraph_height), + ends_with_newline: !is_last_paragraph, }), pos: pos2(paragraph.cursor_x, 0.0), - ends_with_newline: !is_last_paragraph, }); } else { let paragraph_max_x = paragraph.glyphs.last().unwrap().max_x(); @@ -236,13 +236,15 @@ fn rows_from_paragraphs( glyphs: paragraph.glyphs, visuals: Default::default(), size: rect.size(), + ends_with_newline: !is_last_paragraph, }), pos: rect.min, - ends_with_newline: !is_last_paragraph, }); } else { line_break(¶graph, job, &mut rows, elided); - rows.last_mut().unwrap().ends_with_newline = !is_last_paragraph; + let placed_row = rows.last_mut().unwrap(); + let row = Arc::get_mut(&mut placed_row.row).unwrap(); + row.ends_with_newline = !is_last_paragraph; } } } @@ -288,9 +290,9 @@ fn line_break( glyphs: vec![], visuals: Default::default(), size: rect.size(), + ends_with_newline: false, }), pos: rect.min, - ends_with_newline: false, }); row_start_x += first_row_indentation; first_row_indentation = 0.0; @@ -316,9 +318,9 @@ fn line_break( glyphs, visuals: Default::default(), size: rect.size(), + ends_with_newline: false, }), pos: rect.min, - ends_with_newline: false, }); // Start a new row: @@ -359,9 +361,9 @@ fn line_break( glyphs, visuals: Default::default(), size: rect.size(), + ends_with_newline: false, }), pos: rect.min, - ends_with_newline: false, }); } } diff --git a/crates/epaint/src/text/text_layout_types.rs b/crates/epaint/src/text/text_layout_types.rs index 0229665cb..b3d26764e 100644 --- a/crates/epaint/src/text/text_layout_types.rs +++ b/crates/epaint/src/text/text_layout_types.rs @@ -546,13 +546,6 @@ pub struct PlacedRow { /// The position of this [`Row`] relative to the galley. pub pos: Pos2, - - /// If true, this [`PlacedRow`] came from a paragraph ending with a `\n`. - /// The `\n` itself is omitted from [`Row::glyphs`]. - /// A `\n` in the input text always creates a new [`PlacedRow`] below it, - /// so that text that ends with `\n` has an empty [`PlacedRow`] last. - /// This also implies that the last [`PlacedRow`] in a [`Galley`] always has `ends_with_newline == false`. - pub ends_with_newline: bool, } impl PlacedRow { @@ -586,6 +579,13 @@ pub struct Row { /// The mesh, ready to be rendered. pub visuals: RowVisuals, + + /// If true, this [`Row`] came from a paragraph ending with a `\n`. + /// The `\n` itself is omitted from [`glyphs`]. + /// A `\n` in the input text always creates a new [`Row`] below it, + /// so that text that ends with `\n` has an empty [`Row`] last. + /// This also implies that the last [`Row`] in a [`Galley`] always has `ends_with_newline == false`. + pub ends_with_newline: bool, } /// The tessellated output of a row.