diff --git a/src/buffer.rs b/src/buffer.rs index 123ce38..fd3df69 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -331,7 +331,7 @@ impl Buffer { redraw: false, wrap: Wrap::Word, }; - buffer.set_text(font_system, "", Attrs::new()); + buffer.set_text(font_system, "", Attrs::new(), true); buffer } @@ -562,16 +562,28 @@ impl Buffer { } /// Set text of buffer, using provided attributes for each line by default - pub fn set_text(&mut self, font_system: &mut FontSystem, text: &str, attrs: Attrs) { + pub fn set_text( + &mut self, + font_system: &mut FontSystem, + text: &str, + attrs: Attrs, + skip_shaping: bool, + ) { self.lines.clear(); for line in text.lines() { - self.lines - .push(BufferLine::new(line.to_string(), AttrsList::new(attrs))); + self.lines.push(BufferLine::new( + line.to_string(), + AttrsList::new(attrs), + skip_shaping, + )); } // Make sure there is always one line if self.lines.is_empty() { - self.lines - .push(BufferLine::new(String::new(), AttrsList::new(attrs))); + self.lines.push(BufferLine::new( + String::new(), + AttrsList::new(attrs), + skip_shaping, + )); } self.scroll = 0; @@ -769,8 +781,9 @@ impl<'a> BorrowedWithFontSystem<'a, Buffer> { } /// Set text of buffer, using provided attributes for each line by default - pub fn set_text(&mut self, text: &str, attrs: Attrs) { - self.inner.set_text(self.font_system, text, attrs); + pub fn set_text(&mut self, text: &str, attrs: Attrs, skip_shaping: bool) { + self.inner + .set_text(self.font_system, text, attrs, skip_shaping); } /// Draw the buffer diff --git a/src/buffer_line.rs b/src/buffer_line.rs index fb808f0..2bedd8b 100644 --- a/src/buffer_line.rs +++ b/src/buffer_line.rs @@ -12,13 +12,14 @@ pub struct BufferLine { align: Option, shape_opt: Option, layout_opt: Option>, + skip_shaping: bool, } impl BufferLine { /// Create a new line with the given text and attributes list /// Cached shaping and layout can be done using the [`Self::shape`] and /// [`Self::layout`] functions - pub fn new>(text: T, attrs_list: AttrsList) -> Self { + pub fn new>(text: T, attrs_list: AttrsList, skip_shaping: bool) -> Self { Self { text: text.into(), attrs_list, @@ -26,6 +27,7 @@ impl BufferLine { align: None, shape_opt: None, layout_opt: None, + skip_shaping, } } @@ -142,7 +144,7 @@ impl BufferLine { let attrs_list = self.attrs_list.split_off(index); self.reset(); - let mut new = Self::new(text, attrs_list); + let mut new = Self::new(text, attrs_list, self.skip_shaping); new.wrap = self.wrap; new } @@ -167,7 +169,12 @@ impl BufferLine { /// Shape line, will cache results pub fn shape(&mut self, font_system: &mut FontSystem) -> &ShapeLine { if self.shape_opt.is_none() { - self.shape_opt = Some(ShapeLine::new(font_system, &self.text, &self.attrs_list)); + self.shape_opt = Some(ShapeLine::new( + font_system, + &self.text, + &self.attrs_list, + self.skip_shaping, + )); self.layout_opt = None; } self.shape_opt.as_ref().expect("shape not found") diff --git a/src/edit/editor.rs b/src/edit/editor.rs index 1b82018..2a1d8a6 100644 --- a/src/edit/editor.rs +++ b/src/edit/editor.rs @@ -245,6 +245,7 @@ impl Edit for Editor { .strip_suffix(char::is_control) .unwrap_or(data_line), these_attrs, + false, )); } else { panic!("str::lines() did not yield any elements"); @@ -256,6 +257,7 @@ impl Edit for Editor { .strip_suffix(char::is_control) .unwrap_or(data_line), final_attrs.split_off(remaining_split_len), + false, ); tmp.append(after); self.buffer.lines.insert(insert_line, tmp); @@ -270,6 +272,7 @@ impl Edit for Editor { .strip_suffix(char::is_control) .unwrap_or(data_line), final_attrs.split_off(remaining_split_len), + false, ); self.buffer.lines.insert(insert_line, tmp); self.cursor.line += 1; diff --git a/src/lib.rs b/src/lib.rs index 7c98dcb..fd12af8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,7 +36,7 @@ //! let attrs = Attrs::new(); //! //! // Add some text! -//! buffer.set_text("Hello, Rust! 🦀\n", attrs); +//! buffer.set_text("Hello, Rust! 🦀\n", attrs, false); //! //! // Perform shaping as desired //! buffer.shape_until_scroll(); diff --git a/src/shape.rs b/src/shape.rs index d3ba9bd..fcb259b 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -213,6 +213,49 @@ fn shape_run( glyphs } +fn shape_skip( + font_system: &mut FontSystem, + line: &str, + attrs_list: &AttrsList, + start_run: usize, + end_run: usize, +) -> Vec { + let attrs = attrs_list.get_span(start_run); + let fonts = font_system.get_font_matches(attrs); + + let default_families = [&attrs.family]; + let mut font_iter = FontFallbackIter::new(font_system, &fonts, &default_families, vec![]); + + let font = font_iter.next().expect("no default font found"); + let font_id = font.id(); + let font = font.as_swash(); + + let charmap = font.charmap(); + let glyph_metrics = font.glyph_metrics(&[]).scale(1.0); + + line[start_run..end_run] + .chars() + .enumerate() + .map(|(i, codepoint)| { + let glyph_id = charmap.map(codepoint); + let x_advance = glyph_metrics.advance_width(glyph_id); + + ShapeGlyph { + start: i, + end: i + 1, + x_advance, + y_advance: 0.0, + x_offset: 0.0, + y_offset: 0.0, + font_id, + glyph_id, + color_opt: attrs.color_opt, + metadata: attrs.metadata, + } + }) + .collect() +} + /// A shaped glyph pub struct ShapeGlyph { pub start: usize, @@ -278,6 +321,7 @@ impl ShapeWord { word_range: Range, level: unicode_bidi::Level, blank: bool, + skip_shaping: bool, ) -> Self { let word = &line[word_range.clone()]; @@ -297,14 +341,24 @@ impl ShapeWord { let attrs_egc = attrs_list.get_span(start_egc); if !attrs.compatible(&attrs_egc) { //TODO: more efficient - glyphs.append(&mut shape_run( - font_system, - line, - attrs_list, - start_run, - start_egc, - span_rtl, - )); + if skip_shaping { + glyphs.append(&mut shape_skip( + font_system, + line, + attrs_list, + start_run, + start_egc, + )); + } else { + glyphs.append(&mut shape_run( + font_system, + line, + attrs_list, + start_run, + start_egc, + span_rtl, + )); + }; start_run = start_egc; attrs = attrs_egc; @@ -312,14 +366,24 @@ impl ShapeWord { } if start_run < word_range.end { //TODO: more efficient - glyphs.append(&mut shape_run( - font_system, - line, - attrs_list, - start_run, - word_range.end, - span_rtl, - )); + if skip_shaping { + glyphs.append(&mut shape_skip( + font_system, + line, + attrs_list, + start_run, + word_range.end, + )); + } else { + glyphs.append(&mut shape_run( + font_system, + line, + attrs_list, + start_run, + word_range.end, + span_rtl, + )); + } } let mut x_advance = 0.0; @@ -352,6 +416,7 @@ impl ShapeSpan { span_range: Range, line_rtl: bool, level: unicode_bidi::Level, + skip_shaping: bool, ) -> Self { let span = &line[span_range.start..span_range.end]; @@ -382,6 +447,7 @@ impl ShapeSpan { (span_range.start + start_word)..(span_range.start + start_lb), level, false, + skip_shaping, )); } if start_lb < end_lb { @@ -395,6 +461,7 @@ impl ShapeSpan { ..(span_range.start + start_lb + i + c.len_utf8()), level, true, + skip_shaping, )); } } @@ -437,7 +504,12 @@ impl ShapeLine { /// # Panics /// /// Will panic if `line` contains more than one paragraph. - pub fn new(font_system: &mut FontSystem, line: &str, attrs_list: &AttrsList) -> Self { + pub fn new( + font_system: &mut FontSystem, + line: &str, + attrs_list: &AttrsList, + skip_shaping: bool, + ) -> Self { let mut spans = Vec::new(); let bidi = unicode_bidi::BidiInfo::new(line, None); @@ -473,6 +545,7 @@ impl ShapeLine { start..i, line_rtl, run_level, + skip_shaping, )); start = i; run_level = new_level; @@ -485,6 +558,7 @@ impl ShapeLine { start..line_range.end, line_rtl, run_level, + skip_shaping, )); line_rtl };