Skip to content

Commit

Permalink
Add background styling to span / rich text
Browse files Browse the repository at this point in the history
  • Loading branch information
tarkah committed Jul 23, 2024
1 parent a8c772e commit 1feb29a
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 16 deletions.
4 changes: 4 additions & 0 deletions core/src/renderer/null.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ impl text::Paragraph for () {
fn hit_span(&self, _point: Point) -> Option<usize> {
None
}

fn span_bounds(&self, _index: usize) -> Vec<Rectangle> {
vec![]
}
}

impl text::Editor for () {
Expand Down
39 changes: 38 additions & 1 deletion core/src/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub use highlighter::Highlighter;
pub use paragraph::Paragraph;

use crate::alignment;
use crate::{Color, Pixels, Point, Rectangle, Size};
use crate::{Border, Color, Pixels, Point, Rectangle, Size};

use std::borrow::Cow;
use std::hash::{Hash, Hasher};
Expand Down Expand Up @@ -235,6 +235,8 @@ pub struct Span<'a, Link = (), Font = crate::Font> {
pub font: Option<Font>,
/// The [`Color`] of the [`Span`].
pub color: Option<Color>,
/// The [`Background`] of the [`Span`].
pub background: Option<Background>,
/// The link of the [`Span`].
pub link: Option<Link>,
}
Expand All @@ -248,6 +250,7 @@ impl<'a, Link, Font> Span<'a, Link, Font> {
line_height: None,
font: None,
color: None,
background: None,
link: None,
}
}
Expand Down Expand Up @@ -288,6 +291,21 @@ impl<'a, Link, Font> Span<'a, Link, Font> {
self
}

/// Sets the [`Background`] of the [`Span`].
pub fn background(mut self, background: impl Into<Background>) -> Self {
self.background = Some(background.into());
self
}

/// Sets the [`Background`] of the [`Span`], if any.
pub fn background_maybe(
mut self,
background: Option<impl Into<Background>>,
) -> Self {
self.background = background.map(Into::into);
self
}

/// Sets the link of the [`Span`].
pub fn link(mut self, link: impl Into<Link>) -> Self {
self.link = Some(link.into());
Expand All @@ -308,6 +326,7 @@ impl<'a, Link, Font> Span<'a, Link, Font> {
line_height: self.line_height,
font: self.font,
color: self.color,
background: self.background,
link: self.link,
}
}
Expand Down Expand Up @@ -406,3 +425,21 @@ into_fragment!(isize);

into_fragment!(f32);
into_fragment!(f64);

/// The background style of text
#[derive(Debug, Clone, Copy)]
pub struct Background {
/// The background [`Color`]
pub color: Color,
/// The background [`Border`]
pub border: Border,
}

impl From<Color> for Background {
fn from(color: Color) -> Self {
Background {
color,
border: Border::default(),
}
}
}
6 changes: 5 additions & 1 deletion core/src/text/paragraph.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Draw paragraphs.
use crate::alignment;
use crate::text::{Difference, Hit, Span, Text};
use crate::{Point, Size};
use crate::{Point, Rectangle, Size};

