gooey/
application.rs

1//! Application layer.
2//!
3//! The `Application` trait defines the associated type of generic callback ID
4//! that can be converted with concrete callback IDs and dereferenced into
5//! associated `Callback` functions.
6//!
7//! A `Callback` function takes a mutable reference to the `Application`, a
8//! model data component (possibly empty), and an interface node ID.
9//!
10//! Callbacks are submitted to the `Application` as `Event`s in the output of
11//! the `Interface::update()` function.
12//!
13//! Additional `Event`s notify the `Application` of changes in model data
14//! components. It is left to application code to handle `Event`s, including
15//! callbacks, and is free to ignore them.
16//!
17//! `Application`s can also optionally define custom controls for handling input
18//! in interface widgets (see `interface::controller::controls` module).
19
20use std::convert::TryFrom;
21use strum::FromRepr;
22use crate::tree::NodeId;
23use crate::interface::model;
24use crate::interface::controller::controls;
25
26/// Implements a type context of application callbacks and control callbacks
27pub trait Application : Sized {
28  type CallbackIds    : std::fmt::Debug + Into <CallbackId>
29    + TryFrom <CallbackId>
30    + std::ops::Deref <Target = Callback <Self>>;
31  // extra controls
32  type AxisControls    : controls::Id <controls::Axis>    = controls::Nil;
33  type ButtonControls  : controls::Id <controls::Button>  = controls::Nil;
34  type MotionControls  : controls::Id <controls::Motion>  = controls::Nil;
35  type PointerControls : controls::Id <controls::Pointer> = controls::Nil;
36  type SystemControls  : controls::Id <controls::System>  = controls::Nil;
37  type TextControls    : controls::Id <controls::Text>    = controls::Nil;
38  type WheelControls   : controls::Id <controls::Wheel>   = controls::Nil;
39}
40
41/// Concrete callback representation
42#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
43pub struct CallbackId (pub CallbackReprType);
44/// Primitive index type of callback IDs
45pub type CallbackReprType = u16;
46
47/// A parameterized application callback function signature
48pub type Callback <A> = fn (&mut A, model::Component, NodeId);
49
50/// Map a concrete callback ID into its call-able type parameter.
51///
52/// It is fatal error if the concrete ID does not correspond to any existing
53/// callback.
54pub fn callback <A : Application> (callback_id : CallbackId) -> A::CallbackIds {
55  A::CallbackIds::try_from (callback_id).ok().unwrap()
56}
57
58/// A default application definition with `Nil` associated callback types
59#[derive(Debug, Default)]
60pub struct Default;
61
62impl Application for Default {
63  type CallbackIds     = Nil;
64  type AxisControls    = controls::Nil;
65  type ButtonControls  = controls::Nil;
66  type MotionControls  = controls::Nil;
67  type PointerControls = controls::Nil;
68  type SystemControls  = controls::Nil;
69  type TextControls    = controls::Nil;
70  type WheelControls   = controls::Nil;
71}
72
73/// Type representing no callbacks
74#[derive(Debug, Eq, PartialEq, FromRepr)]
75pub enum Nil { }
76
77impl From <Nil> for CallbackId {
78  fn from (_ : Nil) -> CallbackId {
79    unreachable!()
80  }
81}
82impl std::convert::TryFrom <CallbackId> for Nil {
83  type Error = CallbackId;
84  fn try_from (_ : CallbackId) -> Result <Self, CallbackId> {
85    unreachable!()
86  }
87}
88impl std::ops::Deref for Nil {
89  type Target = fn (&mut Default, model::Component, NodeId);
90  fn deref (&self) -> &fn (&mut Default, model::Component, NodeId) {
91    unreachable!()
92  }
93}
94
95/// Define an enum where each variant derefs into an application [`Callback`]
96#[macro_export]
97macro_rules! def_callback_ids {
98  ( $callback_ids:ident <$application:ty> {
99      $($callback_id:ident => $callback:path)*
100    }
101  ) => {
102    #[derive(Debug, Eq, PartialEq, $crate::strum::FromRepr)]
103    // NOTE: repr must match $crate::application::CallbackReprType
104    #[repr(u16)]
105    pub enum $callback_ids {
106      $($callback_id),*
107    }
108
109    impl From <$callback_ids> for $crate::application::CallbackId {
110      fn from (id : $callback_ids) -> $crate::application::CallbackId {
111        $crate::application::CallbackId (
112          id as $crate::application::CallbackReprType
113        )
114      }
115    }
116
117    impl std::convert::TryFrom <$crate::application::CallbackId>
118      for $callback_ids
119    {
120      type Error = $crate::application::CallbackId;
121      fn try_from (callback_id : $crate::application::CallbackId)
122        -> Result <Self, $crate::application::CallbackId>
123      {
124        $callback_ids::from_repr (callback_id.0).ok_or (callback_id)
125      }
126    }
127
128    impl std::ops::Deref for $callback_ids {
129      type Target = fn (&mut $application,
130        $crate::interface::model::Component, $crate::tree::NodeId);
131      fn deref (&self) -> &fn (&mut $application,
132        $crate::interface::model::Component, $crate::tree::NodeId
133      ) {
134        match self {
135          $($callback_ids :: $callback_id => {
136            static F : fn (&mut $application,
137              $crate::interface::model::Component, $crate::tree::NodeId
138            ) = $callback;
139            &F
140          })*
141        }
142      }
143    }
144  }
145}