use crate::{point, px, FontId, GlyphId, Pixels, PlatformTextSystem, Point, Size};use collections::FxHashMap;use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};use smallvec::SmallVec;use std::{ borrow::Borrow, hash::{Hash, Hasher}, ops::Range, sync::Arc,}; use super::LineWrapper; /// A laid out and styled line of text#[derive(Default, Debug)]pub struct LineLayout { /// The font size for this line pub font_size: Pixels, /// The width of the line pub width: Pixels, /// The ascent of the line pub ascent: Pixels, /// The descent of the line pub descent: Pixels, /// The shaped runs that make up this line pub runs: Vec<ShapedRun>, /// The length of the line in utf-8 bytes pub len: usize,} /// A run of text that has been shaped .#[derive(Debug, Clone)]pub struct ShapedRun { /// The font id for this run pub font_id: FontId, /// The glyphs that make up this run pub glyphs: SmallVec<[ShapedGlyph; 8]>,} /// A single glyph, ready to paint.#[derive(Clone, Debug)]pub struct ShapedGlyph { /// The ID for this glyph, as determined by the text system. pub id: GlyphId, /// The position of this glyph in its containing line. pub position: Point<Pixels>, /// The index of this glyph in the original text. pub index: usize, /// Whether this glyph is an emoji pub is_emoji: bool,} impl LineLayout { /// The index for the character at the given x coordinate pub fn index_for_x(&self, x: Pixels) -> Option<usize> { if x >= self.width { None } else { for run in self.runs.iter().rev() { for glyph in run.glyphs.iter().rev() { if glyph.position.x <= x { return Some(glyph.index); } } } Some(0) } } /// closest_index_for_x returns the character boundary closest to the given x coordinate /// (e.g. to handle aligning up/down arrow keys) pub fn closest_index_for_x(&self, x: Pixels) -> usize { let mut prev_index = 0; let mut prev_x = px(0.); for run in self.runs.iter() { for glyph in run.glyphs.iter() { if glyph.position.x >= x { if glyph.position.x - x < x - prev_x { return glyph.index; } else { return prev_index; } } prev_index = glyph.index; prev_x = glyph.position.x; } } self.len } /// The x position of the character at the given index pub fn x_for_index(&self, index: usize) -> Pixels { for run in &self.runs { for glyph in &run.glyphs { if glyph.index >= index { return glyph.position.x; } } } self.width } /// The corresponding Font at the given index pub fn font_id_for_index(&self, index: usize) -> Option<FontId> { for run in &self.runs { for glyph in &run.glyphs { if glyph.index >= index { return Some(run.font_id); } } } None } fn compute_wrap_boundaries( &self, text: &str, wrap_width: Pixels, ) -> SmallVec<[WrapBoundary; 1]> { let mut boundaries = SmallVec::new(); let mut first_non_whitespace_ix = None; let mut last_candidate_ix = None; let mut last_candidate_x = px(0.); let mut last_boundary = WrapBoundary { run_ix: 0, glyph_ix: 0, }; let mut last_boundary_x = px(0.); let mut prev_ch = '\0'; let mut glyphs = self .runs .iter() .enumerate() .flat_map(move |(run_ix, run)| { run.glyphs.iter().enumerate().map(move |(glyph_ix, glyph)| { let character = text[glyph.index..].chars().next().unwrap(); ( WrapBoundary { run_ix, glyph_ix }, character, glyph.position.x, ) }) }) .peekable(); while let Some((boundary, ch, x)) = glyphs.next() { if ch == '\n' { continue; } // Here is very similar to `LineWrapper::wrap_line` to determine text wrapping, // but there are some differences, so we have to duplicate the code here. if LineWrapper::is_word_char(ch) { if prev_ch == ' ' && ch != ' ' && first_non_whitespace_ix.is_some() { last_candidate_ix = Some(boundary); last_candidate_x = x; } } else { if ch != ' ' && first_non_whitespace_ix.is_some() { last_candidate_ix = Some(boundary); last_candidate_x = x; } } if ch != ' ' && first_non_whitespace_ix.is_none() { first_non_whitespace_ix = Some(boundary); } let next_x = glyphs.peek().map_or(self.width, |(_, _, x)| *x); let width = next_x - last_boundary_x; if width > wrap_width && boundary > last_boundary { if let Some(last_candidate_ix) = last_candidate_ix.take() { last_boundary = last_candidate_ix; last_boundary_x = last_candidate_x; } else { last_boundary = boundary; last_boundary_x = x; } boundaries.push(last_boundary); } prev_ch = ch; } boundaries }} /// A line of text that has been wrapped to fit a given width#[derive(Default, Debug)]pub struct WrappedLineLayout { /// The line layout, pre-wrapping. pub unwrapped_layout: Arc<LineLayout>, /// The boundaries at which the line was wrapped pub wrap_boundaries: SmallVec<[WrapBoundary; 1]>, /// The width of the line, if it was wrapped pub wrap_width: Option<Pixels>,} /// A boundary at which a line was wrapped#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]pub struct WrapBoundary { /// The index in the run just before the line was wrapped pub run_ix: usize, /// The index of the glyph just before the line was wrapped pub glyph_ix: usize,} impl WrappedLineLayout { /// The length of the underlying text, in utf8 bytes. #[allow(clippy::len_without_is_empty)] pub fn len(&self) -> usize { self.unwrapped_layout.len } /// The width of this line, in pixels, whether or not it was wrapped. pub fn width(&self) -> Pixels { self.wrap_width .unwrap_or(Pixels::MAX) .min(self.unwrapped_layout.width) } /// The size of the whole wrapped text, for the given line_height. /// can span multiple lines if there are multiple wrap boundaries. pub fn size(&self, line_height: Pixels) -> Size<Pixels> { Size { width: self.width(), height: line_height * (self.wrap_boundaries.len() + 1), } } /// The ascent of a line in this layout pub fn ascent(&self) -> Pixels { self.unwrapped_layout.ascent } /// The descent of a line in this layout pub fn descent(&self) -> Pixels { self.unwrapped_layout.descent } /// The wrap boundaries in this layout pub fn wrap_boundaries(&self) -> &[WrapBoundary] { &self.wrap_boundaries } /// The font size of this layout pub fn font_size(&self) -> Pixels { self.unwrapped_layout.font_size } /// The runs in this layout, sans wrapping pub fn runs(&self) -> &[ShapedRun] { &self.unwrapped_layout.runs } /// The index corresponding to a given position in this layout for the given line height. pub fn index_for_position( &self, mut position: Point<Pixels>, line_height: Pixels, ) -> Result<usize, usize> { let wrapped_line_ix = (position.y / line_height) as usize; let wrapped_line_start_index; let wrapped_line_start_x; if wrapped_line_ix > 0 { let Some(line_start_boundary) = self.wrap_boundaries.get(wrapped_line_ix - 1) else { return Err(0); }; let run = &self.unwrapped_layout.runs[line_start_boundary.run_ix]; let glyph = &run.glyphs[line_start_boundary.glyph_ix]; wrapped_line_start_index = glyph.index; wrapped_line_start_x = glyph.position.x; } else { wrapped_line_start_index = 0; wrapped_line_start_x = Pixels::ZERO; }; let wrapped_line_end_index; let wrapped_line_end_x; if wrapped_line_ix < self.wrap_boundaries.len() { let next_wrap_boundary_ix = wrapped_line_ix; let next_wrap_boundary = self.wrap_boundaries[next_wrap_boundary_ix]; let run = &self.unwrapped_layout.runs[next_wrap_boundary.run_ix]; let glyph = &run.glyphs[next_wrap_boundary.glyph_ix]; wrapped_line_end_index = glyph.index; wrapped_line_end_x = glyph.position.x; } else { wrapped_line_end_index = self.unwrapped_layout.len; wrapped_line_end_x = self.unwrapped_layout.width; }; let mut position_in_unwrapped_line = position; position_in_unwrapped_line.x += wrapped_line_start_x; if position_in_unwrapped_line.x < wrapped_line_start_x { Err(wrapped_line_start_index) } else if position_in_unwrapped_line.x >= wrapped_line_end_x { Err(wrapped_line_end_index) } else { Ok(self .unwrapped_layout .index_for_x(position_in_unwrapped_line.x) .unwrap()) } } /// Returns the pixel position for the given byte index. pub fn position_for_index(&self, index: usize, line_height: Pixels) -> Option<Point<Pixels>> { let mut line_start_ix = 0; let mut line_end_indices = self .wrap_boundaries .iter() .map(|wrap_boundary| { let run = &self.unwrapped_layout.runs[wrap_boundary.run_ix]; let glyph = &run.glyphs[wrap_boundary.glyph_ix]; glyph.index }) .chain([self.len()]) .enumerate(); for (ix, line_end_ix) in line_end_indices { let line_y = ix as f32 * line_height; if index < line_start_ix { break; } else if index > line_end_ix { line_start_ix = line_end_ix; continue; } else { let line_start_x = self.unwrapped_layout.x_for_index(line_start_ix); let x = self.unwrapped_layout.x_for_index(index) - line_start_x; return Some(point(x, line_y)); } } None }} pub(crate) struct LineLayoutCache { previous_frame: Mutex<FrameCache>, current_frame: RwLock<FrameCache>, platform_text_system: Arc<dyn PlatformTextSystem>,} #[derive(Default)]struct FrameCache { lines: FxHashMap<Arc<CacheKey>, Arc<LineLayout>>, wrapped_lines: FxHashMap<Arc<CacheKey>, Arc<WrappedLineLayout>>, used_lines: Vec<Arc<CacheKey>>, used_wrapped_lines: Vec<Arc<CacheKey>>,} #[derive(Clone, Default)]pub(crate) struct LineLayoutIndex { lines_index: usize, wrapped_lines_index: usize,} impl LineLayoutCache { pub fn new(platform_text_system: Arc<dyn PlatformTextSystem>) -> Self { Self { previous_frame: Mutex::default(), current_frame: RwLock::default(), platform_text_system, } } pub fn layout_index(&self) -> LineLayoutIndex { let frame = self.current_frame.read(); LineLayoutIndex { lines_index: frame.used_lines.len(), wrapped_lines_index: frame.used_wrapped_lines.len(), } } pub fn reuse_layouts(&self, range: Range<LineLayoutIndex>) { let mut previous_frame = &mut *self.previous_frame.lock(); let mut current_frame = &mut *self.current_frame.write(); if let Some(cached_keys) = previous_frame .used_lines .get(range.start.lines_index..range.end.lines_index) { for key in cached_keys { if let Some((key, line)) = previous_frame.lines.remove_entry(key) { current_frame.lines.insert(key, line); } current_frame.used_lines.push(key.clone()); } } if let Some(cached_keys) = previous_frame .used_wrapped_lines .get(range.start.wrapped_lines_index..range.end.wrapped_lines_index) { for key in cached_keys { if let Some((key, line)) = previous_frame.wrapped_lines.remove_entry(key) { current_frame.wrapped_lines.insert(key, line); } current_frame.used_wrapped_lines.push(key.clone()); } } } pub fn truncate_layouts(&self, index: LineLayoutIndex) { let mut current_frame = &mut *self.current_frame.write(); current_frame.used_lines.truncate(index.lines_index); current_frame .used_wrapped_lines .truncate(index.wrapped_lines_index); } pub fn finish_frame(&self) { let mut prev_frame = self.previous_frame.lock(); let mut curr_frame = self.current_frame.write(); std::mem::swap(&mut *prev_frame, &mut *curr_frame); curr_frame.lines.clear(); curr_frame.wrapped_lines.clear(); curr_frame.used_lines.clear(); curr_frame.used_wrapped_lines.clear(); } pub fn layout_wrapped_line( &self, text: &str, font_size: Pixels, runs: &[FontRun], wrap_width: Option<Pixels>, ) -> Arc<WrappedLineLayout> { let key = &CacheKeyRef { text, font_size, runs, wrap_width, } as &dyn AsCacheKeyRef; let current_frame = self.current_frame.upgradable_read(); if let Some(layout) = current_frame.wrapped_lines.get(key) { return layout.clone(); } let previous_frame_entry = self.previous_frame.lock().wrapped_lines.remove_entry(key); if let Some((key, layout)) = previous_frame_entry { let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame); current_frame .wrapped_lines .insert(key.clone(), layout.clone()); current_frame.used_wrapped_lines.push(key); layout } else { drop(current_frame); let unwrapped_layout = self.layout_line(text, font_size, runs); let wrap_boundaries = if let Some(wrap_width) = wrap_width { unwrapped_layout.compute_wrap_boundaries(text.as_ref(), wrap_width) } else { SmallVec::new() }; let layout = Arc::new(WrappedLineLayout { unwrapped_layout, wrap_boundaries, wrap_width, }); let key = Arc::new(CacheKey { text: text.into(), font_size, runs: SmallVec::from(runs), wrap_width, }); let mut current_frame = self.current_frame.write(); current_frame .wrapped_lines .insert(key.clone(), layout.clone()); current_frame.used_wrapped_lines.push(key); layout } } pub fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> Arc<LineLayout> { let key = &CacheKeyRef { text, font_size, runs, wrap_width: None, } as &dyn AsCacheKeyRef; let current_frame = self.current_frame.upgradable_read(); if let Some(layout) = current_frame.lines.get(key) { return layout.clone(); }Lines 1–500 of 596. Load more
use crate::{point, px, FontId, GlyphId, Pixels, PlatformTextSystem, Point, Size}; use collections::FxHashMap; use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard}; use smallvec::SmallVec; use std::{ borrow::Borrow, hash::{Hash, Hasher}, ops::Range, sync::Arc, }; use super::LineWrapper; /// A laid out and styled line of text #[derive(Default, Debug)] pub struct LineLayout { /// The font size for this line pub font_size: Pixels, /// The width of the line pub width: Pixels, /// The ascent of the line pub ascent: Pixels, /// The descent of the line pub descent: Pixels, /// The shaped runs that make up this line pub runs: Vec<ShapedRun>, /// The length of the line in utf-8 bytes pub len: usize, } /// A run of text that has been shaped . #[derive(Debug, Clone)] pub struct ShapedRun { /// The font id for this run pub font_id: FontId, /// The glyphs that make up this run pub glyphs: SmallVec<[ShapedGlyph; 8]>, } /// A single glyph, ready to paint. #[derive(Clone, Debug)] pub struct ShapedGlyph { /// The ID for this glyph, as determined by the text system. pub id: GlyphId, /// The position of this glyph in its containing line. pub position: Point<Pixels>, /// The index of this glyph in the original text. pub index: usize, /// Whether this glyph is an emoji pub is_emoji: bool, } impl LineLayout { /// The index for the character at the given x coordinate pub fn index_for_x(&self, x: Pixels) -> Option<usize> { if x >= self.width { None } else { for run in self.runs.iter().rev() { for glyph in run.glyphs.iter().rev() { if glyph.position.x <= x { return Some(glyph.index); } } } Some(0) } } /// closest_index_for_x returns the character boundary closest to the given x coordinate /// (e.g. to handle aligning up/down arrow keys) pub fn closest_index_for_x(&self, x: Pixels) -> usize { let mut prev_index = 0; let mut prev_x = px(0.); for run in self.runs.iter() { for glyph in run.glyphs.iter() { if glyph.position.x >= x { if glyph.position.x - x < x - prev_x { return glyph.index; } else { return prev_index; } } prev_index = glyph.index; prev_x = glyph.position.x; } } self.len } /// The x position of the character at the given index pub fn x_for_index(&self, index: usize) -> Pixels { for run in &self.runs { for glyph in &run.glyphs { if glyph.index >= index { return glyph.position.x; } } } self.width } /// The corresponding Font at the given index pub fn font_id_for_index(&self, index: usize) -> Option<FontId> { for run in &self.runs { for glyph in &run.glyphs { if glyph.index >= index { return Some(run.font_id); } } } None } fn compute_wrap_boundaries( &self, text: &str, wrap_width: Pixels, ) -> SmallVec<[WrapBoundary; 1]> { let mut boundaries = SmallVec::new(); let mut first_non_whitespace_ix = None; let mut last_candidate_ix = None; let mut last_candidate_x = px(0.); let mut last_boundary = WrapBoundary { run_ix: 0, glyph_ix: 0, }; let mut last_boundary_x = px(0.); let mut prev_ch = '\0'; let mut glyphs = self .runs .iter() .enumerate() .flat_map(move |(run_ix, run)| { run.glyphs.iter().enumerate().map(move |(glyph_ix, glyph)| { let character = text[glyph.index..].chars().next().unwrap(); ( WrapBoundary { run_ix, glyph_ix }, character, glyph.position.x, ) }) }) .peekable(); while let Some((boundary, ch, x)) = glyphs.next() { if ch == '\n' { continue; } // Here is very similar to `LineWrapper::wrap_line` to determine text wrapping, // but there are some differences, so we have to duplicate the code here. if LineWrapper::is_word_char(ch) { if prev_ch == ' ' && ch != ' ' && first_non_whitespace_ix.is_some() { last_candidate_ix = Some(boundary); last_candidate_x = x; } } else { if ch != ' ' && first_non_whitespace_ix.is_some() { last_candidate_ix = Some(boundary); last_candidate_x = x; } } if ch != ' ' && first_non_whitespace_ix.is_none() { first_non_whitespace_ix = Some(boundary); } let next_x = glyphs.peek().map_or(self.width, |(_, _, x)| *x); let width = next_x - last_boundary_x; if width > wrap_width && boundary > last_boundary { if let Some(last_candidate_ix) = last_candidate_ix.take() { last_boundary = last_candidate_ix; last_boundary_x = last_candidate_x; } else { last_boundary = boundary; last_boundary_x = x; } boundaries.push(last_boundary); } prev_ch = ch; } boundaries } } /// A line of text that has been wrapped to fit a given width #[derive(Default, Debug)] pub struct WrappedLineLayout { /// The line layout, pre-wrapping. pub unwrapped_layout: Arc<LineLayout>, /// The boundaries at which the line was wrapped pub wrap_boundaries: SmallVec<[WrapBoundary; 1]>, /// The width of the line, if it was wrapped pub wrap_width: Option<Pixels>, } /// A boundary at which a line was wrapped #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct WrapBoundary { /// The index in the run just before the line was wrapped pub run_ix: usize, /// The index of the glyph just before the line was wrapped pub glyph_ix: usize, } impl WrappedLineLayout { /// The length of the underlying text, in utf8 bytes. #[allow(clippy::len_without_is_empty)] pub fn len(&self) -> usize { self.unwrapped_layout.len } /// The width of this line, in pixels, whether or not it was wrapped. pub fn width(&self) -> Pixels { self.wrap_width .unwrap_or(Pixels::MAX) .min(self.unwrapped_layout.width) } /// The size of the whole wrapped text, for the given line_height. /// can span multiple lines if there are multiple wrap boundaries. pub fn size(&self, line_height: Pixels) -> Size<Pixels> { Size { width: self.width(), height: line_height * (self.wrap_boundaries.len() + 1), } } /// The ascent of a line in this layout pub fn ascent(&self) -> Pixels { self.unwrapped_layout.ascent } /// The descent of a line in this layout pub fn descent(&self) -> Pixels { self.unwrapped_layout.descent } /// The wrap boundaries in this layout pub fn wrap_boundaries(&self) -> &[WrapBoundary] { &self.wrap_boundaries } /// The font size of this layout pub fn font_size(&self) -> Pixels { self.unwrapped_layout.font_size } /// The runs in this layout, sans wrapping pub fn runs(&self) -> &[ShapedRun] { &self.unwrapped_layout.runs } /// The index corresponding to a given position in this layout for the given line height. pub fn index_for_position( &self, mut position: Point<Pixels>, line_height: Pixels, ) -> Result<usize, usize> { let wrapped_line_ix = (position.y / line_height) as usize; let wrapped_line_start_index; let wrapped_line_start_x; if wrapped_line_ix > 0 { let Some(line_start_boundary) = self.wrap_boundaries.get(wrapped_line_ix - 1) else { return Err(0); }; let run = &self.unwrapped_layout.runs[line_start_boundary.run_ix]; let glyph = &run.glyphs[line_start_boundary.glyph_ix]; wrapped_line_start_index = glyph.index; wrapped_line_start_x = glyph.position.x; } else { wrapped_line_start_index = 0; wrapped_line_start_x = Pixels::ZERO; }; let wrapped_line_end_index; let wrapped_line_end_x; if wrapped_line_ix < self.wrap_boundaries.len() { let next_wrap_boundary_ix = wrapped_line_ix; let next_wrap_boundary = self.wrap_boundaries[next_wrap_boundary_ix]; let run = &self.unwrapped_layout.runs[next_wrap_boundary.run_ix]; let glyph = &run.glyphs[next_wrap_boundary.glyph_ix]; wrapped_line_end_index = glyph.index; wrapped_line_end_x = glyph.position.x; } else { wrapped_line_end_index = self.unwrapped_layout.len; wrapped_line_end_x = self.unwrapped_layout.width; }; let mut position_in_unwrapped_line = position; position_in_unwrapped_line.x += wrapped_line_start_x; if position_in_unwrapped_line.x < wrapped_line_start_x { Err(wrapped_line_start_index) } else if position_in_unwrapped_line.x >= wrapped_line_end_x { Err(wrapped_line_end_index) } else { Ok(self .unwrapped_layout .index_for_x(position_in_unwrapped_line.x) .unwrap()) } } /// Returns the pixel position for the given byte index. pub fn position_for_index(&self, index: usize, line_height: Pixels) -> Option<Point<Pixels>> { let mut line_start_ix = 0; let mut line_end_indices = self .wrap_boundaries .iter() .map(|wrap_boundary| { let run = &self.unwrapped_layout.runs[wrap_boundary.run_ix]; let glyph = &run.glyphs[wrap_boundary.glyph_ix]; glyph.index }) .chain([self.len()]) .enumerate(); for (ix, line_end_ix) in line_end_indices { let line_y = ix as f32 * line_height; if index < line_start_ix { break; } else if index > line_end_ix { line_start_ix = line_end_ix; continue; } else { let line_start_x = self.unwrapped_layout.x_for_index(line_start_ix); let x = self.unwrapped_layout.x_for_index(index) - line_start_x; return Some(point(x, line_y)); } } None } } pub(crate) struct LineLayoutCache { previous_frame: Mutex<FrameCache>, current_frame: RwLock<FrameCache>, platform_text_system: Arc<dyn PlatformTextSystem>, } #[derive(Default)] struct FrameCache { lines: FxHashMap<Arc<CacheKey>, Arc<LineLayout>>, wrapped_lines: FxHashMap<Arc<CacheKey>, Arc<WrappedLineLayout>>, used_lines: Vec<Arc<CacheKey>>, used_wrapped_lines: Vec<Arc<CacheKey>>, } #[derive(Clone, Default)] pub(crate) struct LineLayoutIndex { lines_index: usize, wrapped_lines_index: usize, } impl LineLayoutCache { pub fn new(platform_text_system: Arc<dyn PlatformTextSystem>) -> Self { Self { previous_frame: Mutex::default(), current_frame: RwLock::default(), platform_text_system, } } pub fn layout_index(&self) -> LineLayoutIndex { let frame = self.current_frame.read(); LineLayoutIndex { lines_index: frame.used_lines.len(), wrapped_lines_index: frame.used_wrapped_lines.len(), } } pub fn reuse_layouts(&self, range: Range<LineLayoutIndex>) { let mut previous_frame = &mut *self.previous_frame.lock(); let mut current_frame = &mut *self.current_frame.write(); if let Some(cached_keys) = previous_frame .used_lines .get(range.start.lines_index..range.end.lines_index) { for key in cached_keys { if let Some((key, line)) = previous_frame.lines.remove_entry(key) { current_frame.lines.insert(key, line); } current_frame.used_lines.push(key.clone()); } } if let Some(cached_keys) = previous_frame .used_wrapped_lines .get(range.start.wrapped_lines_index..range.end.wrapped_lines_index) { for key in cached_keys { if let Some((key, line)) = previous_frame.wrapped_lines.remove_entry(key) { current_frame.wrapped_lines.insert(key, line); } current_frame.used_wrapped_lines.push(key.clone()); } } } pub fn truncate_layouts(&self, index: LineLayoutIndex) { let mut current_frame = &mut *self.current_frame.write(); current_frame.used_lines.truncate(index.lines_index); current_frame .used_wrapped_lines .truncate(index.wrapped_lines_index); } pub fn finish_frame(&self) { let mut prev_frame = self.previous_frame.lock(); let mut curr_frame = self.current_frame.write(); std::mem::swap(&mut *prev_frame, &mut *curr_frame); curr_frame.lines.clear(); curr_frame.wrapped_lines.clear(); curr_frame.used_lines.clear(); curr_frame.used_wrapped_lines.clear(); } pub fn layout_wrapped_line( &self, text: &str, font_size: Pixels, runs: &[FontRun], wrap_width: Option<Pixels>, ) -> Arc<WrappedLineLayout> { let key = &CacheKeyRef { text, font_size, runs, wrap_width, } as &dyn AsCacheKeyRef; let current_frame = self.current_frame.upgradable_read(); if let Some(layout) = current_frame.wrapped_lines.get(key) { return layout.clone(); } let previous_frame_entry = self.previous_frame.lock().wrapped_lines.remove_entry(key); if let Some((key, layout)) = previous_frame_entry { let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame); current_frame .wrapped_lines .insert(key.clone(), layout.clone()); current_frame.used_wrapped_lines.push(key); layout } else { drop(current_frame); let unwrapped_layout = self.layout_line(text, font_size, runs); let wrap_boundaries = if let Some(wrap_width) = wrap_width { unwrapped_layout.compute_wrap_boundaries(text.as_ref(), wrap_width) } else { SmallVec::new() }; let layout = Arc::new(WrappedLineLayout { unwrapped_layout, wrap_boundaries, wrap_width, }); let key = Arc::new(CacheKey { text: text.into(), font_size, runs: SmallVec::from(runs), wrap_width, }); let mut current_frame = self.current_frame.write(); current_frame .wrapped_lines .insert(key.clone(), layout.clone()); current_frame.used_wrapped_lines.push(key); layout } } pub fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> Arc<LineLayout> { let key = &CacheKeyRef { text, font_size, runs, wrap_width: None, } as &dyn AsCacheKeyRef; let current_frame = self.current_frame.upgradable_read(); if let Some(layout) = current_frame.lines.get(key) { return layout.clone(); }