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