use crate::core::{Bounds, px};
use crate::view::any_element::AnyElement;
use crate::view::context::ElementContext;
use crate::view::div::div;
use crate::view::element::{IntoElement, ParentElement};
use crate::view::overlay::{Placement, anchored};
use crate::view::styled::Styled;
use crate::view::theme::Token;
use crate::view::window::Window;
use std::rc::Rc;
use std::time::{Duration, Instant};
pub const TOOLTIP_DELAY: Duration = Duration::from_millis(600);
#[derive(Clone)]
pub struct TooltipHover {
pub text: Rc<str>,
pub anchor: Bounds,
pub since: Instant,
}
impl Window {
pub(crate) fn track_tooltip(&mut self) -> bool {
let position = self.mouse.position;
let found = self.frame.hitboxes.hits_at(position).find_map(|hit| {
let text = self.frame.handlers.iter().find(|entry| entry.hitbox == hit)?.tooltip.clone()?;
Some((text, self.frame.hitboxes.bounds(hit)?))
});
let changed = match (&self.tooltip, &found) {
(Some(current), Some((text, anchor))) => current.text != *text || current.anchor != *anchor,
(None, None) => false,
_ => true,
};
if !changed {
return false;
}
let was_visible = self.tooltip.as_ref().is_some_and(|current| current.since.elapsed() >= TOOLTIP_DELAY);
self.tooltip = found.map(|(text, anchor)| TooltipHover { text, anchor, since: Instant::now() });
if self.tooltip.is_some() {
self.entities.schedule(TOOLTIP_DELAY, |entities| entities.timers.request_animation_frame());
}
was_visible
}
pub(crate) fn tooltip_element(&self) -> Option<AnyElement> {
let hover = self.tooltip.as_ref().filter(|hover| hover.since.elapsed() >= TOOLTIP_DELAY)?;
let bubble = div()
.px_2()
.py_1()
.mt(px(4.0))
.rounded_md()
.border_1()
.border_color(Token::Border)
.bg(Token::ElevatedSurface)
.shadow_md()
.text_xs()
.text_color(Token::Text)
.whitespace_nowrap()
.child(hover.text.to_string());
Some(anchored().anchor_to(hover.anchor).placement(Placement::Below).child(bubble).into_element())
}
}
pub(crate) fn prepaint_tooltip(element: &mut AnyElement, cx: &mut ElementContext) {
element.request_layout(cx);
element.prepaint(cx);
}