1use super::completion_helper::{await_promise, PromiseOutcome};
2use super::{non_null, JsObject};
3use crate::browser_top_level_impl::BrowserTopLevelImpl;
4use wasm_bindgen::prelude::*;
5
6#[wasm_bindgen(raw_module = "./ferroui.js")]
7extern "C" {
8 pub type HtmlInputElement;
11
12 #[wasm_bindgen(method, getter, js_name = selectionStart)]
13 fn selection_start_raw(this: &HtmlInputElement) -> Option<i32>;
14
15 #[wasm_bindgen(method, getter, js_name = selectionEnd)]
16 fn selection_end_raw(this: &HtmlInputElement) -> Option<i32>;
17
18 #[wasm_bindgen(js_namespace = InputHelper, js_name = subscribeInputEvents)]
22 pub fn subscribe_input_events(html_element: &JsObject, top_level_id: i32) -> JsObject;
23
24 #[wasm_bindgen(js_namespace = InputHelper, js_name = unsubscribeInputEvents)]
28 pub fn unsubscribe_input_events(subscription: &JsObject);
29
30 #[wasm_bindgen(js_namespace = InputHelper, js_name = getCoalescedEvents)]
33 pub fn get_coalesced_events(pointer_event: &JsObject) -> Vec<f64>;
34
35 #[wasm_bindgen(js_namespace = InputHelper, js_name = clearInput)]
37 pub fn clear_input_element(html_element: &JsObject);
38
39 #[wasm_bindgen(js_namespace = InputHelper, js_name = focusElement)]
41 pub fn focus_element(html_element: &JsObject);
42
43 #[wasm_bindgen(js_namespace = InputHelper, js_name = setCursor)]
45 pub fn set_cursor(html_element: &JsObject, kind: &str);
46
47 #[wasm_bindgen(js_namespace = InputHelper, js_name = hide)]
48 pub fn hide_element(html_element: &JsObject);
49
50 #[wasm_bindgen(js_namespace = InputHelper, js_name = show)]
51 pub fn show_element(html_element: &JsObject);
52
53 #[wasm_bindgen(js_namespace = InputHelper, js_name = setSurroundingText)]
56 pub fn set_surrounding_text(html_element: &JsObject, text: &str, start: i32, end: i32);
57
58 #[wasm_bindgen(js_namespace = InputHelper, js_name = setBounds)]
61 pub fn set_bounds(html_element: &JsObject, x: i32, y: i32, width: i32, height: i32, caret: i32);
62
63 #[wasm_bindgen(js_namespace = InputHelper, js_name = initializeBackgroundHandlers)]
65 pub fn initialize_background_handlers(global_this: &JsObject);
66
67 #[wasm_bindgen(js_namespace = InputHelper, js_name = isClipboardFormatSupported)]
69 pub fn is_clipboard_format_supported(format: &str) -> bool;
70
71 #[wasm_bindgen(js_namespace = InputHelper, js_name = createWriteableClipboardSource)]
73 pub fn create_writeable_clipboard_source() -> JsObject;
74
75 #[wasm_bindgen(js_namespace = InputHelper, js_name = createWriteableClipboardItem)]
78 pub fn create_writeable_clipboard_item(source: &JsObject) -> JsObject;
79
80 #[wasm_bindgen(js_namespace = InputHelper, js_name = addStringToWriteableClipboardItem)]
81 pub fn add_string_to_writeable_clipboard_item(item: &JsObject, format: &str, value: &str);
82
83 #[wasm_bindgen(js_namespace = InputHelper, js_name = addBytesToWriteableClipboardItem)]
84 pub fn add_bytes_to_writeable_clipboard_item(item: &JsObject, format: &str, value: &[u8]);
85
86 #[wasm_bindgen(js_namespace = InputHelper, js_name = readClipboard)]
89 fn read_clipboard_raw(window: &JsObject) -> JsObject;
90
91 #[wasm_bindgen(js_namespace = InputHelper, js_name = writeClipboard)]
95 fn write_clipboard_raw(global_this: &JsObject, source: Option<&JsObject>) -> JsObject;
96
97 #[wasm_bindgen(js_namespace = InputHelper, js_name = getReadableDataItemFormats)]
99 pub fn get_readable_data_item_formats(item: &JsObject) -> Vec<String>;
100
101 #[wasm_bindgen(js_namespace = InputHelper, js_name = tryGetReadableDataItemValueAsync)]
104 fn try_get_readable_data_item_value_async_raw(item: &JsObject, format: &str) -> JsObject;
105
106 #[wasm_bindgen(js_namespace = InputHelper, js_name = tryGetReadableDataItemValue)]
110 fn try_get_readable_data_item_value_raw(item: &JsObject, format: &str) -> JsObject;
111
112 #[wasm_bindgen(js_namespace = InputHelper, js_name = setPointerCapture)]
114 fn set_pointer_capture_raw(container_element: &JsObject, pointer_id: f64);
115
116 #[wasm_bindgen(js_namespace = InputHelper, js_name = releasePointerCapture)]
117 fn release_pointer_capture_raw(container_element: &JsObject, pointer_id: f64);
118
119 pub type ClipboardResult;
122
123 #[wasm_bindgen(method, getter)]
124 fn error(this: &ClipboardResult) -> Option<String>;
125
126 #[wasm_bindgen(method, getter)]
127 fn result(this: &ClipboardResult) -> JsObject;
128
129 pub type JsArray;
131
132 #[wasm_bindgen(method, getter)]
133 fn length(this: &JsArray) -> u32;
134
135 #[wasm_bindgen(method, indexing_getter)]
136 fn get(this: &JsArray, index: u32) -> JsObject;
137
138 pub type ReadableDataValue;
141
142 #[wasm_bindgen(method, getter, js_name = type)]
143 fn value_type(this: &ReadableDataValue) -> Option<String>;
144
145 #[wasm_bindgen(method, getter, js_name = value)]
146 fn string_value(this: &ReadableDataValue) -> Option<String>;
147
148 #[wasm_bindgen(method, getter, js_name = value)]
149 fn bytes_value(this: &ReadableDataValue) -> Vec<u8>;
150
151 #[wasm_bindgen(method, getter, js_name = value)]
152 fn object_value(this: &ReadableDataValue) -> JsObject;
153
154 pub type DataTransfer;
156
157 #[wasm_bindgen(method, getter, js_name = effectAllowed)]
158 fn effect_allowed_raw(this: &DataTransfer) -> Option<String>;
159
160 #[wasm_bindgen(method, setter, js_name = dropEffect)]
161 fn set_drop_effect_raw(this: &DataTransfer, value: &str);
162}
163
164pub struct ClipboardReadResult {
166 pub error: Option<String>,
168 pub items: Option<JsObject>,
170}
171
172pub async fn read_clipboard_async(window: &JsObject) -> PromiseOutcome<ClipboardReadResult> {
174 let result = await_promise(&read_clipboard_raw(window)).await?;
175 let result = result.unchecked_ref::<ClipboardResult>();
176 Ok(ClipboardReadResult { error: result.error(), items: non_null(result.result()) })
177}
178
179pub async fn write_clipboard_async(global_this: &JsObject, source: Option<&JsObject>) -> PromiseOutcome<String> {
182 let result = await_promise(&write_clipboard_raw(global_this, source)).await?;
183 Ok(result.as_string().unwrap_or_default())
184}
185
186pub async fn try_get_readable_data_item_value_async(item: &JsObject, format: &str) -> PromiseOutcome<Option<JsObject>> {
188 let value = await_promise(&try_get_readable_data_item_value_async_raw(item, format)).await?;
189 Ok(non_null(value))
190}
191
192pub fn try_get_readable_data_item_value(item: &JsObject, format: &str) -> Option<JsObject> {
194 non_null(try_get_readable_data_item_value_raw(item, format))
195}
196
197pub fn get_array_length(array: &JsObject) -> u32 {
199 array.unchecked_ref::<JsArray>().length()
200}
201
202pub fn get_array_item(array: &JsObject, index: u32) -> JsObject {
204 array.unchecked_ref::<JsArray>().get(index)
205}
206
207pub enum ReadableDataValueContent {
209 String(String),
210 Bytes(Vec<u8>),
211 File(JsObject),
212}
213
214pub fn get_readable_data_value(value: &JsObject) -> Option<ReadableDataValueContent> {
216 let value = value.unchecked_ref::<ReadableDataValue>();
217 match value.value_type().as_deref() {
218 Some("string") => value.string_value().map(ReadableDataValueContent::String),
219 Some("bytes") => Some(ReadableDataValueContent::Bytes(value.bytes_value())),
220 Some("file") => non_null(value.object_value()).map(ReadableDataValueContent::File),
221 _ => None,
222 }
223}
224
225pub fn get_effect_allowed(data_transfer: &JsObject) -> Option<String> {
227 data_transfer.unchecked_ref::<DataTransfer>().effect_allowed_raw()
228}
229
230pub fn set_drop_effect(data_transfer: &JsObject, value: &str) {
232 data_transfer.unchecked_ref::<DataTransfer>().set_drop_effect_raw(value);
233}
234
235pub fn get_selection_start(input_element: &JsObject) -> i32 {
237 input_element.unchecked_ref::<HtmlInputElement>().selection_start_raw().unwrap_or(0)
238}
239
240pub fn get_selection_end(input_element: &JsObject) -> i32 {
242 input_element.unchecked_ref::<HtmlInputElement>().selection_end_raw().unwrap_or(0)
243}
244
245pub fn set_pointer_capture(container_element: &JsObject, pointer_id: i64) {
247 set_pointer_capture_raw(container_element, pointer_id as f64);
248}
249
250pub fn release_pointer_capture(container_element: &JsObject, pointer_id: i64) {
252 release_pointer_capture_raw(container_element, pointer_id as f64);
253}
254
255#[wasm_bindgen(js_name = InputHelper_OnKeyDown)]
262pub fn on_key_down(top_level_id: i32, code: Option<String>, key: Option<String>, modifier: i32) -> bool {
263 match BrowserTopLevelImpl::try_get_top_level(top_level_id) {
264 Some(top_level) => top_level.input_handler().on_key_down(code.as_deref(), key.as_deref(), modifier),
265 None => false,
266 }
267}
268
269#[wasm_bindgen(js_name = InputHelper_OnKeyUp)]
271pub fn on_key_up(top_level_id: i32, code: Option<String>, key: Option<String>, modifier: i32) -> bool {
272 match BrowserTopLevelImpl::try_get_top_level(top_level_id) {
273 Some(top_level) => top_level.input_handler().on_key_up(code.as_deref(), key.as_deref(), modifier),
274 None => false,
275 }
276}
277
278#[wasm_bindgen(js_name = InputHelper_OnBeforeInput)]
279pub fn on_before_input(top_level_id: i32, input_type: &str, start: i32, end: i32) {
280 if let Some(top_level) = BrowserTopLevelImpl::try_get_top_level(top_level_id) {
281 top_level.input_handler().text_input_method().on_before_input(input_type, start, end);
282 }
283}
284
285#[wasm_bindgen(js_name = InputHelper_OnCompositionStart)]
286pub fn on_composition_start(top_level_id: i32) {
287 if let Some(top_level) = BrowserTopLevelImpl::try_get_top_level(top_level_id) {
288 top_level.input_handler().text_input_method().on_composition_start();
289 }
290}
291
292#[wasm_bindgen(js_name = InputHelper_OnCompositionUpdate)]
293pub fn on_composition_update(top_level_id: i32, data: Option<String>) {
294 if let Some(top_level) = BrowserTopLevelImpl::try_get_top_level(top_level_id) {
295 top_level.input_handler().text_input_method().on_composition_update(data.as_deref());
296 }
297}
298
299#[wasm_bindgen(js_name = InputHelper_OnCompositionEnd)]
300pub fn on_composition_end(top_level_id: i32, data: Option<String>) {
301 if let Some(top_level) = BrowserTopLevelImpl::try_get_top_level(top_level_id) {
302 top_level.input_handler().text_input_method().on_composition_end(data.as_deref());
303 }
304}
305
306#[wasm_bindgen(js_name = InputHelper_OnPointerMove)]
310#[allow(clippy::too_many_arguments)]
311pub fn on_pointer_move(
312 top_level_id: i32,
313 pointer_type: &str,
314 pointer_id: f64,
315 offset_x: f64,
316 offset_y: f64,
317 pressure: f64,
318 tilt_x: f64,
319 tilt_y: f64,
320 twist: f64,
321 modifier: i32,
322 event_age: f64,
323 args_obj: JsObject,
324) {
325 if let Some(top_level) = BrowserTopLevelImpl::try_get_top_level(top_level_id) {
326 top_level.input_handler().on_pointer_move(
327 pointer_type,
328 pointer_id as i64,
329 offset_x,
330 offset_y,
331 pressure,
332 tilt_x,
333 tilt_y,
334 twist,
335 modifier,
336 event_age,
337 args_obj,
338 );
339 }
340}
341
342#[wasm_bindgen(js_name = InputHelper_OnPointerDown)]
345#[allow(clippy::too_many_arguments)]
346pub fn on_pointer_down(
347 top_level_id: i32,
348 pointer_type: &str,
349 pointer_id: f64,
350 buttons: i32,
351 offset_x: f64,
352 offset_y: f64,
353 pressure: f64,
354 tilt_x: f64,
355 tilt_y: f64,
356 twist: f64,
357 modifier: i32,
358 event_age: f64,
359) {
360 if let Some(top_level) = BrowserTopLevelImpl::try_get_top_level(top_level_id) {
361 top_level.input_handler().on_pointer_down(
362 pointer_type,
363 pointer_id as i64,
364 buttons,
365 offset_x,
366 offset_y,
367 pressure,
368 tilt_x,
369 tilt_y,
370 twist,
371 modifier,
372 event_age,
373 );
374 }
375}
376
377#[wasm_bindgen(js_name = InputHelper_OnPointerUp)]
380#[allow(clippy::too_many_arguments)]
381pub fn on_pointer_up(
382 top_level_id: i32,
383 pointer_type: &str,
384 pointer_id: f64,
385 buttons: i32,
386 offset_x: f64,
387 offset_y: f64,
388 pressure: f64,
389 tilt_x: f64,
390 tilt_y: f64,
391 twist: f64,
392 modifier: i32,
393 event_age: f64,
394) {
395 if let Some(top_level) = BrowserTopLevelImpl::try_get_top_level(top_level_id) {
396 top_level.input_handler().on_pointer_up(
397 pointer_type,
398 pointer_id as i64,
399 buttons,
400 offset_x,
401 offset_y,
402 pressure,
403 tilt_x,
404 tilt_y,
405 twist,
406 modifier,
407 event_age,
408 );
409 }
410}
411
412#[wasm_bindgen(js_name = InputHelper_OnPointerCancel)]
413#[allow(clippy::too_many_arguments)]
414pub fn on_pointer_cancel(
415 top_level_id: i32,
416 pointer_type: &str,
417 pointer_id: f64,
418 offset_x: f64,
419 offset_y: f64,
420 pressure: f64,
421 tilt_x: f64,
422 tilt_y: f64,
423 twist: f64,
424 modifier: i32,
425 event_age: f64,
426) {
427 if let Some(top_level) = BrowserTopLevelImpl::try_get_top_level(top_level_id) {
428 top_level.input_handler().on_pointer_cancel(
429 pointer_type,
430 pointer_id as i64,
431 offset_x,
432 offset_y,
433 pressure,
434 tilt_x,
435 tilt_y,
436 twist,
437 modifier,
438 event_age,
439 );
440 }
441}
442
443#[wasm_bindgen(js_name = InputHelper_OnWheel)]
447pub fn on_wheel(
448 top_level_id: i32,
449 offset_x: f64,
450 offset_y: f64,
451 delta_x: f64,
452 delta_y: f64,
453 delta_mode: i32,
454 modifier: i32,
455) {
456 if let Some(top_level) = BrowserTopLevelImpl::try_get_top_level(top_level_id) {
457 top_level.input_handler().on_wheel(offset_x, offset_y, delta_x, delta_y, delta_mode, modifier);
458 }
459}
460
461#[wasm_bindgen(js_name = InputHelper_OnDragDrop)]
465pub fn on_drag_drop(
466 top_level_id: i32,
467 type_: &str,
468 offset_x: f64,
469 offset_y: f64,
470 modifiers: i32,
471 data_transfer: JsObject,
472 items: JsObject,
473) {
474 if let Some(top_level) = BrowserTopLevelImpl::try_get_top_level(top_level_id) {
475 top_level.input_handler().on_drag_event(type_, offset_x, offset_y, modifiers, &data_transfer, items);
476 }
477}
478
479#[wasm_bindgen(js_name = InputHelper_OnKeyboardGeometryChange)]
481pub fn on_keyboard_geometry_change(top_level_id: i32, x: f64, y: f64, width: f64, height: f64) {
482 if let Some(top_level) = BrowserTopLevelImpl::try_get_top_level(top_level_id) {
483 top_level.input_handler().input_pane().on_geometry_change(x, y, width, height);
484 }
485}
486
487#[wasm_bindgen(js_name = InputHelper_OnLostFocus)]
490pub fn on_lost_focus(top_level_id: i32) {
491 if let Some(top_level) = BrowserTopLevelImpl::try_get_top_level(top_level_id) {
492 top_level.on_lost_focus();
493 }
494}