use crate::{ColorPair, Result, Window};
use std::time::{Duration, Instant};
pub struct Tooltip {
text: String,
delay: Duration,
colors: ColorPair,
position: (u16, u16),
}
impl Tooltip {
pub fn new(text: impl Into<String>) -> Self {
Self {
text: text.into(),
delay: Duration::from_millis(500),
colors: ColorPair::new(crate::Color::Yellow, crate::Color::Black),
position: (0, 0),
}
}
pub fn with_delay(mut self, delay: Duration) -> Self {
self.delay = delay;
self
}
pub fn with_color(mut self, colors: ColorPair) -> Self {
self.colors = colors;
self
}
pub fn at(mut self, x: u16, y: u16) -> Self {
self.position = (x, y);
self
}
pub fn text(&self) -> &str {
&self.text
}
pub fn delay(&self) -> Duration {
self.delay
}
pub fn position_near_mouse(
&self,
mouse_x: u16,
mouse_y: u16,
window_width: u16,
window_height: u16,
) -> (u16, u16) {
let tooltip_width = self.text.chars().count() as u16;
let tooltip_height = 1u16;
let max_x = window_width.saturating_sub(tooltip_width);
let max_y = window_height.saturating_sub(tooltip_height);
let mut x = mouse_x;
let mut y = mouse_y.saturating_add(1);
if x > max_x {
x = max_x;
}
if y > max_y {
y = max_y;
}
(x, y)
}
pub fn draw_at(&self, window: &mut dyn Window, x: u16, y: u16) -> Result<()> {
let (window_width, window_height) = window.get_size();
let text_len = self.text.chars().count() as u16;
if x >= window_width || y >= window_height {
return Ok(());
}
let text_to_show = if x + text_len <= window_width {
self.text.clone()
} else {
let max_len = window_width.saturating_sub(x);
let chars: Vec<char> = self.text.chars().take(max_len as usize).collect();
chars.into_iter().collect::<String>()
};
window.write_str_colored(y, x, &text_to_show, self.colors)
}
}
pub struct HoverTracker {
hover_start: Option<Instant>,
is_hovering: bool,
}
impl HoverTracker {
pub fn new() -> Self {
Self {
hover_start: None,
is_hovering: false,
}
}
pub fn start_hover(&mut self) {
if !self.is_hovering {
self.is_hovering = true;
self.hover_start = Some(Instant::now());
}
}
pub fn end_hover(&mut self) {
self.is_hovering = false;
self.hover_start = None;
}
pub fn hover_duration(&self) -> Option<Duration> {
if self.is_hovering {
Some(
self.hover_start
.map_or(Duration::ZERO, |start| start.elapsed()),
)
} else {
None
}
}
pub fn is_hovering(&self) -> bool {
self.is_hovering
}
pub fn should_show_tooltip(&self, delay: Duration) -> bool {
if let Some(duration) = self.hover_duration() {
duration >= delay
} else {
false
}
}
pub fn should_hide_tooltip(&self) -> bool {
!self.is_hovering
}
pub fn has_delay_passed(&self, delay: Duration) -> bool {
if let Some(duration) = self.hover_duration() {
duration >= delay
} else {
false
}
}
pub fn should_show_tooltip_once(&mut self, delay: Duration) -> bool {
let should_show = self.should_show_tooltip(delay);
if should_show {
self.end_hover();
}
should_show
}
}
impl Default for HoverTracker {
fn default() -> Self {
Self::new()
}
}