use crate::primitives::toolbar::{ToolbarHit, ToolbarLayout};
use crate::types::WidgetId;
#[derive(Debug, Default, Clone)]
pub struct ToolbarHoverTracker {
hovered: Option<WidgetId>,
}
impl ToolbarHoverTracker {
pub fn new() -> Self {
Self { hovered: None }
}
pub fn update(&mut self, layout: &ToolbarLayout, x: f32, y: f32) -> bool {
let new_hover = match layout.hit_test(x, y) {
ToolbarHit::Button(id) => Some(id),
ToolbarHit::Empty => None,
};
if new_hover != self.hovered {
self.hovered = new_hover;
true
} else {
false
}
}
pub fn clear(&mut self) -> bool {
if self.hovered.is_some() {
self.hovered = None;
true
} else {
false
}
}
pub fn hovered_id(&self) -> Option<&WidgetId> {
self.hovered.as_ref()
}
pub fn current(&self) -> Option<WidgetId> {
self.hovered
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::primitives::toolbar::{Toolbar, ToolbarButton, ToolbarItemMeasure};
fn mk_action(id: &str, label: &str) -> ToolbarButton {
ToolbarButton::Action {
id: WidgetId::new(id),
label: label.to_string(),
icon: None,
key_hint: None,
enabled: true,
is_active: false,
tooltip: String::new(),
}
}
fn sample_layout() -> ToolbarLayout {
let bar = Toolbar {
id: WidgetId::new("tb"),
buttons: vec![mk_action("a", "A"), mk_action("b", "B")],
bg: None,
focused_index: None,
};
bar.layout(0.0, 0.0, 40.0, 1.0, |_| ToolbarItemMeasure::new(6.0))
}
#[test]
fn update_records_button_under_cursor() {
let layout = sample_layout();
let mut t = ToolbarHoverTracker::new();
assert!(t.update(&layout, 1.0, 0.0));
assert_eq!(t.hovered_id().map(|i| i.as_str()), Some("a"));
}
#[test]
fn update_returns_false_when_unchanged() {
let layout = sample_layout();
let mut t = ToolbarHoverTracker::new();
assert!(t.update(&layout, 1.0, 0.0));
assert!(!t.update(&layout, 2.0, 0.0));
}
#[test]
fn update_returns_true_when_moving_between_buttons() {
let layout = sample_layout();
let mut t = ToolbarHoverTracker::new();
assert!(t.update(&layout, 1.0, 0.0)); assert!(t.update(&layout, 7.0, 0.0)); assert_eq!(t.hovered_id().map(|i| i.as_str()), Some("b"));
}
#[test]
fn update_clears_when_cursor_leaves_buttons() {
let layout = sample_layout();
let mut t = ToolbarHoverTracker::new();
assert!(t.update(&layout, 1.0, 0.0));
assert!(t.update(&layout, 30.0, 0.0));
assert!(t.hovered_id().is_none());
}
#[test]
fn clear_resets_hover_state() {
let layout = sample_layout();
let mut t = ToolbarHoverTracker::new();
t.update(&layout, 1.0, 0.0);
assert!(t.clear());
assert!(t.hovered_id().is_none());
assert!(!t.clear());
}
}