Seregon/StratoSDK

StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.

Rust/27.3 KB/No license
crates/strato-ui-renderer/src/windowing/winit/wasm.rs
1use crate::{clipboard::ClipboardContent, Clipboard};
2use js_sys::{Array, Object};
3use wasm_bindgen::{self, prelude::*, JsCast};
4use web_sys::{Blob, BlobPropertyBag};
5 
6pub struct WebClipboard {
7 inner: web_sys::Clipboard,
8 saved_content: ClipboardContent,
9}
10 
11impl WebClipboard {
12 pub fn new() -> Self {
13 Self {
14 inner: gloo::utils::window().navigator().clipboard(),
15 saved_content: Default::default(),
16 }
17 }
18}
19 
20impl Default for WebClipboard {
21 fn default() -> Self {
22 Self::new()
23 }
24}
25 
26impl Clipboard for WebClipboard {
27 fn write(&mut self, contents: ClipboardContent) {
28 match create_item_list(&contents) {
29 Ok(item_list) => {
30 // This returns a Promise, which succeeds iff the copy succeeds. There's nothing we can do
31 // if the copy fails, though, and this API doesn't support async, so we just ignore the
32 // promise. It's not necessary to hold a reference to the promise for the copy to succeed.
33 let _ = self.inner.write(&item_list);
34 }
35 Err(error) => {
36 // Fall back to just writing plain text.
37 // ClipboardItems are not supported in Firefox yet.
38 log::warn!("Failed to construct clipboard data: {error:?}");
39 let _ = self.inner.write_text(&contents.plain_text);
40 }
41 }
42 }
43 
44 fn read(&mut self) -> ClipboardContent {
45 std::mem::take(&mut self.saved_content)
46 }
47 
48 fn save(&mut self, content: ClipboardContent) {
49 self.saved_content = content;
50 }
51}
52 
53fn create_item_list(contents: &ClipboardContent) -> Result<Array, JsValue> {
54 // The Clipboard.write method
55 // (https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/write)
56 // requires an array of ClipboardItem objects
57 // (https://developer.mozilla.org/en-US/docs/Web/API/ClipboardItem).
58 // This function constructs a single element array containing a ClipboardItem, which contains
59 // both plain text and html data that's being copied.
60 
61 let items = Object::new();
62 
63 // We always have plain text data.
64 let text_blob = create_blob(&contents.plain_text, "text/plain")?;
65 js_sys::Reflect::set(&items, &JsValue::from_str("text/plain"), &text_blob)?;
66 
67 // We sometimes have html data.
68 if let Some(html) = &contents.html {
69 let html_blob = create_blob(html, "text/html")?;
70 js_sys::Reflect::set(&items, &JsValue::from_str("text/html"), &html_blob)?;
71 }
72 
73 // web_sys doesn't have this constructor, so we have to do things the hard way.
74 let clipboard_item_constructor: js_sys::Function = js_sys::Reflect::get(
75 &JsValue::from(gloo::utils::window()),
76 &JsValue::from_str("ClipboardItem"),
77 )?
78 .dyn_into()?;
79 let clipboard_item =
80 js_sys::Reflect::construct(&clipboard_item_constructor, &Array::of1(&items))?;
81 
82 // Write the ClipboardItem to the clipboard
83 let item_list = Array::new();
84 item_list.push(&clipboard_item);
85 
86 Ok(item_list)
87}
88 
89fn create_blob(contents: &str, type_: &str) -> Result<Blob, JsValue> {
90 // See the JS Blob constructor docs for more info:
91 // https://developer.mozilla.org/en-US/docs/Web/API/Blob/Blob
92 let blob_parts = Array::new();
93 blob_parts.push(&JsValue::from_str(contents));
94 let blob_opts = BlobPropertyBag::new();
95 blob_opts.set_type(type_);
96 Blob::new_with_str_sequence_and_options(&blob_parts, &blob_opts)
97}
98