optimization: reduce unnecessary rebuilds in Component
This commit is contained in:
parent
d7fffaa801
commit
a3f6b782a1
1 changed files with 157 additions and 79 deletions
|
|
@ -13,6 +13,7 @@ use iced_native::{
|
||||||
use ouroboros::self_referencing;
|
use ouroboros::self_referencing;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
/// A reusable, custom widget that uses The Elm Architecture.
|
/// A reusable, custom widget that uses The Elm Architecture.
|
||||||
///
|
///
|
||||||
|
|
@ -58,6 +59,8 @@ pub trait Component<Message, Renderer> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Tag<T>(T);
|
||||||
|
|
||||||
/// Turns an implementor of [`Component`] into an [`Element`] that can be
|
/// Turns an implementor of [`Component`] into an [`Element`] that can be
|
||||||
/// embedded in any application.
|
/// embedded in any application.
|
||||||
pub fn view<'a, C, Message, Renderer>(
|
pub fn view<'a, C, Message, Renderer>(
|
||||||
|
|
@ -79,11 +82,13 @@ where
|
||||||
}
|
}
|
||||||
.build(),
|
.build(),
|
||||||
)),
|
)),
|
||||||
|
tree: RefCell::new(Rc::new(RefCell::new(None))),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Instance<'a, Message, Renderer, Event, S> {
|
struct Instance<'a, Message, Renderer, Event, S> {
|
||||||
state: RefCell<Option<State<'a, Message, Renderer, Event, S>>>,
|
state: RefCell<Option<State<'a, Message, Renderer, Event, S>>>,
|
||||||
|
tree: RefCell<Rc<RefCell<Option<Tree>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[self_referencing]
|
#[self_referencing]
|
||||||
|
|
@ -100,40 +105,91 @@ struct State<'a, Message: 'a, Renderer: 'a, Event: 'a, S: 'a> {
|
||||||
|
|
||||||
impl<'a, Message, Renderer, Event, S> Instance<'a, Message, Renderer, Event, S>
|
impl<'a, Message, Renderer, Event, S> Instance<'a, Message, Renderer, Event, S>
|
||||||
where
|
where
|
||||||
S: Default,
|
S: Default + 'static,
|
||||||
|
Renderer: iced_native::Renderer,
|
||||||
{
|
{
|
||||||
fn rebuild_element(&self, state: &S) {
|
fn diff_self(&self) {
|
||||||
let heads = self.state.borrow_mut().take().unwrap().into_heads();
|
self.with_element(|element| {
|
||||||
|
self.tree
|
||||||
|
.borrow_mut()
|
||||||
|
.borrow_mut()
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.diff_children(std::slice::from_ref(&element));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
*self.state.borrow_mut() = Some(
|
fn rebuild_element_if_necessary(&self) {
|
||||||
StateBuilder {
|
let inner = self.state.borrow_mut().take().unwrap();
|
||||||
component: heads.component,
|
if inner.borrow_element().is_none() {
|
||||||
message: PhantomData,
|
let heads = inner.into_heads();
|
||||||
state: PhantomData,
|
|
||||||
element_builder: |component| Some(component.view(state)),
|
*self.state.borrow_mut() = Some(
|
||||||
}
|
StateBuilder {
|
||||||
.build(),
|
component: heads.component,
|
||||||
);
|
message: PhantomData,
|
||||||
|
state: PhantomData,
|
||||||
|
element_builder: |component| {
|
||||||
|
Some(
|
||||||
|
component.view(
|
||||||
|
self.tree
|
||||||
|
.borrow()
|
||||||
|
.borrow()
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.state
|
||||||
|
.downcast_ref::<S>(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
self.diff_self();
|
||||||
|
} else {
|
||||||
|
*self.state.borrow_mut() = Some(inner);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rebuild_element_with_operation(
|
fn rebuild_element_with_operation(
|
||||||
&self,
|
&self,
|
||||||
state: &mut S,
|
|
||||||
operation: &mut dyn widget::Operation<Message>,
|
operation: &mut dyn widget::Operation<Message>,
|
||||||
) {
|
) {
|
||||||
let heads = self.state.borrow_mut().take().unwrap().into_heads();
|
let heads = self.state.borrow_mut().take().unwrap().into_heads();
|
||||||
|
|
||||||
heads.component.operate(state, operation);
|
heads.component.operate(
|
||||||
|
self.tree
|
||||||
|
.borrow_mut()
|
||||||
|
.borrow_mut()
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.state
|
||||||
|
.downcast_mut(),
|
||||||
|
operation,
|
||||||
|
);
|
||||||
|
|
||||||
*self.state.borrow_mut() = Some(
|
*self.state.borrow_mut() = Some(
|
||||||
StateBuilder {
|
StateBuilder {
|
||||||
component: heads.component,
|
component: heads.component,
|
||||||
message: PhantomData,
|
message: PhantomData,
|
||||||
state: PhantomData,
|
state: PhantomData,
|
||||||
element_builder: |component| Some(component.view(state)),
|
element_builder: |component| {
|
||||||
|
Some(
|
||||||
|
component.view(
|
||||||
|
self.tree
|
||||||
|
.borrow()
|
||||||
|
.borrow()
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.state
|
||||||
|
.downcast_ref(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
.build(),
|
.build(),
|
||||||
);
|
);
|
||||||
|
self.diff_self();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_element<T>(
|
fn with_element<T>(
|
||||||
|
|
@ -147,6 +203,7 @@ where
|
||||||
&self,
|
&self,
|
||||||
f: impl FnOnce(&mut Element<'_, Event, Renderer>) -> T,
|
f: impl FnOnce(&mut Element<'_, Event, Renderer>) -> T,
|
||||||
) -> T {
|
) -> T {
|
||||||
|
self.rebuild_element_if_necessary();
|
||||||
self.state
|
self.state
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.as_mut()
|
.as_mut()
|
||||||
|
|
@ -162,24 +219,27 @@ where
|
||||||
Renderer: iced_native::Renderer,
|
Renderer: iced_native::Renderer,
|
||||||
{
|
{
|
||||||
fn tag(&self) -> tree::Tag {
|
fn tag(&self) -> tree::Tag {
|
||||||
struct Tag<T>(T);
|
|
||||||
tree::Tag::of::<Tag<S>>()
|
tree::Tag::of::<Tag<S>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn state(&self) -> tree::State {
|
fn state(&self) -> tree::State {
|
||||||
tree::State::new(S::default())
|
let state = Rc::new(RefCell::new(Some(Tree {
|
||||||
|
tag: tree::Tag::of::<Tag<S>>(),
|
||||||
|
state: tree::State::new(S::default()),
|
||||||
|
children: vec![Tree::empty()],
|
||||||
|
})));
|
||||||
|
*self.tree.borrow_mut() = state.clone();
|
||||||
|
tree::State::new(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn children(&self) -> Vec<Tree> {
|
fn children(&self) -> Vec<Tree> {
|
||||||
self.rebuild_element(&S::default());
|
vec![]
|
||||||
self.with_element(|element| vec![Tree::new(element)])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn diff(&self, tree: &mut Tree) {
|
fn diff(&self, tree: &mut Tree) {
|
||||||
self.rebuild_element(tree.state.downcast_ref());
|
let tree = tree.state.downcast_ref::<Rc<RefCell<Option<Tree>>>>();
|
||||||
self.with_element(|element| {
|
*self.tree.borrow_mut() = tree.clone();
|
||||||
tree.diff_children(std::slice::from_ref(&element))
|
self.rebuild_element_if_necessary();
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn width(&self) -> Length {
|
fn width(&self) -> Length {
|
||||||
|
|
@ -213,9 +273,10 @@ where
|
||||||
let mut local_messages = Vec::new();
|
let mut local_messages = Vec::new();
|
||||||
let mut local_shell = Shell::new(&mut local_messages);
|
let mut local_shell = Shell::new(&mut local_messages);
|
||||||
|
|
||||||
|
let t = tree.state.downcast_mut::<Rc<RefCell<Option<Tree>>>>();
|
||||||
let event_status = self.with_element_mut(|element| {
|
let event_status = self.with_element_mut(|element| {
|
||||||
element.as_widget_mut().on_event(
|
element.as_widget_mut().on_event(
|
||||||
&mut tree.children[0],
|
&mut t.borrow_mut().as_mut().unwrap().children[0],
|
||||||
event,
|
event,
|
||||||
layout,
|
layout,
|
||||||
cursor_position,
|
cursor_position,
|
||||||
|
|
@ -235,9 +296,10 @@ where
|
||||||
let mut heads = self.state.take().unwrap().into_heads();
|
let mut heads = self.state.take().unwrap().into_heads();
|
||||||
|
|
||||||
for message in local_messages.into_iter().filter_map(|message| {
|
for message in local_messages.into_iter().filter_map(|message| {
|
||||||
heads
|
heads.component.update(
|
||||||
.component
|
t.borrow_mut().as_mut().unwrap().state.downcast_mut(),
|
||||||
.update(tree.state.downcast_mut::<S>(), message)
|
message,
|
||||||
|
)
|
||||||
}) {
|
}) {
|
||||||
shell.publish(message);
|
shell.publish(message);
|
||||||
}
|
}
|
||||||
|
|
@ -247,17 +309,11 @@ where
|
||||||
component: heads.component,
|
component: heads.component,
|
||||||
message: PhantomData,
|
message: PhantomData,
|
||||||
state: PhantomData,
|
state: PhantomData,
|
||||||
element_builder: |state| {
|
element_builder: |_| None,
|
||||||
Some(state.view(tree.state.downcast_ref::<S>()))
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
.build(),
|
.build(),
|
||||||
));
|
));
|
||||||
|
|
||||||
self.with_element(|element| {
|
|
||||||
tree.diff_children(std::slice::from_ref(&element))
|
|
||||||
});
|
|
||||||
|
|
||||||
shell.invalidate_layout();
|
shell.invalidate_layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -271,10 +327,7 @@ where
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
operation: &mut dyn widget::Operation<Message>,
|
operation: &mut dyn widget::Operation<Message>,
|
||||||
) {
|
) {
|
||||||
self.rebuild_element_with_operation(
|
self.rebuild_element_with_operation(operation);
|
||||||
tree.state.downcast_mut(),
|
|
||||||
operation,
|
|
||||||
);
|
|
||||||
|
|
||||||
struct MapOperation<'a, B> {
|
struct MapOperation<'a, B> {
|
||||||
operation: &'a mut dyn widget::Operation<B>,
|
operation: &'a mut dyn widget::Operation<B>,
|
||||||
|
|
@ -310,11 +363,10 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let tree = tree.state.downcast_mut::<Rc<RefCell<Option<Tree>>>>();
|
||||||
self.with_element(|element| {
|
self.with_element(|element| {
|
||||||
tree.diff_children(std::slice::from_ref(&element));
|
|
||||||
|
|
||||||
element.as_widget().operate(
|
element.as_widget().operate(
|
||||||
&mut tree.children[0],
|
&mut tree.borrow_mut().as_mut().unwrap().children[0],
|
||||||
layout,
|
layout,
|
||||||
renderer,
|
renderer,
|
||||||
&mut MapOperation { operation },
|
&mut MapOperation { operation },
|
||||||
|
|
@ -332,9 +384,10 @@ where
|
||||||
cursor_position: Point,
|
cursor_position: Point,
|
||||||
viewport: &Rectangle,
|
viewport: &Rectangle,
|
||||||
) {
|
) {
|
||||||
|
let tree = tree.state.downcast_ref::<Rc<RefCell<Option<Tree>>>>();
|
||||||
self.with_element(|element| {
|
self.with_element(|element| {
|
||||||
element.as_widget().draw(
|
element.as_widget().draw(
|
||||||
&tree.children[0],
|
&tree.borrow().as_ref().unwrap().children[0],
|
||||||
renderer,
|
renderer,
|
||||||
theme,
|
theme,
|
||||||
style,
|
style,
|
||||||
|
|
@ -353,9 +406,10 @@ where
|
||||||
viewport: &Rectangle,
|
viewport: &Rectangle,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
) -> mouse::Interaction {
|
) -> mouse::Interaction {
|
||||||
|
let tree = tree.state.downcast_ref::<Rc<RefCell<Option<Tree>>>>();
|
||||||
self.with_element(|element| {
|
self.with_element(|element| {
|
||||||
element.as_widget().mouse_interaction(
|
element.as_widget().mouse_interaction(
|
||||||
&tree.children[0],
|
&tree.borrow().as_ref().unwrap().children[0],
|
||||||
layout,
|
layout,
|
||||||
cursor_position,
|
cursor_position,
|
||||||
viewport,
|
viewport,
|
||||||
|
|
@ -370,25 +424,34 @@ where
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
) -> Option<overlay::Element<'b, Message, Renderer>> {
|
) -> Option<overlay::Element<'b, Message, Renderer>> {
|
||||||
let overlay = OverlayBuilder {
|
self.rebuild_element_if_necessary();
|
||||||
instance: self,
|
let tree = tree
|
||||||
tree,
|
.state
|
||||||
types: PhantomData,
|
.downcast_mut::<Rc<RefCell<Option<Tree>>>>()
|
||||||
overlay_builder: |instance, tree| {
|
.borrow_mut()
|
||||||
instance.state.get_mut().as_mut().unwrap().with_element_mut(
|
.take()
|
||||||
move |element| {
|
.unwrap();
|
||||||
element.as_mut().unwrap().as_widget_mut().overlay(
|
let overlay = Overlay(Some(
|
||||||
&mut tree.children[0],
|
InnerBuilder {
|
||||||
layout,
|
instance: self,
|
||||||
renderer,
|
tree,
|
||||||
)
|
types: PhantomData,
|
||||||
},
|
overlay_builder: |instance, tree| {
|
||||||
)
|
instance.state.get_mut().as_mut().unwrap().with_element_mut(
|
||||||
},
|
move |element| {
|
||||||
}
|
element.as_mut().unwrap().as_widget_mut().overlay(
|
||||||
.build();
|
&mut tree.children[0],
|
||||||
|
layout,
|
||||||
|
renderer,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
.build(),
|
||||||
|
));
|
||||||
|
|
||||||
let has_overlay = overlay.with_overlay(|overlay| {
|
let has_overlay = overlay.0.as_ref().unwrap().with_overlay(|overlay| {
|
||||||
overlay.as_ref().map(overlay::Element::position)
|
overlay.as_ref().map(overlay::Element::position)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -403,10 +466,24 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Overlay<'a, 'b, Message, Renderer, Event, S>(
|
||||||
|
Option<Inner<'a, 'b, Message, Renderer, Event, S>>,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl<'a, 'b, Message, Renderer, Event, S> Drop
|
||||||
|
for Overlay<'a, 'b, Message, Renderer, Event, S>
|
||||||
|
{
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Some(heads) = self.0.take().map(|inner| inner.into_heads()) {
|
||||||
|
*heads.instance.tree.borrow_mut().borrow_mut() = Some(heads.tree);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[self_referencing]
|
#[self_referencing]
|
||||||
struct Overlay<'a, 'b, Message, Renderer, Event, S> {
|
struct Inner<'a, 'b, Message, Renderer, Event, S> {
|
||||||
instance: &'a mut Instance<'b, Message, Renderer, Event, S>,
|
instance: &'a mut Instance<'b, Message, Renderer, Event, S>,
|
||||||
tree: &'a mut Tree,
|
tree: Tree,
|
||||||
types: PhantomData<(Message, Event, S)>,
|
types: PhantomData<(Message, Event, S)>,
|
||||||
|
|
||||||
#[borrows(mut instance, mut tree)]
|
#[borrows(mut instance, mut tree)]
|
||||||
|
|
@ -426,6 +503,9 @@ impl<'a, 'b, Message, Renderer, Event, S>
|
||||||
f: impl FnOnce(&overlay::Element<'_, Event, Renderer>) -> T,
|
f: impl FnOnce(&overlay::Element<'_, Event, Renderer>) -> T,
|
||||||
) -> Option<T> {
|
) -> Option<T> {
|
||||||
self.overlay
|
self.overlay
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.0
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.borrow_overlay()
|
.borrow_overlay()
|
||||||
|
|
@ -438,6 +518,9 @@ impl<'a, 'b, Message, Renderer, Event, S>
|
||||||
f: impl FnOnce(&mut overlay::Element<'_, Event, Renderer>) -> T,
|
f: impl FnOnce(&mut overlay::Element<'_, Event, Renderer>) -> T,
|
||||||
) -> Option<T> {
|
) -> Option<T> {
|
||||||
self.overlay
|
self.overlay
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.0
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.with_overlay_mut(|overlay| overlay.as_mut().map(f))
|
.with_overlay_mut(|overlay| overlay.as_mut().map(f))
|
||||||
|
|
@ -523,42 +606,37 @@ where
|
||||||
local_shell.revalidate_layout(|| shell.invalidate_layout());
|
local_shell.revalidate_layout(|| shell.invalidate_layout());
|
||||||
|
|
||||||
if !local_messages.is_empty() {
|
if !local_messages.is_empty() {
|
||||||
let overlay = self.overlay.take().unwrap().into_heads();
|
let mut inner =
|
||||||
let mut heads = overlay.instance.state.take().unwrap().into_heads();
|
self.overlay.take().unwrap().0.take().unwrap().into_heads();
|
||||||
|
let mut heads = inner.instance.state.take().unwrap().into_heads();
|
||||||
|
|
||||||
for message in local_messages.into_iter().filter_map(|message| {
|
for message in local_messages.into_iter().filter_map(|message| {
|
||||||
heads
|
heads
|
||||||
.component
|
.component
|
||||||
.update(overlay.tree.state.downcast_mut::<S>(), message)
|
.update(inner.tree.state.downcast_mut(), message)
|
||||||
}) {
|
}) {
|
||||||
shell.publish(message);
|
shell.publish(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
*overlay.instance.state.borrow_mut() = Some(
|
*inner.instance.state.borrow_mut() = Some(
|
||||||
StateBuilder {
|
StateBuilder {
|
||||||
component: heads.component,
|
component: heads.component,
|
||||||
message: PhantomData,
|
message: PhantomData,
|
||||||
state: PhantomData,
|
state: PhantomData,
|
||||||
element_builder: |state| {
|
element_builder: |_| None,
|
||||||
Some(state.view(overlay.tree.state.downcast_ref::<S>()))
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
.build(),
|
.build(),
|
||||||
);
|
);
|
||||||
|
|
||||||
overlay.instance.with_element(|element| {
|
self.overlay = Some(Overlay(Some(
|
||||||
overlay.tree.diff_children(std::slice::from_ref(&element))
|
InnerBuilder {
|
||||||
});
|
instance: inner.instance,
|
||||||
|
tree: inner.tree,
|
||||||
self.overlay = Some(
|
|
||||||
OverlayBuilder {
|
|
||||||
instance: overlay.instance,
|
|
||||||
tree: overlay.tree,
|
|
||||||
types: PhantomData,
|
types: PhantomData,
|
||||||
overlay_builder: |_, _| None,
|
overlay_builder: |_, _| None,
|
||||||
}
|
}
|
||||||
.build(),
|
.build(),
|
||||||
);
|
)));
|
||||||
|
|
||||||
shell.invalidate_layout();
|
shell.invalidate_layout();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue