attributed_string.rs

  1use cocoa::base::id;
  2use cocoa::foundation::NSRange;
  3use objc::{class, msg_send, sel, sel_impl};
  4
  5/// The `cocoa` crate does not define NSAttributedString (and related Cocoa classes),
  6/// which are needed for copying rich text (that is, text intermingled with images)
  7/// to the clipboard. This adds access to those APIs.
  8
  9#[allow(non_snake_case)]
 10pub trait NSAttributedString: Sized {
 11    unsafe fn alloc(_: Self) -> id {
 12        msg_send![class!(NSAttributedString), alloc]
 13    }
 14
 15    unsafe fn init_attributed_string(self, string: id) -> id;
 16    unsafe fn appendAttributedString_(self, attr_string: id);
 17    unsafe fn RTFDFromRange_documentAttributes_(self, range: NSRange, attrs: id) -> id;
 18    unsafe fn RTFFromRange_documentAttributes_(self, range: NSRange, attrs: id) -> id;
 19    unsafe fn string(self) -> id;
 20}
 21
 22impl NSAttributedString for id {
 23    unsafe fn init_attributed_string(self, string: id) -> id {
 24        msg_send![self, initWithString: string]
 25    }
 26
 27    unsafe fn appendAttributedString_(self, attr_string: id) {
 28        let _: () = msg_send![self, appendAttributedString: attr_string];
 29    }
 30
 31    unsafe fn RTFDFromRange_documentAttributes_(self, range: NSRange, attrs: id) -> id {
 32        msg_send![self, RTFDFromRange: range documentAttributes: attrs]
 33    }
 34
 35    unsafe fn RTFFromRange_documentAttributes_(self, range: NSRange, attrs: id) -> id {
 36        msg_send![self, RTFFromRange: range documentAttributes: attrs]
 37    }
 38
 39    unsafe fn string(self) -> id {
 40        msg_send![self, string]
 41    }
 42}
 43
 44pub trait NSMutableAttributedString: NSAttributedString {
 45    unsafe fn alloc(_: Self) -> id {
 46        msg_send![class!(NSMutableAttributedString), alloc]
 47    }
 48}
 49
 50impl NSMutableAttributedString for id {}
 51
 52#[cfg(test)]
 53mod tests {
 54    use super::*;
 55    use cocoa::appkit::NSImage;
 56    use cocoa::base::nil;
 57    use cocoa::foundation::NSString;
 58    #[test]
 59    #[ignore] // This was SIGSEGV-ing on CI but not locally; need to investigate https://github.com/zed-industries/zed/actions/runs/10362363230/job/28684225486?pr=15782#step:4:1348
 60    fn test_nsattributed_string() {
 61        // TODO move these to parent module once it's actually ready to be used
 62        #[allow(non_snake_case)]
 63        pub trait NSTextAttachment: Sized {
 64            unsafe fn alloc(_: Self) -> id {
 65                msg_send![class!(NSTextAttachment), alloc]
 66            }
 67        }
 68
 69        impl NSTextAttachment for id {}
 70
 71        unsafe {
 72            let image: id = msg_send![class!(NSImage), alloc];
 73            image.initWithContentsOfFile_(NSString::alloc(nil).init_str("test.jpeg"));
 74            let _size = image.size();
 75
 76            let string = NSString::alloc(nil).init_str("Test String");
 77            let attr_string = NSMutableAttributedString::alloc(nil).init_attributed_string(string);
 78            let hello_string = NSString::alloc(nil).init_str("Hello World");
 79            let hello_attr_string =
 80                NSAttributedString::alloc(nil).init_attributed_string(hello_string);
 81            attr_string.appendAttributedString_(hello_attr_string);
 82
 83            let attachment = NSTextAttachment::alloc(nil);
 84            let _: () = msg_send![attachment, setImage: image];
 85            let image_attr_string =
 86                msg_send![class!(NSAttributedString), attributedStringWithAttachment: attachment];
 87            attr_string.appendAttributedString_(image_attr_string);
 88
 89            let another_string = NSString::alloc(nil).init_str("Another String");
 90            let another_attr_string =
 91                NSAttributedString::alloc(nil).init_attributed_string(another_string);
 92            attr_string.appendAttributedString_(another_attr_string);
 93
 94            let _len: cocoa::foundation::NSUInteger = msg_send![attr_string, length];
 95
 96            ///////////////////////////////////////////////////
 97            // pasteboard.clearContents();
 98
 99            let rtfd_data = attr_string.RTFDFromRange_documentAttributes_(
100                NSRange::new(0, msg_send![attr_string, length]),
101                nil,
102            );
103            assert_ne!(rtfd_data, nil);
104            // if rtfd_data != nil {
105            //     pasteboard.setData_forType(rtfd_data, NSPasteboardTypeRTFD);
106            // }
107
108            // let rtf_data = attributed_string.RTFFromRange_documentAttributes_(
109            //     NSRange::new(0, attributed_string.length()),
110            //     nil,
111            // );
112            // if rtf_data != nil {
113            //     pasteboard.setData_forType(rtf_data, NSPasteboardTypeRTF);
114            // }
115
116            // let plain_text = attributed_string.string();
117            // pasteboard.setString_forType(plain_text, NSPasteboardTypeString);
118        }
119    }
120}