use crate::ecs::{PointF, Rect};
use bevy_ecs::prelude::*;
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum TooltipArea {
WholeWindow,
Rect(Rect),
}
#[derive(Debug, Clone, PartialEq)]
pub struct TooltipRange {
pub area: TooltipArea,
pub text: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TooltipRangeId {
window: Entity,
serial: u64,
}
impl TooltipRangeId {
pub fn window(&self) -> Entity {
self.window
}
}
#[derive(Component, Debug, Default)]
pub struct TooltipRanges {
entries: Vec<(TooltipRangeId, TooltipRange)>,
next_serial: u64,
}
impl TooltipRanges {
pub(crate) fn add(&mut self, window: Entity, range: TooltipRange) -> TooltipRangeId {
let id = TooltipRangeId {
window,
serial: self.next_serial,
};
self.next_serial += 1;
self.entries.push((id, normalize(range)));
id
}
pub(crate) fn replace(&mut self, id: TooltipRangeId, range: TooltipRange) -> bool {
match self.entries.iter_mut().find(|(k, _)| *k == id) {
Some((_, r)) => {
*r = normalize(range);
true
}
None => false,
}
}
pub(crate) fn remove(&mut self, id: TooltipRangeId) -> bool {
let before = self.entries.len();
self.entries.retain(|(k, _)| *k != id);
self.entries.len() != before
}
pub(crate) fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub(crate) fn get(&self, id: TooltipRangeId) -> Option<&TooltipRange> {
self.entries.iter().find(|(k, _)| *k == id).map(|(_, r)| r)
}
pub(crate) fn hit(&self, logical: PointF) -> Option<(TooltipRangeId, &TooltipRange)> {
self.entries
.iter()
.rev()
.find(|(_, r)| contains(r.area, logical))
.map(|(k, r)| (*k, r))
}
}
fn normalize(mut range: TooltipRange) -> TooltipRange {
if range.text.as_deref() == Some("") {
range.text = None;
}
range
}
fn contains(area: TooltipArea, p: PointF) -> bool {
match area {
TooltipArea::WholeWindow => true,
TooltipArea::Rect(r) => r.left <= p.x && p.x < r.right && r.top <= p.y && p.y < r.bottom,
}
}
#[cfg(test)]
#[path = "ranges_tests.rs"]
mod ranges_tests;