Component: Add information to stich together ItemTrees

Add accessors to the information necessary to stitch together the
Component-wide ItemTrees (which include DynamicNodes) into one logical
tree of Items.
This commit is contained in:
Tobias Hunger 2022-03-25 09:20:16 +01:00 committed by Tobias Hunger
parent c1bb22bd00
commit ee68716d07
8 changed files with 368 additions and 17 deletions

View file

@ -42,10 +42,12 @@ namespace slint {
namespace private_api { namespace private_api {
using cbindgen_private::ComponentVTable; using cbindgen_private::ComponentVTable;
using cbindgen_private::ItemVTable; using cbindgen_private::ItemVTable;
using ComponentRc = vtable::VRc<private_api::ComponentVTable>;
using ComponentRef = vtable::VRef<private_api::ComponentVTable>; using ComponentRef = vtable::VRef<private_api::ComponentVTable>;
using IndexRange = cbindgen_private::IndexRange;
using ItemRef = vtable::VRef<private_api::ItemVTable>; using ItemRef = vtable::VRef<private_api::ItemVTable>;
using ItemVisitorRefMut = vtable::VRefMut<cbindgen_private::ItemVisitorVTable>; using ItemVisitorRefMut = vtable::VRefMut<cbindgen_private::ItemVisitorVTable>;
using cbindgen_private::ComponentRc; using cbindgen_private::ComponentWeak;
using cbindgen_private::ItemWeak; using cbindgen_private::ItemWeak;
using cbindgen_private::TraversalOrder; using cbindgen_private::TraversalOrder;
} }
@ -846,6 +848,16 @@ public:
return { &C::static_vtable, const_cast<C *>(&(**x.ptr)) }; return { &C::static_vtable, const_cast<C *>(&(**x.ptr)) };
} }
void component_at(int i, vtable::VWeak<private_api::ComponentVTable> *result) const
{
const auto &x = inner->data.at(i);
*result = vtable::VWeak<private_api::ComponentVTable>{x.ptr->into_dyn()};
}
private_api::IndexRange index_range() const {
return private_api::IndexRange { 0, inner->data.size() };
}
float compute_layout_listview(const private_api::Property<float> *viewport_width, float compute_layout_listview(const private_api::Property<float> *viewport_width,
float listview_width) const float listview_width) const
{ {

View file

@ -1,6 +1,8 @@
// Copyright © SixtyFPS GmbH <info@slint-ui.com> // Copyright © SixtyFPS GmbH <info@slint-ui.com>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
// cSpell: ignore buildrs
/*! /*!
# Slint # Slint
@ -283,7 +285,7 @@ pub mod re_exports {
pub use i_slint_core::callbacks::Callback; pub use i_slint_core::callbacks::Callback;
pub use i_slint_core::component::{ pub use i_slint_core::component::{
free_component_item_graphics_resources, init_component_items, Component, ComponentRefPin, free_component_item_graphics_resources, init_component_items, Component, ComponentRefPin,
ComponentVTable, ComponentVTable, ComponentWeak, IndexRange,
}; };
pub use i_slint_core::graphics::*; pub use i_slint_core::graphics::*;
pub use i_slint_core::input::{ pub use i_slint_core::input::{

View file

@ -848,15 +848,25 @@ fn generate_item_tree(
let mut visit_children_statements = vec![ let mut visit_children_statements = vec![
"static const auto dyn_visit = [] (const uint8_t *base, [[maybe_unused]] slint::private_api::TraversalOrder order, [[maybe_unused]] slint::private_api::ItemVisitorRefMut visitor, [[maybe_unused]] uintptr_t dyn_index) -> uint64_t {".to_owned(), "static const auto dyn_visit = [] (const uint8_t *base, [[maybe_unused]] slint::private_api::TraversalOrder order, [[maybe_unused]] slint::private_api::ItemVisitorRefMut visitor, [[maybe_unused]] uintptr_t dyn_index) -> uint64_t {".to_owned(),
format!(" [[maybe_unused]] auto self = reinterpret_cast<const {}*>(base);", item_tree_class_name)]; format!(" [[maybe_unused]] auto self = reinterpret_cast<const {}*>(base);", item_tree_class_name)];
let mut subtree_range_statement = vec![" std::abort();".into()];
let mut subtree_component_statement = vec![" std::abort();".into()];
if target_struct.members.iter().any(|(_, declaration)| { if target_struct.members.iter().any(|(_, declaration)| {
matches!(&declaration, Declaration::Function(func @ Function { .. }) if func.name == "visit_dynamic_children") matches!(&declaration, Declaration::Function(func @ Function { .. }) if func.name == "visit_dynamic_children")
}) { }) {
visit_children_statements visit_children_statements
.push(" return self->visit_dynamic_children(dyn_index, order, visitor);".into()); .push(" return self->visit_dynamic_children(dyn_index, order, visitor);".into());
subtree_range_statement = vec![
format!("auto self = reinterpret_cast<const {}*>(component.instance);", item_tree_class_name),
"return self->subtree_range(dyn_index);".to_owned(),
];
subtree_component_statement = vec![
format!("auto self = reinterpret_cast<const {}*>(component.instance);", item_tree_class_name),
"self->subtree_component(dyn_index, subtree_index, result);".to_owned(),
];
} else { } else {
visit_children_statements.push(" std::abort();".into()); visit_children_statements.push(" std::abort();".into());
} }
visit_children_statements.extend([ visit_children_statements.extend([
"};".into(), "};".into(),
@ -888,6 +898,28 @@ fn generate_item_tree(
}), }),
)); ));
target_struct.members.push((
Access::Private,
Declaration::Function(Function {
name: "get_subtree_range".into(),
signature: "([[maybe_unused]] slint::private_api::ComponentRef component, [[maybe_unused]] uintptr_t dyn_index) -> slint::private_api::IndexRange".into(),
is_static: true,
statements: Some(subtree_range_statement),
..Default::default()
}),
));
target_struct.members.push((
Access::Private,
Declaration::Function(Function {
name: "get_subtree_component".into(),
signature: "([[maybe_unused]] slint::private_api::ComponentRef component, [[maybe_unused]] uintptr_t dyn_index, [[maybe_unused]] uintptr_t subtree_index, [[maybe_unused]] slint::private_api::ComponentWeak *result) -> void".into(),
is_static: true,
statements: Some(subtree_component_statement),
..Default::default()
}),
));
target_struct.members.push(( target_struct.members.push((
Access::Private, Access::Private,
Declaration::Function(Function { Declaration::Function(Function {
@ -910,7 +942,7 @@ fn generate_item_tree(
format!( format!(
// that does not work when the parent is not a component with a ComponentVTable // that does not work when the parent is not a component with a ComponentVTable
//" *result = slint::private_api::parent_item(self->parent->self_weak.into_dyn(), self->parent->get_item_tree(), {});", //" *result = slint::private_api::parent_item(self->parent->self_weak.into_dyn(), self->parent->get_item_tree(), {});",
"self->parent->self_weak.vtable()->parent_item(self->parent->self_weak.lock()->borrow(), {}, result);", "*result = {{ self->parent->self_weak, {} }};",
parent_index, parent_index,
) )
} else { } else {
@ -934,6 +966,17 @@ fn generate_item_tree(
}), }),
)); ));
target_struct.members.push((
Access::Private,
Declaration::Function(Function {
name: "subtree_index".into(),
signature: "(slint::private_api::ComponentRef /* component */) -> uintptr_t".into(),
is_static: true,
statements: Some(vec!["/* unimplemented! */".to_owned(), "return 0;".into()]),
..Default::default()
}),
));
target_struct.members.push(( target_struct.members.push((
Access::Private, Access::Private,
Declaration::Function(Function { Declaration::Function(Function {
@ -995,7 +1038,7 @@ fn generate_item_tree(
ty: "const slint::private_api::ComponentVTable".to_owned(), ty: "const slint::private_api::ComponentVTable".to_owned(),
name: format!("{}::static_vtable", item_tree_class_name), name: format!("{}::static_vtable", item_tree_class_name),
init: Some(format!( init: Some(format!(
"{{ visit_children, get_item_ref, get_item_tree, parent_item, layout_info, slint::private_api::drop_in_place<{}>, slint::private_api::dealloc }}", "{{ visit_children, get_item_ref, get_subtree_range, get_subtree_component, get_item_tree, parent_item, subtree_index, layout_info, slint::private_api::drop_in_place<{}>, slint::private_api::dealloc }}",
item_tree_class_name) item_tree_class_name)
), ),
..Default::default() ..Default::default()
@ -1181,6 +1224,8 @@ fn generate_sub_component(
} }
let mut children_visitor_cases = Vec::new(); let mut children_visitor_cases = Vec::new();
let mut subtrees_ranges_cases = Vec::new();
let mut subtrees_components_cases = Vec::new();
let mut subcomponent_init_code = Vec::new(); let mut subcomponent_init_code = Vec::new();
for sub in &component.sub_components { for sub in &component.sub_components {
@ -1223,6 +1268,23 @@ fn generate_sub_component(
id = field_name, id = field_name,
base = repeater_offset, base = repeater_offset,
)); ));
subtrees_ranges_cases.push(format!(
"\n {case_code} {{
return self->{id}.subtree_range(dyn_index - {base});
}}",
case_code = case_code,
id = field_name,
base = repeater_offset,
));
subtrees_components_cases.push(format!(
"\n {case_code} {{
self->{id}.subtree_component(dyn_index - {base}, subtree_index, result);
return;
}}",
case_code = case_code,
id = field_name,
base = repeater_offset,
));
} }
target_struct.members.push(( target_struct.members.push((
@ -1321,6 +1383,25 @@ fn generate_sub_component(
i = idx, i = idx,
e_u = ensure_updated, e_u = ensure_updated,
)); ));
subtrees_ranges_cases.push(format!(
"\n case {i}: {{
{e_u}
return self->{id}.index_range();
}}",
i = idx,
e_u = ensure_updated,
id = repeater_id,
));
subtrees_components_cases.push(format!(
"\n case {i}: {{
{e_u}
self->{id}.component_at(subtree_index, result);
return;
}}",
i = idx,
e_u = ensure_updated,
id = repeater_id,
));
target_struct.members.push(( target_struct.members.push((
Access::Private, Access::Private,
@ -1382,6 +1463,32 @@ fn generate_sub_component(
..Default::default() ..Default::default()
}), }),
)); ));
target_struct.members.push((
field_access,
Declaration::Function(Function {
name: "subtree_range".into(),
signature: "(uintptr_t dyn_index) const -> slint::private_api::IndexRange".into(),
statements: Some(vec![
"[[maybe_unused]] auto self = this;".to_owned(),
format!(" switch(dyn_index) {{ {} }};", subtrees_ranges_cases.join("")),
" std::abort();".to_owned(),
]),
..Default::default()
}),
));
target_struct.members.push((
field_access,
Declaration::Function(Function {
name: "subtree_component".into(),
signature: "(uintptr_t dyn_index, [[maybe_unused]] uintptr_t subtree_index, [[maybe_unused]] slint::private_api::ComponentWeak *result) const -> void".into(),
statements: Some(vec![
"[[maybe_unused]] auto self = this;".to_owned(),
format!(" switch(dyn_index) {{ {} }};", subtrees_components_cases.join("")),
" std::abort();".to_owned(),
]),
..Default::default()
}),
));
} }
} }

