mirror of
https://github.com/tauri-apps/tauri.git
synced 2026-01-31 00:35:19 +01:00
feat: introduce App::run_return (#12668)
* Introduce `run_return` * Fix compile error * Clone web_context * Refactor to Result API * Fix clippy * Impl mock runtime * Make it desktop-only * Add changelog entry * Fix compile error * Make it semver compatible * Extend changelog entry * Undo semver-hack * Reduce diff * Remove unnecessary mut * Make it take `self` by value * Reduce diff * Undo diff hack * Make everything cfg(desktop) * Rename vars to reduce diff * Fix clippy * Extract make_event_handler * Reduce diff * Deprecate `App::run_return` * Update changelog * Fix compile errors * Accept reference * Create event handler first * Update example * Update manifest * Fix example * Fix example docs * Call `setup` only upon Ready * Update changelog entry * Update docs * Update changelog * Add platform-specific note * update docs * run_return on mobile * Apply suggestions from code review * remove change file --------- Co-authored-by: Lucas Nogueira <lucas@tauri.app>
This commit is contained in:
9
.changes/introduce-run-return.md
Normal file
9
.changes/introduce-run-return.md
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
tauri: 'minor:feat'
|
||||
tauri-runtime: 'minor:feat'
|
||||
tauri-runtime-wry: 'minor:feat'
|
||||
---
|
||||
|
||||
Add `App::run_return` function. Contrary to `App::run`, this will **not** exit the process but instead return the requested exit-code. This allows the host app to perform further cleanup after Tauri has exited. `App::run_return` is not available on iOS and fallbacks to the regular `App::run` functionality.
|
||||
|
||||
The `App::run_iteration` function is deprecated as part of this because calling it in a loop - as suggested by the name - will cause a busy-loop.
|
||||
@@ -2917,39 +2917,51 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
|
||||
});
|
||||
}
|
||||
|
||||
fn run<F: FnMut(RunEvent<T>) + 'static>(self, mut callback: F) {
|
||||
let windows = self.context.main_thread.windows.clone();
|
||||
let window_id_map = self.context.window_id_map.clone();
|
||||
let web_context = self.context.main_thread.web_context;
|
||||
let plugins = self.context.plugins.clone();
|
||||
fn run<F: FnMut(RunEvent<T>) + 'static>(self, callback: F) {
|
||||
let event_handler = make_event_handler(&self, callback);
|
||||
|
||||
#[cfg(feature = "tracing")]
|
||||
let active_tracing_spans = self.context.main_thread.active_tracing_spans.clone();
|
||||
let proxy = self.event_loop.create_proxy();
|
||||
self.event_loop.run(event_handler)
|
||||
}
|
||||
|
||||
self.event_loop.run(move |event, event_loop, control_flow| {
|
||||
for p in plugins.lock().unwrap().iter_mut() {
|
||||
let prevent_default = p.on_event(
|
||||
&event,
|
||||
event_loop,
|
||||
&proxy,
|
||||
control_flow,
|
||||
EventLoopIterationContext {
|
||||
callback: &mut callback,
|
||||
window_id_map: window_id_map.clone(),
|
||||
windows: windows.clone(),
|
||||
#[cfg(feature = "tracing")]
|
||||
active_tracing_spans: active_tracing_spans.clone(),
|
||||
},
|
||||
&web_context,
|
||||
);
|
||||
if prevent_default {
|
||||
return;
|
||||
}
|
||||
}
|
||||
handle_event_loop(
|
||||
event,
|
||||
#[cfg(not(target_os = "ios"))]
|
||||
fn run_return<F: FnMut(RunEvent<T>) + 'static>(mut self, callback: F) -> i32 {
|
||||
use tao::platform::run_return::EventLoopExtRunReturn;
|
||||
|
||||
let event_handler = make_event_handler(&self, callback);
|
||||
|
||||
self.event_loop.run_return(event_handler)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "ios")]
|
||||
fn run_return<F: FnMut(RunEvent<T>) + 'static>(mut self, callback: F) -> i32 {
|
||||
self.run(callback);
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
fn make_event_handler<T, F>(
|
||||
runtime: &Wry<T>,
|
||||
mut callback: F,
|
||||
) -> impl FnMut(Event<'_, Message<T>>, &EventLoopWindowTarget<Message<T>>, &mut ControlFlow)
|
||||
where
|
||||
T: UserEvent,
|
||||
F: FnMut(RunEvent<T>) + 'static,
|
||||
{
|
||||
let windows = runtime.context.main_thread.windows.clone();
|
||||
let window_id_map = runtime.context.window_id_map.clone();
|
||||
let web_context = runtime.context.main_thread.web_context.clone();
|
||||
let plugins = runtime.context.plugins.clone();
|
||||
|
||||
#[cfg(feature = "tracing")]
|
||||
let active_tracing_spans = runtime.context.main_thread.active_tracing_spans.clone();
|
||||
let proxy = runtime.event_loop.create_proxy();
|
||||
|
||||
move |event, event_loop, control_flow| {
|
||||
for p in plugins.lock().unwrap().iter_mut() {
|
||||
let prevent_default = p.on_event(
|
||||
&event,
|
||||
event_loop,
|
||||
&proxy,
|
||||
control_flow,
|
||||
EventLoopIterationContext {
|
||||
callback: &mut callback,
|
||||
@@ -2958,8 +2970,24 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
|
||||
#[cfg(feature = "tracing")]
|
||||
active_tracing_spans: active_tracing_spans.clone(),
|
||||
},
|
||||
&web_context,
|
||||
);
|
||||
})
|
||||
if prevent_default {
|
||||
return;
|
||||
}
|
||||
}
|
||||
handle_event_loop(
|
||||
event,
|
||||
event_loop,
|
||||
control_flow,
|
||||
EventLoopIterationContext {
|
||||
callback: &mut callback,
|
||||
window_id_map: window_id_map.clone(),
|
||||
windows: windows.clone(),
|
||||
#[cfg(feature = "tracing")]
|
||||
active_tracing_spans: active_tracing_spans.clone(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -458,6 +458,9 @@ pub trait Runtime<T: UserEvent>: Debug + Sized + 'static {
|
||||
#[cfg(desktop)]
|
||||
fn run_iteration<F: FnMut(RunEvent<T>) + 'static>(&mut self, callback: F);
|
||||
|
||||
/// Equivalent to [`Runtime::run`] but returns the exit code instead of exiting the process.
|
||||
fn run_return<F: FnMut(RunEvent<T>) + 'static>(self, callback: F) -> i32;
|
||||
|
||||
/// Run the webview runtime.
|
||||
fn run<F: FnMut(RunEvent<T>) + 'static>(self, callback: F);
|
||||
}
|
||||
|
||||
@@ -230,8 +230,8 @@ name = "multiwindow"
|
||||
path = "../../examples/multiwindow/main.rs"
|
||||
|
||||
[[example]]
|
||||
name = "run-iteration"
|
||||
path = "../../examples/run-iteration/main.rs"
|
||||
name = "run-return"
|
||||
path = "../../examples/run-return/main.rs"
|
||||
|
||||
[[example]]
|
||||
name = "splashscreen"
|
||||
|
||||
@@ -1139,6 +1139,13 @@ impl<R: Runtime> App<R> {
|
||||
|
||||
/// Runs the application.
|
||||
///
|
||||
/// This function never returns. When the application finishes, the process is exited directly using [`std::process::exit`].
|
||||
/// See [`run_return`](Self::run_return) if you need to run code after the application event loop exits.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if the setup-function supplied in [`Builder::setup`] fails.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```,no_run
|
||||
/// let app = tauri::Builder::default()
|
||||
@@ -1179,6 +1186,60 @@ impl<R: Runtime> App<R> {
|
||||
});
|
||||
}
|
||||
|
||||
/// Runs the application, returning its intended exit code.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **iOS**: Unsupported. The application will fallback to [`run`](Self::run).
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if the setup-function supplied in [`Builder::setup`] fails.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```,no_run
|
||||
/// let 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");
|
||||
/// let exit_code = app
|
||||
/// .run_return(|_app_handle, event| match event {
|
||||
/// tauri::RunEvent::ExitRequested { api, .. } => {
|
||||
/// api.prevent_exit();
|
||||
/// }
|
||||
/// _ => {}
|
||||
/// });
|
||||
///
|
||||
/// std::process::exit(exit_code);
|
||||
/// ```
|
||||
pub fn run_return<F: FnMut(&AppHandle<R>, RunEvent) + 'static>(mut self, mut callback: F) -> i32 {
|
||||
let manager = self.manager.clone();
|
||||
let app_handle = self.handle().clone();
|
||||
|
||||
self
|
||||
.runtime
|
||||
.take()
|
||||
.unwrap()
|
||||
.run_return(move |event| match event {
|
||||
RuntimeRunEvent::Ready => {
|
||||
if let Err(e) = setup(&mut self) {
|
||||
panic!("Failed to setup app: {e}");
|
||||
}
|
||||
let event = on_event_loop_event(&app_handle, RuntimeRunEvent::Ready, &manager);
|
||||
callback(&app_handle, event);
|
||||
}
|
||||
RuntimeRunEvent::Exit => {
|
||||
let event = on_event_loop_event(&app_handle, RuntimeRunEvent::Exit, &manager);
|
||||
callback(&app_handle, event);
|
||||
app_handle.cleanup_before_exit();
|
||||
}
|
||||
_ => {
|
||||
let event = on_event_loop_event(&app_handle, event, &manager);
|
||||
callback(&app_handle, event);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Runs an iteration of the runtime event loop and immediately return.
|
||||
///
|
||||
/// Note that when using this API, app cleanup is not automatically done.
|
||||
@@ -1202,6 +1263,9 @@ impl<R: Runtime> App<R> {
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(desktop)]
|
||||
#[deprecated(
|
||||
note = "When called in a loop (as suggested by the name), this function will busy-loop. To re-gain control of control flow after the app has exited, use `App::run_return` instead."
|
||||
)]
|
||||
pub fn run_iteration<F: FnMut(&AppHandle<R>, RunEvent) + 'static>(&mut self, mut callback: F) {
|
||||
let manager = self.manager.clone();
|
||||
let app_handle = self.handle().clone();
|
||||
|
||||
@@ -1230,6 +1230,12 @@ impl<T: UserEvent> Runtime<T> for MockRuntime {
|
||||
))]
|
||||
fn run_iteration<F: FnMut(RunEvent<T>)>(&mut self, callback: F) {}
|
||||
|
||||
fn run_return<F: FnMut(RunEvent<T>) + 'static>(self, callback: F) -> i32 {
|
||||
self.run(callback);
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
fn run<F: FnMut(RunEvent<T>) + 'static>(self, mut callback: F) {
|
||||
self.is_running.store(true, Ordering::Relaxed);
|
||||
callback(RunEvent::Ready);
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# Run Iteration Example
|
||||
|
||||
To execute run the following on the root directory of the repository: `cargo run --example run-iteration`.
|
||||
3
examples/run-return/README.md
Normal file
3
examples/run-return/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Run Return Example
|
||||
|
||||
To execute run the following on the root directory of the repository: `cargo run --example run-return`.
|
||||
@@ -4,23 +4,18 @@
|
||||
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
use tauri::Manager;
|
||||
|
||||
fn main() {
|
||||
let mut app = tauri::Builder::default()
|
||||
let app = tauri::Builder::default()
|
||||
.build(tauri::generate_context!(
|
||||
"../../examples/run-iteration/tauri.conf.json"
|
||||
"../../examples/run-return/tauri.conf.json"
|
||||
))
|
||||
.expect("error while building tauri application");
|
||||
|
||||
loop {
|
||||
app.run_iteration(|_app, _event| {
|
||||
//println!("{:?}", _event);
|
||||
});
|
||||
let exit_code = app.run_return(|_app, _event| {
|
||||
//println!("{:?}", _event);
|
||||
});
|
||||
|
||||
if app.webview_windows().is_empty() {
|
||||
app.cleanup_before_exit();
|
||||
break;
|
||||
}
|
||||
}
|
||||
println!("I run after exit");
|
||||
|
||||
std::process::exit(exit_code);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "../../crates/tauri-schema-generator/schemas/config.schema.json",
|
||||
"productName": "RunIteration",
|
||||
"productName": "RunReturn",
|
||||
"version": "0.1.0",
|
||||
"identifier": "com.tauri.dev",
|
||||
"build": {
|
||||
Reference in New Issue
Block a user