use crate::ecs::ui::components::UiAnimationPhase;
use crate::ecs::ui::state::{
STATE_COUNT, UiBase, UiDisabled, UiFocused, UiHover, UiPressed, UiStateTrait,
};
use crate::ecs::world::World;
pub fn ui_layout_state_update_system(world: &mut World) {
if !world.resources.retained_ui.enabled {
return;
}
let delta_time = world.resources.window.timing.delta_time;
let reduced_motion = world.resources.retained_ui.reduced_motion;
let total_state_count = world.resources.retained_ui.total_state_count();
let entities: Vec<freecs::Entity> = world
.ui
.query_entities(crate::ecs::world::UI_STATE_WEIGHTS)
.collect();
let mut any_weight_changed = false;
for entity in entities {
let (hover_target, pressed_target, focused_target, disabled_target) = {
match world.ui.get_ui_node_interaction(entity) {
Some(interaction) => (
if interaction.hovered { 1.0 } else { 0.0 },
if interaction.pressed { 1.0 } else { 0.0 },
if interaction.focused { 1.0 } else { 0.0 },
if interaction.disabled { 1.0 } else { 0.0 },
),
None => (0.0, 0.0, 0.0, 0.0),
}
};
if let Some(state_weights) = world.ui.get_ui_state_weights_mut(entity) {
state_weights.ensure_state_capacity(total_state_count);
let targets = [
(UiHover::INDEX, hover_target),
(UiPressed::INDEX, pressed_target),
(UiFocused::INDEX, focused_target),
(UiDisabled::INDEX, disabled_target),
];
for (index, target) in targets {
let previous = state_weights.weights[index];
if (state_weights.targets[index] - target).abs() > f32::EPSILON {
state_weights.targets[index] = target;
state_weights.start_weights[index] = previous;
state_weights.progress[index] = 0.0;
}
if reduced_motion {
state_weights.weights[index] = state_weights.targets[index];
state_weights.progress[index] = 1.0;
} else if state_weights.progress[index] < 1.0 {
let transition = state_weights.transitions[index].unwrap_or_default();
let speed = if target > state_weights.start_weights[index] {
transition.enter_speed
} else {
transition.exit_speed
};
state_weights.progress[index] =
(state_weights.progress[index] + speed * delta_time).min(1.0);
let eased = transition.easing.evaluate(state_weights.progress[index]);
let start = state_weights.start_weights[index];
state_weights.weights[index] =
(start + (target - start) * eased).clamp(0.0, 1.0);
}
if (state_weights.weights[index] - previous).abs() > f32::EPSILON {
any_weight_changed = true;
}
}
for index in STATE_COUNT..state_weights.weights.len() {
let previous = state_weights.weights[index];
let target = state_weights.targets[index];
if reduced_motion {
state_weights.weights[index] = target;
state_weights.progress[index] = 1.0;
} else if state_weights.progress[index] < 1.0 {
let transition = state_weights.transitions[index].unwrap_or_default();
let speed = if target > state_weights.start_weights[index] {
transition.enter_speed
} else {
transition.exit_speed
};
state_weights.progress[index] =
(state_weights.progress[index] + speed * delta_time).min(1.0);
let eased = transition.easing.evaluate(state_weights.progress[index]);
let start = state_weights.start_weights[index];
state_weights.weights[index] =
(start + (target - start) * eased).clamp(0.0, 1.0);
}
if (state_weights.weights[index] - previous).abs() > f32::EPSILON {
any_weight_changed = true;
}
}
let non_base_sum: f32 = state_weights.weights[1..].iter().sum();
state_weights.weights[UiBase::INDEX] = (1.0 - non_base_sum).clamp(0.0, 1.0);
}
}
if any_weight_changed {
world.resources.retained_ui.layout_dirty = true;
}
let anim_entities: Vec<freecs::Entity> = world
.ui
.query_entities(crate::ecs::world::UI_LAYOUT_NODE)
.collect();
for entity in anim_entities {
let should_hide = if let Some(node) = world.ui.get_ui_layout_node_mut(entity)
&& let Some(animation) = &mut node.animation
{
match animation.phase {
UiAnimationPhase::IntroPlaying => {
animation.progress += delta_time / animation.duration;
if animation.progress >= 1.0 {
animation.progress = 1.0;
animation.phase = UiAnimationPhase::Idle;
}
world.resources.retained_ui.layout_dirty = true;
false
}
UiAnimationPhase::OutroPlaying => {
animation.progress += delta_time / animation.duration;
if animation.progress >= 1.0 {
animation.progress = 1.0;
animation.phase = UiAnimationPhase::OutroComplete;
world.resources.retained_ui.layout_dirty = true;
true
} else {
world.resources.retained_ui.layout_dirty = true;
false
}
}
_ => false,
}
} else {
false
};
if should_hide && let Some(node) = world.ui.get_ui_layout_node_mut(entity) {
node.visible = false;
}
}
world.ui_tick_toasts();
}