diff --git a/examples/editor-libcosmic/src/text.rs b/examples/editor-libcosmic/src/text.rs index cc53054..40a07ad 100644 --- a/examples/editor-libcosmic/src/text.rs +++ b/examples/editor-libcosmic/src/text.rs @@ -45,7 +45,7 @@ impl StyleSheet for Theme { } pub struct Text { - line: BufferLine<'static>, + line: BufferLine, metrics: Metrics, } @@ -223,25 +223,25 @@ pub fn draw_pixel( // Do not draw if alpha is zero return; } - + if y < 0 || y >= height { // Skip if y out of bounds return; } - + if x < 0 || x >= width { // Skip if x out of bounds return; } - + let offset = (y as usize * width as usize + x as usize) * 4; - + let mut current = buffer[offset] as u32 | (buffer[offset + 1] as u32) << 8 | (buffer[offset + 2] as u32) << 16 | (buffer[offset + 3] as u32) << 24; - + if alpha >= 255 || current == 0 { // Alpha is 100% or current is null, replace with no blending current = color.0; @@ -253,7 +253,7 @@ pub fn draw_pixel( + (alpha * (0x01000000 | ((color.0 & 0x0000FF00) >> 8))); current = (rb & 0x00FF00FF) | (ag & 0xFF00FF00); } - + buffer[offset] = current as u8; buffer[offset + 1] = (current >> 8) as u8; buffer[offset + 2] = (current >> 16) as u8; diff --git a/src/attrs.rs b/src/attrs.rs index 4392192..0282a7e 100644 --- a/src/attrs.rs +++ b/src/attrs.rs @@ -51,6 +51,40 @@ impl Color { } } +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub enum FamilyOwned { + Name(String), + Serif, + SansSerif, + Cursive, + Fantasy, + Monospace, +} + +impl FamilyOwned { + pub fn new(family: Family) -> Self { + match family { + Family::Name(name) => FamilyOwned::Name(name.to_string()), + Family::Serif => FamilyOwned::Serif, + Family::SansSerif => FamilyOwned::SansSerif, + Family::Cursive => FamilyOwned::Cursive, + Family::Fantasy => FamilyOwned::Fantasy, + Family::Monospace => FamilyOwned::Monospace, + } + } + + pub fn as_family(&self) -> Family { + match self { + FamilyOwned::Name(name) => Family::Name(&name), + FamilyOwned::Serif => Family::Serif, + FamilyOwned::SansSerif => Family::SansSerif, + FamilyOwned::Cursive => Family::Cursive, + FamilyOwned::Fantasy => Family::Fantasy, + FamilyOwned::Monospace => Family::Monospace, + } + } +} + /// Text attributes #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct Attrs<'a> { @@ -136,30 +170,65 @@ impl<'a> Attrs<'a> { } } +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct AttrsOwned { + //TODO: should this be an option? + pub color_opt: Option, + pub family_owned: FamilyOwned, + pub monospaced: bool, + pub stretch: Stretch, + pub style: Style, + pub weight: Weight, +} + +impl AttrsOwned { + pub fn new(attrs: Attrs) -> Self { + Self { + color_opt: attrs.color_opt, + family_owned: FamilyOwned::new(attrs.family), + monospaced: attrs.monospaced, + stretch: attrs.stretch, + style: attrs.style, + weight: attrs.weight, + } + } + + pub fn as_attrs(&self) -> Attrs { + Attrs { + color_opt: self.color_opt, + family: self.family_owned.as_family(), + monospaced: self.monospaced, + stretch: self.stretch, + style: self.style, + weight: self.weight, + } + } +} + /// List of text attributes to apply to a line //TODO: have this clean up the spans when changes are made #[derive(Eq, PartialEq)] -pub struct AttrsList<'a> { - defaults: Attrs<'a>, - spans: Vec<(Range, Attrs<'a>)>, +pub struct AttrsList { + defaults: AttrsOwned, + spans: Vec<(Range, AttrsOwned)>, } -impl<'a> AttrsList<'a> { +impl AttrsList { /// Create a new attributes list with a set of default [Attrs] - pub fn new(defaults: Attrs<'a>) -> Self { + pub fn new(defaults: Attrs) -> Self { Self { - defaults, + defaults: AttrsOwned::new(defaults), spans: Vec::new(), } } /// Get the default [Attrs] - pub fn defaults(&self) -> Attrs<'a> { - self.defaults + pub fn defaults(&self) -> Attrs { + self.defaults.as_attrs() } /// Get the current attribute spans - pub fn spans(&self) -> &Vec<(Range, Attrs<'a>)> { + pub fn spans(&self) -> &Vec<(Range, AttrsOwned)> { &self.spans } @@ -169,7 +238,7 @@ impl<'a> AttrsList<'a> { } /// Add an attribute span, removes any previous matching parts of spans - pub fn add_span(&mut self, range: Range, attrs: Attrs<'a>) { + pub fn add_span(&mut self, range: Range, attrs: Attrs) { //do not support 1..1 even if by accident. if range.start == range.end { return; @@ -191,7 +260,7 @@ impl<'a> AttrsList<'a> { rework_spans.push((rework.0.start..range.start, rework.1)) } else if self.spans[i].0.end > range.end && self.spans[i].0.start < range.start { let rework = self.spans.remove(i); - rework_spans.push((rework.0.start..range.start, rework.1)); + rework_spans.push((rework.0.start..range.start, rework.1.clone())); rework_spans.push((range.end..rework.0.end, rework.1)); } else if self.spans[i].0.start > range.end { break; @@ -208,7 +277,7 @@ impl<'a> AttrsList<'a> { // Combine span if possible let mut combined = false; for span in self.spans.iter_mut() { - if span.1 != attrs { + if span.1.as_attrs() != attrs { // Ignore not matching attrs continue; } @@ -230,7 +299,7 @@ impl<'a> AttrsList<'a> { if ! combined { //Finally lets add the new span. it should fit now. - self.spans.push((range, attrs)); + self.spans.push((range, AttrsOwned::new(attrs))); } //sort by start to speed up further additions @@ -240,18 +309,18 @@ impl<'a> AttrsList<'a> { /// Get the highest priority attribute span for a range /// /// This returns the first span that contains the range - pub fn get_span(&self, range: Range) -> Attrs<'a> { + pub fn get_span(&self, range: Range) -> Attrs { for span in self.spans.iter() { if range.start >= span.0.start && range.end <= span.0.end { - return span.1; + return span.1.as_attrs(); } } - self.defaults + self.defaults.as_attrs() } /// Split attributes list at an offset pub fn split_off(&mut self, index: usize) -> Self { - let mut new = Self::new(self.defaults); + let mut new = Self::new(self.defaults.as_attrs()); let mut i = 0; while i < self.spans.len() { if self.spans[i].0.end <= index { @@ -268,7 +337,7 @@ impl<'a> AttrsList<'a> { // New span has index..end new.spans.push(( 0..self.spans[i].0.end - index, - self.spans[i].1 + self.spans[i].1.clone() )); // Old span has start..index self.spans[i].0.end = index; diff --git a/src/buffer.rs b/src/buffer.rs index d030bc6..0ecd3c0 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -141,7 +141,7 @@ impl fmt::Display for Metrics { pub struct Buffer<'a> { font_system: &'a FontSystem<'a>, /// Lines (or paragraphs) of text in the buffer - pub lines: Vec>, + pub lines: Vec, metrics: Metrics, width: i32, height: i32, diff --git a/src/buffer_line.rs b/src/buffer_line.rs index 673d968..611114d 100644 --- a/src/buffer_line.rs +++ b/src/buffer_line.rs @@ -1,20 +1,20 @@ use crate::{AttrsList, FontSystem, LayoutLine, ShapeLine}; /// A line (or paragraph) of text that is shaped and laid out -pub struct BufferLine<'a> { +pub struct BufferLine { //TODO: make this not pub(crate) text: String, - attrs_list: AttrsList<'a>, + attrs_list: AttrsList, wrap_simple: bool, shape_opt: Option, layout_opt: Option>, } -impl<'a> BufferLine<'a> { +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<'a>) -> Self { + pub fn new>(text: T, attrs_list: AttrsList) -> Self { Self { text: text.into(), attrs_list, @@ -33,7 +33,7 @@ impl<'a> BufferLine<'a> { /// /// Will reset shape and layout if it differs from current text and attributes list. /// Returns true if the line was reset - pub fn set_text + Into>(&mut self, text: T, attrs_list: AttrsList<'a>) -> bool { + pub fn set_text + Into>(&mut self, text: T, attrs_list: AttrsList) -> bool { if text.as_ref() != &self.text || attrs_list != self.attrs_list { self.text = text.into(); self.attrs_list = attrs_list; @@ -45,7 +45,7 @@ impl<'a> BufferLine<'a> { } /// Get attributes list - pub fn attrs_list(&self) -> &AttrsList<'a> { + pub fn attrs_list(&self) -> &AttrsList { &self.attrs_list } @@ -53,7 +53,7 @@ impl<'a> BufferLine<'a> { /// /// Will reset shape and layout if it differs from current attributes list. /// Returns true if the line was reset - pub fn set_attrs_list(&mut self, attrs_list: AttrsList<'a>) -> bool { + pub fn set_attrs_list(&mut self, attrs_list: AttrsList) -> bool { if attrs_list != self.attrs_list { self.attrs_list = attrs_list; self.reset(); @@ -97,7 +97,7 @@ impl<'a> BufferLine<'a> { for (other_range, attrs) in other.attrs_list.spans() { // Add previous attrs spans let range = other_range.start + len..other_range.end + len; - self.attrs_list.add_span(range, *attrs); + self.attrs_list.add_span(range, attrs.as_attrs()); } self.reset(); @@ -132,7 +132,7 @@ impl<'a> BufferLine<'a> { } /// Shape line, will cache results - pub fn shape(&mut self, font_system: &'a FontSystem<'a>) -> &ShapeLine { + pub fn shape<'a>(&mut self, font_system: &'a FontSystem<'a>) -> &ShapeLine { if self.shape_opt.is_none() { self.shape_opt = Some(ShapeLine::new(font_system, &self.text, &self.attrs_list)); self.layout_opt = None; @@ -146,7 +146,7 @@ impl<'a> BufferLine<'a> { } /// Layout line, will cache results - pub fn layout(&mut self, font_system: &'a FontSystem<'a>, font_size: i32, width: i32) -> &[LayoutLine] { + pub fn layout<'a>(&mut self, font_system: &'a FontSystem<'a>, font_size: i32, width: i32) -> &[LayoutLine] { if self.layout_opt.is_none() { let wrap_simple = self.wrap_simple; let shape = self.shape(font_system); diff --git a/src/font/system.rs b/src/font/system.rs index 07d1ef5..b8902ee 100644 --- a/src/font/system.rs +++ b/src/font/system.rs @@ -5,14 +5,14 @@ use std::{ sync::{Arc, Mutex}, }; -use crate::{Attrs, Font, FontMatches}; +use crate::{Attrs, AttrsOwned, Font, FontMatches}; /// Access system fonts pub struct FontSystem<'a> { pub locale: String, pub db: fontdb::Database, pub font_cache: Mutex>>>>, - pub font_matches_cache: Mutex, Arc>>>, + pub font_matches_cache: Mutex>>>, } impl<'a> FontSystem<'a> { @@ -79,9 +79,10 @@ impl<'a> FontSystem<'a> { }).clone() } - pub fn get_font_matches(&'a self, attrs: Attrs<'a>) -> Arc> { + pub fn get_font_matches(&'a self, attrs: Attrs) -> Arc> { let mut font_matches_cache = self.font_matches_cache.lock().unwrap(); - font_matches_cache.entry(attrs).or_insert_with(|| { + //TODO: do not create AttrsOwned unless entry does not already exist + font_matches_cache.entry(AttrsOwned::new(attrs)).or_insert_with(|| { let now = std::time::Instant::now(); let mut fonts = Vec::new(); diff --git a/src/shape.rs b/src/shape.rs index 8e96636..d88309b 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -104,7 +104,7 @@ fn shape_fallback( fn shape_run<'a>( font_system: &'a FontSystem<'a>, line: &str, - attrs_list: &AttrsList<'a>, + attrs_list: &AttrsList, start_run: usize, end_run: usize, span_rtl: bool, @@ -283,7 +283,7 @@ impl ShapeWord { pub fn new<'a>( font_system: &'a FontSystem<'a>, line: &str, - attrs_list: &AttrsList<'a>, + attrs_list: &AttrsList, start_word: usize, end_word: usize, span_rtl: bool, @@ -346,7 +346,7 @@ impl ShapeSpan { pub fn new<'a>( font_system: &'a FontSystem<'a>, line: &str, - attrs_list: &AttrsList<'a>, + attrs_list: &AttrsList, start_span: usize, end_span: usize, line_rtl: bool, @@ -426,7 +426,7 @@ impl ShapeLine { pub fn new<'a>( font_system: &'a FontSystem<'a>, line: &str, - attrs_list: &AttrsList<'a> + attrs_list: &AttrsList ) -> Self { let mut spans = Vec::new();