// Copyright © SixtyFPS GmbH // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial // cSpell: ignore unerase use crate::{api::Value, dynamic_type, eval}; use core::convert::TryInto; use core::ptr::NonNull; use dynamic_type::{Instance, InstanceBox}; use i_slint_compiler::expression_tree::{Expression, NamedReference}; use i_slint_compiler::langtype::{ElementType, Type}; use i_slint_compiler::object_tree::ElementRc; use i_slint_compiler::*; use i_slint_compiler::{diagnostics::BuildDiagnostics, object_tree::PropertyDeclaration}; use i_slint_core::accessibility::AccessibleStringProperty; use i_slint_core::component::{ Component, ComponentRef, ComponentRefPin, ComponentVTable, ComponentWeak, IndexRange, }; use i_slint_core::item_tree::{ ItemRc, ItemTreeNode, ItemVisitorRefMut, ItemVisitorVTable, ItemWeak, TraversalOrder, VisitChildrenResult, }; use i_slint_core::items::{AccessibleRole, Flickable, ItemRef, ItemVTable, PropertyAnimation}; use i_slint_core::layout::{BoxLayoutCellData, LayoutInfo, Orientation}; use i_slint_core::lengths::LogicalLength; use i_slint_core::model::RepeatedComponent; use i_slint_core::model::Repeater; use i_slint_core::platform::PlatformError; use i_slint_core::properties::InterpolatedPropertyValue; use i_slint_core::rtti::{self, AnimatedBindingKind, FieldOffset, PropertyInfo}; use i_slint_core::slice::Slice; use i_slint_core::window::{WindowAdapter, WindowInner}; use i_slint_core::{Brush, Color, Property, SharedString, SharedVector}; use std::collections::BTreeMap; use std::collections::HashMap; use std::{pin::Pin, rc::Rc}; pub struct ComponentBox<'id> { instance: InstanceBox<'id>, component_type: Rc>, } impl<'id> ComponentBox<'id> { /// Borrow this component as a `Pin` pub fn borrow(&self) -> ComponentRefPin { self.borrow_instance().borrow() } /// Safety: the lifetime is not unique pub fn description(&self) -> Rc> { self.component_type.clone() } pub fn borrow_instance<'a>(&'a self) -> InstanceRef<'a, 'id> { InstanceRef { instance: self.instance.as_pin_ref(), component_type: &self.component_type } } pub fn window_adapter(&self) -> &Rc { self.component_type .window_adapter_offset .apply(self.instance.as_pin_ref().get_ref()) .as_ref() .as_ref() .unwrap() } } pub(crate) struct ItemWithinComponent { offset: usize, pub(crate) rtti: Rc, elem: ElementRc, } impl ItemWithinComponent { pub(crate) unsafe fn item_from_component( &self, mem: *const u8, ) -> Pin> { Pin::new_unchecked(vtable::VRef::from_raw( NonNull::from(self.rtti.vtable), NonNull::new(mem.add(self.offset) as _).unwrap(), )) } pub(crate) fn item_index(&self) -> usize { *self.elem.borrow().item_index.get().unwrap() } } pub(crate) struct PropertiesWithinComponent { pub(crate) offset: usize, pub(crate) prop: Box>, } pub(crate) struct RepeaterWithinComponent<'par_id, 'sub_id> { /// The component description of the items to repeat pub(crate) component_to_repeat: Rc>, /// The model pub(crate) model: Expression, /// Offset of the `Repeater` offset: FieldOffset, Repeater>, } impl RepeatedComponent for ErasedComponentBox { type Data = Value; fn update(&self, index: usize, data: Self::Data) { generativity::make_guard!(guard); let s = self.unerase(guard); s.component_type.set_property(s.borrow(), "index", index.try_into().unwrap()).unwrap(); s.component_type.set_property(s.borrow(), "model_data", data).unwrap(); } fn init(&self) { self.run_setup_code(); } fn listview_layout( self: Pin<&Self>, offset_y: &mut LogicalLength, viewport_width: Pin<&Property>, ) { generativity::make_guard!(guard); let s = self.unerase(guard); s.component_type .set_property(s.borrow(), "y", Value::Number(offset_y.get() as f64)) .expect("cannot set y"); let h: f32 = s .component_type .get_property(s.borrow(), "height") .expect("missing height") .try_into() .expect("height not the right type"); let w: f32 = s .component_type .get_property(s.borrow(), "width") .expect("missing width") .try_into() .expect("width not the right type"); let h = LogicalLength::new(h); let w = LogicalLength::new(w); *offset_y += h; let vp_w = viewport_width.get(); if vp_w < w { viewport_width.set(w); } } fn box_layout_data(self: Pin<&Self>, o: Orientation) -> BoxLayoutCellData { BoxLayoutCellData { constraint: self.borrow().as_ref().layout_info(o) } } } impl Component for ErasedComponentBox { fn visit_children_item( self: Pin<&Self>, index: isize, order: TraversalOrder, visitor: ItemVisitorRefMut, ) -> VisitChildrenResult { self.borrow().as_ref().visit_children_item(index, order, visitor) } fn layout_info(self: Pin<&Self>, orientation: Orientation) -> i_slint_core::layout::LayoutInfo { self.borrow().as_ref().layout_info(orientation) } fn get_item_tree(self: Pin<&Self>) -> Slice { get_item_tree(self.get_ref().borrow()) } fn get_item_ref(self: Pin<&Self>, index: usize) -> Pin { // We're having difficulties transferring the lifetime to a pinned reference // to the other ComponentVTable with the same life time. So skip the vtable // indirection and call our implementation directly. unsafe { get_item_ref(self.get_ref().borrow(), index) } } fn get_subtree_range(self: Pin<&Self>, index: usize) -> IndexRange { self.borrow().as_ref().get_subtree_range(index) } fn get_subtree_component( self: Pin<&Self>, index: usize, subindex: usize, result: &mut ComponentWeak, ) { self.borrow().as_ref().get_subtree_component(index, subindex, result); } fn parent_node(self: Pin<&Self>, result: &mut ItemWeak) { self.borrow().as_ref().parent_node(result) } fn subtree_index(self: Pin<&Self>) -> usize { self.borrow().as_ref().subtree_index() } fn accessible_role(self: Pin<&Self>, index: usize) -> AccessibleRole { self.borrow().as_ref().accessible_role(index) } fn accessible_string_property( self: Pin<&Self>, index: usize, what: AccessibleStringProperty, result: &mut SharedString, ) { self.borrow().as_ref().accessible_string_property(index, what, result) } } i_slint_core::ComponentVTable_static!(static COMPONENT_BOX_VT for ErasedComponentBox); impl<'id> Drop for ErasedComponentBox { fn drop(&mut self) { generativity::make_guard!(guard); let unerase = self.unerase(guard); let instance_ref = unerase.borrow_instance(); if let Some(window_adapter) = eval::window_adapter_ref(instance_ref) { i_slint_core::component::unregister_component( instance_ref.instance, vtable::VRef::new(self), instance_ref.component_type.item_array.as_slice(), window_adapter, ); } } } pub type DynamicComponentVRc = vtable::VRc; #[derive(Default)] pub(crate) struct ComponentExtraData { pub(crate) globals: crate::global_component::GlobalStorage, pub(crate) self_weak: once_cell::unsync::OnceCell>, // resource id -> file path pub(crate) embedded_file_resources: HashMap, } struct ErasedRepeaterWithinComponent<'id>(RepeaterWithinComponent<'id, 'static>); impl<'id, 'sub_id> From> for ErasedRepeaterWithinComponent<'id> { fn from(from: RepeaterWithinComponent<'id, 'sub_id>) -> Self { // Safety: this is safe as we erase the sub_id lifetime. // As long as when we get it back we get an unique lifetime with ErasedRepeaterWithinComponent::unerase Self(unsafe { core::mem::transmute::< RepeaterWithinComponent<'id, 'sub_id>, RepeaterWithinComponent<'id, 'static>, >(from) }) } } impl<'id> ErasedRepeaterWithinComponent<'id> { pub fn unerase<'a, 'sub_id>( &'a self, _guard: generativity::Guard<'sub_id>, ) -> &'a RepeaterWithinComponent<'id, 'sub_id> { // Safety: we just go from 'static to an unique lifetime unsafe { core::mem::transmute::< &'a RepeaterWithinComponent<'id, 'static>, &'a RepeaterWithinComponent<'id, 'sub_id>, >(&self.0) } } /// Return a repeater with a component with a 'static lifetime /// /// Safety: one should ensure that the inner component is not mixed with other inner component unsafe fn get_untagged(&self) -> &RepeaterWithinComponent<'id, 'static> { &self.0 } } type Callback = i_slint_core::Callback<[Value], Value>; #[derive(Clone)] pub struct ErasedComponentDescription(Rc>); impl ErasedComponentDescription { pub fn unerase<'a, 'id>( &'a self, _guard: generativity::Guard<'id>, ) -> &'a Rc> { // Safety: we just go from 'static to an unique lifetime unsafe { core::mem::transmute::< &'a Rc>, &'a Rc>, >(&self.0) } } } impl<'id> From>> for ErasedComponentDescription { fn from(from: Rc>) -> Self { // Safety: We never access the ComponentDescription with the static lifetime, only after we unerase it Self(unsafe { core::mem::transmute::>, Rc>>( from, ) }) } } /// ComponentDescription is a representation of a component suitable for interpretation /// /// It contains information about how to create and destroy the Component. /// Its first member is the ComponentVTable for this component, since it is a `#[repr(C)]` /// structure, it is valid to cast a pointer to the ComponentVTable back to a /// ComponentDescription to access the extra field that are needed at runtime #[repr(C)] pub struct ComponentDescription<'id> { pub(crate) ct: ComponentVTable, /// INVARIANT: both dynamic_type and item_tree have the same lifetime id. Here it is erased to 'static dynamic_type: Rc>, item_tree: Vec, item_array: Vec, ItemVTable, vtable::AllowPin>>, pub(crate) items: HashMap, pub(crate) custom_properties: HashMap, pub(crate) custom_callbacks: HashMap, Callback>>, repeater: Vec>, /// Map the Element::id of the repeater to the index in the `repeater` vec pub repeater_names: HashMap, /// Offset to a Option pub(crate) parent_component_offset: Option, Option>>>, /// Offset to the window reference pub(crate) window_adapter_offset: FieldOffset, Option>>, /// Offset of a ComponentExtraData pub(crate) extra_data_offset: FieldOffset, ComponentExtraData>, /// Keep the Rc alive pub(crate) original: Rc, /// Maps from an item_id to the original element it came from pub(crate) original_elements: Vec, /// Copy of original.root_element.property_declarations, without a guarded refcell public_properties: BTreeMap, /// compiled globals compiled_globals: Vec, /// Map of all exported global singletons and their index in the compiled_globals vector. The key /// is the normalized name of the global. exported_globals_by_name: BTreeMap, } fn internal_properties_to_public<'a>( prop_iter: impl Iterator + 'a, ) -> impl Iterator + 'a { prop_iter.filter(|(_, v)| v.expose_in_public_api).map(|(s, v)| { let name = v .node .as_ref() .and_then(|n| { n.child_node(parser::SyntaxKind::DeclaredIdentifier) .and_then(|n| n.child_token(parser::SyntaxKind::Identifier)) }) .map(|n| n.to_string()) .unwrap_or_else(|| s.clone()); (name, v.property_type.clone()) }) } impl<'id> ComponentDescription<'id> { /// The name of this Component as written in the .slint file pub fn id(&self) -> &str { self.original.id.as_str() } /// List of publicly declared properties or callbacks /// /// We try to preserve the dashes and underscore as written in the property declaration pub fn properties( &self, ) -> impl Iterator + '_ { internal_properties_to_public(self.public_properties.iter()) } /// List names of exported global singletons pub fn global_names(&self) -> impl Iterator + '_ { self.compiled_globals .iter() .filter(|g| g.visible_in_public_api()) .flat_map(|g| g.names().into_iter()) } pub fn global_properties( &self, name: &str, ) -> Option + '_> { self.exported_globals_by_name .get(crate::normalize_identifier(name).as_ref()) .and_then(|global_idx| self.compiled_globals.get(*global_idx)) .map(|global| internal_properties_to_public(global.public_properties())) } /// Instantiate a runtime component from this ComponentDescription pub fn create( self: Rc, #[cfg(target_arch = "wasm32")] canvas_id: String, ) -> Result { let window_adapter = i_slint_backend_selector::with_platform(|_b| { #[cfg(not(target_arch = "wasm32"))] return _b.create_window_adapter(); #[cfg(target_arch = "wasm32")] Ok(i_slint_backend_winit::create_gl_window_with_canvas_id(canvas_id)) })?; Ok(self.create_with_existing_window(&window_adapter)) } #[doc(hidden)] pub fn create_with_existing_window( self: Rc, window_adapter: &Rc, ) -> DynamicComponentVRc { let component_ref = instantiate(self, None, window_adapter, Default::default()); WindowInner::from_pub(component_ref.as_pin_ref().window_adapter().window()) .set_component(&vtable::VRc::into_dyn(component_ref.clone())); component_ref.run_setup_code(); component_ref } /// Set a value to property. /// /// Return an error if the property with this name does not exist in this component, /// or if the value is the wrong type. /// Panics if the component is not an instance corresponding to this ComponentDescription, pub fn set_property( &self, component: ComponentRefPin, name: &str, value: Value, ) -> Result<(), crate::api::SetPropertyError> { if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) { panic!("mismatch instance and vtable"); } generativity::make_guard!(guard); let c = unsafe { InstanceRef::from_pin_ref(component, guard) }; if let Some(alias) = self .original .root_element .borrow() .property_declarations .get(name) .and_then(|d| d.is_alias.as_ref()) { eval::store_property(c, &alias.element(), alias.name(), value) } else { eval::store_property(c, &self.original.root_element, name, value) } } /// Set a binding to a property /// /// Returns an error if the component is not an instance corresponding to this ComponentDescription, /// or if the property with this name does not exist in this component #[allow(unused)] pub fn set_binding( &self, component: ComponentRefPin, name: &str, binding: Box Value>, ) -> Result<(), ()> { if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) { return Err(()); } let x = self.custom_properties.get(name).ok_or(())?; unsafe { x.prop .set_binding( Pin::new_unchecked(&*component.as_ptr().add(x.offset)), binding, i_slint_core::rtti::AnimatedBindingKind::NotAnimated, ) .unwrap() }; Ok(()) } /// Return the value of a property /// /// Returns an error if the component is not an instance corresponding to this ComponentDescription, /// or if a callback with this name does not exist in this component pub fn get_property(&self, component: ComponentRefPin, name: &str) -> Result { if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) { return Err(()); } generativity::make_guard!(guard); // Safety: we just verified that the component has the right vtable let c = unsafe { InstanceRef::from_pin_ref(component, guard) }; if let Some(alias) = self .original .root_element .borrow() .property_declarations .get(name) .and_then(|d| d.is_alias.as_ref()) { eval::load_property(c, &alias.element(), alias.name()) } else { eval::load_property(c, &self.original.root_element, name) } } /// Sets an handler for a callback /// /// Returns an error if the component is not an instance corresponding to this ComponentDescription, /// or if the property with this name does not exist in this component pub fn set_callback_handler( &self, component: Pin, name: &str, handler: Box Value>, ) -> Result<(), ()> { if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) { return Err(()); } if let Some(alias) = self .original .root_element .borrow() .property_declarations .get(name) .and_then(|d| d.is_alias.as_ref()) { generativity::make_guard!(guard); // Safety: we just verified that the component has the right vtable let c = unsafe { InstanceRef::from_pin_ref(component, guard) }; generativity::make_guard!(guard); let element = alias.element(); match eval::enclosing_component_instance_for_element( &element, eval::ComponentInstance::InstanceRef(c), guard, ) { eval::ComponentInstance::InstanceRef(enclosing_component) => { let component_type = enclosing_component.component_type; let item_info = &component_type.items[element.borrow().id.as_str()]; let item = unsafe { item_info.item_from_component(enclosing_component.as_ptr()) }; if let Some(callback) = item_info.rtti.callbacks.get(alias.name()) { callback.set_handler(item, handler) } else if let Some(callback_offset) = component_type.custom_callbacks.get(alias.name()) { let callback = callback_offset.apply(&*enclosing_component.instance); callback.set_handler(handler) } else { return Err(()); } } eval::ComponentInstance::GlobalComponent(glob) => { return glob.as_ref().set_callback_handler(alias.name(), handler); } } } else { let x = self.custom_callbacks.get(name).ok_or(())?; let sig = x.apply(unsafe { &*(component.as_ptr() as *const dynamic_type::Instance) }); sig.set_handler(handler); } Ok(()) } /// Invoke the specified callback or function /// /// Returns an error if the component is not an instance corresponding to this ComponentDescription, /// or if the callback with this name does not exist in this component pub fn invoke( &self, component: ComponentRefPin, name: &str, args: &[Value], ) -> Result { if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) { return Err(()); } generativity::make_guard!(guard); // Safety: we just verified that the component has the right vtable let c = unsafe { InstanceRef::from_pin_ref(component, guard) }; let borrow = self.original.root_element.borrow(); let decl = borrow.property_declarations.get(name).ok_or(())?; let (elem, name) = if let Some(alias) = &decl.is_alias { (alias.element(), alias.name()) } else { (self.original.root_element.clone(), name) }; let inst = eval::ComponentInstance::InstanceRef(c); if matches!(&decl.property_type, Type::Function { .. }) { eval::call_function(inst, &elem, name, args.iter().cloned().collect()).ok_or(()) } else { eval::invoke_callback(inst, &elem, name, args).ok_or(()) } } // Return the global with the given name pub fn get_global( &self, component: ComponentRefPin, global_name: &str, ) -> Result>, ()> { if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) { return Err(()); } generativity::make_guard!(guard); // Safety: we just verified that the component has the right vtable let c = unsafe { InstanceRef::from_pin_ref(component, guard) }; let extra_data = c.component_type.extra_data_offset.apply(c.instance.get_ref()); extra_data.globals.get(global_name).cloned().ok_or(()) } } extern "C" fn visit_children_item( component: ComponentRefPin, index: isize, order: TraversalOrder, v: ItemVisitorRefMut, ) -> VisitChildrenResult { generativity::make_guard!(guard); let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) }; let comp_rc = instance_ref.self_weak().get().unwrap().upgrade().unwrap(); i_slint_core::item_tree::visit_item_tree( instance_ref.instance, &vtable::VRc::into_dyn(comp_rc), get_item_tree(component).as_slice(), index, order, v, |_, order, visitor, index| { // `ensure_updated` needs a 'static lifetime so we must call get_untagged. // Safety: we do not mix the component with other component id in this function let rep_in_comp = unsafe { instance_ref.component_type.repeater[index].get_untagged() }; ensure_repeater_updated(instance_ref, rep_in_comp); let repeater = rep_in_comp.offset.apply_pin(instance_ref.instance); repeater.visit(order, visitor) }, ) } /// Make sure that the repeater is updated fn ensure_repeater_updated<'id>( instance_ref: InstanceRef<'_, 'id>, rep_in_comp: &RepeaterWithinComponent<'id, '_>, ) { let repeater = rep_in_comp.offset.apply_pin(instance_ref.instance); let init = || { let window_adapter = instance_ref .component_type .window_adapter_offset .apply(instance_ref.as_ref()) .as_ref() .unwrap(); let instance = instantiate( rep_in_comp.component_to_repeat.clone(), Some(instance_ref.borrow()), window_adapter, Default::default(), ); instance }; if let Some(lv) = &rep_in_comp .component_to_repeat .original .parent_element .upgrade() .unwrap() .borrow() .repeated .as_ref() .unwrap() .is_listview { let assume_property_logical_length = |prop| unsafe { Pin::new_unchecked(&*(prop as *const Property)) }; let get_prop = |nr: &NamedReference| -> LogicalLength { eval::load_property(instance_ref, &nr.element(), nr.name()).unwrap().try_into().unwrap() }; repeater.ensure_updated_listview( init, assume_property_logical_length(get_property_ptr(&lv.viewport_width, instance_ref)), assume_property_logical_length(get_property_ptr(&lv.viewport_height, instance_ref)), assume_property_logical_length(get_property_ptr(&lv.viewport_y, instance_ref)), get_prop(&lv.listview_width), assume_property_logical_length(get_property_ptr(&lv.listview_height, instance_ref)), ); } else { repeater.ensure_updated(init); } } /// Information attached to a builtin item pub(crate) struct ItemRTTI { vtable: &'static ItemVTable, type_info: dynamic_type::StaticTypeInfo, pub(crate) properties: HashMap<&'static str, Box>, pub(crate) callbacks: HashMap<&'static str, Box>, } fn rtti_for>( ) -> (&'static str, Rc) { let rtti = ItemRTTI { vtable: T::static_vtable(), type_info: dynamic_type::StaticTypeInfo::new::(), properties: T::properties() .into_iter() .map(|(k, v)| (k, Box::new(v) as Box)) .collect(), callbacks: T::callbacks() .into_iter() .map(|(k, v)| (k, Box::new(v) as Box)) .collect(), }; (T::name(), Rc::new(rtti)) } /// Create a ComponentDescription from a source. /// The path corresponding to the source need to be passed as well (path is used for diagnostics /// and loading relative assets) pub async fn load( source: String, path: std::path::PathBuf, mut compiler_config: CompilerConfiguration, guard: generativity::Guard<'_>, ) -> (Result>, ()>, i_slint_compiler::diagnostics::BuildDiagnostics) { if compiler_config.style.is_none() && std::env::var("SLINT_STYLE").is_err() { // Defaults to native if it exists: compiler_config.style = Some(if i_slint_backend_selector::HAS_NATIVE_STYLE { "native".to_owned() } else { "fluent".to_owned() }); } let mut diag = BuildDiagnostics::default(); let syntax_node = parser::parse(source, Some(path.as_path()), &mut diag); if diag.has_error() { return (Err(()), diag); } let (doc, mut diag) = compile_syntax_node(syntax_node, diag, compiler_config).await; if diag.has_error() { return (Err(()), diag); } if matches!( doc.root_component.root_element.borrow().base_type, ElementType::Global | ElementType::Error ) { diag.push_error_with_span("No component found".into(), Default::default()); return (Err(()), diag); } #[cfg(feature = "highlight")] crate::highlight::add_highlight_items(&doc); (Ok(generate_component(&doc.root_component, guard)), diag) } pub(crate) fn generate_component<'id>( component: &Rc, guard: generativity::Guard<'id>, ) -> Rc> { //dbg!(&*component.root_element.borrow()); let mut rtti = HashMap::new(); { use i_slint_core::items::*; rtti.extend( [ rtti_for::(), rtti_for::(), rtti_for::(), rtti_for::(), rtti_for::(), rtti_for::(), rtti_for::(), rtti_for::(), rtti_for::(), rtti_for::(), rtti_for::(), rtti_for::(), rtti_for::(), rtti_for::(), rtti_for::(), rtti_for::(), rtti_for::(), ] .iter() .cloned(), ); trait NativeHelper { fn push(rtti: &mut HashMap<&str, Rc>); } impl NativeHelper for () { fn push(_rtti: &mut HashMap<&str, Rc>) {} } impl< T: 'static + Default + rtti::BuiltinItem + vtable::HasStaticVTable, Next: NativeHelper, > NativeHelper for (T, Next) { fn push(rtti: &mut HashMap<&str, Rc>) { let info = rtti_for::(); rtti.insert(info.0, info.1); Next::push(rtti); } } i_slint_backend_selector::NativeWidgets::push(&mut rtti); } struct TreeBuilder<'id> { tree_array: Vec, item_array: Vec, ItemVTable, vtable::AllowPin>>, original_elements: Vec, items_types: HashMap, type_builder: dynamic_type::TypeBuilder<'id>, repeater: Vec>, repeater_names: HashMap, rtti: Rc>>, } impl<'id> generator::ItemTreeBuilder for TreeBuilder<'id> { type SubComponentState = (); fn push_repeated_item( &mut self, item_rc: &ElementRc, repeater_count: u32, parent_index: u32, _component_state: &Self::SubComponentState, ) { self.tree_array .push(ItemTreeNode::DynamicTree { index: repeater_count as usize, parent_index }); self.original_elements.push(item_rc.clone()); let item = item_rc.borrow(); let base_component = item.base_type.as_component(); self.repeater_names.insert(item.id.clone(), self.repeater.len()); generativity::make_guard!(guard); self.repeater.push( RepeaterWithinComponent { component_to_repeat: generate_component(base_component, guard), offset: self.type_builder.add_field_type::>(), model: item.repeated.as_ref().unwrap().model.clone(), } .into(), ); } fn push_native_item( &mut self, rc_item: &ElementRc, child_offset: u32, parent_index: u32, _component_state: &Self::SubComponentState, ) { let item = rc_item.borrow(); let rt = self.rtti.get(&*item.base_type.as_native().class_name).unwrap_or_else(|| { panic!("Native type not registered: {}", item.base_type.as_native().class_name) }); let offset = if item.is_flickable_viewport { let parent = &self.items_types [&object_tree::find_parent_element(rc_item).unwrap().borrow().id]; assert_eq!( parent.elem.borrow().base_type.as_native().class_name.as_str(), "Flickable" ); parent.offset + Flickable::FIELD_OFFSETS.viewport.get_byte_offset() } else { self.type_builder.add_field(rt.type_info) }; self.tree_array.push(ItemTreeNode::Item { is_accessible: !item.accessibility_props.0.is_empty(), children_index: child_offset, children_count: item.children.len() as u32, parent_index, item_array_index: self.item_array.len() as u32, }); self.item_array.push(unsafe { vtable::VOffset::from_raw(rt.vtable, offset) }); self.original_elements.push(rc_item.clone()); debug_assert_eq!(self.original_elements.len(), self.tree_array.len()); self.items_types.insert( item.id.clone(), ItemWithinComponent { offset, rtti: rt.clone(), elem: rc_item.clone() }, ); } fn enter_component( &mut self, _item: &ElementRc, _sub_component: &Rc, _children_offset: u32, _component_state: &Self::SubComponentState, ) -> Self::SubComponentState { /* nothing to do */ } fn enter_component_children( &mut self, _item: &ElementRc, _repeater_count: u32, _component_state: &Self::SubComponentState, _sub_component_state: &Self::SubComponentState, ) { todo!() } } let mut builder = TreeBuilder { tree_array: vec![], item_array: vec![], original_elements: vec![], items_types: HashMap::new(), type_builder: dynamic_type::TypeBuilder::new(guard), repeater: vec![], repeater_names: HashMap::new(), rtti: Rc::new(rtti), }; if !component.is_global() { generator::build_item_tree(component, &(), &mut builder); } let mut custom_properties = HashMap::new(); let mut custom_callbacks = HashMap::new(); fn property_info( ) -> (Box>, dynamic_type::StaticTypeInfo) where T: std::convert::TryInto, Value: std::convert::TryInto, { // Fixme: using u8 in PropertyInfo<> is not sound, we would need to materialize a type for out component ( Box::new(unsafe { vtable::FieldOffset::, _>::new_from_offset_pinned(0) }), dynamic_type::StaticTypeInfo::new::>(), ) } fn animated_property_info( ) -> (Box>, dynamic_type::StaticTypeInfo) where T: std::convert::TryInto, Value: std::convert::TryInto, { // Fixme: using u8 in PropertyInfo<> is not sound, we would need to materialize a type for out component ( Box::new(unsafe { rtti::MaybeAnimatedPropertyInfoWrapper( vtable::FieldOffset::, _>::new_from_offset_pinned(0), ) }), dynamic_type::StaticTypeInfo::new::>(), ) } for (name, decl) in &component.root_element.borrow().property_declarations { if decl.is_alias.is_some() { continue; } let (prop, type_info) = match &decl.property_type { Type::Float32 => animated_property_info::(), Type::Int32 => animated_property_info::(), Type::String => property_info::(), Type::Color => animated_property_info::(), Type::Brush => animated_property_info::(), Type::Duration => animated_property_info::(), Type::Angle => animated_property_info::(), Type::PhysicalLength => animated_property_info::(), Type::LogicalLength => animated_property_info::(), Type::Rem => animated_property_info::(), Type::Image => property_info::(), Type::Bool => property_info::(), Type::Callback { .. } => { custom_callbacks .insert(name.clone(), builder.type_builder.add_field_type::()); continue; } Type::Struct { name: Some(name), .. } if name.ends_with("::StateInfo") => { property_info::() } Type::Struct { .. } => property_info::(), Type::Array(_) => property_info::(), Type::Percent => property_info::(), Type::Enumeration(e) => { macro_rules! match_enum_type { ($( $(#[$enum_doc:meta])* enum $Name:ident { $($body:tt)* })*) => { match e.name.as_str() { $( stringify!($Name) => property_info::(), )* _ => panic!("unknown enum"), } } } i_slint_common::for_each_enums!(match_enum_type) } Type::LayoutCache => property_info::>(), Type::Function { .. } => continue, _ => panic!("bad type {:?}", &decl.property_type), }; custom_properties.insert( name.clone(), PropertiesWithinComponent { offset: builder.type_builder.add_field(type_info), prop }, ); } if component.parent_element.upgrade().is_some() { let (prop, type_info) = property_info::(); custom_properties.insert( "index".into(), PropertiesWithinComponent { offset: builder.type_builder.add_field(type_info), prop }, ); // FIXME: make it a property for the correct type instead of being generic let (prop, type_info) = property_info::(); custom_properties.insert( "model_data".into(), PropertiesWithinComponent { offset: builder.type_builder.add_field(type_info), prop }, ); } else { let (prop, type_info) = property_info::(); custom_properties.insert( "scale_factor".into(), PropertiesWithinComponent { offset: builder.type_builder.add_field(type_info), prop }, ); } let parent_component_offset = if component.parent_element.upgrade().is_some() { Some(builder.type_builder.add_field_type::>()) } else { None }; let window_adapter_offset = builder.type_builder.add_field_type::>>(); let extra_data_offset = builder.type_builder.add_field_type::(); let public_properties = component.root_element.borrow().property_declarations.clone(); let mut exported_globals_by_name: BTreeMap = Default::default(); let compiled_globals = component .used_types .borrow() .globals .iter() .enumerate() .map(|(index, component)| { let mut global = crate::global_component::generate(component); if component.visible_in_public_api() { global.extend_public_properties( component.root_element.borrow().property_declarations.clone().into_iter(), ); exported_globals_by_name.extend( component .exported_global_names .borrow() .iter() .map(|exported_name| (exported_name.name.clone(), index)), ) } global }) .collect(); let t = ComponentVTable { visit_children_item, layout_info, get_item_ref, get_item_tree, get_subtree_range, get_subtree_component, parent_node, subtree_index, accessible_role, accessible_string_property, drop_in_place, dealloc, }; let t = ComponentDescription { ct: t, dynamic_type: builder.type_builder.build(), item_tree: builder.tree_array, item_array: builder.item_array, items: builder.items_types, custom_properties, custom_callbacks, original: component.clone(), original_elements: builder.original_elements, repeater: builder.repeater, repeater_names: builder.repeater_names, parent_component_offset, window_adapter_offset, extra_data_offset, public_properties, compiled_globals, exported_globals_by_name, }; Rc::new(t) } pub fn animation_for_property( component: InstanceRef, animation: &Option, ) -> AnimatedBindingKind { match animation { Some(i_slint_compiler::object_tree::PropertyAnimation::Static(anim_elem)) => { AnimatedBindingKind::Animation(eval::new_struct_with_bindings( &anim_elem.borrow().bindings, &mut eval::EvalLocalContext::from_component_instance(component), )) } Some(i_slint_compiler::object_tree::PropertyAnimation::Transition { animations, state_ref, }) => { let component_ptr = component.as_ptr(); let vtable = NonNull::from(&component.component_type.ct).cast(); let animations = animations.clone(); let state_ref = state_ref.clone(); AnimatedBindingKind::Transition(Box::new( move || -> (PropertyAnimation, i_slint_core::animations::Instant) { generativity::make_guard!(guard); let component = unsafe { InstanceRef::from_pin_ref( Pin::new_unchecked(vtable::VRef::from_raw( vtable, NonNull::new_unchecked(component_ptr as *mut u8), )), guard, ) }; let mut context = eval::EvalLocalContext::from_component_instance(component); let state = eval::eval_expression(&state_ref, &mut context); let state_info: i_slint_core::properties::StateInfo = state.try_into().unwrap(); for a in &animations { if (a.is_out && a.state_id == state_info.previous_state) || (!a.is_out && a.state_id == state_info.current_state) { return ( eval::new_struct_with_bindings( &a.animation.borrow().bindings, &mut context, ), state_info.change_time, ); } } Default::default() }, )) } None => AnimatedBindingKind::NotAnimated, } } fn make_callback_eval_closure( expr: Expression, self_weak: &vtable::VWeak, ) -> impl Fn(&[Value]) -> Value { let self_weak = self_weak.clone(); move |args| { let self_rc = self_weak.upgrade().unwrap(); generativity::make_guard!(guard); let self_ = self_rc.unerase(guard); let instance_ref = self_.borrow_instance(); let mut local_context = eval::EvalLocalContext::from_function_arguments(instance_ref, args.to_vec()); eval::eval_expression(&expr, &mut local_context) } } fn make_binding_eval_closure( expr: Expression, self_weak: &vtable::VWeak, ) -> impl Fn() -> Value { let self_weak = self_weak.clone(); move || { let self_rc = self_weak.upgrade().unwrap(); generativity::make_guard!(guard); let self_ = self_rc.unerase(guard); let instance_ref = self_.borrow_instance(); eval::eval_expression( &expr, &mut eval::EvalLocalContext::from_component_instance(instance_ref), ) } } pub fn instantiate( component_type: Rc, parent_ctx: Option, window_adapter: &Rc, mut globals: crate::global_component::GlobalStorage, ) -> DynamicComponentVRc { let mut instance = component_type.dynamic_type.clone().create_instance(); if let Some(parent) = parent_ctx { *component_type.parent_component_offset.unwrap().apply_mut(instance.as_mut()) = Some(parent); } else { for g in &component_type.compiled_globals { crate::global_component::instantiate(g, &mut globals, window_adapter); } let extra_data = component_type.extra_data_offset.apply_mut(instance.as_mut()); extra_data.globals = globals; extra_data.embedded_file_resources = component_type .original .embedded_file_resources .borrow() .iter() .map(|(path, er)| (er.id, path.clone())) .collect(); } *component_type.window_adapter_offset.apply_mut(instance.as_mut()) = Some(window_adapter.clone()); let component_box = ComponentBox { instance, component_type: component_type.clone() }; let instance_ref = component_box.borrow_instance(); if !component_type.original.is_global() { i_slint_core::component::register_component( instance_ref.instance, instance_ref.component_type.item_array.as_slice(), eval::window_adapter_ref(instance_ref).unwrap(), ); } let self_rc = vtable::VRc::new(ErasedComponentBox::from(component_box)); let self_weak = vtable::VRc::downgrade(&self_rc); generativity::make_guard!(guard); let comp = self_rc.unerase(guard); let instance_ref = comp.borrow_instance(); instance_ref.self_weak().set(self_weak.clone()).ok(); let component_type = comp.description(); // Some properties are generated as Value, but for which the default constructed Value must be initialized for (prop_name, decl) in &component_type.original.root_element.borrow().property_declarations { if !matches!(decl.property_type, Type::Struct { .. } | Type::Array(_)) || decl.is_alias.is_some() { continue; } if let Some(b) = component_type.original.root_element.borrow().bindings.get(prop_name) { if b.borrow().two_way_bindings.is_empty() { continue; } } let p = component_type.custom_properties.get(prop_name).unwrap(); unsafe { let item = Pin::new_unchecked(&*instance_ref.as_ptr().add(p.offset)); p.prop.set(item, eval::default_value_for_type(&decl.property_type), None).unwrap(); } } generator::handle_property_bindings_init( &component_type.original, |elem, prop_name, binding| unsafe { let is_root = Rc::ptr_eq( elem, &elem.borrow().enclosing_component.upgrade().unwrap().root_element, ); let elem = elem.borrow(); let is_const = binding.analysis.as_ref().map_or(false, |a| a.is_const); let property_type = elem.lookup_property(prop_name).property_type; if let Type::Function { .. } = property_type { // function don't need initialization } else if let Type::Callback { .. } = property_type { let expr = binding.expression.clone(); let component_type = component_type.clone(); if let Some(callback_offset) = component_type.custom_callbacks.get(prop_name).filter(|_| is_root) { let callback = callback_offset.apply(instance_ref.as_ref()); callback.set_handler(make_callback_eval_closure(expr, &self_weak)); } else { let item_within_component = &component_type.items[&elem.id]; let item = item_within_component.item_from_component(instance_ref.as_ptr()); if let Some(callback) = item_within_component.rtti.callbacks.get(prop_name) { callback.set_handler( item, Box::new(make_callback_eval_closure(expr, &self_weak)), ); } else { panic!("unknown callback {}", prop_name) } } } else if let Some(PropertiesWithinComponent { offset, prop: prop_info, .. }) = component_type.custom_properties.get(prop_name).filter(|_| is_root) { let is_state_info = matches!(property_type, Type::Struct { name: Some(name), .. } if name.ends_with("::StateInfo")); if is_state_info { let prop = Pin::new_unchecked( &*(instance_ref.as_ptr().add(*offset) as *const Property), ); let e = binding.expression.clone(); let state_binding = make_binding_eval_closure(e, &self_weak); i_slint_core::properties::set_state_binding(prop, move || { state_binding().try_into().unwrap() }); return; } let maybe_animation = animation_for_property(instance_ref, &binding.animation); let item = Pin::new_unchecked(&*instance_ref.as_ptr().add(*offset)); if !matches!(binding.expression, Expression::Invalid) { if is_const { let v = eval::eval_expression( &binding.expression, &mut eval::EvalLocalContext::from_component_instance(instance_ref), ); prop_info.set(item, v, None).unwrap(); } else { let e = binding.expression.clone(); prop_info .set_binding( item, Box::new(make_binding_eval_closure(e, &self_weak)), maybe_animation, ) .unwrap(); } } for nr in &binding.two_way_bindings { // Safety: The compiler must have ensured that the properties exist and are of the same type prop_info.link_two_ways(item, get_property_ptr(nr, instance_ref)); } } else { let item_within_component = &component_type.items[&elem.id]; let item = item_within_component.item_from_component(instance_ref.as_ptr()); if let Some(prop_rtti) = item_within_component.rtti.properties.get(prop_name) { let maybe_animation = animation_for_property(instance_ref, &binding.animation); for nr in &binding.two_way_bindings { // Safety: The compiler must have ensured that the properties exist and are of the same type prop_rtti.link_two_ways(item, get_property_ptr(nr, instance_ref)); } if !matches!(binding.expression, Expression::Invalid) { if is_const { prop_rtti .set( item, eval::eval_expression( &binding.expression, &mut eval::EvalLocalContext::from_component_instance( instance_ref, ), ), maybe_animation.as_animation(), ) .unwrap(); } else { let e = binding.expression.clone(); prop_rtti.set_binding( item, Box::new(make_binding_eval_closure(e, &self_weak)), maybe_animation, ); } } } else { panic!("unknown property {}", prop_name); } } }, ); for rep_in_comp in &component_type.repeater { generativity::make_guard!(guard); let rep_in_comp = rep_in_comp.unerase(guard); let repeater = rep_in_comp.offset.apply_pin(instance_ref.instance); let expr = rep_in_comp.model.clone(); let model_binding_closure = make_binding_eval_closure(expr, &self_weak); repeater.set_model_binding(move || { let m = model_binding_closure(); i_slint_core::model::ModelRc::new(crate::value_model::ValueModel::new(m)) }); } self_rc } pub(crate) fn get_property_ptr(nr: &NamedReference, instance: InstanceRef) -> *const () { let element = nr.element(); generativity::make_guard!(guard); let enclosing_component = eval::enclosing_component_instance_for_element( &element, eval::ComponentInstance::InstanceRef(instance), guard, ); match enclosing_component { eval::ComponentInstance::InstanceRef(enclosing_component) => { let element = element.borrow(); if element.id == element.enclosing_component.upgrade().unwrap().root_element.borrow().id { if let Some(x) = enclosing_component.component_type.custom_properties.get(nr.name()) { return unsafe { enclosing_component.as_ptr().add(x.offset).cast() }; }; }; let item_info = enclosing_component .component_type .items .get(element.id.as_str()) .unwrap_or_else(|| panic!("Unknown element for {}.{}", element.id, nr.name())); let prop_info = item_info .rtti .properties .get(nr.name()) .unwrap_or_else(|| panic!("Property {} not in {}", nr.name(), element.id)); core::mem::drop(element); let item = unsafe { item_info.item_from_component(enclosing_component.as_ptr()) }; unsafe { item.as_ptr().add(prop_info.offset()).cast() } } eval::ComponentInstance::GlobalComponent(glob) => glob.as_ref().get_property_ptr(nr.name()), } } pub struct ErasedComponentBox(ComponentBox<'static>); impl ErasedComponentBox { pub fn unerase<'a, 'id>( &'a self, _guard: generativity::Guard<'id>, ) -> Pin<&'a ComponentBox<'id>> { Pin::new( //Safety: 'id is unique because of `_guard` unsafe { core::mem::transmute::<&ComponentBox<'static>, &ComponentBox<'id>>(&self.0) }, ) } pub fn borrow(&self) -> ComponentRefPin { // Safety: it is safe to access self.0 here because the 'id lifetime does not leak self.0.borrow() } pub fn window_adapter(&self) -> &Rc { self.0.window_adapter() } pub fn run_setup_code(&self) { generativity::make_guard!(guard); let compo_box = self.unerase(guard); let instance_ref = compo_box.borrow_instance(); for extra_init_code in self.0.component_type.original.init_code.borrow().iter() { eval::eval_expression( extra_init_code, &mut eval::EvalLocalContext::from_component_instance(instance_ref), ); } } } impl<'id> From> for ErasedComponentBox { fn from(inner: ComponentBox<'id>) -> Self { // Safety: Nothing access the component directly, we only access it through unerased where // the lifetime is unique again unsafe { ErasedComponentBox(core::mem::transmute::, ComponentBox<'static>>( inner, )) } } } pub fn get_repeater_by_name<'a, 'id>( instance_ref: InstanceRef<'a, '_>, name: &str, guard: generativity::Guard<'id>, ) -> (std::pin::Pin<&'a Repeater>, Rc>) { let rep_index = instance_ref.component_type.repeater_names[name]; let rep_in_comp = instance_ref.component_type.repeater[rep_index].unerase(guard); (rep_in_comp.offset.apply_pin(instance_ref.instance), rep_in_comp.component_to_repeat.clone()) } extern "C" fn layout_info(component: ComponentRefPin, orientation: Orientation) -> LayoutInfo { generativity::make_guard!(guard); // This is fine since we can only be called with a component that with our vtable which is a ComponentDescription let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) }; let orientation = crate::eval_layout::from_runtime(orientation); let mut result = crate::eval_layout::get_layout_info( &instance_ref.component_type.original.root_element, instance_ref, eval::window_adapter_ref(instance_ref).unwrap(), orientation, ); let constraints = instance_ref.component_type.original.root_constraints.borrow(); if constraints.has_explicit_restrictions() { crate::eval_layout::fill_layout_info_constraints( &mut result, &constraints, orientation, &|nr: &NamedReference| { eval::load_property(instance_ref, &nr.element(), nr.name()) .unwrap() .try_into() .unwrap() }, ); } result } unsafe extern "C" fn get_item_ref(component: ComponentRefPin, index: usize) -> Pin { let tree = get_item_tree(component); match &tree[index] { ItemTreeNode::Item { item_array_index, .. } => { generativity::make_guard!(guard); let instance_ref = InstanceRef::from_pin_ref(component, guard); core::mem::transmute::, Pin>( instance_ref.component_type.item_array[*item_array_index as usize] .apply_pin(instance_ref.instance), ) } ItemTreeNode::DynamicTree { .. } => panic!("get_item_ref called on dynamic tree"), } } extern "C" fn get_subtree_range(component: ComponentRefPin, index: usize) -> IndexRange { generativity::make_guard!(guard); let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) }; let rep_in_comp = unsafe { instance_ref.component_type.repeater[index].get_untagged() }; ensure_repeater_updated(instance_ref, rep_in_comp); let repeater = rep_in_comp.offset.apply(&instance_ref.instance); repeater.range().into() } extern "C" fn get_subtree_component( component: ComponentRefPin, index: usize, subtree_index: usize, result: &mut ComponentWeak, ) { generativity::make_guard!(guard); let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) }; let rep_in_comp = unsafe { instance_ref.component_type.repeater[index].get_untagged() }; ensure_repeater_updated(instance_ref, rep_in_comp); let repeater = rep_in_comp.offset.apply(&instance_ref.instance); *result = vtable::VRc::downgrade(&vtable::VRc::into_dyn( repeater.component_at(subtree_index).unwrap(), )) } extern "C" fn get_item_tree(component: ComponentRefPin) -> Slice { generativity::make_guard!(guard); let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) }; let tree = instance_ref.component_type.item_tree.as_slice(); unsafe { core::mem::transmute::<&[ItemTreeNode], &[ItemTreeNode]>(tree) }.into() } extern "C" fn subtree_index(component: ComponentRefPin) -> usize { generativity::make_guard!(guard); let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) }; if let Ok(value) = instance_ref.component_type.get_property(component, "index") { value.try_into().unwrap() } else { core::usize::MAX } } unsafe extern "C" fn parent_node(component: ComponentRefPin, result: &mut ItemWeak) { generativity::make_guard!(guard); let instance_ref = InstanceRef::from_pin_ref(component, guard); let parent_item_index = instance_ref .component_type .original .parent_element .upgrade() .and_then(|e| e.borrow().item_index.get().cloned()); if let (Some(parent_offset), Some(parent_index)) = (instance_ref.component_type.parent_component_offset, parent_item_index) { if let Some(parent) = parent_offset.apply(instance_ref.as_ref()) { generativity::make_guard!(new_guard); let parent_instance = InstanceRef::from_pin_ref(*parent, new_guard); let parent_rc = parent_instance.self_weak().get().unwrap().clone().into_dyn().upgrade().unwrap(); *result = ItemRc::new(parent_rc, parent_index).downgrade(); }; } } extern "C" fn accessible_role(component: ComponentRefPin, item_index: usize) -> AccessibleRole { generativity::make_guard!(guard); let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) }; let nr = instance_ref.component_type.original_elements[item_index] .borrow() .accessibility_props .0 .get("accessible-role") .cloned(); match nr { Some(nr) => crate::eval::load_property(instance_ref, &nr.element(), nr.name()) .unwrap() .try_into() .unwrap(), None => AccessibleRole::default(), } } extern "C" fn accessible_string_property( component: ComponentRefPin, item_index: usize, what: AccessibleStringProperty, result: &mut SharedString, ) { generativity::make_guard!(guard); let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) }; let prop_name = format!("accessible-{}", what.to_string()); let nr = instance_ref.component_type.original_elements[item_index] .borrow() .accessibility_props .0 .get(&prop_name) .cloned(); if let Some(nr) = nr { let value = crate::eval::load_property(instance_ref, &nr.element(), nr.name()).unwrap(); match value { Value::String(s) => *result = s, Value::Bool(b) => *result = if b { "true" } else { "false" }.into(), Value::Number(x) => *result = x.to_string().into(), _ => unimplemented!("invalid type for accessible_string_property"), }; } } unsafe extern "C" fn drop_in_place(component: vtable::VRefMut) -> vtable::Layout { let instance_ptr = component.as_ptr() as *mut Instance<'static>; let layout = (*instance_ptr).type_info().layout(); dynamic_type::TypeInfo::drop_in_place(instance_ptr); layout.into() } unsafe extern "C" fn dealloc(_vtable: &ComponentVTable, ptr: *mut u8, layout: vtable::Layout) { std::alloc::dealloc(ptr, layout.try_into().unwrap()); } #[derive(Copy, Clone)] pub struct InstanceRef<'a, 'id> { pub instance: Pin<&'a Instance<'id>>, pub component_type: &'a ComponentDescription<'id>, } impl<'a, 'id> InstanceRef<'a, 'id> { pub unsafe fn from_pin_ref( component: ComponentRefPin<'a>, _guard: generativity::Guard<'id>, ) -> Self { Self { instance: Pin::new_unchecked(&*(component.as_ref().as_ptr() as *const Instance<'id>)), component_type: &*(Pin::into_inner_unchecked(component).get_vtable() as *const ComponentVTable as *const ComponentDescription<'id>), } } pub fn as_ptr(&self) -> *const u8 { (&*self.instance.as_ref()) as *const Instance as *const u8 } pub fn as_ref(&self) -> &Instance<'id> { &*self.instance } /// Borrow this component as a `Pin` pub fn borrow(self) -> ComponentRefPin<'a> { unsafe { Pin::new_unchecked(vtable::VRef::from_raw( NonNull::from(&self.component_type.ct).cast(), NonNull::from(self.instance.get_ref()).cast(), )) } } pub fn self_weak( &self, ) -> &once_cell::unsync::OnceCell> { let extra_data = self.component_type.extra_data_offset.apply(self.as_ref()); &extra_data.self_weak } pub fn window_adapter(&self) -> &Rc { self.component_type.window_adapter_offset.apply(self.as_ref()).as_ref().as_ref().unwrap() } pub fn parent_instance(&self) -> Option> { if let Some(parent_offset) = self.component_type.parent_component_offset { if let Some(parent) = parent_offset.apply(self.as_ref()) { let parent_instance = unsafe { Self { instance: Pin::new_unchecked( &*(parent.as_ref().as_ptr() as *const Instance<'id>), ), component_type: &*(Pin::into_inner_unchecked(*parent).get_vtable() as *const ComponentVTable as *const ComponentDescription<'id>), } }; return Some(parent_instance); }; } None } pub fn toplevel_instance(&self) -> InstanceRef<'a, 'id> { if let Some(parent) = self.parent_instance() { parent.toplevel_instance() } else { *self } } } /// Show the popup at the given location pub fn show_popup( popup: &object_tree::PopupWindow, pos: i_slint_core::graphics::Point, parent_comp: ComponentRefPin, parent_window_adapter: &Rc, parent_item: &ItemRc, ) { generativity::make_guard!(guard); // FIXME: we should compile once and keep the cached compiled component let compiled = generate_component(&popup.component, guard); let inst = instantiate(compiled, Some(parent_comp), parent_window_adapter, Default::default()); inst.run_setup_code(); WindowInner::from_pub(parent_window_adapter.window()).show_popup( &vtable::VRc::into_dyn(inst), pos, parent_item, ); }