Selaa lähdekoodia

refactor(core)!: App::run_iteration improvements (#8696)

* refactor(core): App::run_iteration improvements

* lint

* fixes

* fix exit

* Apply suggestions from code review

Co-authored-by: Fabian-Lars <fabianlars@fabianlars.de>

* update cargo.toml

* booooool

* fix mock runtime

* fix doctests

* fix doctest againrrrr

---------

Co-authored-by: Fabian-Lars <fabianlars@fabianlars.de>
Lucas Fernandes Nogueira 1 vuosi sitten
vanhempi
sitoutus
ec9818accb

+ 5 - 0
.changes/run-iteration-improvements.md

@@ -0,0 +1,5 @@
+---
+"tauri": patch:breaking
+---
+
+Added a callback to the `App::run_iteration` and removed its return value.

+ 10 - 24
core/tauri-runtime-wry/src/lib.rs

@@ -21,8 +21,8 @@ use tauri_runtime::{
     WindowBuilderBase, WindowEvent, WindowId,
   },
   DeviceEventFilter, Error, EventLoopProxy, ExitRequestedEventAction, Icon, Result, RunEvent,
-  RunIteration, Runtime, RuntimeHandle, RuntimeInitArgs, UserAttentionType, UserEvent,
-  WebviewDispatch, WindowDispatch, WindowEventId,
+  Runtime, RuntimeHandle, RuntimeInitArgs, UserAttentionType, UserEvent, WebviewDispatch,
+  WindowDispatch, WindowEventId,
 };
 
 #[cfg(target_os = "macos")]
@@ -2286,7 +2286,7 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
   }
 
   #[cfg(desktop)]
-  fn run_iteration<F: FnMut(RunEvent<T>) + 'static>(&mut self, mut callback: F) -> RunIteration {
+  fn run_iteration<F: FnMut(RunEvent<T>)>(&mut self, mut callback: F) {
     use tao::platform::run_return::EventLoopExtRunReturn;
     let windows = self.context.main_thread.windows.clone();
     let webview_id_map = self.context.webview_id_map.clone();
@@ -2296,8 +2296,6 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
     #[cfg(feature = "tracing")]
     let active_tracing_spans = self.context.main_thread.active_tracing_spans.clone();
 
-    let mut iteration = RunIteration::default();
-
     let proxy = self.event_loop.create_proxy();
 
     self
@@ -2328,7 +2326,7 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
           }
         }
 
-        iteration = handle_event_loop(
+        handle_event_loop(
           event,
           event_loop,
           control_flow,
@@ -2341,8 +2339,6 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
           },
         );
       });
-
-    iteration
   }
 
   fn run<F: FnMut(RunEvent<T>) + 'static>(self, mut callback: F) {
@@ -2392,7 +2388,7 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
 }
 
 pub struct EventLoopIterationContext<'a, T: UserEvent> {
-  pub callback: &'a mut (dyn FnMut(RunEvent<T>) + 'static),
+  pub callback: &'a mut (dyn FnMut(RunEvent<T>)),
   pub webview_id_map: WindowIdStore,
   pub windows: Rc<RefCell<HashMap<WindowId, WindowWrapper>>>,
   #[cfg(feature = "tracing")]
