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