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::futures;
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 std::mem::ManuallyDrop;
@ -177,7 +178,7 @@ async fn run_instance<A, E, C>(
let mut user_interface =
ManuallyDrop::new(application::build_user_interface(
&mut application,
Cache::default(),
user_interface::Cache::default(),
&mut renderer,
state.logical_size(),
&mut debug,
@ -198,7 +199,7 @@ async fn run_instance<A, E, C>(
debug.event_processing_started();
let statuses = user_interface.update(
let (interface_state, statuses) = user_interface.update(
&events,
state.cursor_position(),
&mut renderer,
@ -212,7 +213,12 @@ async fn run_instance<A, E, C>(
runtime.broadcast(event);
}
if !messages.is_empty() {
if !messages.is_empty()
|| matches!(
interface_state,
user_interface::State::Outdated
)
{
let 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() {
let mut component = self
@ -375,7 +375,7 @@ where
})
.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() {
let mut component =

View file

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

View file

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

View file

@ -8,6 +8,7 @@
pub struct Shell<'a, Message> {
messages: &'a mut Vec<Message>,
is_layout_invalid: bool,
are_widgets_invalid: bool,
}
impl<'a, Message> Shell<'a, Message> {
@ -16,12 +17,13 @@ impl<'a, Message> Shell<'a, Message> {
Self {
messages,
is_layout_invalid: false,
are_widgets_invalid: false,
}
}
/// Triggers the given function if the layout is invalid, cleaning it in the
/// 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 {
self.is_layout_invalid = false;
@ -41,6 +43,13 @@ impl<'a, Message> Shell<'a, Message> {
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
/// 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 || 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:
///
/// ```no_run
/// use iced_native::{UserInterface, Cache, Size};
/// use iced_native::Size;
/// use iced_native::user_interface::{self, UserInterface};
/// use iced_wgpu::Renderer;
///
/// # mod iced_wgpu {
@ -61,7 +62,7 @@ where
/// # }
/// // Initialization
/// let mut counter = Counter::new();
/// let mut cache = Cache::new();
/// let mut cache = user_interface::Cache::new();
/// let mut renderer = Renderer::new();
/// let mut window_size = Size::new(1024.0, 768.0);
///
@ -136,7 +137,8 @@ where
/// completing [the previous example](#example):
///
/// ```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;
///
/// # mod iced_wgpu {
@ -155,7 +157,7 @@ where
/// # pub fn update(&mut self, message: ()) {}
/// # }
/// let mut counter = Counter::new();
/// let mut cache = Cache::new();
/// let mut cache = user_interface::Cache::new();
/// let mut renderer = Renderer::new();
/// let mut window_size = Size::new(1024.0, 768.0);
/// let mut cursor_position = Point::default();
@ -176,7 +178,7 @@ where
/// );
///
/// // Update the user interface
/// let event_statuses = user_interface.update(
/// let (state, event_statuses) = user_interface.update(
/// &events,
/// cursor_position,
/// &mut renderer,
@ -199,7 +201,9 @@ where
renderer: &mut Renderer,
clipboard: &mut dyn Clipboard,
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) =
self.root.overlay(Layout::new(&self.base.layout))
{
@ -227,7 +231,7 @@ where
&mut shell,
);
shell.with_invalid_layout(|| {
shell.revalidate_layout(|| {
layer = Self::overlay_layer(
None,
bounds,
@ -236,6 +240,10 @@ where
);
});
if shell.are_widgets_invalid() {
state = State::Outdated;
}
event_status
})
.collect();
@ -255,7 +263,7 @@ where
(cursor_position, vec![event::Status::Ignored; events.len()])
};
events
let event_statuses = events
.iter()
.cloned()
.zip(overlay_statuses.into_iter())
@ -271,7 +279,7 @@ where
&mut shell,
);
shell.with_invalid_layout(|| {
shell.revalidate_layout(|| {
let hash = {
let hasher = &mut crate::Hasher::default();
self.root.hash_layout(hasher);
@ -288,9 +296,15 @@ where
self.overlay = None;
});
if shell.are_widgets_invalid() {
state = State::Outdated;
}
event_status.merge(overlay_status)
})
.collect()
.collect();
(state, event_statuses)
}
/// Draws the [`UserInterface`] with the provided [`Renderer`].
@ -306,7 +320,8 @@ where
/// [completing the last example](#example-1):
///
/// ```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;
///
/// # mod iced_wgpu {
@ -325,7 +340,7 @@ where
/// # pub fn update(&mut self, message: ()) {}
/// # }
/// let mut counter = Counter::new();
/// let mut cache = Cache::new();
/// let mut cache = user_interface::Cache::new();
/// let mut renderer = Renderer::new();
/// let mut window_size = Size::new(1024.0, 768.0);
/// let mut cursor_position = Point::default();
@ -548,3 +563,9 @@ impl Default for Cache {
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();
if state.last_size != Some(state.last_layout.size()) {
shell.invalidate_widgets();
}
let content = content.resolve(state, renderer);
let content_layout = Layout::with_offset(

View file

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