1pub mod builder;
2pub mod common;
3pub mod event;
4pub mod panel;
5
6#[doc(hidden)]
8pub use objc2;
9#[doc(hidden)]
10pub use objc2_app_kit;
11#[doc(hidden)]
12pub use objc2_foundation;
13#[doc(hidden)]
14pub use pastey;
15
16use std::{
17 any::Any,
18 collections::HashMap,
19 sync::{Arc, Mutex},
20};
21
22use objc2::runtime::ProtocolObject;
23use objc2_app_kit::NSWindowDelegate;
24use tauri::{
25 plugin::{Builder, TauriPlugin},
26 Manager, Runtime, WebviewWindow,
27};
28
29pub use builder::{CollectionBehavior, PanelBuilder, PanelLevel, StyleMask, TrackingAreaOptions};
30
31pub use objc2::runtime::AnyObject;
33pub use objc2_app_kit::{NSPanel, NSResponder, NSView, NSWindow};
34pub use objc2_foundation::{NSNotification, NSObject, NSPoint, NSRect, NSSize};
35
36pub trait EventHandler {
38 fn as_delegate(&self) -> ProtocolObject<dyn NSWindowDelegate>;
40}
41
42pub trait Panel<R: tauri::Runtime = tauri::Wry>: Send + Sync {
44 fn show(&self);
46
47 fn hide(&self);
49
50 fn to_window(&self) -> Option<tauri::WebviewWindow<R>>;
52
53 fn as_panel(&self) -> &objc2_app_kit::NSPanel;
55
56 fn label(&self) -> &str;
58
59 fn as_any(&self) -> &dyn Any;
61
62 fn set_event_handler(&self, handler: Option<&ProtocolObject<dyn NSWindowDelegate>>);
65
66 fn is_visible(&self) -> bool;
69
70 fn is_floating_panel(&self) -> bool;
72
73 fn becomes_key_only_if_needed(&self) -> bool;
75
76 fn can_become_key_window(&self) -> bool;
78
79 fn can_become_main_window(&self) -> bool;
81
82 fn hides_on_deactivate(&self) -> bool;
84
85 fn make_key_window(&self);
88
89 fn make_main_window(&self);
91
92 fn resign_key_window(&self);
94
95 fn make_key_and_order_front(&self);
97
98 fn order_front_regardless(&self);
100
101 fn show_and_make_key(&self);
103
104 fn set_level(&self, level: i64);
107
108 fn set_floating_panel(&self, value: bool);
110
111 fn set_becomes_key_only_if_needed(&self, value: bool);
113
114 fn set_hides_on_deactivate(&self, value: bool);
116
117 fn set_works_when_modal(&self, value: bool);
119
120 fn set_alpha_value(&self, value: f64);
122
123 fn set_released_when_closed(&self, released: bool);
125
126 fn set_content_size(&self, width: f64, height: f64);
128
129 fn set_has_shadow(&self, value: bool);
131
132 fn set_opaque(&self, value: bool);
134
135 fn set_accepts_mouse_moved_events(&self, value: bool);
137
138 fn set_ignores_mouse_events(&self, value: bool);
140
141 fn set_movable_by_window_background(&self, value: bool);
143
144 fn set_collection_behavior(&self, behavior: objc2_app_kit::NSWindowCollectionBehavior);
146
147 fn content_view(&self) -> objc2::rc::Retained<objc2_app_kit::NSView>;
149
150 fn resign_main_window(&self);
152
153 fn set_style_mask(&self, style_mask: objc2_app_kit::NSWindowStyleMask);
155
156 fn make_first_responder(&self, responder: Option<&objc2_app_kit::NSResponder>) -> bool;
158
159 fn set_corner_radius(&self, radius: f64);
161
162 fn set_transparent(&self, transparent: bool);
164}
165
166pub trait FromWindow<R: Runtime>: Panel<R> + Sized {
168 fn from_window(window: WebviewWindow<R>, label: String) -> tauri::Result<Self>;
170}
171
172pub type PanelHandle<R> = Arc<dyn Panel<R>>;
174
175pub struct Store<R: Runtime> {
176 panels: HashMap<String, PanelHandle<R>>,
177}
178
179impl<R: Runtime> Default for Store<R> {
180 fn default() -> Self {
181 Self {
182 panels: HashMap::new(),
183 }
184 }
185}
186
187pub struct WebviewPanelManager<R: Runtime>(pub Mutex<Store<R>>);
188
189impl<R: Runtime> Default for WebviewPanelManager<R> {
190 fn default() -> Self {
191 Self(Mutex::new(Store::default()))
192 }
193}
194
195pub trait ManagerExt<R: Runtime> {
196 fn get_webview_panel(&self, label: &str) -> Result<PanelHandle<R>, Error>;
197 fn remove_webview_panel(&self, label: &str) -> Option<PanelHandle<R>>;
198}
199
200#[derive(Debug)]
201pub enum Error {
202 PanelNotFound,
203}
204
205impl<R: Runtime, T: Manager<R>> ManagerExt<R> for T {
206 fn get_webview_panel(&self, label: &str) -> Result<PanelHandle<R>, Error> {
207 let manager = self.state::<self::WebviewPanelManager<R>>();
208 let manager = manager.0.lock().unwrap();
209
210 match manager.panels.get(label) {
211 Some(panel) => Ok(panel.clone()),
212 None => Err(Error::PanelNotFound),
213 }
214 }
215
216 fn remove_webview_panel(&self, label: &str) -> Option<PanelHandle<R>> {
217 self.state::<self::WebviewPanelManager<R>>()
218 .0
219 .lock()
220 .unwrap()
221 .panels
222 .remove(label)
223 }
224}
225
226pub trait WebviewWindowExt<R: Runtime> {
227 fn to_panel<P: FromWindow<R> + 'static>(&self) -> tauri::Result<PanelHandle<R>>;
229}
230
231impl<R: Runtime> WebviewWindowExt<R> for WebviewWindow<R> {
232 fn to_panel<P: FromWindow<R> + 'static>(&self) -> tauri::Result<PanelHandle<R>> {
233 let label = self.label().to_string();
234 let panel = P::from_window(self.clone(), label.clone())?;
235 let arc_panel = Arc::new(panel) as PanelHandle<R>;
236
237 let manager = self.state::<WebviewPanelManager<R>>();
238 manager
239 .0
240 .lock()
241 .unwrap()
242 .panels
243 .insert(label, arc_panel.clone());
244
245 Ok(arc_panel)
246 }
247}
248
249pub fn init<R: Runtime>() -> TauriPlugin<R> {
251 Builder::new("nspanel")
252 .setup(|app, _api| {
253 app.manage(self::WebviewPanelManager::<R>::default());
254
255 Ok(())
256 })
257 .build()
258}