gooey-rs 0.12.1

Tile-based UI library with audio support
Documentation
//! Application layer.
//!
//! The `Application` trait defines the associated type of generic callback ID that can
//! be converted with concrete callback IDs and dereferenced into associated `Callback`
//! functions.
//!
//! A `Callback` function takes a mutable reference to the `Application`, a model data
//! component (possibly empty), and an interface node ID.
//!
//! Callbacks are submitted to the `Application` as `Event`s in the output of the
//! `Interface::update()` function.
//!
//! Additional `Event`s notify the `Application` of changes in model data components. It
//! is left to application code to handle `Event`s, including callbacks, and is free to
//! ignore them.
//!
//! `Application`s can also optionally define custom controls for handling input in
//! interface widgets (see `interface::controller::controls` module).

use std::convert::TryFrom;
use strum::FromRepr;
use crate::tree::NodeId;
use crate::interface::model;
use crate::interface::controller::controls;

/// Implements a type context of application callbacks and control callbacks
pub trait Application : Sized {
  type CallbackIds    : std::fmt::Debug + Into <CallbackId> + TryFrom <CallbackId>
    + std::ops::Deref <Target = Callback <Self>>;
  // extra controls
  type AxisControls    : controls::Id <controls::Axis>    = controls::Nil;
  type ButtonControls  : controls::Id <controls::Button>  = controls::Nil;
  type MotionControls  : controls::Id <controls::Motion>  = controls::Nil;
  type PointerControls : controls::Id <controls::Pointer> = controls::Nil;
  type SystemControls  : controls::Id <controls::System>  = controls::Nil;
  type TextControls    : controls::Id <controls::Text>    = controls::Nil;
  type WheelControls   : controls::Id <controls::Wheel>   = controls::Nil;
}

/// Concrete callback representation
#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
pub struct CallbackId (pub CallbackReprType);
/// Primitive index type of callback IDs
pub type CallbackReprType = u16;

/// A parameterized application callback function signature
pub type Callback <A> = fn (&mut A, model::Component, NodeId);

/// Map a concrete callback ID into its call-able type parameter.
///
/// It is fatal error if the concrete ID does not correspond to any existing callback.
pub fn callback <A : Application> (callback_id : CallbackId) -> A::CallbackIds {
  A::CallbackIds::try_from (callback_id).ok().unwrap()
}

/// A default application definition with `Nil` associated callback types
#[derive(Debug, Default)]
pub struct Default;

impl Application for Default {
  type CallbackIds     = Nil;
  type AxisControls    = controls::Nil;
  type ButtonControls  = controls::Nil;
  type MotionControls  = controls::Nil;
  type PointerControls = controls::Nil;
  type SystemControls  = controls::Nil;
  type TextControls    = controls::Nil;
  type WheelControls   = controls::Nil;
}

/// Type representing no callbacks
#[derive(Debug, Eq, PartialEq, FromRepr)]
pub enum Nil { }

impl From <Nil> for CallbackId {
  fn from (_ : Nil) -> CallbackId {
    unreachable!()
  }
}
impl std::convert::TryFrom <CallbackId> for Nil {
  type Error = CallbackId;
  fn try_from (_ : CallbackId) -> Result <Self, CallbackId> {
    unreachable!()
  }
}
impl std::ops::Deref for Nil {
  type Target = fn (&mut Default, model::Component, NodeId);
  fn deref (&self) -> &fn (&mut Default, model::Component, NodeId) {
    unreachable!()
  }
}

/// Define an enum where each variant derefs into an application [`Callback`]
#[macro_export]
macro_rules! def_callback_ids {
  ( $callback_ids:ident <$application:ty> {
      $($callback_id:ident => $callback:path)*
    }
  ) => {
    #[derive(Debug, Eq, PartialEq, $crate::strum::FromRepr)]
    // NOTE: repr must match $crate::application::CallbackReprType
    #[repr(u16)]
    pub enum $callback_ids {
      $($callback_id),*
    }

    impl From <$callback_ids> for $crate::application::CallbackId {
      fn from (id : $callback_ids) -> $crate::application::CallbackId {
        $crate::application::CallbackId (
          id as $crate::application::CallbackReprType
        )
      }
    }

    impl std::convert::TryFrom <$crate::application::CallbackId> for $callback_ids {
      type Error = $crate::application::CallbackId;
      fn try_from (callback_id : $crate::application::CallbackId)
        -> Result <Self, $crate::application::CallbackId>
      {
        $callback_ids::from_repr (callback_id.0).ok_or (callback_id)
      }
    }

    impl std::ops::Deref for $callback_ids {
      type Target = fn (&mut $application,
        $crate::interface::model::Component, $crate::tree::NodeId);
      fn deref (&self) -> &fn (&mut $application,
        $crate::interface::model::Component, $crate::tree::NodeId
      ) {
        match self {
          $($callback_ids :: $callback_id => {
            static F : fn (
              &mut $application,
              $crate::interface::model::Component,
              $crate::tree::NodeId
            ) = $callback;
            &F
          })*
        }
      }
    }
  }
}