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