diff --git a/.changes/run-iteration-improvements.md b/.changes/run-iteration-improvements.md new file mode 100644 index 000000000000..bbe6b7c46e45 --- /dev/null +++ b/.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. diff --git a/core/tauri-runtime-wry/src/lib.rs b/core/tauri-runtime-wry/src/lib.rs index 55cafb162ace..8b88537b9137 100644 --- a/core/tauri-runtime-wry/src/lib.rs +++ b/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 Runtime for Wry { } #[cfg(desktop)] - fn run_iteration) + 'static>(&mut self, mut callback: F) -> RunIteration { + fn run_iteration)>(&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 Runtime for Wry { #[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 Runtime for Wry { } } - iteration = handle_event_loop( + handle_event_loop( event, event_loop, control_flow, @@ -2341,8 +2339,6 @@ impl Runtime for Wry { }, ); }); - - iteration } fn run) + 'static>(self, mut callback: F) { @@ -2392,7 +2388,7 @@ impl Runtime for Wry { } pub struct EventLoopIterationContext<'a, T: UserEvent> { - pub callback: &'a mut (dyn FnMut(RunEvent) + 'static), + pub callback: &'a mut (dyn FnMut(RunEvent)), pub webview_id_map: WindowIdStore, pub windows: Rc>>, #[cfg(feature = "tracing")] @@ -2408,7 +2404,7 @@ fn handle_user_message( event_loop: &EventLoopWindowTarget>, message: Message, context: UserMessageContext, -) -> RunIteration { +) { let UserMessageContext { webview_id_map, windows, @@ -2825,11 +2821,6 @@ fn handle_user_message( Message::UserEvent(_) => (), } - - let it = RunIteration { - window_count: windows.borrow().len(), - }; - it } fn handle_event_loop( @@ -2837,7 +2828,7 @@ fn handle_event_loop( event_loop: &EventLoopWindowTarget>, control_flow: &mut ControlFlow, context: EventLoopIterationContext<'_, T>, -) -> RunIteration { +) { let EventLoopIterationContext { callback, webview_id_map, @@ -2994,7 +2985,7 @@ fn handle_event_loop( } 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( } _ => (), } - - let it = RunIteration { - window_count: windows.borrow().len(), - }; - it } -fn on_close_requested<'a, T: UserEvent>( - callback: &'a mut (dyn FnMut(RunEvent) + 'static), +fn on_close_requested( + callback: &mut (dyn FnMut(RunEvent)), window_id: WindowId, windows: Rc>>, ) { diff --git a/core/tauri-runtime/src/lib.rs b/core/tauri-runtime/src/lib.rs index 5c57c5b8a114..2d5ceb121149 100644 --- a/core/tauri-runtime/src/lib.rs +++ b/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: 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) + 'static>(&mut self, callback: F) -> RunIteration; + fn run_iteration)>(&mut self, callback: F); /// Run the webview runtime. fn run) + 'static>(self, callback: F); diff --git a/core/tauri/Cargo.toml b/core/tauri/Cargo.toml index 4d4d42a17fc4..92a970c78762 100644 --- a/core/tauri/Cargo.toml +++ b/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" diff --git a/core/tauri/src/app.rs b/core/tauri/src/app.rs index 0505280291b2..c91d33936d9a 100644 --- a/core/tauri/src/app.rs +++ b/core/tauri/src/app.rs @@ -406,6 +406,7 @@ pub struct App { setup: Option>, manager: Arc>, handle: AppHandle, + ran_setup: bool, } impl fmt::Debug for App { @@ -862,59 +863,57 @@ impl App { 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, 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, 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 HasRawDisplayHandle for App { #[cfg_attr(feature = "tracing", tracing::instrument(name = "app::setup"))] fn setup(app: &mut App) -> crate::Result<()> { + app.ran_setup = true; + let window_labels = app .config() .tauri @@ -1701,19 +1703,17 @@ fn setup(app: &mut App) -> crate::Result<()> { Ok(()) } -fn on_event_loop_event, RunEvent) + 'static>( +fn on_event_loop_event( app_handle: &AppHandle, event: RuntimeRunEvent, manager: &AppManager, - 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, RunEvent) + 'static>( .expect("poisoned plugin store") .on_event(app_handle, &event); - if let Some(c) = callback { - c(app_handle, event); - } + event } #[cfg(test)] diff --git a/core/tauri/src/lib.rs b/core/tauri/src/lib.rs index 6185844a6ca5..532067bb41f0 100644 --- a/core/tauri/src/lib.rs +++ b/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::{ diff --git a/core/tauri/src/manager/mod.rs b/core/tauri/src/manager/mod.rs index 07b06ba6ab4e..65dffb47003f 100644 --- a/core/tauri/src/manager/mod.rs +++ b/core/tauri/src/manager/mod.rs @@ -542,6 +542,14 @@ impl AppManager { .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> { self.window.windows_lock().clone() } diff --git a/core/tauri/src/manager/window.rs b/core/tauri/src/manager/window.rs index 4f2e2649d867..2ae3f863d94f 100644 --- a/core/tauri/src/manager/window.rs +++ b/core/tauri/src/manager/window.rs @@ -125,10 +125,6 @@ impl WindowManager { window } - pub(crate) fn on_window_close(&self, label: &str) { - self.windows_lock().remove(label); - } - pub fn labels(&self) -> HashSet { self.windows_lock().keys().cloned().collect() } diff --git a/core/tauri/src/test/mock_runtime.rs b/core/tauri/src/test/mock_runtime.rs index a663cc44a5e0..d7472c1bd440 100644 --- a/core/tauri/src/test/mock_runtime.rs +++ b/core/tauri/src/test/mock_runtime.rs @@ -992,12 +992,7 @@ impl Runtime for MockRuntime { target_os = "netbsd", target_os = "openbsd" ))] - fn run_iteration) + 'static>( - &mut self, - callback: F, - ) -> tauri_runtime::RunIteration { - Default::default() - } + fn run_iteration)>(&mut self, callback: F) {} fn run) + 'static>(self, mut callback: F) { self.is_running.store(true, Ordering::Relaxed); diff --git a/examples/run-iteration/README.md b/examples/run-iteration/README.md new file mode 100644 index 000000000000..81a408271772 --- /dev/null +++ b/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`. diff --git a/examples/run-iteration/index.html b/examples/run-iteration/index.html new file mode 100644 index 000000000000..30db0bd4eb99 --- /dev/null +++ b/examples/run-iteration/index.html @@ -0,0 +1,11 @@ + + + + + + Welcome to Tauri! + + +

Welcome to Tauri!

+ + diff --git a/examples/run-iteration/main.rs b/examples/run-iteration/main.rs new file mode 100644 index 000000000000..3ff40b4b1008 --- /dev/null +++ b/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; + } + } +} diff --git a/examples/run-iteration/tauri.conf.json b/examples/run-iteration/tauri.conf.json new file mode 100644 index 000000000000..017ada2512c7 --- /dev/null +++ b/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" + } + } +}