diff options
Diffstat (limited to 'winit/src')
| -rw-r--r-- | winit/src/application.rs | 472 | ||||
| -rw-r--r-- | winit/src/conversion.rs | 6 | ||||
| -rw-r--r-- | winit/src/multi_window.rs | 553 | ||||
| -rw-r--r-- | winit/src/proxy.rs | 27 | ||||
| -rw-r--r-- | winit/src/system.rs | 12 | 
5 files changed, 484 insertions, 586 deletions
| diff --git a/winit/src/application.rs b/winit/src/application.rs index d93ea42e..a93878ea 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<Self::Message>); +    fn new(flags: Self::Flags) -> (Self, Task<Self::Message>);      /// 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,13 +196,13 @@ where          instance: std::pin::Pin<Box<F>>,          context: task::Context<'static>,          boot: Option<BootConfig<C>>, -        sender: mpsc::UnboundedSender<winit::event::Event<Message>>, +        sender: mpsc::UnboundedSender<winit::event::Event<Action<Message>>>,          receiver: mpsc::UnboundedReceiver<winit::event_loop::ControlFlow>,          error: Option<Error>,          #[cfg(target_arch = "wasm32")]          is_booted: std::rc::Rc<std::cell::RefCell<bool>>,          #[cfg(target_arch = "wasm32")] -        queued_events: Vec<winit::event::Event<Message>>, +        queued_events: Vec<winit::event::Event<Action<Message>>>,      }      struct BootConfig<C> { @@ -229,7 +232,7 @@ where          queued_events: Vec::new(),      }; -    impl<Message, F, C> winit::application::ApplicationHandler<Message> +    impl<Message, F, C> winit::application::ApplicationHandler<Action<Message>>          for Runner<Message, F, C>      where          F: Future<Output = ()>, @@ -393,11 +396,11 @@ where          fn user_event(              &mut self,              event_loop: &winit::event_loop::ActiveEventLoop, -            message: Message, +            action: Action<Message>,          ) {              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<Message>, +            event: winit::event::Event<Action<Message>>,          ) {              // 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<C> {  async fn run_instance<A, E, C>(      mut application: A, -    mut runtime: Runtime<E, Proxy<A::Message>, A::Message>, +    mut runtime: Runtime<E, Proxy<A::Message>, Action<A::Message>>,      mut proxy: Proxy<A::Message>,      mut debug: Debug,      mut boot: oneshot::Receiver<Boot<C>>,      mut event_receiver: mpsc::UnboundedReceiver< -        winit::event::Event<A::Message>, +        winit::event::Event<Action<A::Message>>,      >,      mut control_sender: mpsc::UnboundedSender<winit::event_loop::ControlFlow>, -    init_command: Command<A::Message>,      fonts: Vec<Cow<'static, [u8]>>,  ) where      A: Application + 'static, @@ -518,7 +520,7 @@ async fn run_instance<A, E, C>(      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<A, E, C>(          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<A, E, C>(                      ),                  ));              } -            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<A, E, C>(                          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<A: Application, C, E: Executor>( +/// resulting [`Task`], and tracking its [`Subscription`]. +pub fn update<A: Application, E: Executor>(      application: &mut A, -    compositor: &mut C, -    surface: &mut C::Surface, -    cache: &mut user_interface::Cache,      state: &mut State<A>, -    renderer: &mut A::Renderer, -    runtime: &mut Runtime<E, Proxy<A::Message>, A::Message>, -    clipboard: &mut Clipboard, -    should_exit: &mut bool, -    proxy: &mut Proxy<A::Message>, +    runtime: &mut Runtime<E, Proxy<A::Message>, Action<A::Message>>,      debug: &mut Debug,      messages: &mut Vec<A::Message>,      window: &winit::window::Window,  ) where -    C: Compositor<Renderer = A::Renderer> + '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<A, C, E>( -    application: &A, +/// Runs the actions of a [`Task`]. +pub fn run_action<A, C>( +    action: Action<A::Message>, +    user_interface: &mut UserInterface<'_, A::Message, A::Theme, C::Renderer>,      compositor: &mut C,      surface: &mut C::Surface, -    cache: &mut user_interface::Cache,      state: &State<A>,      renderer: &mut A::Renderer, -    command: Command<A::Message>, -    runtime: &mut Runtime<E, Proxy<A::Message>, A::Message>, +    messages: &mut Vec<A::Message>,      clipboard: &mut Clipboard,      should_exit: &mut bool, -    proxy: &mut Proxy<A::Message>,      debug: &mut Debug,      window: &winit::window::Window,  ) where      A: Application, -    E: Executor,      C: Compositor<Renderer = A::Renderer> + '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::<f32>(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::<f32>(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/conversion.rs b/winit/src/conversion.rs index 79fcf92e..0ed10c88 100644 --- a/winit/src/conversion.rs +++ b/winit/src/conversion.rs @@ -73,11 +73,7 @@ pub fn window_attributes(      #[cfg(target_os = "windows")]      {          use winit::platform::windows::WindowAttributesExtWindows; -        #[allow(unsafe_code)] -        unsafe { -            attributes = attributes -                .with_parent_window(settings.platform_specific.parent); -        } +          attributes = attributes              .with_drag_and_drop(settings.platform_specific.drag_and_drop); diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 2eaf9241..8bd8a64d 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<Self::Message>); +    fn new(flags: Self::Flags) -> (Self, Task<Self::Message>);      /// 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<A, E, C>(      mut application: A, -    mut runtime: Runtime<E, Proxy<A::Message>, A::Message>, +    mut runtime: Runtime<E, Proxy<A::Message>, Action<A::Message>>,      mut proxy: Proxy<A::Message>,      mut debug: Debug,      mut boot: oneshot::Receiver<Boot<C>>, -    mut event_receiver: mpsc::UnboundedReceiver<Event<A::Message>>, +    mut event_receiver: mpsc::UnboundedReceiver<Event<Action<A::Message>>>,      mut control_sender: mpsc::UnboundedSender<Control>, -    init_command: Command<A::Message>,  ) where      A: Application + 'static,      E: Executor + 'static, @@ -511,21 +513,13 @@ async fn run_instance<A, E, C>(          )]),      )); -    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<A, E, C>(                              ),                          );                      } -                    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<A, E, C>(                          // 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<A, E, C>(                              // 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<A: Application, C, E: Executor>( +fn update<A: Application, E: Executor>(      application: &mut A, -    compositor: &mut C, -    runtime: &mut Runtime<E, Proxy<A::Message>, A::Message>, -    clipboard: &mut Clipboard, -    control_sender: &mut mpsc::UnboundedSender<Control>, -    proxy: &mut Proxy<A::Message>, +    runtime: &mut Runtime<E, Proxy<A::Message>, Action<A::Message>>,      debug: &mut Debug,      messages: &mut Vec<A::Message>, -    window_manager: &mut WindowManager<A, C>, -    ui_caches: &mut FxHashMap<window::Id, user_interface::Cache>,  ) where -    C: Compositor<Renderer = A::Renderer> + '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<A, C, E>( +fn run_action<A, C>( +    action: Action<A::Message>,      application: &A,      compositor: &mut C, -    command: Command<A::Message>, -    runtime: &mut Runtime<E, Proxy<A::Message>, A::Message>, +    messages: &mut Vec<A::Message>,      clipboard: &mut Clipboard,      control_sender: &mut mpsc::UnboundedSender<Control>, -    proxy: &mut Proxy<A::Message>,      debug: &mut Debug, +    interfaces: &mut FxHashMap< +        window::Id, +        UserInterface<'_, A::Message, A::Theme, A::Renderer>, +    >,      window_manager: &mut WindowManager<A, C>,      ui_caches: &mut FxHashMap<window::Id, user_interface::Cache>,  ) where      A: Application, -    E: Executor,      C: Compositor<Renderer = A::Renderer> + 'static,      A::Theme: DefaultStyle,  { @@ -1035,279 +1017,254 @@ fn run_command<A, C, E>(      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, channel) => { +                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"); + +                let _ = channel.send(id); +            } +            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::<f32>( -                                    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::<f32>(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<Message: 'static> { -    raw: winit::event_loop::EventLoopProxy<Message>, -    sender: mpsc::Sender<Message>, +pub struct Proxy<T: 'static> { +    raw: winit::event_loop::EventLoopProxy<Action<T>>, +    sender: mpsc::Sender<Action<T>>,      notifier: mpsc::Sender<usize>,  } -impl<Message: 'static> Clone for Proxy<Message> { +impl<T: 'static> Clone for Proxy<T> {      fn clone(&self) -> Self {          Self {              raw: self.raw.clone(), @@ -24,12 +25,12 @@ impl<Message: 'static> Clone for Proxy<Message> {      }  } -impl<Message: 'static> Proxy<Message> { +impl<T: 'static> Proxy<T> {      const MAX_SIZE: usize = 100;      /// Creates a new [`Proxy`] from an `EventLoopProxy`.      pub fn new( -        raw: winit::event_loop::EventLoopProxy<Message>, +        raw: winit::event_loop::EventLoopProxy<Action<T>>,      ) -> (Self, impl Future<Output = ()>) {          let (notifier, mut processed) = mpsc::channel(Self::MAX_SIZE);          let (sender, mut receiver) = mpsc::channel(Self::MAX_SIZE); @@ -72,16 +73,16 @@ impl<Message: 'static> Proxy<Message> {          )      } -    /// 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<Message: 'static> Proxy<Message> {      }  } -impl<Message: 'static> Sink<Message> for Proxy<Message> { +impl<T: 'static> Sink<Action<T>> for Proxy<T> {      type Error = mpsc::SendError;      fn poll_ready( @@ -104,9 +105,9 @@ impl<Message: 'static> Sink<Message> for Proxy<Message> {      fn start_send(          mut self: Pin<&mut Self>, -        message: Message, +        action: Action<T>,      ) -> 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<Message>( -    f: impl Fn(Information) -> Message + Send + 'static, -) -> Command<Message> { -    Command::single(command::Action::System(Action::QueryInformation( -        Box::new(f), -    ))) +pub fn fetch_information() -> Task<Information> { +    Task::oneshot(|channel| { +        runtime::Action::System(Action::QueryInformation(channel)) +    })  }  pub(crate) fn information( | 
