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}