Merge pull request #3084 from iced-rs/synchronous-tasks
Synchronous `Task` Execution and `RedrawRequested` Consistency
This commit is contained in:
commit
fe009142cf
4 changed files with 184 additions and 52 deletions
|
|
@ -192,6 +192,7 @@ where
|
||||||
let mut outdated = false;
|
let mut outdated = false;
|
||||||
let mut redraw_request = window::RedrawRequest::Wait;
|
let mut redraw_request = window::RedrawRequest::Wait;
|
||||||
let mut input_method = InputMethod::Disabled;
|
let mut input_method = InputMethod::Disabled;
|
||||||
|
let mut has_layout_changed = false;
|
||||||
let viewport = Rectangle::with_size(self.bounds);
|
let viewport = Rectangle::with_size(self.bounds);
|
||||||
|
|
||||||
let mut maybe_overlay = self
|
let mut maybe_overlay = self
|
||||||
|
|
@ -259,6 +260,7 @@ where
|
||||||
|
|
||||||
shell.revalidate_layout(|| {
|
shell.revalidate_layout(|| {
|
||||||
layout = overlay.layout(renderer, bounds);
|
layout = overlay.layout(renderer, bounds);
|
||||||
|
has_layout_changed = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -334,6 +336,8 @@ where
|
||||||
input_method.merge(shell.input_method());
|
input_method.merge(shell.input_method());
|
||||||
|
|
||||||
shell.revalidate_layout(|| {
|
shell.revalidate_layout(|| {
|
||||||
|
has_layout_changed = true;
|
||||||
|
|
||||||
self.base = self.root.as_widget_mut().layout(
|
self.base = self.root.as_widget_mut().layout(
|
||||||
&mut self.state,
|
&mut self.state,
|
||||||
renderer,
|
renderer,
|
||||||
|
|
@ -395,6 +399,7 @@ where
|
||||||
mouse_interaction,
|
mouse_interaction,
|
||||||
redraw_request,
|
redraw_request,
|
||||||
input_method,
|
input_method,
|
||||||
|
has_layout_changed,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
event_statuses,
|
event_statuses,
|
||||||
|
|
@ -624,5 +629,19 @@ pub enum State {
|
||||||
redraw_request: window::RedrawRequest,
|
redraw_request: window::RedrawRequest,
|
||||||
/// The current [`InputMethod`] strategy of the user interface.
|
/// The current [`InputMethod`] strategy of the user interface.
|
||||||
input_method: InputMethod,
|
input_method: InputMethod,
|
||||||
|
/// Whether the layout of the [`UserInterface`] has changed.
|
||||||
|
has_layout_changed: bool,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
/// Returns whether the layout of the [`UserInterface`] has changed.
|
||||||
|
pub fn has_layout_changed(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
State::Outdated => true,
|
||||||
|
State::Updated {
|
||||||
|
has_layout_changed, ..
|
||||||
|
} => *has_layout_changed,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -555,7 +555,6 @@ mod parser {
|
||||||
|
|
||||||
fn mouse_click(input: &str) -> IResult<&str, Mouse> {
|
fn mouse_click(input: &str) -> IResult<&str, Mouse> {
|
||||||
let (input, _) = tag("click ")(input)?;
|
let (input, _) = tag("click ")(input)?;
|
||||||
|
|
||||||
let (input, (button, target)) = mouse_button_at(input)?;
|
let (input, (button, target)) = mouse_button_at(input)?;
|
||||||
|
|
||||||
Ok((input, Mouse::Click { button, target }))
|
Ok((input, Mouse::Click { button, target }))
|
||||||
|
|
@ -563,7 +562,6 @@ mod parser {
|
||||||
|
|
||||||
fn mouse_press(input: &str) -> IResult<&str, Mouse> {
|
fn mouse_press(input: &str) -> IResult<&str, Mouse> {
|
||||||
let (input, _) = tag("press ")(input)?;
|
let (input, _) = tag("press ")(input)?;
|
||||||
|
|
||||||
let (input, (button, target)) = mouse_button_at(input)?;
|
let (input, (button, target)) = mouse_button_at(input)?;
|
||||||
|
|
||||||
Ok((input, Mouse::Press { button, target }))
|
Ok((input, Mouse::Press { button, target }))
|
||||||
|
|
@ -571,7 +569,6 @@ mod parser {
|
||||||
|
|
||||||
fn mouse_release(input: &str) -> IResult<&str, Mouse> {
|
fn mouse_release(input: &str) -> IResult<&str, Mouse> {
|
||||||
let (input, _) = tag("release ")(input)?;
|
let (input, _) = tag("release ")(input)?;
|
||||||
|
|
||||||
let (input, (button, target)) = mouse_button_at(input)?;
|
let (input, (button, target)) = mouse_button_at(input)?;
|
||||||
|
|
||||||
Ok((input, Mouse::Release { button, target }))
|
Ok((input, Mouse::Release { button, target }))
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ use crate::core::overlay;
|
||||||
use crate::core::renderer;
|
use crate::core::renderer;
|
||||||
use crate::core::text;
|
use crate::core::text;
|
||||||
use crate::core::widget::{self, Widget};
|
use crate::core::widget::{self, Widget};
|
||||||
|
use crate::core::window;
|
||||||
use crate::core::{
|
use crate::core::{
|
||||||
Clipboard, Element, Event, Length, Padding, Pixels, Point, Rectangle,
|
Clipboard, Element, Event, Length, Padding, Pixels, Point, Rectangle,
|
||||||
Shell, Size, Vector,
|
Shell, Size, Vector,
|
||||||
|
|
@ -201,7 +202,9 @@ where
|
||||||
shell: &mut Shell<'_, Message>,
|
shell: &mut Shell<'_, Message>,
|
||||||
viewport: &Rectangle,
|
viewport: &Rectangle,
|
||||||
) {
|
) {
|
||||||
if let Event::Mouse(_) = event {
|
if let Event::Mouse(_)
|
||||||
|
| Event::Window(window::Event::RedrawRequested(_)) = event
|
||||||
|
{
|
||||||
let state = tree.state.downcast_mut::<State>();
|
let state = tree.state.downcast_mut::<State>();
|
||||||
let previous_state = *state;
|
let previous_state = *state;
|
||||||
let was_idle = *state == State::Idle;
|
let was_idle = *state == State::Idle;
|
||||||
|
|
|
||||||
209
winit/src/lib.rs
209
winit/src/lib.rs
|
|
@ -554,7 +554,7 @@ async fn run_instance<P>(
|
||||||
|
|
||||||
log::info!("System theme: {system_theme:?}");
|
log::info!("System theme: {system_theme:?}");
|
||||||
|
|
||||||
loop {
|
'next_event: loop {
|
||||||
// Empty the queue if possible
|
// Empty the queue if possible
|
||||||
let event = if let Ok(event) = event_receiver.try_next() {
|
let event = if let Ok(event) = event_receiver.try_next() {
|
||||||
event
|
event
|
||||||
|
|
@ -770,11 +770,12 @@ async fn run_instance<P>(
|
||||||
event: event::WindowEvent::RedrawRequested,
|
event: event::WindowEvent::RedrawRequested,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let Some(compositor) = &mut compositor else {
|
let Some(mut current_compositor) = compositor.as_mut()
|
||||||
|
else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some((id, window)) =
|
let Some((id, mut window)) =
|
||||||
window_manager.get_mut_alias(id)
|
window_manager.get_mut_alias(id)
|
||||||
else {
|
else {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -803,7 +804,7 @@ async fn run_instance<P>(
|
||||||
);
|
);
|
||||||
layout_span.finish();
|
layout_span.finish();
|
||||||
|
|
||||||
compositor.configure_surface(
|
current_compositor.configure_surface(
|
||||||
&mut window.surface,
|
&mut window.surface,
|
||||||
physical_size.width,
|
physical_size.width,
|
||||||
physical_size.height,
|
physical_size.height,
|
||||||
|
|
@ -819,20 +820,92 @@ async fn run_instance<P>(
|
||||||
|
|
||||||
let cursor = window.state.cursor();
|
let cursor = window.state.cursor();
|
||||||
|
|
||||||
let ui = user_interfaces
|
let mut interface = user_interfaces
|
||||||
.get_mut(&id)
|
.get_mut(&id)
|
||||||
.expect("Get user interface");
|
.expect("Get user interface");
|
||||||
|
|
||||||
let draw_span = debug::draw(id);
|
let draw_span = debug::draw(id);
|
||||||
let (ui_state, _) = ui.update(
|
let mut change_count = 0;
|
||||||
slice::from_ref(&redraw_event),
|
|
||||||
cursor,
|
|
||||||
&mut window.renderer,
|
|
||||||
&mut clipboard,
|
|
||||||
&mut messages,
|
|
||||||
);
|
|
||||||
|
|
||||||
ui.draw(
|
let state = loop {
|
||||||
|
let (state, _) = interface.update(
|
||||||
|
slice::from_ref(&redraw_event),
|
||||||
|
cursor,
|
||||||
|
&mut window.renderer,
|
||||||
|
&mut clipboard,
|
||||||
|
&mut messages,
|
||||||
|
);
|
||||||
|
|
||||||
|
change_count += 1;
|
||||||
|
|
||||||
|
if messages.is_empty()
|
||||||
|
&& !state.has_layout_changed()
|
||||||
|
{
|
||||||
|
break state;
|
||||||
|
}
|
||||||
|
|
||||||
|
if change_count >= 10 {
|
||||||
|
log::warn!(
|
||||||
|
"More than 10 consecutive RedrawRequested events \
|
||||||
|
produced layout invalidation"
|
||||||
|
);
|
||||||
|
|
||||||
|
break state;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !messages.is_empty() {
|
||||||
|
let caches: FxHashMap<_, _> =
|
||||||
|
ManuallyDrop::into_inner(user_interfaces)
|
||||||
|
.into_iter()
|
||||||
|
.map(|(id, interface)| {
|
||||||
|
(id, interface.into_cache())
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let actions = update(
|
||||||
|
&mut program,
|
||||||
|
&mut runtime,
|
||||||
|
&mut messages,
|
||||||
|
);
|
||||||
|
|
||||||
|
user_interfaces =
|
||||||
|
ManuallyDrop::new(build_user_interfaces(
|
||||||
|
&program,
|
||||||
|
&mut window_manager,
|
||||||
|
caches,
|
||||||
|
));
|
||||||
|
|
||||||
|
for action in actions {
|
||||||
|
run_action(
|
||||||
|
action,
|
||||||
|
&program,
|
||||||
|
&mut runtime,
|
||||||
|
&mut compositor,
|
||||||
|
&mut events,
|
||||||
|
&mut messages,
|
||||||
|
&mut clipboard,
|
||||||
|
&mut control_sender,
|
||||||
|
&mut user_interfaces,
|
||||||
|
&mut window_manager,
|
||||||
|
&mut ui_caches,
|
||||||
|
&mut is_window_opening,
|
||||||
|
&mut system_theme,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(next_compositor) = compositor.as_mut()
|
||||||
|
else {
|
||||||
|
continue 'next_event;
|
||||||
|
};
|
||||||
|
|
||||||
|
current_compositor = next_compositor;
|
||||||
|
window = window_manager.get_mut(id).unwrap();
|
||||||
|
interface =
|
||||||
|
user_interfaces.get_mut(&id).unwrap();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
interface.draw(
|
||||||
&mut window.renderer,
|
&mut window.renderer,
|
||||||
window.state.theme(),
|
window.state.theme(),
|
||||||
&renderer::Style {
|
&renderer::Style {
|
||||||
|
|
@ -842,27 +915,28 @@ async fn run_instance<P>(
|
||||||
);
|
);
|
||||||
draw_span.finish();
|
draw_span.finish();
|
||||||
|
|
||||||
runtime.broadcast(subscription::Event::Interaction {
|
|
||||||
window: id,
|
|
||||||
event: redraw_event,
|
|
||||||
status: core::event::Status::Ignored,
|
|
||||||
});
|
|
||||||
|
|
||||||
if let user_interface::State::Updated {
|
if let user_interface::State::Updated {
|
||||||
redraw_request,
|
redraw_request,
|
||||||
input_method,
|
input_method,
|
||||||
mouse_interaction,
|
mouse_interaction,
|
||||||
} = ui_state
|
..
|
||||||
|
} = state
|
||||||
{
|
{
|
||||||
window.request_redraw(redraw_request);
|
window.request_redraw(redraw_request);
|
||||||
window.request_input_method(input_method);
|
window.request_input_method(input_method);
|
||||||
window.update_mouse(mouse_interaction);
|
window.update_mouse(mouse_interaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
runtime.broadcast(subscription::Event::Interaction {
|
||||||
|
window: id,
|
||||||
|
event: redraw_event,
|
||||||
|
status: core::event::Status::Ignored,
|
||||||
|
});
|
||||||
|
|
||||||
window.draw_preedit();
|
window.draw_preedit();
|
||||||
|
|
||||||
let present_span = debug::present(id);
|
let present_span = debug::present(id);
|
||||||
match compositor.present(
|
match current_compositor.present(
|
||||||
&mut window.renderer,
|
&mut window.renderer,
|
||||||
&mut window.surface,
|
&mut window.surface,
|
||||||
window.state.viewport(),
|
window.state.viewport(),
|
||||||
|
|
@ -1071,31 +1145,17 @@ async fn run_instance<P>(
|
||||||
}
|
}
|
||||||
|
|
||||||
if !messages.is_empty() || uis_stale {
|
if !messages.is_empty() || uis_stale {
|
||||||
let cached_interfaces: FxHashMap<
|
let cached_interfaces: FxHashMap<_, _> =
|
||||||
window::Id,
|
ManuallyDrop::into_inner(user_interfaces)
|
||||||
user_interface::Cache,
|
.into_iter()
|
||||||
> = ManuallyDrop::into_inner(user_interfaces)
|
.map(|(id, ui)| (id, ui.into_cache()))
|
||||||
.drain()
|
.collect();
|
||||||
.map(|(id, ui)| (id, ui.into_cache()))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
update(&mut program, &mut runtime, &mut messages);
|
let actions = update(
|
||||||
|
&mut program,
|
||||||
for (id, window) in window_manager.iter_mut() {
|
&mut runtime,
|
||||||
window.state.synchronize(
|
&mut messages,
|
||||||
&program,
|
);
|
||||||
id,
|
|
||||||
&window.raw,
|
|
||||||
);
|
|
||||||
|
|
||||||
window.raw.request_redraw();
|
|
||||||
}
|
|
||||||
|
|
||||||
debug::theme_changed(|| {
|
|
||||||
window_manager.first().and_then(|window| {
|
|
||||||
theme::Base::palette(window.state.theme())
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
user_interfaces =
|
user_interfaces =
|
||||||
ManuallyDrop::new(build_user_interfaces(
|
ManuallyDrop::new(build_user_interfaces(
|
||||||
|
|
@ -1103,6 +1163,24 @@ async fn run_instance<P>(
|
||||||
&mut window_manager,
|
&mut window_manager,
|
||||||
cached_interfaces,
|
cached_interfaces,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
for action in actions {
|
||||||
|
run_action(
|
||||||
|
action,
|
||||||
|
&program,
|
||||||
|
&mut runtime,
|
||||||
|
&mut compositor,
|
||||||
|
&mut events,
|
||||||
|
&mut messages,
|
||||||
|
&mut clipboard,
|
||||||
|
&mut control_sender,
|
||||||
|
&mut user_interfaces,
|
||||||
|
&mut window_manager,
|
||||||
|
&mut ui_caches,
|
||||||
|
&mut is_window_opening,
|
||||||
|
&mut system_theme,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(redraw_at) = window_manager.redraw_at() {
|
if let Some(redraw_at) = window_manager.redraw_at() {
|
||||||
|
|
@ -1152,14 +1230,36 @@ fn update<P: Program, E: Executor>(
|
||||||
program: &mut program::Instance<P>,
|
program: &mut program::Instance<P>,
|
||||||
runtime: &mut Runtime<E, Proxy<P::Message>, Action<P::Message>>,
|
runtime: &mut Runtime<E, Proxy<P::Message>, Action<P::Message>>,
|
||||||
messages: &mut Vec<P::Message>,
|
messages: &mut Vec<P::Message>,
|
||||||
) where
|
) -> Vec<Action<P::Message>>
|
||||||
|
where
|
||||||
P::Theme: theme::Base,
|
P::Theme: theme::Base,
|
||||||
{
|
{
|
||||||
|
use futures::futures;
|
||||||
|
|
||||||
|
let mut actions = Vec::new();
|
||||||
|
|
||||||
for message in messages.drain(..) {
|
for message in messages.drain(..) {
|
||||||
let task = runtime.enter(|| program.update(message));
|
let task = runtime.enter(|| program.update(message));
|
||||||
|
|
||||||
if let Some(stream) = runtime::task::into_stream(task) {
|
if let Some(mut stream) = runtime::task::into_stream(task) {
|
||||||
runtime.run(stream);
|
let waker = futures::task::noop_waker_ref();
|
||||||
|
let mut context = futures::task::Context::from_waker(waker);
|
||||||
|
|
||||||
|
// Run immediately available actions synchronously (e.g. widget operations)
|
||||||
|
loop {
|
||||||
|
match runtime.enter(|| stream.poll_next_unpin(&mut context)) {
|
||||||
|
futures::task::Poll::Ready(Some(action)) => {
|
||||||
|
actions.push(action);
|
||||||
|
}
|
||||||
|
futures::task::Poll::Ready(None) => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
futures::task::Poll::Pending => {
|
||||||
|
runtime.run(stream);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1167,6 +1267,8 @@ fn update<P: Program, E: Executor>(
|
||||||
let recipes = subscription::into_recipes(subscription.map(Action::Output));
|
let recipes = subscription::into_recipes(subscription.map(Action::Output));
|
||||||
|
|
||||||
runtime.track(recipes);
|
runtime.track(recipes);
|
||||||
|
|
||||||
|
actions
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_action<'a, P, C>(
|
fn run_action<'a, P, C>(
|
||||||
|
|
@ -1606,6 +1708,17 @@ where
|
||||||
C: Compositor<Renderer = P::Renderer>,
|
C: Compositor<Renderer = P::Renderer>,
|
||||||
P::Theme: theme::Base,
|
P::Theme: theme::Base,
|
||||||
{
|
{
|
||||||
|
for (id, window) in window_manager.iter_mut() {
|
||||||
|
window.state.synchronize(program, id, &window.raw);
|
||||||
|
window.raw.request_redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
debug::theme_changed(|| {
|
||||||
|
window_manager
|
||||||
|
.first()
|
||||||
|
.and_then(|window| theme::Base::palette(window.state.theme()))
|
||||||
|
});
|
||||||
|
|
||||||
cached_user_interfaces
|
cached_user_interfaces
|
||||||
.drain()
|
.drain()
|
||||||
.filter_map(|(id, cache)| {
|
.filter_map(|(id, cache)| {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue