Support for changed callback in global

Fixes #6599

ChangeLog: Support property changed callbacks in globals
This commit is contained in:
Olivier Goffart 2024-10-21 17:06:37 +02:00
parent 25607a9706
commit ff53791ce7
8 changed files with 114 additions and 4 deletions

View file

@ -2534,6 +2534,22 @@ fn generate_global(
}
}
for (i, _) in global.change_callbacks.iter() {
global_struct.members.push((
Access::Private,
Declaration::Var(Var {
ty: "slint::private_api::ChangeTracker".into(),
name: format_smolstr!("change_tracker{}", i),
..Default::default()
}),
));
}
init.extend(global.change_callbacks.iter().map(|(p, e)| {
let code = compile_expression(&e.borrow(), &ctx);
let prop = access_member(&llr::PropertyReference::Local { sub_component_path: vec![], property_index: *p }, &ctx);
format!("this->change_tracker{p}.init(this, [this]([[maybe_unused]] auto self) {{ return {prop}.get(); }}, [this]([[maybe_unused]] auto self, auto) {{ {code}; }});")
}));
global_struct.members.push((
Access::Public,
Declaration::Function(Function {

View file

@ -1357,6 +1357,37 @@ fn generate_global(global: &llr::GlobalComponent, root: &llr::CompilationUnit) -
}
}
let public_component_id = ident(&global.name);
let global_id = format_ident!("global_{}", public_component_id);
let change_tracker_names =
global.change_callbacks.iter().map(|(idx, _)| format_ident!("change_tracker{idx}"));
init.extend(global.change_callbacks.iter().map(|(p, e)| {
let code = compile_expression(&e.borrow(), &ctx);
let prop = access_member(
&llr::PropertyReference::Local { sub_component_path: vec![], property_index: *p },
&ctx,
)
.unwrap();
let change_tracker = format_ident!("change_tracker{p}");
quote! {
#[allow(dead_code, unused)]
_self.#change_tracker.init(
self_rc.globals.get().unwrap().clone(),
move |global_weak| {
let self_rc = global_weak.upgrade().unwrap().#global_id.clone();
let _self = self_rc.as_ref();
#prop.get()
},
move |global_weak, _| {
let self_rc = global_weak.upgrade().unwrap().#global_id.clone();
let _self = self_rc.as_ref();
#code;
}
);
}
}));
let public_interface = global.exported.then(|| {
let property_and_callback_accessors = public_api(
&global.public_properties,
@ -1364,8 +1395,6 @@ fn generate_global(global: &llr::GlobalComponent, root: &llr::CompilationUnit) -
quote!(self.0.as_ref()),
&ctx,
);
let public_component_id = ident(&global.name);
let global_id = format_ident!("global_{}", public_component_id);
let aliases = global.aliases.iter().map(|name| ident(name));
let getters = root.public_components.iter().map(|c| {
let root_component_id = ident(&c.name);
@ -1398,6 +1427,7 @@ fn generate_global(global: &llr::GlobalComponent, root: &llr::CompilationUnit) -
struct #inner_component_id {
#(#declared_property_vars: sp::Property<#declared_property_types>,)*
#(#declared_callbacks: sp::Callback<(#(#declared_callbacks_types,)*), #declared_callbacks_ret>,)*
#(#change_tracker_names : sp::ChangeTracker,)*
globals : sp::OnceCell<sp::Weak<SharedGlobals>>,
}

View file

@ -56,6 +56,8 @@ pub struct GlobalComponent {
pub functions: Vec<Function>,
/// One entry per property
pub init_values: Vec<Option<BindingExpression>>,
// maps property to its changed callback
pub change_callbacks: BTreeMap<usize, MutExpression>,
pub const_properties: Vec<bool>,
pub public_properties: PublicProperties,
pub private_properties: PrivateProperties,
@ -434,6 +436,9 @@ impl CompilationUnit {
for e in g.init_values.iter().filter_map(|x| x.as_ref()) {
visitor(&e.expression, &ctx)
}
for e in g.change_callbacks.values() {
visitor(e, &ctx)
}
}
}
}

View file

@ -795,6 +795,20 @@ fn lower_global(
});
}
let mut change_callbacks = BTreeMap::new();
for (prop, expr) in &global.root_element.borrow().change_callbacks {
let nr = NamedReference::new(&global.root_element, prop);
let property_index = match mapping.property_mapping[&nr] {
PropertyReference::Local { property_index, .. } => property_index,
_ => unreachable!(),
};
let expression = super::lower_expression::lower_expression(
&tree_Expression::CodeBlock(expr.borrow().clone()),
&ctx,
);
change_callbacks.insert(property_index, expression.into());
}
let is_builtin = if let Some(builtin) = global.root_element.borrow().native_class() {
// We just generate the property so we know how to address them
for (p, x) in &builtin.properties {
@ -828,6 +842,7 @@ fn lower_global(
properties,
functions,
init_values,
change_callbacks,
const_properties,
public_properties,
private_properties: global.private_properties.borrow().clone(),

View file

@ -154,6 +154,15 @@ impl<'a> PrettyPrinter<'a> {
if *is_const { " const" } else { "" }
)?;
}
for (p, e) in &global.change_callbacks {
self.indent()?;
writeln!(
self.writer,
"changed {} => {};",
global.properties[*p].name,
DisplayExpression(&e.borrow(), &ctx),
)?
}
for f in &global.functions {
self.indent()?;
writeln!(