From 287306e1ebec610c31e37782320fe00d20a6c9ac Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 1 Apr 2022 17:27:19 -0300 Subject: Introduce `multi_window` in `iced_winit` --- winit/src/multi_window.rs | 742 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 742 insertions(+) create mode 100644 winit/src/multi_window.rs (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs new file mode 100644 index 00000000..4ea7383a --- /dev/null +++ b/winit/src/multi_window.rs @@ -0,0 +1,742 @@ +//! Create interactive, native cross-platform applications. +mod state; + +pub use state::State; + +use crate::clipboard::{self, Clipboard}; +use crate::conversion; +use crate::mouse; +use crate::renderer; +use crate::widget::operation; +use crate::{ + Command, Debug, Error, Executor, Proxy, Runtime, Settings, Size, + Subscription, +}; + +use iced_futures::futures; +use iced_futures::futures::channel::mpsc; +use iced_graphics::compositor; +use iced_graphics::window; +use iced_native::program::Program; +use iced_native::user_interface::{self, UserInterface}; + +pub use iced_native::application::{Appearance, StyleSheet}; + +use std::mem::ManuallyDrop; + +/// An interactive, native cross-platform application. +/// +/// This trait is the main entrypoint of Iced. Once implemented, you can run +/// your GUI application by simply calling [`run`]. It will run in +/// its own window. +/// +/// An [`Application`] can execute asynchronous actions by returning a +/// [`Command`] in some of its methods. +/// +/// When using an [`Application`] with the `debug` feature enabled, a debug view +/// can be toggled by pressing `F12`. +pub trait Application: Program +where + ::Theme: StyleSheet, +{ + /// The data needed to initialize your [`Application`]. + type Flags; + + /// Initializes the [`Application`] with the flags provided to + /// [`run`] as part of the [`Settings`]. + /// + /// Here is where you should return the initial state of your app. + /// + /// Additionally, you can return a [`Command`] 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); + + /// Returns the current title of the [`Application`]. + /// + /// This title can be dynamic! The runtime will automatically update the + /// title of your application when necessary. + fn title(&self) -> String; + + /// Returns the current [`Theme`] of the [`Application`]. + fn theme(&self) -> ::Theme; + + /// Returns the [`Style`] variation of the [`Theme`]. + fn style( + &self, + ) -> <::Theme as StyleSheet>::Style { + Default::default() + } + + /// Returns the event `Subscription` for the current state of the + /// application. + /// + /// The messages produced by the `Subscription` will be handled by + /// [`update`](#tymethod.update). + /// + /// A `Subscription` will be kept alive as long as you keep returning it! + /// + /// By default, it returns an empty subscription. + fn subscription(&self) -> Subscription { + Subscription::none() + } + + /// Returns the scale factor of the [`Application`]. + /// + /// It can be used to dynamically control the size of the UI at runtime + /// (i.e. zooming). + /// + /// For instance, a scale factor of `2.0` will make widgets twice as big, + /// while a scale factor of `0.5` will shrink them to half their size. + /// + /// By default, it returns `1.0`. + fn scale_factor(&self) -> f64 { + 1.0 + } + + /// Returns whether the [`Application`] should be terminated. + /// + /// By default, it returns `false`. + fn should_exit(&self) -> bool { + false + } +} + +/// Runs an [`Application`] with an executor, compositor, and the provided +/// settings. +pub fn run( + settings: Settings, + compositor_settings: C::Settings, +) -> Result<(), Error> +where + A: Application + 'static, + E: Executor + 'static, + C: window::Compositor + 'static, + ::Theme: StyleSheet, +{ + use futures::task; + use futures::Future; + use winit::event_loop::EventLoopBuilder; + + let mut debug = Debug::new(); + debug.startup_started(); + + let event_loop = EventLoopBuilder::with_user_event().build(); + let proxy = event_loop.create_proxy(); + + let runtime = { + let proxy = Proxy::new(event_loop.create_proxy()); + let executor = E::new().map_err(Error::ExecutorCreationFailed)?; + + Runtime::new(executor, proxy) + }; + + let (application, init_command) = { + let flags = settings.flags; + + runtime.enter(|| A::new(flags)) + }; + + let builder = settings.window.into_builder( + &application.title(), + event_loop.primary_monitor(), + settings.id, + ); + + log::info!("Window builder: {:#?}", builder); + + let window = builder + .build(&event_loop) + .map_err(Error::WindowCreationFailed)?; + + #[cfg(target_arch = "wasm32")] + { + use winit::platform::web::WindowExtWebSys; + + let canvas = window.canvas(); + + let window = web_sys::window().unwrap(); + let document = window.document().unwrap(); + let body = document.body().unwrap(); + + let _ = body + .append_child(&canvas) + .expect("Append canvas to HTML body"); + } + + let (compositor, renderer) = C::new(compositor_settings, Some(&window))?; + + let (mut sender, receiver) = mpsc::unbounded(); + + let mut instance = Box::pin(run_instance::( + application, + compositor, + renderer, + runtime, + proxy, + debug, + receiver, + init_command, + window, + settings.exit_on_close_request, + )); + + let mut context = task::Context::from_waker(task::noop_waker_ref()); + + platform::run(event_loop, move |event, _, control_flow| { + use winit::event_loop::ControlFlow; + + if let ControlFlow::ExitWithCode(_) = control_flow { + return; + } + + let event = match event { + winit::event::Event::WindowEvent { + event: + winit::event::WindowEvent::ScaleFactorChanged { + new_inner_size, + .. + }, + window_id, + } => Some(winit::event::Event::WindowEvent { + event: winit::event::WindowEvent::Resized(*new_inner_size), + window_id, + }), + _ => event.to_static(), + }; + + if let Some(event) = event { + sender.start_send(event).expect("Send event"); + + let poll = instance.as_mut().poll(&mut context); + + *control_flow = match poll { + task::Poll::Pending => ControlFlow::Wait, + task::Poll::Ready(_) => ControlFlow::Exit, + }; + } + }) +} + +async fn run_instance( + mut application: A, + mut compositor: C, + mut renderer: A::Renderer, + mut runtime: Runtime, A::Message>, + mut proxy: winit::event_loop::EventLoopProxy, + mut debug: Debug, + mut receiver: mpsc::UnboundedReceiver>, + init_command: Command, + window: winit::window::Window, + exit_on_close_request: bool, +) where + A: Application + 'static, + E: Executor + 'static, + C: window::Compositor + 'static, + ::Theme: StyleSheet, +{ + use iced_futures::futures::stream::StreamExt; + use winit::event; + + let mut clipboard = Clipboard::connect(&window); + let mut cache = user_interface::Cache::default(); + let mut surface = compositor.create_surface(&window); + + let mut state = State::new(&application, &window); + let mut viewport_version = state.viewport_version(); + + let physical_size = state.physical_size(); + + compositor.configure_surface( + &mut surface, + physical_size.width, + physical_size.height, + ); + + run_command( + &application, + &mut cache, + &state, + &mut renderer, + init_command, + &mut runtime, + &mut clipboard, + &mut proxy, + &mut debug, + &window, + || compositor.fetch_information(), + ); + runtime.track(application.subscription()); + + let mut user_interface = ManuallyDrop::new(build_user_interface( + &application, + user_interface::Cache::default(), + &mut renderer, + state.logical_size(), + &mut debug, + )); + + let mut mouse_interaction = mouse::Interaction::default(); + let mut events = Vec::new(); + let mut messages = Vec::new(); + + debug.startup_finished(); + + while let Some(event) = receiver.next().await { + match event { + event::Event::MainEventsCleared => { + if events.is_empty() && messages.is_empty() { + continue; + } + + debug.event_processing_started(); + + let (interface_state, statuses) = user_interface.update( + &events, + state.cursor_position(), + &mut renderer, + &mut clipboard, + &mut messages, + ); + + debug.event_processing_finished(); + + for event in events.drain(..).zip(statuses.into_iter()) { + runtime.broadcast(event); + } + + if !messages.is_empty() + || matches!( + interface_state, + user_interface::State::Outdated, + ) + { + let mut cache = + ManuallyDrop::into_inner(user_interface).into_cache(); + + // Update application + update( + &mut application, + &mut cache, + &state, + &mut renderer, + &mut runtime, + &mut clipboard, + &mut proxy, + &mut debug, + &mut messages, + &window, + || compositor.fetch_information(), + ); + + // Update window + state.synchronize(&application, &window); + + let should_exit = application.should_exit(); + + user_interface = ManuallyDrop::new(build_user_interface( + &application, + cache, + &mut renderer, + state.logical_size(), + &mut debug, + )); + + if should_exit { + break; + } + } + + debug.draw_started(); + let new_mouse_interaction = user_interface.draw( + &mut renderer, + state.theme(), + &renderer::Style { + text_color: state.text_color(), + }, + state.cursor_position(), + ); + debug.draw_finished(); + + if new_mouse_interaction != mouse_interaction { + window.set_cursor_icon(conversion::mouse_interaction( + new_mouse_interaction, + )); + + mouse_interaction = new_mouse_interaction; + } + + window.request_redraw(); + } + event::Event::PlatformSpecific(event::PlatformSpecific::MacOS( + event::MacOS::ReceivedUrl(url), + )) => { + use iced_native::event; + + events.push(iced_native::Event::PlatformSpecific( + event::PlatformSpecific::MacOS(event::MacOS::ReceivedUrl( + url, + )), + )); + } + event::Event::UserEvent(message) => { + messages.push(message); + } + event::Event::RedrawRequested(_) => { + let physical_size = state.physical_size(); + + if physical_size.width == 0 || physical_size.height == 0 { + continue; + } + + debug.render_started(); + let current_viewport_version = state.viewport_version(); + + if viewport_version != current_viewport_version { + let logical_size = state.logical_size(); + + debug.layout_started(); + user_interface = ManuallyDrop::new( + ManuallyDrop::into_inner(user_interface) + .relayout(logical_size, &mut renderer), + ); + debug.layout_finished(); + + debug.draw_started(); + let new_mouse_interaction = user_interface.draw( + &mut renderer, + state.theme(), + &renderer::Style { + text_color: state.text_color(), + }, + state.cursor_position(), + ); + + if new_mouse_interaction != mouse_interaction { + window.set_cursor_icon(conversion::mouse_interaction( + new_mouse_interaction, + )); + + mouse_interaction = new_mouse_interaction; + } + debug.draw_finished(); + + compositor.configure_surface( + &mut surface, + physical_size.width, + physical_size.height, + ); + + viewport_version = current_viewport_version; + } + + match compositor.present( + &mut renderer, + &mut surface, + state.viewport(), + state.background_color(), + &debug.overlay(), + ) { + Ok(()) => { + debug.render_finished(); + + // TODO: Handle animations! + // Maybe we can use `ControlFlow::WaitUntil` for this. + } + Err(error) => match error { + // This is an unrecoverable error. + compositor::SurfaceError::OutOfMemory => { + panic!("{:?}", error); + } + _ => { + debug.render_finished(); + + // Try rendering again next frame. + window.request_redraw(); + } + }, + } + } + event::Event::WindowEvent { + event: window_event, + .. + } => { + if requests_exit(&window_event, state.modifiers()) + && exit_on_close_request + { + break; + } + + state.update(&window, &window_event, &mut debug); + + if let Some(event) = conversion::window_event( + &window_event, + state.scale_factor(), + state.modifiers(), + ) { + events.push(event); + } + } + _ => {} + } + } + + // Manually drop the user interface + drop(ManuallyDrop::into_inner(user_interface)); +} + +/// Returns true if the provided event should cause an [`Application`] to +/// exit. +pub fn requests_exit( + event: &winit::event::WindowEvent<'_>, + _modifiers: winit::event::ModifiersState, +) -> bool { + use winit::event::WindowEvent; + + match event { + WindowEvent::CloseRequested => true, + #[cfg(target_os = "macos")] + WindowEvent::KeyboardInput { + input: + winit::event::KeyboardInput { + virtual_keycode: Some(winit::event::VirtualKeyCode::Q), + state: winit::event::ElementState::Pressed, + .. + }, + .. + } if _modifiers.logo() => true, + _ => false, + } +} + +/// Builds a [`UserInterface`] for the provided [`Application`], logging +/// [`struct@Debug`] information accordingly. +pub fn build_user_interface<'a, A: Application>( + application: &'a A, + cache: user_interface::Cache, + renderer: &mut A::Renderer, + size: Size, + debug: &mut Debug, +) -> UserInterface<'a, A::Message, A::Renderer> +where + ::Theme: StyleSheet, +{ + debug.view_started(); + let view = application.view(); + debug.view_finished(); + + debug.layout_started(); + let user_interface = UserInterface::build(view, size, cache, renderer); + debug.layout_finished(); + + user_interface +} + +/// Updates an [`Application`] by feeding it the provided messages, spawning any +/// resulting [`Command`], and tracking its [`Subscription`]. +pub fn update( + application: &mut A, + cache: &mut user_interface::Cache, + state: &State, + renderer: &mut A::Renderer, + runtime: &mut Runtime, A::Message>, + clipboard: &mut Clipboard, + proxy: &mut winit::event_loop::EventLoopProxy, + debug: &mut Debug, + messages: &mut Vec, + window: &winit::window::Window, + graphics_info: impl FnOnce() -> compositor::Information + Copy, +) where + ::Theme: StyleSheet, +{ + for message in messages.drain(..) { + debug.log_message(&message); + + debug.update_started(); + let command = runtime.enter(|| application.update(message)); + debug.update_finished(); + + run_command( + application, + cache, + state, + renderer, + command, + runtime, + clipboard, + proxy, + debug, + window, + graphics_info, + ); + } + + let subscription = application.subscription(); + runtime.track(subscription); +} + +/// Runs the actions of a [`Command`]. +pub fn run_command( + application: &A, + cache: &mut user_interface::Cache, + state: &State, + renderer: &mut A::Renderer, + command: Command, + runtime: &mut Runtime, A::Message>, + clipboard: &mut Clipboard, + proxy: &mut winit::event_loop::EventLoopProxy, + debug: &mut Debug, + window: &winit::window::Window, + _graphics_info: impl FnOnce() -> compositor::Information + Copy, +) where + A: Application, + E: Executor, + ::Theme: StyleSheet, +{ + use iced_native::command; + use iced_native::system; + use iced_native::window; + + for action in command.actions() { + match action { + command::Action::Future(future) => { + runtime.spawn(future); + } + command::Action::Clipboard(action) => match action { + clipboard::Action::Read(tag) => { + let message = tag(clipboard.read()); + + proxy + .send_event(message) + .expect("Send message to event loop"); + } + clipboard::Action::Write(contents) => { + clipboard.write(contents); + } + }, + command::Action::Window(action) => match action { + window::Action::Resize { width, height } => { + window.set_inner_size(winit::dpi::LogicalSize { + width, + height, + }); + } + window::Action::Move { x, y } => { + window.set_outer_position(winit::dpi::LogicalPosition { + x, + y, + }); + } + window::Action::SetMode(mode) => { + window.set_visible(conversion::visible(mode)); + window.set_fullscreen(conversion::fullscreen( + window.primary_monitor(), + mode, + )); + } + window::Action::FetchMode(tag) => { + let mode = if window.is_visible().unwrap_or(true) { + conversion::mode(window.fullscreen()) + } else { + window::Mode::Hidden + }; + + proxy + .send_event(tag(mode)) + .expect("Send message to event loop"); + } + }, + command::Action::System(action) => match action { + system::Action::QueryInformation(_tag) => { + #[cfg(feature = "system")] + { + let graphics_info = _graphics_info(); + let proxy = proxy.clone(); + + let _ = std::thread::spawn(move || { + let information = + crate::system::information(graphics_info); + + let message = _tag(information); + + proxy + .send_event(message) + .expect("Send message to event loop") + }); + } + } + }, + command::Action::Widget(action) => { + let mut current_cache = std::mem::take(cache); + let mut current_operation = Some(action.into_operation()); + + let mut user_interface = build_user_interface( + application, + current_cache, + renderer, + state.logical_size(), + debug, + ); + + while let Some(mut operation) = current_operation.take() { + user_interface.operate(renderer, operation.as_mut()); + + match operation.finish() { + operation::Outcome::None => {} + operation::Outcome::Some(message) => { + proxy + .send_event(message) + .expect("Send message to event loop"); + } + operation::Outcome::Chain(next) => { + current_operation = Some(next); + } + } + } + + current_cache = user_interface.into_cache(); + *cache = current_cache; + } + } + } +} + +#[cfg(not(target_arch = "wasm32"))] +mod platform { + pub fn run( + mut event_loop: winit::event_loop::EventLoop, + event_handler: F, + ) -> Result<(), super::Error> + where + F: 'static + + FnMut( + winit::event::Event<'_, T>, + &winit::event_loop::EventLoopWindowTarget, + &mut winit::event_loop::ControlFlow, + ), + { + use winit::platform::run_return::EventLoopExtRunReturn; + + let _ = event_loop.run_return(event_handler); + + Ok(()) + } +} + +#[cfg(target_arch = "wasm32")] +mod platform { + pub fn run( + event_loop: winit::event_loop::EventLoop, + event_handler: F, + ) -> ! + where + F: 'static + + FnMut( + winit::event::Event<'_, T>, + &winit::event_loop::EventLoopWindowTarget, + &mut winit::event_loop::ControlFlow, + ), + { + event_loop.run(event_handler) + } +} -- cgit From b896e41c6e03f1447419194ce41d15fb0db39d96 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 1 Apr 2022 17:39:08 -0300 Subject: Unify `Application` and `Program` Instead of creating a separate `multi_window::Program`, the new `multi_window::Application` unifies both traits --- winit/src/multi_window.rs | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 4ea7383a..b519e471 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -9,15 +9,14 @@ use crate::mouse; use crate::renderer; use crate::widget::operation; use crate::{ - Command, Debug, Error, Executor, Proxy, Runtime, Settings, Size, - Subscription, + Command, Debug, Element, Error, Executor, Proxy, Renderer, Runtime, + Settings, Size, Subscription, }; use iced_futures::futures; use iced_futures::futures::channel::mpsc; use iced_graphics::compositor; use iced_graphics::window; -use iced_native::program::Program; use iced_native::user_interface::{self, UserInterface}; pub use iced_native::application::{Appearance, StyleSheet}; @@ -35,13 +34,34 @@ use std::mem::ManuallyDrop; /// /// When using an [`Application`] with the `debug` feature enabled, a debug view /// can be toggled by pressing `F12`. -pub trait Application: Program +pub trait Application: Sized where ::Theme: StyleSheet, { /// The data needed to initialize your [`Application`]. type Flags; + /// The graphics backend to use to draw the [`Program`]. + type Renderer: Renderer; + + /// The type of __messages__ your [`Program`] will produce. + type Message: std::fmt::Debug + Send; + + /// Handles a __message__ and updates the state of the [`Program`]. + /// + /// This is where you define your __update logic__. All the __messages__, + /// produced by either user interactions or commands, will be handled by + /// this method. + /// + /// Any [`Command`] returned will be executed immediately in the + /// background by shells. + fn update(&mut self, message: Self::Message) -> Command; + + /// Returns the widgets to display in the [`Program`]. + /// + /// These widgets can produce __messages__ based on user interaction. + fn view(&self) -> Element<'_, Self::Message, Self::Renderer>; + /// Initializes the [`Application`] with the flags provided to /// [`run`] as part of the [`Settings`]. /// -- cgit From 12538d3c5be08f2109f1dc61936ceb2fda0fc5c6 Mon Sep 17 00:00:00 2001 From: Richard Date: Wed, 15 Jun 2022 16:46:37 -0300 Subject: Use map of windows internally --- winit/src/multi_window.rs | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index b519e471..8a967207 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -21,6 +21,7 @@ use iced_native::user_interface::{self, UserInterface}; pub use iced_native::application::{Appearance, StyleSheet}; +use std::collections::HashMap; use std::mem::ManuallyDrop; /// An interactive, native cross-platform application. @@ -169,6 +170,10 @@ where .build(&event_loop) .map_err(Error::WindowCreationFailed)?; + let windows: HashMap = + HashMap::from([(0usize, window)]); + let window = windows.values().next().expect("No window found"); + #[cfg(target_arch = "wasm32")] { use winit::platform::web::WindowExtWebSys; @@ -197,7 +202,7 @@ where debug, receiver, init_command, - window, + windows, settings.exit_on_close_request, )); @@ -247,7 +252,7 @@ async fn run_instance( mut debug: Debug, mut receiver: mpsc::UnboundedReceiver>, init_command: Command, - window: winit::window::Window, + windows: HashMap, exit_on_close_request: bool, ) where A: Application + 'static, @@ -258,11 +263,12 @@ async fn run_instance( use iced_futures::futures::stream::StreamExt; use winit::event; - let mut clipboard = Clipboard::connect(&window); + let window = windows.values().next().expect("No window found"); + let mut clipboard = Clipboard::connect(window); let mut cache = user_interface::Cache::default(); let mut surface = compositor.create_surface(&window); - let mut state = State::new(&application, &window); + let mut state = State::new(&application, window); let mut viewport_version = state.viewport_version(); let physical_size = state.physical_size(); @@ -283,7 +289,7 @@ async fn run_instance( &mut clipboard, &mut proxy, &mut debug, - &window, + &windows, || compositor.fetch_information(), ); runtime.track(application.subscription()); @@ -345,12 +351,12 @@ async fn run_instance( &mut proxy, &mut debug, &mut messages, - &window, + &windows, || compositor.fetch_information(), ); // Update window - state.synchronize(&application, &window); + state.synchronize(&application, window); let should_exit = application.should_exit(); @@ -487,7 +493,7 @@ async fn run_instance( break; } - state.update(&window, &window_event, &mut debug); + state.update(window, &window_event, &mut debug); if let Some(event) = conversion::window_event( &window_event, @@ -564,7 +570,7 @@ pub fn update( proxy: &mut winit::event_loop::EventLoopProxy, debug: &mut Debug, messages: &mut Vec, - window: &winit::window::Window, + windows: &HashMap, graphics_info: impl FnOnce() -> compositor::Information + Copy, ) where ::Theme: StyleSheet, @@ -586,7 +592,7 @@ pub fn update( clipboard, proxy, debug, - window, + windows, graphics_info, ); } @@ -606,7 +612,7 @@ pub fn run_command( clipboard: &mut Clipboard, proxy: &mut winit::event_loop::EventLoopProxy, debug: &mut Debug, - window: &winit::window::Window, + windows: &HashMap, _graphics_info: impl FnOnce() -> compositor::Information + Copy, ) where A: Application, @@ -617,6 +623,8 @@ pub fn run_command( use iced_native::system; use iced_native::window; + let window = windows.values().next().expect("No window found"); + for action in command.actions() { match action { command::Action::Future(future) => { -- cgit From 5919325d9b9ebf86f7358059201e6fc1af2412d8 Mon Sep 17 00:00:00 2001 From: Richard Date: Wed, 15 Jun 2022 20:01:50 -0300 Subject: Internally wrap `Message` with a `Event` enum --- winit/src/multi_window.rs | 43 ++++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 15 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 8a967207..61984b93 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -13,8 +13,8 @@ use crate::{ Settings, Size, Subscription, }; -use iced_futures::futures; use iced_futures::futures::channel::mpsc; +use iced_futures::futures::{self, FutureExt}; use iced_graphics::compositor; use iced_graphics::window; use iced_native::user_interface::{self, UserInterface}; @@ -24,6 +24,16 @@ pub use iced_native::application::{Appearance, StyleSheet}; use std::collections::HashMap; use std::mem::ManuallyDrop; +/// TODO(derezzedex) +// This is the an wrapper around the `Application::Message` associate type +// to allows the `shell` to create internal messages, while still having +// the current user specified custom messages. +#[derive(Debug, Clone)] +pub enum Event { + /// An [`Application`] generated message + Application(Message), +} + /// An interactive, native cross-platform application. /// /// This trait is the main entrypoint of Iced. Once implemented, you can run @@ -247,10 +257,12 @@ async fn run_instance( mut application: A, mut compositor: C, mut renderer: A::Renderer, - mut runtime: Runtime, A::Message>, - mut proxy: winit::event_loop::EventLoopProxy, + mut runtime: Runtime>, Event>, + mut proxy: winit::event_loop::EventLoopProxy>, mut debug: Debug, - mut receiver: mpsc::UnboundedReceiver>, + mut receiver: mpsc::UnboundedReceiver< + winit::event::Event<'_, Event>, + >, init_command: Command, windows: HashMap, exit_on_close_request: bool, @@ -292,7 +304,7 @@ async fn run_instance( &windows, || compositor.fetch_information(), ); - runtime.track(application.subscription()); + runtime.track(application.subscription().map(Event::Application)); let mut user_interface = ManuallyDrop::new(build_user_interface( &application, @@ -406,6 +418,7 @@ async fn run_instance( )); } event::Event::UserEvent(message) => { + let Event::Application(message) = message; messages.push(message); } event::Event::RedrawRequested(_) => { @@ -565,9 +578,9 @@ pub fn update( cache: &mut user_interface::Cache, state: &State, renderer: &mut A::Renderer, - runtime: &mut Runtime, A::Message>, + runtime: &mut Runtime>, Event>, clipboard: &mut Clipboard, - proxy: &mut winit::event_loop::EventLoopProxy, + proxy: &mut winit::event_loop::EventLoopProxy>, debug: &mut Debug, messages: &mut Vec, windows: &HashMap, @@ -597,7 +610,7 @@ pub fn update( ); } - let subscription = application.subscription(); + let subscription = application.subscription().map(Event::Application); runtime.track(subscription); } @@ -608,9 +621,9 @@ pub fn run_command( state: &State, renderer: &mut A::Renderer, command: Command, - runtime: &mut Runtime, A::Message>, + runtime: &mut Runtime>, Event>, clipboard: &mut Clipboard, - proxy: &mut winit::event_loop::EventLoopProxy, + proxy: &mut winit::event_loop::EventLoopProxy>, debug: &mut Debug, windows: &HashMap, _graphics_info: impl FnOnce() -> compositor::Information + Copy, @@ -628,14 +641,14 @@ pub fn run_command( for action in command.actions() { match action { command::Action::Future(future) => { - runtime.spawn(future); + runtime.spawn(Box::pin(future.map(Event::Application))); } command::Action::Clipboard(action) => match action { clipboard::Action::Read(tag) => { let message = tag(clipboard.read()); proxy - .send_event(message) + .send_event(Event::Application(message)) .expect("Send message to event loop"); } clipboard::Action::Write(contents) => { @@ -670,7 +683,7 @@ pub fn run_command( }; proxy - .send_event(tag(mode)) + .send_event(Event::Application(tag(mode))) .expect("Send message to event loop"); } }, @@ -688,7 +701,7 @@ pub fn run_command( let message = _tag(information); proxy - .send_event(message) + .send_event(Event::Application(message)) .expect("Send message to event loop") }); } @@ -713,7 +726,7 @@ pub fn run_command( operation::Outcome::None => {} operation::Outcome::Some(message) => { proxy - .send_event(message) + .send_event(Event::Application(message)) .expect("Send message to event loop"); } operation::Outcome::Chain(next) => { -- cgit From 8fdd5ee8b60e551088d4a18fb1d58b6c3e62ba7d Mon Sep 17 00:00:00 2001 From: Richard Date: Wed, 15 Jun 2022 20:38:51 -0300 Subject: Synchronize window list with `windows` method --- winit/src/multi_window.rs | 72 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 59 insertions(+), 13 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 61984b93..900ee92a 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -7,6 +7,7 @@ use crate::clipboard::{self, Clipboard}; use crate::conversion; use crate::mouse; use crate::renderer; +use crate::settings; use crate::widget::operation; use crate::{ Command, Debug, Element, Error, Executor, Proxy, Renderer, Runtime, @@ -28,10 +29,17 @@ use std::mem::ManuallyDrop; // This is the an wrapper around the `Application::Message` associate type // to allows the `shell` to create internal messages, while still having // the current user specified custom messages. -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum Event { /// An [`Application`] generated message Application(Message), + + /// TODO(derezzedex) + // Create a wrapper variant of `window::Event` type instead + // (maybe we should also allow users to listen/react to those internal messages?) + NewWindow(usize, settings::Window), + /// TODO(derezzedex) + WindowCreated(usize, winit::window::Window), } /// An interactive, native cross-platform application. @@ -218,7 +226,7 @@ where let mut context = task::Context::from_waker(task::noop_waker_ref()); - platform::run(event_loop, move |event, _, control_flow| { + platform::run(event_loop, move |event, event_loop, control_flow| { use winit::event_loop::ControlFlow; if let ControlFlow::ExitWithCode(_) = control_flow { @@ -237,6 +245,21 @@ where event: winit::event::WindowEvent::Resized(*new_inner_size), window_id, }), + winit::event::Event::UserEvent(Event::NewWindow(id, settings)) => { + // TODO(derezzedex) + let window = settings + .into_builder( + "fix window title", + event_loop.primary_monitor(), + None, + ) + .build(event_loop) + .expect("Failed to build window"); + + Some(winit::event::Event::UserEvent(Event::WindowCreated( + id, window, + ))) + } _ => event.to_static(), }; @@ -264,7 +287,7 @@ async fn run_instance( winit::event::Event<'_, Event>, >, init_command: Command, - windows: HashMap, + mut windows: HashMap, exit_on_close_request: bool, ) where A: Application + 'static, @@ -275,12 +298,18 @@ async fn run_instance( use iced_futures::futures::stream::StreamExt; use winit::event; - let window = windows.values().next().expect("No window found"); - let mut clipboard = Clipboard::connect(window); + // TODO(derezzedex) + let mut clipboard = + Clipboard::connect(windows.values().next().expect("No window found")); let mut cache = user_interface::Cache::default(); - let mut surface = compositor.create_surface(&window); + let mut surface = compositor + .create_surface(&windows.values().next().expect("No window found")); - let mut state = State::new(&application, window); + // TODO(derezzedex) + let mut state = State::new( + &application, + windows.values().next().expect("No window found"), + ); let mut viewport_version = state.viewport_version(); let physical_size = state.physical_size(); @@ -368,7 +397,7 @@ async fn run_instance( ); // Update window - state.synchronize(&application, window); + state.synchronize(&application, &windows, &proxy); let should_exit = application.should_exit(); @@ -396,6 +425,8 @@ async fn run_instance( ); debug.draw_finished(); + // TODO(derezzedex) + let window = windows.values().next().expect("No window found"); if new_mouse_interaction != mouse_interaction { window.set_cursor_icon(conversion::mouse_interaction( new_mouse_interaction, @@ -417,10 +448,15 @@ async fn run_instance( )), )); } - event::Event::UserEvent(message) => { - let Event::Application(message) = message; - messages.push(message); - } + event::Event::UserEvent(event) => match event { + Event::Application(message) => { + messages.push(message); + } + Event::WindowCreated(id, window) => { + let _ = windows.insert(id, window); + } + Event::NewWindow(_, _) => unreachable!(), + }, event::Event::RedrawRequested(_) => { let physical_size = state.physical_size(); @@ -451,6 +487,9 @@ async fn run_instance( state.cursor_position(), ); + // TODO(derezzedex) + let window = + windows.values().next().expect("No window found"); if new_mouse_interaction != mouse_interaction { window.set_cursor_icon(conversion::mouse_interaction( new_mouse_interaction, @@ -491,7 +530,12 @@ async fn run_instance( debug.render_finished(); // Try rendering again next frame. - window.request_redraw(); + // TODO(derezzedex) + windows + .values() + .next() + .expect("No window found") + .request_redraw(); } }, } @@ -506,6 +550,8 @@ async fn run_instance( break; } + // TODO(derezzedex) + let window = windows.values().next().expect("No window found"); state.update(window, &window_event, &mut debug); if let Some(event) = conversion::window_event( -- cgit From ec56c0686df1a200e37af951a3a8eca562c32a5c Mon Sep 17 00:00:00 2001 From: Richard Date: Tue, 21 Jun 2022 15:59:45 -0300 Subject: Introduce opaque `window::Id` type --- winit/src/multi_window.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 900ee92a..14be4de3 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -9,6 +9,7 @@ use crate::mouse; use crate::renderer; use crate::settings; use crate::widget::operation; +use crate::window; use crate::{ Command, Debug, Element, Error, Executor, Proxy, Renderer, Runtime, Settings, Size, Subscription, @@ -17,7 +18,6 @@ use crate::{ use iced_futures::futures::channel::mpsc; use iced_futures::futures::{self, FutureExt}; use iced_graphics::compositor; -use iced_graphics::window; use iced_native::user_interface::{self, UserInterface}; pub use iced_native::application::{Appearance, StyleSheet}; @@ -37,9 +37,9 @@ pub enum Event { /// TODO(derezzedex) // Create a wrapper variant of `window::Event` type instead // (maybe we should also allow users to listen/react to those internal messages?) - NewWindow(usize, settings::Window), + NewWindow(window::Id, settings::Window), /// TODO(derezzedex) - WindowCreated(usize, winit::window::Window), + WindowCreated(window::Id, winit::window::Window), } /// An interactive, native cross-platform application. @@ -66,6 +66,9 @@ where /// The type of __messages__ your [`Program`] will produce. type Message: std::fmt::Debug + Send; + /// TODO(derezzedex) + fn windows(&self) -> Vec<(window::Id, settings::Window)>; + /// Handles a __message__ and updates the state of the [`Program`]. /// /// This is where you define your __update logic__. All the __messages__, @@ -150,7 +153,7 @@ pub fn run( where A: Application + 'static, E: Executor + 'static, - C: window::Compositor + 'static, + C: iced_graphics::window::Compositor + 'static, ::Theme: StyleSheet, { use futures::task; @@ -188,8 +191,8 @@ where .build(&event_loop) .map_err(Error::WindowCreationFailed)?; - let windows: HashMap = - HashMap::from([(0usize, window)]); + let windows: HashMap = + HashMap::from([(window::Id::new(0usize), window)]); let window = windows.values().next().expect("No window found"); #[cfg(target_arch = "wasm32")] @@ -287,12 +290,12 @@ async fn run_instance( winit::event::Event<'_, Event>, >, init_command: Command, - mut windows: HashMap, + mut windows: HashMap, exit_on_close_request: bool, ) where A: Application + 'static, E: Executor + 'static, - C: window::Compositor + 'static, + C: iced_graphics::window::Compositor + 'static, ::Theme: StyleSheet, { use iced_futures::futures::stream::StreamExt; @@ -629,7 +632,7 @@ pub fn update( proxy: &mut winit::event_loop::EventLoopProxy>, debug: &mut Debug, messages: &mut Vec, - windows: &HashMap, + windows: &HashMap, graphics_info: impl FnOnce() -> compositor::Information + Copy, ) where ::Theme: StyleSheet, @@ -671,7 +674,7 @@ pub fn run_command( clipboard: &mut Clipboard, proxy: &mut winit::event_loop::EventLoopProxy>, debug: &mut Debug, - windows: &HashMap, + windows: &HashMap, _graphics_info: impl FnOnce() -> compositor::Information + Copy, ) where A: Application, -- cgit From 97914daaab477ce47a8329f07958332b5caa4ed0 Mon Sep 17 00:00:00 2001 From: Richard Date: Tue, 12 Jul 2022 10:26:16 -0300 Subject: what is this --- winit/src/multi_window.rs | 421 ++++++++++++++++++++++++++++++---------------- 1 file changed, 277 insertions(+), 144 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 14be4de3..82ee30ed 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -301,50 +301,63 @@ async fn run_instance( use iced_futures::futures::stream::StreamExt; use winit::event; - // TODO(derezzedex) let mut clipboard = Clipboard::connect(windows.values().next().expect("No window found")); let mut cache = user_interface::Cache::default(); - let mut surface = compositor - .create_surface(&windows.values().next().expect("No window found")); + let mut window_ids: HashMap<_, _> = windows + .iter() + .map(|(&id, window)| (window.id(), id)) + .collect(); - // TODO(derezzedex) - let mut state = State::new( - &application, - windows.values().next().expect("No window found"), - ); - let mut viewport_version = state.viewport_version(); + let mut states = HashMap::new(); + let mut interfaces = ManuallyDrop::new(HashMap::new()); - let physical_size = state.physical_size(); + for (&id, window) in windows.keys().zip(windows.values()) { + let mut surface = compositor.create_surface(window); - compositor.configure_surface( - &mut surface, - physical_size.width, - physical_size.height, - ); + let state = State::new(&application, window); - run_command( - &application, - &mut cache, - &state, - &mut renderer, - init_command, - &mut runtime, - &mut clipboard, - &mut proxy, - &mut debug, - &windows, - || compositor.fetch_information(), - ); - runtime.track(application.subscription().map(Event::Application)); + let physical_size = state.physical_size(); - let mut user_interface = ManuallyDrop::new(build_user_interface( - &application, - user_interface::Cache::default(), - &mut renderer, - state.logical_size(), - &mut debug, - )); + compositor.configure_surface( + &mut surface, + physical_size.width, + physical_size.height, + ); + + let user_interface = build_user_interface( + &application, + user_interface::Cache::default(), + &mut renderer, + state.logical_size(), + &mut debug, + ); + + let window_state: WindowState = WindowState { surface, state }; + + let _ = states.insert(id, window_state); + let _ = interfaces.insert(id, user_interface); + } + + { + // TODO(derezzedex) + let window_state = states.values().next().expect("No state found"); + + run_command( + &application, + &mut cache, + &window_state.state, + &mut renderer, + init_command, + &mut runtime, + &mut clipboard, + &mut proxy, + &mut debug, + &windows, + || compositor.fetch_information(), + ); + } + runtime.track(application.subscription().map(Event::Application)); let mut mouse_interaction = mouse::Interaction::default(); let mut events = Vec::new(); @@ -352,93 +365,118 @@ async fn run_instance( debug.startup_finished(); - while let Some(event) = receiver.next().await { + 'main: while let Some(event) = receiver.next().await { match event { event::Event::MainEventsCleared => { - if events.is_empty() && messages.is_empty() { - continue; - } - - debug.event_processing_started(); + dbg!(states.keys().collect::>()); + for id in states.keys().copied().collect::>() { + let cursor_position = + states.get(&id).unwrap().state.cursor_position(); + let window = windows.get(&id).unwrap(); + + if events.is_empty() && messages.is_empty() { + continue; + } - let (interface_state, statuses) = user_interface.update( - &events, - state.cursor_position(), - &mut renderer, - &mut clipboard, - &mut messages, - ); + debug.event_processing_started(); + + let (interface_state, statuses) = { + let user_interface = interfaces.get_mut(&id).unwrap(); + user_interface.update( + &events, + cursor_position, + &mut renderer, + &mut clipboard, + &mut messages, + ) + }; - debug.event_processing_finished(); + debug.event_processing_finished(); - for event in events.drain(..).zip(statuses.into_iter()) { - runtime.broadcast(event); - } + // TODO(derezzedex): only drain events for this window + for event in events.drain(..).zip(statuses.into_iter()) { + runtime.broadcast(event); + } - if !messages.is_empty() - || matches!( - interface_state, - user_interface::State::Outdated, - ) - { - let mut cache = - ManuallyDrop::into_inner(user_interface).into_cache(); - - // Update application - update( - &mut application, - &mut cache, - &state, - &mut renderer, - &mut runtime, - &mut clipboard, - &mut proxy, - &mut debug, - &mut messages, - &windows, - || compositor.fetch_information(), - ); + if !messages.is_empty() + || matches!( + interface_state, + user_interface::State::Outdated, + ) + { + let state = &mut states.get_mut(&id).unwrap().state; + let pure_states: HashMap<_, _> = + ManuallyDrop::into_inner(interfaces) + .drain() + .map( + |(id, interface): ( + window::Id, + UserInterface<'_, _, _>, + )| { + (id, interface.into_cache()) + }, + ) + .collect(); + + // Update application + update( + &mut application, + &mut cache, + state, + &mut renderer, + &mut runtime, + &mut clipboard, + &mut proxy, + &mut debug, + &mut messages, + &windows, + || compositor.fetch_information(), + ); + + // Update window + state.synchronize(&application, &windows, &proxy); + + let should_exit = application.should_exit(); + + interfaces = ManuallyDrop::new(build_user_interfaces( + &application, + &mut renderer, + &mut debug, + &states, + pure_states, + )); - // Update window - state.synchronize(&application, &windows, &proxy); + if should_exit { + break 'main; + } + } - let should_exit = application.should_exit(); + debug.draw_started(); + let new_mouse_interaction = { + let user_interface = interfaces.get_mut(&id).unwrap(); + let state = &states.get(&id).unwrap().state; + + user_interface.draw( + &mut renderer, + state.theme(), + &renderer::Style { + text_color: state.text_color(), + }, + state.cursor_position(), + ) + }; + debug.draw_finished(); - user_interface = ManuallyDrop::new(build_user_interface( - &application, - cache, - &mut renderer, - state.logical_size(), - &mut debug, - )); + if new_mouse_interaction != mouse_interaction { + window.set_cursor_icon(conversion::mouse_interaction( + new_mouse_interaction, + )); - if should_exit { - break; + mouse_interaction = new_mouse_interaction; } - } - - debug.draw_started(); - let new_mouse_interaction = user_interface.draw( - &mut renderer, - state.theme(), - &renderer::Style { - text_color: state.text_color(), - }, - state.cursor_position(), - ); - debug.draw_finished(); - - // TODO(derezzedex) - let window = windows.values().next().expect("No window found"); - if new_mouse_interaction != mouse_interaction { - window.set_cursor_icon(conversion::mouse_interaction( - new_mouse_interaction, - )); - mouse_interaction = new_mouse_interaction; + window.request_redraw(); } - - window.request_redraw(); } event::Event::PlatformSpecific(event::PlatformSpecific::MacOS( event::MacOS::ReceivedUrl(url), @@ -456,43 +494,81 @@ async fn run_instance( messages.push(message); } Event::WindowCreated(id, window) => { + let mut surface = compositor.create_surface(&window); + + let state = State::new(&application, &window); + + let physical_size = state.physical_size(); + + compositor.configure_surface( + &mut surface, + physical_size.width, + physical_size.height, + ); + + let user_interface = build_user_interface( + &application, + user_interface::Cache::default(), + &mut renderer, + state.logical_size(), + &mut debug, + ); + + let window_state: WindowState = + WindowState { surface, state }; + + let _ = states.insert(id, window_state); + let _ = interfaces.insert(id, user_interface); + let _ = window_ids.insert(window.id(), id); let _ = windows.insert(id, window); } Event::NewWindow(_, _) => unreachable!(), }, - event::Event::RedrawRequested(_) => { - let physical_size = state.physical_size(); + event::Event::RedrawRequested(id) => { + let window_state = window_ids + .get(&id) + .and_then(|id| states.get_mut(id)) + .unwrap(); + + let mut user_interface = window_ids + .get(&id) + .and_then(|id| interfaces.remove(id)) + .unwrap(); + + let physical_size = window_state.state.physical_size(); if physical_size.width == 0 || physical_size.height == 0 { continue; } debug.render_started(); - let current_viewport_version = state.viewport_version(); - if viewport_version != current_viewport_version { - let logical_size = state.logical_size(); + if window_state.state.viewport_changed() { + let logical_size = window_state.state.logical_size(); debug.layout_started(); - user_interface = ManuallyDrop::new( - ManuallyDrop::into_inner(user_interface) - .relayout(logical_size, &mut renderer), - ); + user_interface = + user_interface.relayout(logical_size, &mut renderer); debug.layout_finished(); debug.draw_started(); - let new_mouse_interaction = user_interface.draw( - &mut renderer, - state.theme(), - &renderer::Style { - text_color: state.text_color(), - }, - state.cursor_position(), - ); + let new_mouse_interaction = { + let state = &window_state.state; + + user_interface.draw( + &mut renderer, + state.theme(), + &renderer::Style { + text_color: state.text_color(), + }, + state.cursor_position(), + ) + }; - // TODO(derezzedex) - let window = - windows.values().next().expect("No window found"); + let window = window_ids + .get(&id) + .and_then(|id| windows.get(id)) + .unwrap(); if new_mouse_interaction != mouse_interaction { window.set_cursor_icon(conversion::mouse_interaction( new_mouse_interaction, @@ -502,20 +578,21 @@ async fn run_instance( } debug.draw_finished(); + let _ = interfaces + .insert(*window_ids.get(&id).unwrap(), user_interface); + compositor.configure_surface( - &mut surface, + &mut window_state.surface, physical_size.width, physical_size.height, ); - - viewport_version = current_viewport_version; } match compositor.present( &mut renderer, - &mut surface, - state.viewport(), - state.background_color(), + &mut window_state.surface, + window_state.state.viewport(), + window_state.state.background_color(), &debug.overlay(), ) { Ok(()) => { @@ -545,22 +622,30 @@ async fn run_instance( } event::Event::WindowEvent { event: window_event, - .. + window_id, } => { - if requests_exit(&window_event, state.modifiers()) + // dbg!(window_id); + let window = window_ids + .get(&window_id) + .and_then(|id| windows.get(id)) + .unwrap(); + let window_state = window_ids + .get(&window_id) + .and_then(|id| states.get_mut(id)) + .unwrap(); + + if requests_exit(&window_event, window_state.state.modifiers()) && exit_on_close_request { break; } - // TODO(derezzedex) - let window = windows.values().next().expect("No window found"); - state.update(window, &window_event, &mut debug); + window_state.state.update(window, &window_event, &mut debug); if let Some(event) = conversion::window_event( &window_event, - state.scale_factor(), - state.modifiers(), + window_state.state.scale_factor(), + window_state.state.modifiers(), ) { events.push(event); } @@ -570,7 +655,7 @@ async fn run_instance( } // Manually drop the user interface - drop(ManuallyDrop::into_inner(user_interface)); + // drop(ManuallyDrop::into_inner(user_interface)); } /// Returns true if the provided event should cause an [`Application`] to @@ -791,6 +876,54 @@ pub fn run_command( } } +struct WindowState +where + A: Application, + C: iced_graphics::window::Compositor, + ::Theme: StyleSheet, +{ + surface: ::Surface, + state: State, +} + +fn build_user_interfaces<'a, A, C>( + application: &'a A, + renderer: &mut A::Renderer, + debug: &mut Debug, + states: &HashMap>, + mut pure_states: HashMap, +) -> HashMap< + window::Id, + UserInterface< + 'a, + ::Message, + ::Renderer, + >, +> +where + A: Application + 'static, + C: iced_graphics::window::Compositor + 'static, + ::Theme: StyleSheet, +{ + let mut interfaces = HashMap::new(); + + for (id, pure_state) in pure_states.drain() { + let state = &states.get(&id).unwrap().state; + + let user_interface = build_user_interface( + application, + pure_state, + renderer, + state.logical_size(), + debug, + ); + + let _ = interfaces.insert(id, user_interface); + } + + interfaces +} + #[cfg(not(target_arch = "wasm32"))] mod platform { pub fn run( -- cgit From 8f53df560e1bde33e874977e5115cd0f9301640d Mon Sep 17 00:00:00 2001 From: Richard Date: Thu, 14 Jul 2022 10:13:37 -0300 Subject: fix: temporarily add `window::Id` to events internaly --- winit/src/multi_window.rs | 53 +++++++++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 18 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 82ee30ed..6c2dc888 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -368,13 +368,27 @@ async fn run_instance( 'main: while let Some(event) = receiver.next().await { match event { event::Event::MainEventsCleared => { - dbg!(states.keys().collect::>()); for id in states.keys().copied().collect::>() { + let (filtered, remaining): (Vec<_>, Vec<_>) = events + .iter() + .cloned() + .partition( + |(window_id, _event): &( + Option, + iced_native::event::Event, + )| { + *window_id == Some(id) || *window_id == None + }, + ); + + events.retain(|el| remaining.contains(el)); + let filtered: Vec<_> = filtered.into_iter().map(|(_id, event)| event.clone()).collect(); + let cursor_position = states.get(&id).unwrap().state.cursor_position(); let window = windows.get(&id).unwrap(); - if events.is_empty() && messages.is_empty() { + if filtered.is_empty() && messages.is_empty() { continue; } @@ -383,7 +397,7 @@ async fn run_instance( let (interface_state, statuses) = { let user_interface = interfaces.get_mut(&id).unwrap(); user_interface.update( - &events, + &filtered, cursor_position, &mut renderer, &mut clipboard, @@ -393,11 +407,11 @@ async fn run_instance( debug.event_processing_finished(); - // TODO(derezzedex): only drain events for this window - for event in events.drain(..).zip(statuses.into_iter()) { + for event in filtered.into_iter().zip(statuses.into_iter()) { runtime.broadcast(event); } + // TODO(derezzedex): Should we redraw every window? We can't know what changed. if !messages.is_empty() || matches!( interface_state, @@ -475,18 +489,22 @@ async fn run_instance( mouse_interaction = new_mouse_interaction; } - window.request_redraw(); + for window in windows.values(){ + window.request_redraw(); + } } } event::Event::PlatformSpecific(event::PlatformSpecific::MacOS( event::MacOS::ReceivedUrl(url), )) => { use iced_native::event; - - events.push(iced_native::Event::PlatformSpecific( - event::PlatformSpecific::MacOS(event::MacOS::ReceivedUrl( - url, - )), + events.push(( + None, + iced_native::Event::PlatformSpecific( + event::PlatformSpecific::MacOS( + event::MacOS::ReceivedUrl(url), + ), + ), )); } event::Event::UserEvent(event) => match event { @@ -529,12 +547,6 @@ async fn run_instance( .get(&id) .and_then(|id| states.get_mut(id)) .unwrap(); - - let mut user_interface = window_ids - .get(&id) - .and_then(|id| interfaces.remove(id)) - .unwrap(); - let physical_size = window_state.state.physical_size(); if physical_size.width == 0 || physical_size.height == 0 { @@ -544,6 +556,11 @@ async fn run_instance( debug.render_started(); if window_state.state.viewport_changed() { + let mut user_interface = window_ids + .get(&id) + .and_then(|id| interfaces.remove(id)) + .unwrap(); + let logical_size = window_state.state.logical_size(); debug.layout_started(); @@ -647,7 +664,7 @@ async fn run_instance( window_state.state.scale_factor(), window_state.state.modifiers(), ) { - events.push(event); + events.push((window_ids.get(&window_id).cloned(), event)); } } _ => {} -- cgit From 2fe58e12619186eb3755491db2bdaf02de297afb Mon Sep 17 00:00:00 2001 From: Richard Date: Thu, 21 Jul 2022 09:52:32 -0300 Subject: add `window::Id` to `view` --- winit/src/multi_window.rs | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 6c2dc888..dc00d737 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -82,7 +82,10 @@ where /// Returns the widgets to display in the [`Program`]. /// /// These widgets can produce __messages__ based on user interaction. - fn view(&self) -> Element<'_, Self::Message, Self::Renderer>; + fn view( + &self, + window: window::Id, + ) -> Element<'_, Self::Message, Self::Renderer>; /// Initializes the [`Application`] with the flags provided to /// [`run`] as part of the [`Settings`]. @@ -331,6 +334,7 @@ async fn run_instance( &mut renderer, state.logical_size(), &mut debug, + id, ); let window_state: WindowState = WindowState { surface, state }; @@ -354,6 +358,7 @@ async fn run_instance( &mut proxy, &mut debug, &windows, + &window_ids, || compositor.fetch_information(), ); } @@ -369,10 +374,8 @@ async fn run_instance( match event { event::Event::MainEventsCleared => { for id in states.keys().copied().collect::>() { - let (filtered, remaining): (Vec<_>, Vec<_>) = events - .iter() - .cloned() - .partition( + let (filtered, remaining): (Vec<_>, Vec<_>) = + events.iter().cloned().partition( |(window_id, _event): &( Option, iced_native::event::Event, @@ -382,7 +385,10 @@ async fn run_instance( ); events.retain(|el| remaining.contains(el)); - let filtered: Vec<_> = filtered.into_iter().map(|(_id, event)| event.clone()).collect(); + let filtered: Vec<_> = filtered + .into_iter() + .map(|(_id, event)| event) + .collect(); let cursor_position = states.get(&id).unwrap().state.cursor_position(); @@ -407,7 +413,8 @@ async fn run_instance( debug.event_processing_finished(); - for event in filtered.into_iter().zip(statuses.into_iter()) { + for event in filtered.into_iter().zip(statuses.into_iter()) + { runtime.broadcast(event); } @@ -444,6 +451,7 @@ async fn run_instance( &mut debug, &mut messages, &windows, + &window_ids, || compositor.fetch_information(), ); @@ -489,7 +497,7 @@ async fn run_instance( mouse_interaction = new_mouse_interaction; } - for window in windows.values(){ + for window in windows.values() { window.request_redraw(); } } @@ -530,6 +538,7 @@ async fn run_instance( &mut renderer, state.logical_size(), &mut debug, + id, ); let window_state: WindowState = @@ -707,12 +716,13 @@ pub fn build_user_interface<'a, A: Application>( renderer: &mut A::Renderer, size: Size, debug: &mut Debug, + id: window::Id, ) -> UserInterface<'a, A::Message, A::Renderer> where ::Theme: StyleSheet, { debug.view_started(); - let view = application.view(); + let view = application.view(id); debug.view_finished(); debug.layout_started(); @@ -735,6 +745,7 @@ pub fn update( debug: &mut Debug, messages: &mut Vec, windows: &HashMap, + window_ids: &HashMap, graphics_info: impl FnOnce() -> compositor::Information + Copy, ) where ::Theme: StyleSheet, @@ -757,6 +768,7 @@ pub fn update( proxy, debug, windows, + window_ids, graphics_info, ); } @@ -777,6 +789,7 @@ pub fn run_command( proxy: &mut winit::event_loop::EventLoopProxy>, debug: &mut Debug, windows: &HashMap, + window_ids: &HashMap, _graphics_info: impl FnOnce() -> compositor::Information + Copy, ) where A: Application, @@ -787,7 +800,9 @@ pub fn run_command( use iced_native::system; use iced_native::window; + // TODO(derezzedex) let window = windows.values().next().expect("No window found"); + let id = *window_ids.get(&window.id()).unwrap(); for action in command.actions() { match action { @@ -868,6 +883,7 @@ pub fn run_command( renderer, state.logical_size(), debug, + id, ); while let Some(mut operation) = current_operation.take() { @@ -933,6 +949,7 @@ where renderer, state.logical_size(), debug, + id, ); let _ = interfaces.insert(id, user_interface); -- cgit From 35331d0a41a53b8ff5c642b8274c7377ae6c6182 Mon Sep 17 00:00:00 2001 From: Richard Date: Tue, 26 Jul 2022 16:46:12 -0300 Subject: Allow closing the window from user code --- winit/src/multi_window.rs | 77 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 57 insertions(+), 20 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index dc00d737..3c720a69 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -39,6 +39,8 @@ pub enum Event { // (maybe we should also allow users to listen/react to those internal messages?) NewWindow(window::Id, settings::Window), /// TODO(derezzedex) + CloseWindow(window::Id), + /// TODO(derezzedex) WindowCreated(window::Id, winit::window::Window), } @@ -549,6 +551,27 @@ async fn run_instance( let _ = window_ids.insert(window.id(), id); let _ = windows.insert(id, window); } + Event::CloseWindow(id) => { + // TODO(derezzedex): log errors + if let Some(window) = windows.get(&id) { + if window_ids.remove(&window.id()).is_none() { + println!("Failed to remove from `window_ids`!"); + } + } + if states.remove(&id).is_none() { + println!("Failed to remove from `states`!") + } + if interfaces.remove(&id).is_none() { + println!("Failed to remove from `interfaces`!"); + } + if windows.remove(&id).is_none() { + println!("Failed to remove from `windows`!") + } + + if windows.is_empty() { + break 'main; + } + } Event::NewWindow(_, _) => unreachable!(), }, event::Event::RedrawRequested(id) => { @@ -651,29 +674,43 @@ async fn run_instance( window_id, } => { // dbg!(window_id); - let window = window_ids - .get(&window_id) - .and_then(|id| windows.get(id)) - .unwrap(); - let window_state = window_ids - .get(&window_id) - .and_then(|id| states.get_mut(id)) - .unwrap(); - - if requests_exit(&window_event, window_state.state.modifiers()) - && exit_on_close_request + if let Some(window) = + window_ids.get(&window_id).and_then(|id| windows.get(id)) { - break; - } + if let Some(window_state) = window_ids + .get(&window_id) + .and_then(|id| states.get_mut(id)) + { + if requests_exit( + &window_event, + window_state.state.modifiers(), + ) && exit_on_close_request + { + break; + } - window_state.state.update(window, &window_event, &mut debug); + window_state.state.update( + window, + &window_event, + &mut debug, + ); - if let Some(event) = conversion::window_event( - &window_event, - window_state.state.scale_factor(), - window_state.state.modifiers(), - ) { - events.push((window_ids.get(&window_id).cloned(), event)); + if let Some(event) = conversion::window_event( + &window_event, + window_state.state.scale_factor(), + window_state.state.modifiers(), + ) { + events.push(( + window_ids.get(&window_id).cloned(), + event, + )); + } + } else { + // TODO(derezzedex): log error + } + } else { + // TODO(derezzedex): log error + // println!("{:?}: {:?}", window_id, window_event); } } _ => {} -- cgit From dc86bd03733969033df7389c3d21e78ecc6291bb Mon Sep 17 00:00:00 2001 From: Richard Date: Wed, 27 Jul 2022 15:37:48 -0300 Subject: Introduce `close_requested` for `multi-window` --- winit/src/multi_window.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 3c720a69..6fbedc55 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -147,6 +147,9 @@ where fn should_exit(&self) -> bool { false } + + /// TODO(derezzedex) + fn close_requested(&self, window: window::Id) -> Self::Message; } /// Runs an [`Application`] with an executor, compositor, and the provided @@ -296,7 +299,7 @@ async fn run_instance( >, init_command: Command, mut windows: HashMap, - exit_on_close_request: bool, + _exit_on_close_request: bool, ) where A: Application + 'static, E: Executor + 'static, @@ -684,9 +687,13 @@ async fn run_instance( if requests_exit( &window_event, window_state.state.modifiers(), - ) && exit_on_close_request - { - break; + ) { + if let Some(id) = + window_ids.get(&window_id).cloned() + { + let message = application.close_requested(id); + messages.push(message); + } } window_state.state.update( -- cgit From 7f35256573db789fa6552c2cfd8aa16dde2a1a4d Mon Sep 17 00:00:00 2001 From: Richard Date: Thu, 15 Sep 2022 05:02:18 -0300 Subject: Split `Surface` and `Window` --- winit/src/multi_window.rs | 82 ++++++++++++++++++++--------------------------- 1 file changed, 35 insertions(+), 47 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 6fbedc55..3e7fecd0 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -318,6 +318,7 @@ async fn run_instance( .collect(); let mut states = HashMap::new(); + let mut surfaces = HashMap::new(); let mut interfaces = ManuallyDrop::new(HashMap::new()); for (&id, window) in windows.keys().zip(windows.values()) { @@ -342,20 +343,19 @@ async fn run_instance( id, ); - let window_state: WindowState = WindowState { surface, state }; - - let _ = states.insert(id, window_state); + let _ = states.insert(id, state); + let _ = surfaces.insert(id, surface); let _ = interfaces.insert(id, user_interface); } { // TODO(derezzedex) - let window_state = states.values().next().expect("No state found"); + let state = states.values().next().expect("No state found"); run_command( &application, &mut cache, - &window_state.state, + state, &mut renderer, init_command, &mut runtime, @@ -396,7 +396,7 @@ async fn run_instance( .collect(); let cursor_position = - states.get(&id).unwrap().state.cursor_position(); + states.get(&id).unwrap().cursor_position(); let window = windows.get(&id).unwrap(); if filtered.is_empty() && messages.is_empty() { @@ -430,7 +430,7 @@ async fn run_instance( user_interface::State::Outdated, ) { - let state = &mut states.get_mut(&id).unwrap().state; + let state = &mut states.get_mut(&id).unwrap(); let pure_states: HashMap<_, _> = ManuallyDrop::into_inner(interfaces) .drain() @@ -481,7 +481,7 @@ async fn run_instance( debug.draw_started(); let new_mouse_interaction = { let user_interface = interfaces.get_mut(&id).unwrap(); - let state = &states.get(&id).unwrap().state; + let state = states.get(&id).unwrap(); user_interface.draw( &mut renderer, @@ -546,10 +546,8 @@ async fn run_instance( id, ); - let window_state: WindowState = - WindowState { surface, state }; - - let _ = states.insert(id, window_state); + let _ = states.insert(id, state); + let _ = surfaces.insert(id, surface); let _ = interfaces.insert(id, user_interface); let _ = window_ids.insert(window.id(), id); let _ = windows.insert(id, window); @@ -570,6 +568,9 @@ async fn run_instance( if windows.remove(&id).is_none() { println!("Failed to remove from `windows`!") } + if surfaces.remove(&id).is_none() { + println!("Failed to remove from `surfaces`!") + } if windows.is_empty() { break 'main; @@ -578,11 +579,15 @@ async fn run_instance( Event::NewWindow(_, _) => unreachable!(), }, event::Event::RedrawRequested(id) => { - let window_state = window_ids + let state = window_ids .get(&id) .and_then(|id| states.get_mut(id)) .unwrap(); - let physical_size = window_state.state.physical_size(); + let surface = window_ids + .get(&id) + .and_then(|id| surfaces.get_mut(id)) + .unwrap(); + let physical_size = state.physical_size(); if physical_size.width == 0 || physical_size.height == 0 { continue; @@ -590,13 +595,13 @@ async fn run_instance( debug.render_started(); - if window_state.state.viewport_changed() { + if state.viewport_changed() { let mut user_interface = window_ids .get(&id) .and_then(|id| interfaces.remove(id)) .unwrap(); - let logical_size = window_state.state.logical_size(); + let logical_size = state.logical_size(); debug.layout_started(); user_interface = @@ -605,7 +610,7 @@ async fn run_instance( debug.draw_started(); let new_mouse_interaction = { - let state = &window_state.state; + let state = &state; user_interface.draw( &mut renderer, @@ -634,7 +639,7 @@ async fn run_instance( .insert(*window_ids.get(&id).unwrap(), user_interface); compositor.configure_surface( - &mut window_state.surface, + surface, physical_size.width, physical_size.height, ); @@ -642,9 +647,9 @@ async fn run_instance( match compositor.present( &mut renderer, - &mut window_state.surface, - window_state.state.viewport(), - window_state.state.background_color(), + surface, + state.viewport(), + state.background_color(), &debug.overlay(), ) { Ok(()) => { @@ -680,14 +685,11 @@ async fn run_instance( if let Some(window) = window_ids.get(&window_id).and_then(|id| windows.get(id)) { - if let Some(window_state) = window_ids + if let Some(state) = window_ids .get(&window_id) .and_then(|id| states.get_mut(id)) { - if requests_exit( - &window_event, - window_state.state.modifiers(), - ) { + if requests_exit(&window_event, state.modifiers()) { if let Some(id) = window_ids.get(&window_id).cloned() { @@ -696,16 +698,12 @@ async fn run_instance( } } - window_state.state.update( - window, - &window_event, - &mut debug, - ); + state.update(window, &window_event, &mut debug); if let Some(event) = conversion::window_event( &window_event, - window_state.state.scale_factor(), - window_state.state.modifiers(), + state.scale_factor(), + state.modifiers(), ) { events.push(( window_ids.get(&window_id).cloned(), @@ -953,21 +951,12 @@ pub fn run_command( } } -struct WindowState -where - A: Application, - C: iced_graphics::window::Compositor, - ::Theme: StyleSheet, -{ - surface: ::Surface, - state: State, -} - -fn build_user_interfaces<'a, A, C>( +/// TODO(derezzedex) +pub fn build_user_interfaces<'a, A>( application: &'a A, renderer: &mut A::Renderer, debug: &mut Debug, - states: &HashMap>, + states: &HashMap>, mut pure_states: HashMap, ) -> HashMap< window::Id, @@ -979,13 +968,12 @@ fn build_user_interfaces<'a, A, C>( > where A: Application + 'static, - C: iced_graphics::window::Compositor + 'static, ::Theme: StyleSheet, { let mut interfaces = HashMap::new(); for (id, pure_state) in pure_states.drain() { - let state = &states.get(&id).unwrap().state; + let state = &states.get(&id).unwrap(); let user_interface = build_user_interface( application, -- cgit From 0ad53a3d5c7b5fb5785a64102ee1ad7df9a5fb2b Mon Sep 17 00:00:00 2001 From: Richard Date: Mon, 19 Sep 2022 20:59:37 -0300 Subject: add `window::Id` to `Event` and `Action` --- winit/src/multi_window.rs | 77 ++++++++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 41 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 3e7fecd0..9f46b88d 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -363,7 +363,6 @@ async fn run_instance( &mut proxy, &mut debug, &windows, - &window_ids, || compositor.fetch_information(), ); } @@ -456,7 +455,6 @@ async fn run_instance( &mut debug, &mut messages, &windows, - &window_ids, || compositor.fetch_information(), ); @@ -701,6 +699,7 @@ async fn run_instance( state.update(window, &window_event, &mut debug); if let Some(event) = conversion::window_event( + *window_ids.get(&window_id).unwrap(), &window_event, state.scale_factor(), state.modifiers(), @@ -787,7 +786,6 @@ pub fn update( debug: &mut Debug, messages: &mut Vec, windows: &HashMap, - window_ids: &HashMap, graphics_info: impl FnOnce() -> compositor::Information + Copy, ) where ::Theme: StyleSheet, @@ -810,7 +808,6 @@ pub fn update( proxy, debug, windows, - window_ids, graphics_info, ); } @@ -831,7 +828,6 @@ pub fn run_command( proxy: &mut winit::event_loop::EventLoopProxy>, debug: &mut Debug, windows: &HashMap, - window_ids: &HashMap, _graphics_info: impl FnOnce() -> compositor::Information + Copy, ) where A: Application, @@ -842,10 +838,6 @@ pub fn run_command( use iced_native::system; use iced_native::window; - // TODO(derezzedex) - let window = windows.values().next().expect("No window found"); - let id = *window_ids.get(&window.id()).unwrap(); - for action in command.actions() { match action { command::Action::Future(future) => { @@ -863,38 +855,41 @@ pub fn run_command( clipboard.write(contents); } }, - command::Action::Window(action) => match action { - window::Action::Resize { width, height } => { - window.set_inner_size(winit::dpi::LogicalSize { - width, - height, - }); - } - window::Action::Move { x, y } => { - window.set_outer_position(winit::dpi::LogicalPosition { - x, - y, - }); - } - window::Action::SetMode(mode) => { - window.set_visible(conversion::visible(mode)); - window.set_fullscreen(conversion::fullscreen( - window.primary_monitor(), - mode, - )); - } - window::Action::FetchMode(tag) => { - let mode = if window.is_visible().unwrap_or(true) { - conversion::mode(window.fullscreen()) - } else { - window::Mode::Hidden - }; - - proxy - .send_event(Event::Application(tag(mode))) - .expect("Send message to event loop"); + command::Action::Window(id, action) => { + let window = windows.get(&id).expect("No window found"); + + match action { + window::Action::Resize { width, height } => { + window.set_inner_size(winit::dpi::LogicalSize { + width, + height, + }); + } + window::Action::Move { x, y } => { + window.set_outer_position( + winit::dpi::LogicalPosition { x, y }, + ); + } + window::Action::SetMode(mode) => { + window.set_visible(conversion::visible(mode)); + window.set_fullscreen(conversion::fullscreen( + window.primary_monitor(), + mode, + )); + } + window::Action::FetchMode(tag) => { + let mode = if window.is_visible().unwrap_or(true) { + conversion::mode(window.fullscreen()) + } else { + window::Mode::Hidden + }; + + proxy + .send_event(Event::Application(tag(mode))) + .expect("Send message to event loop"); + } } - }, + } command::Action::System(action) => match action { system::Action::QueryInformation(_tag) => { #[cfg(feature = "system")] @@ -925,7 +920,7 @@ pub fn run_command( renderer, state.logical_size(), debug, - id, + window::Id::MAIN, // TODO(derezzedex): run the operation on every widget tree ); while let Some(mut operation) = current_operation.take() { -- cgit From f93fa0254329ebddca21ea1a79bd8ee6d8b4bdaf Mon Sep 17 00:00:00 2001 From: Richard Date: Wed, 19 Oct 2022 23:33:20 -0300 Subject: introduce `window::spawn` and `window::close` --- winit/src/multi_window.rs | 79 +++++++++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 34 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 9f46b88d..1d71d801 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -855,41 +855,52 @@ pub fn run_command( clipboard.write(contents); } }, - command::Action::Window(id, action) => { - let window = windows.get(&id).expect("No window found"); - - match action { - window::Action::Resize { width, height } => { - window.set_inner_size(winit::dpi::LogicalSize { - width, - height, - }); - } - window::Action::Move { x, y } => { - window.set_outer_position( - winit::dpi::LogicalPosition { x, y }, - ); - } - window::Action::SetMode(mode) => { - window.set_visible(conversion::visible(mode)); - window.set_fullscreen(conversion::fullscreen( - window.primary_monitor(), - mode, - )); - } - window::Action::FetchMode(tag) => { - let mode = if window.is_visible().unwrap_or(true) { - conversion::mode(window.fullscreen()) - } else { - window::Mode::Hidden - }; - - proxy - .send_event(Event::Application(tag(mode))) - .expect("Send message to event loop"); - } + command::Action::Window(id, action) => match action { + window::Action::Spawn { settings } => { + proxy + .send_event(Event::NewWindow(id, settings.into())) + .expect("Send message to event loop"); } - } + window::Action::Close => { + proxy + .send_event(Event::CloseWindow(id)) + .expect("Send message to event loop"); + } + window::Action::Resize { width, height } => { + let window = windows.get(&id).expect("No window found"); + window.set_inner_size(winit::dpi::LogicalSize { + width, + height, + }); + } + window::Action::Move { x, y } => { + let window = windows.get(&id).expect("No window found"); + window.set_outer_position(winit::dpi::LogicalPosition { + x, + y, + }); + } + window::Action::SetMode(mode) => { + let window = windows.get(&id).expect("No window found"); + window.set_visible(conversion::visible(mode)); + window.set_fullscreen(conversion::fullscreen( + window.primary_monitor(), + mode, + )); + } + window::Action::FetchMode(tag) => { + let window = windows.get(&id).expect("No window found"); + let mode = if window.is_visible().unwrap_or(true) { + conversion::mode(window.fullscreen()) + } else { + window::Mode::Hidden + }; + + proxy + .send_event(Event::Application(tag(mode))) + .expect("Send message to event loop"); + } + }, command::Action::System(action) => match action { system::Action::QueryInformation(_tag) => { #[cfg(feature = "system")] -- cgit From 5e4e410b18eb744cf70ae1f18b9ef08611f59150 Mon Sep 17 00:00:00 2001 From: Richard Date: Thu, 3 Nov 2022 14:53:05 -0300 Subject: remove `windows` method (use commands instead) --- winit/src/multi_window.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 1d71d801..c0c233c5 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -68,9 +68,6 @@ where /// The type of __messages__ your [`Program`] will produce. type Message: std::fmt::Debug + Send; - /// TODO(derezzedex) - fn windows(&self) -> Vec<(window::Id, settings::Window)>; - /// Handles a __message__ and updates the state of the [`Program`]. /// /// This is where you define your __update logic__. All the __messages__, @@ -459,7 +456,7 @@ async fn run_instance( ); // Update window - state.synchronize(&application, &windows, &proxy); + state.synchronize(&application, &windows); let should_exit = application.should_exit(); -- cgit From 942f1c91afb8257e289af8d0c229f74819f68361 Mon Sep 17 00:00:00 2001 From: bungoboingo Date: Mon, 2 Jan 2023 10:58:07 -0800 Subject: merged in iced master --- winit/src/multi_window.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index c0c233c5..0a2f71ad 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -863,6 +863,10 @@ pub fn run_command( .send_event(Event::CloseWindow(id)) .expect("Send message to event loop"); } + window::Action::Drag => { + let window = windows.get(&id).expect("No window found"); + let _res = window.drag_window(); + } window::Action::Resize { width, height } => { let window = windows.get(&id).expect("No window found"); window.set_inner_size(winit::dpi::LogicalSize { @@ -897,6 +901,22 @@ pub fn run_command( .send_event(Event::Application(tag(mode))) .expect("Send message to event loop"); } + window::Action::Maximize(value) => { + let window = windows.get(&id).expect("No window found!"); + window.set_maximized(value); + } + window::Action::Minimize(value) => { + let window = windows.get(&id).expect("No window found!"); + window.set_minimized(value); + } + window::Action::ToggleMaximize => { + let window = windows.get(&id).expect("No window found!"); + window.set_maximized(!window.is_maximized()); + } + window::Action::ToggleDecorations => { + let window = windows.get(&id).expect("No window found!"); + window.set_decorations(!window.is_decorated()); + } }, command::Action::System(action) => match action { system::Action::QueryInformation(_tag) => { -- cgit From f43419d4752fe18065c0e1b7c2a26e65b9d6e253 Mon Sep 17 00:00:00 2001 From: bungoboingo Date: Mon, 2 Jan 2023 18:14:31 -0800 Subject: Fixed issue with window ID on winit --- winit/src/multi_window.rs | 110 +++++++++++++++++++++++++--------------------- 1 file changed, 59 insertions(+), 51 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 0a2f71ad..7d8bbc39 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -33,7 +33,6 @@ use std::mem::ManuallyDrop; pub enum Event { /// An [`Application`] generated message Application(Message), - /// TODO(derezzedex) // Create a wrapper variant of `window::Event` type instead // (maybe we should also allow users to listen/react to those internal messages?) @@ -197,7 +196,8 @@ where .map_err(Error::WindowCreationFailed)?; let windows: HashMap = - HashMap::from([(window::Id::new(0usize), window)]); + HashMap::from([(window::Id::MAIN, window)]); + let window = windows.values().next().expect("No window found"); #[cfg(target_arch = "wasm32")] @@ -515,64 +515,72 @@ async fn run_instance( ), )); } - event::Event::UserEvent(event) => match event { - Event::Application(message) => { - messages.push(message); - } - Event::WindowCreated(id, window) => { - let mut surface = compositor.create_surface(&window); + event::Event::UserEvent(event) => { + match event { + Event::Application(message) => { + messages.push(message); + } + Event::WindowCreated(id, window) => { + let mut surface = compositor.create_surface(&window); - let state = State::new(&application, &window); + let state = State::new(&application, &window); - let physical_size = state.physical_size(); + let physical_size = state.physical_size(); - compositor.configure_surface( - &mut surface, - physical_size.width, - physical_size.height, - ); + compositor.configure_surface( + &mut surface, + physical_size.width, + physical_size.height, + ); - let user_interface = build_user_interface( - &application, - user_interface::Cache::default(), - &mut renderer, - state.logical_size(), - &mut debug, - id, - ); + let user_interface = build_user_interface( + &application, + user_interface::Cache::default(), + &mut renderer, + state.logical_size(), + &mut debug, + id, + ); - let _ = states.insert(id, state); - let _ = surfaces.insert(id, surface); - let _ = interfaces.insert(id, user_interface); - let _ = window_ids.insert(window.id(), id); - let _ = windows.insert(id, window); - } - Event::CloseWindow(id) => { - // TODO(derezzedex): log errors - if let Some(window) = windows.get(&id) { - if window_ids.remove(&window.id()).is_none() { - println!("Failed to remove from `window_ids`!"); - } - } - if states.remove(&id).is_none() { - println!("Failed to remove from `states`!") - } - if interfaces.remove(&id).is_none() { - println!("Failed to remove from `interfaces`!"); - } - if windows.remove(&id).is_none() { - println!("Failed to remove from `windows`!") - } - if surfaces.remove(&id).is_none() { - println!("Failed to remove from `surfaces`!") + let _ = states.insert(id, state); + let _ = surfaces.insert(id, surface); + let _ = interfaces.insert(id, user_interface); + let _ = window_ids.insert(window.id(), id); + let _ = windows.insert(id, window); } + Event::CloseWindow(id) => { + println!("Closing window {:?}. Total: {}", id, windows.len()); - if windows.is_empty() { - break 'main; + if let Some(window) = windows.get(&id) { + if window_ids.remove(&window.id()).is_none() { + log::error!("Failed to remove window with id {:?} from window_ids.", window.id()); + } + } else { + log::error!("Could not find window with id {:?} in windows.", id); + } + if states.remove(&id).is_none() { + log::error!("Failed to remove window {:?} from states.", id); + } + if interfaces.remove(&id).is_none() { + log::error!("Failed to remove window {:?} from interfaces.", id); + } + if windows.remove(&id).is_none() { + log::error!("Failed to remove window {:?} from windows.", id); + } + if surfaces.remove(&id).is_none() { + log::error!("Failed to remove window {:?} from surfaces.", id); + } + + if windows.is_empty() { + log::info!("All windows are closed. Terminating program."); + break 'main; + } else { + log::info!("Remaining windows: {:?}", windows.len()); + } } + Event::NewWindow(_, _) => unreachable!(), } - Event::NewWindow(_, _) => unreachable!(), - }, + } event::Event::RedrawRequested(id) => { let state = window_ids .get(&id) -- cgit From 1944e98f82b7efd5b268e04ba5ced065e55a218e Mon Sep 17 00:00:00 2001 From: Bingus Date: Mon, 2 Jan 2023 21:06:59 -0800 Subject: Fix multi-window example for Glutin on MacOS --- winit/src/multi_window.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 7d8bbc39..43455148 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -320,6 +320,7 @@ async fn run_instance( for (&id, window) in windows.keys().zip(windows.values()) { let mut surface = compositor.create_surface(window); + println!("Creating surface for window: {:?}", window); let state = State::new(&application, window); -- cgit From ec41918ec40bddaba81235372f1566da59fd09f2 Mon Sep 17 00:00:00 2001 From: bungoboingo Date: Thu, 5 Jan 2023 15:26:28 -0800 Subject: Implemented window title update functionality for multiwindow. --- winit/src/multi_window.rs | 190 +++++++++++++++++++++++++++------------------- 1 file changed, 112 insertions(+), 78 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 43455148..6a2bdca9 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -22,6 +22,7 @@ use iced_native::user_interface::{self, UserInterface}; pub use iced_native::application::{Appearance, StyleSheet}; +use iced_native::window::Action; use std::collections::HashMap; use std::mem::ManuallyDrop; @@ -36,7 +37,14 @@ pub enum Event { /// TODO(derezzedex) // Create a wrapper variant of `window::Event` type instead // (maybe we should also allow users to listen/react to those internal messages?) - NewWindow(window::Id, settings::Window), + NewWindow { + /// The [window::Id] of the newly spawned [`Window`]. + id: window::Id, + /// The [settings::Window] of the newly spawned [`Window`]. + settings: settings::Window, + /// The title of the newly spawned [`Window`]. + title: String, + }, /// TODO(derezzedex) CloseWindow(window::Id), /// TODO(derezzedex) @@ -95,11 +103,11 @@ where /// load state from a file, perform an initial HTTP request, etc. fn new(flags: Self::Flags) -> (Self, Command); - /// Returns the current title of the [`Application`]. + /// Returns the current title of the current [`Application`] window. /// /// This title can be dynamic! The runtime will automatically update the /// title of your application when necessary. - fn title(&self) -> String; + fn title(&self, window_id: window::Id) -> String; /// Returns the current [`Theme`] of the [`Application`]. fn theme(&self) -> ::Theme; @@ -144,7 +152,7 @@ where false } - /// TODO(derezzedex) + /// Requests that the [`window`] be closed. fn close_requested(&self, window: window::Id) -> Self::Message; } @@ -184,7 +192,7 @@ where }; let builder = settings.window.into_builder( - &application.title(), + &application.title(window::Id::MAIN), event_loop.primary_monitor(), settings.id, ); @@ -253,14 +261,13 @@ where event: winit::event::WindowEvent::Resized(*new_inner_size), window_id, }), - winit::event::Event::UserEvent(Event::NewWindow(id, settings)) => { - // TODO(derezzedex) + winit::event::Event::UserEvent(Event::NewWindow { + id, + settings, + title, + }) => { let window = settings - .into_builder( - "fix window title", - event_loop.primary_monitor(), - None, - ) + .into_builder(&title, event_loop.primary_monitor(), None) .build(event_loop) .expect("Failed to build window"); @@ -320,10 +327,7 @@ async fn run_instance( for (&id, window) in windows.keys().zip(windows.values()) { let mut surface = compositor.create_surface(window); - println!("Creating surface for window: {:?}", window); - - let state = State::new(&application, window); - + let state = State::new(&application, id, window); let physical_size = state.physical_size(); compositor.configure_surface( @@ -457,7 +461,11 @@ async fn run_instance( ); // Update window - state.synchronize(&application, &windows); + state.synchronize( + &application, + id, + windows.get(&id).expect("No window found with ID."), + ); let should_exit = application.should_exit(); @@ -516,72 +524,85 @@ async fn run_instance( ), )); } - event::Event::UserEvent(event) => { - match event { - Event::Application(message) => { - messages.push(message); - } - Event::WindowCreated(id, window) => { - let mut surface = compositor.create_surface(&window); - - let state = State::new(&application, &window); + event::Event::UserEvent(event) => match event { + Event::Application(message) => { + messages.push(message); + } + Event::WindowCreated(id, window) => { + let mut surface = compositor.create_surface(&window); - let physical_size = state.physical_size(); + let state = State::new(&application, id, &window); - compositor.configure_surface( - &mut surface, - physical_size.width, - physical_size.height, - ); + let physical_size = state.physical_size(); - let user_interface = build_user_interface( - &application, - user_interface::Cache::default(), - &mut renderer, - state.logical_size(), - &mut debug, - id, - ); + compositor.configure_surface( + &mut surface, + physical_size.width, + physical_size.height, + ); - let _ = states.insert(id, state); - let _ = surfaces.insert(id, surface); - let _ = interfaces.insert(id, user_interface); - let _ = window_ids.insert(window.id(), id); - let _ = windows.insert(id, window); - } - Event::CloseWindow(id) => { - println!("Closing window {:?}. Total: {}", id, windows.len()); + let user_interface = build_user_interface( + &application, + user_interface::Cache::default(), + &mut renderer, + state.logical_size(), + &mut debug, + id, + ); - if let Some(window) = windows.get(&id) { - if window_ids.remove(&window.id()).is_none() { - log::error!("Failed to remove window with id {:?} from window_ids.", window.id()); - } - } else { - log::error!("Could not find window with id {:?} in windows.", id); - } - if states.remove(&id).is_none() { - log::error!("Failed to remove window {:?} from states.", id); - } - if interfaces.remove(&id).is_none() { - log::error!("Failed to remove window {:?} from interfaces.", id); - } - if windows.remove(&id).is_none() { - log::error!("Failed to remove window {:?} from windows.", id); - } - if surfaces.remove(&id).is_none() { - log::error!("Failed to remove window {:?} from surfaces.", id); + let _ = states.insert(id, state); + let _ = surfaces.insert(id, surface); + let _ = interfaces.insert(id, user_interface); + let _ = window_ids.insert(window.id(), id); + let _ = windows.insert(id, window); + } + Event::CloseWindow(id) => { + if let Some(window) = windows.get(&id) { + if window_ids.remove(&window.id()).is_none() { + log::error!("Failed to remove window with id {:?} from window_ids.", window.id()); } + } else { + log::error!( + "Could not find window with id {:?} in windows.", + id + ); + } + if states.remove(&id).is_none() { + log::error!( + "Failed to remove window {:?} from states.", + id + ); + } + if interfaces.remove(&id).is_none() { + log::error!( + "Failed to remove window {:?} from interfaces.", + id + ); + } + if windows.remove(&id).is_none() { + log::error!( + "Failed to remove window {:?} from windows.", + id + ); + } + if surfaces.remove(&id).is_none() { + log::error!( + "Failed to remove window {:?} from surfaces.", + id + ); + } - if windows.is_empty() { - log::info!("All windows are closed. Terminating program."); - break 'main; - } else { - log::info!("Remaining windows: {:?}", windows.len()); - } + if windows.is_empty() { + log::info!( + "All windows are closed. Terminating program." + ); + break 'main; + } else { + log::info!("Remaining windows: {:?}", windows.len()); } - Event::NewWindow(_, _) => unreachable!(), } - } + Event::NewWindow { .. } => unreachable!(), + }, event::Event::RedrawRequested(id) => { let state = window_ids .get(&id) @@ -716,11 +737,10 @@ async fn run_instance( )); } } else { - // TODO(derezzedex): log error + log::error!("No window state found for id: {:?}", window_id); } } else { - // TODO(derezzedex): log error - // println!("{:?}: {:?}", window_id, window_event); + log::error!("No window found with id: {:?}", window_id); } } _ => {} @@ -864,7 +884,11 @@ pub fn run_command( command::Action::Window(id, action) => match action { window::Action::Spawn { settings } => { proxy - .send_event(Event::NewWindow(id, settings.into())) + .send_event(Event::NewWindow { + id, + settings: settings.into(), + title: application.title(id), + }) .expect("Send message to event loop"); } window::Action::Close => { @@ -926,6 +950,16 @@ pub fn run_command( let window = windows.get(&id).expect("No window found!"); window.set_decorations(!window.is_decorated()); } + window::Action::RequestUserAttention(attention_type) => { + let window = windows.get(&id).expect("No window found!"); + window.request_user_attention( + attention_type.map(conversion::user_attention), + ); + } + Action::GainFocus => { + let window = windows.get(&id).expect("No window found!"); + window.focus_window(); + } }, command::Action::System(action) => match action { system::Action::QueryInformation(_tag) => { -- cgit From 790fa3e7a01a790aa3f07083fe9abf6b68fa7ba1 Mon Sep 17 00:00:00 2001 From: Bingus Date: Fri, 13 Jan 2023 11:56:28 -0800 Subject: Added tracing to multi_window applications --- winit/src/multi_window.rs | 61 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 12 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 6a2bdca9..d7378a1d 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -26,6 +26,11 @@ use iced_native::window::Action; use std::collections::HashMap; use std::mem::ManuallyDrop; +#[cfg(feature = "trace")] +pub use crate::Profiler; +#[cfg(feature = "trace")] +use tracing::{info_span, instrument::Instrument}; + /// TODO(derezzedex) // This is the an wrapper around the `Application::Message` associate type // to allows the `shell` to create internal messages, while still having @@ -172,9 +177,15 @@ where use futures::Future; use winit::event_loop::EventLoopBuilder; + #[cfg(feature = "trace")] + let _guard = Profiler::init(); + let mut debug = Debug::new(); debug.startup_started(); + #[cfg(feature = "trace")] + let _ = info_span!("Application", "RUN").entered(); + let event_loop = EventLoopBuilder::with_user_event().build(); let proxy = event_loop.create_proxy(); @@ -227,18 +238,26 @@ where let (mut sender, receiver) = mpsc::unbounded(); - let mut instance = Box::pin(run_instance::( - application, - compositor, - renderer, - runtime, - proxy, - debug, - receiver, - init_command, - windows, - settings.exit_on_close_request, - )); + let mut instance = Box::pin({ + let run_instance = run_instance::( + application, + compositor, + renderer, + runtime, + proxy, + debug, + receiver, + init_command, + windows, + settings.exit_on_close_request, + ); + + #[cfg(feature = "trace")] + let run_instance = + run_instance.instrument(info_span!("Application", "LOOP")); + + run_instance + }); let mut context = task::Context::from_waker(task::noop_waker_ref()); @@ -604,6 +623,9 @@ async fn run_instance( Event::NewWindow { .. } => unreachable!(), }, event::Event::RedrawRequested(id) => { + #[cfg(feature = "trace")] + let _ = info_span!("Application", "FRAME").entered(); + let state = window_ids .get(&id) .and_then(|id| states.get_mut(id)) @@ -788,12 +810,22 @@ pub fn build_user_interface<'a, A: Application>( where ::Theme: StyleSheet, { + #[cfg(feature = "trace")] + let view_span = info_span!("Application", "VIEW").entered(); + debug.view_started(); let view = application.view(id); + + #[cfg(feature = "trace")] + let _ = view_span.exit(); debug.view_finished(); + #[cfg(feature = "trace")] + let layout_span = info_span!("Application", "LAYOUT").entered(); debug.layout_started(); let user_interface = UserInterface::build(view, size, cache, renderer); + #[cfg(feature = "trace")] + let _ = layout_span.exit(); debug.layout_finished(); user_interface @@ -817,10 +849,15 @@ pub fn update( ::Theme: StyleSheet, { for message in messages.drain(..) { + #[cfg(feature = "trace")] + let update_span = info_span!("Application", "UPDATE").entered(); + debug.log_message(&message); debug.update_started(); let command = runtime.enter(|| application.update(message)); + #[cfg(feature = "trace")] + let _ = update_span.exit(); debug.update_finished(); run_command( -- cgit From 0a643287deece9234b64cc843a9f6ae3e6e4806e Mon Sep 17 00:00:00 2001 From: Bingus Date: Wed, 18 Jan 2023 17:04:11 -0800 Subject: Added window::Id to multi_window application's scale_factor --- winit/src/multi_window.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index d7378a1d..ad65e6a5 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -146,7 +146,7 @@ where /// while a scale factor of `0.5` will shrink them to half their size. /// /// By default, it returns `1.0`. - fn scale_factor(&self) -> f64 { + fn scale_factor(&self, window: window::Id) -> f64 { 1.0 } -- cgit From 367fea5dc8e94584334e880970126b40a046bfa6 Mon Sep 17 00:00:00 2001 From: Bingus Date: Wed, 15 Feb 2023 11:28:36 -0800 Subject: Redraw request events for multiwindow. --- winit/src/multi_window.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index ad65e6a5..430e6706 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -25,6 +25,7 @@ pub use iced_native::application::{Appearance, StyleSheet}; use iced_native::window::Action; use std::collections::HashMap; use std::mem::ManuallyDrop; +use std::time::Instant; #[cfg(feature = "trace")] pub use crate::Profiler; @@ -402,7 +403,7 @@ async fn run_instance( let (filtered, remaining): (Vec<_>, Vec<_>) = events.iter().cloned().partition( |(window_id, _event): &( - Option, + Option, iced_native::event::Event, )| { *window_id == Some(id) || *window_id == None @@ -410,10 +411,14 @@ async fn run_instance( ); events.retain(|el| remaining.contains(el)); - let filtered: Vec<_> = filtered + let mut filtered: Vec<_> = filtered .into_iter() .map(|(_id, event)| event) .collect(); + filtered.push(iced_native::Event::Window( + id, + window::Event::RedrawRequested(Instant::now()), + )); let cursor_position = states.get(&id).unwrap().cursor_position(); @@ -450,7 +455,7 @@ async fn run_instance( user_interface::State::Outdated, ) { - let state = &mut states.get_mut(&id).unwrap(); + let state = states.get_mut(&id).unwrap(); let pure_states: HashMap<_, _> = ManuallyDrop::into_inner(interfaces) .drain() -- cgit From 64e0e817c27d720dc954ee94de58ded35b3f9f9a Mon Sep 17 00:00:00 2001 From: Bingus Date: Wed, 15 Feb 2023 14:31:16 -0800 Subject: Widget operations for multi-window. --- winit/src/multi_window.rs | 117 ++++++++++++++++++++++++---------------------- 1 file changed, 62 insertions(+), 55 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 430e6706..bd7e9d44 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -4,12 +4,12 @@ mod state; pub use state::State; use crate::clipboard::{self, Clipboard}; -use crate::conversion; use crate::mouse; use crate::renderer; use crate::settings; use crate::widget::operation; use crate::window; +use crate::{conversion, multi_window}; use crate::{ Command, Debug, Element, Error, Executor, Proxy, Renderer, Runtime, Settings, Size, Subscription, @@ -254,7 +254,7 @@ where ); #[cfg(feature = "trace")] - let run_instance = + let run_instance = run_instance.instrument(info_span!("Application", "LOOP")); run_instance @@ -335,7 +335,7 @@ async fn run_instance( let mut clipboard = Clipboard::connect(windows.values().next().expect("No window found")); - let mut cache = user_interface::Cache::default(); + let mut caches = HashMap::new(); let mut window_ids: HashMap<_, _> = windows .iter() .map(|(&id, window)| (window.id(), id)) @@ -368,26 +368,23 @@ async fn run_instance( let _ = states.insert(id, state); let _ = surfaces.insert(id, surface); let _ = interfaces.insert(id, user_interface); + let _ = caches.insert(id, user_interface::Cache::default()); } - { - // TODO(derezzedex) - let state = states.values().next().expect("No state found"); + run_command( + &application, + &mut caches, + &states, + &mut renderer, + init_command, + &mut runtime, + &mut clipboard, + &mut proxy, + &mut debug, + &windows, + || compositor.fetch_information(), + ); - run_command( - &application, - &mut cache, - state, - &mut renderer, - init_command, - &mut runtime, - &mut clipboard, - &mut proxy, - &mut debug, - &windows, - || compositor.fetch_information(), - ); - } runtime.track(application.subscription().map(Event::Application)); let mut mouse_interaction = mouse::Interaction::default(); @@ -455,8 +452,7 @@ async fn run_instance( user_interface::State::Outdated, ) { - let state = states.get_mut(&id).unwrap(); - let pure_states: HashMap<_, _> = + let user_interfaces: HashMap<_, _> = ManuallyDrop::into_inner(interfaces) .drain() .map( @@ -472,8 +468,8 @@ async fn run_instance( // Update application update( &mut application, - &mut cache, - state, + &mut caches, + &states, &mut renderer, &mut runtime, &mut clipboard, @@ -485,7 +481,7 @@ async fn run_instance( ); // Update window - state.synchronize( + states.get_mut(&id).unwrap().synchronize( &application, id, windows.get(&id).expect("No window found with ID."), @@ -498,7 +494,7 @@ async fn run_instance( &mut renderer, &mut debug, &states, - pure_states, + user_interfaces, )); if should_exit { @@ -579,6 +575,7 @@ async fn run_instance( let _ = interfaces.insert(id, user_interface); let _ = window_ids.insert(window.id(), id); let _ = windows.insert(id, window); + let _ = caches.insert(id, user_interface::Cache::default()); } Event::CloseWindow(id) => { if let Some(window) = windows.get(&id) { @@ -764,7 +761,10 @@ async fn run_instance( )); } } else { - log::error!("No window state found for id: {:?}", window_id); + log::error!( + "No window state found for id: {:?}", + window_id + ); } } else { log::error!("No window found with id: {:?}", window_id); @@ -840,8 +840,8 @@ where /// resulting [`Command`], and tracking its [`Subscription`]. pub fn update( application: &mut A, - cache: &mut user_interface::Cache, - state: &State, + caches: &mut HashMap, + states: &HashMap>, renderer: &mut A::Renderer, runtime: &mut Runtime>, Event>, clipboard: &mut Clipboard, @@ -851,6 +851,7 @@ pub fn update( windows: &HashMap, graphics_info: impl FnOnce() -> compositor::Information + Copy, ) where + A: Application + 'static, ::Theme: StyleSheet, { for message in messages.drain(..) { @@ -867,8 +868,8 @@ pub fn update( run_command( application, - cache, - state, + caches, + states, renderer, command, runtime, @@ -887,8 +888,8 @@ pub fn update( /// Runs the actions of a [`Command`]. pub fn run_command( application: &A, - cache: &mut user_interface::Cache, - state: &State, + caches: &mut HashMap, + states: &HashMap>, renderer: &mut A::Renderer, command: Command, runtime: &mut Runtime>, Event>, @@ -898,7 +899,7 @@ pub fn run_command( windows: &HashMap, _graphics_info: impl FnOnce() -> compositor::Information + Copy, ) where - A: Application, + A: Application + 'static, E: Executor, ::Theme: StyleSheet, { @@ -1024,36 +1025,42 @@ pub fn run_command( } }, command::Action::Widget(action) => { - let mut current_cache = std::mem::take(cache); + let mut current_caches = std::mem::take(caches); let mut current_operation = Some(action.into_operation()); - let mut user_interface = build_user_interface( + let mut user_interfaces = build_user_interfaces( application, - current_cache, renderer, - state.logical_size(), debug, - window::Id::MAIN, // TODO(derezzedex): run the operation on every widget tree + states, + current_caches, ); while let Some(mut operation) = current_operation.take() { - user_interface.operate(renderer, operation.as_mut()); - - match operation.finish() { - operation::Outcome::None => {} - operation::Outcome::Some(message) => { - proxy - .send_event(Event::Application(message)) - .expect("Send message to event loop"); - } - operation::Outcome::Chain(next) => { - current_operation = Some(next); + for user_interface in user_interfaces.values_mut() { + user_interface.operate(renderer, operation.as_mut()); + + match operation.finish() { + operation::Outcome::None => {} + operation::Outcome::Some(message) => { + proxy + .send_event(Event::Application(message)) + .expect("Send message to event loop"); + } + operation::Outcome::Chain(next) => { + current_operation = Some(next); + } } } } - current_cache = user_interface.into_cache(); - *cache = current_cache; + let user_interfaces: HashMap<_, _> = user_interfaces + .drain() + .map(|(id, interface)| (id, interface.into_cache())) + .collect(); + + current_caches = user_interfaces; + *caches = current_caches; } } } @@ -1065,7 +1072,7 @@ pub fn build_user_interfaces<'a, A>( renderer: &mut A::Renderer, debug: &mut Debug, states: &HashMap>, - mut pure_states: HashMap, + mut cached_user_interfaces: HashMap, ) -> HashMap< window::Id, UserInterface< @@ -1080,12 +1087,12 @@ where { let mut interfaces = HashMap::new(); - for (id, pure_state) in pure_states.drain() { + for (id, cache) in cached_user_interfaces.drain() { let state = &states.get(&id).unwrap(); let user_interface = build_user_interface( application, - pure_state, + cache, renderer, state.logical_size(), debug, -- cgit From 3c095aa3f09f28c6fd9d2a7ba220ced407693e0b Mon Sep 17 00:00:00 2001 From: Bingus Date: Wed, 15 Feb 2023 14:56:15 -0800 Subject: Merged in iced master --- winit/src/multi_window.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index bd7e9d44..f846e124 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -957,7 +957,7 @@ pub fn run_command( y, }); } - window::Action::SetMode(mode) => { + window::Action::ChangeMode(mode) => { let window = windows.get(&id).expect("No window found"); window.set_visible(conversion::visible(mode)); window.set_fullscreen(conversion::fullscreen( -- cgit From 8da098330b58542cc929f4f24d02e26bd654bae4 Mon Sep 17 00:00:00 2001 From: Bingus Date: Fri, 17 Feb 2023 11:42:49 -0800 Subject: Fixed widget animations implementation --- winit/src/multi_window.rs | 110 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 84 insertions(+), 26 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index f846e124..17eaa6fe 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -9,7 +9,7 @@ use crate::renderer; use crate::settings; use crate::widget::operation; use crate::window; -use crate::{conversion, multi_window}; +use crate::conversion; use crate::{ Command, Debug, Element, Error, Executor, Proxy, Renderer, Runtime, Settings, Size, Subscription, @@ -237,7 +237,8 @@ where let (compositor, renderer) = C::new(compositor_settings, Some(&window))?; - let (mut sender, receiver) = mpsc::unbounded(); + let (mut event_sender, event_receiver) = mpsc::unbounded(); + let (control_sender, mut control_receiver) = mpsc::unbounded(); let mut instance = Box::pin({ let run_instance = run_instance::( @@ -247,7 +248,8 @@ where runtime, proxy, debug, - receiver, + event_receiver, + control_sender, init_command, windows, settings.exit_on_close_request, @@ -299,13 +301,19 @@ where }; if let Some(event) = event { - sender.start_send(event).expect("Send event"); + event_sender.start_send(event).expect("Send event"); let poll = instance.as_mut().poll(&mut context); - *control_flow = match poll { - task::Poll::Pending => ControlFlow::Wait, - task::Poll::Ready(_) => ControlFlow::Exit, + match poll { + task::Poll::Pending => { + if let Ok(Some(flow)) = control_receiver.try_next() { + *control_flow = flow; + } + } + task::Poll::Ready(_) => { + *control_flow = ControlFlow::Exit; + } }; } }) @@ -318,9 +326,10 @@ async fn run_instance( mut runtime: Runtime>, Event>, mut proxy: winit::event_loop::EventLoopProxy>, mut debug: Debug, - mut receiver: mpsc::UnboundedReceiver< + mut event_receiver: mpsc::UnboundedReceiver< winit::event::Event<'_, Event>, >, + mut control_sender: mpsc::UnboundedSender, init_command: Command, mut windows: HashMap, _exit_on_close_request: bool, @@ -332,6 +341,7 @@ async fn run_instance( { use iced_futures::futures::stream::StreamExt; use winit::event; + use winit::event_loop::ControlFlow; let mut clipboard = Clipboard::connect(windows.values().next().expect("No window found")); @@ -390,11 +400,20 @@ async fn run_instance( let mut mouse_interaction = mouse::Interaction::default(); let mut events = Vec::new(); let mut messages = Vec::new(); + let mut redraw_pending = false; debug.startup_finished(); - 'main: while let Some(event) = receiver.next().await { + 'main: while let Some(event) = event_receiver.next().await { match event { + event::Event::NewEvents(start_cause) => { + redraw_pending = matches!( + start_cause, + event::StartCause::Init + | event::StartCause::Poll + | event::StartCause::ResumeTimeReached { .. } + ); + } event::Event::MainEventsCleared => { for id in states.keys().copied().collect::>() { let (filtered, remaining): (Vec<_>, Vec<_>) = @@ -408,29 +427,27 @@ async fn run_instance( ); events.retain(|el| remaining.contains(el)); - let mut filtered: Vec<_> = filtered + let window_events: Vec<_> = filtered .into_iter() .map(|(_id, event)| event) .collect(); - filtered.push(iced_native::Event::Window( - id, - window::Event::RedrawRequested(Instant::now()), - )); - let cursor_position = - states.get(&id).unwrap().cursor_position(); - let window = windows.get(&id).unwrap(); - - if filtered.is_empty() && messages.is_empty() { + if !redraw_pending + && window_events.is_empty() + && messages.is_empty() + { continue; } debug.event_processing_started(); + let cursor_position = + states.get(&id).unwrap().cursor_position(); + let (interface_state, statuses) = { let user_interface = interfaces.get_mut(&id).unwrap(); user_interface.update( - &filtered, + &window_events, cursor_position, &mut renderer, &mut clipboard, @@ -440,7 +457,8 @@ async fn run_instance( debug.event_processing_finished(); - for event in filtered.into_iter().zip(statuses.into_iter()) + for event in + window_events.into_iter().zip(statuses.into_iter()) { runtime.broadcast(event); } @@ -487,8 +505,6 @@ async fn run_instance( windows.get(&id).expect("No window found with ID."), ); - let should_exit = application.should_exit(); - interfaces = ManuallyDrop::new(build_user_interfaces( &application, &mut renderer, @@ -497,17 +513,35 @@ async fn run_instance( user_interfaces, )); - if should_exit { + if application.should_exit() { break 'main; } } + // TODO: Avoid redrawing all the time by forcing widgets to + // request redraws on state changes + // + // Then, we can use the `interface_state` here to decide if a redraw + // is needed right away, or simply wait until a specific time. + let redraw_event = iced_native::Event::Window( + id, + window::Event::RedrawRequested(Instant::now()), + ); + + let (interface_state, _) = + interfaces.get_mut(&id).unwrap().update( + &[redraw_event.clone()], + cursor_position, + &mut renderer, + &mut clipboard, + &mut messages, + ); + debug.draw_started(); let new_mouse_interaction = { - let user_interface = interfaces.get_mut(&id).unwrap(); let state = states.get(&id).unwrap(); - user_interface.draw( + interfaces.get_mut(&id).unwrap().draw( &mut renderer, state.theme(), &renderer::Style { @@ -518,6 +552,8 @@ async fn run_instance( }; debug.draw_finished(); + let window = windows.get(&id).unwrap(); + if new_mouse_interaction != mouse_interaction { window.set_cursor_icon(conversion::mouse_interaction( new_mouse_interaction, @@ -528,6 +564,28 @@ async fn run_instance( for window in windows.values() { window.request_redraw(); + + runtime.broadcast(( + redraw_event.clone(), + crate::event::Status::Ignored, + )); + + let _ = + control_sender.start_send(match interface_state { + user_interface::State::Updated { + redraw_request: Some(redraw_request), + } => match redraw_request { + window::RedrawRequest::NextFrame => { + ControlFlow::Poll + } + window::RedrawRequest::At(at) => { + ControlFlow::WaitUntil(at) + } + }, + _ => ControlFlow::Wait, + }); + + redraw_pending = false; } } } -- cgit From 2d427455ce8f9627da7c09eb80f38a615f0ddcb7 Mon Sep 17 00:00:00 2001 From: Bingus Date: Fri, 17 Feb 2023 11:50:52 -0800 Subject: Iced master merge (again) --- winit/src/multi_window.rs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 17eaa6fe..96dd1c8b 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -4,12 +4,12 @@ mod state; pub use state::State; use crate::clipboard::{self, Clipboard}; +use crate::conversion; use crate::mouse; use crate::renderer; use crate::settings; use crate::widget::operation; use crate::window; -use crate::conversion; use crate::{ Command, Debug, Element, Error, Executor, Proxy, Renderer, Runtime, Settings, Size, Subscription, @@ -22,7 +22,6 @@ use iced_native::user_interface::{self, UserInterface}; pub use iced_native::application::{Appearance, StyleSheet}; -use iced_native::window::Action; use std::collections::HashMap; use std::mem::ManuallyDrop; use std::time::Instant; @@ -147,6 +146,7 @@ where /// while a scale factor of `0.5` will shrink them to half their size. /// /// By default, it returns `1.0`. + #[allow(unused_variables)] fn scale_factor(&self, window: window::Id) -> f64 { 1.0 } @@ -470,7 +470,7 @@ async fn run_instance( user_interface::State::Outdated, ) { - let user_interfaces: HashMap<_, _> = + let cached_interfaces: HashMap<_, _> = ManuallyDrop::into_inner(interfaces) .drain() .map( @@ -510,7 +510,7 @@ async fn run_instance( &mut renderer, &mut debug, &states, - user_interfaces, + cached_interfaces, )); if application.should_exit() { @@ -1057,10 +1057,21 @@ pub fn run_command( attention_type.map(conversion::user_attention), ); } - Action::GainFocus => { + window::Action::GainFocus => { let window = windows.get(&id).expect("No window found!"); window.focus_window(); } + window::Action::ChangeAlwaysOnTop(on_top) => { + let window = windows.get(&id).expect("No window found!"); + window.set_always_on_top(on_top); + } + window::Action::FetchId(tag) => { + let window = windows.get(&id).expect("No window found!"); + + proxy + .send_event(Event::Application(tag(window.id().into()))) + .expect("Send message to event loop.") + } }, command::Action::System(action) => match action { system::Action::QueryInformation(_tag) => { -- cgit From 3aaf5d8873b16302badb14dc5508329c943862fb Mon Sep 17 00:00:00 2001 From: Bingus Date: Fri, 17 Feb 2023 13:26:31 -0800 Subject: Fixed widget operations --- winit/src/multi_window.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 96dd1c8b..fc91f41b 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -470,7 +470,7 @@ async fn run_instance( user_interface::State::Outdated, ) { - let cached_interfaces: HashMap<_, _> = + let mut cached_interfaces: HashMap<_, _> = ManuallyDrop::into_inner(interfaces) .drain() .map( @@ -486,7 +486,7 @@ async fn run_instance( // Update application update( &mut application, - &mut caches, + &mut cached_interfaces, &states, &mut renderer, &mut runtime, -- cgit From bd58d5fe25182908e99fdb0ced07b86666e45081 Mon Sep 17 00:00:00 2001 From: Bingus Date: Mon, 20 Feb 2023 12:34:04 -0800 Subject: Cargo fix --- winit/src/multi_window.rs | 56 +++++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 26 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index fc91f41b..788f39f7 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -416,6 +416,7 @@ async fn run_instance( } event::Event::MainEventsCleared => { for id in states.keys().copied().collect::>() { + // Partition events into only events for this window let (filtered, remaining): (Vec<_>, Vec<_>) = events.iter().cloned().partition( |(window_id, _event): &( @@ -426,7 +427,9 @@ async fn run_instance( }, ); + // Only retain events which have not been processed for next iteration events.retain(|el| remaining.contains(el)); + let window_events: Vec<_> = filtered .into_iter() .map(|(_id, event)| event) @@ -439,8 +442,8 @@ async fn run_instance( continue; } + // Process winit events for window debug.event_processing_started(); - let cursor_position = states.get(&id).unwrap().cursor_position(); @@ -455,15 +458,16 @@ async fn run_instance( ) }; - debug.event_processing_finished(); - for event in window_events.into_iter().zip(statuses.into_iter()) { runtime.broadcast(event); } + debug.event_processing_finished(); - // TODO(derezzedex): Should we redraw every window? We can't know what changed. + // Update application with app message(s) + // Note: without tying an app message to a window ID, we must redraw all windows + // as we cannot know what changed without some kind of damage tracking. if !messages.is_empty() || matches!( interface_state, @@ -498,7 +502,7 @@ async fn run_instance( || compositor.fetch_information(), ); - // Update window + // synchronize window state with application state. states.get_mut(&id).unwrap().synchronize( &application, id, @@ -564,29 +568,29 @@ async fn run_instance( for window in windows.values() { window.request_redraw(); + } - runtime.broadcast(( - redraw_event.clone(), - crate::event::Status::Ignored, - )); + runtime.broadcast(( + redraw_event.clone(), + crate::event::Status::Ignored, + )); - let _ = - control_sender.start_send(match interface_state { - user_interface::State::Updated { - redraw_request: Some(redraw_request), - } => match redraw_request { - window::RedrawRequest::NextFrame => { - ControlFlow::Poll - } - window::RedrawRequest::At(at) => { - ControlFlow::WaitUntil(at) - } - }, - _ => ControlFlow::Wait, - }); - - redraw_pending = false; - } + let _ = + control_sender.start_send(match interface_state { + user_interface::State::Updated { + redraw_request: Some(redraw_request), + } => match redraw_request { + window::RedrawRequest::NextFrame => { + ControlFlow::Poll + } + window::RedrawRequest::At(at) => { + ControlFlow::WaitUntil(at) + } + }, + _ => ControlFlow::Wait, + }); + + redraw_pending = false; } } event::Event::PlatformSpecific(event::PlatformSpecific::MacOS( -- cgit From 50b9c778b1e49fc83f827a340dce36a09bb291d6 Mon Sep 17 00:00:00 2001 From: Bingus Date: Tue, 21 Feb 2023 16:46:32 -0800 Subject: Fixed state syncing issue with MW. --- winit/src/multi_window.rs | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 788f39f7..1a9d4a1c 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -502,12 +502,16 @@ async fn run_instance( || compositor.fetch_information(), ); - // synchronize window state with application state. - states.get_mut(&id).unwrap().synchronize( - &application, - id, - windows.get(&id).expect("No window found with ID."), - ); + // synchronize window states with application states. + for (id, state) in states.iter_mut() { + state.synchronize( + &application, + *id, + windows + .get(id) + .expect("No window found with ID."), + ); + } interfaces = ManuallyDrop::new(build_user_interfaces( &application, @@ -575,20 +579,19 @@ async fn run_instance( crate::event::Status::Ignored, )); - let _ = - control_sender.start_send(match interface_state { - user_interface::State::Updated { - redraw_request: Some(redraw_request), - } => match redraw_request { - window::RedrawRequest::NextFrame => { - ControlFlow::Poll - } - window::RedrawRequest::At(at) => { - ControlFlow::WaitUntil(at) - } - }, - _ => ControlFlow::Wait, - }); + let _ = control_sender.start_send(match interface_state { + user_interface::State::Updated { + redraw_request: Some(redraw_request), + } => match redraw_request { + window::RedrawRequest::NextFrame => { + ControlFlow::Poll + } + window::RedrawRequest::At(at) => { + ControlFlow::WaitUntil(at) + } + }, + _ => ControlFlow::Wait, + }); redraw_pending = false; } -- cgit From e36daa6f937abd7cb2071fd8852a3c12263944ea Mon Sep 17 00:00:00 2001 From: bungoboingo Date: Tue, 28 Feb 2023 13:44:36 -0800 Subject: Removed glutin MW support and reverted glutin changes back to Iced master since it's being axed as we speak. --- winit/src/multi_window.rs | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 1a9d4a1c..eac8b260 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -1,4 +1,4 @@ -//! Create interactive, native cross-platform applications. +//! Create interactive, native cross-platform applications for WGPU. mod state; pub use state::State; @@ -31,17 +31,14 @@ pub use crate::Profiler; #[cfg(feature = "trace")] use tracing::{info_span, instrument::Instrument}; -/// TODO(derezzedex) -// This is the an wrapper around the `Application::Message` associate type -// to allows the `shell` to create internal messages, while still having -// the current user specified custom messages. +/// This is a wrapper around the `Application::Message` associate type +/// to allows the `shell` to create internal messages, while still having +/// the current user specified custom messages. #[derive(Debug)] pub enum Event { /// An [`Application`] generated message Application(Message), - /// TODO(derezzedex) - // Create a wrapper variant of `window::Event` type instead - // (maybe we should also allow users to listen/react to those internal messages?) + /// A message which spawns a new window. NewWindow { /// The [window::Id] of the newly spawned [`Window`]. id: window::Id, @@ -50,9 +47,9 @@ pub enum Event { /// The title of the newly spawned [`Window`]. title: String, }, - /// TODO(derezzedex) + /// Close a window. CloseWindow(window::Id), - /// TODO(derezzedex) + /// A message for when the window has finished being created. WindowCreated(window::Id, winit::window::Window), } @@ -90,7 +87,7 @@ where /// background by shells. fn update(&mut self, message: Self::Message) -> Command; - /// Returns the widgets to display in the [`Program`]. + /// Returns the widgets to display for the `window` in the [`Program`]. /// /// These widgets can produce __messages__ based on user interaction. fn view( @@ -108,7 +105,7 @@ where /// load state from a file, perform an initial HTTP request, etc. fn new(flags: Self::Flags) -> (Self, Command); - /// Returns the current title of the current [`Application`] window. + /// Returns the current title of each [`Application`] window. /// /// This title can be dynamic! The runtime will automatically update the /// title of your application when necessary. @@ -137,7 +134,7 @@ where Subscription::none() } - /// Returns the scale factor of the [`Application`]. + /// Returns the scale factor of the window of the [`Application`]. /// /// It can be used to dynamically control the size of the UI at runtime /// (i.e. zooming). @@ -1142,7 +1139,7 @@ pub fn run_command( } } -/// TODO(derezzedex) +/// Build the user interfaces for every window. pub fn build_user_interfaces<'a, A>( application: &'a A, renderer: &mut A::Renderer, -- cgit From 8ba18430800142965549077373e2a45d0a3429a1 Mon Sep 17 00:00:00 2001 From: Bingus Date: Mon, 13 Mar 2023 14:16:45 -0700 Subject: Code cleanup, clearer comments + removed some unnecessary dupe; Removed `Frames` struct return for `window::frames()` since we are just redrawing every window anyways; Interface dropping; --- winit/src/multi_window.rs | 126 +++++++++++++++++----------------------------- 1 file changed, 46 insertions(+), 80 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index eac8b260..d5da406c 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -33,7 +33,7 @@ use tracing::{info_span, instrument::Instrument}; /// This is a wrapper around the `Application::Message` associate type /// to allows the `shell` to create internal messages, while still having -/// the current user specified custom messages. +/// the current user-specified custom messages. #[derive(Debug)] pub enum Event { /// An [`Application`] generated message @@ -53,9 +53,9 @@ pub enum Event { WindowCreated(window::Id, winit::window::Window), } -/// An interactive, native cross-platform application. +/// An interactive, native, cross-platform, multi-windowed application. /// -/// This trait is the main entrypoint of Iced. Once implemented, you can run +/// This trait is the main entrypoint of multi-window Iced. Once implemented, you can run /// your GUI application by simply calling [`run`]. It will run in /// its own window. /// @@ -105,7 +105,7 @@ where /// load state from a file, perform an initial HTTP request, etc. fn new(flags: Self::Flags) -> (Self, Command); - /// Returns the current title of each [`Application`] window. + /// Returns the current title of each window of the [`Application`]. /// /// This title can be dynamic! The runtime will automatically update the /// title of your application when necessary. @@ -155,7 +155,8 @@ where false } - /// Requests that the [`window`] be closed. + /// Returns the `Self::Message` that should be processed when a `window` is requested to + /// be closed. fn close_requested(&self, window: window::Id) -> Self::Message; } @@ -462,9 +463,9 @@ async fn run_instance( } debug.event_processing_finished(); - // Update application with app message(s) - // Note: without tying an app message to a window ID, we must redraw all windows - // as we cannot know what changed without some kind of damage tracking. + // Update application with app messages + // Unless we implement some kind of diffing, we must redraw all windows as we + // cannot know what changed. if !messages.is_empty() || matches!( interface_state, @@ -612,9 +613,7 @@ async fn run_instance( } Event::WindowCreated(id, window) => { let mut surface = compositor.create_surface(&window); - let state = State::new(&application, id, &window); - let physical_size = state.physical_size(); compositor.configure_surface( @@ -776,14 +775,12 @@ async fn run_instance( } _ => { debug.render_finished(); + log::error!("Error {error:?} when presenting surface."); - // Try rendering again next frame. - // TODO(derezzedex) - windows - .values() - .next() - .expect("No window found") - .request_redraw(); + // Try rendering windows again next frame. + for window in windows.values() { + window.request_redraw(); + } } }, } @@ -792,80 +789,45 @@ async fn run_instance( event: window_event, window_id, } => { - // dbg!(window_id); - if let Some(window) = - window_ids.get(&window_id).and_then(|id| windows.get(id)) - { - if let Some(state) = window_ids + if let (Some(window), Some(state)) = ( + window_ids.get(&window_id).and_then(|id| windows.get(id)), + window_ids .get(&window_id) - .and_then(|id| states.get_mut(id)) - { - if requests_exit(&window_event, state.modifiers()) { - if let Some(id) = - window_ids.get(&window_id).cloned() - { - let message = application.close_requested(id); - messages.push(message); - } + .and_then(|id| states.get_mut(id)), + ) { + if crate::application::requests_exit(&window_event, state.modifiers()) { + if let Some(id) = window_ids.get(&window_id).cloned() { + let message = application.close_requested(id); + messages.push(message); } + } - state.update(window, &window_event, &mut debug); - - if let Some(event) = conversion::window_event( - *window_ids.get(&window_id).unwrap(), - &window_event, - state.scale_factor(), - state.modifiers(), - ) { - events.push(( - window_ids.get(&window_id).cloned(), - event, - )); - } - } else { - log::error!( - "No window state found for id: {:?}", - window_id - ); + state.update(window, &window_event, &mut debug); + + if let Some(event) = conversion::window_event( + *window_ids.get(&window_id).unwrap(), + &window_event, + state.scale_factor(), + state.modifiers(), + ) { + events + .push((window_ids.get(&window_id).cloned(), event)); } } else { - log::error!("No window found with id: {:?}", window_id); + log::error!( + "Could not find window or state for id: {window_id:?}" + ); } } _ => {} } } - // Manually drop the user interface - // drop(ManuallyDrop::into_inner(user_interface)); -} - -/// Returns true if the provided event should cause an [`Application`] to -/// exit. -pub fn requests_exit( - event: &winit::event::WindowEvent<'_>, - _modifiers: winit::event::ModifiersState, -) -> bool { - use winit::event::WindowEvent; - - match event { - WindowEvent::CloseRequested => true, - #[cfg(target_os = "macos")] - WindowEvent::KeyboardInput { - input: - winit::event::KeyboardInput { - virtual_keycode: Some(winit::event::VirtualKeyCode::Q), - state: winit::event::ElementState::Pressed, - .. - }, - .. - } if _modifiers.logo() => true, - _ => false, - } + // Manually drop the user interfaces + drop(ManuallyDrop::into_inner(interfaces)); } -/// Builds a [`UserInterface`] for the provided [`Application`], logging -/// [`struct@Debug`] information accordingly. +/// Builds a window's [`UserInterface`] for the [`Application`]. pub fn build_user_interface<'a, A: Application>( application: &'a A, cache: user_interface::Cache, @@ -890,7 +852,9 @@ where #[cfg(feature = "trace")] let layout_span = info_span!("Application", "LAYOUT").entered(); debug.layout_started(); + let user_interface = UserInterface::build(view, size, cache, renderer); + #[cfg(feature = "trace")] let _ = layout_span.exit(); debug.layout_finished(); @@ -898,7 +862,7 @@ where user_interface } -/// Updates an [`Application`] by feeding it the provided messages, spawning any +/// Updates an [`Application`] by feeding it messages, spawning any /// resulting [`Command`], and tracking its [`Subscription`]. pub fn update( application: &mut A, @@ -923,7 +887,9 @@ pub fn update( debug.log_message(&message); debug.update_started(); + let command = runtime.enter(|| application.update(message)); + #[cfg(feature = "trace")] let _ = update_span.exit(); debug.update_finished(); @@ -1023,7 +989,7 @@ pub fn run_command( let window = windows.get(&id).expect("No window found"); window.set_visible(conversion::visible(mode)); window.set_fullscreen(conversion::fullscreen( - window.primary_monitor(), + window.current_monitor(), mode, )); } -- cgit From ce4b2c93d9802dfb8cd3fc9033d76651d4bbc75b Mon Sep 17 00:00:00 2001 From: Bingus Date: Mon, 13 Mar 2023 18:19:16 -0700 Subject: Added simpler MW example --- winit/src/multi_window.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index d5da406c..6e28f1fa 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -109,7 +109,7 @@ where /// /// This title can be dynamic! The runtime will automatically update the /// title of your application when necessary. - fn title(&self, window_id: window::Id) -> String; + fn title(&self, window: window::Id) -> String; /// Returns the current [`Theme`] of the [`Application`]. fn theme(&self) -> ::Theme; -- cgit From 41836dd80d0534608e7aedfbf2319c540a23de1a Mon Sep 17 00:00:00 2001 From: Bingus Date: Wed, 15 Mar 2023 18:20:38 -0700 Subject: Added per-window theme support. --- winit/src/multi_window.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 6e28f1fa..9b395c1d 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -112,7 +112,7 @@ where fn title(&self, window: window::Id) -> String; /// Returns the current [`Theme`] of the [`Application`]. - fn theme(&self) -> ::Theme; + fn theme(&self, window: window::Id) -> ::Theme; /// Returns the [`Style`] variation of the [`Theme`]. fn style( -- cgit From d53ccc857da4d4cda769904342aeb5a82a64f146 Mon Sep 17 00:00:00 2001 From: Bingus Date: Wed, 12 Jul 2023 19:21:05 -0700 Subject: refactored window storage; new helper window events (Destroyed, Created); clippy + fmt; --- winit/src/multi_window.rs | 1078 ++++++++++++++++++++++----------------------- 1 file changed, 524 insertions(+), 554 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 9b395c1d..e6f440bc 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -1,58 +1,57 @@ //! Create interactive, native cross-platform applications for WGPU. mod state; +mod windows; pub use state::State; -use crate::clipboard::{self, Clipboard}; -use crate::conversion; -use crate::mouse; -use crate::renderer; -use crate::settings; -use crate::widget::operation; -use crate::window; -use crate::{ - Command, Debug, Element, Error, Executor, Proxy, Renderer, Runtime, - Settings, Size, Subscription, -}; - -use iced_futures::futures::channel::mpsc; -use iced_futures::futures::{self, FutureExt}; -use iced_graphics::compositor; -use iced_native::user_interface::{self, UserInterface}; - -pub use iced_native::application::{Appearance, StyleSheet}; - -use std::collections::HashMap; +use crate::core::widget::operation; +use crate::core::{self, mouse, renderer, window, Size}; +use crate::futures::futures::channel::mpsc; +use crate::futures::futures::{task, Future, FutureExt, StreamExt}; +use crate::futures::{Executor, Runtime, Subscription}; +use crate::graphics::{compositor, Compositor}; +use crate::multi_window::windows::Windows; +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::settings::window_builder; +use crate::style::application::StyleSheet; +use crate::{conversion, settings, Clipboard, Error, Proxy, Settings}; + +use iced_runtime::user_interface::Cache; use std::mem::ManuallyDrop; use std::time::Instant; - -#[cfg(feature = "trace")] -pub use crate::Profiler; -#[cfg(feature = "trace")] -use tracing::{info_span, instrument::Instrument}; +use winit::monitor::MonitorHandle; /// This is a wrapper around the `Application::Message` associate type /// to allows the `shell` to create internal messages, while still having /// the current user-specified custom messages. #[derive(Debug)] pub enum Event { - /// An [`Application`] generated message + /// An internal event which contains an [`Application`] generated message. Application(Message), - /// A message which spawns a new window. + /// An internal event which spawns a new window. NewWindow { /// The [window::Id] of the newly spawned [`Window`]. id: window::Id, /// The [settings::Window] of the newly spawned [`Window`]. - settings: settings::Window, + settings: window::Settings, /// The title of the newly spawned [`Window`]. title: String, + /// The monitor on which to spawn the window. If `None`, will use monitor of the last window + /// spawned. + monitor: Option, }, - /// Close a window. + /// An internal event for closing a window. CloseWindow(window::Id), - /// A message for when the window has finished being created. + /// An internal event for when the window has finished being created. WindowCreated(window::Id, winit::window::Window), } +#[allow(unsafe_code)] +unsafe impl std::marker::Send for Event {} + /// An interactive, native, cross-platform, multi-windowed application. /// /// This trait is the main entrypoint of multi-window Iced. Once implemented, you can run @@ -64,37 +63,13 @@ pub enum Event { /// /// When using an [`Application`] with the `debug` feature enabled, a debug view /// can be toggled by pressing `F12`. -pub trait Application: Sized +pub trait Application: Program where - ::Theme: StyleSheet, + ::Theme: StyleSheet, { /// The data needed to initialize your [`Application`]. type Flags; - /// The graphics backend to use to draw the [`Program`]. - type Renderer: Renderer; - - /// The type of __messages__ your [`Program`] will produce. - type Message: std::fmt::Debug + Send; - - /// Handles a __message__ and updates the state of the [`Program`]. - /// - /// This is where you define your __update logic__. All the __messages__, - /// produced by either user interactions or commands, will be handled by - /// this method. - /// - /// Any [`Command`] returned will be executed immediately in the - /// background by shells. - fn update(&mut self, message: Self::Message) -> Command; - - /// Returns the widgets to display for the `window` in the [`Program`]. - /// - /// These widgets can produce __messages__ based on user interaction. - fn view( - &self, - window: window::Id, - ) -> Element<'_, Self::Message, Self::Renderer>; - /// Initializes the [`Application`] with the flags provided to /// [`run`] as part of the [`Settings`]. /// @@ -105,19 +80,22 @@ where /// load state from a file, perform an initial HTTP request, etc. fn new(flags: Self::Flags) -> (Self, Command); - /// Returns the current title of each window of the [`Application`]. + /// Returns the current title of the [`Application`]. /// /// This title can be dynamic! The runtime will automatically update the /// title of your application when necessary. fn title(&self, window: window::Id) -> String; - /// Returns the current [`Theme`] of the [`Application`]. - fn theme(&self, window: window::Id) -> ::Theme; + /// Returns the current `Theme` of the [`Application`]. + fn theme( + &self, + window: window::Id, + ) -> ::Theme; - /// Returns the [`Style`] variation of the [`Theme`]. + /// Returns the `Style` variation of the `Theme`. fn style( &self, - ) -> <::Theme as StyleSheet>::Style { + ) -> <::Theme as StyleSheet>::Style { Default::default() } @@ -147,17 +125,6 @@ where fn scale_factor(&self, window: window::Id) -> f64 { 1.0 } - - /// Returns whether the [`Application`] should be terminated. - /// - /// By default, it returns `false`. - fn should_exit(&self) -> bool { - false - } - - /// Returns the `Self::Message` that should be processed when a `window` is requested to - /// be closed. - fn close_requested(&self, window: window::Id) -> Self::Message; } /// Runs an [`Application`] with an executor, compositor, and the provided @@ -169,22 +136,14 @@ pub fn run( where A: Application + 'static, E: Executor + 'static, - C: iced_graphics::window::Compositor + 'static, - ::Theme: StyleSheet, + C: Compositor + 'static, + ::Theme: StyleSheet, { - use futures::task; - use futures::Future; use winit::event_loop::EventLoopBuilder; - #[cfg(feature = "trace")] - let _guard = Profiler::init(); - let mut debug = Debug::new(); debug.startup_started(); - #[cfg(feature = "trace")] - let _ = info_span!("Application", "RUN").entered(); - let event_loop = EventLoopBuilder::with_user_event().build(); let proxy = event_loop.create_proxy(); @@ -201,68 +160,77 @@ where runtime.enter(|| A::new(flags)) }; - let builder = settings.window.into_builder( + let should_main_be_visible = settings.window.visible; + let builder = window_builder( + settings.window, &application.title(window::Id::MAIN), event_loop.primary_monitor(), settings.id, - ); + ) + .with_visible(false); log::info!("Window builder: {:#?}", builder); - let window = builder + let main_window = builder .build(&event_loop) .map_err(Error::WindowCreationFailed)?; - let windows: HashMap = - HashMap::from([(window::Id::MAIN, window)]); - - let window = windows.values().next().expect("No window found"); - #[cfg(target_arch = "wasm32")] { use winit::platform::web::WindowExtWebSys; - let canvas = window.canvas(); + let canvas = main_window.canvas(); let window = web_sys::window().unwrap(); let document = window.document().unwrap(); let body = document.body().unwrap(); - let _ = body - .append_child(&canvas) - .expect("Append canvas to HTML body"); + let target = target.and_then(|target| { + body.query_selector(&format!("#{}", target)) + .ok() + .unwrap_or(None) + }); + + match target { + Some(node) => { + let _ = node + .replace_with_with_node_1(&canvas) + .expect(&format!("Could not replace #{}", node.id())); + } + None => { + let _ = body + .append_child(&canvas) + .expect("Append canvas to HTML body"); + } + }; } - let (compositor, renderer) = C::new(compositor_settings, Some(&window))?; + let (mut compositor, renderer) = + C::new(compositor_settings, Some(&main_window))?; + + let windows = + Windows::new(&application, &mut compositor, renderer, main_window); let (mut event_sender, event_receiver) = mpsc::unbounded(); let (control_sender, mut control_receiver) = mpsc::unbounded(); - let mut instance = Box::pin({ - let run_instance = run_instance::( - application, - compositor, - renderer, - runtime, - proxy, - debug, - event_receiver, - control_sender, - init_command, - windows, - settings.exit_on_close_request, - ); - - #[cfg(feature = "trace")] - let run_instance = - run_instance.instrument(info_span!("Application", "LOOP")); - - run_instance - }); + let mut instance = Box::pin(run_instance::( + application, + compositor, + runtime, + proxy, + debug, + event_receiver, + control_sender, + init_command, + windows, + should_main_be_visible, + settings.exit_on_close_request, + )); let mut context = task::Context::from_waker(task::noop_waker_ref()); - platform::run(event_loop, move |event, event_loop, control_flow| { + platform::run(event_loop, move |event, window_target, control_flow| { use winit::event_loop::ControlFlow; if let ControlFlow::ExitWithCode(_) = control_flow { @@ -285,11 +253,12 @@ where id, settings, title, + monitor, }) => { - let window = settings - .into_builder(&title, event_loop.primary_monitor(), None) - .build(event_loop) - .expect("Failed to build window"); + let window = + settings::window_builder(settings, &title, monitor, None) + .build(window_target) + .expect("Failed to build window"); Some(winit::event::Event::UserEvent(Event::WindowCreated( id, window, @@ -320,7 +289,6 @@ where async fn run_instance( mut application: A, mut compositor: C, - mut renderer: A::Renderer, mut runtime: Runtime>, Event>, mut proxy: winit::event_loop::EventLoopProxy>, mut debug: Debug, @@ -329,74 +297,65 @@ async fn run_instance( >, mut control_sender: mpsc::UnboundedSender, init_command: Command, - mut windows: HashMap, - _exit_on_close_request: bool, + mut windows: Windows, + should_main_window_be_visible: bool, + exit_on_main_closed: bool, ) where A: Application + 'static, E: Executor + 'static, - C: iced_graphics::window::Compositor + 'static, - ::Theme: StyleSheet, + C: Compositor + 'static, + ::Theme: StyleSheet, { - use iced_futures::futures::stream::StreamExt; use winit::event; use winit::event_loop::ControlFlow; - let mut clipboard = - Clipboard::connect(windows.values().next().expect("No window found")); - let mut caches = HashMap::new(); - let mut window_ids: HashMap<_, _> = windows - .iter() - .map(|(&id, window)| (window.id(), id)) - .collect(); - - let mut states = HashMap::new(); - let mut surfaces = HashMap::new(); - let mut interfaces = ManuallyDrop::new(HashMap::new()); - - for (&id, window) in windows.keys().zip(windows.values()) { - let mut surface = compositor.create_surface(window); - let state = State::new(&application, id, window); - let physical_size = state.physical_size(); - - compositor.configure_surface( - &mut surface, - physical_size.width, - physical_size.height, - ); + let mut clipboard = Clipboard::connect(windows.main()); - let user_interface = build_user_interface( - &application, - user_interface::Cache::default(), - &mut renderer, - state.logical_size(), - &mut debug, - id, - ); + let mut ui_caches = vec![user_interface::Cache::default()]; + let mut user_interfaces = ManuallyDrop::new(build_user_interfaces( + &application, + &mut debug, + &mut windows, + vec![user_interface::Cache::default()], + )); - let _ = states.insert(id, state); - let _ = surfaces.insert(id, surface); - let _ = interfaces.insert(id, user_interface); - let _ = caches.insert(id, user_interface::Cache::default()); + if should_main_window_be_visible { + windows.main().set_visible(true); } run_command( &application, - &mut caches, - &states, - &mut renderer, + &mut compositor, init_command, &mut runtime, &mut clipboard, &mut proxy, &mut debug, - &windows, - || compositor.fetch_information(), + &mut windows, + &mut ui_caches, ); - runtime.track(application.subscription().map(Event::Application)); + runtime.track( + application + .subscription() + .map(Event::Application) + .into_recipes(), + ); let mut mouse_interaction = mouse::Interaction::default(); - let mut events = Vec::new(); + + let mut events = + if let Some((position, size)) = logical_bounds_of(windows.main()) { + vec![( + Some(window::Id::MAIN), + core::Event::Window( + window::Id::MAIN, + window::Event::Created { position, size }, + ), + )] + } else { + Vec::new() + }; let mut messages = Vec::new(); let mut redraw_pending = false; @@ -413,25 +372,20 @@ async fn run_instance( ); } event::Event::MainEventsCleared => { - for id in states.keys().copied().collect::>() { - // Partition events into only events for this window - let (filtered, remaining): (Vec<_>, Vec<_>) = - events.iter().cloned().partition( - |(window_id, _event): &( - Option, - iced_native::event::Event, - )| { - *window_id == Some(id) || *window_id == None - }, - ); - - // Only retain events which have not been processed for next iteration - events.retain(|el| remaining.contains(el)); - - let window_events: Vec<_> = filtered - .into_iter() - .map(|(_id, event)| event) - .collect(); + debug.event_processing_started(); + let mut uis_stale = false; + + for (i, id) in windows.ids.iter().enumerate() { + let mut window_events = vec![]; + + events.retain(|(window_id, event)| { + if *window_id == Some(*id) || window_id.is_none() { + window_events.push(event.clone()); + false + } else { + true + } + }); if !redraw_pending && window_events.is_empty() @@ -440,144 +394,124 @@ async fn run_instance( continue; } - // Process winit events for window - debug.event_processing_started(); - let cursor_position = - states.get(&id).unwrap().cursor_position(); - - let (interface_state, statuses) = { - let user_interface = interfaces.get_mut(&id).unwrap(); - user_interface.update( - &window_events, - cursor_position, - &mut renderer, - &mut clipboard, - &mut messages, - ) - }; + let (ui_state, statuses) = user_interfaces[i].update( + &window_events, + windows.states[i].cursor(), + &mut windows.renderers[i], + &mut clipboard, + &mut messages, + ); + + if !uis_stale { + uis_stale = + matches!(ui_state, user_interface::State::Outdated); + } - for event in + for (event, status) in window_events.into_iter().zip(statuses.into_iter()) { - runtime.broadcast(event); + runtime.broadcast(event, status); } - debug.event_processing_finished(); - - // Update application with app messages - // Unless we implement some kind of diffing, we must redraw all windows as we - // cannot know what changed. - if !messages.is_empty() - || matches!( - interface_state, - user_interface::State::Outdated, - ) - { - let mut cached_interfaces: HashMap<_, _> = - ManuallyDrop::into_inner(interfaces) - .drain() - .map( - |(id, interface): ( - window::Id, - UserInterface<'_, _, _>, - )| { - (id, interface.into_cache()) - }, - ) - .collect(); - - // Update application - update( - &mut application, - &mut cached_interfaces, - &states, - &mut renderer, - &mut runtime, - &mut clipboard, - &mut proxy, - &mut debug, - &mut messages, - &windows, - || compositor.fetch_information(), - ); - - // synchronize window states with application states. - for (id, state) in states.iter_mut() { - state.synchronize( - &application, - *id, - windows - .get(id) - .expect("No window found with ID."), - ); - } + } - interfaces = ManuallyDrop::new(build_user_interfaces( - &application, - &mut renderer, - &mut debug, - &states, - cached_interfaces, - )); + debug.event_processing_finished(); + + // TODO mw application update returns which window IDs to update + if !messages.is_empty() || uis_stale { + let mut cached_interfaces: Vec = + ManuallyDrop::into_inner(user_interfaces) + .drain(..) + .map(UserInterface::into_cache) + .collect(); + + // Update application + update( + &mut application, + &mut compositor, + &mut runtime, + &mut clipboard, + &mut proxy, + &mut debug, + &mut messages, + &mut windows, + &mut cached_interfaces, + ); - if application.should_exit() { - break 'main; - } + // we must synchronize all window states with application state after an + // application update since we don't know what changed + for (state, (id, window)) in windows + .states + .iter_mut() + .zip(windows.ids.iter().zip(windows.raw.iter())) + { + state.synchronize(&application, *id, window); } + // rebuild UIs with the synchronized states + user_interfaces = ManuallyDrop::new(build_user_interfaces( + &application, + &mut debug, + &mut windows, + cached_interfaces, + )); + } + + debug.draw_started(); + + for (i, id) in windows.ids.iter().enumerate() { // TODO: Avoid redrawing all the time by forcing widgets to - // request redraws on state changes + // request redraws on state changes // // Then, we can use the `interface_state` here to decide if a redraw // is needed right away, or simply wait until a specific time. - let redraw_event = iced_native::Event::Window( - id, + let redraw_event = core::Event::Window( + *id, window::Event::RedrawRequested(Instant::now()), ); - let (interface_state, _) = - interfaces.get_mut(&id).unwrap().update( - &[redraw_event.clone()], - cursor_position, - &mut renderer, - &mut clipboard, - &mut messages, - ); + let cursor = windows.states[i].cursor(); + + let (ui_state, _) = user_interfaces[i].update( + &[redraw_event.clone()], + cursor, + &mut windows.renderers[i], + &mut clipboard, + &mut messages, + ); - debug.draw_started(); let new_mouse_interaction = { - let state = states.get(&id).unwrap(); + let state = &windows.states[i]; - interfaces.get_mut(&id).unwrap().draw( - &mut renderer, + user_interfaces[i].draw( + &mut windows.renderers[i], state.theme(), &renderer::Style { text_color: state.text_color(), }, - state.cursor_position(), + cursor, ) }; - debug.draw_finished(); - - let window = windows.get(&id).unwrap(); if new_mouse_interaction != mouse_interaction { - window.set_cursor_icon(conversion::mouse_interaction( - new_mouse_interaction, - )); + windows.raw[i].set_cursor_icon( + conversion::mouse_interaction( + new_mouse_interaction, + ), + ); mouse_interaction = new_mouse_interaction; } - for window in windows.values() { - window.request_redraw(); - } + // TODO once widgets can request to be redrawn, we can avoid always requesting a + // redraw + windows.raw[i].request_redraw(); - runtime.broadcast(( + runtime.broadcast( redraw_event.clone(), - crate::event::Status::Ignored, - )); + core::event::Status::Ignored, + ); - let _ = control_sender.start_send(match interface_state { + let _ = control_sender.start_send(match ui_state { user_interface::State::Updated { redraw_request: Some(redraw_request), } => match redraw_request { @@ -590,17 +524,20 @@ async fn run_instance( }, _ => ControlFlow::Wait, }); - - redraw_pending = false; } + + redraw_pending = false; + + debug.draw_finished(); } event::Event::PlatformSpecific(event::PlatformSpecific::MacOS( event::MacOS::ReceivedUrl(url), )) => { - use iced_native::event; + use crate::core::event; + events.push(( None, - iced_native::Event::PlatformSpecific( + event::Event::PlatformSpecific( event::PlatformSpecific::MacOS( event::MacOS::ReceivedUrl(url), ), @@ -612,91 +549,48 @@ async fn run_instance( messages.push(message); } Event::WindowCreated(id, window) => { - let mut surface = compositor.create_surface(&window); - let state = State::new(&application, id, &window); - let physical_size = state.physical_size(); + let bounds = logical_bounds_of(&window); - compositor.configure_surface( - &mut surface, - physical_size.width, - physical_size.height, - ); + let (inner_size, i) = + windows.add(&application, &mut compositor, id, window); - let user_interface = build_user_interface( + user_interfaces.push(build_user_interface( &application, user_interface::Cache::default(), - &mut renderer, - state.logical_size(), + &mut windows.renderers[i], + inner_size, &mut debug, id, - ); - - let _ = states.insert(id, state); - let _ = surfaces.insert(id, surface); - let _ = interfaces.insert(id, user_interface); - let _ = window_ids.insert(window.id(), id); - let _ = windows.insert(id, window); - let _ = caches.insert(id, user_interface::Cache::default()); + )); + ui_caches.push(user_interface::Cache::default()); + + if let Some(bounds) = bounds { + events.push(( + Some(id), + core::Event::Window( + id, + window::Event::Created { + position: bounds.0, + size: bounds.1, + }, + ), + )); + } } Event::CloseWindow(id) => { - if let Some(window) = windows.get(&id) { - if window_ids.remove(&window.id()).is_none() { - log::error!("Failed to remove window with id {:?} from window_ids.", window.id()); - } - } else { - log::error!( - "Could not find window with id {:?} in windows.", - id - ); - } - if states.remove(&id).is_none() { - log::error!( - "Failed to remove window {:?} from states.", - id - ); - } - if interfaces.remove(&id).is_none() { - log::error!( - "Failed to remove window {:?} from interfaces.", - id - ); - } - if windows.remove(&id).is_none() { - log::error!( - "Failed to remove window {:?} from windows.", - id - ); - } - if surfaces.remove(&id).is_none() { - log::error!( - "Failed to remove window {:?} from surfaces.", - id - ); - } + let i = windows.delete(id); + let _ = user_interfaces.remove(i); + let _ = ui_caches.remove(i); if windows.is_empty() { - log::info!( - "All windows are closed. Terminating program." - ); break 'main; - } else { - log::info!("Remaining windows: {:?}", windows.len()); } } Event::NewWindow { .. } => unreachable!(), }, event::Event::RedrawRequested(id) => { - #[cfg(feature = "trace")] - let _ = info_span!("Application", "FRAME").entered(); - - let state = window_ids - .get(&id) - .and_then(|id| states.get_mut(id)) - .unwrap(); - let surface = window_ids - .get(&id) - .and_then(|id| surfaces.get_mut(id)) - .unwrap(); + let i = windows.index_from_raw(id); + let state = &windows.states[i]; let physical_size = state.physical_size(); if physical_size.width == 0 || physical_size.height == 0 { @@ -704,60 +598,55 @@ async fn run_instance( } debug.render_started(); + let current_viewport_version = state.viewport_version(); + let window_viewport_version = windows.viewport_versions[i]; - if state.viewport_changed() { - let mut user_interface = window_ids - .get(&id) - .and_then(|id| interfaces.remove(id)) - .unwrap(); - + if window_viewport_version != current_viewport_version { let logical_size = state.logical_size(); debug.layout_started(); - user_interface = - user_interface.relayout(logical_size, &mut renderer); + + let renderer = &mut windows.renderers[i]; + let ui = user_interfaces.remove(i); + + user_interfaces + .insert(i, ui.relayout(logical_size, renderer)); + debug.layout_finished(); debug.draw_started(); - let new_mouse_interaction = { - let state = &state; - - user_interface.draw( - &mut renderer, - state.theme(), - &renderer::Style { - text_color: state.text_color(), - }, - state.cursor_position(), - ) - }; + let new_mouse_interaction = user_interfaces[i].draw( + renderer, + state.theme(), + &renderer::Style { + text_color: state.text_color(), + }, + state.cursor(), + ); - let window = window_ids - .get(&id) - .and_then(|id| windows.get(id)) - .unwrap(); if new_mouse_interaction != mouse_interaction { - window.set_cursor_icon(conversion::mouse_interaction( - new_mouse_interaction, - )); + windows.raw[i].set_cursor_icon( + conversion::mouse_interaction( + new_mouse_interaction, + ), + ); mouse_interaction = new_mouse_interaction; } debug.draw_finished(); - let _ = interfaces - .insert(*window_ids.get(&id).unwrap(), user_interface); - compositor.configure_surface( - surface, + &mut windows.surfaces[i], physical_size.width, physical_size.height, ); + + windows.viewport_versions[i] = current_viewport_version; } match compositor.present( - &mut renderer, - surface, + &mut windows.renderers[i], + &mut windows.surfaces[i], state.viewport(), state.background_color(), &debug.overlay(), @@ -775,10 +664,12 @@ async fn run_instance( } _ => { debug.render_finished(); - log::error!("Error {error:?} when presenting surface."); + log::error!( + "Error {error:?} when presenting surface." + ); - // Try rendering windows again next frame. - for window in windows.values() { + // Try rendering all windows again next frame. + for window in &windows.raw { window.request_redraw(); } } @@ -789,42 +680,58 @@ async fn run_instance( event: window_event, window_id, } => { - if let (Some(window), Some(state)) = ( - window_ids.get(&window_id).and_then(|id| windows.get(id)), - window_ids - .get(&window_id) - .and_then(|id| states.get_mut(id)), - ) { - if crate::application::requests_exit(&window_event, state.modifiers()) { - if let Some(id) = window_ids.get(&window_id).cloned() { - let message = application.close_requested(id); - messages.push(message); - } + let window_deleted = windows + .pending_destroy + .iter() + .any(|(_, w_id)| window_id == *w_id); + + if matches!(window_event, winit::event::WindowEvent::Destroyed) + { + // This is the only special case, since in order trigger the Destroyed event the + // window reference from winit must be dropped, but we still want to inform the + // user that the window was destroyed so they can clean up any specific window + // code for this window + let id = windows.get_pending_destroy(window_id); + + events.push(( + None, + core::Event::Window(id, window::Event::Destroyed), + )); + } else if !window_deleted { + let i = windows.index_from_raw(window_id); + let id = windows.ids[i]; + let raw = &windows.raw[i]; + let state = &mut windows.states[i]; + + // first check if we need to just break the entire application + // e.g. a user does a force quit on MacOS, or if a user has set "exit on main closed" + // as an option in window settings and wants to close the main window + if requests_exit( + i, + exit_on_main_closed, + &window_event, + state.modifiers(), + ) { + break 'main; } - state.update(window, &window_event, &mut debug); + state.update(raw, &window_event, &mut debug); if let Some(event) = conversion::window_event( - *window_ids.get(&window_id).unwrap(), + id, &window_event, state.scale_factor(), state.modifiers(), ) { - events - .push((window_ids.get(&window_id).cloned(), event)); + events.push((Some(id), event)); } - } else { - log::error!( - "Could not find window or state for id: {window_id:?}" - ); } } _ => {} } } - // Manually drop the user interfaces - drop(ManuallyDrop::into_inner(interfaces)); + let _ = ManuallyDrop::into_inner(user_interfaces); } /// Builds a window's [`UserInterface`] for the [`Application`]. @@ -837,103 +744,79 @@ pub fn build_user_interface<'a, A: Application>( id: window::Id, ) -> UserInterface<'a, A::Message, A::Renderer> where - ::Theme: StyleSheet, + ::Theme: StyleSheet, { - #[cfg(feature = "trace")] - let view_span = info_span!("Application", "VIEW").entered(); - debug.view_started(); let view = application.view(id); - - #[cfg(feature = "trace")] - let _ = view_span.exit(); debug.view_finished(); - #[cfg(feature = "trace")] - let layout_span = info_span!("Application", "LAYOUT").entered(); debug.layout_started(); - let user_interface = UserInterface::build(view, size, cache, renderer); - - #[cfg(feature = "trace")] - let _ = layout_span.exit(); debug.layout_finished(); user_interface } -/// Updates an [`Application`] by feeding it messages, spawning any +/// Updates a multi-window [`Application`] by feeding it messages, spawning any /// resulting [`Command`], and tracking its [`Subscription`]. -pub fn update( +pub fn update( application: &mut A, - caches: &mut HashMap, - states: &HashMap>, - renderer: &mut A::Renderer, + compositor: &mut C, runtime: &mut Runtime>, Event>, clipboard: &mut Clipboard, proxy: &mut winit::event_loop::EventLoopProxy>, debug: &mut Debug, messages: &mut Vec, - windows: &HashMap, - graphics_info: impl FnOnce() -> compositor::Information + Copy, + windows: &mut Windows, + ui_caches: &mut Vec, ) where - A: Application + 'static, - ::Theme: StyleSheet, + C: Compositor + 'static, + ::Theme: StyleSheet, { for message in messages.drain(..) { - #[cfg(feature = "trace")] - let update_span = info_span!("Application", "UPDATE").entered(); - debug.log_message(&message); - debug.update_started(); let command = runtime.enter(|| application.update(message)); - - #[cfg(feature = "trace")] - let _ = update_span.exit(); debug.update_finished(); run_command( application, - caches, - states, - renderer, + compositor, command, runtime, clipboard, proxy, debug, windows, - graphics_info, + ui_caches, ); } let subscription = application.subscription().map(Event::Application); - runtime.track(subscription); + runtime.track(subscription.into_recipes()); } /// Runs the actions of a [`Command`]. -pub fn run_command( +pub fn run_command( application: &A, - caches: &mut HashMap, - states: &HashMap>, - renderer: &mut A::Renderer, + compositor: &mut C, command: Command, runtime: &mut Runtime>, Event>, clipboard: &mut Clipboard, proxy: &mut winit::event_loop::EventLoopProxy>, debug: &mut Debug, - windows: &HashMap, - _graphics_info: impl FnOnce() -> compositor::Information + Copy, + windows: &mut Windows, + ui_caches: &mut Vec, ) where - A: Application + 'static, + A: Application, E: Executor, - ::Theme: StyleSheet, + C: Compositor + 'static, + ::Theme: StyleSheet, { - use iced_native::command; - use iced_native::system; - use iced_native::window; + use crate::runtime::clipboard; + use crate::runtime::system; + use crate::runtime::window; for action in command.actions() { match action { @@ -954,11 +837,14 @@ pub fn run_command( }, command::Action::Window(id, action) => match action { window::Action::Spawn { settings } => { + let monitor = windows.last_monitor(); + proxy .send_event(Event::NewWindow { id, - settings: settings.into(), + settings, title: application.title(id), + monitor, }) .expect("Send message to event loop"); } @@ -968,86 +854,117 @@ pub fn run_command( .expect("Send message to event loop"); } window::Action::Drag => { - let window = windows.get(&id).expect("No window found"); - let _res = window.drag_window(); + let _ = windows.with_raw(id).drag_window(); } - window::Action::Resize { width, height } => { - let window = windows.get(&id).expect("No window found"); - window.set_inner_size(winit::dpi::LogicalSize { - width, - height, - }); + window::Action::Resize(size) => { + windows.with_raw(id).set_inner_size( + winit::dpi::LogicalSize { + width: size.width, + height: size.height, + }, + ); + } + window::Action::FetchSize(callback) => { + let window = windows.with_raw(id); + let size = window.inner_size(); + + proxy + .send_event(Event::Application(callback(Size::new( + size.width, + size.height, + )))) + .expect("Send message to event loop") + } + window::Action::Maximize(maximized) => { + windows.with_raw(id).set_maximized(maximized); + } + window::Action::Minimize(minimized) => { + windows.with_raw(id).set_minimized(minimized); } window::Action::Move { x, y } => { - let window = windows.get(&id).expect("No window found"); - window.set_outer_position(winit::dpi::LogicalPosition { - x, - y, - }); + windows.with_raw(id).set_outer_position( + winit::dpi::LogicalPosition { x, y }, + ); } window::Action::ChangeMode(mode) => { - let window = windows.get(&id).expect("No window found"); + let window = windows.with_raw(id); window.set_visible(conversion::visible(mode)); window.set_fullscreen(conversion::fullscreen( window.current_monitor(), mode, )); } + window::Action::ChangeIcon(icon) => { + windows.with_raw(id).set_window_icon(conversion::icon(icon)) + } window::Action::FetchMode(tag) => { - let window = windows.get(&id).expect("No window found"); + let window = windows.with_raw(id); let mode = if window.is_visible().unwrap_or(true) { conversion::mode(window.fullscreen()) } else { - window::Mode::Hidden + core::window::Mode::Hidden }; proxy .send_event(Event::Application(tag(mode))) - .expect("Send message to event loop"); - } - window::Action::Maximize(value) => { - let window = windows.get(&id).expect("No window found!"); - window.set_maximized(value); - } - window::Action::Minimize(value) => { - let window = windows.get(&id).expect("No window found!"); - window.set_minimized(value); + .expect("Event loop doesn't exist."); } window::Action::ToggleMaximize => { - let window = windows.get(&id).expect("No window found!"); + let window = windows.with_raw(id); window.set_maximized(!window.is_maximized()); } window::Action::ToggleDecorations => { - let window = windows.get(&id).expect("No window found!"); + let window = windows.with_raw(id); window.set_decorations(!window.is_decorated()); } window::Action::RequestUserAttention(attention_type) => { - let window = windows.get(&id).expect("No window found!"); - window.request_user_attention( + windows.with_raw(id).request_user_attention( attention_type.map(conversion::user_attention), ); } window::Action::GainFocus => { - let window = windows.get(&id).expect("No window found!"); - window.focus_window(); + windows.with_raw(id).focus_window(); } - window::Action::ChangeAlwaysOnTop(on_top) => { - let window = windows.get(&id).expect("No window found!"); - window.set_always_on_top(on_top); + window::Action::ChangeLevel(level) => { + windows + .with_raw(id) + .set_window_level(conversion::window_level(level)); } - window::Action::FetchId(tag) => { - let window = windows.get(&id).expect("No window found!"); + window::Action::FetchId(tag) => proxy + .send_event(Event::Application(tag(windows + .with_raw(id) + .id() + .into()))) + .expect("Event loop doesn't exist."), + window::Action::Screenshot(tag) => { + let i = windows.index_from_id(id); + let state = &windows.states[i]; + let surface = &mut windows.surfaces[i]; + let renderer = &mut windows.renderers[i]; + + let bytes = compositor.screenshot( + renderer, + surface, + state.viewport(), + state.background_color(), + &debug.overlay(), + ); proxy - .send_event(Event::Application(tag(window.id().into()))) - .expect("Send message to event loop.") + .send_event(Event::Application(tag( + window::Screenshot::new( + bytes, + state.physical_size(), + ), + ))) + .expect("Event loop doesn't exist.") } }, command::Action::System(action) => match action { system::Action::QueryInformation(_tag) => { #[cfg(feature = "system")] { - let graphics_info = _graphics_info(); + let graphics_info = compositor.fetch_information(); let proxy = proxy.clone(); let _ = std::thread::spawn(move || { @@ -1058,33 +975,36 @@ pub fn run_command( proxy .send_event(Event::Application(message)) - .expect("Send message to event loop") + .expect("Event loop doesn't exist.") }); } } }, command::Action::Widget(action) => { - let mut current_caches = std::mem::take(caches); - let mut current_operation = Some(action.into_operation()); + let mut current_operation = Some(action); - let mut user_interfaces = build_user_interfaces( + let mut uis = build_user_interfaces( application, - renderer, debug, - states, - current_caches, + windows, + std::mem::take(ui_caches), ); - while let Some(mut operation) = current_operation.take() { - for user_interface in user_interfaces.values_mut() { - user_interface.operate(renderer, operation.as_mut()); + 'operate: while let Some(mut operation) = + current_operation.take() + { + for (i, ui) in uis.iter_mut().enumerate() { + ui.operate(&windows.renderers[i], operation.as_mut()); match operation.finish() { operation::Outcome::None => {} operation::Outcome::Some(message) => { proxy .send_event(Event::Application(message)) - .expect("Send message to event loop"); + .expect("Event loop doesn't exist."); + + // operation completed, don't need to try to operate on rest of UIs + break 'operate; } operation::Outcome::Chain(next) => { current_operation = Some(next); @@ -1093,55 +1013,105 @@ pub fn run_command( } } - let user_interfaces: HashMap<_, _> = user_interfaces - .drain() - .map(|(id, interface)| (id, interface.into_cache())) - .collect(); + *ui_caches = + uis.drain(..).map(UserInterface::into_cache).collect(); + } + command::Action::LoadFont { bytes, tagger } => { + use crate::core::text::Renderer; + + // TODO change this once we change each renderer to having a single backend reference.. :pain: + // TODO: Error handling (?) + for renderer in &mut windows.renderers { + renderer.load_font(bytes.clone()); + } - current_caches = user_interfaces; - *caches = current_caches; + proxy + .send_event(Event::Application(tagger(Ok(())))) + .expect("Send message to event loop"); } } } } -/// Build the user interfaces for every window. -pub fn build_user_interfaces<'a, A>( +/// Build the user interface for every window. +pub fn build_user_interfaces<'a, A: Application, C: Compositor>( application: &'a A, - renderer: &mut A::Renderer, debug: &mut Debug, - states: &HashMap>, - mut cached_user_interfaces: HashMap, -) -> HashMap< - window::Id, - UserInterface< - 'a, - ::Message, - ::Renderer, - >, -> + windows: &mut Windows, + mut cached_user_interfaces: Vec, +) -> Vec> where - A: Application + 'static, - ::Theme: StyleSheet, + ::Theme: StyleSheet, + C: Compositor, { - let mut interfaces = HashMap::new(); - - for (id, cache) in cached_user_interfaces.drain() { - let state = &states.get(&id).unwrap(); - - let user_interface = build_user_interface( - application, - cache, - renderer, - state.logical_size(), - debug, - id, - ); + cached_user_interfaces + .drain(..) + .zip( + windows + .ids + .iter() + .zip(windows.states.iter().zip(windows.renderers.iter_mut())), + ) + .fold(vec![], |mut uis, (cache, (id, (state, renderer)))| { + uis.push(build_user_interface( + application, + cache, + renderer, + state.logical_size(), + debug, + *id, + )); + + uis + }) +} - let _ = interfaces.insert(id, user_interface); +/// Returns true if the provided event should cause an [`Application`] to +/// exit. +pub fn requests_exit( + window: usize, + exit_on_main_closed: bool, + event: &winit::event::WindowEvent<'_>, + _modifiers: winit::event::ModifiersState, +) -> bool { + use winit::event::WindowEvent; + + //TODO alt f4..? + match event { + WindowEvent::CloseRequested => exit_on_main_closed && window == 0, + #[cfg(target_os = "macos")] + WindowEvent::KeyboardInput { + input: + winit::event::KeyboardInput { + virtual_keycode: Some(winit::event::VirtualKeyCode::Q), + state: winit::event::ElementState::Pressed, + .. + }, + .. + } if _modifiers.logo() => true, + _ => false, } +} + +fn logical_bounds_of( + window: &winit::window::Window, +) -> Option<((i32, i32), Size)> { + let scale = window.scale_factor(); + let pos = window + .inner_position() + .map(|pos| { + ((pos.x as f64 / scale) as i32, (pos.y as f64 / scale) as i32) + }) + .ok()?; + let size = { + let size = window.inner_size(); + Size::new( + (size.width as f64 / scale) as u32, + (size.height as f64 / scale) as u32, + ) + }; - interfaces + Some((pos, size)) } #[cfg(not(target_arch = "wasm32"))] -- cgit From 83c7870c569a2976923ee6243a19813094d44673 Mon Sep 17 00:00:00 2001 From: Bingus Date: Mon, 24 Jul 2023 14:32:59 -0700 Subject: Moved `exit_on_close_request` to window settings. This now controls whether each INDIVIDUAL window should close on CloseRequested events. --- winit/src/multi_window.rs | 148 ++++++++++++++++++++++++++++------------------ 1 file changed, 90 insertions(+), 58 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index e6f440bc..b67c0a48 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -46,7 +46,14 @@ pub enum Event { /// An internal event for closing a window. CloseWindow(window::Id), /// An internal event for when the window has finished being created. - WindowCreated(window::Id, winit::window::Window), + WindowCreated { + /// The internal ID of the window. + id: window::Id, + /// The raw window. + window: winit::window::Window, + /// Whether or not the window should close when a user requests it does. + exit_on_close_request: bool, + }, } #[allow(unsafe_code)] @@ -161,6 +168,8 @@ where }; let should_main_be_visible = settings.window.visible; + let exit_on_close_request = settings.window.exit_on_close_request; + let builder = window_builder( settings.window, &application.title(window::Id::MAIN), @@ -208,8 +217,13 @@ where let (mut compositor, renderer) = C::new(compositor_settings, Some(&main_window))?; - let windows = - Windows::new(&application, &mut compositor, renderer, main_window); + let windows = Windows::new( + &application, + &mut compositor, + renderer, + main_window, + exit_on_close_request, + ); let (mut event_sender, event_receiver) = mpsc::unbounded(); let (control_sender, mut control_receiver) = mpsc::unbounded(); @@ -225,7 +239,6 @@ where init_command, windows, should_main_be_visible, - settings.exit_on_close_request, )); let mut context = task::Context::from_waker(task::noop_waker_ref()); @@ -255,14 +268,18 @@ where title, monitor, }) => { + let exit_on_close_request = settings.exit_on_close_request; + let window = settings::window_builder(settings, &title, monitor, None) .build(window_target) .expect("Failed to build window"); - Some(winit::event::Event::UserEvent(Event::WindowCreated( - id, window, - ))) + Some(winit::event::Event::UserEvent(Event::WindowCreated { + id, + window, + exit_on_close_request, + })) } _ => event.to_static(), }; @@ -299,7 +316,6 @@ async fn run_instance( init_command: Command, mut windows: Windows, should_main_window_be_visible: bool, - exit_on_main_closed: bool, ) where A: Application + 'static, E: Executor + 'static, @@ -548,11 +564,20 @@ async fn run_instance( Event::Application(message) => { messages.push(message); } - Event::WindowCreated(id, window) => { + Event::WindowCreated { + id, + window, + exit_on_close_request, + } => { let bounds = logical_bounds_of(&window); - let (inner_size, i) = - windows.add(&application, &mut compositor, id, window); + let (inner_size, i) = windows.add( + &application, + &mut compositor, + id, + window, + exit_on_close_request, + ); user_interfaces.push(build_user_interface( &application, @@ -680,50 +705,61 @@ async fn run_instance( event: window_event, window_id, } => { - let window_deleted = windows - .pending_destroy - .iter() - .any(|(_, w_id)| window_id == *w_id); + let window_index = + windows.raw.iter().position(|w| w.id() == window_id); + + match window_index { + Some(i) => { + let id = windows.ids[i]; + let raw = &windows.raw[i]; + let exit_on_close_request = + windows.exit_on_close_requested[i]; + + if matches!( + window_event, + winit::event::WindowEvent::CloseRequested + ) && exit_on_close_request + { + let i = windows.delete(id); + let _ = user_interfaces.remove(i); + let _ = ui_caches.remove(i); + + if windows.is_empty() { + break 'main; + } + } else { + let state = &mut windows.states[i]; + state.update(raw, &window_event, &mut debug); - if matches!(window_event, winit::event::WindowEvent::Destroyed) - { - // This is the only special case, since in order trigger the Destroyed event the - // window reference from winit must be dropped, but we still want to inform the - // user that the window was destroyed so they can clean up any specific window - // code for this window - let id = windows.get_pending_destroy(window_id); - - events.push(( - None, - core::Event::Window(id, window::Event::Destroyed), - )); - } else if !window_deleted { - let i = windows.index_from_raw(window_id); - let id = windows.ids[i]; - let raw = &windows.raw[i]; - let state = &mut windows.states[i]; - - // first check if we need to just break the entire application - // e.g. a user does a force quit on MacOS, or if a user has set "exit on main closed" - // as an option in window settings and wants to close the main window - if requests_exit( - i, - exit_on_main_closed, - &window_event, - state.modifiers(), - ) { - break 'main; + if let Some(event) = conversion::window_event( + id, + &window_event, + state.scale_factor(), + state.modifiers(), + ) { + events.push((Some(id), event)); + } + } } - - state.update(raw, &window_event, &mut debug); - - if let Some(event) = conversion::window_event( - id, - &window_event, - state.scale_factor(), - state.modifiers(), - ) { - events.push((Some(id), event)); + None => { + // This is the only special case, since in order to trigger the Destroyed event the + // window reference from winit must be dropped, but we still want to inform the + // user that the window was destroyed so they can clean up any specific window + // code for this window + if matches!( + window_event, + winit::event::WindowEvent::Destroyed + ) { + let id = windows.get_pending_destroy(window_id); + + events.push(( + None, + core::Event::Window( + id, + window::Event::Destroyed, + ), + )); + } } } } @@ -1068,17 +1104,13 @@ where /// Returns true if the provided event should cause an [`Application`] to /// exit. -pub fn requests_exit( - window: usize, - exit_on_main_closed: bool, +pub fn user_force_quit( event: &winit::event::WindowEvent<'_>, _modifiers: winit::event::ModifiersState, ) -> bool { use winit::event::WindowEvent; - //TODO alt f4..? match event { - WindowEvent::CloseRequested => exit_on_main_closed && window == 0, #[cfg(target_os = "macos")] WindowEvent::KeyboardInput { input: -- cgit From 7def3ee38a3f0f24a331d722b09f325fc9584625 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 29 Nov 2023 22:37:54 +0100 Subject: Fix `clippy` lints --- winit/src/multi_window.rs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index f2452eb3..b233564a 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -912,7 +912,7 @@ pub fn run_command( size.width, size.height, )))) - .expect("Send message to event loop") + .expect("Send message to event loop"); } window::Action::Maximize(maximized) => { windows.with_raw(id).set_maximized(maximized); @@ -934,7 +934,9 @@ pub fn run_command( )); } window::Action::ChangeIcon(icon) => { - windows.with_raw(id).set_window_icon(conversion::icon(icon)) + windows + .with_raw(id) + .set_window_icon(conversion::icon(icon)); } window::Action::FetchMode(tag) => { let window = windows.with_raw(id); @@ -969,12 +971,14 @@ pub fn run_command( .with_raw(id) .set_window_level(conversion::window_level(level)); } - window::Action::FetchId(tag) => proxy - .send_event(Event::Application(tag(windows - .with_raw(id) - .id() - .into()))) - .expect("Event loop doesn't exist."), + window::Action::FetchId(tag) => { + proxy + .send_event(Event::Application(tag(windows + .with_raw(id) + .id() + .into()))) + .expect("Event loop doesn't exist."); + } window::Action::Screenshot(tag) => { let i = windows.index_from_id(id); let state = &windows.states[i]; @@ -996,7 +1000,7 @@ pub fn run_command( state.physical_size(), ), ))) - .expect("Event loop doesn't exist.") + .expect("Event loop doesn't exist."); } }, command::Action::System(action) => match action { @@ -1014,7 +1018,7 @@ pub fn run_command( proxy .send_event(Event::Application(message)) - .expect("Event loop doesn't exist.") + .expect("Event loop doesn't exist."); }); } } -- cgit From 6740c2c5d6b24399dab1343abdfec5daf4b03c98 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 29 Nov 2023 22:46:47 +0100 Subject: Fix broken intra-doc links --- winit/src/multi_window.rs | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index b233564a..0e08a081 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -23,34 +23,19 @@ use std::mem::ManuallyDrop; use std::time::Instant; use winit::monitor::MonitorHandle; -/// This is a wrapper around the `Application::Message` associate type -/// to allows the `shell` to create internal messages, while still having -/// the current user-specified custom messages. #[derive(Debug)] -pub enum Event { - /// An internal event which contains an [`Application`] generated message. +enum Event { Application(Message), - /// An internal event which spawns a new window. NewWindow { - /// The [window::Id] of the newly spawned [`Window`]. id: window::Id, - /// The [settings::Window] of the newly spawned [`Window`]. settings: window::Settings, - /// The title of the newly spawned [`Window`]. title: String, - /// The monitor on which to spawn the window. If `None`, will use monitor of the last window - /// spawned. monitor: Option, }, - /// An internal event for closing a window. CloseWindow(window::Id), - /// An internal event for when the window has finished being created. WindowCreated { - /// The internal ID of the window. id: window::Id, - /// The raw window. window: winit::window::Window, - /// Whether or not the window should close when a user requests it does. exit_on_close_request: bool, }, } @@ -771,7 +756,7 @@ async fn run_instance( } /// Builds a window's [`UserInterface`] for the [`Application`]. -pub fn build_user_interface<'a, A: Application>( +fn build_user_interface<'a, A: Application>( application: &'a A, cache: user_interface::Cache, renderer: &mut A::Renderer, @@ -795,7 +780,7 @@ where /// Updates a multi-window [`Application`] by feeding it messages, spawning any /// resulting [`Command`], and tracking its [`Subscription`]. -pub fn update( +fn update( application: &mut A, compositor: &mut C, runtime: &mut Runtime>, Event>, @@ -834,7 +819,7 @@ pub fn update( } /// Runs the actions of a [`Command`]. -pub fn run_command( +fn run_command( application: &A, compositor: &mut C, command: Command, -- cgit From ac12d2d099d9ae996d0ccfdc7e5b82d9cef990ee Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 29 Nov 2023 22:50:35 +0100 Subject: Remove unnecessary unsafe `Send` marker in `iced_winit` --- winit/src/multi_window.rs | 3 --- 1 file changed, 3 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 0e08a081..ef142c77 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -40,9 +40,6 @@ enum Event { }, } -#[allow(unsafe_code)] -unsafe impl std::marker::Send for Event {} - /// An interactive, native, cross-platform, multi-windowed application. /// /// This trait is the main entrypoint of multi-window Iced. Once implemented, you can run -- cgit From d34bc4e4a251bb28854770575d379d4a53f2db12 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 29 Nov 2023 23:55:17 +0100 Subject: Refactor event loop <-> instance communication in `multi_window` --- winit/src/multi_window.rs | 871 ++++++++++++++++++++++++---------------------- 1 file changed, 452 insertions(+), 419 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index ef142c77..f4ebbe09 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -8,7 +8,7 @@ use crate::conversion; use crate::core::widget::operation; use crate::core::{self, mouse, renderer, window, Size}; use crate::futures::futures::channel::mpsc; -use crate::futures::futures::{task, Future, FutureExt, StreamExt}; +use crate::futures::futures::{task, Future, StreamExt}; use crate::futures::{Executor, Runtime, Subscription}; use crate::graphics::{compositor, Compositor}; use crate::multi_window::windows::Windows; @@ -21,23 +21,24 @@ use crate::{Clipboard, Error, Proxy, Settings}; use std::mem::ManuallyDrop; use std::time::Instant; -use winit::monitor::MonitorHandle; -#[derive(Debug)] -enum Event { - Application(Message), - NewWindow { - id: window::Id, - settings: window::Settings, - title: String, - monitor: Option, - }, - CloseWindow(window::Id), +enum Event { WindowCreated { id: window::Id, window: winit::window::Window, exit_on_close_request: bool, }, + EventLoopAwakened(winit::event::Event<'static, Message>), +} + +enum Control { + ChangeFlow(winit::event_loop::ControlFlow), + CreateWindow { + id: window::Id, + settings: window::Settings, + title: String, + monitor: Option, + }, } /// An interactive, native, cross-platform, multi-windowed application. @@ -243,44 +244,57 @@ where event: winit::event::WindowEvent::Resized(*new_inner_size), window_id, }), - winit::event::Event::UserEvent(Event::NewWindow { - id, - settings, - title, - monitor, - }) => { - let exit_on_close_request = settings.exit_on_close_request; - - let window = conversion::window_settings( - settings, &title, monitor, None, - ) - .build(window_target) - .expect("Failed to build window"); - - Some(winit::event::Event::UserEvent(Event::WindowCreated { - id, - window, - exit_on_close_request, - })) - } _ => event.to_static(), }; if let Some(event) = event { - event_sender.start_send(event).expect("Send event"); - - let poll = instance.as_mut().poll(&mut context); - - match poll { - task::Poll::Pending => { - if let Ok(Some(flow)) = control_receiver.try_next() { - *control_flow = flow; + event_sender + .start_send(Event::EventLoopAwakened(event)) + .expect("Send event"); + + loop { + let poll = instance.as_mut().poll(&mut context); + + match poll { + task::Poll::Pending => match control_receiver.try_next() { + Ok(Some(control)) => match control { + Control::ChangeFlow(flow) => { + *control_flow = flow; + } + Control::CreateWindow { + id, + settings, + title, + monitor, + } => { + let exit_on_close_request = + settings.exit_on_close_request; + + let window = conversion::window_settings( + settings, &title, monitor, None, + ) + .build(window_target) + .expect("Failed to build window"); + + event_sender + .start_send(Event::WindowCreated { + id, + window, + exit_on_close_request, + }) + .expect("Send event"); + } + }, + _ => { + break; + } + }, + task::Poll::Ready(_) => { + *control_flow = ControlFlow::Exit; + break; } - } - task::Poll::Ready(_) => { - *control_flow = ControlFlow::Exit; - } - }; + }; + } } }) } @@ -288,13 +302,11 @@ where async fn run_instance( mut application: A, mut compositor: C, - mut runtime: Runtime>, Event>, - mut proxy: winit::event_loop::EventLoopProxy>, + mut runtime: Runtime, A::Message>, + mut proxy: winit::event_loop::EventLoopProxy, mut debug: Debug, - mut event_receiver: mpsc::UnboundedReceiver< - winit::event::Event<'_, Event>, - >, - mut control_sender: mpsc::UnboundedSender, + mut event_receiver: mpsc::UnboundedReceiver>, + mut control_sender: mpsc::UnboundedSender, init_command: Command, mut windows: Windows, should_main_window_be_visible: bool, @@ -327,18 +339,14 @@ async fn run_instance( init_command, &mut runtime, &mut clipboard, + &mut control_sender, &mut proxy, &mut debug, &mut windows, &mut ui_caches, ); - runtime.track( - application - .subscription() - .map(Event::Application) - .into_recipes(), - ); + runtime.track(application.subscription().into_recipes()); let mut mouse_interaction = mouse::Interaction::default(); @@ -361,391 +369,409 @@ async fn run_instance( 'main: while let Some(event) = event_receiver.next().await { match event { - event::Event::NewEvents(start_cause) => { - redraw_pending = matches!( - start_cause, - event::StartCause::Init - | event::StartCause::Poll - | event::StartCause::ResumeTimeReached { .. } - ); - } - event::Event::MainEventsCleared => { - debug.event_processing_started(); - let mut uis_stale = false; - - for (i, id) in windows.ids.iter().enumerate() { - let mut window_events = vec![]; - - events.retain(|(window_id, event)| { - if *window_id == Some(*id) || window_id.is_none() { - window_events.push(event.clone()); - false - } else { - true - } - }); - - if !redraw_pending - && window_events.is_empty() - && messages.is_empty() - { - continue; - } + Event::WindowCreated { + id, + window, + exit_on_close_request, + } => { + let bounds = logical_bounds_of(&window); - let (ui_state, statuses) = user_interfaces[i].update( - &window_events, - windows.states[i].cursor(), - &mut windows.renderers[i], - &mut clipboard, - &mut messages, - ); + let (inner_size, i) = windows.add( + &application, + &mut compositor, + id, + window, + exit_on_close_request, + ); - if !uis_stale { - uis_stale = - matches!(ui_state, user_interface::State::Outdated); - } + user_interfaces.push(build_user_interface( + &application, + user_interface::Cache::default(), + &mut windows.renderers[i], + inner_size, + &mut debug, + id, + )); + ui_caches.push(user_interface::Cache::default()); - for (event, status) in - window_events.into_iter().zip(statuses.into_iter()) - { - runtime.broadcast(event, status); - } + if let Some(bounds) = bounds { + events.push(( + Some(id), + core::Event::Window( + id, + window::Event::Created { + position: bounds.0, + size: bounds.1, + }, + ), + )); } - - debug.event_processing_finished(); - - // TODO mw application update returns which window IDs to update - if !messages.is_empty() || uis_stale { - let mut cached_interfaces: Vec = - ManuallyDrop::into_inner(user_interfaces) - .drain(..) - .map(UserInterface::into_cache) - .collect(); - - // Update application - update( - &mut application, - &mut compositor, - &mut runtime, - &mut clipboard, - &mut proxy, - &mut debug, - &mut messages, - &mut windows, - &mut cached_interfaces, - ); - - // we must synchronize all window states with application state after an - // application update since we don't know what changed - for (state, (id, window)) in windows - .states - .iter_mut() - .zip(windows.ids.iter().zip(windows.raw.iter())) - { - state.synchronize(&application, *id, window); + } + Event::EventLoopAwakened(event) => { + match event { + event::Event::NewEvents(start_cause) => { + redraw_pending = matches!( + start_cause, + event::StartCause::Init + | event::StartCause::Poll + | event::StartCause::ResumeTimeReached { .. } + ); } + event::Event::MainEventsCleared => { + debug.event_processing_started(); + let mut uis_stale = false; + + for (i, id) in windows.ids.iter().enumerate() { + let mut window_events = vec![]; + + events.retain(|(window_id, event)| { + if *window_id == Some(*id) + || window_id.is_none() + { + window_events.push(event.clone()); + false + } else { + true + } + }); + + if !redraw_pending + && window_events.is_empty() + && messages.is_empty() + { + continue; + } - // rebuild UIs with the synchronized states - user_interfaces = ManuallyDrop::new(build_user_interfaces( - &application, - &mut debug, - &mut windows, - cached_interfaces, - )); - } + let (ui_state, statuses) = user_interfaces[i] + .update( + &window_events, + windows.states[i].cursor(), + &mut windows.renderers[i], + &mut clipboard, + &mut messages, + ); + + if !uis_stale { + uis_stale = matches!( + ui_state, + user_interface::State::Outdated + ); + } - debug.draw_started(); - - for (i, id) in windows.ids.iter().enumerate() { - // TODO: Avoid redrawing all the time by forcing widgets to - // request redraws on state changes - // - // Then, we can use the `interface_state` here to decide if a redraw - // is needed right away, or simply wait until a specific time. - let redraw_event = core::Event::Window( - *id, - window::Event::RedrawRequested(Instant::now()), - ); + for (event, status) in window_events + .into_iter() + .zip(statuses.into_iter()) + { + runtime.broadcast(event, status); + } + } - let cursor = windows.states[i].cursor(); + debug.event_processing_finished(); + + // TODO mw application update returns which window IDs to update + if !messages.is_empty() || uis_stale { + let mut cached_interfaces: Vec< + user_interface::Cache, + > = ManuallyDrop::into_inner(user_interfaces) + .drain(..) + .map(UserInterface::into_cache) + .collect(); + + // Update application + update( + &mut application, + &mut compositor, + &mut runtime, + &mut clipboard, + &mut control_sender, + &mut proxy, + &mut debug, + &mut messages, + &mut windows, + &mut cached_interfaces, + ); - let (ui_state, _) = user_interfaces[i].update( - &[redraw_event.clone()], - cursor, - &mut windows.renderers[i], - &mut clipboard, - &mut messages, - ); + // we must synchronize all window states with application state after an + // application update since we don't know what changed + for (state, (id, window)) in windows + .states + .iter_mut() + .zip(windows.ids.iter().zip(windows.raw.iter())) + { + state.synchronize(&application, *id, window); + } - let new_mouse_interaction = { - let state = &windows.states[i]; + // rebuild UIs with the synchronized states + user_interfaces = + ManuallyDrop::new(build_user_interfaces( + &application, + &mut debug, + &mut windows, + cached_interfaces, + )); + } - user_interfaces[i].draw( - &mut windows.renderers[i], - state.theme(), - &renderer::Style { - text_color: state.text_color(), - }, - cursor, - ) - }; + debug.draw_started(); + + for (i, id) in windows.ids.iter().enumerate() { + // TODO: Avoid redrawing all the time by forcing widgets to + // request redraws on state changes + // + // Then, we can use the `interface_state` here to decide if a redraw + // is needed right away, or simply wait until a specific time. + let redraw_event = core::Event::Window( + *id, + window::Event::RedrawRequested(Instant::now()), + ); - if new_mouse_interaction != mouse_interaction { - windows.raw[i].set_cursor_icon( - conversion::mouse_interaction( - new_mouse_interaction, - ), - ); + let cursor = windows.states[i].cursor(); - mouse_interaction = new_mouse_interaction; - } + let (ui_state, _) = user_interfaces[i].update( + &[redraw_event.clone()], + cursor, + &mut windows.renderers[i], + &mut clipboard, + &mut messages, + ); - // TODO once widgets can request to be redrawn, we can avoid always requesting a - // redraw - windows.raw[i].request_redraw(); + let new_mouse_interaction = { + let state = &windows.states[i]; + + user_interfaces[i].draw( + &mut windows.renderers[i], + state.theme(), + &renderer::Style { + text_color: state.text_color(), + }, + cursor, + ) + }; + + if new_mouse_interaction != mouse_interaction { + windows.raw[i].set_cursor_icon( + conversion::mouse_interaction( + new_mouse_interaction, + ), + ); + + mouse_interaction = new_mouse_interaction; + } - runtime.broadcast( - redraw_event.clone(), - core::event::Status::Ignored, - ); + // TODO once widgets can request to be redrawn, we can avoid always requesting a + // redraw + windows.raw[i].request_redraw(); - let _ = control_sender.start_send(match ui_state { - user_interface::State::Updated { - redraw_request: Some(redraw_request), - } => match redraw_request { - window::RedrawRequest::NextFrame => { - ControlFlow::Poll - } - window::RedrawRequest::At(at) => { - ControlFlow::WaitUntil(at) - } - }, - _ => ControlFlow::Wait, - }); - } + runtime.broadcast( + redraw_event.clone(), + core::event::Status::Ignored, + ); - redraw_pending = false; + let _ = control_sender.start_send( + Control::ChangeFlow(match ui_state { + user_interface::State::Updated { + redraw_request: Some(redraw_request), + } => match redraw_request { + window::RedrawRequest::NextFrame => { + ControlFlow::Poll + } + window::RedrawRequest::At(at) => { + ControlFlow::WaitUntil(at) + } + }, + _ => ControlFlow::Wait, + }), + ); + } - debug.draw_finished(); - } - event::Event::PlatformSpecific(event::PlatformSpecific::MacOS( - event::MacOS::ReceivedUrl(url), - )) => { - use crate::core::event; + redraw_pending = false; - events.push(( - None, + debug.draw_finished(); + } event::Event::PlatformSpecific( event::PlatformSpecific::MacOS( event::MacOS::ReceivedUrl(url), ), - ), - )); - } - event::Event::UserEvent(event) => match event { - Event::Application(message) => { - messages.push(message); - } - Event::WindowCreated { - id, - window, - exit_on_close_request, - } => { - let bounds = logical_bounds_of(&window); - - let (inner_size, i) = windows.add( - &application, - &mut compositor, - id, - window, - exit_on_close_request, - ); + ) => { + use crate::core::event; - user_interfaces.push(build_user_interface( - &application, - user_interface::Cache::default(), - &mut windows.renderers[i], - inner_size, - &mut debug, - id, - )); - ui_caches.push(user_interface::Cache::default()); - - if let Some(bounds) = bounds { events.push(( - Some(id), - core::Event::Window( - id, - window::Event::Created { - position: bounds.0, - size: bounds.1, - }, + None, + event::Event::PlatformSpecific( + event::PlatformSpecific::MacOS( + event::MacOS::ReceivedUrl(url), + ), ), )); } - } - Event::CloseWindow(id) => { - let i = windows.delete(id); - let _ = user_interfaces.remove(i); - let _ = ui_caches.remove(i); - - if windows.is_empty() { - break 'main; + event::Event::UserEvent(message) => { + messages.push(message); } - } - Event::NewWindow { .. } => unreachable!(), - }, - event::Event::RedrawRequested(id) => { - let i = windows.index_from_raw(id); - let state = &windows.states[i]; - let physical_size = state.physical_size(); - - if physical_size.width == 0 || physical_size.height == 0 { - continue; - } - - debug.render_started(); - let current_viewport_version = state.viewport_version(); - let window_viewport_version = windows.viewport_versions[i]; + event::Event::RedrawRequested(id) => { + let i = windows.index_from_raw(id); + let state = &windows.states[i]; + let physical_size = state.physical_size(); - if window_viewport_version != current_viewport_version { - let logical_size = state.logical_size(); + if physical_size.width == 0 || physical_size.height == 0 + { + continue; + } - debug.layout_started(); + debug.render_started(); + let current_viewport_version = state.viewport_version(); + let window_viewport_version = + windows.viewport_versions[i]; - let renderer = &mut windows.renderers[i]; - let ui = user_interfaces.remove(i); + if window_viewport_version != current_viewport_version { + let logical_size = state.logical_size(); - user_interfaces - .insert(i, ui.relayout(logical_size, renderer)); + debug.layout_started(); - debug.layout_finished(); + let renderer = &mut windows.renderers[i]; + let ui = user_interfaces.remove(i); - debug.draw_started(); - let new_mouse_interaction = user_interfaces[i].draw( - renderer, - state.theme(), - &renderer::Style { - text_color: state.text_color(), - }, - state.cursor(), - ); + user_interfaces + .insert(i, ui.relayout(logical_size, renderer)); - if new_mouse_interaction != mouse_interaction { - windows.raw[i].set_cursor_icon( - conversion::mouse_interaction( - new_mouse_interaction, - ), - ); + debug.layout_finished(); - mouse_interaction = new_mouse_interaction; - } - debug.draw_finished(); + debug.draw_started(); + let new_mouse_interaction = user_interfaces[i] + .draw( + renderer, + state.theme(), + &renderer::Style { + text_color: state.text_color(), + }, + state.cursor(), + ); - compositor.configure_surface( - &mut windows.surfaces[i], - physical_size.width, - physical_size.height, - ); + if new_mouse_interaction != mouse_interaction { + windows.raw[i].set_cursor_icon( + conversion::mouse_interaction( + new_mouse_interaction, + ), + ); - windows.viewport_versions[i] = current_viewport_version; - } + mouse_interaction = new_mouse_interaction; + } + debug.draw_finished(); - match compositor.present( - &mut windows.renderers[i], - &mut windows.surfaces[i], - state.viewport(), - state.background_color(), - &debug.overlay(), - ) { - Ok(()) => { - debug.render_finished(); - - // TODO: Handle animations! - // Maybe we can use `ControlFlow::WaitUntil` for this. - } - Err(error) => match error { - // This is an unrecoverable error. - compositor::SurfaceError::OutOfMemory => { - panic!("{:?}", error); - } - _ => { - debug.render_finished(); - log::error!( - "Error {error:?} when presenting surface." + compositor.configure_surface( + &mut windows.surfaces[i], + physical_size.width, + physical_size.height, ); - // Try rendering all windows again next frame. - for window in &windows.raw { - window.request_redraw(); - } + windows.viewport_versions[i] = + current_viewport_version; } - }, - } - } - event::Event::WindowEvent { - event: window_event, - window_id, - } => { - let window_index = - windows.raw.iter().position(|w| w.id() == window_id); - - match window_index { - Some(i) => { - let id = windows.ids[i]; - let raw = &windows.raw[i]; - let exit_on_close_request = - windows.exit_on_close_requested[i]; - - if matches!( - window_event, - winit::event::WindowEvent::CloseRequested - ) && exit_on_close_request - { - let i = windows.delete(id); - let _ = user_interfaces.remove(i); - let _ = ui_caches.remove(i); - if windows.is_empty() { - break 'main; - } - } else { - let state = &mut windows.states[i]; - state.update(raw, &window_event, &mut debug); + match compositor.present( + &mut windows.renderers[i], + &mut windows.surfaces[i], + state.viewport(), + state.background_color(), + &debug.overlay(), + ) { + Ok(()) => { + debug.render_finished(); - if let Some(event) = conversion::window_event( - id, - &window_event, - state.scale_factor(), - state.modifiers(), - ) { - events.push((Some(id), event)); + // TODO: Handle animations! + // Maybe we can use `ControlFlow::WaitUntil` for this. } + Err(error) => match error { + // This is an unrecoverable error. + compositor::SurfaceError::OutOfMemory => { + panic!("{:?}", error); + } + _ => { + debug.render_finished(); + log::error!( + "Error {error:?} when presenting surface." + ); + + // Try rendering all windows again next frame. + for window in &windows.raw { + window.request_redraw(); + } + } + }, } } - None => { - // This is the only special case, since in order to trigger the Destroyed event the - // window reference from winit must be dropped, but we still want to inform the - // user that the window was destroyed so they can clean up any specific window - // code for this window - if matches!( - window_event, - winit::event::WindowEvent::Destroyed - ) { - let id = windows.get_pending_destroy(window_id); - - events.push(( - None, - core::Event::Window( - id, - window::Event::Destroyed, - ), - )); + event::Event::WindowEvent { + event: window_event, + window_id, + } => { + let window_index = windows + .raw + .iter() + .position(|w| w.id() == window_id); + + match window_index { + Some(i) => { + let id = windows.ids[i]; + let raw = &windows.raw[i]; + let exit_on_close_request = + windows.exit_on_close_requested[i]; + + if matches!( + window_event, + winit::event::WindowEvent::CloseRequested + ) && exit_on_close_request + { + let i = windows.delete(id); + let _ = user_interfaces.remove(i); + let _ = ui_caches.remove(i); + + if windows.is_empty() { + break 'main; + } + } else { + let state = &mut windows.states[i]; + state.update( + raw, + &window_event, + &mut debug, + ); + + if let Some(event) = + conversion::window_event( + id, + &window_event, + state.scale_factor(), + state.modifiers(), + ) + { + events.push((Some(id), event)); + } + } + } + None => { + // This is the only special case, since in order to trigger the Destroyed event the + // window reference from winit must be dropped, but we still want to inform the + // user that the window was destroyed so they can clean up any specific window + // code for this window + if matches!( + window_event, + winit::event::WindowEvent::Destroyed + ) { + let id = + windows.get_pending_destroy(window_id); + + events.push(( + None, + core::Event::Window( + id, + window::Event::Destroyed, + ), + )); + } + } } } + _ => {} } } - _ => {} } } @@ -780,9 +806,10 @@ where fn update( application: &mut A, compositor: &mut C, - runtime: &mut Runtime>, Event>, + runtime: &mut Runtime, A::Message>, clipboard: &mut Clipboard, - proxy: &mut winit::event_loop::EventLoopProxy>, + control_sender: &mut mpsc::UnboundedSender, + proxy: &mut winit::event_loop::EventLoopProxy, debug: &mut Debug, messages: &mut Vec, windows: &mut Windows, @@ -804,6 +831,7 @@ fn update( command, runtime, clipboard, + control_sender, proxy, debug, windows, @@ -811,7 +839,7 @@ fn update( ); } - let subscription = application.subscription().map(Event::Application); + let subscription = application.subscription(); runtime.track(subscription.into_recipes()); } @@ -820,9 +848,10 @@ fn run_command( application: &A, compositor: &mut C, command: Command, - runtime: &mut Runtime>, Event>, + runtime: &mut Runtime, A::Message>, clipboard: &mut Clipboard, - proxy: &mut winit::event_loop::EventLoopProxy>, + control_sender: &mut mpsc::UnboundedSender, + proxy: &mut winit::event_loop::EventLoopProxy, debug: &mut Debug, windows: &mut Windows, ui_caches: &mut Vec, @@ -839,17 +868,17 @@ fn run_command( for action in command.actions() { match action { command::Action::Future(future) => { - runtime.spawn(Box::pin(future.map(Event::Application))); + runtime.spawn(Box::pin(future)); } command::Action::Stream(stream) => { - runtime.run(Box::pin(stream.map(Event::Application))); + runtime.run(Box::pin(stream)); } command::Action::Clipboard(action) => match action { clipboard::Action::Read(tag) => { let message = tag(clipboard.read()); proxy - .send_event(Event::Application(message)) + .send_event(message) .expect("Send message to event loop"); } clipboard::Action::Write(contents) => { @@ -860,19 +889,28 @@ fn run_command( window::Action::Spawn { settings } => { let monitor = windows.last_monitor(); - proxy - .send_event(Event::NewWindow { + control_sender + .start_send(Control::CreateWindow { id, settings, title: application.title(id), monitor, }) - .expect("Send message to event loop"); + .expect("Send control action"); } window::Action::Close => { - proxy - .send_event(Event::CloseWindow(id)) - .expect("Send message to event loop"); + use winit::event_loop::ControlFlow; + + let i = windows.delete(id); + let _ = ui_caches.remove(i); + + if windows.is_empty() { + control_sender + .start_send(Control::ChangeFlow( + ControlFlow::ExitWithCode(0), + )) + .expect("Send control action"); + } } window::Action::Drag => { let _ = windows.with_raw(id).drag_window(); @@ -890,10 +928,10 @@ fn run_command( let size = window.inner_size(); proxy - .send_event(Event::Application(callback(Size::new( + .send_event(callback(Size::new( size.width, size.height, - )))) + ))) .expect("Send message to event loop"); } window::Action::Maximize(maximized) => { @@ -929,7 +967,7 @@ fn run_command( }; proxy - .send_event(Event::Application(tag(mode))) + .send_event(tag(mode)) .expect("Event loop doesn't exist."); } window::Action::ToggleMaximize => { @@ -955,10 +993,7 @@ fn run_command( } window::Action::FetchId(tag) => { proxy - .send_event(Event::Application(tag(windows - .with_raw(id) - .id() - .into()))) + .send_event(tag(windows.with_raw(id).id().into())) .expect("Event loop doesn't exist."); } window::Action::Screenshot(tag) => { @@ -976,11 +1011,9 @@ fn run_command( ); proxy - .send_event(Event::Application(tag( - window::Screenshot::new( - bytes, - state.physical_size(), - ), + .send_event(tag(window::Screenshot::new( + bytes, + state.physical_size(), ))) .expect("Event loop doesn't exist."); } @@ -999,7 +1032,7 @@ fn run_command( let message = _tag(information); proxy - .send_event(Event::Application(message)) + .send_event(message) .expect("Event loop doesn't exist."); }); } @@ -1025,7 +1058,7 @@ fn run_command( operation::Outcome::None => {} operation::Outcome::Some(message) => { proxy - .send_event(Event::Application(message)) + .send_event(message) .expect("Event loop doesn't exist."); // operation completed, don't need to try to operate on rest of UIs @@ -1051,7 +1084,7 @@ fn run_command( } proxy - .send_event(Event::Application(tagger(Ok(())))) + .send_event(tagger(Ok(()))) .expect("Send message to event loop"); } } -- cgit From 9f29aec128ccf51c620a8b69a9fbd64186ab8c65 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 30 Nov 2023 00:01:32 +0100 Subject: Move `Event` and `Control` types after `multi_window::run` --- winit/src/multi_window.rs | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index f4ebbe09..f8cedcb8 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -22,25 +22,6 @@ use crate::{Clipboard, Error, Proxy, Settings}; use std::mem::ManuallyDrop; use std::time::Instant; -enum Event { - WindowCreated { - id: window::Id, - window: winit::window::Window, - exit_on_close_request: bool, - }, - EventLoopAwakened(winit::event::Event<'static, Message>), -} - -enum Control { - ChangeFlow(winit::event_loop::ControlFlow), - CreateWindow { - id: window::Id, - settings: window::Settings, - title: String, - monitor: Option, - }, -} - /// An interactive, native, cross-platform, multi-windowed application. /// /// This trait is the main entrypoint of multi-window Iced. Once implemented, you can run @@ -299,6 +280,25 @@ where }) } +enum Event { + WindowCreated { + id: window::Id, + window: winit::window::Window, + exit_on_close_request: bool, + }, + EventLoopAwakened(winit::event::Event<'static, Message>), +} + +enum Control { + ChangeFlow(winit::event_loop::ControlFlow), + CreateWindow { + id: window::Id, + settings: window::Settings, + title: String, + monitor: Option, + }, +} + async fn run_instance( mut application: A, mut compositor: C, -- cgit From 67408311f45d341509538f8cc185978da66b6ace Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 30 Nov 2023 23:40:33 +0100 Subject: Use actual floats for logical coordinates --- winit/src/multi_window.rs | 89 ++++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 44 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index f8cedcb8..73476452 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -5,8 +5,12 @@ mod windows; pub use state::State; use crate::conversion; +use crate::core; +use crate::core::mouse; +use crate::core::renderer; use crate::core::widget::operation; -use crate::core::{self, mouse, renderer, window, Size}; +use crate::core::window; +use crate::core::{Point, Size}; use crate::futures::futures::channel::mpsc; use crate::futures::futures::{task, Future, StreamExt}; use crate::futures::{Executor, Runtime, Subscription}; @@ -350,18 +354,18 @@ async fn run_instance( let mut mouse_interaction = mouse::Interaction::default(); - let mut events = - if let Some((position, size)) = logical_bounds_of(windows.main()) { - vec![( - Some(window::Id::MAIN), - core::Event::Window( - window::Id::MAIN, - window::Event::Created { position, size }, - ), - )] - } else { - Vec::new() - }; + let mut events = { + let (position, size) = logical_bounds_of(windows.main()); + + vec![( + Some(window::Id::MAIN), + core::Event::Window( + window::Id::MAIN, + window::Event::Created { position, size }, + ), + )] + }; + let mut messages = Vec::new(); let mut redraw_pending = false; @@ -374,7 +378,7 @@ async fn run_instance( window, exit_on_close_request, } => { - let bounds = logical_bounds_of(&window); + let (position, size) = logical_bounds_of(&window); let (inner_size, i) = windows.add( &application, @@ -394,18 +398,13 @@ async fn run_instance( )); ui_caches.push(user_interface::Cache::default()); - if let Some(bounds) = bounds { - events.push(( - Some(id), - core::Event::Window( - id, - window::Event::Created { - position: bounds.0, - size: bounds.1, - }, - ), - )); - } + events.push(( + Some(id), + core::Event::Window( + id, + window::Event::Created { position, size }, + ), + )); } Event::EventLoopAwakened(event) => { match event { @@ -925,7 +924,8 @@ fn run_command( } window::Action::FetchSize(callback) => { let window = windows.with_raw(id); - let size = window.inner_size(); + let size = + window.inner_size().to_logical(window.scale_factor()); proxy .send_event(callback(Size::new( @@ -940,9 +940,12 @@ fn run_command( window::Action::Minimize(minimized) => { windows.with_raw(id).set_minimized(minimized); } - window::Action::Move { x, y } => { + window::Action::Move(position) => { windows.with_raw(id).set_outer_position( - winit::dpi::LogicalPosition { x, y }, + winit::dpi::LogicalPosition { + x: position.x, + y: position.y, + }, ); } window::Action::ChangeMode(mode) => { @@ -1145,25 +1148,23 @@ pub fn user_force_quit( } } -fn logical_bounds_of( - window: &winit::window::Window, -) -> Option<((i32, i32), Size)> { - let scale = window.scale_factor(); - let pos = window +fn logical_bounds_of(window: &winit::window::Window) -> (Option, Size) { + let position = window .inner_position() - .map(|pos| { - ((pos.x as f64 / scale) as i32, (pos.y as f64 / scale) as i32) - }) - .ok()?; + .ok() + .map(|position| position.to_logical(window.scale_factor())) + .map(|position| Point { + x: position.x, + y: position.y, + }); + let size = { - let size = window.inner_size(); - Size::new( - (size.width as f64 / scale) as u32, - (size.height as f64 / scale) as u32, - ) + let size = window.inner_size().to_logical(window.scale_factor()); + + Size::new(size.width, size.height) }; - Some((pos, size)) + (position, size) } #[cfg(not(target_arch = "wasm32"))] -- cgit From ea42af766f345715ff7a7168182d3896ee79cfbc Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 2 Dec 2023 20:41:58 +0100 Subject: Use `AtomicU64` for `window::Id` --- winit/src/multi_window.rs | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 73476452..aeb2c5e1 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -361,7 +361,7 @@ async fn run_instance( Some(window::Id::MAIN), core::Event::Window( window::Id::MAIN, - window::Event::Created { position, size }, + window::Event::Opened { position, size }, ), )] }; @@ -402,7 +402,7 @@ async fn run_instance( Some(id), core::Event::Window( id, - window::Event::Created { position, size }, + window::Event::Opened { position, size }, ), )); } @@ -761,7 +761,7 @@ async fn run_instance( None, core::Event::Window( id, - window::Event::Destroyed, + window::Event::Closed, ), )); } @@ -884,8 +884,8 @@ fn run_command( clipboard.write(contents); } }, - command::Action::Window(id, action) => match action { - window::Action::Spawn { settings } => { + command::Action::Window(action) => match action { + window::Action::Spawn(id, settings) => { let monitor = windows.last_monitor(); control_sender @@ -897,7 +897,7 @@ fn run_command( }) .expect("Send control action"); } - window::Action::Close => { + window::Action::Close(id) => { use winit::event_loop::ControlFlow; let i = windows.delete(id); @@ -911,10 +911,10 @@ fn run_command( .expect("Send control action"); } } - window::Action::Drag => { + window::Action::Drag(id) => { let _ = windows.with_raw(id).drag_window(); } - window::Action::Resize(size) => { + window::Action::Resize(id, size) => { windows.with_raw(id).set_inner_size( winit::dpi::LogicalSize { width: size.width, @@ -922,7 +922,7 @@ fn run_command( }, ); } - window::Action::FetchSize(callback) => { + window::Action::FetchSize(id, callback) => { let window = windows.with_raw(id); let size = window.inner_size().to_logical(window.scale_factor()); @@ -934,13 +934,13 @@ fn run_command( ))) .expect("Send message to event loop"); } - window::Action::Maximize(maximized) => { + window::Action::Maximize(id, maximized) => { windows.with_raw(id).set_maximized(maximized); } - window::Action::Minimize(minimized) => { + window::Action::Minimize(id, minimized) => { windows.with_raw(id).set_minimized(minimized); } - window::Action::Move(position) => { + window::Action::Move(id, position) => { windows.with_raw(id).set_outer_position( winit::dpi::LogicalPosition { x: position.x, @@ -948,7 +948,7 @@ fn run_command( }, ); } - window::Action::ChangeMode(mode) => { + window::Action::ChangeMode(id, mode) => { let window = windows.with_raw(id); window.set_visible(conversion::visible(mode)); window.set_fullscreen(conversion::fullscreen( @@ -956,12 +956,12 @@ fn run_command( mode, )); } - window::Action::ChangeIcon(icon) => { + window::Action::ChangeIcon(id, icon) => { windows .with_raw(id) .set_window_icon(conversion::icon(icon)); } - window::Action::FetchMode(tag) => { + window::Action::FetchMode(id, tag) => { let window = windows.with_raw(id); let mode = if window.is_visible().unwrap_or(true) { conversion::mode(window.fullscreen()) @@ -973,33 +973,33 @@ fn run_command( .send_event(tag(mode)) .expect("Event loop doesn't exist."); } - window::Action::ToggleMaximize => { + window::Action::ToggleMaximize(id) => { let window = windows.with_raw(id); window.set_maximized(!window.is_maximized()); } - window::Action::ToggleDecorations => { + window::Action::ToggleDecorations(id) => { let window = windows.with_raw(id); window.set_decorations(!window.is_decorated()); } - window::Action::RequestUserAttention(attention_type) => { + window::Action::RequestUserAttention(id, attention_type) => { windows.with_raw(id).request_user_attention( attention_type.map(conversion::user_attention), ); } - window::Action::GainFocus => { + window::Action::GainFocus(id) => { windows.with_raw(id).focus_window(); } - window::Action::ChangeLevel(level) => { + window::Action::ChangeLevel(id, level) => { windows .with_raw(id) .set_window_level(conversion::window_level(level)); } - window::Action::FetchId(tag) => { + window::Action::FetchId(id, tag) => { proxy .send_event(tag(windows.with_raw(id).id().into())) .expect("Event loop doesn't exist."); } - window::Action::Screenshot(tag) => { + window::Action::Screenshot(id, tag) => { let i = windows.index_from_id(id); let state = &windows.states[i]; let surface = &mut windows.surfaces[i]; -- cgit From b152ecda63238136f77b6eda3c582fa1eff99737 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 2 Dec 2023 20:49:47 +0100 Subject: Separate `Compositor::new` from `Compositor::create_renderer` --- winit/src/multi_window.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index aeb2c5e1..31c27a6d 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -181,8 +181,8 @@ where }; } - let (mut compositor, renderer) = - C::new(compositor_settings, Some(&main_window))?; + let mut compositor = C::new(compositor_settings, Some(&main_window))?; + let renderer = compositor.create_renderer(); let windows = Windows::new( &application, -- cgit From 5c5e7653bed248ba63faa6563e4d673e4441415e Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 2 Dec 2023 22:26:01 +0100 Subject: Refactor `Windows` abstraction into `WindowManager` --- winit/src/multi_window.rs | 620 +++++++++++++++++++++++----------------------- 1 file changed, 312 insertions(+), 308 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 31c27a6d..84651d40 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -1,21 +1,20 @@ //! Create interactive, native cross-platform applications for WGPU. mod state; -mod windows; +mod window_manager; pub use state::State; use crate::conversion; use crate::core; -use crate::core::mouse; use crate::core::renderer; use crate::core::widget::operation; use crate::core::window; -use crate::core::{Point, Size}; +use crate::core::Size; use crate::futures::futures::channel::mpsc; use crate::futures::futures::{task, Future, StreamExt}; use crate::futures::{Executor, Runtime, Subscription}; use crate::graphics::{compositor, Compositor}; -use crate::multi_window::windows::Windows; +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}; @@ -23,6 +22,7 @@ use crate::runtime::Debug; use crate::style::application::StyleSheet; use crate::{Clipboard, Error, Proxy, Settings}; +use std::collections::HashMap; use std::mem::ManuallyDrop; use std::time::Instant; @@ -182,13 +182,13 @@ where } let mut compositor = C::new(compositor_settings, Some(&main_window))?; - let renderer = compositor.create_renderer(); - let windows = Windows::new( + let mut window_manager = WindowManager::new(); + let _ = window_manager.insert( + window::Id::MAIN, + main_window, &application, &mut compositor, - renderer, - main_window, exit_on_close_request, ); @@ -204,7 +204,7 @@ where event_receiver, control_sender, init_command, - windows, + window_manager, should_main_be_visible, )); @@ -312,7 +312,7 @@ async fn run_instance( mut event_receiver: mpsc::UnboundedReceiver>, mut control_sender: mpsc::UnboundedSender, init_command: Command, - mut windows: Windows, + mut window_manager: WindowManager, should_main_window_be_visible: bool, ) where A: Application + 'static, @@ -323,20 +323,39 @@ async fn run_instance( use winit::event; use winit::event_loop::ControlFlow; - let mut clipboard = Clipboard::connect(windows.main()); + let main_window = window_manager + .get_mut(window::Id::MAIN) + .expect("Get main window"); + + if should_main_window_be_visible { + main_window.raw.set_visible(true); + } + + let mut clipboard = Clipboard::connect(&main_window.raw); + let mut events = { + vec![( + Some(window::Id::MAIN), + core::Event::Window( + window::Id::MAIN, + window::Event::Opened { + position: main_window.position(), + size: main_window.size(), + }, + ), + )] + }; - let mut ui_caches = vec![user_interface::Cache::default()]; + let mut ui_caches = HashMap::new(); let mut user_interfaces = ManuallyDrop::new(build_user_interfaces( &application, &mut debug, - &mut windows, - vec![user_interface::Cache::default()], + &mut window_manager, + HashMap::from_iter([( + window::Id::MAIN, + user_interface::Cache::default(), + )]), )); - if should_main_window_be_visible { - windows.main().set_visible(true); - } - run_command( &application, &mut compositor, @@ -346,26 +365,12 @@ async fn run_instance( &mut control_sender, &mut proxy, &mut debug, - &mut windows, + &mut window_manager, &mut ui_caches, ); runtime.track(application.subscription().into_recipes()); - let mut mouse_interaction = mouse::Interaction::default(); - - let mut events = { - let (position, size) = logical_bounds_of(windows.main()); - - vec![( - Some(window::Id::MAIN), - core::Event::Window( - window::Id::MAIN, - window::Event::Opened { position, size }, - ), - )] - }; - let mut messages = Vec::new(); let mut redraw_pending = false; @@ -378,31 +383,37 @@ async fn run_instance( window, exit_on_close_request, } => { - let (position, size) = logical_bounds_of(&window); - - let (inner_size, i) = windows.add( - &application, - &mut compositor, + let window = window_manager.insert( id, window, + &application, + &mut compositor, exit_on_close_request, ); - user_interfaces.push(build_user_interface( - &application, - user_interface::Cache::default(), - &mut windows.renderers[i], - inner_size, - &mut debug, + let logical_size = window.state.logical_size(); + + let _ = user_interfaces.insert( id, - )); - ui_caches.push(user_interface::Cache::default()); + build_user_interface( + &application, + user_interface::Cache::default(), + &mut window.renderer, + logical_size, + &mut debug, + id, + ), + ); + let _ = ui_caches.insert(id, user_interface::Cache::default()); events.push(( Some(id), core::Event::Window( id, - window::Event::Opened { position, size }, + window::Event::Opened { + position: window.position(), + size: window.size(), + }, ), )); } @@ -420,12 +431,11 @@ async fn run_instance( debug.event_processing_started(); let mut uis_stale = false; - for (i, id) in windows.ids.iter().enumerate() { + for (id, window) in window_manager.iter_mut() { let mut window_events = vec![]; events.retain(|(window_id, event)| { - if *window_id == Some(*id) - || window_id.is_none() + if *window_id == Some(id) || window_id.is_none() { window_events.push(event.clone()); false @@ -441,11 +451,13 @@ async fn run_instance( continue; } - let (ui_state, statuses) = user_interfaces[i] + let (ui_state, statuses) = user_interfaces + .get_mut(&id) + .expect("Get user interface") .update( &window_events, - windows.states[i].cursor(), - &mut windows.renderers[i], + window.state.cursor(), + &mut window.renderer, &mut clipboard, &mut messages, ); @@ -469,11 +481,12 @@ async fn run_instance( // TODO mw application update returns which window IDs to update if !messages.is_empty() || uis_stale { - let mut cached_interfaces: Vec< + let mut cached_interfaces: HashMap< + window::Id, user_interface::Cache, > = ManuallyDrop::into_inner(user_interfaces) - .drain(..) - .map(UserInterface::into_cache) + .drain() + .map(|(id, ui)| (id, ui.into_cache())) .collect(); // Update application @@ -486,18 +499,18 @@ async fn run_instance( &mut proxy, &mut debug, &mut messages, - &mut windows, + &mut window_manager, &mut cached_interfaces, ); // we must synchronize all window states with application state after an // application update since we don't know what changed - for (state, (id, window)) in windows - .states - .iter_mut() - .zip(windows.ids.iter().zip(windows.raw.iter())) - { - state.synchronize(&application, *id, window); + for (id, window) in window_manager.iter_mut() { + window.state.synchronize( + &application, + id, + &window.raw, + ); } // rebuild UIs with the synchronized states @@ -505,39 +518,43 @@ async fn run_instance( ManuallyDrop::new(build_user_interfaces( &application, &mut debug, - &mut windows, + &mut window_manager, cached_interfaces, )); } debug.draw_started(); - for (i, id) in windows.ids.iter().enumerate() { + for (id, window) in window_manager.iter_mut() { // TODO: Avoid redrawing all the time by forcing widgets to // request redraws on state changes // // Then, we can use the `interface_state` here to decide if a redraw // is needed right away, or simply wait until a specific time. let redraw_event = core::Event::Window( - *id, + id, window::Event::RedrawRequested(Instant::now()), ); - let cursor = windows.states[i].cursor(); + let cursor = window.state.cursor(); + + let ui = user_interfaces + .get_mut(&id) + .expect("Get user interface"); - let (ui_state, _) = user_interfaces[i].update( + let (ui_state, _) = ui.update( &[redraw_event.clone()], cursor, - &mut windows.renderers[i], + &mut window.renderer, &mut clipboard, &mut messages, ); let new_mouse_interaction = { - let state = &windows.states[i]; + let state = &window.state; - user_interfaces[i].draw( - &mut windows.renderers[i], + ui.draw( + &mut window.renderer, state.theme(), &renderer::Style { text_color: state.text_color(), @@ -546,19 +563,21 @@ async fn run_instance( ) }; - if new_mouse_interaction != mouse_interaction { - windows.raw[i].set_cursor_icon( + if new_mouse_interaction != window.mouse_interaction + { + window.raw.set_cursor_icon( conversion::mouse_interaction( new_mouse_interaction, ), ); - mouse_interaction = new_mouse_interaction; + window.mouse_interaction = + new_mouse_interaction; } // TODO once widgets can request to be redrawn, we can avoid always requesting a // redraw - windows.raw[i].request_redraw(); + window.raw.request_redraw(); runtime.broadcast( redraw_event.clone(), @@ -606,9 +625,13 @@ async fn run_instance( messages.push(message); } event::Event::RedrawRequested(id) => { - let i = windows.index_from_raw(id); - let state = &windows.states[i]; - let physical_size = state.physical_size(); + let Some((id, window)) = + window_manager.get_mut_alias(id) + else { + continue; + }; + + let physical_size = window.state.physical_size(); if physical_size.width == 0 || physical_size.height == 0 { @@ -616,60 +639,65 @@ async fn run_instance( } debug.render_started(); - let current_viewport_version = state.viewport_version(); - let window_viewport_version = - windows.viewport_versions[i]; - - if window_viewport_version != current_viewport_version { - let logical_size = state.logical_size(); + if window.viewport_version + != window.state.viewport_version() + { + let logical_size = window.state.logical_size(); debug.layout_started(); - let renderer = &mut windows.renderers[i]; - let ui = user_interfaces.remove(i); + let ui = user_interfaces + .remove(&id) + .expect("Remove user interface"); - user_interfaces - .insert(i, ui.relayout(logical_size, renderer)); + let _ = user_interfaces.insert( + id, + ui.relayout(logical_size, &mut window.renderer), + ); debug.layout_finished(); debug.draw_started(); - let new_mouse_interaction = user_interfaces[i] + let new_mouse_interaction = user_interfaces + .get_mut(&id) + .expect("Get user interface") .draw( - renderer, - state.theme(), + &mut window.renderer, + window.state.theme(), &renderer::Style { - text_color: state.text_color(), + text_color: window.state.text_color(), }, - state.cursor(), + window.state.cursor(), ); - if new_mouse_interaction != mouse_interaction { - windows.raw[i].set_cursor_icon( + if new_mouse_interaction != window.mouse_interaction + { + window.raw.set_cursor_icon( conversion::mouse_interaction( new_mouse_interaction, ), ); - mouse_interaction = new_mouse_interaction; + window.mouse_interaction = + new_mouse_interaction; } debug.draw_finished(); compositor.configure_surface( - &mut windows.surfaces[i], + &mut window.surface, physical_size.width, physical_size.height, ); - windows.viewport_versions[i] = - current_viewport_version; + window.viewport_version = + window.state.viewport_version(); } match compositor.present( - &mut windows.renderers[i], - &mut windows.surfaces[i], - state.viewport(), - state.background_color(), + &mut window.renderer, + &mut window.surface, + window.state.viewport(), + window.state.background_color(), &debug.overlay(), ) { Ok(()) => { @@ -690,8 +718,10 @@ async fn run_instance( ); // Try rendering all windows again next frame. - for window in &windows.raw { - window.request_redraw(); + for (_id, window) in + window_manager.iter_mut() + { + window.raw.request_redraw(); } } }, @@ -701,70 +731,43 @@ async fn run_instance( event: window_event, window_id, } => { - let window_index = windows - .raw - .iter() - .position(|w| w.id() == window_id); + let Some((id, window)) = + window_manager.get_mut_alias(window_id) + else { + continue; + }; - match window_index { - Some(i) => { - let id = windows.ids[i]; - let raw = &windows.raw[i]; - let exit_on_close_request = - windows.exit_on_close_requested[i]; + if matches!( + window_event, + winit::event::WindowEvent::CloseRequested + ) && window.exit_on_close_request + { + let _ = window_manager.remove(id); + let _ = user_interfaces.remove(&id); + let _ = ui_caches.remove(&id); - if matches!( - window_event, - winit::event::WindowEvent::CloseRequested - ) && exit_on_close_request - { - let i = windows.delete(id); - let _ = user_interfaces.remove(i); - let _ = ui_caches.remove(i); + events.push(( + None, + core::Event::Window(id, window::Event::Closed), + )); - if windows.is_empty() { - break 'main; - } - } else { - let state = &mut windows.states[i]; - state.update( - raw, - &window_event, - &mut debug, - ); - - if let Some(event) = - conversion::window_event( - id, - &window_event, - state.scale_factor(), - state.modifiers(), - ) - { - events.push((Some(id), event)); - } - } + if window_manager.is_empty() { + break 'main; } - None => { - // This is the only special case, since in order to trigger the Destroyed event the - // window reference from winit must be dropped, but we still want to inform the - // user that the window was destroyed so they can clean up any specific window - // code for this window - if matches!( - window_event, - winit::event::WindowEvent::Destroyed - ) { - let id = - windows.get_pending_destroy(window_id); - - events.push(( - None, - core::Event::Window( - id, - window::Event::Closed, - ), - )); - } + } else { + window.state.update( + &window.raw, + &window_event, + &mut debug, + ); + + if let Some(event) = conversion::window_event( + id, + &window_event, + window.state.scale_factor(), + window.state.modifiers(), + ) { + events.push((Some(id), event)); } } } @@ -811,8 +814,8 @@ fn update( proxy: &mut winit::event_loop::EventLoopProxy, debug: &mut Debug, messages: &mut Vec, - windows: &mut Windows, - ui_caches: &mut Vec, + window_manager: &mut WindowManager, + ui_caches: &mut HashMap, ) where C: Compositor + 'static, ::Theme: StyleSheet, @@ -833,7 +836,7 @@ fn update( control_sender, proxy, debug, - windows, + window_manager, ui_caches, ); } @@ -852,8 +855,8 @@ fn run_command( control_sender: &mut mpsc::UnboundedSender, proxy: &mut winit::event_loop::EventLoopProxy, debug: &mut Debug, - windows: &mut Windows, - ui_caches: &mut Vec, + window_manager: &mut WindowManager, + ui_caches: &mut HashMap, ) where A: Application, E: Executor, @@ -886,7 +889,7 @@ fn run_command( }, command::Action::Window(action) => match action { window::Action::Spawn(id, settings) => { - let monitor = windows.last_monitor(); + let monitor = window_manager.last_monitor(); control_sender .start_send(Control::CreateWindow { @@ -900,10 +903,10 @@ fn run_command( window::Action::Close(id) => { use winit::event_loop::ControlFlow; - let i = windows.delete(id); - let _ = ui_caches.remove(i); + let _ = window_manager.remove(id); + let _ = ui_caches.remove(&id); - if windows.is_empty() { + if window_manager.is_empty() { control_sender .start_send(Control::ChangeFlow( ControlFlow::ExitWithCode(0), @@ -912,113 +915,133 @@ fn run_command( } } window::Action::Drag(id) => { - let _ = windows.with_raw(id).drag_window(); + if let Some(window) = window_manager.get_mut(id) { + let _ = window.raw.drag_window(); + } } window::Action::Resize(id, size) => { - windows.with_raw(id).set_inner_size( - winit::dpi::LogicalSize { + if let Some(window) = window_manager.get_mut(id) { + window.raw.set_inner_size(winit::dpi::LogicalSize { width: size.width, height: size.height, - }, - ); + }); + } } window::Action::FetchSize(id, callback) => { - let window = windows.with_raw(id); - let size = - window.inner_size().to_logical(window.scale_factor()); - - proxy - .send_event(callback(Size::new( - size.width, - size.height, - ))) - .expect("Send message to event loop"); + if let Some(window) = window_manager.get_mut(id) { + let size = window + .raw + .inner_size() + .to_logical(window.raw.scale_factor()); + + proxy + .send_event(callback(Size::new( + size.width, + size.height, + ))) + .expect("Send message to event loop"); + } } window::Action::Maximize(id, maximized) => { - windows.with_raw(id).set_maximized(maximized); + if let Some(window) = window_manager.get_mut(id) { + window.raw.set_maximized(maximized); + } } window::Action::Minimize(id, minimized) => { - windows.with_raw(id).set_minimized(minimized); + if let Some(window) = window_manager.get_mut(id) { + window.raw.set_minimized(minimized); + } } window::Action::Move(id, position) => { - windows.with_raw(id).set_outer_position( - winit::dpi::LogicalPosition { - x: position.x, - y: position.y, - }, - ); + 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) => { - let window = windows.with_raw(id); - window.set_visible(conversion::visible(mode)); - window.set_fullscreen(conversion::fullscreen( - window.current_monitor(), - 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) => { - windows - .with_raw(id) - .set_window_icon(conversion::icon(icon)); + if let Some(window) = window_manager.get_mut(id) { + window.raw.set_window_icon(conversion::icon(icon)); + } } window::Action::FetchMode(id, tag) => { - let window = windows.with_raw(id); - let mode = if window.is_visible().unwrap_or(true) { - conversion::mode(window.fullscreen()) - } else { - core::window::Mode::Hidden - }; - - proxy - .send_event(tag(mode)) - .expect("Event loop doesn't exist."); + 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_event(tag(mode)) + .expect("Event loop doesn't exist."); + } } window::Action::ToggleMaximize(id) => { - let window = windows.with_raw(id); - window.set_maximized(!window.is_maximized()); + if let Some(window) = window_manager.get_mut(id) { + window.raw.set_maximized(!window.raw.is_maximized()); + } } window::Action::ToggleDecorations(id) => { - let window = windows.with_raw(id); - window.set_decorations(!window.is_decorated()); + if let Some(window) = window_manager.get_mut(id) { + window.raw.set_decorations(!window.raw.is_decorated()); + } } window::Action::RequestUserAttention(id, attention_type) => { - windows.with_raw(id).request_user_attention( - attention_type.map(conversion::user_attention), - ); + if let Some(window) = window_manager.get_mut(id) { + window.raw.request_user_attention( + attention_type.map(conversion::user_attention), + ); + } } window::Action::GainFocus(id) => { - windows.with_raw(id).focus_window(); + if let Some(window) = window_manager.get_mut(id) { + window.raw.focus_window(); + } } window::Action::ChangeLevel(id, level) => { - windows - .with_raw(id) - .set_window_level(conversion::window_level(level)); + if let Some(window) = window_manager.get_mut(id) { + window + .raw + .set_window_level(conversion::window_level(level)); + } } window::Action::FetchId(id, tag) => { - proxy - .send_event(tag(windows.with_raw(id).id().into())) - .expect("Event loop doesn't exist."); + if let Some(window) = window_manager.get_mut(id) { + proxy + .send_event(tag(window.raw.id().into())) + .expect("Event loop doesn't exist."); + } } window::Action::Screenshot(id, tag) => { - let i = windows.index_from_id(id); - let state = &windows.states[i]; - let surface = &mut windows.surfaces[i]; - let renderer = &mut windows.renderers[i]; - - let bytes = compositor.screenshot( - renderer, - surface, - state.viewport(), - state.background_color(), - &debug.overlay(), - ); + 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_event(tag(window::Screenshot::new( - bytes, - state.physical_size(), - ))) - .expect("Event loop doesn't exist."); + proxy + .send_event(tag(window::Screenshot::new( + bytes, + window.state.physical_size(), + ))) + .expect("Event loop doesn't exist."); + } } }, command::Action::System(action) => match action { @@ -1047,43 +1070,45 @@ fn run_command( let mut uis = build_user_interfaces( application, debug, - windows, + window_manager, std::mem::take(ui_caches), ); 'operate: while let Some(mut operation) = current_operation.take() { - for (i, ui) in uis.iter_mut().enumerate() { - ui.operate(&windows.renderers[i], operation.as_mut()); - - match operation.finish() { - operation::Outcome::None => {} - operation::Outcome::Some(message) => { - proxy - .send_event(message) - .expect("Event loop doesn't exist."); - - // operation completed, don't need to try to operate on rest of UIs - break 'operate; - } - operation::Outcome::Chain(next) => { - current_operation = Some(next); + for (id, ui) in uis.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_event(message) + .expect("Event loop doesn't exist."); + + // operation completed, don't need to try to operate on rest of UIs + break 'operate; + } + operation::Outcome::Chain(next) => { + current_operation = Some(next); + } } } } } *ui_caches = - uis.drain(..).map(UserInterface::into_cache).collect(); + uis.drain().map(|(id, ui)| (id, ui.into_cache())).collect(); } command::Action::LoadFont { bytes, tagger } => { use crate::core::text::Renderer; // TODO change this once we change each renderer to having a single backend reference.. :pain: // TODO: Error handling (?) - for renderer in &mut windows.renderers { - renderer.load_font(bytes.clone()); + for (_, window) in window_manager.iter_mut() { + window.renderer.load_font(bytes.clone()); } proxy @@ -1098,33 +1123,31 @@ fn run_command( pub fn build_user_interfaces<'a, A: Application, C: Compositor>( application: &'a A, debug: &mut Debug, - windows: &mut Windows, - mut cached_user_interfaces: Vec, -) -> Vec> + window_manager: &mut WindowManager, + mut cached_user_interfaces: HashMap, +) -> HashMap> where ::Theme: StyleSheet, C: Compositor, { cached_user_interfaces - .drain(..) - .zip( - windows - .ids - .iter() - .zip(windows.states.iter().zip(windows.renderers.iter_mut())), - ) - .fold(vec![], |mut uis, (cache, (id, (state, renderer)))| { - uis.push(build_user_interface( - application, - cache, - renderer, - state.logical_size(), - debug, - *id, - )); - - uis + .drain() + .filter_map(|(id, cache)| { + let window = window_manager.get_mut(id)?; + + Some(( + id, + build_user_interface( + application, + cache, + &mut window.renderer, + window.state.logical_size(), + debug, + id, + ), + )) }) + .collect() } /// Returns true if the provided event should cause an [`Application`] to @@ -1148,25 +1171,6 @@ pub fn user_force_quit( } } -fn logical_bounds_of(window: &winit::window::Window) -> (Option, Size) { - let position = window - .inner_position() - .ok() - .map(|position| position.to_logical(window.scale_factor())) - .map(|position| Point { - x: position.x, - y: position.y, - }); - - let size = { - let size = window.inner_size().to_logical(window.scale_factor()); - - Size::new(size.width, size.height) - }; - - (position, size) -} - #[cfg(not(target_arch = "wasm32"))] mod platform { pub fn run( -- cgit From e819c2390bad76e811265245bd5fab63fc30a8b2 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 15 Dec 2023 13:15:44 +0100 Subject: Update `winit` to `0.29.4` --- winit/src/multi_window.rs | 527 ++++++++++++++++++++-------------------------- 1 file changed, 229 insertions(+), 298 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 84651d40..16b41e7d 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -118,7 +118,10 @@ where let mut debug = Debug::new(); debug.startup_started(); - let event_loop = EventLoopBuilder::with_user_event().build(); + let event_loop = EventLoopBuilder::with_user_event() + .build() + .expect("Create event loop"); + let proxy = event_loop.create_proxy(); let runtime = { @@ -210,78 +213,64 @@ where let mut context = task::Context::from_waker(task::noop_waker_ref()); - platform::run(event_loop, move |event, window_target, control_flow| { - use winit::event_loop::ControlFlow; - - if let ControlFlow::ExitWithCode(_) = control_flow { + let _ = event_loop.run(move |event, event_loop| { + if event_loop.exiting() { return; } - let event = match event { - winit::event::Event::WindowEvent { - event: - winit::event::WindowEvent::ScaleFactorChanged { - new_inner_size, - .. - }, - window_id, - } => Some(winit::event::Event::WindowEvent { - event: winit::event::WindowEvent::Resized(*new_inner_size), - window_id, - }), - _ => event.to_static(), - }; + event_sender + .start_send(Event::EventLoopAwakened(event)) + .expect("Send event"); - if let Some(event) = event { - event_sender - .start_send(Event::EventLoopAwakened(event)) - .expect("Send event"); + loop { + let poll = instance.as_mut().poll(&mut context); - loop { - let poll = instance.as_mut().poll(&mut context); - - match poll { - task::Poll::Pending => match control_receiver.try_next() { - Ok(Some(control)) => match control { - Control::ChangeFlow(flow) => { - *control_flow = flow; - } - Control::CreateWindow { - id, - settings, - title, - monitor, - } => { - let exit_on_close_request = - settings.exit_on_close_request; - - let window = conversion::window_settings( - settings, &title, monitor, None, - ) - .build(window_target) - .expect("Failed to build window"); - - event_sender - .start_send(Event::WindowCreated { - id, - window, - exit_on_close_request, - }) - .expect("Send event"); - } - }, - _ => { - break; + match poll { + task::Poll::Pending => match control_receiver.try_next() { + Ok(Some(control)) => match control { + Control::ChangeFlow(flow) => { + event_loop.set_control_flow(flow); + } + Control::CreateWindow { + id, + settings, + title, + monitor, + } => { + let exit_on_close_request = + settings.exit_on_close_request; + + let window = conversion::window_settings( + settings, &title, monitor, None, + ) + .build(event_loop) + .expect("Failed to build window"); + + event_sender + .start_send(Event::WindowCreated { + id, + window, + exit_on_close_request, + }) + .expect("Send event"); + } + Control::Exit => { + event_loop.exit(); } }, - task::Poll::Ready(_) => { - *control_flow = ControlFlow::Exit; + _ => { break; } - }; - } + }, + task::Poll::Ready(_) => { + event_loop.exit(); + break; + } + }; } - }) + }); + + Ok(()) } enum Event { @@ -290,11 +279,12 @@ enum Event { window: winit::window::Window, exit_on_close_request: bool, }, - EventLoopAwakened(winit::event::Event<'static, Message>), + EventLoopAwakened(winit::event::Event), } enum Control { ChangeFlow(winit::event_loop::ControlFlow), + Exit, CreateWindow { id: window::Id, settings: window::Settings, @@ -427,184 +417,6 @@ async fn run_instance( | event::StartCause::ResumeTimeReached { .. } ); } - event::Event::MainEventsCleared => { - debug.event_processing_started(); - let mut uis_stale = false; - - for (id, window) in window_manager.iter_mut() { - let mut window_events = vec![]; - - events.retain(|(window_id, event)| { - if *window_id == Some(id) || window_id.is_none() - { - window_events.push(event.clone()); - false - } else { - true - } - }); - - if !redraw_pending - && window_events.is_empty() - && messages.is_empty() - { - continue; - } - - let (ui_state, statuses) = user_interfaces - .get_mut(&id) - .expect("Get user interface") - .update( - &window_events, - window.state.cursor(), - &mut window.renderer, - &mut clipboard, - &mut messages, - ); - - if !uis_stale { - uis_stale = matches!( - ui_state, - user_interface::State::Outdated - ); - } - - for (event, status) in window_events - .into_iter() - .zip(statuses.into_iter()) - { - runtime.broadcast(event, status); - } - } - - debug.event_processing_finished(); - - // TODO mw application update returns which window IDs to update - if !messages.is_empty() || uis_stale { - let mut cached_interfaces: HashMap< - window::Id, - user_interface::Cache, - > = ManuallyDrop::into_inner(user_interfaces) - .drain() - .map(|(id, ui)| (id, ui.into_cache())) - .collect(); - - // 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 - // application update since we don't know what changed - for (id, window) in window_manager.iter_mut() { - window.state.synchronize( - &application, - id, - &window.raw, - ); - } - - // rebuild UIs with the synchronized states - user_interfaces = - ManuallyDrop::new(build_user_interfaces( - &application, - &mut debug, - &mut window_manager, - cached_interfaces, - )); - } - - debug.draw_started(); - - for (id, window) in window_manager.iter_mut() { - // TODO: Avoid redrawing all the time by forcing widgets to - // request redraws on state changes - // - // Then, we can use the `interface_state` here to decide if a redraw - // is needed right away, or simply wait until a specific time. - let redraw_event = core::Event::Window( - id, - window::Event::RedrawRequested(Instant::now()), - ); - - let cursor = window.state.cursor(); - - let ui = user_interfaces - .get_mut(&id) - .expect("Get user interface"); - - let (ui_state, _) = ui.update( - &[redraw_event.clone()], - cursor, - &mut window.renderer, - &mut clipboard, - &mut messages, - ); - - let new_mouse_interaction = { - let state = &window.state; - - ui.draw( - &mut window.renderer, - state.theme(), - &renderer::Style { - text_color: state.text_color(), - }, - cursor, - ) - }; - - if new_mouse_interaction != window.mouse_interaction - { - window.raw.set_cursor_icon( - conversion::mouse_interaction( - new_mouse_interaction, - ), - ); - - window.mouse_interaction = - new_mouse_interaction; - } - - // TODO once widgets can request to be redrawn, we can avoid always requesting a - // redraw - window.raw.request_redraw(); - - runtime.broadcast( - redraw_event.clone(), - core::event::Status::Ignored, - ); - - let _ = control_sender.start_send( - Control::ChangeFlow(match ui_state { - user_interface::State::Updated { - redraw_request: Some(redraw_request), - } => match redraw_request { - window::RedrawRequest::NextFrame => { - ControlFlow::Poll - } - window::RedrawRequest::At(at) => { - ControlFlow::WaitUntil(at) - } - }, - _ => ControlFlow::Wait, - }), - ); - } - - redraw_pending = false; - - debug.draw_finished(); - } event::Event::PlatformSpecific( event::PlatformSpecific::MacOS( event::MacOS::ReceivedUrl(url), @@ -624,7 +436,11 @@ async fn run_instance( event::Event::UserEvent(message) => { messages.push(message); } - event::Event::RedrawRequested(id) => { + event::Event::WindowEvent { + window_id: id, + event: event::WindowEvent::RedrawRequested, + .. + } => { let Some((id, window)) = window_manager.get_mut_alias(id) else { @@ -775,6 +591,163 @@ async fn run_instance( } } } + + debug.event_processing_started(); + let mut uis_stale = false; + + for (id, window) in window_manager.iter_mut() { + let mut window_events = vec![]; + + events.retain(|(window_id, event)| { + if *window_id == Some(id) || window_id.is_none() { + window_events.push(event.clone()); + false + } else { + true + } + }); + + if !redraw_pending + && window_events.is_empty() + && messages.is_empty() + { + continue; + } + + let (ui_state, statuses) = user_interfaces + .get_mut(&id) + .expect("Get user interface") + .update( + &window_events, + window.state.cursor(), + &mut window.renderer, + &mut clipboard, + &mut messages, + ); + + if !uis_stale { + uis_stale = matches!(ui_state, user_interface::State::Outdated); + } + + for (event, status) in + window_events.into_iter().zip(statuses.into_iter()) + { + runtime.broadcast(event, status); + } + } + + debug.event_processing_finished(); + + // TODO mw application update returns which window IDs to update + if !messages.is_empty() || uis_stale { + let mut cached_interfaces: HashMap< + window::Id, + user_interface::Cache, + > = ManuallyDrop::into_inner(user_interfaces) + .drain() + .map(|(id, ui)| (id, ui.into_cache())) + .collect(); + + // 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 + // application update since we don't know what changed + for (id, window) in window_manager.iter_mut() { + window.state.synchronize(&application, id, &window.raw); + } + + // rebuild UIs with the synchronized states + user_interfaces = ManuallyDrop::new(build_user_interfaces( + &application, + &mut debug, + &mut window_manager, + cached_interfaces, + )); + } + + debug.draw_started(); + + for (id, window) in window_manager.iter_mut() { + // TODO: Avoid redrawing all the time by forcing widgets to + // request redraws on state changes + // + // Then, we can use the `interface_state` here to decide if a redraw + // is needed right away, or simply wait until a specific time. + let redraw_event = core::Event::Window( + id, + window::Event::RedrawRequested(Instant::now()), + ); + + let cursor = window.state.cursor(); + + let ui = user_interfaces.get_mut(&id).expect("Get user interface"); + + let (ui_state, _) = ui.update( + &[redraw_event.clone()], + cursor, + &mut window.renderer, + &mut clipboard, + &mut messages, + ); + + let new_mouse_interaction = { + let state = &window.state; + + ui.draw( + &mut window.renderer, + state.theme(), + &renderer::Style { + text_color: state.text_color(), + }, + cursor, + ) + }; + + if new_mouse_interaction != window.mouse_interaction { + window.raw.set_cursor_icon(conversion::mouse_interaction( + new_mouse_interaction, + )); + + window.mouse_interaction = new_mouse_interaction; + } + + // TODO once widgets can request to be redrawn, we can avoid always requesting a + // redraw + window.raw.request_redraw(); + + runtime + .broadcast(redraw_event.clone(), core::event::Status::Ignored); + + let _ = control_sender.start_send(Control::ChangeFlow( + match ui_state { + user_interface::State::Updated { + redraw_request: Some(redraw_request), + } => match redraw_request { + window::RedrawRequest::NextFrame => ControlFlow::Poll, + window::RedrawRequest::At(at) => { + ControlFlow::WaitUntil(at) + } + }, + _ => ControlFlow::Wait, + }, + )); + } + + redraw_pending = false; + + debug.draw_finished(); } let _ = ManuallyDrop::into_inner(user_interfaces); @@ -901,16 +874,12 @@ fn run_command( .expect("Send control action"); } window::Action::Close(id) => { - use winit::event_loop::ControlFlow; - let _ = window_manager.remove(id); let _ = ui_caches.remove(&id); if window_manager.is_empty() { control_sender - .start_send(Control::ChangeFlow( - ControlFlow::ExitWithCode(0), - )) + .start_send(Control::Exit) .expect("Send control action"); } } @@ -921,10 +890,12 @@ fn run_command( } window::Action::Resize(id, size) => { if let Some(window) = window_manager.get_mut(id) { - window.raw.set_inner_size(winit::dpi::LogicalSize { - width: size.width, - height: size.height, - }); + let _ = window.raw.request_inner_size( + winit::dpi::LogicalSize { + width: size.width, + height: size.height, + }, + ); } } window::Action::FetchSize(id, callback) => { @@ -1153,60 +1124,20 @@ where /// Returns true if the provided event should cause an [`Application`] to /// exit. pub fn user_force_quit( - event: &winit::event::WindowEvent<'_>, - _modifiers: winit::event::ModifiersState, + event: &winit::event::WindowEvent, + _modifiers: winit::keyboard::ModifiersState, ) -> bool { match event { #[cfg(target_os = "macos")] winit::event::WindowEvent::KeyboardInput { - input: - winit::event::KeyboardInput { - virtual_keycode: Some(winit::event::VirtualKeyCode::Q), + event: + winit::event::KeyEvent { + logical_key: winit::keyboard::Key::Character(c), state: winit::event::ElementState::Pressed, .. }, .. - } if _modifiers.logo() => true, + } if c == "q" && _modifiers.super_key() => true, _ => false, } } - -#[cfg(not(target_arch = "wasm32"))] -mod platform { - pub fn run( - mut event_loop: winit::event_loop::EventLoop, - event_handler: F, - ) -> Result<(), super::Error> - where - F: 'static - + FnMut( - winit::event::Event<'_, T>, - &winit::event_loop::EventLoopWindowTarget, - &mut winit::event_loop::ControlFlow, - ), - { - use winit::platform::run_return::EventLoopExtRunReturn; - - let _ = event_loop.run_return(event_handler); - - Ok(()) - } -} - -#[cfg(target_arch = "wasm32")] -mod platform { - pub fn run( - event_loop: winit::event_loop::EventLoop, - event_handler: F, - ) -> ! - where - F: 'static - + FnMut( - winit::event::Event<'_, T>, - &winit::event_loop::EventLoopWindowTarget, - &mut winit::event_loop::ControlFlow, - ), - { - event_loop.run(event_handler) - } -} -- cgit From af917a08d8c60f1684439989f63f856d445d0383 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 19 Dec 2023 12:44:08 +0100 Subject: Fix request redraw event handling for multi-window apps --- winit/src/multi_window.rs | 144 +++++++++++++++++++++++----------------------- 1 file changed, 73 insertions(+), 71 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 16b41e7d..0ba51387 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -447,6 +447,72 @@ async fn run_instance( continue; }; + // TODO: Avoid redrawing all the time by forcing widgets to + // request redraws on state changes + // + // Then, we can use the `interface_state` here to decide if a redraw + // is needed right away, or simply wait until a specific time. + let redraw_event = core::Event::Window( + id, + window::Event::RedrawRequested(Instant::now()), + ); + + let cursor = window.state.cursor(); + + let ui = user_interfaces + .get_mut(&id) + .expect("Get user interface"); + + let (ui_state, _) = ui.update( + &[redraw_event.clone()], + cursor, + &mut window.renderer, + &mut clipboard, + &mut messages, + ); + + debug.draw_started(); + let new_mouse_interaction = ui.draw( + &mut window.renderer, + window.state.theme(), + &renderer::Style { + text_color: window.state.text_color(), + }, + cursor, + ); + debug.draw_finished(); + + if new_mouse_interaction != window.mouse_interaction { + window.raw.set_cursor_icon( + conversion::mouse_interaction( + new_mouse_interaction, + ), + ); + + window.mouse_interaction = new_mouse_interaction; + } + + runtime.broadcast( + redraw_event.clone(), + core::event::Status::Ignored, + ); + + let _ = control_sender.start_send(Control::ChangeFlow( + match ui_state { + user_interface::State::Updated { + redraw_request: Some(redraw_request), + } => match redraw_request { + window::RedrawRequest::NextFrame => { + ControlFlow::Poll + } + window::RedrawRequest::At(at) => { + ControlFlow::WaitUntil(at) + } + }, + _ => ControlFlow::Wait, + }, + )); + let physical_size = window.state.physical_size(); if physical_size.width == 0 || physical_size.height == 0 @@ -454,14 +520,12 @@ async fn run_instance( continue; } - debug.render_started(); if window.viewport_version != window.state.viewport_version() { let logical_size = window.state.logical_size(); debug.layout_started(); - let ui = user_interfaces .remove(&id) .expect("Remove user interface"); @@ -470,7 +534,6 @@ async fn run_instance( id, ui.relayout(logical_size, &mut window.renderer), ); - debug.layout_finished(); debug.draw_started(); @@ -485,6 +548,7 @@ async fn run_instance( }, window.state.cursor(), ); + debug.draw_finished(); if new_mouse_interaction != window.mouse_interaction { @@ -497,7 +561,6 @@ async fn run_instance( window.mouse_interaction = new_mouse_interaction; } - debug.draw_finished(); compositor.configure_surface( &mut window.surface, @@ -509,6 +572,7 @@ async fn run_instance( window.state.viewport_version(); } + debug.render_started(); match compositor.present( &mut window.renderer, &mut window.surface, @@ -529,9 +593,11 @@ async fn run_instance( } _ => { debug.render_finished(); + log::error!( - "Error {error:?} when presenting surface." - ); + "Error {error:?} when \ + presenting surface." + ); // Try rendering all windows again next frame. for (_id, window) in @@ -677,77 +743,13 @@ async fn run_instance( )); } - debug.draw_started(); - - for (id, window) in window_manager.iter_mut() { - // TODO: Avoid redrawing all the time by forcing widgets to - // request redraws on state changes - // - // Then, we can use the `interface_state` here to decide if a redraw - // is needed right away, or simply wait until a specific time. - let redraw_event = core::Event::Window( - id, - window::Event::RedrawRequested(Instant::now()), - ); - - let cursor = window.state.cursor(); - - let ui = user_interfaces.get_mut(&id).expect("Get user interface"); - - let (ui_state, _) = ui.update( - &[redraw_event.clone()], - cursor, - &mut window.renderer, - &mut clipboard, - &mut messages, - ); - - let new_mouse_interaction = { - let state = &window.state; - - ui.draw( - &mut window.renderer, - state.theme(), - &renderer::Style { - text_color: state.text_color(), - }, - cursor, - ) - }; - - if new_mouse_interaction != window.mouse_interaction { - window.raw.set_cursor_icon(conversion::mouse_interaction( - new_mouse_interaction, - )); - - window.mouse_interaction = new_mouse_interaction; - } - + for (_id, window) in window_manager.iter_mut() { // TODO once widgets can request to be redrawn, we can avoid always requesting a // redraw window.raw.request_redraw(); - - runtime - .broadcast(redraw_event.clone(), core::event::Status::Ignored); - - let _ = control_sender.start_send(Control::ChangeFlow( - match ui_state { - user_interface::State::Updated { - redraw_request: Some(redraw_request), - } => match redraw_request { - window::RedrawRequest::NextFrame => ControlFlow::Poll, - window::RedrawRequest::At(at) => { - ControlFlow::WaitUntil(at) - } - }, - _ => ControlFlow::Wait, - }, - )); } redraw_pending = false; - - debug.draw_finished(); } let _ = ManuallyDrop::into_inner(user_interfaces); -- cgit From 2aa2b1712dfdc93762ebe0958614154920068731 Mon Sep 17 00:00:00 2001 From: Calastrophe Date: Tue, 9 Jan 2024 02:37:45 -0600 Subject: Implemented fetch_maximized and fetch_minimized --- winit/src/multi_window.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 84651d40..1550b94b 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -942,11 +942,25 @@ fn run_command( .expect("Send message to event loop"); } } + window::Action::FetchMaximized(id, callback) => { + if let Some(window) = window_manager.get_mut(id) { + proxy + .send_event(callback(window.raw.is_maximized())) + .expect("Send message to event loop"); + } + } 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_event(callback(window.raw.is_minimized())) + .expect("Send message to event loop"); + } + } window::Action::Minimize(id, minimized) => { if let Some(window) = window_manager.get_mut(id) { window.raw.set_minimized(minimized); -- cgit From 64d1ce5532f55d152fa5819532a138da2dca1a39 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 16 Jan 2024 13:28:00 +0100 Subject: Refactor `KeyCode` into `Key` and `Location` --- winit/src/multi_window.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 93f0cde3..dc659c1a 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -645,7 +645,7 @@ async fn run_instance( if let Some(event) = conversion::window_event( id, - &window_event, + window_event, window.state.scale_factor(), window.state.modifiers(), ) { -- cgit From 985acb2b1532b3e56161bd35201c4a2e21a86b85 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 17 Jan 2024 08:05:19 +0100 Subject: Fine-tune event loop of `multi-window` applications --- winit/src/multi_window.rs | 219 ++++++++++++++++++++++++++-------------------- 1 file changed, 123 insertions(+), 96 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index dc659c1a..84c81bea 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -229,7 +229,21 @@ where task::Poll::Pending => match control_receiver.try_next() { Ok(Some(control)) => match control { Control::ChangeFlow(flow) => { - event_loop.set_control_flow(flow); + use winit::event_loop::ControlFlow; + + match (event_loop.control_flow(), flow) { + ( + ControlFlow::WaitUntil(current), + ControlFlow::WaitUntil(new), + ) if new < current => {} + ( + ControlFlow::WaitUntil(target), + ControlFlow::Wait, + ) if target > Instant::now() => {} + _ => { + event_loop.set_control_flow(flow); + } + } } Control::CreateWindow { id, @@ -362,7 +376,6 @@ async fn run_instance( runtime.track(application.subscription().into_recipes()); let mut messages = Vec::new(); - let mut redraw_pending = false; debug.startup_finished(); @@ -409,13 +422,15 @@ async fn run_instance( } Event::EventLoopAwakened(event) => { match event { - event::Event::NewEvents(start_cause) => { - redraw_pending = matches!( - start_cause, - event::StartCause::Init - | event::StartCause::Poll - | event::StartCause::ResumeTimeReached { .. } - ); + event::Event::NewEvents( + event::StartCause::Init + | event::StartCause::ResumeTimeReached { .. }, + ) => { + for (_id, window) in window_manager.iter_mut() { + // TODO once widgets can request to be redrawn, we can avoid always requesting a + // redraw + window.raw.request_redraw(); + } } event::Event::PlatformSpecific( event::PlatformSpecific::MacOS( @@ -503,7 +518,9 @@ async fn run_instance( redraw_request: Some(redraw_request), } => match redraw_request { window::RedrawRequest::NextFrame => { - ControlFlow::Poll + window.raw.request_redraw(); + + ControlFlow::Wait } window::RedrawRequest::At(at) => { ControlFlow::WaitUntil(at) @@ -653,103 +670,113 @@ async fn run_instance( } } } - _ => {} - } - } - } + event::Event::AboutToWait => { + if events.is_empty() && messages.is_empty() { + continue; + } - debug.event_processing_started(); - let mut uis_stale = false; + debug.event_processing_started(); + let mut uis_stale = false; - for (id, window) in window_manager.iter_mut() { - let mut window_events = vec![]; + for (id, window) in window_manager.iter_mut() { + let mut window_events = vec![]; - events.retain(|(window_id, event)| { - if *window_id == Some(id) || window_id.is_none() { - window_events.push(event.clone()); - false - } else { - true - } - }); + events.retain(|(window_id, event)| { + if *window_id == Some(id) || window_id.is_none() + { + window_events.push(event.clone()); + false + } else { + true + } + }); - if !redraw_pending - && window_events.is_empty() - && messages.is_empty() - { - continue; - } + if window_events.is_empty() && messages.is_empty() { + continue; + } - let (ui_state, statuses) = user_interfaces - .get_mut(&id) - .expect("Get user interface") - .update( - &window_events, - window.state.cursor(), - &mut window.renderer, - &mut clipboard, - &mut messages, - ); + let (ui_state, statuses) = user_interfaces + .get_mut(&id) + .expect("Get user interface") + .update( + &window_events, + window.state.cursor(), + &mut window.renderer, + &mut clipboard, + &mut messages, + ); - if !uis_stale { - uis_stale = matches!(ui_state, user_interface::State::Outdated); - } + window.raw.request_redraw(); - for (event, status) in - window_events.into_iter().zip(statuses.into_iter()) - { - runtime.broadcast(event, status); - } - } + if !uis_stale { + uis_stale = matches!( + ui_state, + user_interface::State::Outdated + ); + } - debug.event_processing_finished(); - - // TODO mw application update returns which window IDs to update - if !messages.is_empty() || uis_stale { - let mut cached_interfaces: HashMap< - window::Id, - user_interface::Cache, - > = ManuallyDrop::into_inner(user_interfaces) - .drain() - .map(|(id, ui)| (id, ui.into_cache())) - .collect(); - - // 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 - // application update since we don't know what changed - for (id, window) in window_manager.iter_mut() { - window.state.synchronize(&application, id, &window.raw); - } + for (event, status) in window_events + .into_iter() + .zip(statuses.into_iter()) + { + runtime.broadcast(event, status); + } + } - // rebuild UIs with the synchronized states - user_interfaces = ManuallyDrop::new(build_user_interfaces( - &application, - &mut debug, - &mut window_manager, - cached_interfaces, - )); - } + debug.event_processing_finished(); + + // TODO mw application update returns which window IDs to update + if !messages.is_empty() || uis_stale { + let mut cached_interfaces: HashMap< + window::Id, + user_interface::Cache, + > = ManuallyDrop::into_inner(user_interfaces) + .drain() + .map(|(id, ui)| (id, ui.into_cache())) + .collect(); + + // 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, + ); - for (_id, window) in window_manager.iter_mut() { - // TODO once widgets can request to be redrawn, we can avoid always requesting a - // redraw - window.raw.request_redraw(); - } + // we must synchronize all window states with application state after an + // application update since we don't know what changed + for (id, window) in window_manager.iter_mut() { + window.state.synchronize( + &application, + id, + &window.raw, + ); + + // TODO once widgets can request to be redrawn, we can avoid always requesting a + // redraw + window.raw.request_redraw(); + } - redraw_pending = false; + // rebuild UIs with the synchronized states + user_interfaces = + ManuallyDrop::new(build_user_interfaces( + &application, + &mut debug, + &mut window_manager, + cached_interfaces, + )); + } + } + _ => {} + } + } + } } let _ = ManuallyDrop::into_inner(user_interfaces); -- cgit From 8bf238697226e827dc983f9d89afbd0e252c5254 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 18 Jan 2024 09:55:27 +0100 Subject: Remove `Compositor` window generic And update `glyphon` and `window_clipboard` --- winit/src/multi_window.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 84c81bea..21196460 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -24,6 +24,7 @@ use crate::{Clipboard, Error, Proxy, Settings}; use std::collections::HashMap; use std::mem::ManuallyDrop; +use std::sync::Arc; use std::time::Instant; /// An interactive, native, cross-platform, multi-windowed application. @@ -150,9 +151,11 @@ where log::info!("Window builder: {:#?}", builder); - let main_window = builder - .build(&event_loop) - .map_err(Error::WindowCreationFailed)?; + let main_window = Arc::new( + builder + .build(&event_loop) + .map_err(Error::WindowCreationFailed)?, + ); #[cfg(target_arch = "wasm32")] { @@ -184,7 +187,8 @@ where }; } - let mut compositor = C::new(compositor_settings, Some(&main_window))?; + let mut compositor = + C::new(compositor_settings, Some(main_window.clone()))?; let mut window_manager = WindowManager::new(); let _ = window_manager.insert( @@ -388,7 +392,7 @@ async fn run_instance( } => { let window = window_manager.insert( id, - window, + Arc::new(window), &application, &mut compositor, exit_on_close_request, -- cgit From 5fc49edc55a0e64c4c46ca55eddafe9d4e8232e1 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 18 Jan 2024 10:06:30 +0100 Subject: Make `compatible_window` mandatory in `Compositor` --- winit/src/multi_window.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'winit/src/multi_window.rs') diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 21196460..3f0ba056 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -187,8 +187,7 @@ where }; } - let mut compositor = - C::new(compositor_settings, Some(main_window.clone()))?; + let mut compositor = C::new(compositor_settings, main_window.clone())?; let mut window_manager = WindowManager::new(); let _ = window_manager.insert( -- cgit