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>
This commit is contained in:
Lucas Fernandes Nogueira 2024-01-29 10:52:44 -03:00 committed by GitHub
parent 9cb9aa7978
commit ec9818accb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 158 additions and 75 deletions

View File

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

View File

@ -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>>>,
) {

View File

@ -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);

View File

@ -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"

View File

@ -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)]

View File

@ -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::{

View File

@ -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()
}

View File

@ -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()
}

View File

@ -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);

View File

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

View File

@ -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>

View File

@ -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;
}
}
}

View File

@ -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"
}
}
}