Change component factory API

... to avoid winit panicing on us when embedding some item tree.
This commit is contained in:
Tobias Hunger 2023-10-17 16:29:55 +02:00 committed by Tobias Hunger
parent 3ab98a7596
commit d6ec7f23a1
5 changed files with 62 additions and 29 deletions

View file

@ -5,13 +5,22 @@
//! This module defines a `ComponentFactory` and related code.
use crate::api::ComponentHandle;
use crate::item_tree::{ItemTreeRc, ItemTreeVTable};
use crate::item_tree::{ItemTreeRc, ItemTreeVTable, ItemTreeWeak};
use alloc::boxed::Box;
use alloc::rc::Rc;
use core::fmt::Debug;
/// The `FactoryContext` provides extra information to the ComponentFactory
pub struct FactoryContext {
/// The item tree to embed the factory product into
pub parent_item_tree: ItemTreeWeak,
/// The index in the parent item tree with the dynamic node to connect
/// the factories product to.
pub parent_item_tree_index: u32,
}
#[derive(Clone)]
struct ComponentFactoryInner(Rc<dyn Fn() -> Option<ItemTreeRc> + 'static>);
struct ComponentFactoryInner(Rc<dyn Fn(FactoryContext) -> Option<ItemTreeRc> + 'static>);
impl PartialEq for ComponentFactoryInner {
fn eq(&self, other: &Self) -> bool {
@ -26,8 +35,9 @@ impl Debug for ComponentFactoryInner {
}
/// A `ComponentFactory` can be used to create new Components at runtime,
/// taking a factory function with no arguments and returning
/// a [`ComponentHandle`].
/// taking a factory function returning a [`ComponentHandle`].
///
/// The `FactoryContext` is passed to that factory function.
///
/// A `ComponentFactory` implements the `component-factory` type for
/// properties in the Slint language.
@ -40,20 +50,22 @@ pub struct ComponentFactory(Option<ComponentFactoryInner>);
impl ComponentFactory {
/// Create a new `ComponentFactory`
pub fn new<T: ComponentHandle + 'static>(factory: impl Fn() -> Option<T> + 'static) -> Self
pub fn new<T: ComponentHandle + 'static>(
factory: impl Fn(FactoryContext) -> Option<T> + 'static,
) -> Self
where
T::Inner: vtable::HasStaticVTable<ItemTreeVTable> + 'static,
{
let factory = Box::new(factory) as Box<dyn Fn() -> Option<T> + 'static>;
let factory = Box::new(factory) as Box<dyn Fn(FactoryContext) -> Option<T> + 'static>;
Self(Some(ComponentFactoryInner(Rc::new(move || -> Option<ItemTreeRc> {
let product = (factory)();
Self(Some(ComponentFactoryInner(Rc::new(move |ctx| -> Option<ItemTreeRc> {
let product = (factory)(ctx);
product.map(|p| vtable::VRc::into_dyn(p.as_weak().inner().upgrade().unwrap()))
}))))
}
/// Build a `Component`
pub(crate) fn build(&self) -> Option<ItemTreeRc> {
self.0.as_ref().and_then(|b| (b.0)())
pub(crate) fn build(&self, ctx: FactoryContext) -> Option<ItemTreeRc> {
self.0.as_ref().and_then(move |b| (b.0)(ctx))
}
}

View file

@ -8,7 +8,7 @@ When adding an item or a property, it needs to be kept in sync with different pl
Lookup the [`crate::items`] module documentation.
*/
use super::{Item, ItemConsts, ItemRc, Rectangle, RenderingResult};
use crate::component_factory::ComponentFactory;
use crate::component_factory::{ComponentFactory, FactoryContext};
use crate::input::{
FocusEvent, FocusEventResult, InputEventFilterResult, InputEventResult, KeyEvent,
KeyEventResult, MouseEvent,
@ -77,15 +77,12 @@ impl ComponentContainer {
return;
};
let product = factory.build().and_then(|rc| {
vtable::VRc::borrow_pin(&rc)
.as_ref()
.embed_component(
self.my_component.get().unwrap(),
*self.embedding_item_tree_index.get().unwrap(),
)
.then_some(rc)
});
let factory_context = FactoryContext {
parent_item_tree: self.my_component.get().unwrap().clone(),
parent_item_tree_index: *self.embedding_item_tree_index.get().unwrap(),
};
let product = factory.build(factory_context);
if let Some(rc) = &product {
// The change resulted in a new component to set up:

View file

@ -3,7 +3,7 @@
use core::convert::TryFrom;
use i_slint_compiler::langtype::Type as LangType;
use i_slint_core::component_factory::ComponentFactory;
use i_slint_core::component_factory::{ComponentFactory, FactoryContext};
use i_slint_core::graphics::Image;
use i_slint_core::model::{Model, ModelRc};
use i_slint_core::window::WindowInner;
@ -668,6 +668,17 @@ impl ComponentDefinition {
})
}
/// Creates a new instance of the component and returns a shared handle to it.
pub fn create_embedded(&self, ctx: FactoryContext) -> Result<ComponentInstance, PlatformError> {
generativity::make_guard!(guard);
Ok(ComponentInstance {
inner: self.inner.unerase(guard).clone().create(WindowOptions::Embed {
parent_item_tree: ctx.parent_item_tree,
parent_item_tree_index: ctx.parent_item_tree_index,
})?,
})
}
/// Instantiate the component for wasm using the given canvas id
#[cfg(target_arch = "wasm32")]
pub fn create_with_canvas_id(

View file

@ -392,6 +392,10 @@ pub enum WindowOptions {
UseExistingWindow(WindowAdapterRc),
#[cfg(target_arch = "wasm32")]
CreateWithCanvasId(String),
Embed {
parent_item_tree: ItemTreeWeak,
parent_item_tree_index: u32,
},
}
impl<'id> ItemTreeDescription<'id> {
@ -1265,10 +1269,18 @@ pub fn instantiate(
instance_ref.self_weak().set(self_weak.clone()).ok();
let description = comp.description();
let root = root
.or_else(|| instance_ref.parent_instance().map(|parent| parent.root_weak().clone()))
.unwrap_or_else(|| self_weak.clone());
description.root_offset.apply(instance_ref.as_ref()).set(root).ok().unwrap();
if let Some(WindowOptions::Embed { parent_item_tree, parent_item_tree_index }) = window_options
{
vtable::VRc::borrow_pin(&self_rc)
.as_ref()
.embed_component(parent_item_tree, *parent_item_tree_index);
description.root_offset.apply(instance_ref.as_ref()).set(self_weak.clone()).ok().unwrap();
} else {
let root = root
.or_else(|| instance_ref.parent_instance().map(|parent| parent.root_weak().clone()))
.unwrap_or_else(|| self_weak.clone());
description.root_offset.apply(instance_ref.as_ref()).set(root).ok().unwrap();
}
if !description.original.is_global() {
let maybe_window_adapter =

View file

@ -25,9 +25,10 @@ export component TestCase inherits Rectangle {
```
```rust
let factory = slint::ComponentFactory::new(|| {
let mut compiler = slint_interpreter::ComponentCompiler::new();
let e = spin_on::spin_on(compiler.build_from_source(
let factory = slint::ComponentFactory::new(|ctx| {
let mut compiler = slint_interpreter::ComponentCompiler::new();
let e = spin_on::spin_on(compiler.build_from_source(
r#"import { Button } from "std-widgets.slint";
export component E1 inherits Rectangle {
@ -39,7 +40,7 @@ export component E1 inherits Rectangle {
}"#.into(),
std::path::PathBuf::from("embedded.slint"),
)).unwrap();
e.create().ok()
e.create_embedded(ctx).ok()
});
let instance = TestCase::new().unwrap();