diff options
| author | Polesznyák Márk <contact@pml68.dev> | 2025-10-31 23:57:50 +0100 |
|---|---|---|
| committer | Polesznyák Márk <contact@pml68.dev> | 2025-11-03 17:35:42 +0100 |
| commit | f560a25f2867ad1d40752313eed712299850e2b6 (patch) | |
| tree | 3a2dda76042ba515fc146e02e876ba52e3025836 /src | |
| parent | feat: add `span!` macro (same as `text!`) (diff) | |
| download | iced_selection-f560a25f2867ad1d40752313eed712299850e2b6.tar.gz | |
feat(rich): add `on_link_hover` and `on_hover_lost`
Diffstat (limited to '')
| -rw-r--r-- | src/text/rich.rs | 75 |
1 files changed, 63 insertions, 12 deletions
diff --git a/src/text/rich.rs b/src/text/rich.rs index 3268160..a30663b 100644 --- a/src/text/rich.rs +++ b/src/text/rich.rs @@ -40,8 +40,9 @@ pub struct Rich< align_y: alignment::Vertical, wrapping: Wrapping, class: Theme::Class<'a>, - hovered_link: Option<usize>, on_link_click: Option<Box<dyn Fn(Link) -> Message + 'a>>, + on_link_hover: Option<Box<dyn Fn(Link) -> Message + 'a>>, + on_hover_lost: Option<Box<dyn Fn() -> Message + 'a>>, } impl<'a, Link, Message, Theme, Renderer> @@ -65,8 +66,9 @@ where align_y: alignment::Vertical::Top, wrapping: Wrapping::default(), class: Theme::default(), - hovered_link: None, on_link_click: None, + on_link_hover: None, + on_hover_lost: None, } } @@ -147,6 +149,36 @@ where self } + /// Sets the message that will be produced when a link of the [`Rich`] text + /// is hovered. + pub fn on_link_hover( + mut self, + on_link_hovered: impl Fn(Link) -> Message + 'a, + ) -> Self { + self.on_link_hover = Some(Box::new(on_link_hovered)); + self + } + + /// Sets the message that will be produced when a link of the [`Rich`] text + /// is no longer hovered. + pub fn on_hover_lost(mut self, on_hover_lost: Message) -> Self + where + Message: Clone + 'a, + { + self.on_hover_lost = Some(Box::new(move || on_hover_lost.clone())); + self + } + + /// Sets the message that will be produced when a link of the [`Rich`] text + /// is no longer hovered. + pub fn on_hover_lost_with( + mut self, + on_hover_lost: impl Fn() -> Message + 'a, + ) -> Self { + self.on_hover_lost = Some(Box::new(on_hover_lost)); + self + } + /// Sets the default style of the [`Rich`] text. #[must_use] pub fn style(mut self, style: impl Fn(&Theme) -> Style + 'a) -> Self @@ -181,6 +213,7 @@ where struct State<Link> { spans: Vec<Span<'static, Link, Font>>, span_pressed: Option<usize>, + hovered_link: Option<usize>, paragraph: Paragraph, is_hovered: bool, selection: Selection, @@ -335,6 +368,7 @@ where tree::State::new(State::<Link> { spans: Vec::new(), span_pressed: None, + hovered_link: None, paragraph: Paragraph::default(), is_hovered: false, selection: Selection::default(), @@ -393,7 +427,7 @@ where for (index, span) in self.spans.as_ref().as_ref().iter().enumerate() { let is_hovered_link = self.on_link_click.is_some() - && Some(index) == self.hovered_link; + && Some(index) == state.hovered_link; if span.highlight.is_some() || span.underline @@ -572,14 +606,14 @@ where return; } - let link_was_hovered = self.hovered_link.is_some(); let was_hovered = state.is_hovered; + let hovered_link_before = state.hovered_link; let selection_before = state.selection; state.is_hovered = click_position.is_some(); if let Some(position) = click_position { - self.hovered_link = + state.hovered_link = state.paragraph.hit_span(position).and_then(|span| { if self.spans.as_ref().as_ref().get(span)?.link.is_some() { Some(span) @@ -588,14 +622,14 @@ where } }); } else { - self.hovered_link = None; + state.hovered_link = None; } match event { Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) | Event::Touch(touch::Event::FingerPressed { .. }) => { - if self.hovered_link.is_some() { - state.span_pressed = self.hovered_link; + if state.hovered_link.is_some() { + state.span_pressed = state.hovered_link; shell.capture_event(); } @@ -653,7 +687,7 @@ where ) && state.selection.is_empty() { match state.span_pressed { - Some(span) if Some(span) == self.hovered_link => { + Some(span) if Some(span) == state.hovered_link => { if let Some((link, on_link_clicked)) = self .spans .as_ref() @@ -823,21 +857,38 @@ where if state.is_hovered != was_hovered || state.selection != selection_before - || self.hovered_link.is_some() != link_was_hovered + || state.hovered_link != hovered_link_before { + if let Some(span) = state.hovered_link { + if let Some((link, on_link_hovered)) = self + .spans + .as_ref() + .as_ref() + .get(span) + .and_then(|span| span.link.clone()) + .zip(self.on_link_hover.as_deref()) + { + shell.publish(on_link_hovered(link)); + } + } else if let Some(on_hover_lost) = self.on_hover_lost.as_deref() { + shell.publish(on_hover_lost()); + } + shell.request_redraw(); } } fn mouse_interaction( &self, - _tree: &Tree, + tree: &Tree, layout: Layout<'_>, cursor: mouse::Cursor, _viewport: &Rectangle, _renderer: &Renderer, ) -> mouse::Interaction { - if self.hovered_link.is_some() { + let state = tree.state.downcast_ref::<State<Link>>(); + + if state.hovered_link.is_some() { mouse::Interaction::Pointer } else if cursor.is_over(layout.bounds()) { mouse::Interaction::Text |
