diff --git a/runtime/src/user_interface.rs b/runtime/src/user_interface.rs index b63b4747..a40ab221 100644 --- a/runtime/src/user_interface.rs +++ b/runtime/src/user_interface.rs @@ -192,6 +192,7 @@ where let mut outdated = false; let mut redraw_request = window::RedrawRequest::Wait; let mut input_method = InputMethod::Disabled; + let mut has_layout_changed = false; let viewport = Rectangle::with_size(self.bounds); let mut maybe_overlay = self @@ -259,6 +260,7 @@ where shell.revalidate_layout(|| { layout = overlay.layout(renderer, bounds); + has_layout_changed = true; }); } @@ -334,6 +336,8 @@ where input_method.merge(shell.input_method()); shell.revalidate_layout(|| { + has_layout_changed = true; + self.base = self.root.as_widget_mut().layout( &mut self.state, renderer, @@ -395,6 +399,7 @@ where mouse_interaction, redraw_request, input_method, + has_layout_changed, } }, event_statuses, @@ -624,5 +629,19 @@ pub enum State { redraw_request: window::RedrawRequest, /// The current [`InputMethod`] strategy of the user interface. 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, + } + } +} diff --git a/test/src/instruction.rs b/test/src/instruction.rs index b87086c5..926619c9 100644 --- a/test/src/instruction.rs +++ b/test/src/instruction.rs @@ -555,7 +555,6 @@ mod parser { fn mouse_click(input: &str) -> IResult<&str, Mouse> { let (input, _) = tag("click ")(input)?; - let (input, (button, target)) = mouse_button_at(input)?; Ok((input, Mouse::Click { button, target })) @@ -563,7 +562,6 @@ mod parser { fn mouse_press(input: &str) -> IResult<&str, Mouse> { let (input, _) = tag("press ")(input)?; - let (input, (button, target)) = mouse_button_at(input)?; Ok((input, Mouse::Press { button, target })) @@ -571,7 +569,6 @@ mod parser { fn mouse_release(input: &str) -> IResult<&str, Mouse> { let (input, _) = tag("release ")(input)?; - let (input, (button, target)) = mouse_button_at(input)?; Ok((input, Mouse::Release { button, target })) diff --git a/widget/src/tooltip.rs b/widget/src/tooltip.rs index 433844cb..c77cbc78 100644 --- a/widget/src/tooltip.rs +++ b/widget/src/tooltip.rs @@ -28,6 +28,7 @@ use crate::core::overlay; use crate::core::renderer; use crate::core::text; use crate::core::widget::{self, Widget}; +use crate::core::window; use crate::core::{ Clipboard, Element, Event, Length, Padding, Pixels, Point, Rectangle, Shell, Size, Vector, @@ -201,7 +202,9 @@ where shell: &mut Shell<'_, Message>, viewport: &Rectangle, ) { - if let Event::Mouse(_) = event { + if let Event::Mouse(_) + | Event::Window(window::Event::RedrawRequested(_)) = event + { let state = tree.state.downcast_mut::(); let previous_state = *state; let was_idle = *state == State::Idle; diff --git a/winit/src/lib.rs b/winit/src/lib.rs index c486db23..802b446b 100644 --- a/winit/src/lib.rs +++ b/winit/src/lib.rs @@ -774,7 +774,7 @@ async fn run_instance

( continue; }; - let Some((id, window)) = + let Some((id, mut window)) = window_manager.get_mut_alias(id) else { continue; @@ -819,20 +819,61 @@ async fn run_instance

( let cursor = window.state.cursor(); - let ui = user_interfaces + let mut interface = user_interfaces .get_mut(&id) .expect("Get user interface"); let draw_span = debug::draw(id); - let (ui_state, _) = ui.update( - slice::from_ref(&redraw_event), - cursor, - &mut window.renderer, - &mut clipboard, - &mut messages, - ); + let mut change_count = 0; - 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; + } + + let caches: FxHashMap<_, _> = + ManuallyDrop::into_inner(user_interfaces) + .into_iter() + .map(|(id, interface)| { + (id, interface.into_cache()) + }) + .collect(); + + update(&mut program, &mut runtime, &mut messages); + + user_interfaces = + ManuallyDrop::new(build_user_interfaces( + &program, + &mut window_manager, + caches, + )); + + window = window_manager.get_mut(id).unwrap(); + interface = user_interfaces.get_mut(&id).unwrap(); + }; + + interface.draw( &mut window.renderer, window.state.theme(), &renderer::Style { @@ -842,23 +883,24 @@ async fn run_instance

( ); draw_span.finish(); - runtime.broadcast(subscription::Event::Interaction { - window: id, - event: redraw_event, - status: core::event::Status::Ignored, - }); - if let user_interface::State::Updated { redraw_request, input_method, mouse_interaction, - } = ui_state + .. + } = state { window.request_redraw(redraw_request); window.request_input_method(input_method); window.update_mouse(mouse_interaction); } + runtime.broadcast(subscription::Event::Interaction { + window: id, + event: redraw_event, + status: core::event::Status::Ignored, + }); + window.draw_preedit(); let present_span = debug::present(id); @@ -1071,32 +1113,14 @@ async fn run_instance

( } if !messages.is_empty() || uis_stale { - let cached_interfaces: FxHashMap< - window::Id, - user_interface::Cache, - > = ManuallyDrop::into_inner(user_interfaces) - .drain() - .map(|(id, ui)| (id, ui.into_cache())) - .collect(); + let cached_interfaces: FxHashMap<_, _> = + ManuallyDrop::into_inner(user_interfaces) + .into_iter() + .map(|(id, ui)| (id, ui.into_cache())) + .collect(); update(&mut program, &mut runtime, &mut messages); - 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()) - }) - }); - user_interfaces = ManuallyDrop::new(build_user_interfaces( &program, @@ -1606,6 +1630,17 @@ where C: Compositor, 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 .drain() .filter_map(|(id, cache)| {