web/dom/
mod.rs

1use crate::common::*;
2use js::*;
3use std::collections::HashMap;
4use std::sync::Arc;
5use std::sync::Mutex;
6
7pub fn query_selector(selector: &str) -> ExternRef {
8    let query_selector = js!(r#"
9        function(selector){
10            return document.querySelector(selector);
11        }"#);
12    query_selector.invoke_and_return_object(&[selector.into()])
13}
14
15pub fn element_set_inner_html(element: &ExternRef, html: &str) {
16    let set_inner_html = js!(r#"
17        function(element, html){
18            element.innerHTML = html;
19        }"#);
20    set_inner_html.invoke(&[element.into(), html.into()]);
21}
22
23pub fn element_add_class(element: &ExternRef, class: &str) {
24    let add_class = js!(r#"
25        function(element, class){
26            element.classList.add(class);
27        }"#);
28    add_class.invoke(&[element.into(), class.into()]);
29}
30
31pub fn element_remove_class(element: &ExternRef, class: &str) {
32    let remove_class = js!(r#"
33        function(element, class){
34            element.classList.remove(class);
35        }"#);
36    remove_class.invoke(&[element.into(), class.into()]);
37}
38
39pub fn element_set_style_attribute(element: &ExternRef, attribute: &str, value: &str) {
40    let set_style_attribute = js!(r#"
41        function(element, attribute, value){
42            element.style[attribute] = value;
43        }"#);
44    set_style_attribute.invoke(&[element.into(), attribute.into(), value.into()]);
45}
46
47pub fn element_set_attribute(element: &ExternRef, attribute: &str, value: &str) {
48    let set_attribute = js!(r#"
49        function(element, attribute, value){
50            element.setAttribute(attribute, value);
51        }"#);
52    set_attribute.invoke(&[element.into(), attribute.into(), value.into()]);
53}
54
55pub fn element_remove(element: &ExternRef) {
56    let remove = js!(r#"
57        function(element){
58            element.remove();
59        }"#);
60    remove.invoke(&[element.into()]);
61}
62
63// Change Events
64pub struct ChangeEvent {
65    pub value: String,
66}
67
68static CHANGE_EVENT_HANDLERS: Mutex<
69    Option<HashMap<Arc<FunctionHandle>, Box<dyn FnMut(ChangeEvent) + Send + 'static>>>,
70> = Mutex::new(None);
71
72fn add_change_event_handler(
73    id: Arc<FunctionHandle>,
74    handler: Box<dyn FnMut(ChangeEvent) + Send + 'static>,
75) {
76    let mut handlers = CHANGE_EVENT_HANDLERS.lock().unwrap();
77    if let Some(h) = handlers.as_mut() {
78        h.insert(id, handler);
79    } else {
80        let mut h = HashMap::new();
81        h.insert(id, handler);
82        *handlers = Some(h);
83    }
84}
85
86fn remove_change_event_handler(id: &Arc<FunctionHandle>) {
87    let mut handlers = CHANGE_EVENT_HANDLERS.lock().unwrap();
88    if let Some(h) = handlers.as_mut() {
89        h.remove(id);
90    }
91}
92
93#[no_mangle]
94pub extern "C" fn web_handle_change_event(id: i64, allocation_id: usize) {
95    let mut handlers = CHANGE_EVENT_HANDLERS.lock().unwrap();
96    if let Some(h) = handlers.as_mut() {
97        for (key, handler) in h.iter_mut() {
98            if key.0.value == id {
99                let value = extract_string_from_memory(allocation_id);
100                handler(ChangeEvent { value });
101            }
102        }
103    }
104}
105
106pub fn add_change_event_listener(
107    element: &ExternRef,
108    handler: impl FnMut(ChangeEvent) + Send + 'static,
109) -> Arc<FunctionHandle> {
110    let function_ref = js!(r#"
111        function(element ){
112            const handler = (e) => {
113                const value = e.target.value;
114                const allocationId = this.writeUtf8ToMemory(value);
115                this.module.instance.exports.web_handle_change_event_handler(id, allocationId);
116            };
117            const id = this.storeObject(handler);
118            element.addEventListener("change",handler);
119            return id;
120        }"#)
121    .invoke_and_return_bigint(&[element.into()]);
122    let function_handle = Arc::new(FunctionHandle(ExternRef {
123        value: function_ref,
124    }));
125    add_change_event_handler(function_handle.clone(), Box::new(handler));
126    function_handle
127}
128
129pub fn element_remove_change_listener(element: &ExternRef, function_handle: &Arc<FunctionHandle>) {
130    let remove_change_listener = js!(r#"
131        function(element, f){
132            element.removeEventListener("change", f);
133        }"#);
134    remove_change_listener.invoke(&[element.into(), (&(function_handle.0)).into()]);
135    remove_change_event_handler(function_handle);
136}
137
138// Mouse Events
139pub struct MouseEvent {
140    pub offset_x: f64,
141    pub offset_y: f64,
142}
143
144static MOUSE_EVENT_HANDLER: EventHandler<MouseEvent> = EventHandler {
145    listeners: Mutex::new(None),
146};
147
148#[no_mangle]
149pub extern "C" fn web_handle_mouse_event_handler(id: i64, x: f64, y: f64) {
150    MOUSE_EVENT_HANDLER.call(
151        id,
152        MouseEvent {
153            offset_x: x,
154            offset_y: y,
155        },
156    );
157}
158
159pub fn element_add_click_listener(
160    element: &ExternRef,
161    handler: impl FnMut(MouseEvent) + Send + 'static,
162) -> Arc<FunctionHandle> {
163    let function_ref = js!(r#"
164        function(element ){
165            const handler = (e) => {
166                this.module.instance.exports.web_handle_mouse_event_handler(id,e.offsetX, e.offsetY);
167            };
168            const id = this.storeObject(handler);
169            element.addEventListener("click",handler);
170            return id;
171        }"#).invoke_and_return_bigint(&[element.into()]);
172    let function_handle = Arc::new(FunctionHandle(ExternRef {
173        value: function_ref,
174    }));
175    MOUSE_EVENT_HANDLER.add_listener(function_handle.clone(), Box::new(handler));
176    function_handle
177}
178
179pub fn element_remove_click_listener(element: &ExternRef, function_handle: &Arc<FunctionHandle>) {
180    let remove_click_listener = js!(r#"
181        function(element, f){
182            element.removeEventListener("click", f);
183        }"#);
184    remove_click_listener.invoke(&[element.into(), (&(function_handle.0)).into()]);
185    MOUSE_EVENT_HANDLER.remove_listener(function_handle);
186}
187
188pub fn element_add_mouse_move_listener(
189    element: &ExternRef,
190    handler: impl FnMut(MouseEvent) + Send + 'static,
191) -> Arc<FunctionHandle> {
192    let function_ref = js!(r#"
193        function(element ){
194            const handler = (e) => {
195                this.module.instance.exports.web_handle_mouse_event_handler(id,e.offsetX, e.offsetY);
196            };
197            const id = this.storeObject(handler);
198            element.addEventListener("mousemove",handler);
199            return id;
200        }"#).invoke_and_return_bigint(&[element.into()]);
201    let function_handle = Arc::new(FunctionHandle(ExternRef {
202        value: function_ref,
203    }));
204    MOUSE_EVENT_HANDLER.add_listener(function_handle.clone(), Box::new(handler));
205    function_handle
206}
207
208pub fn element_remove_mouse_move_listener(
209    element: &ExternRef,
210    function_handle: &Arc<FunctionHandle>,
211) {
212    let remove_mouse_move_listener = js!(r#"
213        function(element, f){
214            element.removeEventListener("mousemove", f);
215        }"#);
216    remove_mouse_move_listener.invoke(&[element.into(), (&(function_handle.0)).into()]);
217    MOUSE_EVENT_HANDLER.remove_listener(function_handle);
218}
219
220pub fn element_add_mouse_down_listener(
221    element: &ExternRef,
222    handler: impl FnMut(MouseEvent) + Send + 'static,
223) -> Arc<FunctionHandle> {
224    let function_ref = js!(r#"
225        function(element ){
226            const handler = (e) => {
227                this.module.instance.exports.web_handle_mouse_event_handler(id,e.offsetX, e.offsetY);
228            };
229            const id = this.storeObject(handler);
230            element.addEventListener("mousedown",handler);
231            return id;
232        }"#).invoke_and_return_bigint(&[element.into()]);
233    let function_handle = Arc::new(FunctionHandle(ExternRef {
234        value: function_ref,
235    }));
236    MOUSE_EVENT_HANDLER.add_listener(function_handle.clone(), Box::new(handler));
237    function_handle
238}
239
240pub fn element_remove_mouse_down_listener(
241    element: &ExternRef,
242    function_handle: &Arc<FunctionHandle>,
243) {
244    let remove_mouse_down_listener = js!(r#"
245        function(element, f){
246            element.removeEventListener("mousedown", f);
247        }"#);
248    remove_mouse_down_listener.invoke(&[element.into(), (&(function_handle.0)).into()]);
249    MOUSE_EVENT_HANDLER.remove_listener(function_handle);
250}
251
252pub fn element_add_mouse_up_listener(
253    element: &ExternRef,
254    handler: impl FnMut(MouseEvent) + Send + 'static,
255) -> Arc<FunctionHandle> {
256    let function_ref = js!(r#"
257        function(element ){
258            const handler = (e) => {
259                this.module.instance.exports.web_handle_mouse_event_handler(id,e.offsetX, e.offsetY);
260            };
261            const id = this.storeObject(handler);
262            element.addEventListener("mouseup",handler);
263            return id;
264        }"#).invoke_and_return_bigint(&[element.into()]);
265    let function_handle = Arc::new(FunctionHandle(ExternRef {
266        value: function_ref,
267    }));
268    MOUSE_EVENT_HANDLER.add_listener(function_handle.clone(), Box::new(handler));
269    function_handle
270}
271
272pub fn element_remove_mouse_up_listener(
273    element: &ExternRef,
274    function_handle: &Arc<FunctionHandle>,
275) {
276    let remove_mouse_up_listener = js!(r#"
277        function(element, f){
278            element.removeEventListener("mouseup", f);
279        }"#);
280    remove_mouse_up_listener.invoke(&[element.into(), (&(function_handle.0)).into()]);
281    MOUSE_EVENT_HANDLER.remove_listener(function_handle);
282}
283
284// Keyboard Events
285
286pub struct KeyboardEvent {
287    pub key_code: f64,
288}
289
290static KEYBOARD_EVENT_HANDLERS: Mutex<
291    Option<HashMap<Arc<FunctionHandle>, Box<dyn FnMut(KeyboardEvent) + Send + 'static>>>,
292> = Mutex::new(None);
293
294fn add_keyboard_event_handler(
295    function_handle: Arc<FunctionHandle>,
296    handler: Box<dyn FnMut(KeyboardEvent) + Send + 'static>,
297) {
298    let mut h = KEYBOARD_EVENT_HANDLERS.lock().unwrap();
299    if h.is_none() {
300        *h = Some(HashMap::new());
301    }
302    h.as_mut().unwrap().insert(function_handle, handler);
303}
304
305fn remove_keyboard_event_handler(function_handle: &Arc<FunctionHandle>) {
306    let mut h = KEYBOARD_EVENT_HANDLERS.lock().unwrap();
307    if h.is_none() {
308        return;
309    }
310    h.as_mut().unwrap().remove(function_handle);
311}
312
313#[no_mangle]
314pub extern "C" fn web_handle_keyboard_event_handler(id: i64, key_code: f64) {
315    let mut handlers = KEYBOARD_EVENT_HANDLERS.lock().unwrap();
316    if let Some(h) = handlers.as_mut() {
317        for (key, handler) in h.iter_mut() {
318            if key.0.value == id {
319                handler(KeyboardEvent { key_code });
320            }
321        }
322    }
323}
324
325pub fn element_add_key_down_listener(
326    element: &ExternRef,
327    handler: impl FnMut(KeyboardEvent) + Send + 'static,
328) -> Arc<FunctionHandle> {
329    let function_ref = js!(r#"
330        function(element ){
331            const handler = (e) => {
332                this.module.instance.exports.web_handle_keyboard_event_handler(id,e.keyCode);
333            };
334            const id = this.storeObject(handler);
335            element.addEventListener("keydown",handler);
336            return id;
337        }"#)
338    .invoke_and_return_bigint(&[element.into()]);
339    let function_handle = Arc::new(FunctionHandle(ExternRef {
340        value: function_ref,
341    }));
342    add_keyboard_event_handler(function_handle.clone(), Box::new(handler));
343    function_handle
344}
345
346pub fn element_remove_key_down_listener(
347    element: &ExternRef,
348    function_handle: &Arc<FunctionHandle>,
349) {
350    let remove_key_down_listener = js!(r#"
351        function(element, f){
352            element.removeEventListener("keydown", f);
353        }"#);
354    remove_key_down_listener.invoke(&[element.into(), (&(function_handle.0)).into()]);
355    remove_keyboard_event_handler(function_handle);
356}
357
358pub fn element_add_key_up_listener(
359    element: &ExternRef,
360    handler: impl FnMut(KeyboardEvent) + Send + 'static,
361) -> Arc<FunctionHandle> {
362    let function_ref = js!(r#"
363        function(element ){
364            const handler = (e) => {
365                this.module.instance.exports.web_handle_keyboard_event_handler(id,e.keyCode);
366            };
367            const id = this.storeObject(handler);
368            element.addEventListener("keyup",handler);
369            return id;
370        }"#)
371    .invoke_and_return_bigint(&[element.into()]);
372    let function_handle = Arc::new(FunctionHandle(ExternRef {
373        value: function_ref,
374    }));
375    add_keyboard_event_handler(function_handle.clone(), Box::new(handler));
376    function_handle
377}
378
379pub fn element_remove_key_up_listener(element: &ExternRef, function_handle: &Arc<FunctionHandle>) {
380    let remove_key_up_listener = js!(r#"
381        function(element, f){
382            element.removeEventListener("keyup", f);
383        }"#);
384    remove_key_up_listener.invoke(&[element.into(), (&(function_handle.0)).into()]);
385    remove_keyboard_event_handler(function_handle);
386}