Merge pull request #2526 from iced-rs/feature/rich-text-underline

Underline support for `rich_text`
This commit is contained in:
Héctor Ramón 2024-07-28 17:22:32 +02:00 committed by GitHub
commit ebc6c0eba8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 78 additions and 20 deletions

View file

@ -245,6 +245,8 @@ pub struct Span<'a, Link = (), Font = crate::Font> {
/// ///
/// Currently, it only affects the bounds of the [`Highlight`]. /// Currently, it only affects the bounds of the [`Highlight`].
pub padding: Padding, pub padding: Padding,
/// Whether the [`Span`] should be underlined or not.
pub underline: bool,
} }
/// A text highlight. /// A text highlight.
@ -268,6 +270,7 @@ impl<'a, Link, Font> Span<'a, Link, Font> {
highlight: None, highlight: None,
link: None, link: None,
padding: Padding::ZERO, padding: Padding::ZERO,
underline: false,
} }
} }
@ -386,6 +389,12 @@ impl<'a, Link, Font> Span<'a, Link, Font> {
self self
} }
/// Sets whether the [`Span`] shoud be underlined or not.
pub fn underline(mut self, underline: bool) -> Self {
self.underline = underline;
self
}
/// Turns the [`Span`] into a static one. /// Turns the [`Span`] into a static one.
pub fn to_static(self) -> Span<'static, Link, Font> { pub fn to_static(self) -> Span<'static, Link, Font> {
Span { Span {
@ -397,6 +406,7 @@ impl<'a, Link, Font> Span<'a, Link, Font> {
link: self.link, link: self.link,
highlight: self.highlight, highlight: self.highlight,
padding: self.padding, padding: self.padding,
underline: self.underline,
} }
} }
} }

View file

@ -237,7 +237,7 @@ where
theme: &Theme, theme: &Theme,
defaults: &renderer::Style, defaults: &renderer::Style,
layout: Layout<'_>, layout: Layout<'_>,
_cursor_position: mouse::Cursor, cursor: mouse::Cursor,
viewport: &Rectangle, viewport: &Rectangle,
) { ) {
let state = tree let state = tree
@ -246,29 +246,77 @@ where
let style = theme.style(&self.class); let style = theme.style(&self.class);
let hovered_span = cursor
.position_in(layout.bounds())
.and_then(|position| state.paragraph.hit_span(position));
for (index, span) in self.spans.iter().enumerate() { for (index, span) in self.spans.iter().enumerate() {
if let Some(highlight) = span.highlight { let is_hovered_link =
span.link.is_some() && Some(index) == hovered_span;
if span.highlight.is_some() || span.underline || is_hovered_link {
let translation = layout.position() - Point::ORIGIN; let translation = layout.position() - Point::ORIGIN;
let regions = state.paragraph.span_bounds(index);
for bounds in state.paragraph.span_bounds(index) { if let Some(highlight) = span.highlight {
let bounds = Rectangle::new( for bounds in &regions {
bounds.position() let bounds = Rectangle::new(
- Vector::new(span.padding.left, span.padding.top), bounds.position()
bounds.size() - Vector::new(
+ Size::new( span.padding.left,
span.padding.horizontal(), span.padding.top,
span.padding.vertical(), ),
), bounds.size()
); + Size::new(
span.padding.horizontal(),
span.padding.vertical(),
),
);
renderer.fill_quad( renderer.fill_quad(
renderer::Quad { renderer::Quad {
bounds: bounds + translation, bounds: bounds + translation,
border: highlight.border, border: highlight.border,
..Default::default() ..Default::default()
}, },
highlight.background, highlight.background,
); );
}
}
if span.underline || is_hovered_link {
let size = span
.size
.or(self.size)
.unwrap_or(renderer.default_size());
let line_height = span
.line_height
.unwrap_or(self.line_height)
.to_absolute(size);
for bounds in regions {
renderer.fill_quad(
renderer::Quad {
bounds: Rectangle::new(
bounds.position()
+ translation
+ Vector::new(
0.0,
size.0
+ (line_height.0 - size.0)
/ 2.0
- size.0 * 0.08,
),
Size::new(bounds.width, 1.0),
),
..Default::default()
},
span.color
.or(style.color)
.unwrap_or(defaults.text_color),
);
}
} }
} }
} }