596 lines · 18.5 KB · Rust
RawDownload
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.