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#[allow(non_snake_case)]
  9pub trait NSAttributedString: Sized {
 10    unsafe fn alloc(_: Self) -> id {
 11        msg_send![class!(NSAttributedString), alloc]
 12    }
 13
 14    unsafe fn init_attributed_string(self, string: id) -> id;
 15    unsafe fn appendAttributedString_(self, attr_string: id);
 16    unsafe fn RTFDFromRange_documentAttributes_(self, range: NSRange, attrs: id) -> id;
 17    unsafe fn RTFFromRange_documentAttributes_(self, range: NSRange, attrs: id) -> id;
 18    unsafe fn string(self) -> id;
 19}
 20
 21impl NSAttributedString for id {
 22    unsafe fn init_attributed_string(self, string: id) -> id {
 23        msg_send![self, initWithString: string]
 24    }
 25
 26    unsafe fn appendAttributedString_(self, attr_string: id) {
 27        let _: () = msg_send![self, appendAttributedString: attr_string];
 28    }
 29
 30    unsafe fn RTFDFromRange_documentAttributes_(self, range: NSRange, attrs: id) -> id {
 31        msg_send![self, RTFDFromRange: range documentAttributes: attrs]
 32    }
 33
 34    unsafe fn RTFFromRange_documentAttributes_(self, range: NSRange, attrs: id) -> id {
 35        msg_send![self, RTFFromRange: range documentAttributes: attrs]
 36    }
 37
 38    unsafe fn string(self) -> id {
 39        msg_send![self, string]
 40    }
 41}
 42
 43pub trait NSMutableAttributedString: NSAttributedString {
 44    unsafe fn alloc(_: Self) -> id {
 45        msg_send![class!(NSMutableAttributedString), alloc]
 46    }
 47}
 48
 49impl NSMutableAttributedString for id {}
 50
 51#[cfg(test)]
 52mod tests {
 53    use super::*;
 54    use cocoa::appkit::NSImage;
 55    use cocoa::base::nil;
 56    use cocoa::foundation::NSString;
 57    #[test]
 58    #[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
 59    fn test_nsattributed_string() {
 60        // TODO move these to parent module once it's actually ready to be used
 61        #[allow(non_snake_case)]
 62        pub trait NSTextAttachment: Sized {
 63            unsafe fn alloc(_: Self) -> id {
 64                msg_send![class!(NSTextAttachment), alloc]
 65            }
 66        }
 67
 68        impl NSTextAttachment for id {}
 69
 70        unsafe {
 71            let image: id = msg_send![class!(NSImage), alloc];
 72            image.initWithContentsOfFile_(NSString::alloc(nil).init_str("test.jpeg"));
 73            let _size = image.size();
 74
 75            let string = NSString::alloc(nil).init_str("Test String");
 76            let attr_string = NSMutableAttributedString::alloc(nil).init_attributed_string(string);
 77            let hello_string = NSString::alloc(nil).init_str("Hello World");
 78            let hello_attr_string =
 79                NSAttributedString::alloc(nil).init_attributed_string(hello_string);
 80            attr_string.appendAttributedString_(hello_attr_string);
 81
 82            let attachment = NSTextAttachment::alloc(nil);
 83            let _: () = msg_send![attachment, setImage: image];
 84            let image_attr_string =
 85                msg_send![class!(NSAttributedString), attributedStringWithAttachment: attachment];
 86            attr_string.appendAttributedString_(image_attr_string);
 87
 88            let another_string = NSString::alloc(nil).init_str("Another String");
 89            let another_attr_string =
 90                NSAttributedString::alloc(nil).init_attributed_string(another_string);
 91            attr_string.appendAttributedString_(another_attr_string);
 92
 93            let _len: cocoa::foundation::NSUInteger = msg_send![attr_string, length];
 94
 95            ///////////////////////////////////////////////////
 96            // pasteboard.clearContents();
 97
 98            let rtfd_data = attr_string.RTFDFromRange_documentAttributes_(
 99                NSRange::new(0, msg_send![attr_string, length]),
100                nil,
101            );
102            assert_ne!(rtfd_data, nil);
103            // if rtfd_data != nil {
104            //     pasteboard.setData_forType(rtfd_data, NSPasteboardTypeRTFD);
105            // }
106
107            // let rtf_data = attributed_string.RTFFromRange_documentAttributes_(
108            //     NSRange::new(0, attributed_string.length()),
109            //     nil,
110            // );
111            // if rtf_data != nil {
112            //     pasteboard.setData_forType(rtf_data, NSPasteboardTypeRTF);
113            // }
114
115            // let plain_text = attributed_string.string();
116            // pasteboard.setString_forType(plain_text, NSPasteboardTypeString);
117        }
118    }
119}