Skip to main content

ferroui_view/
element.rs

1//! The element tree: a description of controls, their properties, handlers
2//! and children, and how it becomes controls ([`Element::mount`]).
3//!
4//! An element is data: the class to create, a list of property values (the
5//! property and its value, compared by value), handlers, children and a key.
6//! Mounting creates the controls once; what changes afterwards changes through
7//! the reactive values the element holds (a property set from a signal or a
8//! closure, a list of children that follows a signal). Because an element is
9//! data, a later reconciler can compare two trees of elements and patch the
10//! controls of the first into the second; nothing here assumes that the tree
11//! is mounted only once.
12
13use crate::reactive::{effect, on_cleanup, untrack, Owner};
14use ferroui_base::data::BindingBase;
15use ferroui_base::reactive::IDisposable;
16use ferroui_base::{BoxedValue, FerroProperty, Ref};
17use ferroui_base::data::BindingPriority;
18use ferroui_controls::presenters::ContentPresenter;
19use ferroui_controls::{Control, ContentControl, Decorator, ItemsControl, Panel, TextBlock};
20use std::any::Any;
21use std::cell::RefCell;
22use std::collections::HashMap;
23use std::fmt;
24use std::hash::Hash;
25use std::rc::Rc;
26
27/// The key of an element among its siblings: an element of a dynamic list
28/// keeps its control while its key stays.
29#[derive(Clone, PartialEq, Eq, Hash, Debug)]
30pub enum Key {
31    /// A number.
32    Int(i64),
33    /// A string.
34    Str(Rc<str>),
35}
36
37impl From<i64> for Key {
38    fn from(value: i64) -> Self {
39        Key::Int(value)
40    }
41}
42
43impl From<i32> for Key {
44    fn from(value: i32) -> Self {
45        Key::Int(i64::from(value))
46    }
47}
48
49impl From<u32> for Key {
50    fn from(value: u32) -> Self {
51        Key::Int(i64::from(value))
52    }
53}
54
55impl From<usize> for Key {
56    fn from(value: usize) -> Self {
57        Key::Int(value as i64)
58    }
59}
60
61impl From<&str> for Key {
62    fn from(value: &str) -> Self {
63        Key::Str(Rc::from(value))
64    }
65}
66
67impl From<String> for Key {
68    fn from(value: String) -> Self {
69        Key::Str(Rc::from(value))
70    }
71}
72
73/// The value an element gives a property.
74#[derive(Clone)]
75pub enum PropValue {
76    /// A value, set once. Values compare by value.
77    Value(BoxedValue),
78    /// A value computed by a reactive closure: the property is set again
79    /// whenever what the closure read changes.
80    Dynamic(Rc<dyn Fn() -> BoxedValue>),
81    /// A value that goes both ways: the property follows `get`, and a change
82    /// of the property is given to `set`.
83    TwoWay {
84        /// The value of the source, read reactively.
85        get: Rc<dyn Fn() -> BoxedValue>,
86        /// Takes a new value of the property.
87        set: Rc<dyn Fn(&dyn Any)>,
88    },
89    /// An element, mounted with its parent: the property holds its control
90    /// (`Content`, `Tag`, a header).
91    Element(Rc<RefCell<Option<Element>>>),
92    /// A binding of the property system (`Binding`, a path on the data
93    /// context, a compiled binding).
94    Binding(Rc<dyn BindingBase>),
95}
96
97impl PartialEq for PropValue {
98    /// Values compare by value; anything else is never equal (a reconciler
99    /// sets it again).
100    fn eq(&self, other: &Self) -> bool {
101        match (self, other) {
102            (PropValue::Value(a), PropValue::Value(b)) => a == b,
103            _ => false,
104        }
105    }
106}
107
108impl fmt::Debug for PropValue {
109    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110        match self {
111            PropValue::Value(_) => f.write_str("Value"),
112            PropValue::Dynamic(_) => f.write_str("Dynamic"),
113            PropValue::TwoWay { .. } => f.write_str("TwoWay"),
114            PropValue::Element(_) => f.write_str("Element"),
115            PropValue::Binding(_) => f.write_str("Binding"),
116        }
117    }
118}
119
120/// A property and the value an element gives it.
121#[derive(Clone, Debug, PartialEq)]
122pub struct PropEntry {
123    /// The property.
124    pub property: &'static FerroProperty,
125    /// Its value.
126    pub value: PropValue,
127}
128
129/// What attaches a handler (a routed event, a change of a property) to the
130/// control of an element.
131pub type Attach = Rc<dyn Fn(&Ref<Control>)>;
132
133/// The class of an element: how its control is made.
134#[derive(Clone, Copy)]
135pub struct ElementClass {
136    /// The name of the class, for messages.
137    pub name: &'static str,
138    /// The identity of the class.
139    pub type_id: fn() -> std::any::TypeId,
140    /// Creates a control of the class.
141    pub create: fn() -> Ref<Control>,
142}
143
144impl PartialEq for ElementClass {
145    fn eq(&self, other: &Self) -> bool {
146        (self.type_id)() == (other.type_id)()
147    }
148}
149
150impl fmt::Debug for ElementClass {
151    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152        f.write_str(self.name)
153    }
154}
155
156/// An element of a control class.
157#[derive(Clone)]
158pub struct ControlElement {
159    /// The class of the control.
160    pub class: ElementClass,
161    /// The key among its siblings.
162    pub key: Option<Key>,
163    /// `Name` of the control.
164    pub name: Option<String>,
165    /// Style classes of the control.
166    pub classes: Vec<String>,
167    /// Values of properties, in the order they were given.
168    pub props: Vec<PropEntry>,
169    /// Handlers.
170    pub handlers: Vec<Attach>,
171    /// Children, placed into the content of the control (see
172    /// [`Element::mount`]).
173    pub children: Vec<Element>,
174    /// Called with the control once it was made and given its values.
175    pub on_mount: Vec<Attach>,
176}
177
178impl fmt::Debug for ControlElement {
179    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180        f.debug_struct("ControlElement")
181            .field("class", &self.class)
182            .field("key", &self.key)
183            .field("name", &self.name)
184            .field("classes", &self.classes)
185            .field("props", &self.props)
186            .field("children", &self.children)
187            .finish()
188    }
189}
190
191impl ControlElement {
192    /// An element of `class` with nothing set.
193    pub fn new(class: ElementClass) -> Self {
194        Self {
195            class,
196            key: None,
197            name: None,
198            classes: Vec::new(),
199            props: Vec::new(),
200            handlers: Vec::new(),
201            children: Vec::new(),
202            on_mount: Vec::new(),
203        }
204    }
205}
206
207/// A list of children that follows reactive state: the closure is run in an
208/// effect, and its elements replace the ones it gave before; elements with a
209/// key that stays keep their control (see [`for_each`](crate::for_each)).
210#[derive(Clone)]
211pub struct DynamicChildren {
212    pub(crate) render: Rc<dyn Fn() -> Vec<(Option<Key>, Option<Rc<dyn AnyValueEq>>, Rc<dyn Fn() -> Element>)>>,
213}
214
215/// A value an item of a dynamic list is compared by, to decide whether its
216/// element is made again.
217pub trait AnyValueEq: Any {
218    /// Whether `other` holds an equal value.
219    fn item_equals(&self, other: &dyn AnyValueEq) -> bool;
220    /// The value as `Any`.
221    fn item_as_any(&self) -> &dyn Any;
222}
223
224impl<T: PartialEq + 'static> AnyValueEq for T {
225    fn item_equals(&self, other: &dyn AnyValueEq) -> bool {
226        other.item_as_any().downcast_ref::<T>().is_some_and(|other| other == self)
227    }
228
229    fn item_as_any(&self) -> &dyn Any {
230        self
231    }
232}
233
234/// A description of user interface: a control with its properties and
235/// children, text, several elements, a list that follows state, or a control
236/// that exists already.
237#[derive(Clone, Default)]
238pub enum Element {
239    /// Nothing.
240    #[default]
241    Empty,
242    /// A control.
243    Control(Box<ControlElement>),
244    /// Text: the text of the content of its parent, or a `TextBlock` where a
245    /// control is needed.
246    Text(Rc<str>),
247    /// Several elements in place of one.
248    Fragment(Vec<Element>),
249    /// Children that follow reactive state.
250    Dynamic(DynamicChildren),
251    /// A control made elsewhere (a XAML user control, a control kept by the
252    /// application).
253    Existing(Ref<Control>),
254}
255
256impl fmt::Debug for Element {
257    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
258        match self {
259            Element::Empty => f.write_str("Empty"),
260            Element::Control(control) => control.fmt(f),
261            Element::Text(text) => write!(f, "Text({text:?})"),
262            Element::Fragment(items) => f.debug_tuple("Fragment").field(items).finish(),
263            Element::Dynamic(_) => f.write_str("Dynamic"),
264            Element::Existing(_) => f.write_str("Existing"),
265        }
266    }
267}
268
269/// What turns into an element: an element, a builder of one, a control.
270pub trait IntoElement {
271    /// The element.
272    fn into_element(self) -> Element;
273}
274
275impl IntoElement for Element {
276    fn into_element(self) -> Element {
277        self
278    }
279}
280
281impl<T: ferroui_base::ObjectType + ferroui_base::Upcast<Control>> IntoElement for Ref<T> {
282    fn into_element(self) -> Element {
283        Element::Existing(self.upcast())
284    }
285}
286
287impl<T: IntoElement> IntoElement for Option<T> {
288    fn into_element(self) -> Element {
289        self.map_or(Element::Empty, IntoElement::into_element)
290    }
291}
292
293impl<T: IntoElement> IntoElement for Vec<T> {
294    fn into_element(self) -> Element {
295        Element::Fragment(self.into_iter().map(IntoElement::into_element).collect())
296    }
297}
298
299/// What can be a child of an element: an element, a builder, text, a list of
300/// them.
301pub trait IntoChild {
302    /// The child as an element.
303    fn into_child(self) -> Element;
304}
305
306impl<T: IntoElement> IntoChild for T {
307    fn into_child(self) -> Element {
308        self.into_element()
309    }
310}
311
312impl IntoChild for &str {
313    fn into_child(self) -> Element {
314        Element::Text(Rc::from(self))
315    }
316}
317
318impl IntoChild for String {
319    fn into_child(self) -> Element {
320        Element::Text(Rc::from(self))
321    }
322}
323
324/// What can be several children: a vector, an iterator collected, a tuple.
325pub trait IntoChildren {
326    /// The children.
327    fn into_children(self) -> Vec<Element>;
328}
329
330impl<T: IntoChild> IntoChildren for Vec<T> {
331    fn into_children(self) -> Vec<Element> {
332        self.into_iter().map(IntoChild::into_child).collect()
333    }
334}
335
336impl<T: IntoChild, const N: usize> IntoChildren for [T; N] {
337    fn into_children(self) -> Vec<Element> {
338        self.into_iter().map(IntoChild::into_child).collect()
339    }
340}
341
342macro_rules! tuple_children {
343    ($($name:ident),+) => {
344        impl<$($name: IntoChild),+> IntoChildren for ($($name,)+) {
345            #[allow(non_snake_case)]
346            fn into_children(self) -> Vec<Element> {
347                let ($($name,)+) = self;
348                vec![$($name.into_child()),+]
349            }
350        }
351    };
352}
353
354tuple_children!(A);
355tuple_children!(A, B);
356tuple_children!(A, B, C);
357tuple_children!(A, B, C, D);
358tuple_children!(A, B, C, D, E);
359tuple_children!(A, B, C, D, E, F);
360tuple_children!(A, B, C, D, E, F, G);
361tuple_children!(A, B, C, D, E, F, G, H);
362tuple_children!(A, B, C, D, E, F, G, H, I);
363tuple_children!(A, B, C, D, E, F, G, H, I, J);
364tuple_children!(A, B, C, D, E, F, G, H, I, J, K);
365tuple_children!(A, B, C, D, E, F, G, H, I, J, K, L);
366
367/// What holds the children of a control.
368enum Host {
369    /// `Panel.Children`.
370    Panel(Ref<Panel>),
371    /// `ItemsControl.Items`.
372    Items(Ref<ItemsControl>),
373    /// One child: `ContentControl.Content`, `ContentPresenter.Content`.
374    Content(Ref<Control>, &'static FerroProperty),
375    /// `Decorator.Child`.
376    Decorator(Ref<Decorator>),
377    /// `TextBlock.Text`.
378    Text(Ref<TextBlock>),
379}
380
381/// A child in its host: a control or a value (text in a content, an item).
382#[derive(Clone)]
383enum Placed {
384    Control(Ref<Control>),
385    Value(BoxedValue),
386}
387
388impl Placed {
389    fn boxed(&self) -> BoxedValue {
390        match self {
391            Placed::Control(control) => Control::boxed(control),
392            Placed::Value(value) => value.clone(),
393        }
394    }
395}
396
397impl Host {
398    fn of(control: &Ref<Control>) -> Option<Host> {
399        if let Some(panel) = control.cast::<Panel>() {
400            return Some(Host::Panel(panel));
401        }
402        if let Some(items) = control.cast::<ItemsControl>() {
403            return Some(Host::Items(items));
404        }
405        if control.cast::<ContentControl>().is_some() {
406            return Some(Host::Content(control.clone(), ContentControl::content_property().as_property()));
407        }
408        if control.cast::<ContentPresenter>().is_some() {
409            return Some(Host::Content(control.clone(), ContentPresenter::content_property().as_property()));
410        }
411        if let Some(decorator) = control.cast::<Decorator>() {
412            return Some(Host::Decorator(decorator));
413        }
414        if let Some(text) = control.cast::<TextBlock>() {
415            return Some(Host::Text(text));
416        }
417        None
418    }
419
420    fn is_single(&self) -> bool {
421        !matches!(self, Host::Panel(_) | Host::Items(_))
422    }
423
424    /// Whether text is held as text (a content, an item, the text of a
425    /// `TextBlock`) rather than as a `TextBlock`.
426    fn takes_text(&self) -> bool {
427        matches!(self, Host::Content(..) | Host::Items(_) | Host::Text(_))
428    }
429
430    /// Replaces the children from `start` on (`removed` of them) with `items`.
431    fn splice(&self, start: usize, removed: usize, items: &[Placed]) {
432        match self {
433            Host::Panel(panel) => {
434                let children = panel.children();
435                for _ in 0..removed {
436                    children.remove_at(start);
437                }
438                for (offset, item) in items.iter().enumerate() {
439                    match item {
440                        Placed::Control(control) => children.insert(start + offset, control.clone()),
441                        Placed::Value(_) => panic!("a panel holds controls; text is not a child of a panel (use a TextBlock)"),
442                    }
443                }
444            }
445            Host::Items(items_control) => {
446                let collection = items_control.items();
447                for _ in 0..removed {
448                    collection.remove_at(start);
449                }
450                for (offset, item) in items.iter().enumerate() {
451                    collection.insert(start + offset, Some(item.boxed()));
452                }
453            }
454            Host::Content(control, property) => {
455                assert!(start + items.len() <= 1, "a content holds one child");
456                let value: Option<BoxedValue> = items.first().map(Placed::boxed);
457                control.set_value_untyped(property, &value, BindingPriority::LocalValue);
458            }
459            Host::Decorator(decorator) => {
460                assert!(start + items.len() <= 1, "a decorator holds one child");
461                match items.first() {
462                    Some(Placed::Control(control)) => decorator.set_child(control.clone()),
463                    Some(Placed::Value(_)) => panic!("a decorator holds a control; text is not its child (use a TextBlock)"),
464                    None => decorator.set_child(None::<Ref<Control>>),
465                }
466            }
467            Host::Text(text_block) => {
468                let text: String = items
469                    .iter()
470                    .map(|item| match item {
471                        Placed::Value(value) => value.downcast_ref::<String>().cloned().unwrap_or_default(),
472                        Placed::Control(_) => panic!("a TextBlock holds text, not controls"),
473                    })
474                    .collect();
475                text_block.set_text(Some(text.as_str()));
476            }
477        }
478    }
479}
480
481/// A slot of the children of a host: one placed child, or the children of a
482/// dynamic list.
483enum Slot {
484    Fixed(Placed),
485    Dynamic(Rc<RefCell<Vec<Placed>>>),
486}
487
488impl Element {
489    /// Mounts the element as a control: creates the controls of the tree,
490    /// gives them their values and children, attaches their handlers and
491    /// starts the effects of their reactive values. The effects belong to
492    /// the current owner (see [`Owner`]): disposing it stops them.
493    ///
494    /// Text becomes a `TextBlock`, and several elements a `StackPanel`.
495    ///
496    /// # Panics
497    /// Panics for an empty element or a dynamic list, which have no control
498    /// of their own.
499    pub fn mount(self) -> Ref<Control> {
500        match self {
501            Element::Control(element) => mount_control(*element),
502            Element::Existing(control) => control,
503            Element::Text(text) => {
504                let text_block = TextBlock::new();
505                text_block.set_text(Some(&*text));
506                text_block.upcast()
507            }
508            Element::Fragment(items) => {
509                let panel = ferroui_controls::StackPanel::new();
510                place_children(&panel.clone().upcast(), items);
511                panel.upcast()
512            }
513            Element::Empty | Element::Dynamic(_) => panic!("an empty element or a dynamic list has no control of its own"),
514        }
515    }
516
517    /// Mounts the element under a new owner of its own, which is disposed
518    /// when the returned owner is: the root of a window or of a piece of user
519    /// interface that ends on its own.
520    pub fn mount_root(self) -> (Ref<Control>, Owner) {
521        let owner = Owner::new_root();
522        let control = owner.with(|| self.mount());
523        (control, owner)
524    }
525
526    /// The key of the element, if it has one.
527    pub fn key(&self) -> Option<&Key> {
528        match self {
529            Element::Control(element) => element.key.as_ref(),
530            _ => None,
531        }
532    }
533}
534
535fn mount_control(element: ControlElement) -> Ref<Control> {
536    let control = (element.class.create)();
537    // As the XAML loader does: the control is told that it is being
538    // initialised while it gets its values and children, so that what it
539    // decides from both (a selected index of an items control) is decided
540    // once both are there.
541    control.begin_init();
542    if let Some(name) = element.name {
543        control.set_name(Some(name));
544    }
545    if !element.classes.is_empty() {
546        let classes = control.classes();
547        for class in &element.classes {
548            classes.add(class);
549        }
550    }
551    for entry in element.props {
552        apply_prop(&control, entry.property, entry.value);
553    }
554    for attach in &element.handlers {
555        attach(&control);
556    }
557    place_children(&control, element.children);
558    control.end_init();
559    for attach in &element.on_mount {
560        attach(&control);
561    }
562    control
563}
564
565fn apply_prop(control: &Ref<Control>, property: &'static FerroProperty, value: PropValue) {
566    match value {
567        PropValue::Value(value) => {
568            set_untyped(control, property, value);
569        }
570        PropValue::Dynamic(compute) => {
571            let weak = control.downgrade();
572            effect(move || {
573                let value = compute();
574                if let Some(control) = weak.upgrade() {
575                    untrack(|| set_untyped(&control, property, value));
576                }
577            });
578        }
579        PropValue::TwoWay { get, set } => {
580            let weak = control.downgrade();
581            effect(move || {
582                let value = get();
583                if let Some(control) = weak.upgrade() {
584                    untrack(|| set_untyped(&control, property, value));
585                }
586            });
587            let subscription = control.property_changed(move |e| {
588                if std::ptr::eq(e.property(), property) {
589                    set(e.new_value());
590                }
591            });
592            on_cleanup(move || subscription.dispose());
593        }
594        PropValue::Element(element) => {
595            let element = element.borrow_mut().take().unwrap_or_default();
596            let value: Option<BoxedValue> = match element {
597                Element::Empty => None,
598                Element::Text(text) => Some(Rc::new(text.to_string())),
599                element => Some(Control::boxed(&element.mount())),
600            };
601            control.set_value_untyped(property, &value, BindingPriority::LocalValue);
602        }
603        PropValue::Binding(binding) => {
604            control.bind_binding(property, &*binding);
605        }
606    }
607}
608
609/// Sets a property from a value of its type held untyped.
610fn set_untyped(control: &Ref<Control>, property: &'static FerroProperty, value: BoxedValue) {
611    // The value inside the box, not the box: `as_any` of the handle itself
612    // would give the `Rc`.
613    let inner: &dyn ferroui_base::AnyValue = &*value;
614    let value: &dyn Any = inner.as_any();
615    control.set_value_untyped(property, value, BindingPriority::LocalValue);
616}
617
618/// Places the children of an element into the content of its control.
619fn place_children(control: &Ref<Control>, children: Vec<Element>) {
620    let children: Vec<Element> = flatten(children);
621    if children.is_empty() {
622        return;
623    }
624    let host = Host::of(control).unwrap_or_else(|| {
625        panic!("{} has no content to place children into", control.get_type().full_name())
626    });
627    let host = Rc::new(host);
628    let slots: Rc<RefCell<Vec<Slot>>> = Rc::new(RefCell::new(Vec::new()));
629    let mut fixed = Vec::new();
630    for child in children {
631        match child {
632            Element::Dynamic(dynamic) => {
633                let items = Rc::new(RefCell::new(Vec::new()));
634                slots.borrow_mut().push(Slot::Dynamic(items.clone()));
635                mount_dynamic(host.clone(), slots.clone(), items, dynamic);
636            }
637            child => {
638                let placed = place(child, host.takes_text());
639                fixed.push(placed.clone());
640                slots.borrow_mut().push(Slot::Fixed(placed));
641            }
642        }
643    }
644    if host.is_single() && slots.borrow().len() > 1 {
645        panic!("{} holds one child; it was given {}", control.get_type().full_name(), slots.borrow().len());
646    }
647    // The fixed children in order, with the dynamic lists (mounted above)
648    // where they are.
649    let all: Vec<Placed> = slots
650        .borrow()
651        .iter()
652        .flat_map(|slot| match slot {
653            Slot::Fixed(placed) => vec![placed.clone()],
654            Slot::Dynamic(items) => items.borrow().clone(),
655        })
656        .collect();
657    if !all.is_empty() || !fixed.is_empty() {
658        host.splice(0, 0, &all);
659    }
660}
661
662fn flatten(children: Vec<Element>) -> Vec<Element> {
663    let mut flat = Vec::with_capacity(children.len());
664    for child in children {
665        match child {
666            Element::Fragment(items) => flat.extend(flatten(items)),
667            Element::Empty => {}
668            child => flat.push(child),
669        }
670    }
671    flat
672}
673
674/// A child element as what its host holds: text stays text in a content and
675/// an item, and becomes a `TextBlock` elsewhere.
676fn place(child: Element, takes_text: bool) -> Placed {
677    match child {
678        Element::Text(text) if takes_text => Placed::Value(Rc::new(text.to_string())),
679        child => Placed::Control(child.mount()),
680    }
681}
682
683/// The mounted item of a dynamic list, by key.
684struct MountedItem {
685    value: Option<Rc<dyn AnyValueEq>>,
686    placed: Vec<Placed>,
687    owner: Owner,
688}
689
690fn mount_dynamic(host: Rc<Host>, slots: Rc<RefCell<Vec<Slot>>>, items: Rc<RefCell<Vec<Placed>>>, dynamic: DynamicChildren) {
691    let takes_text = host.takes_text();
692    let mounted: Rc<RefCell<HashMap<Key, MountedItem>>> = Rc::new(RefCell::new(HashMap::new()));
693    let unkeyed: Rc<RefCell<Vec<Owner>>> = Rc::new(RefCell::new(Vec::new()));
694    let first_run = Rc::new(std::cell::Cell::new(true));
695    {
696        let mounted = mounted.clone();
697        let unkeyed = unkeyed.clone();
698        on_cleanup(move || {
699            for (_, item) in mounted.borrow_mut().drain() {
700                item.owner.dispose();
701            }
702            for owner in unkeyed.borrow_mut().drain(..) {
703                owner.dispose();
704            }
705        });
706    }
707    let parent = Owner::current();
708    effect(move || {
709        let entries = (dynamic.render)();
710        untrack(|| {
711            let make = |render: &Rc<dyn Fn() -> Element>| -> (Vec<Placed>, Owner) {
712                let owner = parent.map_or_else(Owner::new_root, |parent| parent.with(Owner::new));
713                let placed = owner.with(|| {
714                    flatten(vec![render()])
715                        .into_iter()
716                        .map(|element| match element {
717                            Element::Dynamic(_) => panic!("a dynamic list gives elements, not other dynamic lists"),
718                            element => place(element, takes_text),
719                        })
720                        .collect()
721                });
722                (placed, owner)
723            };
724            for owner in unkeyed.borrow_mut().drain(..) {
725                owner.dispose();
726            }
727            let mut previous = std::mem::take(&mut *mounted.borrow_mut());
728            let mut next_mounted = HashMap::new();
729            let mut next = Vec::with_capacity(entries.len());
730            for (key, value, render) in &entries {
731                match key {
732                    Some(key) => {
733                        let reuse = previous.remove(key).filter(|item| match (&item.value, value) {
734                            (Some(old), Some(new)) => old.item_equals(&**new),
735                            _ => true,
736                        });
737                        let item = match reuse {
738                            Some(item) => item,
739                            None => {
740                                if let Some(stale) = previous.remove(key) {
741                                    stale.owner.dispose();
742                                }
743                                let (placed, owner) = make(render);
744                                MountedItem { value: value.clone(), placed, owner }
745                            }
746                        };
747                        next.extend(item.placed.iter().cloned());
748                        next_mounted.insert(key.clone(), item);
749                    }
750                    None => {
751                        let (placed, owner) = make(render);
752                        unkeyed.borrow_mut().push(owner);
753                        next.extend(placed);
754                    }
755                }
756            }
757            for (_, item) in previous {
758                item.owner.dispose();
759            }
760            *mounted.borrow_mut() = next_mounted;
761
762            let old_len = items.borrow().len();
763            *items.borrow_mut() = next.clone();
764            if first_run.replace(false) {
765                // The first run places nothing: the parent places every
766                // child at once.
767                return;
768            }
769            let start = slot_start(&slots, &items);
770            host.splice(start, old_len, &next);
771        });
772    });
773}
774
775/// Where the children of a dynamic slot begin among the children of the
776/// host.
777fn slot_start(slots: &Rc<RefCell<Vec<Slot>>>, items: &Rc<RefCell<Vec<Placed>>>) -> usize {
778    let mut start = 0;
779    for slot in slots.borrow().iter() {
780        match slot {
781            Slot::Fixed(_) => start += 1,
782            Slot::Dynamic(other) => {
783                if Rc::ptr_eq(other, items) {
784                    return start;
785                }
786                start += other.borrow().len();
787            }
788        }
789    }
790    start
791}
792
793/// A subscription of the property system ended with its owner.
794#[allow(dead_code)]
795pub(crate) fn dispose_with_owner(subscription: Rc<dyn IDisposable>) {
796    on_cleanup(move || subscription.dispose());
797}
798
799/// The element with `key` (an element of a control keeps it; any other
800/// element has no key and is returned as it is).
801pub fn with_key(element: Element, key: Key) -> Element {
802    match element {
803        Element::Control(mut control) => {
804            control.key = Some(key);
805            Element::Control(control)
806        }
807        element => element,
808    }
809}