i_slint_core/
component_factory.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4#![warn(missing_docs)]
5
6//! This module defines a `ComponentFactory` and related code.
7use crate::api::ComponentHandle;
8use crate::item_tree::{ItemTreeRc, ItemTreeVTable, ItemTreeWeak};
9use alloc::boxed::Box;
10use alloc::rc::Rc;
11use core::fmt::Debug;
12
13/// The `FactoryContext` provides extra information to the ComponentFactory
14pub struct FactoryContext {
15    /// The item tree to embed the factory product into
16    pub parent_item_tree: ItemTreeWeak,
17    /// The index in the parent item tree with the dynamic node to connect
18    /// the factories product to.
19    pub parent_item_tree_index: u32,
20}
21
22#[derive(Clone)]
23struct ComponentFactoryInner(Rc<dyn Fn(FactoryContext) -> Option<ItemTreeRc> + 'static>);
24
25impl PartialEq for ComponentFactoryInner {
26    fn eq(&self, other: &Self) -> bool {
27        Rc::ptr_eq(&self.0, &other.0)
28    }
29}
30
31impl Debug for ComponentFactoryInner {
32    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
33        f.debug_tuple("ComponentFactoryData").finish()
34    }
35}
36
37/// A `ComponentFactory` can be used to create new Components at runtime,
38/// taking a factory function returning a [`ComponentHandle`].
39///
40/// The `FactoryContext` is passed to that factory function.
41///
42/// A `ComponentFactory` implements the `component-factory` type for
43/// properties in the Slint language.
44///
45/// The `component-factory` is used by an `ComponentContainer` element in Slint
46/// files to embed UI elements based on the produced component within the
47/// `ComponentContainer` element.
48#[derive(Clone, Debug, Default, PartialEq)]
49pub struct ComponentFactory(Option<ComponentFactoryInner>);
50
51impl ComponentFactory {
52    /// Create a new `ComponentFactory`
53    pub fn new<
54        X: vtable::HasStaticVTable<ItemTreeVTable> + 'static,
55        T: ComponentHandle<WeakInner = vtable::VWeak<ItemTreeVTable, X>> + 'static,
56    >(
57        factory: impl Fn(FactoryContext) -> Option<T> + 'static,
58    ) -> Self {
59        let factory = Box::new(factory) as Box<dyn Fn(FactoryContext) -> Option<T> + 'static>;
60
61        Self(Some(ComponentFactoryInner(Rc::new(move |ctx| -> Option<ItemTreeRc> {
62            let product = (factory)(ctx);
63            product.map(|p| vtable::VRc::into_dyn(p.as_weak().inner().upgrade().unwrap()))
64        }))))
65    }
66
67    /// Build a `Component`
68    pub(crate) fn build(&self, ctx: FactoryContext) -> Option<ItemTreeRc> {
69        self.0.as_ref().and_then(move |b| (b.0)(ctx))
70    }
71}