1use crate::{
2 register_tooltip_mouse_handlers, set_tooltip_on_window, ActiveTooltip, AnyView, App, Bounds,
3 DispatchPhase, Element, ElementId, GlobalElementId, HighlightStyle, Hitbox, IntoElement,
4 LayoutId, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, Point, SharedString, Size,
5 TextOverflow, TextRun, TextStyle, TooltipId, WhiteSpace, Window, WrappedLine,
6 WrappedLineLayout,
7};
8use anyhow::anyhow;
9use parking_lot::{Mutex, MutexGuard};
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(Arc<Mutex<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 lock(&self) -> MutexGuard<Option<TextLayoutInner>> {
292 self.0.lock()
293 }
294
295 fn layout(
296 &self,
297 text: SharedString,
298 runs: Option<Vec<TextRun>>,
299 window: &mut Window,
300 _: &mut App,
301 ) -> LayoutId {
302 let text_style = window.text_style();
303 let font_size = text_style.font_size.to_pixels(window.rem_size());
304 let line_height = text_style
305 .line_height
306 .to_pixels(font_size.into(), window.rem_size());
307
308 let mut runs = if let Some(runs) = runs {
309 runs
310 } else {
311 vec![text_style.to_run(text.len())]
312 };
313
314 let layout_id = window.request_measured_layout(Default::default(), {
315 let element_state = self.clone();
316
317 move |known_dimensions, available_space, window, cx| {
318 let wrap_width = if text_style.white_space == WhiteSpace::Normal {
319 known_dimensions.width.or(match available_space.width {
320 crate::AvailableSpace::Definite(x) => Some(x),
321 _ => None,
322 })
323 } else {
324 None
325 };
326
327 let (truncate_width, ellipsis) =
328 if let Some(text_overflow) = text_style.text_overflow {
329 let width = known_dimensions.width.or(match available_space.width {
330 crate::AvailableSpace::Definite(x) => match text_style.line_clamp {
331 Some(max_lines) => Some(x * max_lines),
332 None => Some(x),
333 },
334 _ => None,
335 });
336
337 match text_overflow {
338 TextOverflow::Ellipsis(s) => (width, Some(s)),
339 }
340 } else {
341 (None, None)
342 };
343
344 if let Some(text_layout) = element_state.0.lock().as_ref() {
345 if text_layout.size.is_some()
346 && (wrap_width.is_none() || wrap_width == text_layout.wrap_width)
347 {
348 return text_layout.size.unwrap();
349 }
350 }
351
352 let mut line_wrapper = cx.text_system().line_wrapper(text_style.font(), font_size);
353 let text = if let Some(truncate_width) = truncate_width {
354 line_wrapper.truncate_line(text.clone(), truncate_width, ellipsis, &mut runs)
355 } else {
356 text.clone()
357 };
358
359 let Some(lines) = window
360 .text_system()
361 .shape_text(
362 text,
363 font_size,
364 &runs,
365 wrap_width, // Wrap if we know the width.
366 text_style.line_clamp, // Limit the number of lines if line_clamp is set.
367 )
368 .log_err()
369 else {
370 element_state.lock().replace(TextLayoutInner {
371 lines: Default::default(),
372 line_height,
373 wrap_width,
374 size: Some(Size::default()),
375 bounds: None,
376 });
377 return Size::default();
378 };
379
380 let mut size: Size<Pixels> = Size::default();
381 for line in &lines {
382 let line_size = line.size(line_height);
383 size.height += line_size.height;
384 size.width = size.width.max(line_size.width).ceil();
385 }
386
387 element_state.lock().replace(TextLayoutInner {
388 lines,
389 line_height,
390 wrap_width,
391 size: Some(size),
392 bounds: None,
393 });
394
395 size
396 }
397 });
398
399 layout_id
400 }
401
402 fn prepaint(&self, bounds: Bounds<Pixels>, text: &str) {
403 let mut element_state = self.lock();
404 let element_state = element_state
405 .as_mut()
406 .ok_or_else(|| anyhow!("measurement has not been performed on {}", text))
407 .unwrap();
408 element_state.bounds = Some(bounds);
409 }
410
411 fn paint(&self, text: &str, window: &mut Window, cx: &mut App) {
412 let element_state = self.lock();
413 let element_state = element_state
414 .as_ref()
415 .ok_or_else(|| anyhow!("measurement has not been performed on {}", text))
416 .unwrap();
417 let bounds = element_state
418 .bounds
419 .ok_or_else(|| anyhow!("prepaint has not been performed on {:?}", text))
420 .unwrap();
421
422 let line_height = element_state.line_height;
423 let mut line_origin = bounds.origin;
424 let text_style = window.text_style();
425 for line in &element_state.lines {
426 line.paint(
427 line_origin,
428 line_height,
429 text_style.text_align,
430 Some(bounds),
431 window,
432 cx,
433 )
434 .log_err();
435 line_origin.y += line.size(line_height).height;
436 }
437 }
438
439 /// Get the byte index into the input of the pixel position.
440 pub fn index_for_position(&self, mut position: Point<Pixels>) -> Result<usize, usize> {
441 let element_state = self.lock();
442 let element_state = element_state
443 .as_ref()
444 .expect("measurement has not been performed");
445 let bounds = element_state
446 .bounds
447 .expect("prepaint has not been performed");
448
449 if position.y < bounds.top() {
450 return Err(0);
451 }
452
453 let line_height = element_state.line_height;
454 let mut line_origin = bounds.origin;
455 let mut line_start_ix = 0;
456 for line in &element_state.lines {
457 let line_bottom = line_origin.y + line.size(line_height).height;
458 if position.y > line_bottom {
459 line_origin.y = line_bottom;
460 line_start_ix += line.len() + 1;
461 } else {
462 let position_within_line = position - line_origin;
463 match line.index_for_position(position_within_line, line_height) {
464 Ok(index_within_line) => return Ok(line_start_ix + index_within_line),
465 Err(index_within_line) => return Err(line_start_ix + index_within_line),
466 }
467 }
468 }
469
470 Err(line_start_ix.saturating_sub(1))
471 }
472
473 /// Get the pixel position for the given byte index.
474 pub fn position_for_index(&self, index: usize) -> Option<Point<Pixels>> {
475 let element_state = self.lock();
476 let element_state = element_state
477 .as_ref()
478 .expect("measurement has not been performed");
479 let bounds = element_state
480 .bounds
481 .expect("prepaint has not been performed");
482 let line_height = element_state.line_height;
483
484 let mut line_origin = bounds.origin;
485 let mut line_start_ix = 0;
486
487 for line in &element_state.lines {
488 let line_end_ix = line_start_ix + line.len();
489 if index < line_start_ix {
490 break;
491 } else if index > line_end_ix {
492 line_origin.y += line.size(line_height).height;
493 line_start_ix = line_end_ix + 1;
494 continue;
495 } else {
496 let ix_within_line = index - line_start_ix;
497 return Some(line_origin + line.position_for_index(ix_within_line, line_height)?);
498 }
499 }
500
501 None
502 }
503
504 /// Retrieve the layout for the line containing the given byte index.
505 pub fn line_layout_for_index(&self, index: usize) -> Option<Arc<WrappedLineLayout>> {
506 let element_state = self.lock();
507 let element_state = element_state
508 .as_ref()
509 .expect("measurement has not been performed");
510 let bounds = element_state
511 .bounds
512 .expect("prepaint has not been performed");
513 let line_height = element_state.line_height;
514
515 let mut line_origin = bounds.origin;
516 let mut line_start_ix = 0;
517
518 for line in &element_state.lines {
519 let line_end_ix = line_start_ix + line.len();
520 if index < line_start_ix {
521 break;
522 } else if index > line_end_ix {
523 line_origin.y += line.size(line_height).height;
524 line_start_ix = line_end_ix + 1;
525 continue;
526 } else {
527 return Some(line.layout.clone());
528 }
529 }
530
531 None
532 }
533
534 /// The bounds of this layout.
535 pub fn bounds(&self) -> Bounds<Pixels> {
536 self.0.lock().as_ref().unwrap().bounds.unwrap()
537 }
538
539 /// The line height for this layout.
540 pub fn line_height(&self) -> Pixels {
541 self.0.lock().as_ref().unwrap().line_height
542 }
543
544 /// The text for this layout.
545 pub fn text(&self) -> String {
546 self.0
547 .lock()
548 .as_ref()
549 .unwrap()
550 .lines
551 .iter()
552 .map(|s| s.text.to_string())
553 .collect::<Vec<_>>()
554 .join("\n")
555 }
556}
557
558/// A text element that can be interacted with.
559pub struct InteractiveText {
560 element_id: ElementId,
561 text: StyledText,
562 click_listener:
563 Option<Box<dyn Fn(&[Range<usize>], InteractiveTextClickEvent, &mut Window, &mut App)>>,
564 hover_listener: Option<Box<dyn Fn(Option<usize>, MouseMoveEvent, &mut Window, &mut App)>>,
565 tooltip_builder: Option<Rc<dyn Fn(usize, &mut Window, &mut App) -> Option<AnyView>>>,
566 tooltip_id: Option<TooltipId>,
567 clickable_ranges: Vec<Range<usize>>,
568}
569
570struct InteractiveTextClickEvent {
571 mouse_down_index: usize,
572 mouse_up_index: usize,
573}
574
575#[doc(hidden)]
576#[derive(Default)]
577pub struct InteractiveTextState {
578 mouse_down_index: Rc<Cell<Option<usize>>>,
579 hovered_index: Rc<Cell<Option<usize>>>,
580 active_tooltip: Rc<RefCell<Option<ActiveTooltip>>>,
581}
582
583/// InteractiveTest is a wrapper around StyledText that adds mouse interactions.
584impl InteractiveText {
585 /// Creates a new InteractiveText from the given text.
586 pub fn new(id: impl Into<ElementId>, text: StyledText) -> Self {
587 Self {
588 element_id: id.into(),
589 text,
590 click_listener: None,
591 hover_listener: None,
592 tooltip_builder: None,
593 tooltip_id: None,
594 clickable_ranges: Vec::new(),
595 }
596 }
597
598 /// on_click is called when the user clicks on one of the given ranges, passing the index of
599 /// the clicked range.
600 pub fn on_click(
601 mut self,
602 ranges: Vec<Range<usize>>,
603 listener: impl Fn(usize, &mut Window, &mut App) + 'static,
604 ) -> Self {
605 self.click_listener = Some(Box::new(move |ranges, event, window, cx| {
606 for (range_ix, range) in ranges.iter().enumerate() {
607 if range.contains(&event.mouse_down_index) && range.contains(&event.mouse_up_index)
608 {
609 listener(range_ix, window, cx);
610 }
611 }
612 }));
613 self.clickable_ranges = ranges;
614 self
615 }
616
617 /// on_hover is called when the mouse moves over a character within the text, passing the
618 /// index of the hovered character, or None if the mouse leaves the text.
619 pub fn on_hover(
620 mut self,
621 listener: impl Fn(Option<usize>, MouseMoveEvent, &mut Window, &mut App) + 'static,
622 ) -> Self {
623 self.hover_listener = Some(Box::new(listener));
624 self
625 }
626
627 /// tooltip lets you specify a tooltip for a given character index in the string.
628 pub fn tooltip(
629 mut self,
630 builder: impl Fn(usize, &mut Window, &mut App) -> Option<AnyView> + 'static,
631 ) -> Self {
632 self.tooltip_builder = Some(Rc::new(builder));
633 self
634 }
635}
636
637impl Element for InteractiveText {
638 type RequestLayoutState = ();
639 type PrepaintState = Hitbox;
640
641 fn id(&self) -> Option<ElementId> {
642 Some(self.element_id.clone())
643 }
644
645 fn request_layout(
646 &mut self,
647 _id: Option<&GlobalElementId>,
648 window: &mut Window,
649 cx: &mut App,
650 ) -> (LayoutId, Self::RequestLayoutState) {
651 self.text.request_layout(None, window, cx)
652 }
653
654 fn prepaint(
655 &mut self,
656 global_id: Option<&GlobalElementId>,
657 bounds: Bounds<Pixels>,
658 state: &mut Self::RequestLayoutState,
659 window: &mut Window,
660 cx: &mut App,
661 ) -> Hitbox {
662 window.with_optional_element_state::<InteractiveTextState, _>(
663 global_id,
664 |interactive_state, window| {
665 let mut interactive_state = interactive_state
666 .map(|interactive_state| interactive_state.unwrap_or_default());
667
668 if let Some(interactive_state) = interactive_state.as_mut() {
669 if self.tooltip_builder.is_some() {
670 self.tooltip_id =
671 set_tooltip_on_window(&interactive_state.active_tooltip, window);
672 } else {
673 // If there is no longer a tooltip builder, remove the active tooltip.
674 interactive_state.active_tooltip.take();
675 }
676 }
677
678 self.text.prepaint(None, bounds, state, window, cx);
679 let hitbox = window.insert_hitbox(bounds, false);
680 (hitbox, interactive_state)
681 },
682 )
683 }
684
685 fn paint(
686 &mut self,
687 global_id: Option<&GlobalElementId>,
688 bounds: Bounds<Pixels>,
689 _: &mut Self::RequestLayoutState,
690 hitbox: &mut Hitbox,
691 window: &mut Window,
692 cx: &mut App,
693 ) {
694 let current_view = window.current_view();
695 let text_layout = self.text.layout().clone();
696 window.with_element_state::<InteractiveTextState, _>(
697 global_id.unwrap(),
698 |interactive_state, window| {
699 let mut interactive_state = interactive_state.unwrap_or_default();
700 if let Some(click_listener) = self.click_listener.take() {
701 let mouse_position = window.mouse_position();
702 if let Ok(ix) = text_layout.index_for_position(mouse_position) {
703 if self
704 .clickable_ranges
705 .iter()
706 .any(|range| range.contains(&ix))
707 {
708 window.set_cursor_style(crate::CursorStyle::PointingHand, hitbox)
709 }
710 }
711
712 let text_layout = text_layout.clone();
713 let mouse_down = interactive_state.mouse_down_index.clone();
714 if let Some(mouse_down_index) = mouse_down.get() {
715 let hitbox = hitbox.clone();
716 let clickable_ranges = mem::take(&mut self.clickable_ranges);
717 window.on_mouse_event(
718 move |event: &MouseUpEvent, phase, window: &mut Window, cx| {
719 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
720 if let Ok(mouse_up_index) =
721 text_layout.index_for_position(event.position)
722 {
723 click_listener(
724 &clickable_ranges,
725 InteractiveTextClickEvent {
726 mouse_down_index,
727 mouse_up_index,
728 },
729 window,
730 cx,
731 )
732 }
733
734 mouse_down.take();
735 window.refresh();
736 }
737 },
738 );
739 } else {
740 let hitbox = hitbox.clone();
741 window.on_mouse_event(move |event: &MouseDownEvent, phase, window, _| {
742 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
743 if let Ok(mouse_down_index) =
744 text_layout.index_for_position(event.position)
745 {
746 mouse_down.set(Some(mouse_down_index));
747 window.refresh();
748 }
749 }
750 });
751 }
752 }
753
754 window.on_mouse_event({
755 let mut hover_listener = self.hover_listener.take();
756 let hitbox = hitbox.clone();
757 let text_layout = text_layout.clone();
758 let hovered_index = interactive_state.hovered_index.clone();
759 move |event: &MouseMoveEvent, phase, window, cx| {
760 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
761 let current = hovered_index.get();
762 let updated = text_layout.index_for_position(event.position).ok();
763 if current != updated {
764 hovered_index.set(updated);
765 if let Some(hover_listener) = hover_listener.as_ref() {
766 hover_listener(updated, event.clone(), window, cx);
767 }
768 cx.notify(current_view);
769 }
770 }
771 }
772 });
773
774 if let Some(tooltip_builder) = self.tooltip_builder.clone() {
775 let active_tooltip = interactive_state.active_tooltip.clone();
776 let build_tooltip = Rc::new({
777 let tooltip_is_hoverable = false;
778 let text_layout = text_layout.clone();
779 move |window: &mut Window, cx: &mut App| {
780 text_layout
781 .index_for_position(window.mouse_position())
782 .ok()
783 .and_then(|position| tooltip_builder(position, window, cx))
784 .map(|view| (view, tooltip_is_hoverable))
785 }
786 });
787
788 // Use bounds instead of testing hitbox since this is called during prepaint.
789 let check_is_hovered_during_prepaint = Rc::new({
790 let source_bounds = hitbox.bounds;
791 let text_layout = text_layout.clone();
792 let pending_mouse_down = interactive_state.mouse_down_index.clone();
793 move |window: &Window| {
794 text_layout
795 .index_for_position(window.mouse_position())
796 .is_ok()
797 && source_bounds.contains(&window.mouse_position())
798 && pending_mouse_down.get().is_none()
799 }
800 });
801
802 let check_is_hovered = Rc::new({
803 let hitbox = hitbox.clone();
804 let text_layout = text_layout.clone();
805 let pending_mouse_down = interactive_state.mouse_down_index.clone();
806 move |window: &Window| {
807 text_layout
808 .index_for_position(window.mouse_position())
809 .is_ok()
810 && hitbox.is_hovered(window)
811 && pending_mouse_down.get().is_none()
812 }
813 });
814
815 register_tooltip_mouse_handlers(
816 &active_tooltip,
817 self.tooltip_id,
818 build_tooltip,
819 check_is_hovered,
820 check_is_hovered_during_prepaint,
821 window,
822 );
823 }
824
825 self.text.paint(None, bounds, &mut (), &mut (), window, cx);
826
827 ((), interactive_state)
828 },
829 );
830 }
831}
832
833impl IntoElement for InteractiveText {
834 type Element = Self;
835
836 fn into_element(self) -> Self::Element {
837 self
838 }
839}