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 crate::platform::mac::ns_string;
54
55 use super::*;
56 use cocoa::appkit::NSImage;
57 use cocoa::base::nil;
58 use cocoa::foundation::NSAutoreleasePool;
59 #[test]
60 #[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
61 fn test_nsattributed_string() {
62 // TODO move these to parent module once it's actually ready to be used
63 #[allow(non_snake_case)]
64 pub trait NSTextAttachment: Sized {
65 unsafe fn alloc(_: Self) -> id {
66 msg_send![class!(NSTextAttachment), alloc]
67 }
68 }
69
70 impl NSTextAttachment for id {}
71
72 unsafe {
73 let image: id = {
74 let img: id = msg_send![class!(NSImage), alloc];
75 let img: id = msg_send![img, initWithContentsOfFile: ns_string("test.jpeg")];
76 let img: id = msg_send![img, autorelease];
77 img
78 };
79 let _size = image.size();
80
81 let string = ns_string("Test String");
82 let attr_string = NSMutableAttributedString::alloc(nil)
83 .init_attributed_string(string)
84 .autorelease();
85 let hello_string = ns_string("Hello World");
86 let hello_attr_string = NSAttributedString::alloc(nil)
87 .init_attributed_string(hello_string)
88 .autorelease();
89 attr_string.appendAttributedString_(hello_attr_string);
90
91 let attachment: id = msg_send![NSTextAttachment::alloc(nil), autorelease];
92 let _: () = msg_send![attachment, setImage: image];
93 let image_attr_string =
94 msg_send![class!(NSAttributedString), attributedStringWithAttachment: attachment];
95 attr_string.appendAttributedString_(image_attr_string);
96
97 let another_string = ns_string("Another String");
98 let another_attr_string = NSAttributedString::alloc(nil)
99 .init_attributed_string(another_string)
100 .autorelease();
101 attr_string.appendAttributedString_(another_attr_string);
102
103 let _len: cocoa::foundation::NSUInteger = msg_send![attr_string, length];
104
105 ///////////////////////////////////////////////////
106 // pasteboard.clearContents();
107
108 let rtfd_data = attr_string.RTFDFromRange_documentAttributes_(
109 NSRange::new(0, msg_send![attr_string, length]),
110 nil,
111 );
112 assert_ne!(rtfd_data, nil);
113 // if rtfd_data != nil {
114 // pasteboard.setData_forType(rtfd_data, NSPasteboardTypeRTFD);
115 // }
116
117 // let rtf_data = attributed_string.RTFFromRange_documentAttributes_(
118 // NSRange::new(0, attributed_string.length()),
119 // nil,
120 // );
121 // if rtf_data != nil {
122 // pasteboard.setData_forType(rtf_data, NSPasteboardTypeRTF);
123 // }
124
125 // let plain_text = attributed_string.string();
126 // pasteboard.setString_forType(plain_text, NSPasteboardTypeString);
127 }
128 }
129}