-
Notifications
You must be signed in to change notification settings - Fork 76
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add iOS support #103
base: master
Are you sure you want to change the base?
Add iOS support #103
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,18 +29,24 @@ use once_cell::sync::Lazy; | |
use std::borrow::Cow; | ||
|
||
// Required to bring NSPasteboard into the path of the class-resolver | ||
#[cfg(target_os = "macos")] | ||
#[link(name = "AppKit", kind = "framework")] | ||
extern "C" { | ||
static NSPasteboardTypeHTML: *const Object; | ||
static NSPasteboardTypeString: *const Object; | ||
} | ||
|
||
#[cfg(target_os = "macos")] | ||
const PASTEBOARD_CLASS: &str = "NSPasteboard"; | ||
#[cfg(target_os = "ios")] | ||
const PASTEBOARD_CLASS: &str = "UIPasteboard"; | ||
|
||
static NSSTRING_CLASS: Lazy<&Class> = Lazy::new(|| Class::get("NSString").unwrap()); | ||
#[cfg(feature = "image-data")] | ||
static NSIMAGE_CLASS: Lazy<&Class> = Lazy::new(|| Class::get("NSImage").unwrap()); | ||
|
||
/// Returns an NSImage object on success. | ||
#[cfg(feature = "image-data")] | ||
#[cfg(all(feature = "image-data", target_os = "macos"))] | ||
fn image_from_pixels( | ||
pixels: Vec<u8>, | ||
width: usize, | ||
|
@@ -99,7 +105,8 @@ pub(crate) struct Clipboard { | |
|
||
impl Clipboard { | ||
pub(crate) fn new() -> Result<Clipboard, Error> { | ||
let cls = Class::get("NSPasteboard").expect("NSPasteboard not registered"); | ||
let cls = Class::get(PASTEBOARD_CLASS) | ||
.unwrap_or_else(|| panic!("{} not registered", PASTEBOARD_CLASS)); | ||
let pasteboard: *mut Object = unsafe { msg_send![cls, generalPasteboard] }; | ||
|
||
if !pasteboard.is_null() { | ||
|
@@ -115,7 +122,12 @@ impl Clipboard { | |
} | ||
|
||
fn clear(&mut self) { | ||
#[cfg(target_os = "macos")] | ||
let _: usize = unsafe { msg_send![self.pasteboard, clearContents] }; | ||
#[cfg(target_os = "ios")] | ||
let _: () = unsafe { | ||
msg_send![self.pasteboard, setItems: NSArray::from_vec(Vec::<Id<NSString>>::new())] | ||
}; | ||
} | ||
|
||
// fn get_binary_contents(&mut self) -> Result<Option<ClipboardContent>, Box<dyn std::error::Error>> { | ||
|
@@ -178,6 +190,18 @@ impl<'clipboard> Get<'clipboard> { | |
Self { pasteboard: &*clipboard.pasteboard } | ||
} | ||
|
||
#[cfg(target_os = "ios")] | ||
pub(crate) fn text(self) -> Result<String, Error> { | ||
let obj: *mut NSString = unsafe { msg_send![self.pasteboard, string] }; | ||
if obj.is_null() { | ||
Err(Error::ContentNotAvailable) | ||
} else { | ||
let id: Id<NSString> = unsafe { Id::from_ptr(obj) }; | ||
Ok(id.as_str().to_owned()) | ||
} | ||
} | ||
|
||
#[cfg(target_os = "macos")] | ||
pub(crate) fn text(self) -> Result<String, Error> { | ||
let string_class = object_class(&NSSTRING_CLASS); | ||
let classes: Id<NSArray<NSObject, Owned>> = NSArray::from_vec(vec![string_class]); | ||
|
@@ -200,7 +224,7 @@ impl<'clipboard> Get<'clipboard> { | |
.ok_or(Error::ContentNotAvailable) | ||
} | ||
|
||
#[cfg(feature = "image-data")] | ||
#[cfg(all(feature = "image-data", target_os = "macos"))] | ||
pub(crate) fn image(self) -> Result<ImageData<'static>, Error> { | ||
use std::io::Cursor; | ||
|
||
|
@@ -261,13 +285,26 @@ impl<'clipboard> Set<'clipboard> { | |
pub(crate) fn text(self, data: Cow<'_, str>) -> Result<(), Error> { | ||
self.clipboard.clear(); | ||
|
||
let string_array = NSArray::from_vec(vec![NSString::from_str(&data)]); | ||
let success: bool = | ||
unsafe { msg_send![self.clipboard.pasteboard, writeObjects: string_array] }; | ||
#[cfg(target_os = "macos")] | ||
let success: bool = { | ||
let string_array = NSArray::from_vec(vec![NSString::from_str(&data)]); | ||
unsafe { msg_send![self.clipboard.pasteboard, writeObjects: string_array] } | ||
}; | ||
#[cfg(target_os = "ios")] | ||
let success: bool = { | ||
let string = NSString::from_str(&data); | ||
unsafe { msg_send![self.clipboard.pasteboard, setString: string] } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did this (and other callsites) get mixed up? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
}; | ||
|
||
if success { | ||
Ok(()) | ||
} else { | ||
Err(Error::Unknown { description: "NSPasteboard#writeObjects: returned false".into() }) | ||
Err(Error::Unknown { | ||
description: format!( | ||
"{PASTEBOARD_CLASS}#{}: returned false", | ||
if cfg!(target_os = "ios") { "setString" } else { "writeObjects" } | ||
), | ||
}) | ||
} | ||
} | ||
|
||
|
@@ -284,25 +321,39 @@ impl<'clipboard> Set<'clipboard> { | |
html | ||
); | ||
let html_nss = NSString::from_str(&html); | ||
#[cfg(target_os = "macos")] | ||
let mut success: bool = unsafe { | ||
msg_send![self.clipboard.pasteboard, setString: html_nss forType:NSPasteboardTypeHTML] | ||
}; | ||
#[cfg(target_os = "ios")] | ||
let mut success: bool = unsafe { msg_send![self.clipboard.pasteboard, setString: html_nss] }; | ||
|
||
if success { | ||
if let Some(alt_text) = alt { | ||
let alt_nss = NSString::from_str(&alt_text); | ||
success = unsafe { | ||
msg_send![self.clipboard.pasteboard, setString: alt_nss forType:NSPasteboardTypeString] | ||
}; | ||
#[cfg(target_os = "macos")] | ||
{ | ||
success = unsafe { | ||
msg_send![self.clipboard.pasteboard, setString: alt_nss forType:NSPasteboardTypeString] | ||
}; | ||
} | ||
|
||
#[cfg(target_os = "ios")] | ||
{ | ||
success = unsafe { msg_send![self.clipboard.pasteboard, setString: alt_nss] }; | ||
} | ||
} | ||
} | ||
if success { | ||
Ok(()) | ||
} else { | ||
Err(Error::Unknown { description: "NSPasteboard#writeObjects: returned false".into() }) | ||
Err(Error::Unknown { | ||
description: format!("{PASTEBOARD_CLASS}#writeObjects: returned false"), | ||
}) | ||
} | ||
} | ||
|
||
#[cfg(feature = "image-data")] | ||
#[cfg(all(feature = "image-data", target_os = "macos"))] | ||
pub(crate) fn image(self, data: ImageData) -> Result<(), Error> { | ||
let pixels = data.bytes.into(); | ||
let image = image_from_pixels(pixels, data.width, data.height) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this the right array type to clear any existing content type? The docs seem to say this is essentially an array of
[String; Any]
but its unclear if this matches. At a glance, this needs an inner dictionary type instead of a plainNSString
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The type here doesn't really matter, I just want to send an empty array to the
items
property.It is indeed technically wrong, so I'll push a fix.