Fix rich_text reactive rendering when hovering links

This commit is contained in:
Héctor Ramón Jiménez 2025-02-01 01:50:55 +01:00
parent eb81679e60
commit 7493b83031
No known key found for this signature in database
GPG key ID: 7CC46565708259A7

View file

@ -30,6 +30,7 @@ where
align_y: alignment::Vertical,
wrapping: Wrapping,
class: Theme::Class<'a>,
hovered_link: Option<usize>,
}
impl<'a, Link, Theme, Renderer> Rich<'a, Link, Theme, Renderer>
@ -52,6 +53,7 @@ where
align_y: alignment::Vertical::Top,
wrapping: Wrapping::default(),
class: Theme::default(),
hovered_link: None,
}
}
@ -236,7 +238,7 @@ where
theme: &Theme,
defaults: &renderer::Style,
layout: Layout<'_>,
cursor: mouse::Cursor,
_cursor: mouse::Cursor,
viewport: &Rectangle,
) {
if !layout.bounds().intersects(viewport) {
@ -249,13 +251,8 @@ where
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.as_ref().as_ref().iter().enumerate() {
let is_hovered_link =
span.link.is_some() && Some(index) == hovered_span;
let is_hovered_link = Some(index) == self.hovered_link;
if span.highlight.is_some()
|| span.underline
@ -369,39 +366,45 @@ where
shell: &mut Shell<'_, Link>,
_viewport: &Rectangle,
) {
let was_hovered = self.hovered_link.is_some();
if let Some(position) = cursor.position_in(layout.bounds()) {
let state = tree
.state
.downcast_ref::<State<Link, Renderer::Paragraph>>();
self.hovered_link =
state.paragraph.hit_span(position).and_then(|span| {
if self.spans.as_ref().as_ref().get(span)?.link.is_some() {
Some(span)
} else {
None
}
});
} else {
self.hovered_link = None;
}
if was_hovered != self.hovered_link.is_some() {
shell.request_redraw();
}
match event {
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => {
if let Some(position) = cursor.position_in(layout.bounds()) {
let state = tree
.state
.downcast_mut::<State<Link, Renderer::Paragraph>>();
if let Some(span) = state.paragraph.hit_span(position) {
if self
.spans
.as_ref()
.as_ref()
.get(span)
.is_some_and(|span| span.link.is_some())
{
state.span_pressed = Some(span);
state.span_pressed = self.hovered_link;
shell.capture_event();
}
}
}
}
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) => {
let state = tree
.state
.downcast_mut::<State<Link, Renderer::Paragraph>>();
if let Some(span_pressed) = state.span_pressed {
state.span_pressed = None;
if let Some(position) = cursor.position_in(layout.bounds())
{
match state.paragraph.hit_span(position) {
Some(span) if span == span_pressed => {
match state.span_pressed {
Some(span) if Some(span) == self.hovered_link => {
if let Some(link) = self
.spans
.as_ref()
@ -414,8 +417,8 @@ where
}
_ => {}
}
}
}
state.span_pressed = None;
}
_ => {}
}
@ -423,30 +426,18 @@ where
fn mouse_interaction(
&self,
tree: &Tree,
layout: Layout<'_>,
cursor: mouse::Cursor,
_tree: &Tree,
_layout: Layout<'_>,
_cursor: mouse::Cursor,
_viewport: &Rectangle,
_renderer: &Renderer,
) -> mouse::Interaction {
if let Some(position) = cursor.position_in(layout.bounds()) {
let state = tree
.state
.downcast_ref::<State<Link, Renderer::Paragraph>>();
if let Some(span) = state
.paragraph
.hit_span(position)
.and_then(|span| self.spans.as_ref().as_ref().get(span))
{
if span.link.is_some() {
return mouse::Interaction::Pointer;
}
}
}
if self.hovered_link.is_some() {
mouse::Interaction::Pointer
} else {
mouse::Interaction::None
}
}
}
fn layout<Link, Renderer>(