diff --git a/benches/wgpu.rs b/benches/wgpu.rs index a39ebbcd..c8792b7a 100644 --- a/benches/wgpu.rs +++ b/benches/wgpu.rs @@ -122,7 +122,7 @@ fn benchmark<'a>( &mut renderer, ); - let _ = user_interface.draw( + user_interface.draw( &mut renderer, &Theme::Dark, &core::renderer::Style { diff --git a/examples/integration/src/main.rs b/examples/integration/src/main.rs index 639858fd..16794662 100644 --- a/examples/integration/src/main.rs +++ b/examples/integration/src/main.rs @@ -268,7 +268,7 @@ pub fn main() -> Result<(), winit::error::EventLoopError> { renderer, ); - let _ = interface.update( + let (state, _) = interface.update( &[Event::Window( window::Event::RedrawRequested( Instant::now(), @@ -280,7 +280,21 @@ pub fn main() -> Result<(), winit::error::EventLoopError> { &mut Vec::new(), ); - let mouse_interaction = interface.draw( + // Update the mouse cursor + if let user_interface::State::Updated { + mouse_interaction, + .. + } = state + { + window.set_cursor( + conversion::mouse_interaction( + mouse_interaction, + ), + ); + } + + // Draw the interface + interface.draw( renderer, &Theme::Dark, &renderer::Style::default(), @@ -297,11 +311,6 @@ pub fn main() -> Result<(), winit::error::EventLoopError> { // Present the frame frame.present(); - - // Update the mouse cursor - window.set_cursor(conversion::mouse_interaction( - mouse_interaction, - )); } Err(error) => match error { wgpu::SurfaceError::OutOfMemory => { diff --git a/runtime/src/user_interface.rs b/runtime/src/user_interface.rs index 7fcf8a04..88e6df18 100644 --- a/runtime/src/user_interface.rs +++ b/runtime/src/user_interface.rs @@ -27,10 +27,15 @@ pub struct UserInterface<'a, Message, Theme, Renderer> { root: Element<'a, Message, Theme, Renderer>, base: layout::Node, state: widget::Tree, - overlay: Option, + overlay: Option, bounds: Size, } +struct Overlay { + layout: layout::Node, + interaction: mouse::Interaction, +} + impl<'a, Message, Theme, Renderer> UserInterface<'a, Message, Theme, Renderer> where Renderer: crate::core::Renderer, @@ -185,60 +190,58 @@ where clipboard: &mut dyn Clipboard, messages: &mut Vec, ) -> (State, Vec) { - use std::mem::ManuallyDrop; - let mut outdated = false; let mut redraw_request = window::RedrawRequest::Wait; let mut input_method = InputMethod::Disabled; let viewport = Rectangle::with_size(self.bounds); - let mut manual_overlay = ManuallyDrop::new( - self.root - .as_widget_mut() - .overlay( - &mut self.state, - Layout::new(&self.base), - renderer, - &viewport, - Vector::ZERO, - ) - .map(overlay::Nested::new), - ); + let mut maybe_overlay = self + .root + .as_widget_mut() + .overlay( + &mut self.state, + Layout::new(&self.base), + renderer, + &viewport, + Vector::ZERO, + ) + .map(overlay::Nested::new); - let (base_cursor, overlay_statuses) = if manual_overlay.is_some() { - let bounds = self.bounds; + let (base_cursor, overlay_statuses, overlay_interaction) = + if maybe_overlay.is_some() { + let bounds = self.bounds; - let mut overlay = manual_overlay.as_mut().unwrap(); - let mut layout = overlay.layout(renderer, bounds); - let mut event_statuses = Vec::new(); + let mut overlay = maybe_overlay.as_mut().unwrap(); + let mut layout = overlay.layout(renderer, bounds); + let mut event_statuses = Vec::new(); - for event in events { - let mut shell = Shell::new(messages); + for event in events { + let mut shell = Shell::new(messages); - overlay.update( - event, - Layout::new(&layout), - cursor, - renderer, - clipboard, - &mut shell, - ); - - event_statuses.push(shell.event_status()); - redraw_request = redraw_request.min(shell.redraw_request()); - input_method.merge(shell.input_method()); - - if shell.is_layout_invalid() { - let _ = ManuallyDrop::into_inner(manual_overlay); - - self.base = self.root.as_widget().layout( - &mut self.state, + overlay.update( + event, + Layout::new(&layout), + cursor, renderer, - &layout::Limits::new(Size::ZERO, self.bounds), + clipboard, + &mut shell, ); - manual_overlay = ManuallyDrop::new( - self.root + event_statuses.push(shell.event_status()); + redraw_request = redraw_request.min(shell.redraw_request()); + input_method.merge(shell.input_method()); + + if shell.is_layout_invalid() { + drop(maybe_overlay); + + self.base = self.root.as_widget().layout( + &mut self.state, + renderer, + &layout::Limits::new(Size::ZERO, self.bounds), + ); + + maybe_overlay = self + .root .as_widget_mut() .overlay( &mut self.state, @@ -247,53 +250,61 @@ where &viewport, Vector::ZERO, ) - .map(overlay::Nested::new), - ); + .map(overlay::Nested::new); - if manual_overlay.is_none() { - break; + if maybe_overlay.is_none() { + break; + } + + overlay = maybe_overlay.as_mut().unwrap(); + + shell.revalidate_layout(|| { + layout = overlay.layout(renderer, bounds); + }); } - overlay = manual_overlay.as_mut().unwrap(); - - shell.revalidate_layout(|| { - layout = overlay.layout(renderer, bounds); - }); + if shell.are_widgets_invalid() { + outdated = true; + } } - if shell.are_widgets_invalid() { - outdated = true; - } - } + let (base_cursor, interaction) = + if let Some(overlay) = maybe_overlay.as_mut() { + let interaction = cursor + .position() + .map(|cursor_position| { + overlay.mouse_interaction( + Layout::new(&layout), + mouse::Cursor::Available(cursor_position), + renderer, + ) + }) + .unwrap_or_default(); - let base_cursor = if manual_overlay - .as_mut() - .and_then(|overlay| { - cursor.position().map(|cursor_position| { - overlay.mouse_interaction( - Layout::new(&layout), - mouse::Cursor::Available(cursor_position), - renderer, - ) != mouse::Interaction::None - }) - }) - .unwrap_or_default() - { - mouse::Cursor::Unavailable + if interaction == mouse::Interaction::None { + (cursor, mouse::Interaction::None) + } else { + (mouse::Cursor::Unavailable, interaction) + } + } else { + (cursor, mouse::Interaction::None) + }; + + self.overlay = Some(Overlay { + layout, + interaction, + }); + + (base_cursor, event_statuses, interaction) } else { - cursor + ( + cursor, + vec![event::Status::Ignored; events.len()], + mouse::Interaction::None, + ) }; - self.overlay = Some(layout); - - (base_cursor, event_statuses) - } else { - (cursor, vec![event::Status::Ignored; events.len()]) - }; - - let viewport = Rectangle::with_size(self.bounds); - - let _ = ManuallyDrop::into_inner(manual_overlay); + drop(maybe_overlay); let event_statuses = events .iter() @@ -341,11 +352,25 @@ where }) .collect(); + let mouse_interaction = + if overlay_interaction == mouse::Interaction::None { + self.root.as_widget().mouse_interaction( + &self.state, + Layout::new(&self.base), + base_cursor, + &viewport, + renderer, + ) + } else { + overlay_interaction + }; + ( if outdated { State::Outdated } else { State::Updated { + mouse_interaction, redraw_request, input_method, } @@ -433,54 +458,19 @@ where theme: &Theme, style: &renderer::Style, cursor: mouse::Cursor, - ) -> mouse::Interaction { + ) { // TODO: Move to shell level (?) renderer.clear(); let viewport = Rectangle::with_size(self.bounds); - let (base_cursor, overlay_interaction) = if let Some(mut overlay) = self - .root - .as_widget_mut() - .overlay( - &mut self.state, - Layout::new(&self.base), - renderer, - &viewport, - Vector::ZERO, - ) - .map(overlay::Nested::new) - { - let overlay_layout = self - .overlay - .take() - .unwrap_or_else(|| overlay.layout(renderer, self.bounds)); - - let (cursor, overlay_interaction) = { - let overlay_interaction = - cursor.position().map(|cursor_position| { - overlay.mouse_interaction( - Layout::new(&overlay_layout), - mouse::Cursor::Available(cursor_position), - renderer, - ) - }); - - match overlay_interaction { - Some(mouse::Interaction::None) | None => { - (cursor, mouse::Interaction::None) - } - Some(interaction) => { - (mouse::Cursor::Unavailable, interaction) - } - } - }; - - self.overlay = Some(overlay_layout); - - (cursor, overlay_interaction) - } else { - (cursor, mouse::Interaction::None) + let base_cursor = match &self.overlay { + None + | Some(Overlay { + interaction: mouse::Interaction::None, + .. + }) => cursor, + _ => mouse::Cursor::Unavailable, }; self.root.as_widget().draw( @@ -493,14 +483,6 @@ where &viewport, ); - let base_interaction = self.root.as_widget().mouse_interaction( - &self.state, - Layout::new(&self.base), - base_cursor, - &viewport, - renderer, - ); - let Self { overlay, root, @@ -508,40 +490,24 @@ where .. } = self; - // TODO: Currently, we need to call Widget::overlay twice to - // implement the painter's algorithm properly. - // - // Once we have a proper persistent widget tree, we should be able to - // avoid this additional call. - overlay - .as_ref() - .and_then(|layout| { - root.as_widget_mut() - .overlay( - &mut self.state, - Layout::new(base), - renderer, - &viewport, - Vector::ZERO, - ) - .map(overlay::Nested::new) - .map(|mut overlay| { - overlay.draw( - renderer, - theme, - style, - Layout::new(layout), - cursor, - ); + let Some(Overlay { layout, .. }) = overlay.as_ref() else { + return; + }; - if overlay_interaction != mouse::Interaction::None { - overlay_interaction - } else { - base_interaction - } - }) - }) - .unwrap_or(base_interaction) + let overlay = root + .as_widget_mut() + .overlay( + &mut self.state, + Layout::new(base), + renderer, + &viewport, + Vector::ZERO, + ) + .map(overlay::Nested::new); + + if let Some(mut overlay) = overlay { + overlay.draw(renderer, theme, style, Layout::new(layout), cursor); + } } /// Applies a [`widget::Operation`] to the [`UserInterface`]. @@ -572,11 +538,14 @@ where .map(overlay::Nested::new) { if self.overlay.is_none() { - self.overlay = Some(overlay.layout(renderer, self.bounds)); + self.overlay = Some(Overlay { + layout: overlay.layout(renderer, self.bounds), + interaction: mouse::Interaction::None, + }); } overlay.operate( - Layout::new(self.overlay.as_ref().unwrap()), + Layout::new(&self.overlay.as_ref().unwrap().layout), renderer, operation, ); @@ -629,6 +598,8 @@ pub enum State { /// The [`UserInterface`] is up-to-date and can be reused without /// rebuilding. Updated { + /// The current [`mouse::Interaction`] of the user interface. + mouse_interaction: mouse::Interaction, /// The [`window::RedrawRequest`] describing when a redraw should be performed. redraw_request: window::RedrawRequest, /// The current [`InputMethod`] strategy of the user interface. diff --git a/test/src/lib.rs b/test/src/lib.rs index 6fff4c74..636b8173 100644 --- a/test/src/lib.rs +++ b/test/src/lib.rs @@ -436,7 +436,7 @@ where &mut self.messages, ); - let _ = self.raw.draw( + self.raw.draw( &mut self.renderer, theme, &core::renderer::Style { diff --git a/winit/src/lib.rs b/winit/src/lib.rs index 52a987d4..7b94a878 100644 --- a/winit/src/lib.rs +++ b/winit/src/lib.rs @@ -758,7 +758,7 @@ async fn run_instance

( &mut messages, ); - let new_mouse_interaction = ui.draw( + ui.draw( &mut window.renderer, window.state.theme(), &renderer::Style { @@ -768,16 +768,6 @@ async fn run_instance

( ); draw_span.finish(); - if new_mouse_interaction != window.mouse_interaction { - window.raw.set_cursor( - conversion::mouse_interaction( - new_mouse_interaction, - ), - ); - - window.mouse_interaction = new_mouse_interaction; - } - runtime.broadcast(subscription::Event::Interaction { window: id, event: redraw_event, @@ -787,10 +777,12 @@ async fn run_instance

( if let user_interface::State::Updated { redraw_request, input_method, + mouse_interaction, } = ui_state { window.request_redraw(redraw_request); window.request_input_method(input_method); + window.update_mouse(mouse_interaction); } window.draw_preedit(); @@ -944,8 +936,11 @@ async fn run_instance

( match ui_state { user_interface::State::Updated { redraw_request: _redraw_request, + mouse_interaction, .. } => { + window.update_mouse(mouse_interaction); + #[cfg(not( feature = "unconditional-rendering" ))] diff --git a/winit/src/window.rs b/winit/src/window.rs index 801fc086..e9a058af 100644 --- a/winit/src/window.rs +++ b/winit/src/window.rs @@ -246,6 +246,15 @@ where } } + pub fn update_mouse(&mut self, interaction: mouse::Interaction) { + if interaction != self.mouse_interaction { + self.raw + .set_cursor(conversion::mouse_interaction(interaction)); + + self.mouse_interaction = interaction; + } + } + pub fn draw_preedit(&mut self) { if let Some(preedit) = &self.preedit { preedit.draw(