1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
use std::fmt::{Debug, Error, Formatter};
use std::rc::Rc;

/// A callback property for sub-[`Component`][Component]s.
///
/// When a subcomponent needs to communicate with its parent, you can use a `Callback`
/// to simulate a signal handler.
///
/// This is how you would declare a callback property that receives a [`String`][String] when
/// something happens inside the subcomponent. The property value should be a closure
/// which receives the [`String`][String] and returns a message for the parent component, of the
/// parent component's [`Component::Message`][Message] type. The framework will automatically take
/// care of figuring out the callback's type signature when you mount the subcomponent.
///
/// Note that the [`Default`][Default] implementation for `Callback` constructs an empty callback,
/// which does nothing and allocates nothing. This is the desired behaviour when using a callback
/// as a [`Component`][Component] property: if the user doesn't specify a callback explicitly, there
/// shouldn't be a callback.
///
/// ```rust,no_run
/// # use vgtk::Callback;
/// struct MyComponentProperties {
///     on_message: Callback<String>,
/// }
/// ```
///
/// This is how you might provide a callback property to the above:
///
/// ```rust,no_run
/// # use vgtk::{gtk, VNode, Component, Callback};
/// # #[derive(Clone, Debug, Default)]
/// # pub struct MyComponent {
/// #     pub on_message: Callback<String>,
/// # }
/// # impl Component for MyComponent {
/// #     type Message = ();
/// #     type Properties = Self;
/// #     fn view(&self) -> VNode<Self> { todo!() }
/// # }
/// # #[derive(Clone, Debug)] enum ParentMessage { StringReceived(String) }
/// # #[derive(Default)] struct Parent;
/// # impl Component for Parent { type Message = ParentMessage; type Properties = ();
/// # fn view(&self) -> VNode<Self> { gtk! {
/// <@MyComponent on_message=|string| ParentMessage::StringReceived(string) />
/// # }}}
/// ```
///
/// [Component]: trait.Component.html
/// [Message]: trait.Component.html#associatedtype.Message
/// [Default]: https://doc.rust-lang.org/std/default/trait.Default.html
/// [String]: https://doc.rust-lang.org/std/string/struct.String.html
/// [Option]: https://doc.rust-lang.org/std/option/enum.Option.html
pub struct Callback<A>(pub(crate) Option<Rc<dyn Fn(A)>>);

impl<A> Callback<A> {
    /// Send a value to the callback.
    ///
    /// If the callback is empty, this has no effect.
    pub fn send(&self, value: A) {
        if let Some(callback) = &self.0 {
            callback(value)
        }
    }

    /// Test whether a callback is empty.
    pub fn is_empty(&self) -> bool {
        self.0.is_none()
    }
}

impl<A> Default for Callback<A> {
    fn default() -> Self {
        Callback(None)
    }
}

impl<A> Clone for Callback<A> {
    fn clone(&self) -> Self {
        Callback(self.0.clone())
    }
}

impl<A> PartialEq for Callback<A> {
    fn eq(&self, other: &Self) -> bool {
        if let (Some(left), Some(right)) = (&self.0, &other.0) {
            Rc::ptr_eq(left, right)
        } else {
            false
        }
    }
}

impl<A> Debug for Callback<A> {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
        write!(f, "Callback()")
    }
}

impl<A, F: Fn(A) + 'static> From<F> for Callback<A> {
    fn from(func: F) -> Self {
        Callback(Some(Rc::new(func)))
    }
}