use nalgebra_glm::Vec2;
use crate::ecs::ui::state::{STATE_COUNT, UiBase, UiStateTrait};
#[derive(Clone, Debug, PartialEq)]
pub enum AccessibleRole {
Button,
Slider,
Checkbox,
Toggle,
TextInput,
TextArea,
Dropdown,
Tab,
TabPanel,
Tree,
TreeItem,
Grid,
GridCell,
Dialog,
Alert,
ProgressBar,
Menu,
MenuItem,
}
#[derive(Clone, Debug, Default)]
pub struct UiNodeInteraction {
pub hovered: bool,
pub pressed: bool,
pub clicked: bool,
pub focused: bool,
pub drag_start: Option<Vec2>,
pub dragging: bool,
pub cursor_icon: Option<winit::window::CursorIcon>,
pub double_clicked: bool,
pub right_clicked: bool,
pub tooltip_text: Option<String>,
pub tooltip_entity: Option<freecs::Entity>,
pub tab_index: Option<i32>,
pub disabled: bool,
pub error_text: Option<String>,
pub validation_rules: Vec<ValidationRule>,
pub accessible_role: Option<AccessibleRole>,
pub accessible_label: Option<String>,
pub test_id: Option<String>,
}
#[derive(Clone, Copy, Debug)]
pub struct StateTransition {
pub enter_speed: f32,
pub exit_speed: f32,
pub easing: crate::ecs::tween::easing::EasingFunction,
}
impl Default for StateTransition {
fn default() -> Self {
Self {
enter_speed: 10.0,
exit_speed: 4.0,
easing: crate::ecs::tween::easing::EasingFunction::Linear,
}
}
}
#[derive(Clone, Debug)]
pub struct UiStateWeights {
pub weights: Vec<f32>,
pub transitions: Vec<Option<StateTransition>>,
pub progress: Vec<f32>,
pub targets: Vec<f32>,
pub start_weights: Vec<f32>,
}
impl Default for UiStateWeights {
fn default() -> Self {
let mut weights = vec![0.0; STATE_COUNT];
weights[UiBase::INDEX] = 1.0;
let mut targets = vec![0.0; STATE_COUNT];
targets[UiBase::INDEX] = 1.0;
Self {
weights: weights.clone(),
transitions: vec![None; STATE_COUNT],
progress: vec![1.0; STATE_COUNT],
targets,
start_weights: weights,
}
}
}
impl UiStateWeights {
pub fn ensure_state_capacity(&mut self, count: usize) {
if self.weights.len() < count {
self.weights.resize(count, 0.0);
}
if self.transitions.len() < count {
self.transitions.resize(count, None);
}
if self.progress.len() < count {
self.progress.resize(count, 1.0);
}
if self.targets.len() < count {
self.targets.resize(count, 0.0);
}
if self.start_weights.len() < count {
self.start_weights.resize(count, 0.0);
}
}
}
#[derive(Clone)]
pub enum ValidationRule {
Required,
MinLength(usize),
MaxLength(usize),
Custom(fn(&str) -> Result<(), String>),
}
impl std::fmt::Debug for ValidationRule {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Required => write!(formatter, "Required"),
Self::MinLength(n) => write!(formatter, "MinLength({})", n),
Self::MaxLength(n) => write!(formatter, "MaxLength({})", n),
Self::Custom(_) => write!(formatter, "Custom(fn)"),
}
}
}
impl ValidationRule {
pub fn validate(&self, value: &str) -> Result<(), String> {
match self {
Self::Required => {
if value.trim().is_empty() {
Err("This field is required".to_string())
} else {
Ok(())
}
}
Self::MinLength(min) => {
if value.chars().count() < *min {
Err(format!("Minimum {} characters required", min))
} else {
Ok(())
}
}
Self::MaxLength(max) => {
if value.chars().count() > *max {
Err(format!("Maximum {} characters allowed", max))
} else {
Ok(())
}
}
Self::Custom(validator) => validator(value),
}
}
}