Refactor event loop <-> instance communication in multi_window
This commit is contained in:
parent
3b39ba7029
commit
d34bc4e4a2
1 changed files with 469 additions and 436 deletions
|
|
@ -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<Message> {
|
||||
Application(Message),
|
||||
NewWindow {
|
||||
id: window::Id,
|
||||
settings: window::Settings,
|
||||
title: String,
|
||||
monitor: Option<MonitorHandle>,
|
||||
},
|
||||
CloseWindow(window::Id),
|
||||
enum Event<Message: 'static> {
|
||||
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<winit::monitor::MonitorHandle>,
|
||||
},
|
||||
}
|
||||
|
||||
/// An interactive, native, cross-platform, multi-windowed application.
|
||||
|
|
@ -243,13 +244,31 @@ where
|
|||
event: winit::event::WindowEvent::Resized(*new_inner_size),
|
||||
window_id,
|
||||
}),
|
||||
winit::event::Event::UserEvent(Event::NewWindow {
|
||||
_ => event.to_static(),
|
||||
};
|
||||
|
||||
if let Some(event) = event {
|
||||
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 exit_on_close_request =
|
||||
settings.exit_on_close_request;
|
||||
|
||||
let window = conversion::window_settings(
|
||||
settings, &title, monitor, None,
|
||||
|
|
@ -257,44 +276,37 @@ where
|
|||
.build(window_target)
|
||||
.expect("Failed to build window");
|
||||
|
||||
Some(winit::event::Event::UserEvent(Event::WindowCreated {
|
||||
event_sender
|
||||
.start_send(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;
|
||||
})
|
||||
.expect("Send event");
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
break;
|
||||
}
|
||||
},
|
||||
task::Poll::Ready(_) => {
|
||||
*control_flow = ControlFlow::Exit;
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async fn run_instance<A, E, C>(
|
||||
mut application: A,
|
||||
mut compositor: C,
|
||||
mut runtime: Runtime<E, Proxy<Event<A::Message>>, Event<A::Message>>,
|
||||
mut proxy: winit::event_loop::EventLoopProxy<Event<A::Message>>,
|
||||
mut runtime: Runtime<E, Proxy<A::Message>, A::Message>,
|
||||
mut proxy: winit::event_loop::EventLoopProxy<A::Message>,
|
||||
mut debug: Debug,
|
||||
mut event_receiver: mpsc::UnboundedReceiver<
|
||||
winit::event::Event<'_, Event<A::Message>>,
|
||||
>,
|
||||
mut control_sender: mpsc::UnboundedSender<winit::event_loop::ControlFlow>,
|
||||
mut event_receiver: mpsc::UnboundedReceiver<Event<A::Message>>,
|
||||
mut control_sender: mpsc::UnboundedSender<Control>,
|
||||
init_command: Command<A::Message>,
|
||||
mut windows: Windows<A, C>,
|
||||
should_main_window_be_visible: bool,
|
||||
|
|
@ -327,18 +339,14 @@ async fn run_instance<A, E, C>(
|
|||
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();
|
||||
|
||||
|
|
@ -360,6 +368,46 @@ async fn run_instance<A, E, C>(
|
|||
debug.startup_finished();
|
||||
|
||||
'main: while let Some(event) = event_receiver.next().await {
|
||||
match event {
|
||||
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,
|
||||
);
|
||||
|
||||
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,
|
||||
},
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
Event::EventLoopAwakened(event) => {
|
||||
match event {
|
||||
event::Event::NewEvents(start_cause) => {
|
||||
redraw_pending = matches!(
|
||||
|
|
@ -377,7 +425,9 @@ async fn run_instance<A, E, C>(
|
|||
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
|
||||
} else {
|
||||
|
|
@ -392,7 +442,8 @@ async fn run_instance<A, E, C>(
|
|||
continue;
|
||||
}
|
||||
|
||||
let (ui_state, statuses) = user_interfaces[i].update(
|
||||
let (ui_state, statuses) = user_interfaces[i]
|
||||
.update(
|
||||
&window_events,
|
||||
windows.states[i].cursor(),
|
||||
&mut windows.renderers[i],
|
||||
|
|
@ -401,12 +452,15 @@ async fn run_instance<A, E, C>(
|
|||
);
|
||||
|
||||
if !uis_stale {
|
||||
uis_stale =
|
||||
matches!(ui_state, user_interface::State::Outdated);
|
||||
uis_stale = matches!(
|
||||
ui_state,
|
||||
user_interface::State::Outdated
|
||||
);
|
||||
}
|
||||
|
||||
for (event, status) in
|
||||
window_events.into_iter().zip(statuses.into_iter())
|
||||
for (event, status) in window_events
|
||||
.into_iter()
|
||||
.zip(statuses.into_iter())
|
||||
{
|
||||
runtime.broadcast(event, status);
|
||||
}
|
||||
|
|
@ -416,8 +470,9 @@ async fn run_instance<A, E, C>(
|
|||
|
||||
// TODO mw application update returns which window IDs to update
|
||||
if !messages.is_empty() || uis_stale {
|
||||
let mut cached_interfaces: Vec<user_interface::Cache> =
|
||||
ManuallyDrop::into_inner(user_interfaces)
|
||||
let mut cached_interfaces: Vec<
|
||||
user_interface::Cache,
|
||||
> = ManuallyDrop::into_inner(user_interfaces)
|
||||
.drain(..)
|
||||
.map(UserInterface::into_cache)
|
||||
.collect();
|
||||
|
|
@ -428,6 +483,7 @@ async fn run_instance<A, E, C>(
|
|||
&mut compositor,
|
||||
&mut runtime,
|
||||
&mut clipboard,
|
||||
&mut control_sender,
|
||||
&mut proxy,
|
||||
&mut debug,
|
||||
&mut messages,
|
||||
|
|
@ -446,7 +502,8 @@ async fn run_instance<A, E, C>(
|
|||
}
|
||||
|
||||
// rebuild UIs with the synchronized states
|
||||
user_interfaces = ManuallyDrop::new(build_user_interfaces(
|
||||
user_interfaces =
|
||||
ManuallyDrop::new(build_user_interfaces(
|
||||
&application,
|
||||
&mut debug,
|
||||
&mut windows,
|
||||
|
|
@ -509,7 +566,8 @@ async fn run_instance<A, E, C>(
|
|||
core::event::Status::Ignored,
|
||||
);
|
||||
|
||||
let _ = control_sender.start_send(match ui_state {
|
||||
let _ = control_sender.start_send(
|
||||
Control::ChangeFlow(match ui_state {
|
||||
user_interface::State::Updated {
|
||||
redraw_request: Some(redraw_request),
|
||||
} => match redraw_request {
|
||||
|
|
@ -521,16 +579,19 @@ async fn run_instance<A, E, C>(
|
|||
}
|
||||
},
|
||||
_ => ControlFlow::Wait,
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
redraw_pending = false;
|
||||
|
||||
debug.draw_finished();
|
||||
}
|
||||
event::Event::PlatformSpecific(event::PlatformSpecific::MacOS(
|
||||
event::Event::PlatformSpecific(
|
||||
event::PlatformSpecific::MacOS(
|
||||
event::MacOS::ReceivedUrl(url),
|
||||
)) => {
|
||||
),
|
||||
) => {
|
||||
use crate::core::event;
|
||||
|
||||
events.push((
|
||||
|
|
@ -542,71 +603,23 @@ async fn run_instance<A, E, C>(
|
|||
),
|
||||
));
|
||||
}
|
||||
event::Event::UserEvent(event) => match event {
|
||||
Event::Application(message) => {
|
||||
event::Event::UserEvent(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,
|
||||
);
|
||||
|
||||
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,
|
||||
},
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
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::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 {
|
||||
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];
|
||||
let window_viewport_version =
|
||||
windows.viewport_versions[i];
|
||||
|
||||
if window_viewport_version != current_viewport_version {
|
||||
let logical_size = state.logical_size();
|
||||
|
|
@ -622,7 +635,8 @@ async fn run_instance<A, E, C>(
|
|||
debug.layout_finished();
|
||||
|
||||
debug.draw_started();
|
||||
let new_mouse_interaction = user_interfaces[i].draw(
|
||||
let new_mouse_interaction = user_interfaces[i]
|
||||
.draw(
|
||||
renderer,
|
||||
state.theme(),
|
||||
&renderer::Style {
|
||||
|
|
@ -648,7 +662,8 @@ async fn run_instance<A, E, C>(
|
|||
physical_size.height,
|
||||
);
|
||||
|
||||
windows.viewport_versions[i] = current_viewport_version;
|
||||
windows.viewport_versions[i] =
|
||||
current_viewport_version;
|
||||
}
|
||||
|
||||
match compositor.present(
|
||||
|
|
@ -687,8 +702,10 @@ async fn run_instance<A, E, C>(
|
|||
event: window_event,
|
||||
window_id,
|
||||
} => {
|
||||
let window_index =
|
||||
windows.raw.iter().position(|w| w.id() == window_id);
|
||||
let window_index = windows
|
||||
.raw
|
||||
.iter()
|
||||
.position(|w| w.id() == window_id);
|
||||
|
||||
match window_index {
|
||||
Some(i) => {
|
||||
|
|
@ -711,14 +728,20 @@ async fn run_instance<A, E, C>(
|
|||
}
|
||||
} else {
|
||||
let state = &mut windows.states[i];
|
||||
state.update(raw, &window_event, &mut debug);
|
||||
state.update(
|
||||
raw,
|
||||
&window_event,
|
||||
&mut debug,
|
||||
);
|
||||
|
||||
if let Some(event) = conversion::window_event(
|
||||
if let Some(event) =
|
||||
conversion::window_event(
|
||||
id,
|
||||
&window_event,
|
||||
state.scale_factor(),
|
||||
state.modifiers(),
|
||||
) {
|
||||
)
|
||||
{
|
||||
events.push((Some(id), event));
|
||||
}
|
||||
}
|
||||
|
|
@ -732,7 +755,8 @@ async fn run_instance<A, E, C>(
|
|||
window_event,
|
||||
winit::event::WindowEvent::Destroyed
|
||||
) {
|
||||
let id = windows.get_pending_destroy(window_id);
|
||||
let id =
|
||||
windows.get_pending_destroy(window_id);
|
||||
|
||||
events.push((
|
||||
None,
|
||||
|
|
@ -748,6 +772,8 @@ async fn run_instance<A, E, C>(
|
|||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let _ = ManuallyDrop::into_inner(user_interfaces);
|
||||
}
|
||||
|
|
@ -780,9 +806,10 @@ where
|
|||
fn update<A: Application, C, E: Executor>(
|
||||
application: &mut A,
|
||||
compositor: &mut C,
|
||||
runtime: &mut Runtime<E, Proxy<Event<A::Message>>, Event<A::Message>>,
|
||||
runtime: &mut Runtime<E, Proxy<A::Message>, A::Message>,
|
||||
clipboard: &mut Clipboard,
|
||||
proxy: &mut winit::event_loop::EventLoopProxy<Event<A::Message>>,
|
||||
control_sender: &mut mpsc::UnboundedSender<Control>,
|
||||
proxy: &mut winit::event_loop::EventLoopProxy<A::Message>,
|
||||
debug: &mut Debug,
|
||||
messages: &mut Vec<A::Message>,
|
||||
windows: &mut Windows<A, C>,
|
||||
|
|
@ -804,6 +831,7 @@ fn update<A: Application, C, E: Executor>(
|
|||
command,
|
||||
runtime,
|
||||
clipboard,
|
||||
control_sender,
|
||||
proxy,
|
||||
debug,
|
||||
windows,
|
||||
|
|
@ -811,7 +839,7 @@ fn update<A: Application, C, E: Executor>(
|
|||
);
|
||||
}
|
||||
|
||||
let subscription = application.subscription().map(Event::Application);
|
||||
let subscription = application.subscription();
|
||||
runtime.track(subscription.into_recipes());
|
||||
}
|
||||
|
||||
|
|
@ -820,9 +848,10 @@ fn run_command<A, C, E>(
|
|||
application: &A,
|
||||
compositor: &mut C,
|
||||
command: Command<A::Message>,
|
||||
runtime: &mut Runtime<E, Proxy<Event<A::Message>>, Event<A::Message>>,
|
||||
runtime: &mut Runtime<E, Proxy<A::Message>, A::Message>,
|
||||
clipboard: &mut Clipboard,
|
||||
proxy: &mut winit::event_loop::EventLoopProxy<Event<A::Message>>,
|
||||
control_sender: &mut mpsc::UnboundedSender<Control>,
|
||||
proxy: &mut winit::event_loop::EventLoopProxy<A::Message>,
|
||||
debug: &mut Debug,
|
||||
windows: &mut Windows<A, C>,
|
||||
ui_caches: &mut Vec<user_interface::Cache>,
|
||||
|
|
@ -839,17 +868,17 @@ fn run_command<A, C, E>(
|
|||
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<A, C, E>(
|
|||
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<A, C, E>(
|
|||
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<A, C, E>(
|
|||
};
|
||||
|
||||
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<A, C, E>(
|
|||
}
|
||||
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<A, C, E>(
|
|||
);
|
||||
|
||||
proxy
|
||||
.send_event(Event::Application(tag(
|
||||
window::Screenshot::new(
|
||||
.send_event(tag(window::Screenshot::new(
|
||||
bytes,
|
||||
state.physical_size(),
|
||||
),
|
||||
)))
|
||||
.expect("Event loop doesn't exist.");
|
||||
}
|
||||
|
|
@ -999,7 +1032,7 @@ fn run_command<A, C, E>(
|
|||
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<A, C, E>(
|
|||
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<A, C, E>(
|
|||
}
|
||||
|
||||
proxy
|
||||
.send_event(Event::Application(tagger(Ok(()))))
|
||||
.send_event(tagger(Ok(())))
|
||||
.expect("Send message to event loop");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue