#[cfg(test)]
use std::cell::Cell;
use std::{cell::RefCell, rc::Rc};
pub trait PlatformClipboard {
fn write_text(&self, text: &str);
fn read_text(&self) -> Option<String>;
fn can_request_paste(&self) -> bool {
false
}
fn request_paste(&self) -> bool {
false
}
}
pub(crate) struct ClipboardSessionState {
platform: RefCell<Option<Rc<dyn PlatformClipboard>>>,
fallback: RefCell<Option<String>>,
}
impl ClipboardSessionState {
pub(crate) fn new() -> Self {
Self {
platform: RefCell::new(None),
fallback: RefCell::new(None),
}
}
fn set_platform(&self, clipboard: Option<Rc<dyn PlatformClipboard>>) {
*self.platform.borrow_mut() = clipboard;
}
fn write(&self, text: &str) {
*self.fallback.borrow_mut() = Some(text.to_string());
if let Some(platform) = self.platform.borrow().clone() {
platform.write_text(text);
}
}
fn read(&self) -> Option<String> {
if let Some(platform) = self.platform.borrow().clone()
&& let Some(text) = platform.read_text()
{
return Some(text);
}
self.fallback.borrow().clone()
}
fn has_platform(&self) -> bool {
self.platform.borrow().is_some()
}
fn can_request_paste(&self) -> bool {
self.platform
.borrow()
.as_ref()
.is_some_and(|platform| platform.can_request_paste())
}
fn request_paste(&self) -> bool {
self.platform
.borrow()
.clone()
.is_some_and(|platform| platform.request_paste())
}
}
pub fn set_platform_clipboard(clipboard: Rc<dyn PlatformClipboard>) {
crate::render_state::with_clipboard_session(|state| state.set_platform(Some(clipboard)));
}
pub fn clear_platform_clipboard() {
crate::render_state::with_clipboard_session(|state| state.set_platform(None));
}
pub fn clipboard_write_text(text: &str) {
crate::render_state::with_clipboard_session(|state| state.write(text));
}
pub fn clipboard_read_text() -> Option<String> {
crate::render_state::with_clipboard_session(ClipboardSessionState::read)
}
pub fn has_platform_clipboard() -> bool {
crate::render_state::with_clipboard_session(ClipboardSessionState::has_platform)
}
pub fn clipboard_can_paste() -> bool {
crate::render_state::with_clipboard_session(|state| {
state.read().is_some() || state.can_request_paste()
})
}
pub fn clipboard_paste_into_focus() {
if crate::render_state::with_clipboard_session(ClipboardSessionState::request_paste) {
return;
}
if let Some(text) = clipboard_read_text() {
crate::text_field_focus::dispatch_paste(&text);
}
}
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
pub struct ClipboardManager;
impl ClipboardManager {
pub fn set_text(&self, text: &str) {
clipboard_write_text(text);
}
pub fn text(&self) -> Option<String> {
clipboard_read_text()
}
pub fn has_system_clipboard(&self) -> bool {
has_platform_clipboard()
}
}
pub fn local_clipboard() -> cranpose_core::CompositionLocal<ClipboardManager> {
thread_local! {
static LOCAL_CLIPBOARD: RefCell<Option<cranpose_core::CompositionLocal<ClipboardManager>>> =
const { RefCell::new(None) };
}
LOCAL_CLIPBOARD.with(|cell| {
cell.borrow_mut()
.get_or_insert_with(|| cranpose_core::compositionLocalOf(ClipboardManager::default))
.clone()
})
}
#[cfg(test)]
#[path = "tests/clipboard_session_tests.rs"]
mod tests;