slint/internal/interpreter/global_component.rs
Yuri Astrakhan 61de4d56b0 Fix all format arg inlining
Ran this command:

```shell
cargo clippy --fix -- -A clippy::all -W clippy::uninlined_format_args
```
2025-02-06 10:16:20 +01:00

366 lines
12 KiB
Rust

// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
use crate::api::Value;
use crate::dynamic_item_tree::{
ErasedItemTreeBox, ErasedItemTreeDescription, PopupMenuDescription,
};
use crate::SetPropertyError;
use core::cell::RefCell;
use core::pin::Pin;
use i_slint_compiler::langtype::ElementType;
use i_slint_compiler::namedreference::NamedReference;
use i_slint_compiler::object_tree::{Component, Document, PropertyDeclaration};
use i_slint_core::item_tree::ItemTreeVTable;
use i_slint_core::rtti;
use smol_str::SmolStr;
use std::collections::{BTreeMap, HashMap};
use std::rc::Rc;
pub struct CompiledGlobalCollection {
/// compiled globals
pub compiled_globals: Vec<CompiledGlobal>,
/// Map of all exported global singletons and their index in the compiled_globals vector. The key
/// is the normalized name of the global.
pub exported_globals_by_name: BTreeMap<SmolStr, usize>,
}
impl CompiledGlobalCollection {
pub fn compile(doc: &Document) -> Self {
let mut exported_globals_by_name = BTreeMap::new();
let compiled_globals = doc
.used_types
.borrow()
.globals
.iter()
.enumerate()
.map(|(index, component)| {
let mut global = generate(component);
if !component.exported_global_names.borrow().is_empty() {
global.extend_public_properties(
component.root_element.borrow().property_declarations.clone(),
);
exported_globals_by_name.extend(
component
.exported_global_names
.borrow()
.iter()
.map(|exported_name| (exported_name.name.clone(), index)),
)
}
global
})
.collect();
Self { compiled_globals, exported_globals_by_name }
}
}
#[derive(Clone)]
pub enum GlobalStorage {
Strong(Rc<RefCell<HashMap<String, Pin<Rc<dyn GlobalComponent>>>>>),
/// When the storage is held by another global
Weak(std::rc::Weak<RefCell<HashMap<String, Pin<Rc<dyn GlobalComponent>>>>>),
}
impl GlobalStorage {
pub fn get(&self, name: &str) -> Option<Pin<Rc<dyn GlobalComponent>>> {
match self {
GlobalStorage::Strong(storage) => storage.borrow().get(name).cloned(),
GlobalStorage::Weak(storage) => storage.upgrade().unwrap().borrow().get(name).cloned(),
}
}
}
impl Default for GlobalStorage {
fn default() -> Self {
GlobalStorage::Strong(Default::default())
}
}
pub enum CompiledGlobal {
Builtin {
name: SmolStr,
element: Rc<i_slint_compiler::langtype::BuiltinElement>,
// dummy needed for iterator accessor
public_properties: BTreeMap<SmolStr, PropertyDeclaration>,
/// keep the Component alive as it is boing referenced by `NamedReference`s
_original: Rc<Component>,
},
Component {
component: ErasedItemTreeDescription,
public_properties: BTreeMap<SmolStr, PropertyDeclaration>,
},
}
impl CompiledGlobal {
pub fn names(&self) -> Vec<SmolStr> {
match self {
CompiledGlobal::Builtin { name, .. } => vec![name.clone()],
CompiledGlobal::Component { component, .. } => {
generativity::make_guard!(guard);
let component = component.unerase(guard);
let mut names = component.original.global_aliases();
names.push(component.original.root_element.borrow().original_name());
names
}
}
}
pub fn visible_in_public_api(&self) -> bool {
match self {
CompiledGlobal::Builtin { .. } => false,
CompiledGlobal::Component { component, .. } => {
generativity::make_guard!(guard);
let component = component.unerase(guard);
let is_exported = !component.original.exported_global_names.borrow().is_empty();
is_exported
}
}
}
pub fn public_properties(&self) -> impl Iterator<Item = (&SmolStr, &PropertyDeclaration)> + '_ {
match self {
CompiledGlobal::Builtin { public_properties, .. } => public_properties.iter(),
CompiledGlobal::Component { public_properties, .. } => public_properties.iter(),
}
}
pub fn extend_public_properties(
&mut self,
iter: impl IntoIterator<Item = (SmolStr, PropertyDeclaration)>,
) {
match self {
CompiledGlobal::Builtin { public_properties, .. } => public_properties.extend(iter),
CompiledGlobal::Component { public_properties, .. } => public_properties.extend(iter),
}
}
}
pub trait GlobalComponent {
fn invoke_callback(
self: Pin<&Self>,
callback_name: &SmolStr,
args: &[Value],
) -> Result<Value, ()>;
fn set_callback_handler(
self: Pin<&Self>,
callback_name: &str,
handler: Box<dyn Fn(&[Value]) -> Value>,
) -> Result<(), ()>;
fn set_property(
self: Pin<&Self>,
prop_name: &str,
value: Value,
) -> Result<(), SetPropertyError>;
fn get_property(self: Pin<&Self>, prop_name: &str) -> Result<Value, ()>;
fn get_property_ptr(self: Pin<&Self>, prop_name: &SmolStr) -> *const ();
fn eval_function(self: Pin<&Self>, fn_name: &str, args: Vec<Value>) -> Result<Value, ()>;
}
/// Instantiate the global singleton and store it in `globals`
pub fn instantiate(
description: &CompiledGlobal,
globals: &mut GlobalStorage,
root: vtable::VWeak<ItemTreeVTable, ErasedItemTreeBox>,
) {
let GlobalStorage::Strong(ref mut globals) = globals else {
panic!("Global storage is not strong")
};
let instance = match description {
CompiledGlobal::Builtin { element, .. } => {
trait Helper {
fn instantiate(name: &str) -> Pin<Rc<dyn GlobalComponent>> {
panic!("Cannot find native global {name}")
}
}
impl Helper for () {}
impl<T: rtti::BuiltinGlobal + 'static, Next: Helper> Helper for (T, Next) {
fn instantiate(name: &str) -> Pin<Rc<dyn GlobalComponent>> {
if name == T::name() {
T::new()
} else {
Next::instantiate(name)
}
}
}
i_slint_backend_selector::NativeGlobals::instantiate(
element.native_class.class_name.as_ref(),
)
}
CompiledGlobal::Component { component, .. } => {
generativity::make_guard!(guard);
let description = component.unerase(guard);
let inst = crate::dynamic_item_tree::instantiate(
description.clone(),
None,
Some(root),
None,
GlobalStorage::Weak(Rc::downgrade(globals)),
);
inst.run_setup_code();
Rc::pin(GlobalComponentInstance(inst))
}
};
globals.borrow_mut().extend(
description
.names()
.iter()
.map(|name| (crate::normalize_identifier(name).to_string(), instance.clone())),
);
}
/// For the global components, we don't use the dynamic_type optimization,
/// and we don't try to optimize the property to their real type
pub struct GlobalComponentInstance(vtable::VRc<ItemTreeVTable, ErasedItemTreeBox>);
impl GlobalComponent for GlobalComponentInstance {
fn set_property(
self: Pin<&Self>,
prop_name: &str,
value: Value,
) -> Result<(), SetPropertyError> {
generativity::make_guard!(guard);
let comp = self.0.unerase(guard);
comp.description().set_property(comp.borrow(), prop_name, value)
}
fn get_property(self: Pin<&Self>, prop_name: &str) -> Result<Value, ()> {
generativity::make_guard!(guard);
let comp = self.0.unerase(guard);
comp.description().get_property(comp.borrow(), prop_name)
}
fn get_property_ptr(self: Pin<&Self>, prop_name: &SmolStr) -> *const () {
generativity::make_guard!(guard);
let comp = self.0.unerase(guard);
crate::dynamic_item_tree::get_property_ptr(
&NamedReference::new(&comp.description().original.root_element, prop_name.clone()),
comp.borrow_instance(),
)
}
fn invoke_callback(
self: Pin<&Self>,
callback_name: &SmolStr,
args: &[Value],
) -> Result<Value, ()> {
generativity::make_guard!(guard);
let comp = self.0.unerase(guard);
comp.description().invoke(comp.borrow(), callback_name, args)
}
fn set_callback_handler(
self: Pin<&Self>,
callback_name: &str,
handler: Box<dyn Fn(&[Value]) -> Value>,
) -> Result<(), ()> {
generativity::make_guard!(guard);
let comp = self.0.unerase(guard);
comp.description().set_callback_handler(comp.borrow(), callback_name, handler)
}
fn eval_function(self: Pin<&Self>, fn_name: &str, args: Vec<Value>) -> Result<Value, ()> {
generativity::make_guard!(guard);
let comp = self.0.unerase(guard);
let mut ctx =
crate::eval::EvalLocalContext::from_function_arguments(comp.borrow_instance(), args);
let result = crate::eval::eval_expression(
&comp
.description()
.original
.root_element
.borrow()
.bindings
.get(fn_name)
.ok_or(())?
.borrow()
.expression,
&mut ctx,
);
Ok(result)
}
}
impl<T: rtti::BuiltinItem + 'static> GlobalComponent for T {
fn set_property(
self: Pin<&Self>,
prop_name: &str,
value: Value,
) -> Result<(), SetPropertyError> {
let prop = Self::properties()
.into_iter()
.find(|(k, _)| *k == prop_name)
.ok_or(SetPropertyError::NoSuchProperty)?
.1;
prop.set(self, value, None).map_err(|()| SetPropertyError::WrongType)
}
fn get_property(self: Pin<&Self>, prop_name: &str) -> Result<Value, ()> {
let prop = Self::properties().into_iter().find(|(k, _)| *k == prop_name).ok_or(())?.1;
prop.get(self)
}
fn get_property_ptr(self: Pin<&Self>, prop_name: &SmolStr) -> *const () {
let prop: &dyn rtti::PropertyInfo<Self, Value> =
Self::properties().into_iter().find(|(k, _)| *k == prop_name).unwrap().1;
unsafe { (self.get_ref() as *const Self as *const u8).add(prop.offset()) as *const () }
}
fn invoke_callback(
self: Pin<&Self>,
callback_name: &SmolStr,
args: &[Value],
) -> Result<Value, ()> {
let cb = Self::callbacks().into_iter().find(|(k, _)| *k == callback_name).ok_or(())?.1;
cb.call(self, args)
}
fn set_callback_handler(
self: Pin<&Self>,
callback_name: &str,
handler: Box<dyn Fn(&[Value]) -> Value>,
) -> Result<(), ()> {
let cb = Self::callbacks().into_iter().find(|(k, _)| *k == callback_name).ok_or(())?.1;
cb.set_handler(self, handler)
}
fn eval_function(self: Pin<&Self>, _fn_name: &str, _args: Vec<Value>) -> Result<Value, ()> {
Err(())
}
}
fn generate(component: &Rc<Component>) -> CompiledGlobal {
debug_assert!(component.is_global());
match &component.root_element.borrow().base_type {
ElementType::Global => {
generativity::make_guard!(guard);
CompiledGlobal::Component {
component: crate::dynamic_item_tree::generate_item_tree(
component,
None,
PopupMenuDescription::Weak(Default::default()),
false,
guard,
)
.into(),
public_properties: Default::default(),
}
}
ElementType::Builtin(b) => CompiledGlobal::Builtin {
name: component.id.clone(),
element: b.clone(),
public_properties: Default::default(),
_original: component.clone(),
},
ElementType::Error | ElementType::Native(_) | ElementType::Component(_) => unreachable!(),
}
}