Make Widget::diff mutable

This commit is contained in:
Héctor Ramón Jiménez 2025-08-20 23:14:23 +02:00
parent 31bc6d48cd
commit 497ebcd0c3
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
31 changed files with 114 additions and 81 deletions

View file

@ -9,7 +9,7 @@ use crate::{
Vector, Widget, Vector, Widget,
}; };
use std::borrow::Borrow; use std::borrow::{Borrow, BorrowMut};
/// A generic [`Widget`]. /// A generic [`Widget`].
/// ///
@ -239,6 +239,37 @@ impl<'a, Message, Theme, Renderer>
} }
} }
impl<'a, Message, Theme, Renderer>
Borrow<dyn Widget<Message, Theme, Renderer> + 'a>
for &mut Element<'a, Message, Theme, Renderer>
{
fn borrow(&self) -> &(dyn Widget<Message, Theme, Renderer> + 'a) {
self.widget.borrow()
}
}
impl<'a, Message, Theme, Renderer>
BorrowMut<dyn Widget<Message, Theme, Renderer> + 'a>
for Element<'a, Message, Theme, Renderer>
{
fn borrow_mut(
&mut self,
) -> &mut (dyn Widget<Message, Theme, Renderer> + 'a) {
self.widget.borrow_mut()
}
}
impl<'a, Message, Theme, Renderer>
BorrowMut<dyn Widget<Message, Theme, Renderer> + 'a>
for &mut Element<'a, Message, Theme, Renderer>
{
fn borrow_mut(
&mut self,
) -> &mut (dyn Widget<Message, Theme, Renderer> + 'a) {
self.widget.borrow_mut()
}
}
struct Map<'a, A, B, Theme, Renderer> { struct Map<'a, A, B, Theme, Renderer> {
widget: Box<dyn Widget<A, Theme, Renderer> + 'a>, widget: Box<dyn Widget<A, Theme, Renderer> + 'a>,
mapper: Box<dyn Fn(A) -> B + 'a>, mapper: Box<dyn Fn(A) -> B + 'a>,
@ -278,7 +309,7 @@ where
self.widget.children() self.widget.children()
} }
fn diff(&self, tree: &mut Tree) { fn diff(&mut self, tree: &mut Tree) {
self.widget.diff(tree); self.widget.diff(tree);
} }
@ -421,7 +452,7 @@ where
self.element.widget.children() self.element.widget.children()
} }
fn diff(&self, tree: &mut Tree) { fn diff(&mut self, tree: &mut Tree) {
self.element.widget.diff(tree); self.element.widget.diff(tree);
} }

View file

@ -96,7 +96,7 @@ where
} }
/// Reconciles the [`Widget`] with the provided [`Tree`]. /// Reconciles the [`Widget`] with the provided [`Tree`].
fn diff(&self, tree: &mut Tree) { fn diff(&mut self, tree: &mut Tree) {
tree.children.clear(); tree.children.clear();
} }

View file

