diff options
Diffstat (limited to '')
| -rw-r--r-- | graphics/src/text.rs | 13 | ||||
| -rw-r--r-- | graphics/src/text/paragraph.rs | 160 | 
2 files changed, 108 insertions, 65 deletions
| diff --git a/graphics/src/text.rs b/graphics/src/text.rs index 30269e69..23ec14d4 100644 --- a/graphics/src/text.rs +++ b/graphics/src/text.rs @@ -232,13 +232,14 @@ impl PartialEq for Raw {  /// Measures the dimensions of the given [`cosmic_text::Buffer`].  pub fn measure(buffer: &cosmic_text::Buffer) -> Size { -    let (width, total_lines) = buffer -        .layout_runs() -        .fold((0.0, 0usize), |(width, total_lines), run| { -            (run.line_w.max(width), total_lines + 1) -        }); +    let (width, height) = +        buffer +            .layout_runs() +            .fold((0.0, 0.0), |(width, height), run| { +                (run.line_w.max(width), height + run.line_height) +            }); -    Size::new(width, total_lines as f32 * buffer.metrics().line_height) +    Size::new(width, height)  }  /// Returns the attributes of the given [`Font`]. diff --git a/graphics/src/text/paragraph.rs b/graphics/src/text/paragraph.rs index a5fefe8f..37fa97f2 100644 --- a/graphics/src/text/paragraph.rs +++ b/graphics/src/text/paragraph.rs @@ -1,8 +1,8 @@  //! Draw paragraphs.  use crate::core;  use crate::core::alignment; -use crate::core::text::{Hit, LineHeight, Shaping, Text}; -use crate::core::{Font, Pixels, Point, Size}; +use crate::core::text::{Hit, Shaping, Span, Text}; +use crate::core::{Font, Point, Size};  use crate::text;  use std::fmt; @@ -10,11 +10,11 @@ use std::sync::{self, Arc};  /// A bunch of text.  #[derive(Clone, PartialEq)] -pub struct Paragraph(Option<Arc<Internal>>); +pub struct Paragraph(Arc<Internal>); +#[derive(Clone)]  struct Internal {      buffer: cosmic_text::Buffer, -    content: String, // TODO: Reuse from `buffer` (?)      font: Font,      shaping: Shaping,      horizontal_alignment: alignment::Horizontal, @@ -52,9 +52,7 @@ impl Paragraph {      }      fn internal(&self) -> &Arc<Internal> { -        self.0 -            .as_ref() -            .expect("paragraph should always be initialized") +        &self.0      }  } @@ -62,7 +60,7 @@ impl core::text::Paragraph for Paragraph {      type Font = Font;      fn with_text(text: Text<&str>) -> Self { -        log::trace!("Allocating paragraph: {}", text.content); +        log::trace!("Allocating plain paragraph: {}", text.content);          let mut font_system =              text::font_system().write().expect("Write font system"); @@ -90,9 +88,8 @@ impl core::text::Paragraph for Paragraph {          let min_bounds = text::measure(&buffer); -        Self(Some(Arc::new(Internal { +        Self(Arc::new(Internal {              buffer, -            content: text.content.to_owned(),              font: text.font,              horizontal_alignment: text.horizontal_alignment,              vertical_alignment: text.vertical_alignment, @@ -100,59 +97,107 @@ impl core::text::Paragraph for Paragraph {              bounds: text.bounds,              min_bounds,              version: font_system.version(), -        }))) +        })) +    } + +    fn with_spans(text: Text<&[Span<'_>]>) -> Self { +        log::trace!("Allocating rich paragraph: {:?}", text.content); + +        let mut font_system = +            text::font_system().write().expect("Write font system"); + +        let mut buffer = cosmic_text::Buffer::new( +            font_system.raw(), +            cosmic_text::Metrics::new( +                text.size.into(), +                text.line_height.to_absolute(text.size).into(), +            ), +        ); + +        buffer.set_size( +            font_system.raw(), +            Some(text.bounds.width), +            Some(text.bounds.height), +        ); + +        buffer.set_rich_text( +            font_system.raw(), +            text.content.iter().map(|span| { +                let attrs = cosmic_text::Attrs::new(); + +                let attrs = if let Some(font) = span.font { +                    attrs +                        .family(text::to_family(font.family)) +                        .weight(text::to_weight(font.weight)) +                        .stretch(text::to_stretch(font.stretch)) +                        .style(text::to_style(font.style)) +                } else { +                    text::to_attributes(text.font) +                }; + +                let attrs = match (span.size, span.line_height) { +                    (None, None) => attrs, +                    _ => { +                        let size = span.size.unwrap_or(text.size); + +                        attrs.metrics(cosmic_text::Metrics::new( +                            size.into(), +                            span.line_height +                                .unwrap_or(text.line_height) +                                .to_absolute(size) +                                .into(), +                        )) +                    } +                }; + +                let attrs = if let Some(color) = span.color { +                    attrs.color(text::to_color(color)) +                } else { +                    attrs +                }; + +                (span.text.as_ref(), attrs) +            }), +            text::to_attributes(text.font), +            text::to_shaping(text.shaping), +        ); + +        let min_bounds = text::measure(&buffer); + +        Self(Arc::new(Internal { +            buffer, +            font: text.font, +            horizontal_alignment: text.horizontal_alignment, +            vertical_alignment: text.vertical_alignment, +            shaping: text.shaping, +            bounds: text.bounds, +            min_bounds, +            version: font_system.version(), +        }))      }      fn resize(&mut self, new_bounds: Size) { -        let paragraph = self -            .0 -            .take() -            .expect("paragraph should always be initialized"); - -        match Arc::try_unwrap(paragraph) { -            Ok(mut internal) => { -                let mut font_system = -                    text::font_system().write().expect("Write font system"); - -                internal.buffer.set_size( -                    font_system.raw(), -                    Some(new_bounds.width), -                    Some(new_bounds.height), -                ); - -                internal.bounds = new_bounds; -                internal.min_bounds = text::measure(&internal.buffer); - -                self.0 = Some(Arc::new(internal)); -            } -            Err(internal) => { -                let metrics = internal.buffer.metrics(); - -                // If there is a strong reference somewhere, we recompute the -                // buffer from scratch -                *self = Self::with_text(Text { -                    content: &internal.content, -                    bounds: internal.bounds, -                    size: Pixels(metrics.font_size), -                    line_height: LineHeight::Absolute(Pixels( -                        metrics.line_height, -                    )), -                    font: internal.font, -                    horizontal_alignment: internal.horizontal_alignment, -                    vertical_alignment: internal.vertical_alignment, -                    shaping: internal.shaping, -                }); -            } -        } +        let paragraph = Arc::make_mut(&mut self.0); + +        let mut font_system = +            text::font_system().write().expect("Write font system"); + +        paragraph.buffer.set_size( +            font_system.raw(), +            Some(new_bounds.width), +            Some(new_bounds.height), +        ); + +        paragraph.bounds = new_bounds; +        paragraph.min_bounds = text::measure(¶graph.buffer);      } -    fn compare(&self, text: Text<&str>) -> core::text::Difference { +    fn compare(&self, text: Text<()>) -> core::text::Difference {          let font_system = text::font_system().read().expect("Read font system");          let paragraph = self.internal();          let metrics = paragraph.buffer.metrics();          if paragraph.version != font_system.version -            || paragraph.content != text.content              || metrics.font_size != text.size.0              || metrics.line_height != text.line_height.to_absolute(text.size).0              || paragraph.font != text.font @@ -231,7 +276,7 @@ impl core::text::Paragraph for Paragraph {  impl Default for Paragraph {      fn default() -> Self { -        Self(Some(Arc::new(Internal::default()))) +        Self(Arc::new(Internal::default()))      }  } @@ -240,7 +285,6 @@ impl fmt::Debug for Paragraph {          let paragraph = self.internal();          f.debug_struct("Paragraph") -            .field("content", ¶graph.content)              .field("font", ¶graph.font)              .field("shaping", ¶graph.shaping)              .field("horizontal_alignment", ¶graph.horizontal_alignment) @@ -253,8 +297,7 @@ impl fmt::Debug for Paragraph {  impl PartialEq for Internal {      fn eq(&self, other: &Self) -> bool { -        self.content == other.content -            && self.font == other.font +        self.font == other.font              && self.shaping == other.shaping              && self.horizontal_alignment == other.horizontal_alignment              && self.vertical_alignment == other.vertical_alignment @@ -271,7 +314,6 @@ impl Default for Internal {                  font_size: 1.0,                  line_height: 1.0,              }), -            content: String::new(),              font: Font::default(),              shaping: Shaping::default(),              horizontal_alignment: alignment::Horizontal::Left, @@ -298,7 +340,7 @@ pub struct Weak {  impl Weak {      /// Tries to update the reference into a [`Paragraph`].      pub fn upgrade(&self) -> Option<Paragraph> { -        self.raw.upgrade().map(Some).map(Paragraph) +        self.raw.upgrade().map(Paragraph)      }  } | 