@@ -2408,7 +2404,7 @@ fn handle_user_message<T: UserEvent>(
   event_loop: &EventLoopWindowTarget<Message<T>>,
   message: Message<T>,
   context: UserMessageContext,
-) -> RunIteration {
+) {
   let UserMessageContext {
     webview_id_map,
     windows,
@@ -2825,11 +2821,6 @@ fn handle_user_message<T: UserEvent>(
 
     Message::UserEvent(_) => (),
   }
-
-  let it = RunIteration {
-    window_count: windows.borrow().len(),
-  };
-  it
 }
 
 fn handle_event_loop<T: UserEvent>(
@@ -2837,7 +2828,7 @@ fn handle_event_loop<T: UserEvent>(
   event_loop: &EventLoopWindowTarget<Message<T>>,
   control_flow: &mut ControlFlow,
   context: EventLoopIterationContext<'_, T>,
-) -> RunIteration {
+) {
   let EventLoopIterationContext {
     callback,
     webview_id_map,
@@ -2994,7 +2985,7 @@ fn handle_event_loop<T: UserEvent>(
       }
       Message::UserEvent(t) => callback(RunEvent::UserEvent(t)),
       message => {
-        return handle_user_message(
+        handle_user_message(
           event_loop,
           message,
           UserMessageContext {
@@ -3010,15 +3001,10 @@ fn handle_event_loop<T: UserEvent>(
     }
     _ => (),
   }
-
-  let it = RunIteration {
-    window_count: windows.borrow().len(),
-  };
-  it
 }
 
-fn on_close_requested<'a, T: UserEvent>(
-  callback: &'a mut (dyn FnMut(RunEvent<T>) + 'static),
+fn on_close_requested<T: UserEvent>(
+  callback: &mut (dyn FnMut(RunEvent<T>)),
   window_id: WindowId,
   windows: Rc<RefCell<HashMap<WindowId, WindowWrapper>>>,
 ) {

+ 2 - 8
core/tauri-runtime/src/lib.rs

@@ -184,12 +184,6 @@ pub enum ExitRequestedEventAction {
   Prevent,
 }
 
-/// Metadata for a runtime event loop iteration on `run_iteration`.
-#[derive(Debug, Clone, Default)]
-pub struct RunIteration {
-  pub window_count: usize,
-}
-
 /// Application's activation policy. Corresponds to NSApplicationActivationPolicy.
 #[cfg(target_os = "macos")]
 #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
@@ -340,9 +334,9 @@ pub trait Runtime<T: UserEvent>: Debug + Sized + 'static {
   /// [`tao`]: https://crates.io/crates/tao
   fn set_device_event_filter(&mut self, filter: DeviceEventFilter);
 
-  /// Runs the one step of the webview runtime event loop and returns control flow to the caller.
+  /// Runs an iteration of the runtime event loop and returns control flow to the caller.
   #[cfg(desktop)]
-  fn run_iteration<F: Fn(RunEvent<T>) + 'static>(&mut self, callback: F) -> RunIteration;
+  fn run_iteration<F: FnMut(RunEvent<T>)>(&mut self, callback: F);
 
   /// Run the webview runtime.
   fn run<F: FnMut(RunEvent<T>) + 'static>(self, callback: F);

+ 4 - 0
core/tauri/Cargo.toml

@@ -205,3 +205,7 @@ path = "../../examples/streaming/main.rs"
 name = "isolation"
 path = "../../examples/isolation/main.rs"
 required-features = [ "isolation" ]
+
+[[example]]
+name = "run-iteration"
+path = "../../examples/run-iteration/main.rs"

+ 30 - 32
core/tauri/src/app.rs

@@ -406,6 +406,7 @@ pub struct App<R: Runtime> {
   setup: Option<SetupHook<R>>,
   manager: Arc<AppManager<R>>,
   handle: AppHandle<R>,
+  ran_setup: bool,
 }
 
 impl<R: Runtime> fmt::Debug for App<R> {
@@ -862,59 +863,57 @@ impl<R: Runtime> App<R> {
         if let Err(e) = setup(&mut self) {
           panic!("Failed to setup app: {e}");
         }
-        on_event_loop_event(
-          &app_handle,
-          RuntimeRunEvent::Ready,
-          &manager,
-          Some(&mut callback),
-        );
+        let event = on_event_loop_event(&app_handle, RuntimeRunEvent::Ready, &manager);
+        callback(&app_handle, event);
       }
       RuntimeRunEvent::Exit => {
-        on_event_loop_event(
-          &app_handle,
-          RuntimeRunEvent::Exit,
-          &manager,
-          Some(&mut callback),
-        );
+        let event = on_event_loop_event(&app_handle, RuntimeRunEvent::Exit, &manager);
+        callback(&app_handle, event);
         app_handle.cleanup_before_exit();
       }
       _ => {
-        on_event_loop_event(&app_handle, event, &manager, Some(&mut callback));
+        let event = on_event_loop_event(&app_handle, event, &manager);
+        callback(&app_handle, event);
       }
     });
   }
 
-  /// Runs a iteration of the runtime event loop and immediately return.
+  /// Runs an iteration of the runtime event loop and immediately return.
   ///
   /// Note that when using this API, app cleanup is not automatically done.
   /// The cleanup calls [`App::cleanup_before_exit`] so you may want to call that function before exiting the application.
   ///
   /// # Examples
   /// ```no_run
+  /// use tauri::Manager;
+  ///
   /// let mut app = tauri::Builder::default()
   ///   // on an actual app, remove the string argument
   ///   .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
   ///   .expect("error while building tauri application");
+  ///
   /// loop {
-  ///   let iteration = app.run_iteration();
-  ///   if iteration.window_count == 0 {
+  ///   app.run_iteration(|_app, _event| {});
+  ///   if app.webview_windows().is_empty() {
   ///     app.cleanup_before_exit();
   ///     break;
   ///   }
   /// }
   /// ```
   #[cfg(desktop)]
-  #[cfg_attr(feature = "tracing", tracing::instrument(name = "app::run_iteration"))]
-  pub fn run_iteration(&mut self) -> crate::runtime::RunIteration {
+  pub fn run_iteration<F: FnMut(&AppHandle<R>, RunEvent)>(&mut self, mut callback: F) {
     let manager = self.manager.clone();
     let app_handle = self.handle().clone();
+
+    if !self.ran_setup {
+      if let Err(e) = setup(self) {
+        panic!("Failed to setup app: {e}");
+      }
+    }
+
     self.runtime.as_mut().unwrap().run_iteration(move |event| {
-      on_event_loop_event(
-        &app_handle,
-        event,
-        &manager,
-        Option::<&mut Box<dyn FnMut(&AppHandle<R>, RunEvent)>>::None,
-      )
+      let event = on_event_loop_event(&app_handle, event, &manager);
+      callback(&app_handle, event);
     })
   }
 }
@@ -1537,6 +1536,7 @@ tauri::Builder::default()
         runtime_handle,
         manager,
       },
+      ran_setup: false,
     };
 
     #[cfg(desktop)]
@@ -1674,6 +1674,8 @@ unsafe impl<R: Runtime> HasRawDisplayHandle for App<R> {
 
 #[cfg_attr(feature = "tracing", tracing::instrument(name = "app::setup"))]
 fn setup<R: Runtime>(app: &mut App<R>) -> crate::Result<()> {
+  app.ran_setup = true;
+
   let window_labels = app
     .config()
     .tauri
@@ -1701,19 +1703,17 @@ fn setup<R: Runtime>(app: &mut App<R>) -> crate::Result<()> {
   Ok(())
 }
 
-fn on_event_loop_event<R: Runtime, F: FnMut(&AppHandle<R>, RunEvent) + 'static>(
+fn on_event_loop_event<R: Runtime>(
   app_handle: &AppHandle<R>,
   event: RuntimeRunEvent<EventLoopMessage>,
   manager: &AppManager<R>,
-  callback: Option<&mut F>,
-) {
+) -> RunEvent {
   if let RuntimeRunEvent::WindowEvent {
     label,
     event: RuntimeWindowEvent::Destroyed,
   } = &event
   {
-    // TODO: destroy webviews
-    manager.window.on_window_close(label);
+    manager.on_window_close(label);
   }
 
   let event = match event {
@@ -1805,9 +1805,7 @@ fn on_event_loop_event<R: Runtime, F: FnMut(&AppHandle<R>, RunEvent) + 'static>(
     .expect("poisoned plugin store")
     .on_event(app_handle, &event);
 
-  if let Some(c) = callback {
-    c(app_handle, event);
-  }
+  event
 }
 
 #[cfg(test)]

+ 1 - 1
core/tauri/src/lib.rs

@@ -214,7 +214,7 @@ pub use {
       dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Pixel, Position, Size},
       CursorIcon, FileDropEvent,
     },
-    DeviceEventFilter, RunIteration, UserAttentionType,
+    DeviceEventFilter, UserAttentionType,
   },
   self::state::{State, StateManager},
   self::utils::{

+ 8 - 0
core/tauri/src/manager/mod.rs

@@ -542,6 +542,14 @@ impl<R: Runtime> AppManager<R> {
       .map(|w| w.1.clone())
   }
 
+  pub(crate) fn on_window_close(&self, label: &str) {
+    if let Some(window) = self.window.windows_lock().remove(label) {
+      for webview in window.webviews() {
+        self.webview.webviews_lock().remove(webview.label());
+      }
+    }
+  }
+
   pub fn windows(&self) -> HashMap<String, Window<R>> {
     self.window.windows_lock().clone()
   }

+ 0 - 4
core/tauri/src/manager/window.rs

@@ -125,10 +125,6 @@ impl<R: Runtime> WindowManager<R> {
     window
   }
 
-  pub(crate) fn on_window_close(&self, label: &str) {
-    self.windows_lock().remove(label);
-  }
-
   pub fn labels(&self) -> HashSet<String> {
     self.windows_lock().keys().cloned().collect()
   }

+ 1 - 6
core/tauri/src/test/mock_runtime.rs

@@ -992,12 +992,7 @@ impl<T: UserEvent> Runtime<T> for MockRuntime {
     target_os = "netbsd",
     target_os = "openbsd"
   ))]
-  fn run_iteration<F: Fn(RunEvent<T>) + 'static>(
-    &mut self,
-    callback: F,
-  ) -> tauri_runtime::RunIteration {
-    Default::default()
-  }
+  fn run_iteration<F: FnMut(RunEvent<T>)>(&mut self, callback: F) {}
 
   fn run<F: FnMut(RunEvent<T>) + 'static>(self, mut callback: F) {
     self.is_running.store(true, Ordering::Relaxed);

+ 3 - 0
examples/run-iteration/README.md

@@ -0,0 +1,3 @@
+# Run Iteration Example
+
+To execute run the following on the root directory of the repository: `cargo run --example run-iteration`.

+ 11 - 0
examples/run-iteration/index.html

@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>Welcome to Tauri!</title>
+  </head>
+  <body>
+    <h1>Welcome to Tauri!</h1>
+  </body>
+</html>

+ 26 - 0
examples/run-iteration/main.rs

@@ -0,0 +1,26 @@
+// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: MIT
+
+#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
+
+use tauri::Manager;
+
+fn main() {
+  let mut app = tauri::Builder::default()
+    .build(tauri::generate_context!(
+      "../../examples/run-iteration/tauri.conf.json"
+    ))
+    .expect("error while building tauri application");
+
+  loop {
+    app.run_iteration(|_app, _event| {
+      //println!("{:?}", _event);
+    });
+
+    if app.webview_windows().is_empty() {
+      app.cleanup_before_exit();
+      break;
+    }
+  }
+}

+ 57 - 0
examples/run-iteration/tauri.conf.json

@@ -0,0 +1,57 @@
+{
+  "$schema": "../../core/tauri-config-schema/schema.json",
+  "build": {
+    "distDir": [
+      "index.html"
+    ],
+    "devPath": [
+      "index.html"
+    ],
+    "beforeDevCommand": "",
+    "beforeBuildCommand": ""
+  },
+  "package": {
+    "productName": "RunIteration",
+    "version": "0.1.0"
+  },
+  "tauri": {
+    "bundle": {
+      "active": true,
+      "targets": "all",
+      "identifier": "com.tauri.dev",
+      "icon": [
+        "../.icons/32x32.png",
+        "../.icons/128x128.png",
+        "../.icons/128x128@2x.png",
+        "../.icons/icon.icns",
+        "../.icons/icon.ico"
+      ],
+      "resources": [],
+      "externalBin": [],
+      "copyright": "",
+      "category": "DeveloperTool",
+      "shortDescription": "",
+      "longDescription": "",
+      "deb": {
+        "depends": []
+      },
+      "macOS": {
+        "frameworks": [],
+        "files": {},
+        "exceptionDomain": ""
+      }
+    },
+    "windows": [
+      {
+        "title": "Welcome to Tauri!",
+        "width": 800,
+        "height": 600,
+        "resizable": true,
+        "fullscreen": false
+      }
+    ],
+    "security": {
+      "csp": "default-src 'self'; connect-src ipc: http://ipc.localhost"
+    }
+  }
+}