1use crate::{
2 ActiveTooltip, AnyView, App, Bounds, DispatchPhase, Element, ElementId, GlobalElementId,
3 HighlightStyle, Hitbox, IntoElement, LayoutId, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
4 Pixels, Point, SharedString, Size, TextAlign, TextOverflow, TextRun, TextStyle,
5 TextStyleRefinement, TooltipId, WhiteSpace, Window, WrappedLine, WrappedLineLayout,
6 register_tooltip_mouse_handlers, set_tooltip_on_window,
7};
8use anyhow::anyhow;
9use refineable::Refineable as _;
10use smallvec::SmallVec;
11use std::{
12 cell::{Cell, RefCell},
13 mem,
14 ops::Range,
15 rc::Rc,
16 sync::Arc,
17};
18use util::ResultExt;
19
20impl Element for &'static str {
21 type RequestLayoutState = TextLayout;
22 type PrepaintState = ();
23
24 fn id(&self) -> Option<ElementId> {
25 None
26 }
27
28 fn request_layout(
29 &mut self,
30 _id: Option<&GlobalElementId>,
31 window: &mut Window,
32 cx: &mut App,
33 ) -> (LayoutId, Self::RequestLayoutState) {
34 let mut state = TextLayout::default();
35 let layout_id = state.layout(SharedString::from(*self), None, window, cx);
36 (layout_id, state)
37 }
38
39 fn prepaint(
40 &mut self,
41 _id: Option<&GlobalElementId>,
42 bounds: Bounds<Pixels>,
43 text_layout: &mut Self::RequestLayoutState,
44 _window: &mut Window,
45 _cx: &mut App,
46 ) {
47 text_layout.prepaint(bounds, self)
48 }
49
50 fn paint(
51 &mut self,
52 _id: Option<&GlobalElementId>,
53 _bounds: Bounds<Pixels>,
54 text_layout: &mut TextLayout,
55 _: &mut (),
56 window: &mut Window,
57 cx: &mut App,
58 ) {
59 text_layout.paint(self, window, cx)
60 }
61}
62
63impl IntoElement for &'static str {
64 type Element = Self;
65
66 fn into_element(self) -> Self::Element {
67 self
68 }
69}
70
71impl IntoElement for String {
72 type Element = SharedString;
73
74 fn into_element(self) -> Self::Element {
75 self.into()
76 }
77}
78
79impl Element for SharedString {
80 type RequestLayoutState = TextLayout;
81 type PrepaintState = ();
82
83 fn id(&self) -> Option<ElementId> {
84 None
85 }
86
87 fn request_layout(
88 &mut self,
89
90 _id: Option<&GlobalElementId>,
91
92 window: &mut Window,
93 cx: &mut App,
94 ) -> (LayoutId, Self::RequestLayoutState) {
95 let mut state = TextLayout::default();
96 let layout_id = state.layout(self.clone(), None, window, cx);
97 (layout_id, state)
98 }
99
100 fn prepaint(
101 &mut self,
102 _id: Option<&GlobalElementId>,
103 bounds: Bounds<Pixels>,
104 text_layout: &mut Self::RequestLayoutState,
105 _window: &mut Window,
106 _cx: &mut App,
107 ) {
108 text_layout.prepaint(bounds, self.as_ref())
109 }
110
111 fn paint(
112 &mut self,
113 _id: Option<&GlobalElementId>,
114 _bounds: Bounds<Pixels>,
115 text_layout: &mut Self::RequestLayoutState,
116 _: &mut Self::PrepaintState,
117 window: &mut Window,
118 cx: &mut App,
119 ) {
120 text_layout.paint(self.as_ref(), window, cx)
121 }
122}
123
124impl IntoElement for SharedString {
125 type Element = Self;
126
127 fn into_element(self) -> Self::Element {
128 self
129 }
130}
131
132/// Renders text with runs of different styles.
133///
134/// Callers are responsible for setting the correct style for each run.
135/// For text with a uniform style, you can usually avoid calling this constructor
136/// and just pass text directly.
137pub struct StyledText {
138 text: SharedString,
139 runs: Option<Vec<TextRun>>,
140 delayed_highlights: Option<Vec<(Range<usize>, HighlightStyle)>>,
141 layout: TextLayout,
142}
143
144impl StyledText {
145 /// Construct a new styled text element from the given string.
146 pub fn new(text: impl Into<SharedString>) -> Self {
147 StyledText {
148 text: text.into(),
149 runs: None,
150 delayed_highlights: None,
151 layout: TextLayout::default(),
152 }
153 }
154
155 /// Get the layout for this element. This can be used to map indices to pixels and vice versa.
156 pub fn layout(&self) -> &TextLayout {
157 &self.layout
158 }
159
160 /// Set the styling attributes for the given text, as well as
161 /// as any ranges of text that have had their style customized.
162 pub fn with_default_highlights(
163 mut self,
164 default_style: &TextStyle,
165 highlights: impl IntoIterator<Item = (Range<usize>, HighlightStyle)>,
166 ) -> Self {
167 debug_assert!(
168 self.delayed_highlights.is_none(),
169 "Can't use `with_default_highlights` and `with_highlights`"
170 );
171 let runs = Self::compute_runs(&self.text, default_style, highlights);
172 self.runs = Some(runs);
173 self
174 }
175
176 /// Set the styling attributes for the given text, as well as
177 /// as any ranges of text that have had their style customized.
178 pub fn with_highlights(
179 mut self,
180 highlights: impl IntoIterator<Item = (Range<usize>, HighlightStyle)>,
181 ) -> Self {
182 debug_assert!(
183 self.runs.is_none(),
184 "Can't use `with_highlights` and `with_default_highlights`"
185 );
186 self.delayed_highlights = Some(highlights.into_iter().collect::<Vec<_>>());
187 self
188 }
189
190 fn compute_runs(
191 text: &str,
192 default_style: &TextStyle,
193 highlights: impl IntoIterator<Item = (Range<usize>, HighlightStyle)>,
194 ) -> Vec<TextRun> {
195 let mut runs = Vec::new();
196 let mut ix = 0;
197 for (range, highlight) in highlights {
198 if ix < range.start {
199 runs.push(default_style.clone().to_run(range.start - ix));
200 }
201 runs.push(
202 default_style
203 .clone()
204 .highlight(highlight)
205 .to_run(range.len()),
206 );
207 ix = range.end;
208 }
209 if ix < text.len() {
210 runs.push(default_style.to_run(text.len() - ix));
211 }
212 runs
213 }
214
215 /// Set the text runs for this piece of text.
216 pub fn with_runs(mut self, runs: Vec<TextRun>) -> Self {
217 self.runs = Some(runs);
218 self
219 }
220}
221
222impl Element for StyledText {
223 type RequestLayoutState = ();
224 type PrepaintState = ();
225
226 fn id(&self) -> Option<ElementId> {
227 None
228 }
229
230 fn request_layout(
231 &mut self,
232 _id: Option<&GlobalElementId>,
233 window: &mut Window,
234 cx: &mut App,
235 ) -> (LayoutId, Self::RequestLayoutState) {
236 let runs = self.runs.take().or_else(|| {
237 self.delayed_highlights.take().map(|delayed_highlights| {
238 Self::compute_runs(&self.text, &window.text_style(), delayed_highlights)
239 })
240 });
241
242 let layout_id = self.layout.layout(self.text.clone(), runs, window, cx);
243 (layout_id, ())
244 }
245
246 fn prepaint(
247 &mut self,
248 _id: Option<&GlobalElementId>,
249 bounds: Bounds<Pixels>,
250 _: &mut Self::RequestLayoutState,
251 _window: &mut Window,
252 _cx: &mut App,
253 ) {
254 self.layout.prepaint(bounds, &self.text)
255 }
256
257 fn paint(
258 &mut self,
259 _id: Option<&GlobalElementId>,
260 _bounds: Bounds<Pixels>,
261 _: &mut Self::RequestLayoutState,
262 _: &mut Self::PrepaintState,
263 window: &mut Window,
264 cx: &mut App,
265 ) {
266 self.layout.paint(&self.text, window, cx)
267 }
268}
269
270impl IntoElement for StyledText {
271 type Element = Self;
272
273 fn into_element(self) -> Self::Element {
274 self
275 }
276}
277
278/// The Layout for TextElement. This can be used to map indices to pixels and vice versa.
279#[derive(Default, Clone)]
280pub struct TextLayout(Rc<RefCell<Option<TextLayoutInner>>>);
281
282struct TextLayoutInner {
283 lines: SmallVec<[WrappedLine; 1]>,
284 line_height: Pixels,
285 wrap_width: Option<Pixels>,
286 size: Option<Size<Pixels>>,
287 bounds: Option<Bounds<Pixels>>,
288}
289
290impl TextLayout {
291 fn layout(
292 &self,
293 text: SharedString,
294 runs: Option<Vec<TextRun>>,
295 window: &mut Window,
296 _: &mut App,
297 ) -> LayoutId {
298 let text_style = window.text_style();
299 let font_size = text_style.font_size.to_pixels(window.rem_size());
300 let line_height = text_style
301 .line_height
302 .to_pixels(font_size.into(), window.rem_size());
303
304 let mut runs = if let Some(runs) = runs {
305 runs
306 } else {
307 vec![text_style.to_run(text.len())]
308 };
309
310 let layout_id = window.request_measured_layout(Default::default(), {
311 let element_state = self.clone();
312
313 move |known_dimensions, available_space, window, cx| {
314 let wrap_width = if text_style.white_space == WhiteSpace::Normal {
315 known_dimensions.width.or(match available_space.width {
316 crate::AvailableSpace::Definite(x) => Some(x),
317 _ => None,
318 })
319 } else {
320 None
321 };
322
323 let (truncate_width, ellipsis) =
324 if let Some(text_overflow) = text_style.text_overflow {
325 let width = known_dimensions.width.or(match available_space.width {
326 crate::AvailableSpace::Definite(x) => match text_style.line_clamp {
327 Some(max_lines) => Some(x * max_lines),
328 None => Some(x),
329 },
330 _ => None,
331 });
332
333 match text_overflow {
334 TextOverflow::Ellipsis(s) => (width, Some(s)),
335 }
336 } else {
337 (None, None)
338 };
339
340 if let Some(text_layout) = element_state.0.borrow().as_ref() {
341 if text_layout.size.is_some()
342 && (wrap_width.is_none() || wrap_width == text_layout.wrap_width)
343 {
344 return text_layout.size.unwrap();
345 }
346 }
347
348 let mut line_wrapper = cx.text_system().line_wrapper(text_style.font(), font_size);
349 let text = if let Some(truncate_width) = truncate_width {
350 line_wrapper.truncate_line(text.clone(), truncate_width, ellipsis, &mut runs)
351 } else {
352 text.clone()
353 };
354
355 let Some(lines) = window
356 .text_system()
357 .shape_text(
358 text,
359 font_size,
360 &runs,
361 wrap_width, // Wrap if we know the width.
362 text_style.line_clamp, // Limit the number of lines if line_clamp is set.
363 )
364 .log_err()
365 else {
366 element_state.0.borrow_mut().replace(TextLayoutInner {
367 lines: Default::default(),
368 line_height,
369 wrap_width,
370 size: Some(Size::default()),
371 bounds: None,
372 });
373 return Size::default();
374 };
375
376 let mut size: Size<Pixels> = Size::default();
377 for line in &lines {
378 let line_size = line.size(line_height);
379 size.height += line_size.height;
380 size.width = size.width.max(line_size.width).ceil();
381 }
382
383 element_state.0.borrow_mut().replace(TextLayoutInner {
384 lines,
385 line_height,
386 wrap_width,
387 size: Some(size),
388 bounds: None,
389 });
390
391 size
392 }
393 });
394
395 layout_id
396 }
397
398 fn prepaint(&self, bounds: Bounds<Pixels>, text: &str) {
399 let mut element_state = self.0.borrow_mut();
400 let element_state = element_state
401 .as_mut()
402 .ok_or_else(|| anyhow!("measurement has not been performed on {}", text))
403 .unwrap();
404 element_state.bounds = Some(bounds);
405 }
406
407 fn paint(&self, text: &str, window: &mut Window, cx: &mut App) {
408 let element_state = self.0.borrow();
409 let element_state = element_state
410 .as_ref()
411 .ok_or_else(|| anyhow!("measurement has not been performed on {}", text))
412 .unwrap();
413 let bounds = element_state
414 .bounds
415 .ok_or_else(|| anyhow!("prepaint has not been performed on {:?}", text))
416 .unwrap();
417
418 let line_height = element_state.line_height;
419 let mut line_origin = bounds.origin;
420
421 // Get current text_style refinements
422 let mut text_style = TextStyleRefinement::default();
423 for style in window.text_style_stack.iter().as_ref() {
424 text_style.refine(&style);
425 }
426
427 for line in &element_state.lines {
428 line.paint_background(
429 line_origin,
430 line_height,
431 text_style.text_align.unwrap_or(TextAlign::Left),
432 Some(bounds),
433 window,
434 cx,
435 )
436 .log_err();
437 line.paint(
438 line_origin,
439 line_height,
440 Some(&text_style),
441 Some(bounds),
442 window,
443 cx,
444 )
445 .log_err();
446 line_origin.y += line.size(line_height).height;
447 }
448 }
449
450 /// Get the byte index into the input of the pixel position.
451 pub fn index_for_position(&self, mut position: Point<Pixels>) -> Result<usize, usize> {
452 let element_state = self.0.borrow();
453 let element_state = element_state
454 .as_ref()
455 .expect("measurement has not been performed");
456 let bounds = element_state
457 .bounds
458 .expect("prepaint has not been performed");
459
460 if position.y < bounds.top() {
461 return Err(0);
462 }
463
464 let line_height = element_state.line_height;
465 let mut line_origin = bounds.origin;
466 let mut line_start_ix = 0;
467 for line in &element_state.lines {
468 let line_bottom = line_origin.y + line.size(line_height).height;
469 if position.y > line_bottom {
470 line_origin.y = line_bottom;
471 line_start_ix += line.len() + 1;
472 } else {
473 let position_within_line = position - line_origin;
474 match line.index_for_position(position_within_line, line_height) {
475 Ok(index_within_line) => return Ok(line_start_ix + index_within_line),
476 Err(index_within_line) => return Err(line_start_ix + index_within_line),
477 }
478 }
479 }
480
481 Err(line_start_ix.saturating_sub(1))
482 }
483
484 /// Get the pixel position for the given byte index.
485 pub fn position_for_index(&self, index: usize) -> Option<Point<Pixels>> {
486 let element_state = self.0.borrow();
487 let element_state = element_state
488 .as_ref()
489 .expect("measurement has not been performed");
490 let bounds = element_state
491 .bounds
492 .expect("prepaint has not been performed");
493 let line_height = element_state.line_height;
494
495 let mut line_origin = bounds.origin;
496 let mut line_start_ix = 0;
497
498 for line in &element_state.lines {
499 let line_end_ix = line_start_ix + line.len();
500 if index < line_start_ix {
501 break;
502 } else if index > line_end_ix {
503 line_origin.y += line.size(line_height).height;
504 line_start_ix = line_end_ix + 1;
505 continue;
506 } else {
507 let ix_within_line = index - line_start_ix;
508 return Some(line_origin + line.position_for_index(ix_within_line, line_height)?);
509 }
510 }
511
512 None
513 }
514
515 /// Retrieve the layout for the line containing the given byte index.
516 pub fn line_layout_for_index(&self, index: usize) -> Option<Arc<WrappedLineLayout>> {
517 let element_state = self.0.borrow();
518 let element_state = element_state
519 .as_ref()
520 .expect("measurement has not been performed");
521 let bounds = element_state
522 .bounds
523 .expect("prepaint has not been performed");
524 let line_height = element_state.line_height;
525
526 let mut line_origin = bounds.origin;
527 let mut line_start_ix = 0;
528
529 for line in &element_state.lines {
530 let line_end_ix = line_start_ix + line.len();
531 if index < line_start_ix {
532 break;
533 } else if index > line_end_ix {
534 line_origin.y += line.size(line_height).height;
535 line_start_ix = line_end_ix + 1;
536 continue;
537 } else {
538 return Some(line.layout.clone());
539 }
540 }
541
542 None
543 }
544
545 /// The bounds of this layout.
546 pub fn bounds(&self) -> Bounds<Pixels> {
547 self.0.borrow().as_ref().unwrap().bounds.unwrap()
548 }
549
550 /// The line height for this layout.
551 pub fn line_height(&self) -> Pixels {
552 self.0.borrow().as_ref().unwrap().line_height
553 }
554
555 /// The text for this layout.
556 pub fn text(&self) -> String {
557 self.0
558 .borrow()
559 .as_ref()
560 .unwrap()
561 .lines
562 .iter()
563 .map(|s| s.text.to_string())
564 .collect::<Vec<_>>()
565 .join("\n")
566 }
567}
568
569/// A text element that can be interacted with.
570pub struct InteractiveText {
571 element_id: ElementId,
572 text: StyledText,
573 click_listener:
574 Option<Box<dyn Fn(&[Range<usize>], InteractiveTextClickEvent, &mut Window, &mut App)>>,
575 hover_listener: Option<Box<dyn Fn(Option<usize>, MouseMoveEvent, &mut Window, &mut App)>>,
576 tooltip_builder: Option<Rc<dyn Fn(usize, &mut Window, &mut App) -> Option<AnyView>>>,
577 tooltip_id: Option<TooltipId>,
578 clickable_ranges: Vec<Range<usize>>,
579}
580
581struct InteractiveTextClickEvent {
582 mouse_down_index: usize,
583 mouse_up_index: usize,
584}
585
586#[doc(hidden)]
587#[derive(Default)]
588pub struct InteractiveTextState {
589 mouse_down_index: Rc<Cell<Option<usize>>>,
590 hovered_index: Rc<Cell<Option<usize>>>,
591 active_tooltip: Rc<RefCell<Option<ActiveTooltip>>>,
592}
593
594/// InteractiveTest is a wrapper around StyledText that adds mouse interactions.
595impl InteractiveText {
596 /// Creates a new InteractiveText from the given text.
597 pub fn new(id: impl Into<ElementId>, text: StyledText) -> Self {
598 Self {
599 element_id: id.into(),
600 text,
601 click_listener: None,
602 hover_listener: None,
603 tooltip_builder: None,
604 tooltip_id: None,
605 clickable_ranges: Vec::new(),
606 }
607 }
608
609 /// on_click is called when the user clicks on one of the given ranges, passing the index of
610 /// the clicked range.
611 pub fn on_click(
612 mut self,
613 ranges: Vec<Range<usize>>,
614 listener: impl Fn(usize, &mut Window, &mut App) + 'static,
615 ) -> Self {
616 self.click_listener = Some(Box::new(move |ranges, event, window, cx| {
617 for (range_ix, range) in ranges.iter().enumerate() {
618 if range.contains(&event.mouse_down_index) && range.contains(&event.mouse_up_index)
619 {
620 listener(range_ix, window, cx);
621 }
622 }
623 }));
624 self.clickable_ranges = ranges;
625 self
626 }
627
628 /// on_hover is called when the mouse moves over a character within the text, passing the
629 /// index of the hovered character, or None if the mouse leaves the text.
630 pub fn on_hover(
631 mut self,
632 listener: impl Fn(Option<usize>, MouseMoveEvent, &mut Window, &mut App) + 'static,
633 ) -> Self {
634 self.hover_listener = Some(Box::new(listener));
635 self
636 }
637
638 /// tooltip lets you specify a tooltip for a given character index in the string.
639 pub fn tooltip(
640 mut self,
641 builder: impl Fn(usize, &mut Window, &mut App) -> Option<AnyView> + 'static,
642 ) -> Self {
643 self.tooltip_builder = Some(Rc::new(builder));
644 self
645 }
646}
647
648impl Element for InteractiveText {
649 type RequestLayoutState = ();
650 type PrepaintState = Hitbox;
651
652 fn id(&self) -> Option<ElementId> {
653 Some(self.element_id.clone())
654 }
655
656 fn request_layout(
657 &mut self,
658 _id: Option<&GlobalElementId>,
659 window: &mut Window,
660 cx: &mut App,
661 ) -> (LayoutId, Self::RequestLayoutState) {
662 self.text.request_layout(None, window, cx)
663 }
664
665 fn prepaint(
666 &mut self,
667 global_id: Option<&GlobalElementId>,
668 bounds: Bounds<Pixels>,
669 state: &mut Self::RequestLayoutState,
670 window: &mut Window,
671 cx: &mut App,
672 ) -> Hitbox {
673 window.with_optional_element_state::<InteractiveTextState, _>(
674 global_id,
675 |interactive_state, window| {
676 let mut interactive_state = interactive_state
677 .map(|interactive_state| interactive_state.unwrap_or_default());
678
679 if let Some(interactive_state) = interactive_state.as_mut() {
680 if self.tooltip_builder.is_some() {
681 self.tooltip_id =
682 set_tooltip_on_window(&interactive_state.active_tooltip, window);
683 } else {
684 // If there is no longer a tooltip builder, remove the active tooltip.
685 interactive_state.active_tooltip.take();
686 }
687 }
688
689 self.text.prepaint(None, bounds, state, window, cx);
690 let hitbox = window.insert_hitbox(bounds, false);
691 (hitbox, interactive_state)
692 },
693 )
694 }
695
696 fn paint(
697 &mut self,
698 global_id: Option<&GlobalElementId>,
699 bounds: Bounds<Pixels>,
700 _: &mut Self::RequestLayoutState,
701 hitbox: &mut Hitbox,
702 window: &mut Window,
703 cx: &mut App,
704 ) {
705 let current_view = window.current_view();
706 let text_layout = self.text.layout().clone();
707 window.with_element_state::<InteractiveTextState, _>(
708 global_id.unwrap(),
709 |interactive_state, window| {
710 let mut interactive_state = interactive_state.unwrap_or_default();
711 if let Some(click_listener) = self.click_listener.take() {
712 let mouse_position = window.mouse_position();
713 if let Ok(ix) = text_layout.index_for_position(mouse_position) {
714 if self
715 .clickable_ranges
716 .iter()
717 .any(|range| range.contains(&ix))
718 {
719 window.set_cursor_style(crate::CursorStyle::PointingHand, Some(hitbox))
720 }
721 }
722
723 let text_layout = text_layout.clone();
724 let mouse_down = interactive_state.mouse_down_index.clone();
725 if let Some(mouse_down_index) = mouse_down.get() {
726 let hitbox = hitbox.clone();
727 let clickable_ranges = mem::take(&mut self.clickable_ranges);
728 window.on_mouse_event(
729 move |event: &MouseUpEvent, phase, window: &mut Window, cx| {
730 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
731 if let Ok(mouse_up_index) =
732 text_layout.index_for_position(event.position)
733 {
734 click_listener(
735 &clickable_ranges,
736 InteractiveTextClickEvent {
737 mouse_down_index,
738 mouse_up_index,
739 },
740 window,
741 cx,
742 )
743 }
744
745 mouse_down.take();
746 window.refresh();
747 }
748 },
749 );
750 } else {
751 let hitbox = hitbox.clone();
752 window.on_mouse_event(move |event: &MouseDownEvent, phase, window, _| {
753 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
754 if let Ok(mouse_down_index) =
755 text_layout.index_for_position(event.position)
756 {
757 mouse_down.set(Some(mouse_down_index));
758 window.refresh();
759 }
760 }
761 });
762 }
763 }
764
765 window.on_mouse_event({
766 let mut hover_listener = self.hover_listener.take();
767 let hitbox = hitbox.clone();
768 let text_layout = text_layout.clone();
769 let hovered_index = interactive_state.hovered_index.clone();
770 move |event: &MouseMoveEvent, phase, window, cx| {
771 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
772 let current = hovered_index.get();
773 let updated = text_layout.index_for_position(event.position).ok();
774 if current != updated {
775 hovered_index.set(updated);
776 if let Some(hover_listener) = hover_listener.as_ref() {
777 hover_listener(updated, event.clone(), window, cx);
778 }
779 cx.notify(current_view);
780 }
781 }
782 }
783 });
784
785 if let Some(tooltip_builder) = self.tooltip_builder.clone() {
786 let active_tooltip = interactive_state.active_tooltip.clone();
787 let build_tooltip = Rc::new({
788 let tooltip_is_hoverable = false;
789 let text_layout = text_layout.clone();
790 move |window: &mut Window, cx: &mut App| {
791 text_layout
792 .index_for_position(window.mouse_position())
793 .ok()
794 .and_then(|position| tooltip_builder(position, window, cx))
795 .map(|view| (view, tooltip_is_hoverable))
796 }
797 });
798
799 // Use bounds instead of testing hitbox since this is called during prepaint.
800 let check_is_hovered_during_prepaint = Rc::new({
801 let source_bounds = hitbox.bounds;
802 let text_layout = text_layout.clone();
803 let pending_mouse_down = interactive_state.mouse_down_index.clone();
804 move |window: &Window| {
805 text_layout
806 .index_for_position(window.mouse_position())
807 .is_ok()
808 && source_bounds.contains(&window.mouse_position())
809 && pending_mouse_down.get().is_none()
810 }
811 });
812
813 let check_is_hovered = Rc::new({
814 let hitbox = hitbox.clone();
815 let text_layout = text_layout.clone();
816 let pending_mouse_down = interactive_state.mouse_down_index.clone();
817 move |window: &Window| {
818 text_layout
819 .index_for_position(window.mouse_position())
820 .is_ok()
821 && hitbox.is_hovered(window)
822 && pending_mouse_down.get().is_none()
823 }
824 });
825
826 register_tooltip_mouse_handlers(
827 &active_tooltip,
828 self.tooltip_id,
829 build_tooltip,
830 check_is_hovered,
831 check_is_hovered_during_prepaint,
832 window,
833 );
834 }
835
836 self.text.paint(None, bounds, &mut (), &mut (), window, cx);
837
838 ((), interactive_state)
839 },
840 );
841 }
842}
843
844impl IntoElement for InteractiveText {
845 type Element = Self;
846
847 fn into_element(self) -> Self::Element {
848 self
849 }
850}