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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use std::sync::{
    Arc,
    Mutex,
};

use dioxus_core::AttributeValue;
use dioxus_hooks::{
    use_memo,
    use_reactive,
    Dependency,
};
use dioxus_signals::{
    Memo,
    Readable,
};
use freya_core::custom_attributes::{
    CanvasReference,
    CanvasRunner,
    CanvasRunnerContext,
    CustomAttributeValues,
};

/// Holds a rendering hook callback that allows to render to the Canvas.
#[derive(PartialEq, Clone)]
pub struct UseCanvas {
    runner: Memo<UseCanvasRunner>,
}

#[derive(Clone)]
pub struct UseCanvasRunner(pub Arc<Mutex<CanvasRunner>>);

impl PartialEq for UseCanvasRunner {
    fn eq(&self, other: &Self) -> bool {
        Arc::ptr_eq(&self.0, &other.0)
    }
}

impl UseCanvas {
    pub fn attribute(&self) -> AttributeValue {
        AttributeValue::any_value(CustomAttributeValues::Canvas(CanvasReference {
            runner: self.runner.read().0.clone(),
        }))
    }
}

/// Register a rendering hook to gain access to the Canvas.
/// Reactivity managed through signals.
///
/// ## Usage
/// ```rust,no_run
/// # use freya::prelude::*;
/// fn app() -> Element {
///     let (reference, size) = use_node_signal();
///     let mut value = use_signal(|| 0);
///     let platform = use_platform();
///
///     let canvas = use_canvas(move || {
///         let curr = value();
///         platform.invalidate_drawing_area(size.peek().area);
///         platform.request_animation_frame();
///         move |ctx| {
///             // Draw using the canvas !
///             // use `curr`
///         }
///     });
///
///     rsx!(rect {
///         onclick: move |_| {
///             value += 1;
///         },
///         canvas_reference: canvas.attribute(),
///         reference,
///         width: "fill",
///         height: "fill",
///     })
/// }
/// ```
pub fn use_canvas<T: FnMut(&mut CanvasRunnerContext) + Sync + Send + 'static>(
    mut renderer_cb: impl FnMut() -> T + 'static,
) -> UseCanvas {
    let runner = use_memo(move || UseCanvasRunner(Arc::new(Mutex::new(renderer_cb()))));

    UseCanvas { runner }
}

/// Register a rendering hook to gain access to the Canvas.
/// Reactivity managed with manual dependencies.
///
/// ## Usage
/// ```rust,no_run
/// # use freya::prelude::*;
/// fn app() -> Element {
///     let (reference, size) = use_node_signal();
///     let mut value = use_signal(|| 0);
///     let platform = use_platform();
///
///     let canvas = use_canvas_with_deps(&value(), move |curr| {
///         platform.invalidate_drawing_area(size.peek().area);
///         platform.request_animation_frame();
///         move |ctx| {
///             // Draw using the canvas !
///             // use `curr`
///         }
///     });
///
///     rsx!(rect {
///         onclick: move |_| {
///             value += 1;
///         },
///         canvas_reference: canvas.attribute(),
///         reference,
///         width: "fill",
///         height: "fill",
///     })
/// }
/// ```
pub fn use_canvas_with_deps<
    D: Dependency,
    T: FnMut(&mut CanvasRunnerContext) + Sync + Send + 'static,
>(
    dependencies: D,
    mut renderer_cb: impl FnMut(D::Out) -> T + 'static,
) -> UseCanvas
where
    D::Out: 'static,
{
    let runner = use_memo(use_reactive(dependencies, move |dependencies| {
        UseCanvasRunner(Arc::new(Mutex::new(renderer_cb(dependencies))))
    }));

    UseCanvas { runner }
}