From a25b1af45690bdd8e1cbb20ee3a5b1c4342de455 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 14 Jun 2024 01:47:39 +0200 Subject: Replace `Command` with a new `Task` API with chain support --- winit/src/application.rs | 470 +++++++++++++++++---------------------- winit/src/multi_window.rs | 551 +++++++++++++++++++++------------------------- winit/src/proxy.rs | 27 +-- winit/src/system.rs | 12 +- 4 files changed, 480 insertions(+), 580 deletions(-) (limited to 'winit/src') diff --git a/winit/src/application.rs b/winit/src/application.rs index d93ea42e..a08c2010 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -19,7 +19,7 @@ use crate::graphics::compositor::{self, Compositor}; use crate::runtime::clipboard; use crate::runtime::program::Program; use crate::runtime::user_interface::{self, UserInterface}; -use crate::runtime::{Command, Debug}; +use crate::runtime::{Action, Debug, Task}; use crate::{Clipboard, Error, Proxy, Settings}; use futures::channel::mpsc; @@ -36,7 +36,7 @@ use std::sync::Arc; /// its own window. /// /// An [`Application`] can execute asynchronous actions by returning a -/// [`Command`] in some of its methods. +/// [`Task`] in some of its methods. /// /// When using an [`Application`] with the `debug` feature enabled, a debug view /// can be toggled by pressing `F12`. @@ -52,10 +52,10 @@ where /// /// Here is where you should return the initial state of your app. /// - /// Additionally, you can return a [`Command`] if you need to perform some + /// Additionally, you can return a [`Task`] if you need to perform some /// async action in the background on startup. This is useful if you want to /// load state from a file, perform an initial HTTP request, etc. - fn new(flags: Self::Flags) -> (Self, Command); + fn new(flags: Self::Flags) -> (Self, Task); /// Returns the current title of the [`Application`]. /// @@ -155,19 +155,23 @@ where let (proxy, worker) = Proxy::new(event_loop.create_proxy()); - let runtime = { + let mut runtime = { let executor = E::new().map_err(Error::ExecutorCreationFailed)?; executor.spawn(worker); Runtime::new(executor, proxy.clone()) }; - let (application, init_command) = { + let (application, task) = { let flags = settings.flags; runtime.enter(|| A::new(flags)) }; + if let Some(stream) = task.into_stream() { + runtime.run(stream); + } + let id = settings.id; let title = application.title(); @@ -183,7 +187,6 @@ where boot_receiver, event_receiver, control_sender, - init_command, settings.fonts, )); @@ -193,7 +196,7 @@ where instance: std::pin::Pin>, context: task::Context<'static>, boot: Option>, - sender: mpsc::UnboundedSender>, + sender: mpsc::UnboundedSender>>, receiver: mpsc::UnboundedReceiver, error: Option, #[cfg(target_arch = "wasm32")] @@ -229,7 +232,7 @@ where queued_events: Vec::new(), }; - impl winit::application::ApplicationHandler + impl winit::application::ApplicationHandler> for Runner where F: Future, @@ -393,11 +396,11 @@ where fn user_event( &mut self, event_loop: &winit::event_loop::ActiveEventLoop, - message: Message, + action: Action, ) { self.process_event( event_loop, - winit::event::Event::UserEvent(message), + winit::event::Event::UserEvent(action), ); } @@ -416,7 +419,7 @@ where fn process_event( &mut self, event_loop: &winit::event_loop::ActiveEventLoop, - event: winit::event::Event, + event: winit::event::Event>, ) { // On Wasm, events may start being processed before the compositor // boots up. We simply queue them and process them once ready. @@ -480,15 +483,14 @@ struct Boot { async fn run_instance( mut application: A, - mut runtime: Runtime, A::Message>, + mut runtime: Runtime, Action>, mut proxy: Proxy, mut debug: Debug, mut boot: oneshot::Receiver>, mut event_receiver: mpsc::UnboundedReceiver< - winit::event::Event, + winit::event::Event>, >, mut control_sender: mpsc::UnboundedSender, - init_command: Command, fonts: Vec>, ) where A: Application + 'static, @@ -518,7 +520,7 @@ async fn run_instance( let physical_size = state.physical_size(); let mut clipboard = Clipboard::connect(&window); - let mut cache = user_interface::Cache::default(); + let cache = user_interface::Cache::default(); let mut surface = compositor.create_surface( window.clone(), physical_size.width, @@ -530,22 +532,12 @@ async fn run_instance( window.set_visible(true); } - run_command( - &application, - &mut compositor, - &mut surface, - &mut cache, - &state, - &mut renderer, - init_command, - &mut runtime, - &mut clipboard, - &mut should_exit, - &mut proxy, - &mut debug, - &window, + runtime.track( + application + .subscription() + .map(Action::Output) + .into_recipes(), ); - runtime.track(application.subscription().into_recipes()); let mut user_interface = ManuallyDrop::new(build_user_interface( &application, @@ -581,8 +573,21 @@ async fn run_instance( ), )); } - event::Event::UserEvent(message) => { - messages.push(message); + event::Event::UserEvent(action) => { + run_action( + action, + &mut user_interface, + &mut compositor, + &mut surface, + &state, + &mut renderer, + &mut messages, + &mut clipboard, + &mut should_exit, + &mut debug, + &window, + ); + user_events += 1; } event::Event::WindowEvent { @@ -756,21 +761,14 @@ async fn run_instance( user_interface::State::Outdated ) { - let mut cache = + let cache = ManuallyDrop::into_inner(user_interface).into_cache(); // Update application update( &mut application, - &mut compositor, - &mut surface, - &mut cache, &mut state, - &mut renderer, &mut runtime, - &mut clipboard, - &mut should_exit, - &mut proxy, &mut debug, &mut messages, &window, @@ -855,282 +853,230 @@ where } /// Updates an [`Application`] by feeding it the provided messages, spawning any -/// resulting [`Command`], and tracking its [`Subscription`]. -pub fn update( +/// resulting [`Task`], and tracking its [`Subscription`]. +pub fn update( application: &mut A, - compositor: &mut C, - surface: &mut C::Surface, - cache: &mut user_interface::Cache, state: &mut State, - renderer: &mut A::Renderer, - runtime: &mut Runtime, A::Message>, - clipboard: &mut Clipboard, - should_exit: &mut bool, - proxy: &mut Proxy, + runtime: &mut Runtime, Action>, debug: &mut Debug, messages: &mut Vec, window: &winit::window::Window, ) where - C: Compositor + 'static, A::Theme: DefaultStyle, { for message in messages.drain(..) { debug.log_message(&message); debug.update_started(); - let command = runtime.enter(|| application.update(message)); + let task = runtime.enter(|| application.update(message)); debug.update_finished(); - run_command( - application, - compositor, - surface, - cache, - state, - renderer, - command, - runtime, - clipboard, - should_exit, - proxy, - debug, - window, - ); + if let Some(stream) = task.into_stream() { + runtime.run(stream); + } } state.synchronize(application, window); let subscription = application.subscription(); - runtime.track(subscription.into_recipes()); + runtime.track(subscription.map(Action::Output).into_recipes()); } -/// Runs the actions of a [`Command`]. -pub fn run_command( - application: &A, +/// Runs the actions of a [`Task`]. +pub fn run_action( + action: Action, + user_interface: &mut UserInterface<'_, A::Message, A::Theme, C::Renderer>, compositor: &mut C, surface: &mut C::Surface, - cache: &mut user_interface::Cache, state: &State, renderer: &mut A::Renderer, - command: Command, - runtime: &mut Runtime, A::Message>, + messages: &mut Vec, clipboard: &mut Clipboard, should_exit: &mut bool, - proxy: &mut Proxy, debug: &mut Debug, window: &winit::window::Window, ) where A: Application, - E: Executor, C: Compositor + 'static, A::Theme: DefaultStyle, { - use crate::runtime::command; use crate::runtime::system; use crate::runtime::window; - for action in command.actions() { - match action { - command::Action::Future(future) => { - runtime.spawn(future); + match action { + Action::Clipboard(action) => match action { + clipboard::Action::Read { target, channel } => { + let _ = channel.send(clipboard.read(target)); } - command::Action::Stream(stream) => { - runtime.run(stream); + clipboard::Action::Write { target, contents } => { + clipboard.write(target, contents); } - command::Action::Clipboard(action) => match action { - clipboard::Action::Read(tag, kind) => { - let message = tag(clipboard.read(kind)); - - proxy.send(message); - } - clipboard::Action::Write(contents, kind) => { - clipboard.write(kind, contents); - } - }, - command::Action::Window(action) => match action { - window::Action::Close(_id) => { - *should_exit = true; - } - window::Action::Drag(_id) => { - let _res = window.drag_window(); - } - window::Action::Spawn { .. } => { - log::warn!( - "Spawning a window is only available with \ + }, + Action::Window(action) => match action { + window::Action::Close(_id) => { + *should_exit = true; + } + window::Action::Drag(_id) => { + let _res = window.drag_window(); + } + window::Action::Open { .. } => { + log::warn!( + "Spawning a window is only available with \ multi-window applications." - ); - } - window::Action::Resize(_id, size) => { - let _ = - window.request_inner_size(winit::dpi::LogicalSize { - width: size.width, - height: size.height, - }); - } - window::Action::FetchSize(_id, callback) => { - let size = - window.inner_size().to_logical(window.scale_factor()); + ); + } + window::Action::Resize(_id, size) => { + let _ = window.request_inner_size(winit::dpi::LogicalSize { + width: size.width, + height: size.height, + }); + } + window::Action::FetchSize(_id, channel) => { + let size = + window.inner_size().to_logical(window.scale_factor()); - proxy.send(callback(Size::new(size.width, size.height))); - } - window::Action::FetchMaximized(_id, callback) => { - proxy.send(callback(window.is_maximized())); - } - window::Action::Maximize(_id, maximized) => { - window.set_maximized(maximized); - } - window::Action::FetchMinimized(_id, callback) => { - proxy.send(callback(window.is_minimized())); - } - window::Action::Minimize(_id, minimized) => { - window.set_minimized(minimized); - } - window::Action::FetchPosition(_id, callback) => { - let position = window - .inner_position() - .map(|position| { - let position = position - .to_logical::(window.scale_factor()); - - Point::new(position.x, position.y) - }) - .ok(); + let _ = channel.send(Size::new(size.width, size.height)); + } + window::Action::FetchMaximized(_id, channel) => { + let _ = channel.send(window.is_maximized()); + } + window::Action::Maximize(_id, maximized) => { + window.set_maximized(maximized); + } + window::Action::FetchMinimized(_id, channel) => { + let _ = channel.send(window.is_minimized()); + } + window::Action::Minimize(_id, minimized) => { + window.set_minimized(minimized); + } + window::Action::FetchPosition(_id, channel) => { + let position = window + .inner_position() + .map(|position| { + let position = + position.to_logical::(window.scale_factor()); + + Point::new(position.x, position.y) + }) + .ok(); + + let _ = channel.send(position); + } + window::Action::Move(_id, position) => { + window.set_outer_position(winit::dpi::LogicalPosition { + x: position.x, + y: position.y, + }); + } + window::Action::ChangeMode(_id, mode) => { + window.set_visible(conversion::visible(mode)); + window.set_fullscreen(conversion::fullscreen( + window.current_monitor(), + mode, + )); + } + window::Action::ChangeIcon(_id, icon) => { + window.set_window_icon(conversion::icon(icon)); + } + window::Action::FetchMode(_id, channel) => { + let mode = if window.is_visible().unwrap_or(true) { + conversion::mode(window.fullscreen()) + } else { + core::window::Mode::Hidden + }; - proxy.send(callback(position)); - } - window::Action::Move(_id, position) => { - window.set_outer_position(winit::dpi::LogicalPosition { - x: position.x, - y: position.y, + let _ = channel.send(mode); + } + window::Action::ToggleMaximize(_id) => { + window.set_maximized(!window.is_maximized()); + } + window::Action::ToggleDecorations(_id) => { + window.set_decorations(!window.is_decorated()); + } + window::Action::RequestUserAttention(_id, user_attention) => { + window.request_user_attention( + user_attention.map(conversion::user_attention), + ); + } + window::Action::GainFocus(_id) => { + window.focus_window(); + } + window::Action::ChangeLevel(_id, level) => { + window.set_window_level(conversion::window_level(level)); + } + window::Action::ShowSystemMenu(_id) => { + if let mouse::Cursor::Available(point) = state.cursor() { + window.show_window_menu(winit::dpi::LogicalPosition { + x: point.x, + y: point.y, }); } - window::Action::ChangeMode(_id, mode) => { - window.set_visible(conversion::visible(mode)); - window.set_fullscreen(conversion::fullscreen( - window.current_monitor(), - mode, - )); - } - window::Action::ChangeIcon(_id, icon) => { - window.set_window_icon(conversion::icon(icon)); - } - window::Action::FetchMode(_id, tag) => { - let mode = if window.is_visible().unwrap_or(true) { - conversion::mode(window.fullscreen()) - } else { - core::window::Mode::Hidden - }; - - proxy.send(tag(mode)); - } - window::Action::ToggleMaximize(_id) => { - window.set_maximized(!window.is_maximized()); - } - window::Action::ToggleDecorations(_id) => { - window.set_decorations(!window.is_decorated()); - } - window::Action::RequestUserAttention(_id, user_attention) => { - window.request_user_attention( - user_attention.map(conversion::user_attention), - ); - } - window::Action::GainFocus(_id) => { - window.focus_window(); - } - window::Action::ChangeLevel(_id, level) => { - window.set_window_level(conversion::window_level(level)); - } - window::Action::ShowSystemMenu(_id) => { - if let mouse::Cursor::Available(point) = state.cursor() { - window.show_window_menu(winit::dpi::LogicalPosition { - x: point.x, - y: point.y, - }); - } - } - window::Action::FetchId(_id, tag) => { - proxy.send(tag(window.id().into())); - } - window::Action::RunWithHandle(_id, tag) => { - use window::raw_window_handle::HasWindowHandle; + } + window::Action::FetchRawId(_id, channel) => { + let _ = channel.send(window.id().into()); + } + window::Action::RunWithHandle(_id, f) => { + use window::raw_window_handle::HasWindowHandle; - if let Ok(handle) = window.window_handle() { - proxy.send(tag(handle)); - } + if let Ok(handle) = window.window_handle() { + f(handle); } + } - window::Action::Screenshot(_id, tag) => { - let bytes = compositor.screenshot( - renderer, - surface, - state.viewport(), - state.background_color(), - &debug.overlay(), - ); - - proxy.send(tag(window::Screenshot::new( - bytes, - state.physical_size(), - state.viewport().scale_factor(), - ))); - } - }, - command::Action::System(action) => match action { - system::Action::QueryInformation(_tag) => { - #[cfg(feature = "system")] - { - let graphics_info = compositor.fetch_information(); - let mut proxy = proxy.clone(); - - let _ = std::thread::spawn(move || { - let information = - crate::system::information(graphics_info); - - let message = _tag(information); - - proxy.send(message); - }); - } - } - }, - command::Action::Widget(action) => { - let mut current_cache = std::mem::take(cache); - let mut current_operation = Some(action); - - let mut user_interface = build_user_interface( - application, - current_cache, + window::Action::Screenshot(_id, channel) => { + let bytes = compositor.screenshot( renderer, - state.logical_size(), - debug, + surface, + state.viewport(), + state.background_color(), + &debug.overlay(), ); - while let Some(mut operation) = current_operation.take() { - user_interface.operate(renderer, operation.as_mut()); + let _ = channel.send(window::Screenshot::new( + bytes, + state.physical_size(), + state.viewport().scale_factor(), + )); + } + }, + Action::System(action) => match action { + system::Action::QueryInformation(_channel) => { + #[cfg(feature = "system")] + { + let graphics_info = compositor.fetch_information(); - match operation.finish() { - operation::Outcome::None => {} - operation::Outcome::Some(message) => { - proxy.send(message); - } - operation::Outcome::Chain(next) => { - current_operation = Some(next); - } + let _ = std::thread::spawn(move || { + let information = + crate::system::information(graphics_info); + + let _ = _channel.send(information); + }); + } + } + }, + Action::Widget(operation) => { + let mut current_operation = Some(operation); + + while let Some(mut operation) = current_operation.take() { + user_interface.operate(renderer, operation.as_mut()); + + match operation.finish() { + operation::Outcome::None => {} + operation::Outcome::Some(()) => {} + operation::Outcome::Chain(next) => { + current_operation = Some(next); } } - - current_cache = user_interface.into_cache(); - *cache = current_cache; } - command::Action::LoadFont { bytes, tagger } => { - // TODO: Error handling (?) - compositor.load_font(bytes); + } + Action::LoadFont { bytes, channel } => { + // TODO: Error handling (?) + compositor.load_font(bytes); - proxy.send(tagger(Ok(()))); - } - command::Action::Custom(_) => { - log::warn!("Unsupported custom action in `iced_winit` shell"); - } + let _ = channel.send(Ok(())); + } + Action::Output(message) => { + messages.push(message); } } } diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 2eaf9241..d56b47eb 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -21,10 +21,10 @@ use crate::futures::{Executor, Runtime}; use crate::graphics; use crate::graphics::{compositor, Compositor}; use crate::multi_window::window_manager::WindowManager; -use crate::runtime::command::{self, Command}; use crate::runtime::multi_window::Program; use crate::runtime::user_interface::{self, UserInterface}; use crate::runtime::Debug; +use crate::runtime::{Action, Task}; use crate::{Clipboard, Error, Proxy, Settings}; pub use crate::application::{default, Appearance, DefaultStyle}; @@ -41,7 +41,7 @@ use std::time::Instant; /// its own window. /// /// An [`Application`] can execute asynchronous actions by returning a -/// [`Command`] in some of its methods. +/// [`Task`] in some of its methods. /// /// When using an [`Application`] with the `debug` feature enabled, a debug view /// can be toggled by pressing `F12`. @@ -57,10 +57,10 @@ where /// /// Here is where you should return the initial state of your app. /// - /// Additionally, you can return a [`Command`] if you need to perform some + /// Additionally, you can return a [`Task`] if you need to perform some /// async action in the background on startup. This is useful if you want to /// load state from a file, perform an initial HTTP request, etc. - fn new(flags: Self::Flags) -> (Self, Command); + fn new(flags: Self::Flags) -> (Self, Task); /// Returns the current title of the [`Application`]. /// @@ -127,19 +127,23 @@ where let (proxy, worker) = Proxy::new(event_loop.create_proxy()); - let runtime = { + let mut runtime = { let executor = E::new().map_err(Error::ExecutorCreationFailed)?; executor.spawn(worker); Runtime::new(executor, proxy.clone()) }; - let (application, init_command) = { + let (application, task) = { let flags = settings.flags; runtime.enter(|| A::new(flags)) }; + if let Some(stream) = task.into_stream() { + runtime.run(stream); + } + let id = settings.id; let title = application.title(window::Id::MAIN); @@ -155,7 +159,6 @@ where boot_receiver, event_receiver, control_sender, - init_command, )); let context = task::Context::from_waker(task::noop_waker_ref()); @@ -448,13 +451,12 @@ enum Control { async fn run_instance( mut application: A, - mut runtime: Runtime, A::Message>, + mut runtime: Runtime, Action>, mut proxy: Proxy, mut debug: Debug, mut boot: oneshot::Receiver>, - mut event_receiver: mpsc::UnboundedReceiver>, + mut event_receiver: mpsc::UnboundedReceiver>>, mut control_sender: mpsc::UnboundedSender, - init_command: Command, ) where A: Application + 'static, E: Executor + 'static, @@ -511,21 +513,13 @@ async fn run_instance( )]), )); - run_command( - &application, - &mut compositor, - init_command, - &mut runtime, - &mut clipboard, - &mut control_sender, - &mut proxy, - &mut debug, - &mut window_manager, - &mut ui_caches, + runtime.track( + application + .subscription() + .map(Action::Output) + .into_recipes(), ); - runtime.track(application.subscription().into_recipes()); - let mut messages = Vec::new(); let mut user_events = 0; @@ -594,8 +588,19 @@ async fn run_instance( ), ); } - event::Event::UserEvent(message) => { - messages.push(message); + event::Event::UserEvent(action) => { + run_action( + action, + &application, + &mut compositor, + &mut messages, + &mut clipboard, + &mut control_sender, + &mut debug, + &mut user_interfaces, + &mut window_manager, + &mut ui_caches, + ); user_events += 1; } event::Event::WindowEvent { @@ -888,7 +893,7 @@ async fn run_instance( // TODO mw application update returns which window IDs to update if !messages.is_empty() || uis_stale { - let mut cached_interfaces: FxHashMap< + let cached_interfaces: FxHashMap< window::Id, user_interface::Cache, > = ManuallyDrop::into_inner(user_interfaces) @@ -899,15 +904,9 @@ async fn run_instance( // Update application update( &mut application, - &mut compositor, &mut runtime, - &mut clipboard, - &mut control_sender, - &mut proxy, &mut debug, &mut messages, - &mut window_manager, - &mut cached_interfaces, ); // we must synchronize all window states with application state after an @@ -971,63 +970,46 @@ where user_interface } -/// Updates a multi-window [`Application`] by feeding it messages, spawning any -/// resulting [`Command`], and tracking its [`Subscription`]. -fn update( +fn update( application: &mut A, - compositor: &mut C, - runtime: &mut Runtime, A::Message>, - clipboard: &mut Clipboard, - control_sender: &mut mpsc::UnboundedSender, - proxy: &mut Proxy, + runtime: &mut Runtime, Action>, debug: &mut Debug, messages: &mut Vec, - window_manager: &mut WindowManager, - ui_caches: &mut FxHashMap, ) where - C: Compositor + 'static, A::Theme: DefaultStyle, { for message in messages.drain(..) { debug.log_message(&message); debug.update_started(); - let command = runtime.enter(|| application.update(message)); + let task = runtime.enter(|| application.update(message)); debug.update_finished(); - run_command( - application, - compositor, - command, - runtime, - clipboard, - control_sender, - proxy, - debug, - window_manager, - ui_caches, - ); + if let Some(stream) = task.into_stream() { + runtime.run(stream); + } } let subscription = application.subscription(); - runtime.track(subscription.into_recipes()); + runtime.track(subscription.map(Action::Output).into_recipes()); } -/// Runs the actions of a [`Command`]. -fn run_command( +fn run_action( + action: Action, application: &A, compositor: &mut C, - command: Command, - runtime: &mut Runtime, A::Message>, + messages: &mut Vec, clipboard: &mut Clipboard, control_sender: &mut mpsc::UnboundedSender, - proxy: &mut Proxy, debug: &mut Debug, + interfaces: &mut FxHashMap< + window::Id, + UserInterface<'_, A::Message, A::Theme, A::Renderer>, + >, window_manager: &mut WindowManager, ui_caches: &mut FxHashMap, ) where A: Application, - E: Executor, C: Compositor + 'static, A::Theme: DefaultStyle, { @@ -1035,279 +1017,252 @@ fn run_command( use crate::runtime::system; use crate::runtime::window; - for action in command.actions() { - match action { - command::Action::Future(future) => { - runtime.spawn(Box::pin(future)); + match action { + Action::Output(message) => { + messages.push(message); + } + Action::Clipboard(action) => match action { + clipboard::Action::Read { target, channel } => { + let _ = channel.send(clipboard.read(target)); } - command::Action::Stream(stream) => { - runtime.run(Box::pin(stream)); + clipboard::Action::Write { target, contents } => { + clipboard.write(target, contents); } - command::Action::Clipboard(action) => match action { - clipboard::Action::Read(tag, kind) => { - let message = tag(clipboard.read(kind)); + }, + Action::Window(action) => match action { + window::Action::Open(id, settings) => { + let monitor = window_manager.last_monitor(); - proxy.send(message); - } - clipboard::Action::Write(contents, kind) => { - clipboard.write(kind, contents); - } - }, - command::Action::Window(action) => match action { - window::Action::Spawn(id, settings) => { - let monitor = window_manager.last_monitor(); + control_sender + .start_send(Control::CreateWindow { + id, + settings, + title: application.title(id), + monitor, + }) + .expect("Send control action"); + } + window::Action::Close(id) => { + let _ = window_manager.remove(id); + let _ = ui_caches.remove(&id); + if window_manager.is_empty() { control_sender - .start_send(Control::CreateWindow { - id, - settings, - title: application.title(id), - monitor, - }) + .start_send(Control::Exit) .expect("Send control action"); } - window::Action::Close(id) => { - let _ = window_manager.remove(id); - let _ = ui_caches.remove(&id); - - if window_manager.is_empty() { - control_sender - .start_send(Control::Exit) - .expect("Send control action"); - } - } - window::Action::Drag(id) => { - if let Some(window) = window_manager.get_mut(id) { - let _ = window.raw.drag_window(); - } + } + window::Action::Drag(id) => { + if let Some(window) = window_manager.get_mut(id) { + let _ = window.raw.drag_window(); } - window::Action::Resize(id, size) => { - if let Some(window) = window_manager.get_mut(id) { - let _ = window.raw.request_inner_size( - winit::dpi::LogicalSize { - width: size.width, - height: size.height, - }, - ); - } + } + window::Action::Resize(id, size) => { + if let Some(window) = window_manager.get_mut(id) { + let _ = window.raw.request_inner_size( + winit::dpi::LogicalSize { + width: size.width, + height: size.height, + }, + ); } - window::Action::FetchSize(id, callback) => { - if let Some(window) = window_manager.get_mut(id) { - let size = window - .raw - .inner_size() - .to_logical(window.raw.scale_factor()); - - proxy - .send(callback(Size::new(size.width, size.height))); - } + } + window::Action::FetchSize(id, channel) => { + if let Some(window) = window_manager.get_mut(id) { + let size = window + .raw + .inner_size() + .to_logical(window.raw.scale_factor()); + + let _ = channel.send(Size::new(size.width, size.height)); } - window::Action::FetchMaximized(id, callback) => { - if let Some(window) = window_manager.get_mut(id) { - proxy.send(callback(window.raw.is_maximized())); - } + } + window::Action::FetchMaximized(id, channel) => { + if let Some(window) = window_manager.get_mut(id) { + let _ = channel.send(window.raw.is_maximized()); } - window::Action::Maximize(id, maximized) => { - if let Some(window) = window_manager.get_mut(id) { - window.raw.set_maximized(maximized); - } + } + window::Action::Maximize(id, maximized) => { + if let Some(window) = window_manager.get_mut(id) { + window.raw.set_maximized(maximized); } - window::Action::FetchMinimized(id, callback) => { - if let Some(window) = window_manager.get_mut(id) { - proxy.send(callback(window.raw.is_minimized())); - } + } + window::Action::FetchMinimized(id, channel) => { + if let Some(window) = window_manager.get_mut(id) { + let _ = channel.send(window.raw.is_minimized()); } - window::Action::Minimize(id, minimized) => { - if let Some(window) = window_manager.get_mut(id) { - window.raw.set_minimized(minimized); - } + } + window::Action::Minimize(id, minimized) => { + if let Some(window) = window_manager.get_mut(id) { + window.raw.set_minimized(minimized); } - window::Action::FetchPosition(id, callback) => { - if let Some(window) = window_manager.get_mut(id) { - let position = window - .raw - .inner_position() - .map(|position| { - let position = position.to_logical::( - window.raw.scale_factor(), - ); - - Point::new(position.x, position.y) - }) - .ok(); + } + window::Action::FetchPosition(id, channel) => { + if let Some(window) = window_manager.get_mut(id) { + let position = window + .raw + .inner_position() + .map(|position| { + let position = position + .to_logical::(window.raw.scale_factor()); + + Point::new(position.x, position.y) + }) + .ok(); - proxy.send(callback(position)); - } + let _ = channel.send(position); } - window::Action::Move(id, position) => { - if let Some(window) = window_manager.get_mut(id) { - window.raw.set_outer_position( - winit::dpi::LogicalPosition { - x: position.x, - y: position.y, - }, - ); - } + } + window::Action::Move(id, position) => { + if let Some(window) = window_manager.get_mut(id) { + window.raw.set_outer_position( + winit::dpi::LogicalPosition { + x: position.x, + y: position.y, + }, + ); } - window::Action::ChangeMode(id, mode) => { - if let Some(window) = window_manager.get_mut(id) { - window.raw.set_visible(conversion::visible(mode)); - window.raw.set_fullscreen(conversion::fullscreen( - window.raw.current_monitor(), - mode, - )); - } + } + window::Action::ChangeMode(id, mode) => { + if let Some(window) = window_manager.get_mut(id) { + window.raw.set_visible(conversion::visible(mode)); + window.raw.set_fullscreen(conversion::fullscreen( + window.raw.current_monitor(), + mode, + )); } - window::Action::ChangeIcon(id, icon) => { - if let Some(window) = window_manager.get_mut(id) { - window.raw.set_window_icon(conversion::icon(icon)); - } + } + window::Action::ChangeIcon(id, icon) => { + if let Some(window) = window_manager.get_mut(id) { + window.raw.set_window_icon(conversion::icon(icon)); } - window::Action::FetchMode(id, tag) => { - if let Some(window) = window_manager.get_mut(id) { - let mode = if window.raw.is_visible().unwrap_or(true) { - conversion::mode(window.raw.fullscreen()) - } else { - core::window::Mode::Hidden - }; - - proxy.send(tag(mode)); - } + } + window::Action::FetchMode(id, channel) => { + if let Some(window) = window_manager.get_mut(id) { + let mode = if window.raw.is_visible().unwrap_or(true) { + conversion::mode(window.raw.fullscreen()) + } else { + core::window::Mode::Hidden + }; + + let _ = channel.send(mode); } - window::Action::ToggleMaximize(id) => { - if let Some(window) = window_manager.get_mut(id) { - window.raw.set_maximized(!window.raw.is_maximized()); - } + } + window::Action::ToggleMaximize(id) => { + if let Some(window) = window_manager.get_mut(id) { + window.raw.set_maximized(!window.raw.is_maximized()); } - window::Action::ToggleDecorations(id) => { - if let Some(window) = window_manager.get_mut(id) { - window.raw.set_decorations(!window.raw.is_decorated()); - } + } + window::Action::ToggleDecorations(id) => { + if let Some(window) = window_manager.get_mut(id) { + window.raw.set_decorations(!window.raw.is_decorated()); } - window::Action::RequestUserAttention(id, attention_type) => { - if let Some(window) = window_manager.get_mut(id) { - window.raw.request_user_attention( - attention_type.map(conversion::user_attention), - ); - } + } + window::Action::RequestUserAttention(id, attention_type) => { + if let Some(window) = window_manager.get_mut(id) { + window.raw.request_user_attention( + attention_type.map(conversion::user_attention), + ); } - window::Action::GainFocus(id) => { - if let Some(window) = window_manager.get_mut(id) { - window.raw.focus_window(); - } + } + window::Action::GainFocus(id) => { + if let Some(window) = window_manager.get_mut(id) { + window.raw.focus_window(); } - window::Action::ChangeLevel(id, level) => { - if let Some(window) = window_manager.get_mut(id) { - window - .raw - .set_window_level(conversion::window_level(level)); - } + } + window::Action::ChangeLevel(id, level) => { + if let Some(window) = window_manager.get_mut(id) { + window + .raw + .set_window_level(conversion::window_level(level)); } - window::Action::ShowSystemMenu(id) => { - if let Some(window) = window_manager.get_mut(id) { - if let mouse::Cursor::Available(point) = - window.state.cursor() - { - window.raw.show_window_menu( - winit::dpi::LogicalPosition { - x: point.x, - y: point.y, - }, - ); - } + } + window::Action::ShowSystemMenu(id) => { + if let Some(window) = window_manager.get_mut(id) { + if let mouse::Cursor::Available(point) = + window.state.cursor() + { + window.raw.show_window_menu( + winit::dpi::LogicalPosition { + x: point.x, + y: point.y, + }, + ); } } - window::Action::FetchId(id, tag) => { - if let Some(window) = window_manager.get_mut(id) { - proxy.send(tag(window.raw.id().into())); - } + } + window::Action::FetchRawId(id, channel) => { + if let Some(window) = window_manager.get_mut(id) { + let _ = channel.send(window.raw.id().into()); } - window::Action::RunWithHandle(id, tag) => { - use window::raw_window_handle::HasWindowHandle; - - if let Some(handle) = window_manager - .get_mut(id) - .and_then(|window| window.raw.window_handle().ok()) - { - proxy.send(tag(handle)); - } + } + window::Action::RunWithHandle(id, f) => { + use window::raw_window_handle::HasWindowHandle; + + if let Some(handle) = window_manager + .get_mut(id) + .and_then(|window| window.raw.window_handle().ok()) + { + f(handle); } - window::Action::Screenshot(id, tag) => { - if let Some(window) = window_manager.get_mut(id) { - let bytes = compositor.screenshot( - &mut window.renderer, - &mut window.surface, - window.state.viewport(), - window.state.background_color(), - &debug.overlay(), - ); + } + window::Action::Screenshot(id, channel) => { + if let Some(window) = window_manager.get_mut(id) { + let bytes = compositor.screenshot( + &mut window.renderer, + &mut window.surface, + window.state.viewport(), + window.state.background_color(), + &debug.overlay(), + ); - proxy.send(tag(window::Screenshot::new( - bytes, - window.state.physical_size(), - window.state.viewport().scale_factor(), - ))); - } + let _ = channel.send(window::Screenshot::new( + bytes, + window.state.physical_size(), + window.state.viewport().scale_factor(), + )); } - }, - command::Action::System(action) => match action { - system::Action::QueryInformation(_tag) => { - #[cfg(feature = "system")] - { - let graphics_info = compositor.fetch_information(); - let mut proxy = proxy.clone(); - - let _ = std::thread::spawn(move || { - let information = - crate::system::information(graphics_info); - - let message = _tag(information); - - proxy.send(message); - }); - } + } + }, + Action::System(action) => match action { + system::Action::QueryInformation(_channel) => { + #[cfg(feature = "system")] + { + let graphics_info = compositor.fetch_information(); + + let _ = std::thread::spawn(move || { + let information = + crate::system::information(graphics_info); + + let _ = _channel.send(information); + }); } - }, - command::Action::Widget(action) => { - let mut current_operation = Some(action); - - let mut uis = build_user_interfaces( - application, - debug, - window_manager, - std::mem::take(ui_caches), - ); - - while let Some(mut operation) = current_operation.take() { - for (id, ui) in uis.iter_mut() { - if let Some(window) = window_manager.get_mut(*id) { - ui.operate(&window.renderer, operation.as_mut()); - } + } + }, + Action::Widget(operation) => { + let mut current_operation = Some(operation); + + while let Some(mut operation) = current_operation.take() { + for (id, ui) in interfaces.iter_mut() { + if let Some(window) = window_manager.get_mut(*id) { + ui.operate(&window.renderer, operation.as_mut()); } + } - match operation.finish() { - operation::Outcome::None => {} - operation::Outcome::Some(message) => { - proxy.send(message); - } - operation::Outcome::Chain(next) => { - current_operation = Some(next); - } + match operation.finish() { + operation::Outcome::None => {} + operation::Outcome::Some(()) => {} + operation::Outcome::Chain(next) => { + current_operation = Some(next); } } - - *ui_caches = - uis.drain().map(|(id, ui)| (id, ui.into_cache())).collect(); } - command::Action::LoadFont { bytes, tagger } => { - // TODO: Error handling (?) - compositor.load_font(bytes.clone()); + } + Action::LoadFont { bytes, channel } => { + // TODO: Error handling (?) + compositor.load_font(bytes.clone()); - proxy.send(tagger(Ok(()))); - } - command::Action::Custom(_) => { - log::warn!("Unsupported custom action in `iced_winit` shell"); - } + let _ = channel.send(Ok(())); } } } diff --git a/winit/src/proxy.rs b/winit/src/proxy.rs index 3edc30ad..0ab61375 100644 --- a/winit/src/proxy.rs +++ b/winit/src/proxy.rs @@ -4,17 +4,18 @@ use crate::futures::futures::{ task::{Context, Poll}, Future, Sink, StreamExt, }; +use crate::runtime::Action; use std::pin::Pin; /// An event loop proxy with backpressure that implements `Sink`. #[derive(Debug)] -pub struct Proxy { - raw: winit::event_loop::EventLoopProxy, - sender: mpsc::Sender, +pub struct Proxy { + raw: winit::event_loop::EventLoopProxy>, + sender: mpsc::Sender>, notifier: mpsc::Sender, } -impl Clone for Proxy { +impl Clone for Proxy { fn clone(&self) -> Self { Self { raw: self.raw.clone(), @@ -24,12 +25,12 @@ impl Clone for Proxy { } } -impl Proxy { +impl Proxy { const MAX_SIZE: usize = 100; /// Creates a new [`Proxy`] from an `EventLoopProxy`. pub fn new( - raw: winit::event_loop::EventLoopProxy, + raw: winit::event_loop::EventLoopProxy>, ) -> (Self, impl Future) { let (notifier, mut processed) = mpsc::channel(Self::MAX_SIZE); let (sender, mut receiver) = mpsc::channel(Self::MAX_SIZE); @@ -72,16 +73,16 @@ impl Proxy { ) } - /// Sends a `Message` to the event loop. + /// Sends a value to the event loop. /// /// Note: This skips the backpressure mechanism with an unbounded /// channel. Use sparingly! - pub fn send(&mut self, message: Message) + pub fn send(&mut self, value: T) where - Message: std::fmt::Debug, + T: std::fmt::Debug, { self.raw - .send_event(message) + .send_event(Action::Output(value)) .expect("Send message to event loop"); } @@ -92,7 +93,7 @@ impl Proxy { } } -impl Sink for Proxy { +impl Sink> for Proxy { type Error = mpsc::SendError; fn poll_ready( @@ -104,9 +105,9 @@ impl Sink for Proxy { fn start_send( mut self: Pin<&mut Self>, - message: Message, + action: Action, ) -> Result<(), Self::Error> { - self.sender.start_send(message) + self.sender.start_send(action) } fn poll_flush( diff --git a/winit/src/system.rs b/winit/src/system.rs index c5a5b219..7997f311 100644 --- a/winit/src/system.rs +++ b/winit/src/system.rs @@ -1,15 +1,13 @@ //! Access the native system. use crate::graphics::compositor; -use crate::runtime::command::{self, Command}; use crate::runtime::system::{Action, Information}; +use crate::runtime::{self, Task}; /// Query for available system information. -pub fn fetch_information( - f: impl Fn(Information) -> Message + Send + 'static, -) -> Command { - Command::single(command::Action::System(Action::QueryInformation( - Box::new(f), - ))) +pub fn fetch_information() -> Task { + Task::oneshot(|channel| { + runtime::Action::System(Action::QueryInformation(channel)) + }) } pub(crate) fn information( -- cgit