nightshade 0.14.1

A cross-platform data-oriented game engine.
Documentation
use crate::ecs::world::World;
use nalgebra_glm::Vec2;

use crate::ecs::ui::layout_types::FlowDirection;
use crate::ecs::ui::state::UiBase;

use super::date_utils::{format_month_year, populate_calendar_grid};

use crate::prelude::*;
pub fn ui_data_grid_enable_filters(world: &mut World, entity: freecs::Entity) {
    let columns = if let Some(data) = world.ui.get_ui_data_grid(entity) {
        data.columns.clone()
    } else {
        return;
    };

    let theme = world.resources.retained_ui.theme_state.active_theme();
    let font_size = theme.font_size;
    let input_bg = theme.input_background_color;
    let text_color = theme.text_color;
    let border_color = theme.border_color;
    let corner_radius = theme.corner_radius;
    let row_height = 28.0;

    let header_entity = if let Some(data) = world.ui.get_ui_data_grid(entity) {
        data.header_entities
            .first()
            .and_then(|header| world.core.get_parent(*header).and_then(|p| p.0))
    } else {
        None
    };

    let header_parent = if let Some(he) = header_entity {
        world.core.get_parent(he).and_then(|p| p.0)
    } else {
        None
    };

    let filter_row_parent = header_parent.unwrap_or(entity);

    let mut filter_input_entities = Vec::new();
    let mut filter_texts = Vec::new();

    let filter_row = {
        let mut tree = crate::ecs::ui::builder::UiTreeBuilder::new(world);
        tree.push_parent(filter_row_parent);
        let row_entity = tree
            .add_node()
            .flow_child(
                crate::ecs::ui::units::Rl(Vec2::new(100.0, 0.0))
                    + crate::ecs::ui::units::Ab(Vec2::new(0.0, row_height)),
            )
            .flow(FlowDirection::Horizontal, 0.0, 2.0)
            .entity();

        tree.push_parent(row_entity);

        for column in &columns {
            let text_slot = tree.world_mut().resources.text.cache.add_text("");
            let input = tree
                .add_node()
                .flow_child(crate::ecs::ui::units::Ab(Vec2::new(
                    column.width,
                    row_height,
                )))
                .with_rect(corner_radius, 1.0, border_color)
                .color_raw::<UiBase>(input_bg)
                .with_interaction()
                .with_children(|inner| {
                    inner
                        .add_node()
                        .with_text_slot(text_slot, font_size)
                        .color_raw::<UiBase>(text_color)
                        .entity();
                })
                .entity();
            filter_input_entities.push(input);
            filter_texts.push(String::new());
        }

        tree.pop_parent();
        tree.pop_parent();

        row_entity
    };

    if let Some(data) = world.ui.get_ui_data_grid_mut(entity) {
        data.filter_row_entity = Some(filter_row);
        data.filter_input_entities = filter_input_entities;
        data.filter_texts = filter_texts;
    }
}

pub fn ui_data_grid_set_filtered_indices(
    world: &mut World,
    entity: freecs::Entity,
    indices: Option<Vec<usize>>,
) {
    if let Some(data) = world.ui.get_ui_data_grid_mut(entity) {
        data.filtered_indices = indices;
    }
    ui_mark_layout_dirty(world);
}

pub fn ui_set_state_active(
    world: &mut World,
    entity: freecs::Entity,
    state_index: usize,
    active: bool,
) {
    if state_index >= crate::ecs::ui::state::STATE_COUNT {
        return;
    }
    if let Some(weights) = world.ui.get_ui_state_weights_mut(entity) {
        weights.targets[state_index] = if active { 1.0 } else { 0.0 };
        weights.start_weights[state_index] = weights.weights[state_index];
        weights.progress[state_index] = 0.0;
    }
}

pub fn ui_clicked(world: &World, entity: freecs::Entity) -> bool {
    widget::<crate::ecs::ui::components::UiButtonData>(world, entity).is_some_and(|d| d.clicked)
}

pub fn ui_set_reduced_motion(world: &mut World, enabled: bool) {
    world.resources.retained_ui.accessibility.reduced_motion = enabled;
}

pub fn ui_announce(world: &mut World, message: &str) {
    world
        .resources
        .retained_ui
        .accessibility
        .announce_queue
        .push(message.to_string());
}

pub fn ui_announcements(world: &World) -> &[String] {
    &world.resources.retained_ui.accessibility.announce_queue
}

pub fn ui_set_accessible_label(world: &mut World, entity: freecs::Entity, label: &str) {
    if let Some(interaction) = world.ui.get_ui_node_interaction_mut(entity) {
        interaction.accessible_label = Some(label.to_string());
    }
}

pub fn ui_multi_select_set_selected(world: &mut World, entity: freecs::Entity, indices: &[usize]) {
    let update = if let Some(data) = world.ui.get_ui_multi_select_mut(entity) {
        data.selected_indices = indices.iter().copied().collect();
        let count = data.selected_indices.len();
        let header_slot = data.header_text_slot;
        let header_text = format!("{count} selected");
        let check_updates: Vec<(freecs::Entity, bool)> = data
            .check_entities
            .iter()
            .enumerate()
            .map(|(index, &e)| (e, data.selected_indices.contains(&index)))
            .collect();
        Some((header_slot, header_text, check_updates))
    } else {
        None
    };
    if let Some((header_slot, header_text, check_updates)) = update {
        for (check_entity, selected) in check_updates {
            let check_text = if selected { "\u{2713}" } else { " " };
            if let Some(crate::ecs::ui::components::UiNodeContent::Text { text_slot, .. }) =
                world.ui.get_ui_node_content(check_entity)
            {
                let slot = *text_slot;
                world.resources.text.cache.set_text(slot, check_text);
            }
        }
        world
            .resources
            .text
            .cache
            .set_text(header_slot, &header_text);
    }
}

pub fn ui_date_picker_set_value(
    world: &mut World,
    entity: freecs::Entity,
    year: i32,
    month: u32,
    day: u32,
) {
    let header_text = format!("{year:04}-{month:02}-{day:02}");
    let update = world.ui.get_ui_date_picker(entity).map(|data| {
        (
            data.header_text_slot,
            data.month_label_slot,
            data.day_text_slots.clone(),
            data.day_entities.clone(),
        )
    });
    if let Some((header_slot, month_slot, day_slots, day_ents)) = update {
        world
            .resources
            .text
            .cache
            .set_text(header_slot, &header_text);
        let month_label = format_month_year(year, month);
        world
            .resources
            .text
            .cache
            .set_text(month_slot, &month_label);
        let theme = world.resources.retained_ui.theme_state.active_theme();
        let accent_color = theme.accent_color;
        populate_calendar_grid(world, year, month, day, &day_slots, &day_ents, accent_color);
        if let Some(data) = world.ui.get_ui_date_picker_mut(entity) {
            data.year = year;
            data.month = month;
            data.day = day;
        }
    }
}