cotis 0.1.0-alpha

Modular Rust UI framework core
Documentation
use crate::utils::OwnedOrRefMut;
use std::marker::PhantomData;

/// A trait signaling the type can be to configure element attributes
///
/// This is type is used in [`ConfiguredParentElement`] and is required as a
/// type to be provided by the [`LayoutManager`](crate::layout::LayoutManager).
pub trait ConfigureElements<ConfigType> {
    /// Signals the start of a new element
    ///
    /// The [`Seal`] type is meant just as a precaution to prevent
    /// users of the library from calling the function directly. Instead,
    /// the caller should use [`ConfiguredParentElement::new_element`].
    fn open_new_element(&mut self, _: Seal);
    fn set_config(&mut self, config: ConfigType, _: Seal);
    fn close_element(&mut self, _: Seal);
}

/// A trait signaling the type can be used to configure element that can have no childs
///
/// This is type is used in [`ConfiguredParentElement`] and is required as a
/// type to be provided by the [`LayoutManager`](crate::layout::LayoutManager).
pub trait ConfigureLeafElements<ConfigType> {
    /// Sets configuration intended for leaf-only element data.
    fn set_leaf_config(&mut self, config: ConfigType, _: Seal);
}

/// A type meant to prevent the people using this library from calling methods that they shouldn't
/// directly. This is required if we need other crate developers to implement a trait that we
/// don't want the end user to call directly
pub struct Seal(());

/// A type meant for adding elements to the root of the layout UI
pub struct ConfiguredParentElement<'element, Configurer: ConfigureElements<ConfigType>, ConfigType>
{
    layout: OwnedOrRefMut<'element, Configurer>,
    phantom_data: PhantomData<ConfigType>,
}

impl<'element, Configurer: ConfigureElements<ConfigType>, ConfigType>
    ConfiguredParentElement<'element, Configurer, ConfigType>
{
    /// Creates a new `Self`
    pub fn new(layout: OwnedOrRefMut<'element, Configurer>) -> Self {
        Self {
            layout,
            phantom_data: Default::default(),
        }
    }

    /// Returns an [`OpenElement`] that can be used to configure the attributes of this new UI element.
    /// It also attaches the element to the root of the UI
    ///
    /// # Examples
    ///
    /// ```rust
    /// use cotis::element_configuring::{ConfigureElements, ConfiguredParentElement, Seal};
    /// use cotis::utils::OwnedOrRefMut;
    ///
    /// #[derive(Default)]
    /// struct MockConfigurer {
    ///     opened: usize,
    /// }
    ///
    /// impl ConfigureElements<()> for MockConfigurer {
    ///     fn open_new_element(&mut self, _: Seal) { self.opened += 1; }
    ///     fn set_config(&mut self, _: (), _: Seal) {}
    ///     fn close_element(&mut self, _: Seal) {}
    /// }
    ///
    /// let mut configurer = MockConfigurer::default();
    /// let mut root = ConfiguredParentElement::<_, ()>::new(OwnedOrRefMut::Ref(&mut configurer));
    /// root.new_element().set_config(());
    /// drop(root);
    /// assert_eq!(configurer.opened, 1);
    /// ```
    pub fn new_element(&'_ mut self) -> OpenElement<'_, Configurer, ConfigType>
    where
        Self: Sized,
    {
        self.layout.as_mut().open_new_element(Seal(()));
        OpenElement {
            layout: Some(self.layout.reborrow()),
            phantom_data: Default::default(),
        }
    }

    /// Closes the UI element
    ///
    /// It's not strictly necessary to call this method, the actual logic of closing the element gets executed when the
    /// [`OpenElement`] gets dropped.
    pub fn close_element(self) {
        drop(self)
    }
}

/// Handle to an element that has been opened and can still be configured.
///
/// When dropped, the element is automatically closed.
pub struct OpenElement<'element, Configurer: ConfigureElements<ConfigType>, ConfigType> {
    layout: Option<OwnedOrRefMut<'element, Configurer>>,
    phantom_data: PhantomData<ConfigType>,
}

