Invalidate widget tree from Responsive widget

... by introducing a new `invalidate_widgets` method to `Shell`
This commit is contained in:
Héctor Ramón Jiménez 2022-01-11 13:47:43 +07:00
parent 90c20ac46b
commit 6ab4611a6e
No known key found for this signature in database
GPG key ID: 140CC052C94F138E
8 changed files with 85 additions and 33 deletions

View file

@ -9,7 +9,8 @@ use iced_winit::application;
use iced_winit::conversion; use iced_winit::conversion;
use iced_winit::futures; use iced_winit::futures;
use iced_winit::futures::channel::mpsc; use iced_winit::futures::channel::mpsc;
use iced_winit::{Cache, Clipboard, Debug, Proxy, Settings}; use iced_winit::user_interface;
use iced_winit::{Clipboard, Debug, Proxy, Settings};
use glutin::window::Window; use glutin::window::Window;
use std::mem::ManuallyDrop; use std::mem::ManuallyDrop;
@ -177,7 +178,7 @@ async fn run_instance<A, E, C>(
let mut user_interface = let mut user_interface =
ManuallyDrop::new(application::build_user_interface( ManuallyDrop::new(application::build_user_interface(
&mut application, &mut application,
Cache::default(), user_interface::Cache::default(),
&mut renderer, &mut renderer,
state.logical_size(), state.logical_size(),
&mut debug, &mut debug,
@ -198,7 +199,7 @@ async fn run_instance<A, E, C>(
debug.event_processing_started(); debug.event_processing_started();
let statuses = user_interface.update( let (interface_state, statuses) = user_interface.update(
&events, &events,
state.cursor_position(), state.cursor_position(),
&mut renderer, &mut renderer,
@ -212,7 +213,12 @@ async fn run_instance<A, E, C>(
runtime.broadcast(event); runtime.broadcast(event);
} }
if !messages.is_empty() { if !messages.is_empty()
|| matches!(
interface_state,
user_interface::State::Outdated
)
{
let cache = let cache =
ManuallyDrop::into_inner(user_interface).into_cache(); ManuallyDrop::into_inner(user_interface).into_cache();

View file

@ -149,7 +149,7 @@ where
) )
}); });
local_shell.with_invalid_layout(|| shell.invalidate_layout()); local_shell.revalidate_layout(|| shell.invalidate_layout());
if !local_messages.is_empty() { if !local_messages.is_empty() {
let mut component = self let mut component = self
@ -375,7 +375,7 @@ where
}) })
.unwrap_or_else(|| iced_native::event::Status::Ignored); .unwrap_or_else(|| iced_native::event::Status::Ignored);
local_shell.with_invalid_layout(|| shell.invalidate_layout()); local_shell.revalidate_layout(|| shell.invalidate_layout());
if !local_messages.is_empty() { if !local_messages.is_empty() {
let mut component = let mut component =

View file

@ -50,6 +50,7 @@ pub mod subscription;
pub mod svg; pub mod svg;
pub mod text; pub mod text;
pub mod touch; pub mod touch;
pub mod user_interface;
pub mod widget; pub mod widget;
pub mod window; pub mod window;
@ -57,7 +58,6 @@ mod element;
mod hasher; mod hasher;
mod runtime; mod runtime;
mod shell; mod shell;
mod user_interface;
// We disable debug capabilities on release builds unless the `debug` feature // We disable debug capabilities on release builds unless the `debug` feature
// is explicitly enabled. // is explicitly enabled.
@ -91,5 +91,5 @@ pub use renderer::Renderer;
pub use runtime::Runtime; pub use runtime::Runtime;
pub use shell::Shell; pub use shell::Shell;
pub use subscription::Subscription; pub use subscription::Subscription;
pub use user_interface::{Cache, UserInterface}; pub use user_interface::UserInterface;
pub use widget::Widget; pub use widget::Widget;

View file

@ -1,8 +1,6 @@
use crate::mouse; use crate::mouse;
use crate::{ use crate::user_interface::{self, UserInterface};
Cache, Clipboard, Command, Debug, Event, Point, Program, Size, use crate::{Clipboard, Command, Debug, Event, Point, Program, Size};
UserInterface,
};
/// The execution state of a [`Program`]. It leverages caching, event /// The execution state of a [`Program`]. It leverages caching, event
/// processing, and rendering primitive storage. /// processing, and rendering primitive storage.
@ -12,7 +10,7 @@ where
P: Program + 'static, P: Program + 'static,
{ {
program: P, program: P,
cache: Option<Cache>, cache: Option<user_interface::Cache>,
queued_events: Vec<Event>, queued_events: Vec<Event>,
queued_messages: Vec<P::Message>, queued_messages: Vec<P::Message>,
mouse_interaction: mouse::Interaction, mouse_interaction: mouse::Interaction,
@ -32,7 +30,7 @@ where
) -> Self { ) -> Self {
let user_interface = build_user_interface( let user_interface = build_user_interface(
&mut program, &mut program,
Cache::default(), user_interface::Cache::default(),
renderer, renderer,
bounds, bounds,
debug, debug,
@ -161,7 +159,7 @@ where
fn build_user_interface<'a, P: Program>( fn build_user_interface<'a, P: Program>(
program: &'a mut P, program: &'a mut P,
cache: Cache, cache: user_interface::Cache,
renderer: &mut P::Renderer, renderer: &mut P::Renderer,
size: Size, size: Size,
debug: &mut Debug, debug: &mut Debug,

View file

@ -8,6 +8,7 @@
pub struct Shell<'a, Message> { pub struct Shell<'a, Message> {
messages: &'a mut Vec<Message>, messages: &'a mut Vec<Message>,
is_layout_invalid: bool, is_layout_invalid: bool,
are_widgets_invalid: bool,
} }
impl<'a, Message> Shell<'a, Message> { impl<'a, Message> Shell<'a, Message> {
@ -16,12 +17,13 @@ impl<'a, Message> Shell<'a, Message> {
Self { Self {
messages, messages,
is_layout_invalid: false, is_layout_invalid: false,
are_widgets_invalid: false,
} }
} }
/// Triggers the given function if the layout is invalid, cleaning it in the /// Triggers the given function if the layout is invalid, cleaning it in the
/// process. /// process.
pub fn with_invalid_layout(&mut self, f: impl FnOnce()) { pub fn revalidate_layout(&mut self, f: impl FnOnce()) {
if self.is_layout_invalid { if self.is_layout_invalid {
self.is_layout_invalid = false; self.is_layout_invalid = false;
@ -41,6 +43,13 @@ impl<'a, Message> Shell<'a, Message> {
self.is_layout_invalid = true; self.is_layout_invalid = true;
} }
/// Invalidates the current application widgets.
///
/// The shell will rebuild and relayout the widget tree.
pub fn invalidate_widgets(&mut self) {
self.are_widgets_invalid = true;
}
/// Merges the current [`Shell`] with another one by applying the given /// Merges the current [`Shell`] with another one by applying the given
/// function to the messages of the latter. /// function to the messages of the latter.
/// ///
@ -50,5 +59,14 @@ impl<'a, Message> Shell<'a, Message> {
self.is_layout_invalid = self.is_layout_invalid =
self.is_layout_invalid || other.is_layout_invalid; self.is_layout_invalid || other.is_layout_invalid;
self.are_widgets_invalid =
self.are_widgets_invalid || other.are_widgets_invalid;
}
/// Returns whether the widgets of the current application have been
/// invalidated.
pub fn are_widgets_invalid(&self) -> bool {
self.are_widgets_invalid
} }
} }

View file

@ -42,7 +42,8 @@ where
/// is naive way to set up our application loop: /// is naive way to set up our application loop:
/// ///
/// ```no_run /// ```no_run
/// use iced_native::{UserInterface, Cache, Size}; /// use iced_native::Size;
/// use iced_native::user_interface::{self, UserInterface};
/// use iced_wgpu::Renderer; /// use iced_wgpu::Renderer;
/// ///
/// # mod iced_wgpu { /// # mod iced_wgpu {
@ -61,7 +62,7 @@ where
/// # } /// # }
/// // Initialization /// // Initialization
/// let mut counter = Counter::new(); /// let mut counter = Counter::new();
/// let mut cache = Cache::new(); /// let mut cache = user_interface::Cache::new();
/// let mut renderer = Renderer::new(); /// let mut renderer = Renderer::new();
/// let mut window_size = Size::new(1024.0, 768.0); /// let mut window_size = Size::new(1024.0, 768.0);
/// ///
@ -136,7 +137,8 @@ where
/// completing [the previous example](#example): /// completing [the previous example](#example):
/// ///
/// ```no_run /// ```no_run
/// use iced_native::{clipboard, UserInterface, Cache, Size, Point}; /// use iced_native::{clipboard, Size, Point};
/// use iced_native::user_interface::{self, UserInterface};
/// use iced_wgpu::Renderer; /// use iced_wgpu::Renderer;
/// ///
/// # mod iced_wgpu { /// # mod iced_wgpu {
@ -155,7 +157,7 @@ where
/// # pub fn update(&mut self, message: ()) {} /// # pub fn update(&mut self, message: ()) {}
/// # } /// # }
/// let mut counter = Counter::new(); /// let mut counter = Counter::new();
/// let mut cache = Cache::new(); /// let mut cache = user_interface::Cache::new();
/// let mut renderer = Renderer::new(); /// let mut renderer = Renderer::new();
/// let mut window_size = Size::new(1024.0, 768.0); /// let mut window_size = Size::new(1024.0, 768.0);
/// let mut cursor_position = Point::default(); /// let mut cursor_position = Point::default();
@ -176,7 +178,7 @@ where
/// ); /// );
/// ///
/// // Update the user interface /// // Update the user interface
/// let event_statuses = user_interface.update( /// let (state, event_statuses) = user_interface.update(
/// &events, /// &events,
/// cursor_position, /// cursor_position,
/// &mut renderer, /// &mut renderer,
@ -199,7 +201,9 @@ where
renderer: &mut Renderer, renderer: &mut Renderer,
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
messages: &mut Vec<Message>, messages: &mut Vec<Message>,
) -> Vec<event::Status> { ) -> (State, Vec<event::Status>) {
let mut state = State::Updated;
let (base_cursor, overlay_statuses) = if let Some(mut overlay) = let (base_cursor, overlay_statuses) = if let Some(mut overlay) =
self.root.overlay(Layout::new(&self.base.layout)) self.root.overlay(Layout::new(&self.base.layout))
{ {
@ -227,7 +231,7 @@ where
&mut shell, &mut shell,
); );
shell.with_invalid_layout(|| { shell.revalidate_layout(|| {
layer = Self::overlay_layer( layer = Self::overlay_layer(
None, None,
bounds, bounds,
@ -236,6 +240,10 @@ where
); );
}); });
if shell.are_widgets_invalid() {
state = State::Outdated;
}
event_status event_status
}) })
.collect(); .collect();
@ -255,7 +263,7 @@ where
(cursor_position, vec![event::Status::Ignored; events.len()]) (cursor_position, vec![event::Status::Ignored; events.len()])
}; };
events let event_statuses = events
.iter() .iter()
.cloned() .cloned()
.zip(overlay_statuses.into_iter()) .zip(overlay_statuses.into_iter())
@ -271,7 +279,7 @@ where
&mut shell, &mut shell,
); );
shell.with_invalid_layout(|| { shell.revalidate_layout(|| {
let hash = { let hash = {
let hasher = &mut crate::Hasher::default(); let hasher = &mut crate::Hasher::default();
self.root.hash_layout(hasher); self.root.hash_layout(hasher);
@ -288,9 +296,15 @@ where
self.overlay = None; self.overlay = None;
}); });
if shell.are_widgets_invalid() {
state = State::Outdated;
}
event_status.merge(overlay_status) event_status.merge(overlay_status)
}) })
.collect() .collect();
(state, event_statuses)
} }
/// Draws the [`UserInterface`] with the provided [`Renderer`]. /// Draws the [`UserInterface`] with the provided [`Renderer`].
@ -306,7 +320,8 @@ where
/// [completing the last example](#example-1): /// [completing the last example](#example-1):
/// ///
/// ```no_run /// ```no_run
/// use iced_native::{clipboard, UserInterface, Cache, Size, Point}; /// use iced_native::{clipboard, Size, Point};
/// use iced_native::user_interface::{self, UserInterface};
/// use iced_wgpu::Renderer; /// use iced_wgpu::Renderer;
/// ///
/// # mod iced_wgpu { /// # mod iced_wgpu {
@ -325,7 +340,7 @@ where
/// # pub fn update(&mut self, message: ()) {} /// # pub fn update(&mut self, message: ()) {}
/// # } /// # }
/// let mut counter = Counter::new(); /// let mut counter = Counter::new();
/// let mut cache = Cache::new(); /// let mut cache = user_interface::Cache::new();
/// let mut renderer = Renderer::new(); /// let mut renderer = Renderer::new();
/// let mut window_size = Size::new(1024.0, 768.0); /// let mut window_size = Size::new(1024.0, 768.0);
/// let mut cursor_position = Point::default(); /// let mut cursor_position = Point::default();
@ -548,3 +563,9 @@ impl Default for Cache {
Cache::new() Cache::new()
} }
} }
#[derive(Debug, Clone, Copy)]
pub enum State {
Outdated,
Updated,
}

