Skip to content

Commit

Permalink
feat: add AppHandle::remove_plugin and plugin on_drop, closes #4361
Browse files Browse the repository at this point in the history
… (#4443)
  • Loading branch information
lucasfernog committed Jun 24, 2022
1 parent 4b5291d commit be4bb39
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 5 deletions.
5 changes: 5 additions & 0 deletions .changes/plugin-on-drop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tauri": patch
---

Added `on_drop` hook to the `plugin::Builder`.
5 changes: 5 additions & 0 deletions .changes/remove-plugin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tauri": patch
---

Added `AppHandle::remove_plugin`.
103 changes: 101 additions & 2 deletions core/tauri/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,31 @@ impl<R: Runtime> AppHandle<R> {
self.runtime_handle.remove_system_tray().map_err(Into::into)
}

/// Adds a plugin to the runtime.
/// Adds a Tauri application plugin.
/// This function can be used to register a plugin that is loaded dynamically e.g. after login.
/// For plugins that are created when the app is started, prefer [`Builder::plugin`].
///
/// See [`Builder::plugin`] for more information.
///
/// # Examples
///
/// ```
/// use tauri::{plugin::{Builder as PluginBuilder, TauriPlugin}, Runtime};
///
/// fn init_plugin<R: Runtime>() -> TauriPlugin<R> {
/// PluginBuilder::new("dummy").build()
/// }
///
/// tauri::Builder::default()
/// .setup(move |app| {
/// let handle = app.handle();
/// std::thread::spawn(move || {
/// handle.plugin(init_plugin());
/// });
///
/// Ok(())
/// });
/// ```
pub fn plugin<P: Plugin<R> + 'static>(&self, mut plugin: P) -> crate::Result<()> {
plugin
.initialize(
Expand All @@ -434,6 +458,41 @@ impl<R: Runtime> AppHandle<R> {
Ok(())
}

/// Removes the plugin with the given name.
///
/// # Examples
///
/// ```
/// use tauri::{plugin::{Builder as PluginBuilder, TauriPlugin, Plugin}, Runtime};
///
/// fn init_plugin<R: Runtime>() -> TauriPlugin<R> {
/// PluginBuilder::new("dummy").build()
/// }
///
/// let plugin = init_plugin();
/// // `.name()` requires the `PLugin` trait import
/// let plugin_name = plugin.name();
/// tauri::Builder::default()
/// .plugin(plugin)
/// .setup(move |app| {
/// let handle = app.handle();
/// std::thread::spawn(move || {
/// handle.remove_plugin(plugin_name);
/// });
///
/// Ok(())
/// });
/// ```
pub fn remove_plugin(&self, plugin: &'static str) -> bool {
self
.manager()
.inner
.plugins
.lock()
.unwrap()
.unregister(plugin)
}

/// Exits the app. This is the same as [`std::process::exit`], but it performs cleanup on this application.
pub fn exit(&self, exit_code: i32) {
self.cleanup_before_exit();
Expand Down Expand Up @@ -940,7 +999,47 @@ impl<R: Runtime> Builder<R> {
self
}

/// Adds a plugin to the runtime.
/// Adds a Tauri application plugin.
///
/// A plugin is created using the [`crate::plugin::Builder`] struct.Check its documentation for more information.
///
/// # Examples
///
/// ```
/// mod plugin {
/// use tauri::{plugin::{Builder as PluginBuilder, TauriPlugin}, RunEvent, Runtime};
///
/// // this command can be called in the frontend using `invoke('plugin:window|do_something')`.
/// #[tauri::command]
/// async fn do_something<R: Runtime>(app: tauri::AppHandle<R>, window: tauri::Window<R>) -> Result<(), String> {
/// println!("command called");
/// Ok(())
/// }
/// pub fn init<R: Runtime>() -> TauriPlugin<R> {
/// PluginBuilder::new("window")
/// .setup(|app| {
/// // initialize the plugin here
/// Ok(())
/// })
/// .on_event(|app, event| {
/// match event {
/// RunEvent::Ready => {
/// println!("app is ready");
/// }
/// RunEvent::WindowEvent { label, event, .. } => {
/// println!("window {} received an event: {:?}", label, event);
/// }
/// _ => (),
/// }
/// })
/// .invoke_handler(tauri::generate_handler![do_something])
/// .build()
/// }
/// }
///
/// tauri::Builder::default()
/// .plugin(plugin::init());
/// ```
#[must_use]
pub fn plugin<P: Plugin<R> + 'static>(mut self, plugin: P) -> Self {
self.plugins.register(plugin);
Expand Down
52 changes: 49 additions & 3 deletions core/tauri/src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ type SetupWithConfigHook<R, T> = dyn FnOnce(&AppHandle<R>, T) -> Result<()> + Se
type OnWebviewReady<R> = dyn FnMut(Window<R>) + Send;
type OnEvent<R> = dyn FnMut(&AppHandle<R>, &RunEvent) + Send;
type OnPageLoad<R> = dyn FnMut(Window<R>, PageLoadPayload) + Send;
type OnDrop<R> = dyn FnOnce(AppHandle<R>) + Send;

/// Builds a [`TauriPlugin`].
///
Expand Down Expand Up @@ -141,6 +142,7 @@ pub struct Builder<R: Runtime, C: DeserializeOwned = ()> {
on_page_load: Box<OnPageLoad<R>>,
on_webview_ready: Box<OnWebviewReady<R>>,
on_event: Box<OnEvent<R>>,
on_drop: Option<Box<OnDrop<R>>>,
}

impl<R: Runtime, C: DeserializeOwned> Builder<R, C> {
Expand All @@ -155,6 +157,7 @@ impl<R: Runtime, C: DeserializeOwned> Builder<R, C> {
on_page_load: Box::new(|_, _| ()),
on_webview_ready: Box::new(|_| ()),
on_event: Box::new(|_, _| ()),
on_drop: None,
}
}

Expand Down Expand Up @@ -311,7 +314,7 @@ impl<R: Runtime, C: DeserializeOwned> Builder<R, C> {
#[must_use]
pub fn on_page_load<F>(mut self, on_page_load: F) -> Self
where
F: FnMut(Window<R>, PageLoadPayload) + Send + Sync + 'static,
F: FnMut(Window<R>, PageLoadPayload) + Send + 'static,
{
self.on_page_load = Box::new(on_page_load);
self
Expand All @@ -335,7 +338,7 @@ impl<R: Runtime, C: DeserializeOwned> Builder<R, C> {
#[must_use]
pub fn on_webview_ready<F>(mut self, on_webview_ready: F) -> Self
where
F: FnMut(Window<R>) + Send + Sync + 'static,
F: FnMut(Window<R>) + Send + 'static,
{
self.on_webview_ready = Box::new(on_webview_ready);
self
Expand Down Expand Up @@ -367,37 +370,74 @@ impl<R: Runtime, C: DeserializeOwned> Builder<R, C> {
#[must_use]
pub fn on_event<F>(mut self, on_event: F) -> Self
where
F: FnMut(&AppHandle<R>, &RunEvent) + Send + Sync + 'static,
F: FnMut(&AppHandle<R>, &RunEvent) + Send + 'static,
{
self.on_event = Box::new(on_event);
self
}

/// Callback invoked when the plugin is dropped.
///
/// # Examples
///
/// ```rust
/// use tauri::{plugin::{Builder, TauriPlugin}, Runtime};
///
/// fn init<R: Runtime>() -> TauriPlugin<R> {
/// Builder::new("example")
/// .on_drop(|app| {
/// println!("plugin has been dropped and is no longer running");
/// // you can run cleanup logic here
/// })
/// .build()
/// }
/// ```
#[must_use]
pub fn on_drop<F>(mut self, on_drop: F) -> Self
where
F: FnOnce(AppHandle<R>) + Send + 'static,
{
self.on_drop.replace(Box::new(on_drop));
self
}

/// Builds the [TauriPlugin].
pub fn build(self) -> TauriPlugin<R, C> {
TauriPlugin {
name: self.name,
app: None,
invoke_handler: self.invoke_handler,
setup: self.setup,
setup_with_config: self.setup_with_config,
js_init_script: self.js_init_script,
on_page_load: self.on_page_load,
on_webview_ready: self.on_webview_ready,
on_event: self.on_event,
on_drop: self.on_drop,
}
}
}

/// Plugin struct that is returned by the [`Builder`]. Should only be constructed through the builder.
pub struct TauriPlugin<R: Runtime, C: DeserializeOwned = ()> {
name: &'static str,
app: Option<AppHandle<R>>,
invoke_handler: Box<InvokeHandler<R>>,
setup: Option<Box<SetupHook<R>>>,
setup_with_config: Option<Box<SetupWithConfigHook<R, C>>>,
js_init_script: Option<String>,
on_page_load: Box<OnPageLoad<R>>,
on_webview_ready: Box<OnWebviewReady<R>>,
on_event: Box<OnEvent<R>>,
on_drop: Option<Box<OnDrop<R>>>,
}

impl<R: Runtime, C: DeserializeOwned> Drop for TauriPlugin<R, C> {
fn drop(&mut self) {
if let (Some(on_drop), Some(app)) = (self.on_drop.take(), self.app.take()) {
on_drop(app);
}
}
}

impl<R: Runtime, C: DeserializeOwned> Plugin<R> for TauriPlugin<R, C> {
Expand All @@ -406,6 +446,7 @@ impl<R: Runtime, C: DeserializeOwned> Plugin<R> for TauriPlugin<R, C> {
}

fn initialize(&mut self, app: &AppHandle<R>, config: JsonValue) -> Result<()> {
self.app.replace(app.clone());
if let Some(s) = self.setup.take() {
(s)(app)?;
}
Expand Down Expand Up @@ -466,6 +507,11 @@ impl<R: Runtime> PluginStore<R> {
self.store.insert(plugin.name(), Box::new(plugin)).is_some()
}

/// Removes the plugin with the given name from the store.
pub fn unregister(&mut self, plugin: &'static str) -> bool {
self.store.remove(plugin).is_some()
}

/// Initializes all plugins in the store.
pub(crate) fn initialize(
&mut self,
Expand Down

0 comments on commit be4bb39

Please sign in to comment.