nightshade 0.13.1

A cross-platform data-oriented game engine.
Documentation
use crate::ecs::ui::components::UiWidgetState;
use crate::ecs::ui::state::UiStateTrait as _;
use crate::ecs::world::World;
use nalgebra_glm::Vec2;

use super::InteractionSnapshot;

pub(super) fn handle_toggle(
    world: &mut World,
    entity: freecs::Entity,
    interaction: &InteractionSnapshot,
    data: &crate::ecs::ui::components::UiToggleData,
    delta_time: f32,
) {
    let mut value = data.value;
    let mut changed = false;
    let mut animated_position = data.animated_position;

    if interaction.clicked {
        value = !value;
        changed = true;
    }

    let target = if value { 1.0 } else { 0.0 };
    let speed = 8.0;
    let diff = target - animated_position;
    if diff.abs() > 0.001 {
        animated_position += diff.signum() * speed * delta_time;
        animated_position = animated_position.clamp(0.0, 1.0);
    } else {
        animated_position = target;
    }

    let theme = world.resources.retained_ui.theme_state.active_theme();
    let toggle_height = theme.toggle_height;
    let toggle_width = theme.toggle_width;
    let on_color = theme.accent_color;
    let off_color = theme.background_color;
    let knob_padding = 2.0;
    let knob_size = toggle_height - knob_padding * 2.0;
    let knob_travel = toggle_width - knob_size - knob_padding * 2.0;
    let knob_x = knob_padding + animated_position * knob_travel;
    if let Some(knob_node) = world.ui.get_ui_layout_node_mut(data.knob_entity)
        && let Some(crate::ecs::ui::layout_types::UiLayoutType::Window(window)) =
            knob_node.layouts[crate::ecs::ui::state::UiBase::INDEX].as_mut()
    {
        window.position = crate::ecs::ui::units::Ab(Vec2::new(knob_x, knob_padding)).into();
    }
    let blended = off_color + (on_color - off_color) * animated_position;
    if let Some(color) = world.ui.get_ui_node_color_mut(entity) {
        color.colors[crate::ecs::ui::state::UiBase::INDEX] = Some(blended);
    }

    if let Some(UiWidgetState::Toggle(widget_data)) = world.ui.get_ui_widget_state_mut(entity) {
        widget_data.value = value;
        widget_data.changed = changed;
        widget_data.animated_position = animated_position;
    }
    if changed {
        world
            .resources
            .retained_ui
            .frame_events
            .push(crate::ecs::ui::resources::UiEvent::ToggleChanged { entity, value });
    }
}

pub(super) fn handle_checkbox(
    world: &mut World,
    entity: freecs::Entity,
    interaction: &InteractionSnapshot,
    data: &crate::ecs::ui::components::UiCheckboxData,
) {
    if interaction.clicked {
        let new_value = !data.value;
        if let Some(node) = world.ui.get_ui_layout_node_mut(data.inner_entity) {
            node.visible = new_value;
        }
        if let Some(UiWidgetState::Checkbox(widget_data)) = world.ui.get_ui_widget_state_mut(entity)
        {
            widget_data.value = new_value;
            widget_data.changed = true;
        }
        world.resources.retained_ui.frame_events.push(
            crate::ecs::ui::resources::UiEvent::CheckboxChanged {
                entity,
                value: new_value,
            },
        );
    } else if let Some(UiWidgetState::Checkbox(widget_data)) =
        world.ui.get_ui_widget_state_mut(entity)
    {
        widget_data.changed = false;
    }
}

pub(super) fn handle_radio(
    world: &mut World,
    entity: freecs::Entity,
    interaction: &InteractionSnapshot,
    data: &crate::ecs::ui::components::UiRadioData,
) {
    if let Some(UiWidgetState::Radio(widget_data)) = world.ui.get_ui_widget_state_mut(entity) {
        widget_data.changed = false;
    }

    if interaction.clicked && !data.selected {
        if let Some(node) = world.ui.get_ui_layout_node_mut(data.inner_entity) {
            node.visible = true;
        }

        let siblings = world
            .resources
            .retained_ui
            .radio_groups
            .get(&data.group_id)
            .cloned()
            .unwrap_or_default();

        for other_entity in siblings {
            if other_entity == entity {
                continue;
            }
            let other_data_clone = world.ui.get_ui_widget_state(other_entity).cloned();
            if let Some(UiWidgetState::Radio(other_radio)) = other_data_clone
                && other_radio.selected
            {
                if let Some(node) = world.ui.get_ui_layout_node_mut(other_radio.inner_entity) {
                    node.visible = false;
                }
                if let Some(UiWidgetState::Radio(wd)) =
                    world.ui.get_ui_widget_state_mut(other_entity)
                {
                    wd.selected = false;
                }
            }
        }

        if let Some(UiWidgetState::Radio(widget_data)) = world.ui.get_ui_widget_state_mut(entity) {
            widget_data.selected = true;
            widget_data.changed = true;
        }
        world.resources.retained_ui.frame_events.push(
            crate::ecs::ui::resources::UiEvent::RadioChanged {
                entity,
                group_id: data.group_id,
                option_index: data.option_index,
            },
        );
    }
}

pub(super) fn handle_collapsing_header(
    world: &mut World,
    entity: freecs::Entity,
    interaction: &InteractionSnapshot,
    data: &crate::ecs::ui::components::UiCollapsingHeaderData,
) {
    if let Some(UiWidgetState::CollapsingHeader(widget_data)) =
        world.ui.get_ui_widget_state_mut(entity)
    {
        widget_data.changed = false;
    }
    if interaction.clicked {
        let new_open = !data.open;
        if let Some(node) = world.ui.get_ui_layout_node_mut(data.content_entity) {
            node.visible = new_open;
        }
        world.resources.text_cache.set_text(
            data.arrow_text_slot,
            if new_open { "\u{25BC}" } else { "\u{25B6}" },
        );
        if let Some(UiWidgetState::CollapsingHeader(widget_data)) =
            world.ui.get_ui_widget_state_mut(entity)
        {
            widget_data.open = new_open;
            widget_data.changed = true;
        }
    }
}