View file

@ -80,6 +80,10 @@ where
let Internal { content, state } = internal.deref_mut(); let Internal { content, state } = internal.deref_mut();
if state.last_size != Some(state.last_layout.size()) {
shell.invalidate_widgets();
}
let content = content.resolve(state, renderer); let content = content.resolve(state, renderer);
let content_layout = Layout::with_offset( let content_layout = Layout::with_offset(

View file

@ -15,7 +15,7 @@ use iced_futures::futures;
use iced_futures::futures::channel::mpsc; use iced_futures::futures::channel::mpsc;
use iced_graphics::window; use iced_graphics::window;
use iced_native::program::Program; use iced_native::program::Program;
use iced_native::{Cache, UserInterface}; use iced_native::user_interface::{self, UserInterface};
use std::mem::ManuallyDrop; use std::mem::ManuallyDrop;
@ -250,7 +250,7 @@ async fn run_instance<A, E, C>(
let mut user_interface = ManuallyDrop::new(build_user_interface( let mut user_interface = ManuallyDrop::new(build_user_interface(
&mut application, &mut application,
Cache::default(), user_interface::Cache::default(),
&mut renderer, &mut renderer,
state.logical_size(), state.logical_size(),
&mut debug, &mut debug,
@ -271,7 +271,7 @@ async fn run_instance<A, E, C>(
debug.event_processing_started(); debug.event_processing_started();
let statuses = user_interface.update( let (interface_state, statuses) = user_interface.update(
&events, &events,
state.cursor_position(), state.cursor_position(),
&mut renderer, &mut renderer,
@ -285,7 +285,12 @@ async fn run_instance<A, E, C>(
runtime.broadcast(event); runtime.broadcast(event);
} }
if !messages.is_empty() { if !messages.is_empty()
|| matches!(
interface_state,
user_interface::State::Outdated,
)
{
let cache = let cache =
ManuallyDrop::into_inner(user_interface).into_cache(); ManuallyDrop::into_inner(user_interface).into_cache();
@ -471,7 +476,7 @@ pub fn requests_exit(
/// [`struct@Debug`] information accordingly. /// [`struct@Debug`] information accordingly.
pub fn build_user_interface<'a, A: Application>( pub fn build_user_interface<'a, A: Application>(
application: &'a mut A, application: &'a mut A,
cache: Cache, cache: user_interface::Cache,
renderer: &mut A::Renderer, renderer: &mut A::Renderer,
size: Size, size: Size,
debug: &mut Debug, debug: &mut Debug,