mittens-engine 0.6.0

A Vulkan and OpenXR scene engine with ECS, reactive signals, and Meow Meow scripting
Documentation
use crate::engine::ecs::{ComponentId, component::Component};

use super::ComponentRef;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SelectionMode {
    Single,
    Multiple,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SelectionEntry {
    pub index: Option<usize>,
    pub component: ComponentId,
}

#[derive(Debug, Clone)]
pub struct SelectionComponent {
    pub mode: SelectionMode,
    pub allow_empty_single: bool,
    pub target_root_source: Option<ComponentRef>,
    pub selected_index: Option<usize>,
    pub selected_component: Option<ComponentId>,
    pub selected_payload: Option<ComponentId>,
    pub selected_entries: Vec<SelectionEntry>,
    component: Option<ComponentId>,
}

impl SelectionComponent {
    pub fn new() -> Self {
        Self {
            mode: SelectionMode::Single,
            allow_empty_single: false,
            target_root_source: None,
            selected_index: None,
            selected_component: None,
            selected_payload: None,
            selected_entries: Vec::new(),
            component: None,
        }
    }

    pub fn multiple() -> Self {
        Self {
            mode: SelectionMode::Multiple,
            ..Self::new()
        }
    }

    pub fn optional() -> Self {
        Self {
            allow_empty_single: true,
            ..Self::new()
        }
    }

    pub fn is_multiple(&self) -> bool {
        matches!(self.mode, SelectionMode::Multiple)
    }

    pub fn clear(&mut self) {
        self.selected_index = None;
        self.selected_component = None;
        self.selected_payload = None;
        self.selected_entries.clear();
    }

    pub fn select_entry(&mut self, entry: SelectionEntry) {
        self.selected_index = entry.index;
        self.selected_component = Some(entry.component);
        self.selected_payload = None;
        self.selected_entries.clear();
        self.selected_entries.push(entry);
    }

    pub fn contains(&self, component: ComponentId) -> bool {
        self.selected_entries
            .iter()
            .any(|entry| entry.component == component)
    }

    pub fn toggle_entry(&mut self, entry: SelectionEntry) -> bool {
        if !self.is_multiple() {
            self.select_entry(entry);
            return true;
        }

        if let Some(index) = self
            .selected_entries
            .iter()
            .position(|selected| selected.component == entry.component)
        {
            self.selected_entries.remove(index);
            self.sync_primary_from_entries();
            return false;
        }

        self.selected_entries.push(entry.clone());
        self.selected_index = entry.index;
        self.selected_component = Some(entry.component);
        self.selected_payload = None;
        true
    }

    fn sync_primary_from_entries(&mut self) {
        if let Some(entry) = self.selected_entries.last().cloned() {
            self.selected_index = entry.index;
            self.selected_component = Some(entry.component);
            self.selected_payload = None;
        } else {
            self.selected_index = None;
            self.selected_component = None;
            self.selected_payload = None;
        }
    }
}

impl Component for SelectionComponent {
    fn set_id(&mut self, id: ComponentId) {
        self.component = Some(id);
    }

    fn name(&self) -> &'static str {
        "selection"
    }

    fn as_any(&self) -> &dyn std::any::Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
        self
    }

    fn to_mms_ast(
        &self,
        _world: &crate::engine::ecs::World,
    ) -> crate::scripting::ast::ComponentExpression {
        use crate::engine::ecs::component::ce_helpers::*;
        let mut expr = match self.mode {
            SelectionMode::Single => {
                if self.allow_empty_single {
                    ce_call("Selection", "optional", vec![])
                } else {
                    ce_call("Selection", "", vec![])
                }
            }
            SelectionMode::Multiple => ce_call("Selection", "multiple", vec![]),
        };
        if let Some(source) = &self.target_root_source {
            let arg = match source {
                ComponentRef::Guid(uuid) => s(&format!("@uuid:{uuid}")),
                ComponentRef::Query(selector) => s(selector),
            };
            expr.constructors
                .push(crate::scripting::ast::ConstructorCall {
                    method: crate::scripting::ast::Ident("root".to_string()),
                    args: vec![arg],
                });
        }
        expr
    }
}