@ -2,7 +2,7 @@
use crate::Widget; use crate::Widget;
use std::any::{self, Any}; use std::any::{self, Any};
use std::borrow::Borrow; use std::borrow::{Borrow, BorrowMut};
use std::fmt; use std::fmt;
/// A persistent state widget tree. /// A persistent state widget tree.
@ -56,12 +56,12 @@ impl Tree {
/// [`Widget::diff`]: crate::Widget::diff /// [`Widget::diff`]: crate::Widget::diff
pub fn diff<'a, Message, Theme, Renderer>( pub fn diff<'a, Message, Theme, Renderer>(
&mut self, &mut self,
new: impl Borrow<dyn Widget<Message, Theme, Renderer> + 'a>, mut new: impl BorrowMut<dyn Widget<Message, Theme, Renderer> + 'a>,
) where ) where
Renderer: crate::Renderer, Renderer: crate::Renderer,
{ {
if self.tag == new.borrow().tag() { if self.tag == new.borrow_mut().tag() {
new.borrow().diff(self); new.borrow_mut().diff(self);
} else { } else {
*self = Self::new(new); *self = Self::new(new);
} }
@ -70,13 +70,15 @@ impl Tree {
/// Reconciles the children of the tree with the provided list of widgets. /// Reconciles the children of the tree with the provided list of widgets.
pub fn diff_children<'a, Message, Theme, Renderer>( pub fn diff_children<'a, Message, Theme, Renderer>(
&mut self, &mut self,
new_children: &[impl Borrow<dyn Widget<Message, Theme, Renderer> + 'a>], new_children: &mut [impl BorrowMut<
dyn Widget<Message, Theme, Renderer> + 'a,
>],
) where ) where
Renderer: crate::Renderer, Renderer: crate::Renderer,
{ {
self.diff_children_custom( self.diff_children_custom(
new_children, new_children,
|tree, widget| tree.diff(widget.borrow()), |tree, widget| tree.diff(widget.borrow_mut()),
|widget| Self::new(widget.borrow()), |widget| Self::new(widget.borrow()),
); );
} }
@ -85,8 +87,8 @@ impl Tree {
/// logic both for diffing and creating new widget state. /// logic both for diffing and creating new widget state.
pub fn diff_children_custom<T>( pub fn diff_children_custom<T>(
&mut self, &mut self,
new_children: &[T], new_children: &mut [T],
diff: impl Fn(&mut Tree, &T), diff: impl Fn(&mut Tree, &mut T),
new_state: impl Fn(&T) -> Self, new_state: impl Fn(&T) -> Self,
) { ) {
if self.children.len() > new_children.len() { if self.children.len() > new_children.len() {
@ -94,7 +96,7 @@ impl Tree {
} }
for (child_state, new) in for (child_state, new) in
self.children.iter_mut().zip(new_children.iter()) self.children.iter_mut().zip(new_children.iter_mut())
{ {
diff(child_state, new); diff(child_state, new);
} }
@ -114,8 +116,8 @@ impl Tree {
/// `maybe_changed` closure. /// `maybe_changed` closure.
pub fn diff_children_custom_with_search<T>( pub fn diff_children_custom_with_search<T>(
current_children: &mut Vec<Tree>, current_children: &mut Vec<Tree>,
new_children: &[T], new_children: &mut [T],
diff: impl Fn(&mut Tree, &T), diff: impl Fn(&mut Tree, &mut T),
maybe_changed: impl Fn(usize) -> bool, maybe_changed: impl Fn(usize) -> bool,
new_state: impl Fn(&T) -> Tree, new_state: impl Fn(&T) -> Tree,
) { ) {
@ -183,7 +185,7 @@ pub fn diff_children_custom_with_search<T>(
// TODO: Merge loop with extend logic (?) // TODO: Merge loop with extend logic (?)
for (child_state, new) in for (child_state, new) in
current_children.iter_mut().zip(new_children.iter()) current_children.iter_mut().zip(new_children.iter_mut())
{ {
diff(child_state, new); diff(child_state, new);
} }

View file

@ -87,8 +87,8 @@ mod loupe {
self.content.as_widget().children() self.content.as_widget().children()
} }
fn diff(&self, tree: &mut widget::Tree) { fn diff(&mut self, tree: &mut widget::Tree) {
self.content.as_widget().diff(tree); self.content.as_widget_mut().diff(tree);
} }
fn size(&self) -> Size<Length> { fn size(&self) -> Size<Length> {

View file

@ -314,7 +314,7 @@ mod toast {
.collect() .collect()
} }
fn diff(&self, tree: &mut Tree) { fn diff(&mut self, tree: &mut Tree) {
let instants = tree.state.downcast_mut::<Vec<Option<Instant>>>(); let instants = tree.state.downcast_mut::<Vec<Option<Instant>>>();
// Invalidating removed instants to None allows us to remove // Invalidating removed instants to None allows us to remove
@ -336,8 +336,8 @@ mod toast {
} }
tree.diff_children( tree.diff_children(
&std::iter::once(&self.content) &mut std::iter::once(&mut self.content)
.chain(self.toasts.iter()) .chain(self.toasts.iter_mut())
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
); );
} }

View file

@ -100,7 +100,7 @@ where
let mut root = root.into(); let mut root = root.into();
let Cache { mut state } = cache; let Cache { mut state } = cache;
state.diff(root.as_widget()); state.diff(root.as_widget_mut());
let base = root.as_widget_mut().layout( let base = root.as_widget_mut().layout(
&mut state, &mut state,

View file

@ -223,8 +223,8 @@ where
vec![Tree::new(&self.content)] vec![Tree::new(&self.content)]
} }
fn diff(&self, tree: &mut Tree) { fn diff(&mut self, tree: &mut Tree) {
tree.diff_children(std::slice::from_ref(&self.content)); tree.diff_children(std::slice::from_mut(&mut self.content));
} }
fn size(&self) -> Size<Length> { fn size(&self) -> Size<Length> {

View file

@ -194,8 +194,8 @@ where
self.children.iter().map(Tree::new).collect() self.children.iter().map(Tree::new).collect()
} }
fn diff(&self, tree: &mut Tree) { fn diff(&mut self, tree: &mut Tree) {
tree.diff_children(&self.children); tree.diff_children(&mut self.children);
} }
fn size(&self) -> Size<Length> { fn size(&self) -> Size<Length> {

View file

@ -509,7 +509,7 @@ where
vec![widget::Tree::new(&self.text_input as &dyn Widget<_, _, _>)] vec![widget::Tree::new(&self.text_input as &dyn Widget<_, _, _>)]
} }
fn diff(&self, _tree: &mut widget::Tree) { fn diff(&mut self, _tree: &mut widget::Tree) {
// do nothing so the children don't get cleared // do nothing so the children don't get cleared
} }

View file

@ -247,8 +247,8 @@ where
self.content.as_widget().children() self.content.as_widget().children()
} }
fn diff(&self, tree: &mut Tree) { fn diff(&mut self, tree: &mut Tree) {
self.content.as_widget().diff(tree); self.content.as_widget_mut().diff(tree);
} }
fn size(&self) -> Size<Length> { fn size(&self) -> Size<Length> {

View file

@ -103,8 +103,8 @@ where
self.content.as_widget().children() self.content.as_widget().children()
} }
fn diff(&self, tree: &mut widget::Tree) { fn diff(&mut self, tree: &mut widget::Tree) {
self.content.as_widget().diff(tree); self.content.as_widget_mut().diff(tree);
} }
fn size(&self) -> Size<Length> { fn size(&self) -> Size<Length> {

View file

@ -158,8 +158,8 @@ where
self.children.iter().map(Tree::new).collect() self.children.iter().map(Tree::new).collect()
} }
fn diff(&self, tree: &mut Tree) { fn diff(&mut self, tree: &mut Tree) {
tree.diff_children(&self.children); tree.diff_children(&mut self.children);
} }
fn size(&self) -> Size<Length> { fn size(&self) -> Size<Length> {

View file

@ -596,8 +596,8 @@ where
self.content.as_widget().children() self.content.as_widget().children()
} }
fn diff(&self, tree: &mut Tree) { fn diff(&mut self, tree: &mut Tree) {
self.content.as_widget().diff(tree); self.content.as_widget_mut().diff(tree);
} }
fn size(&self) -> Size<Length> { fn size(&self) -> Size<Length> {
@ -759,8 +759,8 @@ where
vec![Tree::new(&self.base), Tree::new(&self.top)] vec![Tree::new(&self.base), Tree::new(&self.top)]
} }
fn diff(&self, tree: &mut Tree) { fn diff(&mut self, tree: &mut Tree) {
tree.diff_children(&[&self.base, &self.top]); tree.diff_children(&mut [&mut self.base, &mut self.top]);
} }
fn size(&self) -> Size<Length> { fn size(&self) -> Size<Length> {

View file

@ -222,7 +222,7 @@ where
self.children.iter().map(Tree::new).collect() self.children.iter().map(Tree::new).collect()
} }
fn diff(&self, tree: &mut Tree) { fn diff(&mut self, tree: &mut Tree) {
let Tree { let Tree {
state, children, .. state, children, ..
} = tree; } = tree;
@ -231,8 +231,8 @@ where
tree::diff_children_custom_with_search( tree::diff_children_custom_with_search(
children, children,
&self.children, &mut self.children,
|tree, child| child.as_widget().diff(tree), |tree, child| child.as_widget_mut().diff(tree),
|index| { |index| {
self.keys.get(index).or_else(|| self.keys.last()).copied() self.keys.get(index).or_else(|| self.keys.last()).copied()
!= Some(state.keys[index]) != Some(state.keys[index])

View file

@ -127,7 +127,7 @@ where
self.with_element(|element| vec![Tree::new(element.as_widget())]) self.with_element(|element| vec![Tree::new(element.as_widget())])
} }
fn diff(&self, tree: &mut Tree) { fn diff(&mut self, tree: &mut Tree) {
let current = tree let current = tree
.state .state
.downcast_mut::<Internal<Message, Theme, Renderer>>(); .downcast_mut::<Internal<Message, Theme, Renderer>>();
@ -146,8 +146,8 @@ where
current.element = Rc::new(RefCell::new(Some(element))); current.element = Rc::new(RefCell::new(Some(element)));
(*self.element.borrow_mut()) = Some(current.element.clone()); (*self.element.borrow_mut()) = Some(current.element.clone());
self.with_element(|element| { self.with_element_mut(|element| {
tree.diff_children(std::slice::from_ref(&element.as_widget())); tree.diff_children(std::slice::from_mut(element));
}); });
} else { } else {
(*self.element.borrow_mut()) = Some(current.element.clone()); (*self.element.borrow_mut()) = Some(current.element.clone());

View file

@ -147,13 +147,13 @@ where
Renderer: renderer::Renderer, Renderer: renderer::Renderer,
{ {
fn diff_self(&self) { fn diff_self(&self) {
self.with_element(|element| { self.with_element_mut(|element| {
self.tree self.tree
.borrow_mut() .borrow_mut()
.borrow_mut() .borrow_mut()
.as_mut() .as_mut()
.unwrap() .unwrap()
.diff_children(std::slice::from_ref(&element)); .diff_children(std::slice::from_mut(element));
}); });
} }
@ -279,7 +279,7 @@ where
vec![] vec![]
} }
fn diff(&self, tree: &mut Tree) { fn diff(&mut self, tree: &mut Tree) {
let tree = tree.state.downcast_ref::<Rc<RefCell<Option<Tree>>>>(); let tree = tree.state.downcast_ref::<Rc<RefCell<Option<Tree>>>>();
*self.tree.borrow_mut() = tree.clone(); *self.tree.borrow_mut() = tree.clone();
self.rebuild_element_if_necessary(); self.rebuild_element_if_necessary();

View file

@ -82,7 +82,7 @@ where
let size = limits.max(); let size = limits.max();
self.content = (self.view)(size); self.content = (self.view)(size);
state.tree.diff(&self.content); state.tree.diff(&mut self.content);
self.content.as_widget_mut().layout( self.content.as_widget_mut().layout(
&mut state.tree, &mut state.tree,

View file

@ -181,8 +181,8 @@ where
vec![Tree::new(&self.content)] vec![Tree::new(&self.content)]
} }
fn diff(&self, tree: &mut Tree) { fn diff(&mut self, tree: &mut Tree) {
tree.diff_children(std::slice::from_ref(&self.content)); tree.diff_children(std::slice::from_mut(&mut self.content));
} }
fn size(&self) -> Size<Length> { fn size(&self) -> Size<Length> {

View file

@ -205,7 +205,7 @@ where
class, class,
} = menu; } = menu;
let list = Scrollable::new(List { let mut list = Scrollable::new(List {
options, options,
hovered_option, hovered_option,
on_selected, on_selected,
@ -218,7 +218,7 @@ where
class, class,
}); });
state.tree.diff(&list as &dyn Widget<_, _, _>); state.tree.diff(&mut list as &mut dyn Widget<_, _, _>);
Self { Self {
position, position,

View file

@ -378,7 +378,7 @@ where
self.contents.iter().map(Content::state).collect() self.contents.iter().map(Content::state).collect()
} }
fn diff(&self, tree: &mut Tree) { fn diff(&mut self, tree: &mut Tree) {
let Memory { order, .. } = tree.state.downcast_ref(); let Memory { order, .. } = tree.state.downcast_ref();
// `Pane` always increments and is iterated by Ord so new // `Pane` always increments and is iterated by Ord so new
@ -401,7 +401,7 @@ where
}); });
tree.diff_children_custom( tree.diff_children_custom(
&self.contents, &mut self.contents,
|state, content| content.diff(state), |state, content| content.diff(state),
Content::state, Content::state,
); );

View file

@ -91,13 +91,13 @@ where
} }
} }
pub(super) fn diff(&self, tree: &mut Tree) { pub(super) fn diff(&mut self, tree: &mut Tree) {
if tree.children.len() == 2 { if tree.children.len() == 2 {
if let Some(title_bar) = self.title_bar.as_ref() { if let Some(title_bar) = self.title_bar.as_mut() {
title_bar.diff(&mut tree.children[1]); title_bar.diff(&mut tree.children[1]);
} }
tree.children[0].diff(&self.body); tree.children[0].diff(&mut self.body);
} else { } else {
*tree = self.state(); *tree = self.state();
} }

View file

@ -128,17 +128,17 @@ where
} }
} }
pub(super) fn diff(&self, tree: &mut Tree) { pub(super) fn diff(&mut self, tree: &mut Tree) {
if tree.children.len() == 3 { if tree.children.len() == 3 {
if let Some(controls) = self.controls.as_ref() { if let Some(controls) = self.controls.as_mut() {
if let Some(compact) = controls.compact.as_ref() { if let Some(compact) = controls.compact.as_mut() {
tree.children[2].diff(compact); tree.children[2].diff(compact);
} }
tree.children[1].diff(&controls.full); tree.children[1].diff(&mut controls.full);
} }
tree.children[0].diff(&self.content); tree.children[0].diff(&mut self.content);
} else { } else {
*tree = self.state(); *tree = self.state();
} }

View file

@ -127,8 +127,8 @@ where
self.content.as_widget().children() self.content.as_widget().children()
} }
fn diff(&self, tree: &mut widget::Tree) { fn diff(&mut self, tree: &mut widget::Tree) {
self.content.as_widget().diff(tree); self.content.as_widget_mut().diff(tree);
} }
fn size(&self) -> Size<Length> { fn size(&self) -> Size<Length> {

View file

@ -196,8 +196,8 @@ where
self.children.iter().map(Tree::new).collect() self.children.iter().map(Tree::new).collect()
} }
fn diff(&self, tree: &mut Tree) { fn diff(&mut self, tree: &mut Tree) {
tree.diff_children(&self.children); tree.diff_children(&mut self.children);
} }
fn size(&self) -> Size<Length> { fn size(&self) -> Size<Length> {
@ -398,7 +398,7 @@ where
self.row.children() self.row.children()
} }
fn diff(&self, tree: &mut Tree) { fn diff(&mut self, tree: &mut Tree) {
self.row.diff(tree); self.row.diff(tree);
} }

View file

@ -409,8 +409,8 @@ where
vec![Tree::new(&self.content)] vec![Tree::new(&self.content)]
} }
fn diff(&self, tree: &mut Tree) { fn diff(&mut self, tree: &mut Tree) {
tree.diff_children(std::slice::from_ref(&self.content)); tree.diff_children(std::slice::from_mut(&mut self.content));
} }
fn size(&self) -> Size<Length> { fn size(&self) -> Size<Length> {

View file

@ -184,8 +184,8 @@ where
vec![Tree::new(&self.content)] vec![Tree::new(&self.content)]
} }
fn diff(&self, tree: &mut Tree) { fn diff(&mut self, tree: &mut Tree) {
tree.diff_children(&[&self.content]); tree.diff_children(std::slice::from_mut(&mut self.content));
} }
fn update( fn update(

View file

@ -146,8 +146,8 @@ where
self.children.iter().map(Tree::new).collect() self.children.iter().map(Tree::new).collect()
} }
fn diff(&self, tree: &mut Tree) { fn diff(&mut self, tree: &mut Tree) {
tree.diff_children(&self.children); tree.diff_children(&mut self.children);
} }
fn size(&self) -> Size<Length> { fn size(&self) -> Size<Length> {

View file

@ -227,8 +227,8 @@ where
.collect() .collect()
} }
fn diff(&self, state: &mut widget::Tree) { fn diff(&mut self, state: &mut widget::Tree) {
state.diff_children(&self.cells); state.diff_children(&mut self.cells);
} }
fn layout( fn layout(

View file

@ -654,7 +654,7 @@ where
tree::State::new(State::<Renderer::Paragraph>::new()) tree::State::new(State::<Renderer::Paragraph>::new())
} }
fn diff(&self, tree: &mut Tree) { fn diff(&mut self, tree: &mut Tree) {
let state = tree.state.downcast_mut::<State<Renderer::Paragraph>>(); let state = tree.state.downcast_mut::<State<Renderer::Paragraph>>();
// Stop pasting if input becomes disabled // Stop pasting if input becomes disabled

View file

@ -81,8 +81,8 @@ where
self.content.as_widget().children() self.content.as_widget().children()
} }
fn diff(&self, tree: &mut Tree) { fn diff(&mut self, tree: &mut Tree) {
self.content.as_widget().diff(tree); self.content.as_widget_mut().diff(tree);
} }
fn size(&self) -> Size<Length> { fn size(&self) -> Size<Length> {

View file

@ -155,10 +155,10 @@ where
] ]
} }
fn diff(&self, tree: &mut widget::Tree) { fn diff(&mut self, tree: &mut widget::Tree) {
tree.diff_children(&[ tree.diff_children(&mut [
self.content.as_widget(), self.content.as_widget_mut(),
self.tooltip.as_widget(), self.tooltip.as_widget_mut(),
]); ]);
} }