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 struct Store<R: Runtime> {
173 panels: HashMap<String, Arc<dyn Panel<R>>>,
174}
175
176impl<R: Runtime> Default for Store<R> {
177 fn default() -> Self {
178 Self {
179 panels: HashMap::new(),
180 }
181 }
182}
183
184pub struct WebviewPanelManager<R: Runtime>(pub Mutex<Store<R>>);
185
186impl<R: Runtime> Default for WebviewPanelManager<R> {
187 fn default() -> Self {
188 Self(Mutex::new(Store::default()))
189 }
190}
191
192pub trait ManagerExt<R: Runtime> {
193 fn get_webview_panel(&self, label: &str) -> Result<Arc<dyn Panel<R>>, Error>;
194 fn remove_webview_panel(&self, label: &str) -> Option<Arc<dyn Panel<R>>>;
195}
196
197#[derive(Debug)]
198pub enum Error {
199 PanelNotFound,
200}
201
202impl<R: Runtime, T: Manager<R>> ManagerExt<R> for T {
203 fn get_webview_panel(&self, label: &str) -> Result<Arc<dyn Panel<R>>, Error> {
204 let manager = self.state::<self::WebviewPanelManager<R>>();
205 let manager = manager.0.lock().unwrap();
206
207 match manager.panels.get(label) {
208 Some(panel) => Ok(panel.clone()),
209 None => Err(Error::PanelNotFound),
210 }
211 }
212
213 fn remove_webview_panel(&self, label: &str) -> Option<Arc<dyn Panel<R>>> {
214 self.state::<self::WebviewPanelManager<R>>()
215 .0
216 .lock()
217 .unwrap()
218 .panels
219 .remove(label)
220 }
221}
222
223pub trait WebviewWindowExt<R: Runtime> {
224 fn to_panel<P: FromWindow<R> + 'static>(&self) -> tauri::Result<Arc<dyn Panel<R>>>;
226}
227
228impl<R: Runtime> WebviewWindowExt<R> for WebviewWindow<R> {
229 fn to_panel<P: FromWindow<R> + 'static>(&self) -> tauri::Result<Arc<dyn Panel<R>>> {
230 let label = self.label().to_string();
231 let panel = P::from_window(self.clone(), label.clone())?;
232 let arc_panel = Arc::new(panel) as Arc<dyn Panel<R>>;
233
234 let manager = self.state::<WebviewPanelManager<R>>();
235 manager
236 .0
237 .lock()
238 .unwrap()
239 .panels
240 .insert(label, arc_panel.clone());
241
242 Ok(arc_panel)
243 }
244}
245
246pub fn init<R: Runtime>() -> TauriPlugin<R> {
248 Builder::new("nspanel")
249 .setup(|app, _api| {
250 app.manage(self::WebviewPanelManager::<R>::default());
251
252 Ok(())
253 })
254 .build()
255}