orbtk-api 0.3.1-alpha3

API crate that provides base api and elements for OrbTk like widgets basis.
use std::collections::BTreeMap;

use dces::prelude::{Component, Entity, EntityComponentManager, StringComponentStore};

use crate::tree::Tree;

use super::State;

/// The `StatesContext` provides access to the widget states.
pub struct StatesContext<'a> {
    states: &'a mut BTreeMap<Entity, Box<dyn State>>,
    ecm: &'a mut EntityComponentManager<Tree, StringComponentStore>,
}

impl<'a> StatesContext<'a> {
    /// Creates a new state context.
    pub fn new(
        states: &'a mut BTreeMap<Entity, Box<dyn State>>,
        ecm: &'a mut EntityComponentManager<Tree, StringComponentStore>,
    ) -> Self {
        StatesContext { states, ecm }
    }

    // Mark the widget as dirty.
    fn mark_as_dirty(&mut self, entity: Entity) {
        *self
            .ecm
            .component_store_mut()
            .get_mut::<bool>("dirty", entity)
            .unwrap() = true;

        let root = self.ecm.entity_store().root();

        if let Ok(dirty_widgets) = self
            .ecm
            .component_store_mut()
            .get_mut::<Vec<Entity>>("dirty_widgets", root)
        {
            // don't add the same widget twice in a row
            if dirty_widgets.is_empty() || *dirty_widgets.last().unwrap() != entity {
                dirty_widgets.push(entity);
            }
        }
    }

    /// Gets the state of the given widget.
    ///
    /// # Panics
    ///
    /// Panics if the there is no state for the given entity or the given state type is wrong.
    pub fn get<S: Component>(&self, entity: Entity) -> &S {
        self.states
            .get(&entity)
            .unwrap_or_else(|| {
                panic!(
                    "StatesContext.get(): state for entity: {:?} could not be found.",
                    entity
                )
            })
            .as_any()
            .downcast_ref()
            .unwrap_or_else(|| {
                panic!(
                    "StatesContext.get(): wrong type of state for entity: {:?}",
                    entity
                )
            })
    }

    /// Gets a mutable state of the given widget.
    ///
    /// # Panics
    ///
    /// Panics if the there is no state for the given entity or the given state type is wrong.
    pub fn get_mut<S: Component>(&mut self, entity: Entity) -> &mut S {
        self.mark_as_dirty(entity);
        self.states
            .get_mut(&entity)
            .unwrap_or_else(|| {
                panic!(
                    "StatesContext.get(): state for entity: {:?} could not be found.",
                    entity
                )
            })
            .as_any_mut()
            .downcast_mut()
            .unwrap_or_else(|| {
                panic!(
                    "StatesContext.get(): wrong type of state for entity: {:?}",
                    entity
                )
            })
    }

    /// Try to get the state of the given widget.
    pub fn try_get<S: Component>(&self, entity: Entity) -> Option<&S> {
        if let Some(e) = self.states.get(&entity) {
            if let Some(r) = e.as_any().downcast_ref() {
                return Some(r);
            }
        }

        None
    }

    /// Try to get a mutable reference of the state of the given widget.
    pub fn try_get_mut<S: Component>(&mut self, entity: Entity) -> Option<&mut S> {
        self.mark_as_dirty(entity);
        if let Some(e) = self.states.get_mut(&entity) {
            if let Some(r) = e.as_any_mut().downcast_mut() {
                return Some(r);
            }
        }

        None
    }
}