diff --git a/crates/epaint/src/text/fonts.rs b/crates/epaint/src/text/fonts.rs index 557d71733c8..1699420eb58 100644 --- a/crates/epaint/src/text/fonts.rs +++ b/crates/epaint/src/text/fonts.rs @@ -703,6 +703,91 @@ impl GalleyCache { cached.galley.clone() } std::collections::hash_map::Entry::Vacant(entry) => { + // Optimize by splitting the job into paragraphs, if possible. + if job.break_on_newline { + // let mut newlines = job.text.match_indices('\n').peekable(); + // if newlines.peek().filter(|(i, _)| *i != job.text.len() - 1).is_some() { + // let mut galleys = newlines.map(|(i, _)| Some(i + 1)).chain(std::iter::once(None)).scan(0, |last, i| { + // let i = i.unwrap_or(job.text.len()); + // // Get the relevant part of the job. Should return None if last == + // // job.text.len() + // let new_job = job.slice(*last..i); + // *last = i; + // new_job + // }).map(|job| self.layout(fonts, job)); + // + // let first_galley = galleys.next().expect("there should be at least one galley"); + // let galley = galleys.try_fold((*first_galley).clone(), |acc_galley, galley| { + // acc_galley.rows.extend(galley.rows); + // acc_galley.elided = + // }) + // self.cache.insert(hash, CachedGalley { + // last_used: self.generation, + // galley: first_galley.clone(), + // }); + // return first_galley; + // + // + // } + let mut newlines = job.text.match_indices('\n').map(|(i, _)| i + 1); + if let Some(first_i) = newlines.next().filter(|i| *i != job.text.len()) { + // TODO(dacid44): remove this .expect() + let mut galley = self + .layout( + fonts, + job.slice(0..first_i, 0) + .expect("we just ensured that 1 <= i < job.text.len()"), + ) + .as_ref() + .clone(); + let mut last = first_i; + let mut paragraph_endings = newlines.chain(std::iter::once(job.text.len())); + while let Some(i) = paragraph_endings.next().filter(|_| !galley.elided) { + // job.slice() should return None if lest == job.text.len(). + let Some(next_job) = job.slice(last..i, galley.rows.len()) else { + break; + }; + let next_galley = self.layout(fonts, next_job); + let offset = emath::vec2(0.0, galley.rect.height()); + galley.rows.extend(next_galley.rows.iter().map(|row| { + crate::text::Row { + rect: row.rect.translate(offset), + visuals: crate::text::RowVisuals { + mesh_bounds: row.visuals.mesh_bounds.translate(offset), + ..row.visuals.clone() + }, + glyphs: row + .glyphs + .iter() + .map(|glyph| crate::text::Glyph { + pos: glyph.pos + offset, + ..*glyph + }) + .collect(), + ..row.clone() + } + })); + galley.elided |= next_galley.elided; + galley.rect = galley.rect.union(next_galley.rect.translate(offset)); + galley.mesh_bounds = galley + .mesh_bounds + .union(next_galley.mesh_bounds.translate(offset)); + galley.num_vertices += next_galley.num_vertices; + galley.num_indices += next_galley.num_indices; + last = i; + } + galley.job = job.into(); + let galley = Arc::new(galley); + self.cache.insert( + hash, + CachedGalley { + last_used: self.generation, + galley: galley.clone(), + }, + ); + return galley; + } + } let galley = super::layout(fonts, job.into()); let galley = Arc::new(galley); entry.insert(CachedGalley { diff --git a/crates/epaint/src/text/text_layout_types.rs b/crates/epaint/src/text/text_layout_types.rs index 70317f771f6..a3d2bd21812 100644 --- a/crates/epaint/src/text/text_layout_types.rs +++ b/crates/epaint/src/text/text_layout_types.rs @@ -167,6 +167,38 @@ impl LayoutJob { } max_height } + + /// Get a new `LayoutJob` containing only the text and sections for the given slice. Needs to + /// know the number of rows before to calculate text wrapping. + pub fn slice(&self, range: Range, rows_before: usize) -> Option { + if range.is_empty() + || range.start >= self.text.len() + || range.end > self.text.len() + || rows_before > self.wrap.max_rows + { + return None; + } + Some(Self { + text: self.text[range.clone()].to_owned(), + sections: self + .sections + .iter() + .filter_map(|section| { + let new_range = range.start.max(section.byte_range.start) + ..range.end.min(section.byte_range.end); + (!new_range.is_empty()).then(|| LayoutSection { + byte_range: new_range.start - range.start..new_range.end - range.start, + ..section.clone() + }) + }) + .collect(), + wrap: TextWrapping { + max_rows: self.wrap.max_rows - rows_before, + ..self.wrap + }, + ..self.clone() + }) + } } impl std::hash::Hash for LayoutJob {