View file

@ -1,6 +1,8 @@
// Copyright © SixtyFPS GmbH <info@slint-ui.com> // Copyright © SixtyFPS GmbH <info@slint-ui.com>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
// cSpell: ignore conv powf punct
/*! module for the Rust code generator /*! module for the Rust code generator
Some convention used in the generated code: Some convention used in the generated code:
@ -117,7 +119,7 @@ pub fn generate(doc: &Document) -> TokenStream {
let sub_compos = llr let sub_compos = llr
.sub_components .sub_components
.iter() .iter()
.map(|sub_compo| generate_sub_component(sub_compo, &llr, None, quote!())) .map(|sub_compo| generate_sub_component(sub_compo, &llr, None, quote!(), None))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let compo = generate_public_component(&llr); let compo = generate_public_component(&llr);
@ -243,7 +245,7 @@ fn generate_public_component(llr: &llr::PublicComponent) -> TokenStream {
let global_container_id = format_ident!("Globals_{}", public_component_id); let global_container_id = format_ident!("Globals_{}", public_component_id);
let component = let component =
generate_item_tree(&llr.item_tree, llr, None, quote!(globals: #global_container_id)); generate_item_tree(&llr.item_tree, llr, None, quote!(globals: #global_container_id), None);
let ctx = EvaluationContext { let ctx = EvaluationContext {
public_component: llr, public_component: llr,
@ -515,6 +517,7 @@ fn generate_sub_component(
root: &llr::PublicComponent, root: &llr::PublicComponent,
parent_ctx: Option<ParentCtx>, parent_ctx: Option<ParentCtx>,
extra_fields: TokenStream, extra_fields: TokenStream,
index_property: Option<llr::PropertyIndex>,
) -> TokenStream { ) -> TokenStream {
let inner_component_id = inner_component_id(component); let inner_component_id = inner_component_id(component);
@ -527,7 +530,9 @@ fn generate_sub_component(
let mut extra_components = component let mut extra_components = component
.popup_windows .popup_windows
.iter() .iter()
.map(|c| generate_item_tree(c, root, Some(ParentCtx::new(&ctx, None)), quote!())) .map(|c| {
generate_item_tree(c, root, Some(ParentCtx::new(&ctx, None)), quote!(), index_property)
})
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let mut declared_property_vars = vec![]; let mut declared_property_vars = vec![];
@ -592,6 +597,8 @@ fn generate_sub_component(
let mut repeated_element_names: Vec<Ident> = vec![]; let mut repeated_element_names: Vec<Ident> = vec![];
let mut repeated_visit_branch: Vec<TokenStream> = vec![]; let mut repeated_visit_branch: Vec<TokenStream> = vec![];
let mut repeated_element_components: Vec<Ident> = vec![]; let mut repeated_element_components: Vec<Ident> = vec![];
let mut repeated_subtree_ranges: Vec<TokenStream> = vec![];
let mut repeated_subtree_components: Vec<TokenStream> = vec![];
for (idx, repeated) in component.repeated.iter().enumerate() { for (idx, repeated) in component.repeated.iter().enumerate() {
extra_components.push(generate_repeated_component( extra_components.push(generate_repeated_component(
@ -643,6 +650,19 @@ fn generate_sub_component(
_self.#repeater_id.visit(order, visitor) _self.#repeater_id.visit(order, visitor)
} }
)); ));
repeated_subtree_ranges.push(quote!(
#idx => {
#ensure_updated
let (start, end) = _self.#repeater_id.range();
slint::re_exports::IndexRange { start, end }
}
));
repeated_subtree_components.push(quote!(
#idx => {
#ensure_updated
*result = vtable::VRc::downgrade(&vtable::VRc::into_dyn(_self.#repeater_id.component_at(subtree_index).unwrap()))
}
));
repeated_element_names.push(repeater_id); repeated_element_names.push(repeater_id);
repeated_element_components.push(rep_inner_component_id); repeated_element_components.push(rep_inner_component_id);
} }
@ -687,6 +707,16 @@ fn generate_sub_component(
#sub_compo_field.apply_pin(_self).visit_dynamic_children(dyn_index - #repeater_offset, order, visitor) #sub_compo_field.apply_pin(_self).visit_dynamic_children(dyn_index - #repeater_offset, order, visitor)
} }
)); ));
repeated_subtree_ranges.push(quote!(
#repeater_offset..=#last_repeater => {
#sub_compo_field.apply_pin(_self).subtree_range(dyn_index - #repeater_offset)
}
));
repeated_subtree_components.push(quote!(
#repeater_offset..=#last_repeater => {
#sub_compo_field.apply_pin(_self).subtree_component(dyn_index - #repeater_offset, subtree_index, result)
}
));
} }
sub_component_names.push(field_name); sub_component_names.push(field_name);
@ -736,6 +766,18 @@ fn generate_sub_component(
let visibility = let visibility =
core::ptr::eq(&root.item_tree.root as *const _, component as *const _).then(|| quote!(pub)); core::ptr::eq(&root.item_tree.root as *const _, component as *const _).then(|| quote!(pub));
let access_prop = |&property_index| {
access_member(
&llr::PropertyReference::Local { sub_component_path: vec![], property_index },
&ctx,
)
};
let prop = index_property.iter().map(access_prop);
let mut subtree_index_function = quote!(#(#prop.get() as usize)*);
if subtree_index_function.is_empty() {
subtree_index_function = quote!(core::usize::MAX);
}
quote!( quote!(
#[derive(slint::re_exports::FieldOffsets, Default)] #[derive(slint::re_exports::FieldOffsets, Default)]
#[const_field_offset(slint::re_exports::const_field_offset)] #[const_field_offset(slint::re_exports::const_field_offset)]
@ -796,6 +838,33 @@ fn generate_sub_component(
slint::re_exports::Orientation::Vertical => #layout_info_v, slint::re_exports::Orientation::Vertical => #layout_info_v,
} }
} }
fn subtree_range(self: ::core::pin::Pin<&Self>, dyn_index: usize) -> slint::re_exports::IndexRange {
#![allow(unused)]
use slint::re_exports::*;
let _self = self;
match dyn_index {
#(#repeated_subtree_ranges)*
_ => panic!("invalid dyn_index {}", dyn_index),
}
}
fn subtree_component(self: ::core::pin::Pin<&Self>, dyn_index: usize, subtree_index: usize, result: &mut slint::re_exports::ComponentWeak) {
#![allow(unused)]
use slint::re_exports::*;
let _self = self;
match dyn_index {
#(#repeated_subtree_components)*
_ => panic!("invalid dyn_index {}", dyn_index),
};
}
fn index_property(self: ::core::pin::Pin<&Self>) -> usize {
#![allow(unused)]
use slint::re_exports::*;
let _self = self;
#subtree_index_function
}
} }
#(#extra_components)* #(#extra_components)*
@ -923,8 +992,15 @@ fn generate_item_tree(
root: &llr::PublicComponent, root: &llr::PublicComponent,
parent_ctx: Option<ParentCtx>, parent_ctx: Option<ParentCtx>,
extra_fields: TokenStream, extra_fields: TokenStream,
index_property: Option<llr::PropertyIndex>,
) -> TokenStream { ) -> TokenStream {
let sub_comp = generate_sub_component(&sub_tree.root, root, parent_ctx.clone(), extra_fields); let sub_comp = generate_sub_component(
&sub_tree.root,
root,
parent_ctx.clone(),
extra_fields,
index_property,
);
let inner_component_id = self::inner_component_id(&sub_tree.root); let inner_component_id = self::inner_component_id(&sub_tree.root);
let parent_component_type = parent_ctx.iter().map(|parent| { let parent_component_type = parent_ctx.iter().map(|parent| {
let parent_component_id = self::inner_component_id(parent.ctx.current_sub_component.unwrap()); let parent_component_id = self::inner_component_id(parent.ctx.current_sub_component.unwrap());
@ -1077,11 +1153,29 @@ fn generate_item_tree(
Self::item_tree().into() Self::item_tree().into()
} }
fn get_subtree_range(
self: ::core::pin::Pin<&Self>, index: usize) -> slint::re_exports::IndexRange
{
self.subtree_range(index)
}
fn get_subtree_component(
self: ::core::pin::Pin<&Self>, index: usize, subtree_index: usize, result: &mut slint::re_exports::ComponentWeak)
{
self.subtree_component(index, subtree_index, result);
}
fn subtree_index(
self: ::core::pin::Pin<&Self>) -> usize
{
self.index_property()
}
fn parent_item(self: ::core::pin::Pin<&Self>, index: usize, result: &mut slint::re_exports::ItemWeak) { fn parent_item(self: ::core::pin::Pin<&Self>, index: usize, result: &mut slint::re_exports::ItemWeak) {
if index == 0 { if index == 0 {
#( #(
if let Some(parent) = self.parent.clone().upgrade().map(|sc| VRcMapped::origin(&sc)) { if let Some(parent) = self.parent.clone().upgrade().map(|sc| VRcMapped::origin(&sc)) {
*result = slint::re_exports::ItemRc::new(parent, #parent_item_index).parent_item(); *result = slint::re_exports::ItemRc::new(parent, #parent_item_index).downgrade();
} }
)* )*
return; return;
@ -1105,8 +1199,13 @@ fn generate_repeated_component(
root: &llr::PublicComponent, root: &llr::PublicComponent,
parent_ctx: ParentCtx, parent_ctx: ParentCtx,
) -> TokenStream { ) -> TokenStream {
let component = let component = generate_item_tree(
generate_item_tree(&repeated.sub_tree, root, Some(parent_ctx.clone()), quote!()); &repeated.sub_tree,
root,
Some(parent_ctx.clone()),
quote!(),
repeated.index_prop,
);
let ctx = EvaluationContext { let ctx = EvaluationContext {
public_component: root, public_component: root,
@ -1120,7 +1219,7 @@ fn generate_repeated_component(
let inner_component_id = self::inner_component_id(&repeated.sub_tree.root); let inner_component_id = self::inner_component_id(&repeated.sub_tree.root);
// let rep_inner_component_id = self::inner_component_id(&repeated.sub_tree.root.name); // let rep_inner_component_id = self::inner_component_id(&repeated.sub_tree.root.name);
// let inner_component_id = self::inner_component_id(&parent_compo); // let inner_component_id = self::inner_component_id(&parent_compo);
let extra_fn = if let Some(listview) = &repeated.listview { let extra_fn = if let Some(listview) = &repeated.listview {
let p_y = access_member(&listview.prop_y, &ctx); let p_y = access_member(&listview.prop_y, &ctx);

View file

@ -1,6 +1,8 @@
// Copyright © SixtyFPS GmbH <info@slint-ui.com> // Copyright © SixtyFPS GmbH <info@slint-ui.com>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
// cSpell: ignore dealloc
#![warn(missing_docs)] #![warn(missing_docs)]
//! This module contains the basic datastructures that are exposed to the C API //! This module contains the basic datastructures that are exposed to the C API
@ -12,6 +14,15 @@ use crate::slice::Slice;
use crate::window::WindowRc; use crate::window::WindowRc;
use vtable::*; use vtable::*;
#[repr(C)]
/// A range of indices
pub struct IndexRange {
/// Start index
pub start: usize,
/// Index one past the last index
pub end: usize,
}
/// A Component is representing an unit that is allocated together /// A Component is representing an unit that is allocated together
#[vtable] #[vtable]
#[repr(C)] #[repr(C)]
@ -32,17 +43,35 @@ pub struct ComponentVTable {
index: usize, index: usize,
) -> core::pin::Pin<VRef<ItemVTable>>, ) -> core::pin::Pin<VRef<ItemVTable>>,
/// Return the range of indices below the dynamic `ItemTreeNode` at `index`
pub get_subtree_range:
extern "C" fn(core::pin::Pin<VRef<ComponentVTable>>, index: usize) -> IndexRange,
/// Return the `ComponentRc` at `subindex` below the dynamic `ItemTreeNode` at `index`
pub get_subtree_component: extern "C" fn(
core::pin::Pin<VRef<ComponentVTable>>,
index: usize,
subindex: usize,
result: &mut vtable::VWeak<ComponentVTable, Dyn>,
),
/// Return the item tree that is defined by this `Component`. /// Return the item tree that is defined by this `Component`.
/// The return value is an item weak because it can be null if there is no parent. /// The return value is an item weak because it can be null if there is no parent.
/// And the return value is passed by &mut because ItemWeak has a destructor /// And the return value is passed by &mut because ItemWeak has a destructor
pub get_item_tree: extern "C" fn(core::pin::Pin<VRef<ComponentVTable>>) -> Slice<ItemTreeNode>, pub get_item_tree: extern "C" fn(core::pin::Pin<VRef<ComponentVTable>>) -> Slice<ItemTreeNode>,
// FIXME: This does return an invalid ItemWeak now that points to the parent repeater!
// FIXME: Get rid of the index and make this always return the "Item" that connects this component
// to its parent-component?
/// Return the parent item. /// Return the parent item.
/// The return value is an item weak because it can be null if there is no parent. /// The return value is an item weak because it can be null if there is no parent.
/// And the return value is passed by &mut because ItemWeak has a destructor /// And the return value is passed by &mut because ItemWeak has a destructor
pub parent_item: pub parent_item:
extern "C" fn(core::pin::Pin<VRef<ComponentVTable>>, index: usize, result: &mut ItemWeak), extern "C" fn(core::pin::Pin<VRef<ComponentVTable>>, index: usize, result: &mut ItemWeak),
/// Return the index of the current subtree or usize::MAX if this is not a subtree
pub subtree_index: extern "C" fn(core::pin::Pin<VRef<ComponentVTable>>) -> usize,
/// Returns the layout info for this component /// Returns the layout info for this component
pub layout_info: pub layout_info:
extern "C" fn(core::pin::Pin<VRef<ComponentVTable>>, Orientation) -> LayoutInfo, extern "C" fn(core::pin::Pin<VRef<ComponentVTable>>, Orientation) -> LayoutInfo,

View file

@ -1,6 +1,8 @@
// Copyright © SixtyFPS GmbH <info@slint-ui.com> // Copyright © SixtyFPS GmbH <info@slint-ui.com>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
// cSpell: ignore dealloc nesw
/*! /*!
This module contains the builtin items, either in this file or in sub-modules. This module contains the builtin items, either in this file or in sub-modules.
@ -177,6 +179,7 @@ impl ItemRc {
pub fn new(component: vtable::VRc<ComponentVTable>, index: usize) -> Self { pub fn new(component: vtable::VRc<ComponentVTable>, index: usize) -> Self {
Self { component, index } Self { component, index }
} }
/// Return a `Pin<ItemRef<'a>>` /// Return a `Pin<ItemRef<'a>>`
pub fn borrow<'a>(&'a self) -> Pin<ItemRef<'a>> { pub fn borrow<'a>(&'a self) -> Pin<ItemRef<'a>> {
let comp_ref_pin = vtable::VRc::borrow_pin(&self.component); let comp_ref_pin = vtable::VRc::borrow_pin(&self.component);
@ -185,15 +188,29 @@ impl ItemRc {
// lifetime of the component, which is 'a. Pin::as_ref removes the lifetime, but we can just put it back. // lifetime of the component, which is 'a. Pin::as_ref removes the lifetime, but we can just put it back.
unsafe { core::mem::transmute::<Pin<ItemRef<'_>>, Pin<ItemRef<'a>>>(result) } unsafe { core::mem::transmute::<Pin<ItemRef<'_>>, Pin<ItemRef<'a>>>(result) }
} }
pub fn downgrade(&self) -> ItemWeak { pub fn downgrade(&self) -> ItemWeak {
ItemWeak { component: VRc::downgrade(&self.component), index: self.index } ItemWeak { component: VRc::downgrade(&self.component), index: self.index }
} }
/// Return the parent Item in the item tree. /// Return the parent Item in the item tree.
/// This is weak because it can be null if there is no parent /// This is weak because it can be null if there is no parent
pub fn parent_item(&self) -> ItemWeak { pub fn parent_item(&self) -> ItemWeak {
let comp_ref_pin = vtable::VRc::borrow_pin(&self.component); let comp_ref_pin = vtable::VRc::borrow_pin(&self.component);
let item_tree = crate::item_tree::ComponentItemTree::new(
comp_ref_pin.as_ref().get_item_tree().as_slice(),
);
if let Some(parent_index) = item_tree.parent(self.index) {
return ItemRc::new(self.component.clone(), parent_index).downgrade();
}
let mut r = ItemWeak::default(); let mut r = ItemWeak::default();
comp_ref_pin.as_ref().parent_item(self.index, &mut r); comp_ref_pin.as_ref().parent_item(self.index, &mut r);
// parent_item returns the repeater node, go up one more level!
if let Some(rc) = r.upgrade() {
r = rc.parent_item();
}
r r
} }

View file

@ -1,12 +1,15 @@
// Copyright © SixtyFPS GmbH <info@slint-ui.com> // Copyright © SixtyFPS GmbH <info@slint-ui.com>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
// cSpell: ignore vecmodel
//! Model and Repeater //! Model and Repeater
// Safety: we use pointer to Repeater in the DependencyList, bue the Drop of the Repeater // Safety: we use pointer to Repeater in the DependencyList, bue the Drop of the Repeater
// will remove them from the list so it will not be accessed after it is dropped // will remove them from the list so it will not be accessed after it is dropped
#![allow(unsafe_code)] #![allow(unsafe_code)]
use crate::component::ComponentVTable;
use crate::item_tree::TraversalOrder; use crate::item_tree::TraversalOrder;
use crate::items::ItemRef; use crate::items::ItemRef;
use crate::layout::Orientation; use crate::layout::Orientation;
@ -567,7 +570,9 @@ impl<T> Model for ModelRc<T> {
} }
/// Component that can be instantiated by a repeater. /// Component that can be instantiated by a repeater.
pub trait RepeatedComponent: crate::component::Component { pub trait RepeatedComponent:
crate::component::Component + vtable::HasStaticVTable<ComponentVTable> + 'static
{
/// The data corresponding to the model /// The data corresponding to the model
type Data: 'static; type Data: 'static;
@ -615,6 +620,7 @@ impl<C: RepeatedComponent> Default for RepeaterInner<C> {
RepeaterInner { components: Default::default(), offset: 0, cached_item_height: 0. } RepeaterInner { components: Default::default(), offset: 0, cached_item_height: 0. }
} }
} }
trait ErasedRepeater { trait ErasedRepeater {
fn row_changed(&self, row: usize); fn row_changed(&self, row: usize);
fn row_added(&self, index: usize, count: usize); fn row_added(&self, index: usize, count: usize);
@ -968,7 +974,24 @@ impl<C: RepeatedComponent> Repeater<C> {
self.inner.borrow().components.len() self.inner.borrow().components.len()
} }
/// Return true if the Repeater is empty /// Return the range of indices used by this Repeater.
///
/// Two values are necessary here since the Repeater can start to insert the data from its
/// model at an offset.
pub fn range(&self) -> (usize, usize) {
let inner = self.inner.borrow();
(inner.offset, inner.offset + inner.components.len())
}
pub fn component_at(&self, index: usize) -> Option<ComponentRc<C>> {
self.inner
.borrow()
.components
.get(index)
.map(|c| c.1.clone().expect("That was updated before!"))
}
/// Return true if the Repeater as empty
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.len() == 0 self.len() == 0
} }

View file

@ -1,6 +1,8 @@
// Copyright © SixtyFPS GmbH <info@slint-ui.com> // Copyright © SixtyFPS GmbH <info@slint-ui.com>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
// cSpell: ignore dealloc refcell unerase
use crate::{api::Value, dynamic_type, eval}; use crate::{api::Value, dynamic_type, eval};
use core::convert::TryInto; use core::convert::TryInto;
@ -12,7 +14,9 @@ use i_slint_compiler::object_tree::ElementRc;
use i_slint_compiler::*; use i_slint_compiler::*;
use i_slint_compiler::{diagnostics::BuildDiagnostics, object_tree::PropertyDeclaration}; use i_slint_compiler::{diagnostics::BuildDiagnostics, object_tree::PropertyDeclaration};
use i_slint_core::api::Window; use i_slint_core::api::Window;
use i_slint_core::component::{Component, ComponentRef, ComponentRefPin, ComponentVTable}; use i_slint_core::component::{
Component, ComponentRef, ComponentRefPin, ComponentVTable, ComponentWeak, IndexRange,
};
use i_slint_core::item_tree::{ use i_slint_core::item_tree::{
ItemTreeNode, ItemVisitorRefMut, ItemVisitorVTable, TraversalOrder, VisitChildrenResult, ItemTreeNode, ItemVisitorRefMut, ItemVisitorVTable, TraversalOrder, VisitChildrenResult,
}; };
@ -174,9 +178,26 @@ impl Component for ErasedComponentBox {
unsafe { get_item_ref(self.get_ref().borrow(), index) } 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_item(self: Pin<&Self>, index: usize, result: &mut ItemWeak) { fn parent_item(self: Pin<&Self>, index: usize, result: &mut ItemWeak) {
self.borrow().as_ref().parent_item(index, result) self.borrow().as_ref().parent_item(index, result)
} }
fn subtree_index(self: Pin<&Self>) -> usize {
self.borrow().as_ref().subtree_index()
}
} }
i_slint_core::ComponentVTable_static!(static COMPONENT_BOX_VT for ErasedComponentBox); i_slint_core::ComponentVTable_static!(static COMPONENT_BOX_VT for ErasedComponentBox);
@ -1030,7 +1051,10 @@ pub(crate) fn generate_component<'id>(
layout_info, layout_info,
get_item_ref, get_item_ref,
get_item_tree, get_item_tree,
get_subtree_range,
get_subtree_component,
parent_item, parent_item,
subtree_index,
drop_in_place, drop_in_place,
dealloc, dealloc,
}; };
@ -1518,6 +1542,34 @@ unsafe extern "C" fn get_item_ref(component: ComponentRefPin, index: usize) -> P
} }
} }
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);
let (start, end) = repeater.range();
IndexRange { start, end }
}
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<ItemTreeNode> { extern "C" fn get_item_tree(component: ComponentRefPin) -> Slice<ItemTreeNode> {
generativity::make_guard!(guard); generativity::make_guard!(guard);
let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) }; let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
@ -1525,6 +1577,16 @@ extern "C" fn get_item_tree(component: ComponentRefPin) -> Slice<ItemTreeNode> {
unsafe { core::mem::transmute::<&[ItemTreeNode], &[ItemTreeNode]>(tree) }.into() 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_item(component: ComponentRefPin, index: usize, result: &mut ItemWeak) { unsafe extern "C" fn parent_item(component: ComponentRefPin, index: usize, result: &mut ItemWeak) {
generativity::make_guard!(guard); generativity::make_guard!(guard);
let instance_ref = InstanceRef::from_pin_ref(component, guard); let instance_ref = InstanceRef::from_pin_ref(component, guard);
@ -1549,7 +1611,7 @@ unsafe extern "C" fn parent_item(component: ComponentRefPin, index: usize, resul
.into_dyn() .into_dyn()
.upgrade() .upgrade()
.unwrap(); .unwrap();
*result = ItemRc::new(parent_rc, parent_index).parent_item(); *result = ItemRc::new(parent_rc, parent_index).downgrade();
}; };
} }
return; return;