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_(
 74                NSString::alloc(nil).init_str("/Users/rtfeldman/Downloads/test.jpeg"),
 75            );
 76            let _size = image.size();
 77
 78            let string = NSString::alloc(nil).init_str("Test String");
 79            let attr_string = NSMutableAttributedString::alloc(nil).init_attributed_string(string);
 80            let hello_string = NSString::alloc(nil).init_str("Hello World");
 81            let hello_attr_string =
 82                NSAttributedString::alloc(nil).init_attributed_string(hello_string);
 83            attr_string.appendAttributedString_(hello_attr_string);
 84
 85            let attachment = NSTextAttachment::alloc(nil);
 86            let _: () = msg_send![attachment, setImage: image];
 87            let image_attr_string =
 88                msg_send![class!(NSAttributedString), attributedStringWithAttachment: attachment];
 89            attr_string.appendAttributedString_(image_attr_string);
 90
 91            let another_string = NSString::alloc(nil).init_str("Another String");
 92            let another_attr_string =
 93                NSAttributedString::alloc(nil).init_attributed_string(another_string);
 94            attr_string.appendAttributedString_(another_attr_string);
 95
 96            let _len: cocoa::foundation::NSUInteger = msg_send![attr_string, length];
 97
 98            ///////////////////////////////////////////////////
 99            // pasteboard.clearContents();
100
101            let rtfd_data = attr_string.RTFDFromRange_documentAttributes_(
102                NSRange::new(0, msg_send![attr_string, length]),
103                nil,
104            );
105            assert_ne!(rtfd_data, nil);
106            // if rtfd_data != nil {
107            //     pasteboard.setData_forType(rtfd_data, NSPasteboardTypeRTFD);
108            // }
109
110            // let rtf_data = attributed_string.RTFFromRange_documentAttributes_(
111            //     NSRange::new(0, attributed_string.length()),
112            //     nil,
113            // );
114            // if rtf_data != nil {
115            //     pasteboard.setData_forType(rtf_data, NSPasteboardTypeRTF);
116            // }
117
118            // let plain_text = attributed_string.string();
119            // pasteboard.setString_forType(plain_text, NSPasteboardTypeString);
120        }
121    }
122}