1use core::fmt;
2use std::fmt::Debug;
3
4use crate::speobject::SPEObject;
5use serde::{Deserialize, Serialize};
6use wasm_bindgen::prelude::*;
7use wasm_bindgen::JsValue;
8use web_sys::HtmlCanvasElement;
9
10#[allow(non_camel_case_types)]
12#[derive(Clone, Copy, Debug)]
13#[wasm_bindgen]
14pub enum SplineEventName {
15 mouseDown,
16 mouseUp,
17 mouseHover,
18 keyDown,
19 keyUp,
20 start,
21 lookAt,
22 follow,
23 scroll,
24 collision,
25 rendered,
26}
27
28impl fmt::Display for SplineEventName {
29 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
30 write!(f, "{:?}", self)
31 }
32}
33
34#[derive(Deserialize, Serialize, Debug)]
35pub struct SplineEvent {
36 pub target: Target,
37}
38
39#[derive(Deserialize, Serialize, Debug)]
40pub struct Target {
41 pub name: String,
42 pub id: String,
43}
44
45impl From<JsValue> for SplineEvent {
46 fn from(value: JsValue) -> Self {
47 serde_wasm_bindgen::from_value(value).unwrap()
48 }
49}
50
51#[wasm_bindgen(module = "/src/runtime.js")]
52extern "C" {
53
54 #[derive(Clone)]
55 #[wasm_bindgen]
56 type Application;
57
58 #[wasm_bindgen(constructor)]
59 fn new_(canvas: &HtmlCanvasElement, render_on_demand: bool) -> Application;
60
61 #[wasm_bindgen(method, js_name = load)]
62 async fn load_(this: &Application, scene: String);
63
64 #[wasm_bindgen(method, js_name = addEventListener)]
65 fn add_event_listener_(
66 this: &Application,
67 event_name: &str,
68 callback: &Closure<dyn FnMut(JsValue)>,
69 );
70
71 #[wasm_bindgen(method, js_name = removeEventListener)]
72 fn remove_event_listener_(this: &Application, event_name: &str);
73
74 #[wasm_bindgen(method, js_name = dispose)]
75 fn dispose_(this: &Application);
76
77 #[wasm_bindgen(method, js_name = findObjectByName)]
78 fn find_object_by_name_(this: &Application, name: String) -> JsValue;
79
80 #[wasm_bindgen(method, js_name = findObjectById)]
81 fn find_object_by_id_(this: &Application, id: String) -> JsValue;
82
83 #[wasm_bindgen(method, js_name = getAllObjects)]
84 fn get_all_objects_(this: &Application) -> Vec<JsValue>;
85
86 #[wasm_bindgen(method, js_name = emitEvent)]
87 fn emit_event_(this: &Application, eventName: SplineEventName, nameOrUuid: String);
88
89 #[wasm_bindgen(method, js_name = emitEventReverse)]
90 fn emit_event_reverse_(this: &Application, eventName: SplineEventName, nameOrUuid: String);
91
92 #[wasm_bindgen(method, js_name = setZoom)]
93 fn set_zoom_(this: &Application, zoomValue: f64);
94
95 #[wasm_bindgen(method, js_name = setBackgroundColor)]
96 fn set_background_color_(this: &Application, color: String);
97
98 #[wasm_bindgen(method, js_name = setGlobalEvents)]
100 fn set_global_events_(this: &Application, global: bool);
101
102 #[wasm_bindgen(method, js_name = setSize)]
103 fn set_size_(this: &Application, width: f64, height: f64);
104
105 #[wasm_bindgen(method, getter, js_name = data)]
107 fn data_(this: &Application) -> JsValue;
108
109 #[wasm_bindgen(method, getter, js_name = eventManager)]
111 fn event_manager_(this: &Application) -> JsValue;
112
113 #[wasm_bindgen(method, getter, js_name = controls)]
115 fn controls_(this: &Application) -> JsValue;
116
117 #[wasm_bindgen(method, getter, js_name = isStopped)]
119 fn is_stopped_(this: &Application) -> bool;
120
121 #[wasm_bindgen(method, js_name = stop)]
123 fn stop_(this: &Application);
124
125 #[wasm_bindgen(method, js_name = play)]
127 fn play_(this: &Application);
128
129 #[wasm_bindgen(method, js_name = requestRender)]
131 fn request_render_(this: &Application);
132
133 #[wasm_bindgen(method, js_name = setVariables)]
135 fn set_variables_(this: &Application, variables: JsValue);
136
137 #[wasm_bindgen(method, js_name = setVariable)]
139 fn set_variable_(this: &Application, name: String, value: JsValue);
140
141 #[wasm_bindgen(method, js_name = getVariables)]
143 fn get_variables_(this: &Application) -> JsValue;
144
145 #[wasm_bindgen(method, js_name = getVariable)]
147 fn get_variable_(this: &Application, name: String) -> JsValue;
148}
149
150#[derive(Clone)]
151pub struct SplineApplication {
152 app: Application,
153}
154
155impl SplineApplication {
156 pub fn new(canvas: &HtmlCanvasElement, render_on_demand: bool) -> Self {
157 let app = Application::new_(canvas, render_on_demand);
158 SplineApplication { app }
159 }
160
161 pub async fn load(&self, scene: String) {
162 self.app.load_(scene).await;
163 }
164
165 pub fn add_event_listener<F>(&self, event_name: &str, callback: F)
166 where
167 F: FnMut(JsValue) + 'static,
168 {
169 let closure = Closure::wrap(Box::new(callback) as Box<dyn FnMut(JsValue)>);
170 self.app.add_event_listener_(event_name, &closure);
171 closure.forget();
172 }
173
174 pub fn remove_event_listener(&self, event_name: &str) {
175 self.app.remove_event_listener_(event_name);
176 }
177
178 pub fn dispose(&self) {
179 self.app.dispose_();
180 }
181
182 pub fn find_object_by_name(&self, name: String) -> SPEObject {
183 let obj = self.app.find_object_by_name_(name);
184 SPEObject::new(obj)
185 }
186
187 pub fn find_object_by_id(&self, id: String) -> SPEObject {
188 let obj = self.app.find_object_by_id_(id);
189 SPEObject::new(obj)
190 }
191
192 pub fn get_all_objects(&self) -> Vec<SPEObject> {
193 let objs = self.app.get_all_objects_();
194 objs.iter().map(|obj| SPEObject::new(obj.clone())).collect()
195 }
196
197 #[deprecated(note = "Experimental")]
198 pub fn emit_event(&self, event_name: SplineEventName, name_or_uuid: String) {
199 self.app.emit_event_(event_name, name_or_uuid);
200 }
201
202 #[deprecated(note = "Experimental")]
203 pub fn emit_event_reverse(&self, event_name: SplineEventName, name_or_uuid: String) {
204 self.app.emit_event_reverse_(event_name, name_or_uuid);
205 }
206
207 #[deprecated(note = "Experimental")]
208 pub fn set_zoom(&self, zoom_value: f64) {
209 self.app.set_zoom_(zoom_value);
210 }
211
212 pub fn set_background_color(&self, color: String) {
213 self.app.set_background_color_(color);
214 }
215
216 #[deprecated(note = "Experimental")]
217 pub fn set_global_events(&self, global: bool) {
218 self.app.set_global_events_(global);
219 }
220
221 pub fn set_size(&self, width: f64, height: f64) {
222 self.app.set_size_(width, height);
223 }
224
225 #[deprecated(note = "Experimental")]
226 pub fn data(&self) -> JsValue {
227 self.app.data_()
228 }
229
230 #[deprecated(note = "Experimental")]
231 pub fn event_manager(&self) -> JsValue {
232 self.app.event_manager_()
233 }
234
235 #[deprecated(note = "Experimental")]
236 pub fn controls(&self) -> JsValue {
237 self.app.controls_()
238 }
239
240 #[deprecated(note = "Experimental")]
241 pub fn is_stopped(&self) -> bool {
242 self.app.is_stopped_()
243 }
244
245 #[deprecated(note = "Experimental")]
246 pub fn stop(&self) {
247 self.app.stop_();
248 }
249
250 #[deprecated(note = "Experimental")]
251 pub fn play(&self) {
252 self.app.play_();
253 }
254
255 #[deprecated(note = "Experimental")]
256 pub fn request_render(&self) {
257 self.app.request_render_();
258 }
259
260 #[deprecated(note = "Experimental")]
261 pub fn set_variables(&self, variables: JsValue) {
262 self.app.set_variables_(variables);
263 }
264
265 #[deprecated(note = "Experimental")]
266 pub fn set_variable(&self, name: String, value: JsValue) {
267 self.app.set_variable_(name, value);
268 }
269
270 #[deprecated(note = "Experimental")]
271 pub fn get_variables(&self) -> JsValue {
272 self.app.get_variables_()
273 }
274
275 #[deprecated(note = "Experimental")]
276 pub fn get_variable(&self, name: String) -> JsValue {
277 self.app.get_variable_(name)
278 }
279}