#![allow(dead_code)]
extern crate alloc;
use bevy::ecs::resource::Resource;
#[cfg(target_arch = "wasm32")]
use {alloc::sync::Arc, bevy_platform::sync::Mutex, wasm_bindgen_futures::JsFuture};
#[derive(Debug)]
pub enum ClipboardRead {
Ready(Result<String, ClipboardError>),
#[cfg(target_arch = "wasm32")]
Pending(Arc<Mutex<Option<Result<String, ClipboardError>>>>),
}
impl ClipboardRead {
pub fn poll_result(&mut self) -> Option<Result<String, ClipboardError>> {
match self {
#[cfg(target_arch = "wasm32")]
Self::Pending(shared) => {
if let Some(contents) = shared.lock().ok().and_then(|mut inner| inner.take()) {
*self = Self::Ready(Err(ClipboardError::ContentTaken));
Some(contents)
} else {
None
}
}
Self::Ready(inner) => {
Some(core::mem::replace(inner, Err(ClipboardError::ContentTaken)))
}
}
}
}
#[cfg(unix)]
#[derive(Resource)]
pub struct Clipboard(Option<arboard::Clipboard>);
#[cfg(unix)]
impl Default for Clipboard {
fn default() -> Self {
{ Self(arboard::Clipboard::new().ok()) }
}
}
#[cfg(not(unix))]
#[derive(Resource, Default)]
pub struct Clipboard;
impl Clipboard {
pub fn fetch_text(&mut self) -> ClipboardRead {
#[cfg(unix)]
{
ClipboardRead::Ready(if let Some(clipboard) = self.0.as_mut() {
clipboard.get_text().map_err(ClipboardError::from)
} else {
Err(ClipboardError::ClipboardNotSupported)
})
}
#[cfg(windows)]
{
ClipboardRead::Ready(
arboard::Clipboard::new()
.and_then(|mut clipboard| clipboard.get_text())
.map_err(ClipboardError::from),
)
}
#[cfg(target_arch = "wasm32")]
{
if let Some(clipboard) = web_sys::window().map(|w| w.navigator().clipboard()) {
let shared = Arc::new(Mutex::new(None));
let shared_clone = shared.clone();
wasm_bindgen_futures::spawn_local(async move {
let text = JsFuture::from(clipboard.read_text()).await;
let text = match text {
Ok(text) => text.as_string().ok_or(ClipboardError::ConversionFailure),
Err(_) => Err(ClipboardError::ContentNotAvailable),
};
shared.lock().unwrap().replace(text);
});
ClipboardRead::Pending(shared_clone)
} else {
ClipboardRead::Ready(Err(ClipboardError::ClipboardNotSupported))
}
}
#[cfg(not(any(unix, windows, target_arch = "wasm32")))]
{
ClipboardRead::Ready(Err(ClipboardError::ClipboardNotSupported))
}
}
pub fn set_text<'a, T: Into<alloc::borrow::Cow<'a, str>>>(
&mut self,
text: T,
) -> Result<(), ClipboardError> {
#[cfg(unix)]
{
if let Some(clipboard) = self.0.as_mut() {
clipboard.set_text(text).map_err(ClipboardError::from)
} else {
Err(ClipboardError::ClipboardNotSupported)
}
}
#[cfg(windows)]
{
arboard::Clipboard::new()
.and_then(|mut clipboard| clipboard.set_text(text))
.map_err(ClipboardError::from)
}
#[cfg(target_arch = "wasm32")]
{
if let Some(clipboard) = web_sys::window().map(|w| w.navigator().clipboard()) {
let text = text.into().to_string();
wasm_bindgen_futures::spawn_local(async move {
let _ = JsFuture::from(clipboard.write_text(&text)).await;
});
Ok(())
} else {
Err(ClipboardError::ClipboardNotSupported)
}
}
#[cfg(not(any(unix, windows, target_arch = "wasm32")))]
{
Err(ClipboardError::ClipboardNotSupported)
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone)]
pub enum ClipboardError {
ContentNotAvailable,
ClipboardNotSupported,
ClipboardOccupied,
ConversionFailure,
ContentTaken,
Unknown {
description: String,
},
}
#[cfg(any(windows, unix))]
impl From<arboard::Error> for ClipboardError {
fn from(value: arboard::Error) -> Self {
match value {
arboard::Error::ContentNotAvailable => ClipboardError::ContentNotAvailable,
arboard::Error::ClipboardNotSupported => ClipboardError::ClipboardNotSupported,
arboard::Error::ClipboardOccupied => ClipboardError::ClipboardOccupied,
arboard::Error::ConversionFailure => ClipboardError::ConversionFailure,
arboard::Error::Unknown { description } => ClipboardError::Unknown { description },
_ => ClipboardError::Unknown {
description: "Unknown arboard error variant".to_owned(),
},
}
}
}