use std::f32::consts::PI;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct GlareHoverState {
pub position: f32,
pub is_hovering: bool,
pub hover_start_time: Option<f64>,
pub has_played: bool,
}
impl Default for GlareHoverState {
fn default() -> Self {
Self {
position: 0.0,
is_hovering: false,
hover_start_time: None,
has_played: false,
}
}
}
pub struct GlareHover {
pub angle: f32,
pub duration: f64,
pub size: f32,
pub opacity: f32,
pub play_once: bool,
}
impl Default for GlareHover {
fn default() -> Self {
Self {
angle: -45.0,
duration: 0.65,
size: 250.0,
opacity: 0.5,
play_once: false,
}
}
}
impl GlareHover {
pub fn new() -> Self {
Self::default()
}
pub fn with_angle(mut self, angle: f32) -> Self {
self.angle = angle;
self
}
pub fn with_duration(mut self, duration: f64) -> Self {
self.duration = duration;
self
}
pub fn with_size(mut self, size: f32) -> Self {
self.size = size;
self
}
pub fn with_opacity(mut self, opacity: f32) -> Self {
self.opacity = opacity;
self
}
pub fn with_play_once(mut self, play_once: bool) -> Self {
self.play_once = play_once;
self
}
pub fn set_hover(&self, state: &mut GlareHoverState, is_hovering: bool, current_time: f64) {
if is_hovering && !state.is_hovering {
if !self.play_once || !state.has_played {
state.hover_start_time = Some(current_time);
state.has_played = true;
}
} else if !is_hovering && state.is_hovering {
state.position = 0.0;
state.hover_start_time = None;
}
state.is_hovering = is_hovering;
}
pub fn update(&self, state: &mut GlareHoverState, current_time: f64) -> f32 {
if let Some(start_time) = state.hover_start_time {
let elapsed = current_time - start_time;
let progress = (elapsed / self.duration).min(1.0) as f32;
let eased = progress * (2.0 - progress);
state.position = eased;
}
state.position
}
pub fn calculate_gradient_position(&self, position: f32) -> (f32, f32) {
let x = -100.0 + position * 200.0;
let y = -100.0 + position * 200.0;
(x, y)
}
pub fn angle_radians(&self) -> f32 {
self.angle * PI / 180.0
}
}
#[derive(Debug, Clone, Copy)]
pub struct GradientStop {
pub position: f32, pub opacity: f32, }
impl GlareHover {
pub fn gradient_stops(&self) -> Vec<GradientStop> {
vec![
GradientStop {
position: 0.0,
opacity: 0.0,
},
GradientStop {
position: 0.6,
opacity: 0.0,
},
GradientStop {
position: 0.7,
opacity: self.opacity,
},
GradientStop {
position: 0.8,
opacity: 0.0,
},
GradientStop {
position: 1.0,
opacity: 0.0,
},
]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_glare_hover_init() {
let state = GlareHoverState::default();
assert_eq!(state.position, 0.0);
assert!(!state.is_hovering);
assert!(state.hover_start_time.is_none());
assert!(!state.has_played);
}
#[test]
fn test_glare_hover_activation() {
let glare = GlareHover::new();
let mut state = GlareHoverState::default();
glare.set_hover(&mut state, true, 0.0);
assert!(state.is_hovering);
assert_eq!(state.hover_start_time, Some(0.0));
assert!(state.has_played);
let pos = glare.update(&mut state, 0.325); assert!(pos > 0.0 && pos < 1.0);
assert_eq!(state.position, pos);
glare.update(&mut state, 1.0);
assert_eq!(state.position, 1.0);
}
#[test]
fn test_glare_hover_reset_on_unhover() {
let glare = GlareHover::new();
let mut state = GlareHoverState::default();
glare.set_hover(&mut state, true, 0.0);
glare.update(&mut state, 0.5);
assert!(state.position > 0.0);
glare.set_hover(&mut state, false, 0.5);
assert!(!state.is_hovering);
assert_eq!(state.position, 0.0);
assert!(state.hover_start_time.is_none());
}
#[test]
fn test_glare_play_once() {
let glare = GlareHover::new().with_play_once(true);
let mut state = GlareHoverState::default();
glare.set_hover(&mut state, true, 0.0);
assert!(state.hover_start_time.is_some());
assert!(state.has_played);
glare.set_hover(&mut state, false, 1.0);
glare.set_hover(&mut state, true, 2.0);
assert!(state.hover_start_time.is_none()); }
#[test]
fn test_gradient_position_calculation() {
let glare = GlareHover::new();
let (x, y) = glare.calculate_gradient_position(0.0);
assert_eq!(x, -100.0);
assert_eq!(y, -100.0);
let (x, y) = glare.calculate_gradient_position(0.5);
assert_eq!(x, 0.0);
assert_eq!(y, 0.0);
let (x, y) = glare.calculate_gradient_position(1.0);
assert_eq!(x, 100.0);
assert_eq!(y, 100.0);
}
#[test]
fn test_gradient_stops() {
let glare = GlareHover::new().with_opacity(0.8);
let stops = glare.gradient_stops();
assert_eq!(stops.len(), 5);
assert_eq!(stops[0].opacity, 0.0); assert_eq!(stops[2].opacity, 0.8); assert_eq!(stops[4].opacity, 0.0); }
#[test]
fn test_angle_conversion() {
let glare = GlareHover::new().with_angle(90.0);
let radians = glare.angle_radians();
assert!((radians - PI / 2.0).abs() < 0.001);
let glare = GlareHover::new().with_angle(-45.0);
let radians = glare.angle_radians();
assert!((radians + PI / 4.0).abs() < 0.001);
}
}