mirror of
https://github.com/1Password/arboard.git
synced 2025-07-07 13:25:18 +00:00
Implement Get::html() for all platforms (#163)
* implement get html operation Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com>
This commit is contained in:
parent
e458e1a26c
commit
4b91bfe93e
7 changed files with 92 additions and 24 deletions
|
@ -15,4 +15,7 @@ consectetur adipiscing elit."#;
|
|||
|
||||
ctx.set_html(html, Some(alt_text)).unwrap();
|
||||
thread::sleep(Duration::from_secs(5));
|
||||
|
||||
let success = ctx.get().html().unwrap() == html;
|
||||
println!("Set and Get html operations were successful: {success}");
|
||||
}
|
23
src/lib.rs
23
src/lib.rs
|
@ -192,6 +192,11 @@ impl Get<'_> {
|
|||
pub fn image(self) -> Result<ImageData<'static>, Error> {
|
||||
self.platform.image()
|
||||
}
|
||||
|
||||
/// Completes the "get" operation by fetching HTML from the clipboard.
|
||||
pub fn html(self) -> Result<String, Error> {
|
||||
self.platform.html()
|
||||
}
|
||||
}
|
||||
|
||||
/// A builder for an operation that sets a value to the clipboard.
|
||||
|
@ -322,6 +327,24 @@ mod tests {
|
|||
ctx.set_html(html, Some(alt_text)).unwrap();
|
||||
assert_eq!(ctx.get_text().unwrap(), alt_text);
|
||||
}
|
||||
{
|
||||
let mut ctx = Clipboard::new().unwrap();
|
||||
|
||||
let html = "<b>hello</b> <i>world</i>!";
|
||||
|
||||
ctx.set().html(html, None).unwrap();
|
||||
|
||||
if cfg!(target_os = "macos") {
|
||||
// Copying HTML on macOS adds wrapper content to work around
|
||||
// historical platform bugs. We control this wrapper, so we are
|
||||
// able to check that the full user data still appears and at what
|
||||
// position in the final copy contents.
|
||||
let content = ctx.get().html().unwrap();
|
||||
assert!(content.ends_with(&format!("{html}</body></html>")));
|
||||
} else {
|
||||
assert_eq!(ctx.get().html().unwrap(), html);
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "image-data")]
|
||||
{
|
||||
let mut ctx = Clipboard::new().unwrap();
|
||||
|
|
|
@ -122,6 +122,14 @@ impl<'clipboard> Get<'clipboard> {
|
|||
Clipboard::WlDataControl(clipboard) => clipboard.get_image(self.selection),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn html(self) -> Result<String, Error> {
|
||||
match self.clipboard {
|
||||
Clipboard::X11(clipboard) => clipboard.get_html(self.selection),
|
||||
#[cfg(feature = "wayland-data-control")]
|
||||
Clipboard::WlDataControl(clipboard) => clipboard.get_html(self.selection),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Linux-specific extensions to the [`Get`](super::Get) builder.
|
||||
|
|
|
@ -53,10 +53,12 @@ impl Clipboard {
|
|||
Ok(Self {})
|
||||
}
|
||||
|
||||
pub(crate) fn get_text(&mut self, selection: LinuxClipboardKind) -> Result<String, Error> {
|
||||
use wl_clipboard_rs::paste::MimeType;
|
||||
|
||||
let result = get_contents(selection.try_into()?, Seat::Unspecified, MimeType::Text);
|
||||
fn string_for_mime(
|
||||
&mut self,
|
||||
selection: LinuxClipboardKind,
|
||||
mime: paste::MimeType,
|
||||
) -> Result<String, Error> {
|
||||
let result = get_contents(selection.try_into()?, Seat::Unspecified, mime);
|
||||
match result {
|
||||
Ok((mut pipe, _)) => {
|
||||
let mut contents = vec![];
|
||||
|
@ -74,6 +76,10 @@ impl Clipboard {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_text(&mut self, selection: LinuxClipboardKind) -> Result<String, Error> {
|
||||
self.string_for_mime(selection, paste::MimeType::Text)
|
||||
}
|
||||
|
||||
pub(crate) fn set_text(
|
||||
&self,
|
||||
text: Cow<'_, str>,
|
||||
|
@ -91,6 +97,10 @@ impl Clipboard {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn get_html(&mut self, selection: LinuxClipboardKind) -> Result<String, Error> {
|
||||
self.string_for_mime(selection, paste::MimeType::Specific("text/html"))
|
||||
}
|
||||
|
||||
pub(crate) fn set_html(
|
||||
&self,
|
||||
html: Cow<'_, str>,
|
||||
|
|
|
@ -885,6 +885,12 @@ impl Clipboard {
|
|||
self.inner.write(data, selection, wait)
|
||||
}
|
||||
|
||||
pub(crate) fn get_html(&self, selection: LinuxClipboardKind) -> Result<String> {
|
||||
let formats = [self.inner.atoms.HTML];
|
||||
let result = self.inner.read(&formats, selection)?;
|
||||
String::from_utf8(result.bytes).map_err(|_| Error::ConversionFailure)
|
||||
}
|
||||
|
||||
pub(crate) fn set_html(
|
||||
&self,
|
||||
html: Cow<'_, str>,
|
||||
|
|
|
@ -121,6 +121,27 @@ impl Clipboard {
|
|||
unsafe { self.pasteboard.clearContents() };
|
||||
}
|
||||
|
||||
fn string_from_type(&self, type_: &'static NSString) -> Result<String, Error> {
|
||||
// XXX: There does not appear to be an alternative for obtaining text without the need for
|
||||
// autorelease behavior.
|
||||
autoreleasepool(|_| {
|
||||
// XXX: We explicitly use `pasteboardItems` and not `stringForType` since the latter will concat
|
||||
// multiple strings, if present, into one and return it instead of reading just the first which is `arboard`'s
|
||||
// historical behavior.
|
||||
let contents = unsafe { self.pasteboard.pasteboardItems() }.ok_or_else(|| {
|
||||
Error::Unknown { description: String::from("NSPasteboard#pasteboardItems errored") }
|
||||
})?;
|
||||
|
||||
for item in contents {
|
||||
if let Some(string) = unsafe { item.stringForType(type_) } {
|
||||
return Ok(string.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::ContentNotAvailable)
|
||||
})
|
||||
}
|
||||
|
||||
// fn get_binary_contents(&mut self) -> Result<Option<ClipboardContent>, Box<dyn std::error::Error>> {
|
||||
// let string_class: Id<NSObject> = {
|
||||
// let cls: Id<Class> = unsafe { Id::from_ptr(class("NSString")) };
|
||||
|
@ -182,27 +203,11 @@ impl<'clipboard> Get<'clipboard> {
|
|||
}
|
||||
|
||||
pub(crate) fn text(self) -> Result<String, Error> {
|
||||
// XXX: There does not appear to be an alternative for obtaining text without the need for
|
||||
// autorelease behavior.
|
||||
autoreleasepool(|_| {
|
||||
// XXX: We explicitly use `pasteboardItems` and not `stringForType` since the latter will concat
|
||||
// multiple strings, if present, into one and return it instead of reading just the first which is `arboard`'s
|
||||
// historical behavior.
|
||||
let contents =
|
||||
unsafe { self.clipboard.pasteboard.pasteboardItems() }.ok_or_else(|| {
|
||||
Error::Unknown {
|
||||
description: String::from("NSPasteboard#pasteboardItems errored"),
|
||||
}
|
||||
})?;
|
||||
unsafe { self.clipboard.string_from_type(NSPasteboardTypeString) }
|
||||
}
|
||||
|
||||
for item in contents {
|
||||
if let Some(string) = unsafe { item.stringForType(NSPasteboardTypeString) } {
|
||||
return Ok(string.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::ContentNotAvailable)
|
||||
})
|
||||
pub(crate) fn html(self) -> Result<String, Error> {
|
||||
unsafe { self.clipboard.string_from_type(NSPasteboardTypeHTML) }
|
||||
}
|
||||
|
||||
#[cfg(feature = "image-data")]
|
||||
|
|
|
@ -567,6 +567,19 @@ impl<'clipboard> Get<'clipboard> {
|
|||
String::from_utf16(&out[..bytes_read]).map_err(|_| Error::ConversionFailure)
|
||||
}
|
||||
|
||||
pub(crate) fn html(self) -> Result<String, Error> {
|
||||
let _clipboard_assertion = self.clipboard?;
|
||||
|
||||
let format = clipboard_win::register_format("HTML Format")
|
||||
.ok_or_else(|| Error::unknown("unable to register HTML format"))?;
|
||||
|
||||
let mut out: Vec<u8> = Vec::new();
|
||||
clipboard_win::raw::get_html(format.get(), &mut out)
|
||||
.map_err(|_| Error::unknown("failed to read clipboard string"))?;
|
||||
|
||||
String::from_utf8(out).map_err(|_| Error::ConversionFailure)
|
||||
}
|
||||
|
||||
#[cfg(feature = "image-data")]
|
||||
pub(crate) fn image(self) -> Result<ImageData<'static>, Error> {
|
||||
const FORMAT: u32 = clipboard_win::formats::CF_DIBV5;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue