Skip to main content

slint_interpreter/
item_holder.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4//! Blanket `ErasedItem` impl for any native item type.
5//!
6//! Items are stored as `Pin<Rc<dyn ErasedItem>>`.
7//! Trait methods look up `PropertyInfo`/`CallbackInfo` by name via `BuiltinItem`.
8
9use crate::Value;
10use crate::erased::{ErasedItem, ErasedItemRc};
11use i_slint_core::Property;
12use i_slint_core::items::{ItemVTable, PropertyAnimation};
13use i_slint_core::rtti::{
14    AnimatedBindingKind, BuiltinItem, CallbackInfo, PropertyInfo, TwoWayBindingMapping,
15};
16use std::pin::Pin;
17use std::rc::Rc;
18use vtable::{HasStaticVTable, VRef};
19
20fn find_property<T: 'static + BuiltinItem>(
21    name: &str,
22) -> Option<&'static dyn PropertyInfo<T, Value>> {
23    T::properties::<Value>().into_iter().find_map(|(n, info)| (n == name).then_some(info))
24}
25
26fn find_callback<T: 'static + BuiltinItem>(
27    name: &str,
28) -> Option<&'static dyn CallbackInfo<T, Value>> {
29    T::callbacks::<Value>().into_iter().find_map(|(n, info)| (n == name).then_some(info))
30}
31
32impl<T> ErasedItem for T
33where
34    T: 'static + BuiltinItem + HasStaticVTable<ItemVTable>,
35{
36    fn get_property(self: Pin<&Self>, name: &str) -> Result<Value, ()> {
37        find_property::<T>(name).ok_or(())?.get(self)
38    }
39
40    fn set_property(
41        self: Pin<&Self>,
42        name: &str,
43        value: Value,
44        animation: Option<PropertyAnimation>,
45    ) -> Result<(), ()> {
46        find_property::<T>(name).ok_or(())?.set(self, value, animation)
47    }
48
49    fn set_property_binding(
50        self: Pin<&Self>,
51        name: &str,
52        binding: Box<dyn Fn() -> Value>,
53        animation: AnimatedBindingKind,
54    ) -> Result<(), ()> {
55        find_property::<T>(name).ok_or(())?.set_binding(self, binding, animation)
56    }
57
58    fn prepare_property_for_two_way_binding(
59        self: Pin<&Self>,
60        name: &str,
61    ) -> Option<Pin<Rc<Property<Value>>>> {
62        Some(find_property::<T>(name)?.prepare_for_two_way_binding(self))
63    }
64
65    fn link_property_two_way_with_map(
66        self: Pin<&Self>,
67        name: &str,
68        other: Pin<Rc<Property<Value>>>,
69        mapper: Option<Rc<dyn TwoWayBindingMapping<Value>>>,
70    ) {
71        if let Some(info) = find_property::<T>(name) {
72            info.link_two_way_with_map(self, other, mapper);
73        }
74    }
75
76    fn call_callback(self: Pin<&Self>, name: &str, args: &[Value]) -> Result<Value, ()> {
77        find_callback::<T>(name).ok_or(())?.call(self, args)
78    }
79
80    fn set_callback_handler(
81        self: Pin<&Self>,
82        name: &str,
83        handler: Box<dyn Fn(&[Value]) -> Value>,
84    ) -> Result<(), ()> {
85        find_callback::<T>(name).ok_or(())?.set_handler(self, handler)
86    }
87
88    fn set_field(self: Pin<&Self>, _name: &str, _value: Value) -> Result<(), ()> {
89        // `FieldInfo::set_field` needs `&mut T`, which a `Pin<&Self>` can't give.
90        // Layouts write geometric fields via the corresponding `Property<Length>`,
91        // so this stays unused until a concrete caller forces the design.
92        Err(())
93    }
94
95    fn as_item_ref(self: Pin<&Self>) -> Pin<VRef<'_, ItemVTable>> {
96        VRef::new_pin(self)
97    }
98}
99
100/// Allocate a default-constructed item of type `T` as a boxed `ErasedItemRc`.
101pub fn make_item<T>() -> ErasedItemRc
102where
103    T: 'static + Default + BuiltinItem + HasStaticVTable<ItemVTable>,
104{
105    Rc::pin(T::default())
106}