gooey-rs 0.12.1

Tile-based UI library with audio support
Documentation
//! Contextual interface elements.
//!
//! `Widget`s are parameterized references to combinations of possibly concrete
//! component types. Each widget also defines its *control functions*.
//!
//! Most widgets are graphical in some way and are parented to a `Frame` widget that
//! provides the primary container and layout functionalities.
//!
//! Non-graphical widgets are the `Form` widget, which is completely generic, and the
//! `Playback` widget which has at least an `Sfx` view component and can be parented to
//! any other widget.

use std::convert::TryFrom;
use crate::{Tree, TreeHelper};
use crate::interface::{controller, model, view, Action, Element};
use crate::tree::NodeId;

pub mod button;
pub mod field;
pub mod form;
pub mod frame;
pub mod menu;
pub mod numbox;
pub mod picture;
pub mod playback;
pub mod textbox;
pub use self::button::Button;
pub use self::field::Field;
pub use self::form::Form;
pub use self::frame::Frame;
pub use self::menu::Menu;
pub use self::numbox::Numbox;
pub use self::picture::Picture;
pub use self::playback::Playback;
pub use self::textbox::Textbox;

pub mod composite;

/// Helper for widget builders.
///
/// Because the `"owned"` builder pattern is used, propagating optional parameters
/// requires the syntax:
/// ```ignore
/// if let Some (some) = some_option {
///    some_builder = some_builder.some_setter (some);
/// }
/// ```
/// and this macro reduces the syntax to a one-liner.
pub (crate) macro set_option ($builder:ident, $setter:ident, $option:expr) {
  if let Some (inner) = $option {
    $builder = $builder.$setter (inner);
  }
}

/// Defines a reference to a particular combination of component kinds in an element
/// node.
///
/// To allow a component to range over any type, each 'Variant' also implements 'Kind'.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Widget <'element, C, M, V> (
  pub &'element C,
  pub &'element M,
  pub &'element V
) where
  C : controller::component::Kind,
  M : model::component::Kind,
  V : view::component::Kind;

impl <'element, C, M, V> TryFrom <&'element Element> for Widget <'element, C, M, V> where
  C : controller::component::Kind,
  M : model::component::Kind,
  V : view::component::Kind
{
  type Error = &'element Element;
  fn try_from (element : &'element Element) -> Result <Self, Self::Error> {
    let controller = C::try_ref (&element.controller.component).ok_or (element)?;
    let model      = M::try_ref (&element.model.component).ok_or (element)?;
    let view       = V::try_ref (&element.view.component).ok_or (element)?;
    Ok (Widget::<'element, C, M, V> (controller, model, view))
  }
}

impl <'element, C, M, V> Widget <'element, C, M, V> where
  C : controller::component::Kind,
  M : model::component::Kind,
  V : view::component::Kind
{
  /// Try to cast the target Element to the Widget type. Target node must exist
  pub fn try_get (elements: &'element Tree <Element>, node_id : &NodeId)
    -> Result <Self, &'element Element>
  {
    let element = elements.get_element (node_id);
    Self::try_from (element)
  }
}

/// Trait for widget `Element` builders
pub trait BuildElement {
  fn build_element (self) -> Element;
}

/// Trait for widgets that are built from a list of `Action`s
pub trait BuildActions {
  fn build_actions (self) -> Vec <(NodeId, Action)>;
}

/// Process the build actions and return the Nth created node ID (starting from 0)
pub macro build_and_return_node_id {
  ($interface:expr, $builder:expr) => {
    build_and_return_node_id!($interface, $builder, 0)
  },
  ($interface:expr, $builder:expr, $nth:expr) => {{
    let actions    = $builder.build_actions();
    let mut events = $interface.actions (actions);
    let mut count  = 0;
    loop {
      match events.next().unwrap() {
        (_, $crate::interface::model::Event::Create (_, new_id, _)) =>
          if count == $nth {
            break new_id
          } else {
            count += 1;
          }
        _ => {}
      }
    }
  }}
}

/// Process the build actions and return the created node IDs with the given names
pub macro build_and_get_node_ids {
  ($interface:expr, $builder:expr, $names:expr) => {{
    let actions   = $builder.build_actions();
    let events    = $interface.actions (actions).collect::<Vec <_>>();
    let mut names = $names.map (|name| Some (name));
    let mut ids   = $names.map (|_| None);
    for event in events {
      match event {
        (_, $crate::interface::model::Event::Create (_, new_id, _)) => {
          let name = $interface.get_element (&new_id).name.as_str();
          if let Some (i) = names.iter().position (|n| n == &Some (name)) {
            names[i] = None;
            ids[i]   = Some (new_id);
          }
        }
        _ => {}
      }
    }
    ids.map (Option::unwrap)
  }}
}