1use bitflags::bitflags;
2use cocoa::{
3 appkit::{NSView, NSViewHeightSizable, NSViewWidthSizable, NSWindowCollectionBehavior},
4 base::{id, nil, BOOL, YES},
5 foundation::NSRect,
6};
7use objc::{
8 class,
9 declare::ClassDecl,
10 msg_send,
11 runtime::{self, Class, Object, Sel},
12 sel, sel_impl, Message,
13};
14use objc_foundation::INSObject;
15use objc_id::{Id, ShareId};
16use tauri::{Runtime, WebviewWindow};
17
18bitflags! {
19 struct NSTrackingAreaOptions: u32 {
20 const NSTrackingActiveAlways = 0x80;
21 const NSTrackingMouseEnteredAndExited = 0x01;
22 const NSTrackingMouseMoved = 0x02;
23 const NSTrackingCursorUpdate = 0x04;
24 }
25}
26
27extern "C" {
28 pub fn object_setClass(obj: id, cls: id) -> id;
29}
30
31const CLS_NAME: &str = "RawNSPanel";
32
33pub struct RawNSPanel;
34
35unsafe impl Sync for RawNSPanel {}
36unsafe impl Send for RawNSPanel {}
37
38impl INSObject for RawNSPanel {
39 fn class() -> &'static runtime::Class {
40 Class::get(CLS_NAME).unwrap_or_else(Self::define_class)
41 }
42}
43
44impl RawNSPanel {
45 extern "C" fn can_become_key_window(_: &Object, _: Sel) -> BOOL {
47 YES
48 }
49
50 extern "C" fn dealloc(this: &mut Object, _cmd: Sel) {
51 unsafe {
52 let superclass = class!(NSObject);
53 let dealloc: extern "C" fn(&mut Object, Sel) =
54 msg_send![super(this, superclass), dealloc];
55 dealloc(this, _cmd);
56 }
57 }
58
59 fn define_class() -> &'static Class {
60 let mut cls = ClassDecl::new(CLS_NAME, class!(NSPanel))
61 .unwrap_or_else(|| panic!("Unable to register {} class", CLS_NAME));
62
63 unsafe {
64 cls.add_method(
65 sel!(canBecomeKeyWindow),
66 Self::can_become_key_window as extern "C" fn(&Object, Sel) -> BOOL,
67 );
68
69 cls.add_method(
70 sel!(dealloc),
71 Self::dealloc as extern "C" fn(&mut Object, Sel),
72 );
73 }
74
75 cls.register()
76 }
77
78 pub fn show(&self) {
79 self.make_first_responder(Some(self.content_view()));
80 self.order_front_regardless();
81 self.make_key_window();
82 }
83
84 pub fn is_visible(&self) -> bool {
85 let flag: BOOL = unsafe { msg_send![self, isVisible] };
86 flag == YES
87 }
88
89 pub fn is_floating_panel(&self) -> bool {
90 let flag: BOOL = unsafe { msg_send![self, isFloatingPanel] };
91 flag == YES
92 }
93
94 pub fn make_key_window(&self) {
95 let _: () = unsafe { msg_send![self, makeKeyWindow] };
96 }
97
98 pub fn resign_key_window(&self) {
99 let _: () = unsafe { msg_send![self, resignKeyWindow] };
100 }
101
102 pub fn make_key_and_order_front(&self, sender: Option<id>) {
103 let _: () = unsafe { msg_send![self, makeKeyAndOrderFront: sender.unwrap_or(nil)] };
104 }
105
106 pub fn order_front_regardless(&self) {
107 let _: () = unsafe { msg_send![self, orderFrontRegardless] };
108 }
109
110 pub fn order_out(&self, sender: Option<id>) {
111 let _: () = unsafe { msg_send![self, orderOut: sender.unwrap_or(nil)] };
112 }
113
114 pub fn content_view(&self) -> id {
115 unsafe { msg_send![self, contentView] }
116 }
117
118 pub fn make_first_responder(&self, sender: Option<id>) {
119 if let Some(responder) = sender {
120 let _: () = unsafe { msg_send![self, makeFirstResponder: responder] };
121 } else {
122 let _: () = unsafe { msg_send![self, makeFirstResponder: self] };
123 }
124 }
125
126 pub fn set_level(&self, level: i32) {
127 let _: () = unsafe { msg_send![self, setLevel: level] };
128 }
129
130 pub fn set_alpha_value(&self, value: f64) {
131 let _: () = unsafe { msg_send![self, setAlphaValue: value] };
132 }
133
134 pub fn set_content_size(&self, width: f64, height: f64) {
135 let _: () = unsafe { msg_send![self, setContentSize: (width, height)] };
136 }
137
138 pub fn set_style_mask(&self, style_mask: i32) {
139 let _: () = unsafe { msg_send![self, setStyleMask: style_mask] };
140 }
141
142 pub fn set_collection_behaviour(&self, behaviour: NSWindowCollectionBehavior) {
143 let _: () = unsafe { msg_send![self, setCollectionBehavior: behaviour] };
144 }
145
146 pub fn set_delegate<T>(&self, delegate: Id<T>) {
147 let _: () = unsafe { msg_send![self, setDelegate: delegate] };
148 }
149
150 pub fn set_floating_panel(&self, value: bool) {
151 let _: () = unsafe { msg_send![self, setFloatingPanel: value] };
152 }
153
154 pub fn set_accepts_mouse_moved_events(&self, value: bool) {
155 let _: () = unsafe { msg_send![self, setAcceptsMouseMovedEvents: value] };
156 }
157
158 pub fn set_ignore_mouse_events(&self, value: bool) {
159 let _: () = unsafe { msg_send![self, setIgnoresMouseEvents: value] };
160 }
161
162 pub fn set_hides_on_deactivate(&self, value: bool) {
163 let _: () = unsafe { msg_send![self, setHidesOnDeactivate: value] };
164 }
165
166 pub fn set_moveable_by_window_background(&self, value: bool) {
167 let _: () = unsafe { msg_send![self, setMovableByWindowBackground: value] };
168 }
169
170 pub fn set_becomes_key_only_if_needed(&self, value: bool) {
171 let _: () = unsafe { msg_send![self, setBecomesKeyOnlyIfNeeded: value] };
172 }
173
174 pub fn set_works_when_modal(&self, value: bool) {
175 let _: () = unsafe { msg_send![self, setWorksWhenModal: value] };
176 }
177
178 pub fn set_opaque(&self, value: bool) {
179 let _: () = unsafe { msg_send![self, setOpaque: value] };
180 }
181
182 pub fn set_has_shadow(&self, value: bool) {
183 let _: () = unsafe { msg_send![self, setHasShadow: value] };
184 }
185
186 pub fn set_released_when_closed(&self, value: bool) {
187 let _: () = unsafe { msg_send![self, setReleasedWhenClosed: value] };
188 }
189
190 #[deprecated(
191 since = "2.0.1",
192 note = "Use set_released_when_closed(bool) instead. This method will be removed in a future version."
193 )]
194 pub fn released_when_closed(&self, value: bool) {
195 self.set_released_when_closed(value);
196 }
197
198 pub fn close(&self) {
199 let _: () = unsafe { msg_send![self, close] };
200 }
201
202 pub fn handle(&mut self) -> ShareId<Self> {
203 unsafe { ShareId::from_ptr(self as *mut Self) }
204 }
205
206 fn add_tracking_area(&self) {
207 let view: id = self.content_view();
208 let bounds: NSRect = unsafe { NSView::bounds(view) };
209 let track_view: id = unsafe { msg_send![class!(NSTrackingArea), alloc] };
210 let track_view: id = unsafe {
211 msg_send![
212 track_view,
213 initWithRect: bounds
214 options: NSTrackingAreaOptions::NSTrackingActiveAlways
215 | NSTrackingAreaOptions::NSTrackingMouseEnteredAndExited
216 | NSTrackingAreaOptions::NSTrackingMouseMoved
217 | NSTrackingAreaOptions::NSTrackingCursorUpdate
218 owner: view
219 userInfo: nil
220 ]
221 };
222 let autoresizing_mask = NSViewWidthSizable | NSViewHeightSizable;
223 let () = unsafe { msg_send![view, setAutoresizingMask: autoresizing_mask] };
224 let () = unsafe { msg_send![view, addTrackingArea: track_view] };
225 }
226
227 pub fn from_window<R: Runtime>(window: WebviewWindow<R>) -> Id<Self> {
229 let nswindow: id = window.ns_window().unwrap() as _;
230 let nspanel_class: id = unsafe { msg_send![Self::class(), class] };
231 unsafe {
232 object_setClass(nswindow, nspanel_class);
233 let panel = Id::from_retained_ptr(nswindow as *mut RawNSPanel);
234
235 panel.add_tracking_area();
238
239 panel
240 }
241 }
242}
243
244unsafe impl Message for RawNSPanel {}