/// A text paragraph.
pub trait Paragraph: Sized + Default {
Expand Down Expand Up @@ -42,6 +42,10 @@ pub trait Paragraph: Sized + Default {
/// that was hit.
fn hit_span(&self, point: Point) -> Option<usize>;

/// Returns all bounds for the provided [`Span`] index of the [`Paragraph`].
/// A [`Span`] can have multiple bounds for each line it's on.
fn span_bounds(&self, index: usize) -> Vec<Rectangle>;

/// Returns the distance to the given grapheme index in the [`Paragraph`].
fn grapheme_position(&self, line: usize, index: usize) -> Option<Point>;

Expand Down
9 changes: 6 additions & 3 deletions examples/markdown/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@ impl Markdown {
(
Self {
content: text_editor::Content::with_text(INITIAL_CONTENT),
items: markdown::parse(INITIAL_CONTENT, theme.palette())
.collect(),
items: markdown::parse(
INITIAL_CONTENT,
theme.extended_palette(),
)
.collect(),
theme,
},
widget::focus_next(),
Expand All @@ -46,7 +49,7 @@ impl Markdown {
if is_edit {
self.items = markdown::parse(
&self.content.text(),
self.theme.palette(),
self.theme.extended_palette(),
)
.collect();
}
Expand Down
53 changes: 52 additions & 1 deletion graphics/src/text/paragraph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use crate::core;
use crate::core::alignment;
use crate::core::text::{Hit, Shaping, Span, Text};
use crate::core::{Font, Point, Size};
use crate::core::{Font, Point, Rectangle, Size};
use crate::text;

use std::fmt;
Expand Down Expand Up @@ -251,6 +251,57 @@ impl core::text::Paragraph for Paragraph {
Some(glyph.metadata)
}

fn span_bounds(&self, index: usize) -> Vec<Rectangle> {
let internal = self.internal();

let mut current_bounds = None;

let mut spans = internal
.buffer
.layout_runs()
.flat_map(|run| {
let line_top = run.line_top;
let line_height = run.line_height;

run.glyphs
.iter()
.map(move |glyph| (line_top, line_height, glyph))
})
.skip_while(|(_, _, glyph)| glyph.metadata != index)
.take_while(|(_, _, glyph)| glyph.metadata == index)
.fold(vec![], |mut spans, (line_top, line_height, glyph)| {
let y = line_top + glyph.y;

let new_bounds = || {
Rectangle::new(
Point::new(glyph.x, y),
Size::new(
glyph.w,
glyph.line_height_opt.unwrap_or(line_height),
),
)
};

match current_bounds.as_mut() {
None => {
current_bounds = Some(new_bounds());
}
Some(bounds) if y != bounds.y => {
spans.push(*bounds);
*bounds = new_bounds();
}
Some(bounds) => {
bounds.width += glyph.w;
}
}

spans
});

spans.extend(current_bounds);
spans
}

fn grapheme_position(&self, line: usize, index: usize) -> Option<Point> {
use unicode_segmentation::UnicodeSegmentation;

Expand Down
24 changes: 16 additions & 8 deletions widget/src/markdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@
//! in code blocks.
//!
//! Only the variants of [`Item`] are currently supported.
use crate::core::border;
use crate::core::font::{self, Font};
use crate::core::padding;
use crate::core::theme::{self, Theme};
use crate::core::text::Background;
use crate::core::theme::palette;
use crate::core::theme::Theme;
use crate::core::{self, Element, Length, Pixels};
use crate::{column, container, rich_text, row, scrollable, span, text};

Expand Down Expand Up @@ -34,10 +37,10 @@ pub enum Item {
}

/// Parse the given Markdown content.
pub fn parse(
markdown: &str,
palette: theme::Palette,
) -> impl Iterator<Item = Item> + '_ {
pub fn parse<'a>(
markdown: &'a str,
palette: &'a palette::Extended,
) -> impl Iterator<Item = Item> + 'a {
struct List {
start: Option<u64>,
items: Vec<Vec<Item>>,
Expand Down Expand Up @@ -247,7 +250,7 @@ pub fn parse(
};

let span = if let Some(link) = link.as_ref() {
span.color(palette.primary).link(link.clone())
span.color(palette.primary.base.color).link(link.clone())
} else {
span
};
Expand All @@ -257,10 +260,15 @@ pub fn parse(
None
}
pulldown_cmark::Event::Code(code) if !metadata && !table => {
let span = span(code.into_string()).font(Font::MONOSPACE);
let span = span(code.into_string())
.font(Font::MONOSPACE)
.background(Background {
color: palette.background.weak.color,
border: border::rounded(2),
});

let span = if let Some(link) = link.as_ref() {
span.color(palette.primary).link(link.clone())
span.color(palette.primary.base.color).link(link.clone())
} else {
span
};
Expand Down
22 changes: 20 additions & 2 deletions widget/src/text/rich.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ use crate::core::widget::text::{
};
use crate::core::widget::tree::{self, Tree};
use crate::core::{
self, Clipboard, Color, Element, Event, Layout, Length, Pixels, Rectangle,
Shell, Size, Widget,
self, Clipboard, Color, Element, Event, Layout, Length, Pixels, Point,
Rectangle, Shell, Size, Widget,
};

use std::borrow::Cow;
Expand Down Expand Up @@ -270,6 +270,24 @@ where

let style = theme.style(&self.class);

// Draw backgrounds
for (index, span) in self.spans.iter().enumerate() {
if let Some(background) = span.background {
let translation = layout.position() - Point::ORIGIN;

for bounds in state.paragraph.span_bounds(index) {
renderer.fill_quad(
renderer::Quad {
bounds: bounds + translation,
border: background.border,
..Default::default()
},
background.color,
);
}
}
}

text::draw(
renderer,
defaults,
Expand Down

0 comments on commit 1feb29a

Please sign in to comment.