impl<'element, ConfigType, Configurer: ConfigureElements<ConfigType>>
    OpenElement<'element, Configurer, ConfigType>
{
    /// Sets the configuration for this UI element
    pub fn set_config(&mut self, config: ConfigType) {
        self.layout
            .as_mut()
            .unwrap()
            .as_mut()
            .set_config(config, Seal(()));
    }

    /// Converts this open element into a parent scope where nested children can be added.
    ///
    /// # Panics (Todo this is not true, because of how is executed this should never happen, probably should proof)
    ///
    /// Panics if this value has already transferred its internal layout handle.
    pub fn make_parent(mut self) -> ConfiguredParentElement<'element, Configurer, ConfigType> {
        ConfiguredParentElement {
            layout: self.layout.take().unwrap(),
            phantom_data: Default::default(),
        }
    }

    /// Closes the UI element
    ///
    /// It's not strictly necessary to call this method, the actual logic of closing the element gets executed when the
    /// [`OpenElement`] gets dropped.
    pub fn close_element(self) {
        drop(self)
    }

    /// Sets leaf-only configuration and keeps this element as a leaf.
    ///
    /// Use this when your configurer differentiates parent and leaf payloads.
    ///
    /// # Panics (Todo this is not true, because of how is executed this should never happen, probably should proof)
    ///
    /// Panics if this value has already transferred its internal layout handle.
    pub fn set_leaf_config<LeafConfig>(mut self, config: LeafConfig)
    where
        Configurer: ConfigureLeafElements<LeafConfig>,
    {
        self.layout
            .as_mut()
            .unwrap()
            .as_mut()
            .set_leaf_config(config, Seal(()));
    }
}

/// Handle to a leaf element configuration scope.
///
/// When dropped, the element is automatically closed.
pub struct OpenLeafElement<'element, Configurer: ConfigureElements<ConfigType>, ConfigType> {
    layout: OwnedOrRefMut<'element, Configurer>,
    phantom_data: PhantomData<ConfigType>,
}

impl<'element, ConfigType, Configurer: ConfigureElements<ConfigType>>
    OpenLeafElement<'element, Configurer, ConfigType>
{
    /// Sets the configuration for this UI element
    pub fn set_config(&mut self, config: ConfigType) {
        self.layout.as_mut().set_config(config, Seal(()));
    }

    /// Sets leaf-only configuration for this element.
    pub fn set_leaf_config<LeafConfig>(&mut self, config: LeafConfig)
    where
        Configurer: ConfigureLeafElements<LeafConfig>,
    {
        self.layout.as_mut().set_leaf_config(config, Seal(()));
    }

    /// Closes the UI element
    ///
    /// It's not strictly necessary to call this method, the actual logic of closing the element gets executed when the
    /// [`OpenElement`] gets dropped.
    pub fn close_element(self) {
        drop(self)
    }
}

/// Ensures all the open elements get closed
impl<'element, Configurer: ConfigureElements<ConfigType>, ConfigType> Drop
    for OpenElement<'element, Configurer, ConfigType>
{
    fn drop(&mut self) {
        if let Some(layout) = &mut self.layout {
            layout.as_mut().close_element(Seal(()));
        }
    }
}

/// Ensures all the open elements get closed
impl<'element, ConfigType, Configurer: ConfigureElements<ConfigType>> Drop
    for ConfiguredParentElement<'element, Configurer, ConfigType>
{
    fn drop(&mut self) {
        self.layout.as_mut().close_element(Seal(()));
    }
}

/// Ensures all the open elements get closed
impl<'element, ConfigType, Configurer: ConfigureElements<ConfigType>> Drop
    for OpenLeafElement<'element, Configurer, ConfigType>
{
    fn drop(&mut self) {
        self.layout.as_mut().close_element(Seal(()));
    }
}