Skip to main content

slint_interpreter/
api.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
4use crate::dynamic_item_tree::{ErasedItemTreeBox, WindowOptions};
5use i_slint_compiler::langtype::Type as LangType;
6use i_slint_core::PathData;
7use i_slint_core::component_factory::ComponentFactory;
8#[cfg(feature = "internal")]
9use i_slint_core::component_factory::FactoryContext;
10use i_slint_core::graphics::euclid::approxeq::ApproxEq as _;
11use i_slint_core::items::*;
12use i_slint_core::model::{Model, ModelExt, ModelRc};
13use i_slint_core::styled_text::StyledText;
14#[cfg(feature = "internal")]
15use i_slint_core::window::WindowInner;
16use smol_str::SmolStr;
17use std::collections::HashMap;
18use std::future::Future;
19use std::path::{Path, PathBuf};
20use std::rc::Rc;
21
22#[doc(inline)]
23pub use i_slint_compiler::diagnostics::{Diagnostic, DiagnosticLevel};
24
25pub use i_slint_backend_selector::api::*;
26pub use i_slint_core::api::*;
27
28/// Argument of [`Compiler::set_default_translation_context()`]
29///
30pub use i_slint_compiler::DefaultTranslationContext;
31
32/// This enum represents the different public variants of the [`Value`] enum, without
33/// the contained values.
34#[derive(Debug, Copy, Clone, PartialEq)]
35#[repr(i8)]
36#[non_exhaustive]
37pub enum ValueType {
38    /// The variant that expresses the non-type. This is the default.
39    Void,
40    /// An `int` or a `float` (this is also used for unit based type such as `length` or `angle`)
41    Number,
42    /// Correspond to the `string` type in .slint
43    String,
44    /// Correspond to the `bool` type in .slint
45    Bool,
46    /// A model (that includes array in .slint)
47    Model,
48    /// An object
49    Struct,
50    /// Correspond to `brush` or `color` type in .slint.  For color, this is then a [`Brush::SolidColor`]
51    Brush,
52    /// Correspond to `image` type in .slint.
53    Image,
54    /// The type is not a public type but something internal.
55    #[doc(hidden)]
56    Other = -1,
57}
58
59impl From<LangType> for ValueType {
60    fn from(ty: LangType) -> Self {
61        match ty {
62            LangType::Float32
63            | LangType::Int32
64            | LangType::Duration
65            | LangType::Angle
66            | LangType::PhysicalLength
67            | LangType::LogicalLength
68            | LangType::Percent
69            | LangType::UnitProduct(_) => Self::Number,
70            LangType::String => Self::String,
71            LangType::Color => Self::Brush,
72            LangType::Brush => Self::Brush,
73            LangType::Array(_) => Self::Model,
74            LangType::Bool => Self::Bool,
75            LangType::Struct { .. } => Self::Struct,
76            LangType::Void => Self::Void,
77            LangType::Image => Self::Image,
78            _ => Self::Other,
79        }
80    }
81}
82
83/// This is a dynamically typed value used in the Slint interpreter.
84/// It can hold a value of different types, and you should use the
85/// [`From`] or [`TryFrom`] traits to access the value.
86///
87/// ```
88/// # use slint_interpreter::*;
89/// use core::convert::TryInto;
90/// // create a value containing an integer
91/// let v = Value::from(100u32);
92/// assert_eq!(v.try_into(), Ok(100u32));
93/// ```
94#[derive(Clone, Default)]
95#[non_exhaustive]
96#[repr(u8)]
97pub enum Value {
98    /// There is nothing in this value. That's the default.
99    /// For example, a function that does not return a result would return a Value::Void
100    #[default]
101    Void = 0,
102    /// An `int` or a `float` (this is also used for unit based type such as `length` or `angle`)
103    Number(f64) = 1,
104    /// Correspond to the `string` type in .slint
105    String(SharedString) = 2,
106    /// Correspond to the `bool` type in .slint
107    Bool(bool) = 3,
108    /// Correspond to the `image` type in .slint
109    Image(Image) = 4,
110    /// A model (that includes array in .slint)
111    Model(ModelRc<Value>) = 5,
112    /// An object
113    Struct(Struct) = 6,
114    /// Correspond to `brush` or `color` type in .slint.  For color, this is then a [`Brush::SolidColor`]
115    Brush(Brush) = 7,
116    #[doc(hidden)]
117    /// The elements of a path
118    PathData(PathData) = 8,
119    #[doc(hidden)]
120    /// An easing curve
121    EasingCurve(i_slint_core::animations::EasingCurve) = 9,
122    #[doc(hidden)]
123    /// An enumeration, like `TextHorizontalAlignment::align_center`, represented by `("TextHorizontalAlignment", "align_center")`.
124    /// FIXME: consider representing that with a number?
125    EnumerationValue(String, String) = 10,
126    #[doc(hidden)]
127    LayoutCache(SharedVector<f32>) = 11,
128    #[doc(hidden)]
129    /// Correspond to the `component-factory` type in .slint
130    ComponentFactory(ComponentFactory) = 12,
131    #[doc(hidden)] // make visible when we make StyledText public
132    /// Correspond to the `styled-text` type in .slint
133    StyledText(StyledText) = 13,
134    #[doc(hidden)]
135    ArrayOfU16(SharedVector<u16>) = 14,
136    /// Correspond to the `keys` type in .slint
137    Keys(Keys) = 15,
138    /// Correspond to the `data-transfer` type in .slint
139    DataTransfer(DataTransfer) = 16,
140}
141
142impl Value {
143    /// Returns the type variant that this value holds without the containing value.
144    pub fn value_type(&self) -> ValueType {
145        match self {
146            Value::Void => ValueType::Void,
147            Value::Number(_) => ValueType::Number,
148            Value::String(_) => ValueType::String,
149            Value::Bool(_) => ValueType::Bool,
150            Value::Model(_) => ValueType::Model,
151            Value::Struct(_) => ValueType::Struct,
152            Value::Brush(_) => ValueType::Brush,
153            Value::Image(_) => ValueType::Image,
154            _ => ValueType::Other,
155        }
156    }
157}
158
159impl PartialEq for Value {
160    fn eq(&self, other: &Self) -> bool {
161        match self {
162            Value::Void => matches!(other, Value::Void),
163            Value::Number(lhs) => matches!(other, Value::Number(rhs) if lhs.approx_eq(rhs)),
164            Value::String(lhs) => matches!(other, Value::String(rhs) if lhs == rhs),
165            Value::Bool(lhs) => matches!(other, Value::Bool(rhs) if lhs == rhs),
166            Value::Image(lhs) => matches!(other, Value::Image(rhs) if lhs == rhs),
167            Value::Model(lhs) => {
168                if let Value::Model(rhs) = other {
169                    lhs == rhs
170                } else {
171                    false
172                }
173            }
174            Value::Struct(lhs) => matches!(other, Value::Struct(rhs) if lhs == rhs),
175            Value::Brush(lhs) => matches!(other, Value::Brush(rhs) if lhs == rhs),
176            Value::PathData(lhs) => matches!(other, Value::PathData(rhs) if lhs == rhs),
177            Value::EasingCurve(lhs) => matches!(other, Value::EasingCurve(rhs) if lhs == rhs),
178            Value::EnumerationValue(lhs_name, lhs_value) => {
179                matches!(other, Value::EnumerationValue(rhs_name, rhs_value) if lhs_name == rhs_name && lhs_value == rhs_value)
180            }
181            Value::LayoutCache(lhs) => matches!(other, Value::LayoutCache(rhs) if lhs == rhs),
182            Value::ArrayOfU16(lhs) => matches!(other, Value::ArrayOfU16(rhs) if lhs == rhs),
183            Value::ComponentFactory(lhs) => {
184                matches!(other, Value::ComponentFactory(rhs) if lhs == rhs)
185            }
186            Value::StyledText(lhs) => {
187                matches!(other, Value::StyledText(rhs) if lhs == rhs)
188            }
189            Value::Keys(lhs) => {
190                matches!(other, Value::Keys(rhs) if lhs == rhs)
191            }
192            Value::DataTransfer(lhs) => {
193                matches!(other, Value::DataTransfer(rhs) if lhs == rhs)
194            }
195        }
196    }
197}
198
199impl std::fmt::Debug for Value {
200    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
201        match self {
202            Value::Void => write!(f, "Value::Void"),
203            Value::Number(n) => write!(f, "Value::Number({n:?})"),
204            Value::String(s) => write!(f, "Value::String({s:?})"),
205            Value::Bool(b) => write!(f, "Value::Bool({b:?})"),
206            Value::Image(i) => write!(f, "Value::Image({i:?})"),
207            Value::Model(m) => {
208                write!(f, "Value::Model(")?;
209                f.debug_list().entries(m.iter()).finish()?;
210                write!(f, "])")
211            }
212            Value::Struct(s) => write!(f, "Value::Struct({s:?})"),
213            Value::Brush(b) => write!(f, "Value::Brush({b:?})"),
214            Value::PathData(e) => write!(f, "Value::PathElements({e:?})"),
215            Value::EasingCurve(c) => write!(f, "Value::EasingCurve({c:?})"),
216            Value::EnumerationValue(n, v) => write!(f, "Value::EnumerationValue({n:?}, {v:?})"),
217            Value::LayoutCache(v) => write!(f, "Value::LayoutCache({v:?})"),
218            Value::ComponentFactory(factory) => write!(f, "Value::ComponentFactory({factory:?})"),
219            Value::StyledText(text) => write!(f, "Value::StyledText({text:?})"),
220            Value::ArrayOfU16(data) => {
221                write!(f, "Value::ArrayOfU16({data:?})")
222            }
223            Value::Keys(ks) => write!(f, "Value::Keys({ks:?})"),
224            Value::DataTransfer(cd) => write!(f, "Value::DataTransfer({cd:?})"),
225        }
226    }
227}
228
229/// Helper macro to implement the From / TryFrom for Value
230///
231/// For example
232/// `declare_value_conversion!(Number => [u32, u64, i32, i64, f32, f64] );`
233/// means that `Value::Number` can be converted to / from each of the said rust types
234///
235/// For `Value::Object` mapping to a rust `struct`, one can use [`declare_value_struct_conversion!`]
236/// And for `Value::EnumerationValue` which maps to a rust `enum`, one can use [`declare_value_enum_conversion!`]
237macro_rules! declare_value_conversion {
238    ( $value:ident => [$($ty:ty),*] ) => {
239        $(
240            impl From<$ty> for Value {
241                fn from(v: $ty) -> Self {
242                    Value::$value(v as _)
243                }
244            }
245            impl TryFrom<Value> for $ty {
246                type Error = Value;
247                fn try_from(v: Value) -> Result<$ty, Self::Error> {
248                    match v {
249                        Value::$value(x) => Ok(x as _),
250                        _ => Err(v)
251                    }
252                }
253            }
254        )*
255    };
256}
257declare_value_conversion!(Number => [u32, u64, i32, i64, f32, f64, usize, isize] );
258declare_value_conversion!(String => [SharedString] );
259declare_value_conversion!(Bool => [bool] );
260declare_value_conversion!(Image => [Image] );
261declare_value_conversion!(Struct => [Struct] );
262declare_value_conversion!(Brush => [Brush] );
263declare_value_conversion!(PathData => [PathData]);
264declare_value_conversion!(EasingCurve => [i_slint_core::animations::EasingCurve]);
265declare_value_conversion!(LayoutCache => [SharedVector<f32>] );
266declare_value_conversion!(ComponentFactory => [ComponentFactory] );
267declare_value_conversion!(StyledText => [StyledText] );
268declare_value_conversion!(ArrayOfU16 => [SharedVector<u16>] );
269declare_value_conversion!(Keys => [Keys]);
270declare_value_conversion!(DataTransfer => [DataTransfer]);
271
272/// Implement From / TryFrom for Value that convert a `struct` to/from `Value::Struct`
273macro_rules! declare_value_struct_conversion {
274    (struct $name:path { $($field:ident),* $(, ..$extra:expr)? }) => {
275        impl From<$name> for Value {
276            fn from($name { $($field),* , .. }: $name) -> Self {
277                let mut struct_ = Struct::default();
278                $(struct_.set_field(stringify!($field).into(), $field.into());)*
279                Value::Struct(struct_)
280            }
281        }
282        impl TryFrom<Value> for $name {
283            type Error = ();
284            fn try_from(v: Value) -> Result<$name, Self::Error> {
285                #[allow(clippy::field_reassign_with_default)]
286                match v {
287                    Value::Struct(x) => {
288                        type Ty = $name;
289                        #[allow(unused)]
290                        let mut res: Ty = Ty::default();
291                        $(let mut res: Ty = $extra;)?
292                        $(res.$field = x.get_field(stringify!($field)).ok_or(())?.clone().try_into().map_err(|_|())?;)*
293                        Ok(res)
294                    }
295                    _ => Err(()),
296                }
297            }
298        }
299    };
300    ($(
301        $(#[$struct_attr:meta])*
302        struct $Name:ident {
303            @name = $inner_name:expr,
304            export {
305                $( $(#[$pub_attr:meta])* $pub_field:ident : $pub_type:ty, )*
306            }
307            private { $($pri:tt)* }
308        }
309    )*) => {
310        $(
311            impl From<$Name> for Value {
312                fn from(item: $Name) -> Self {
313                    let mut struct_ = Struct::default();
314                    $(struct_.set_field(stringify!($pub_field).into(), item.$pub_field.into());)*
315                    Value::Struct(struct_)
316                }
317            }
318            impl TryFrom<Value> for $Name {
319                type Error = ();
320                fn try_from(v: Value) -> Result<$Name, Self::Error> {
321                    #[allow(clippy::field_reassign_with_default)]
322                    match v {
323                        Value::Struct(x) => {
324                            type Ty = $Name;
325                            #[allow(unused)]
326                            let mut res: Ty = Ty::default();
327                            $(res.$pub_field = x.get_field(stringify!($pub_field)).ok_or(())?.clone().try_into().map_err(|_|())?;)*
328                            Ok(res)
329                        }
330                        _ => Err(()),
331                    }
332                }
333            }
334        )*
335    };
336}
337
338declare_value_struct_conversion!(struct i_slint_core::layout::LayoutInfo { min, max, min_percent, max_percent, preferred, stretch });
339declare_value_struct_conversion!(struct i_slint_core::graphics::Point { x, y, ..Default::default()});
340declare_value_struct_conversion!(struct i_slint_core::api::LogicalPosition { x, y });
341declare_value_struct_conversion!(struct i_slint_core::properties::StateInfo { current_state, previous_state, change_time });
342
343i_slint_common::for_each_builtin_structs!(declare_value_struct_conversion);
344
345/// Implement From / TryFrom for Value that convert an `enum` to/from `Value::EnumerationValue`
346///
347/// The `enum` must derive `Display` and `FromStr`
348/// (can be done with `strum_macros::EnumString`, `strum_macros::Display` derive macro)
349macro_rules! declare_value_enum_conversion {
350    ($( $(#[$enum_doc:meta])* enum $Name:ident { $($body:tt)* })*) => { $(
351        impl From<i_slint_core::items::$Name> for Value {
352            fn from(v: i_slint_core::items::$Name) -> Self {
353                Value::EnumerationValue(stringify!($Name).to_owned(), v.to_string())
354            }
355        }
356        impl TryFrom<Value> for i_slint_core::items::$Name {
357            type Error = ();
358            fn try_from(v: Value) -> Result<i_slint_core::items::$Name, ()> {
359                use std::str::FromStr;
360                match v {
361                    Value::EnumerationValue(enumeration, value) => {
362                        if enumeration != stringify!($Name) {
363                            return Err(());
364                        }
365                        i_slint_core::items::$Name::from_str(value.as_str()).map_err(|_| ())
366                    }
367                    _ => Err(()),
368                }
369            }
370        }
371    )*};
372}
373
374i_slint_common::for_each_enums!(declare_value_enum_conversion);
375
376impl From<i_slint_core::animations::Instant> for Value {
377    fn from(value: i_slint_core::animations::Instant) -> Self {
378        Value::Number(value.0 as _)
379    }
380}
381impl TryFrom<Value> for i_slint_core::animations::Instant {
382    type Error = ();
383    fn try_from(v: Value) -> Result<i_slint_core::animations::Instant, Self::Error> {
384        match v {
385            Value::Number(x) => Ok(i_slint_core::animations::Instant(x as _)),
386            _ => Err(()),
387        }
388    }
389}
390
391impl From<()> for Value {
392    #[inline]
393    fn from(_: ()) -> Self {
394        Value::Void
395    }
396}
397impl TryFrom<Value> for () {
398    type Error = ();
399    #[inline]
400    fn try_from(_: Value) -> Result<(), Self::Error> {
401        Ok(())
402    }
403}
404
405impl From<Color> for Value {
406    #[inline]
407    fn from(c: Color) -> Self {
408        Value::Brush(Brush::SolidColor(c))
409    }
410}
411impl TryFrom<Value> for Color {
412    type Error = Value;
413    #[inline]
414    fn try_from(v: Value) -> Result<Color, Self::Error> {
415        match v {
416            Value::Brush(Brush::SolidColor(c)) => Ok(c),
417            _ => Err(v),
418        }
419    }
420}
421
422impl From<i_slint_core::lengths::LogicalLength> for Value {
423    #[inline]
424    fn from(l: i_slint_core::lengths::LogicalLength) -> Self {
425        Value::Number(l.get() as _)
426    }
427}
428impl TryFrom<Value> for i_slint_core::lengths::LogicalLength {
429    type Error = Value;
430    #[inline]
431    fn try_from(v: Value) -> Result<i_slint_core::lengths::LogicalLength, Self::Error> {
432        match v {
433            Value::Number(n) => Ok(i_slint_core::lengths::LogicalLength::new(n as _)),
434            _ => Err(v),
435        }
436    }
437}
438
439impl From<i_slint_core::lengths::LogicalPoint> for Value {
440    #[inline]
441    fn from(pt: i_slint_core::lengths::LogicalPoint) -> Self {
442        Value::Struct(Struct::from_iter([
443            ("x".to_owned(), Value::Number(pt.x as _)),
444            ("y".to_owned(), Value::Number(pt.y as _)),
445        ]))
446    }
447}
448impl TryFrom<Value> for i_slint_core::lengths::LogicalPoint {
449    type Error = Value;
450    #[inline]
451    fn try_from(v: Value) -> Result<i_slint_core::lengths::LogicalPoint, Self::Error> {
452        match v {
453            Value::Struct(s) => {
454                let x = s
455                    .get_field("x")
456                    .cloned()
457                    .unwrap_or_else(|| Value::Number(0 as _))
458                    .try_into()?;
459                let y = s
460                    .get_field("y")
461                    .cloned()
462                    .unwrap_or_else(|| Value::Number(0 as _))
463                    .try_into()?;
464                Ok(i_slint_core::lengths::LogicalPoint::new(x, y))
465            }
466            _ => Err(v),
467        }
468    }
469}
470
471impl From<i_slint_core::lengths::LogicalSize> for Value {
472    #[inline]
473    fn from(s: i_slint_core::lengths::LogicalSize) -> Self {
474        Value::Struct(Struct::from_iter([
475            ("width".to_owned(), Value::Number(s.width as _)),
476            ("height".to_owned(), Value::Number(s.height as _)),
477        ]))
478    }
479}
480impl TryFrom<Value> for i_slint_core::lengths::LogicalSize {
481    type Error = Value;
482    #[inline]
483    fn try_from(v: Value) -> Result<i_slint_core::lengths::LogicalSize, Self::Error> {
484        match v {
485            Value::Struct(s) => {
486                let width = s
487                    .get_field("width")
488                    .cloned()
489                    .unwrap_or_else(|| Value::Number(0 as _))
490                    .try_into()?;
491                let height = s
492                    .get_field("height")
493                    .cloned()
494                    .unwrap_or_else(|| Value::Number(0 as _))
495                    .try_into()?;
496                Ok(i_slint_core::lengths::LogicalSize::new(width, height))
497            }
498            _ => Err(v),
499        }
500    }
501}
502
503impl From<i_slint_core::lengths::LogicalEdges> for Value {
504    #[inline]
505    fn from(s: i_slint_core::lengths::LogicalEdges) -> Self {
506        Value::Struct(Struct::from_iter([
507            ("left".to_owned(), Value::Number(s.left as _)),
508            ("right".to_owned(), Value::Number(s.right as _)),
509            ("top".to_owned(), Value::Number(s.top as _)),
510            ("bottom".to_owned(), Value::Number(s.bottom as _)),
511        ]))
512    }
513}
514impl TryFrom<Value> for i_slint_core::lengths::LogicalEdges {
515    type Error = Value;
516    #[inline]
517    fn try_from(v: Value) -> Result<i_slint_core::lengths::LogicalEdges, Self::Error> {
518        match v {
519            Value::Struct(s) => {
520                let left = s
521                    .get_field("left")
522                    .cloned()
523                    .unwrap_or_else(|| Value::Number(0 as _))
524                    .try_into()?;
525                let right = s
526                    .get_field("right")
527                    .cloned()
528                    .unwrap_or_else(|| Value::Number(0 as _))
529                    .try_into()?;
530                let top = s
531                    .get_field("top")
532                    .cloned()
533                    .unwrap_or_else(|| Value::Number(0 as _))
534                    .try_into()?;
535                let bottom = s
536                    .get_field("bottom")
537                    .cloned()
538                    .unwrap_or_else(|| Value::Number(0 as _))
539                    .try_into()?;
540                Ok(i_slint_core::lengths::LogicalEdges::new(left, right, top, bottom))
541            }
542            _ => Err(v),
543        }
544    }
545}
546
547impl<T: Into<Value> + TryFrom<Value> + 'static> From<ModelRc<T>> for Value {
548    fn from(m: ModelRc<T>) -> Self {
549        if let Some(v) = <dyn core::any::Any>::downcast_ref::<ModelRc<Value>>(&m) {
550            Value::Model(v.clone())
551        } else {
552            Value::Model(ModelRc::new(crate::value_model::ValueMapModel(m)))
553        }
554    }
555}
556impl<T: TryFrom<Value> + Default + 'static> TryFrom<Value> for ModelRc<T> {
557    type Error = Value;
558    #[inline]
559    fn try_from(v: Value) -> Result<ModelRc<T>, Self::Error> {
560        match v {
561            Value::Model(m) => {
562                if let Some(v) = <dyn core::any::Any>::downcast_ref::<ModelRc<T>>(&m) {
563                    Ok(v.clone())
564                } else if let Some(v) =
565                    m.as_any().downcast_ref::<crate::value_model::ValueMapModel<T>>()
566                {
567                    Ok(v.0.clone())
568                } else {
569                    Ok(ModelRc::new(m.map(|v| T::try_from(v).unwrap_or_default())))
570                }
571            }
572            _ => Err(v),
573        }
574    }
575}
576
577#[test]
578fn value_model_conversion() {
579    use i_slint_core::model::*;
580    let m = ModelRc::new(VecModel::from_slice(&[Value::Number(42.), Value::Number(12.)]));
581    let v = Value::from(m.clone());
582    assert_eq!(v, Value::Model(m.clone()));
583    let m2: ModelRc<Value> = v.clone().try_into().unwrap();
584    assert_eq!(m2, m);
585
586    let int_model: ModelRc<i32> = v.clone().try_into().unwrap();
587    assert_eq!(int_model.row_count(), 2);
588    assert_eq!(int_model.iter().collect::<Vec<_>>(), vec![42, 12]);
589
590    let Value::Model(m3) = int_model.clone().into() else { panic!("not a model?") };
591    assert_eq!(m3.row_count(), 2);
592    assert_eq!(m3.iter().collect::<Vec<_>>(), vec![Value::Number(42.), Value::Number(12.)]);
593
594    let str_model: ModelRc<SharedString> = v.clone().try_into().unwrap();
595    assert_eq!(str_model.row_count(), 2);
596    // Value::Int doesn't convert to string, but since the mapping can't report error, we get the default constructed string
597    assert_eq!(str_model.iter().collect::<Vec<_>>(), vec!["", ""]);
598
599    let err: Result<ModelRc<Value>, _> = Value::Bool(true).try_into();
600    assert!(err.is_err());
601
602    let model =
603        Rc::new(VecModel::<SharedString>::from_iter(["foo".into(), "bar".into(), "baz".into()]));
604
605    let value: Value = ModelRc::from(model.clone()).into();
606    let value_model: ModelRc<Value> = value.clone().try_into().unwrap();
607    assert_eq!(value_model.row_data(2).unwrap(), Value::String("baz".into()));
608    value_model.set_row_data(1, Value::String("qux".into()));
609    value_model.set_row_data(0, Value::Bool(true));
610    assert_eq!(value_model.row_data(1).unwrap(), Value::String("qux".into()));
611    // This is backed by a string model, so changing to bool has no effect
612    assert_eq!(value_model.row_data(0).unwrap(), Value::String("foo".into()));
613
614    // The original values are changed
615    assert_eq!(model.row_data(1).unwrap(), SharedString::from("qux"));
616    assert_eq!(model.row_data(0).unwrap(), SharedString::from("foo"));
617
618    let the_model: ModelRc<SharedString> = value.try_into().unwrap();
619    assert_eq!(the_model.row_data(1).unwrap(), SharedString::from("qux"));
620    assert_eq!(
621        model.as_ref() as *const VecModel<SharedString>,
622        the_model.as_any().downcast_ref::<VecModel<SharedString>>().unwrap()
623            as *const VecModel<SharedString>
624    );
625}
626
627pub(crate) fn normalize_identifier(ident: &str) -> SmolStr {
628    i_slint_compiler::parser::normalize_identifier(ident)
629}
630
631/// This type represents a runtime instance of structure in `.slint`.
632///
633/// This can either be an instance of a name structure introduced
634/// with the `struct` keyword in the .slint file, or an anonymous struct
635/// written with the `{ key: value, }`  notation.
636///
637/// It can be constructed with the [`FromIterator`] trait, and converted
638/// into or from a [`Value`] with the [`From`], [`TryFrom`] trait
639///
640///
641/// ```
642/// # use slint_interpreter::*;
643/// use core::convert::TryInto;
644/// // Construct a value from a key/value iterator
645/// let value : Value = [("foo".into(), 45u32.into()), ("bar".into(), true.into())]
646///     .iter().cloned().collect::<Struct>().into();
647///
648/// // get the properties of a `{ foo: 45, bar: true }`
649/// let s : Struct = value.try_into().unwrap();
650/// assert_eq!(s.get_field("foo").cloned().unwrap().try_into(), Ok(45u32));
651/// ```
652#[derive(Clone, PartialEq, Debug, Default)]
653pub struct Struct(pub(crate) HashMap<SmolStr, Value>);
654impl Struct {
655    /// Get the value for a given struct field
656    pub fn get_field(&self, name: &str) -> Option<&Value> {
657        self.0.get(&*normalize_identifier(name))
658    }
659    /// Set the value of a given struct field
660    pub fn set_field(&mut self, name: String, value: Value) {
661        self.0.insert(normalize_identifier(&name), value);
662    }
663
664    /// Iterate over all the fields in this struct
665    pub fn iter(&self) -> impl Iterator<Item = (&str, &Value)> {
666        self.0.iter().map(|(a, b)| (a.as_str(), b))
667    }
668}
669
670impl FromIterator<(String, Value)> for Struct {
671    fn from_iter<T: IntoIterator<Item = (String, Value)>>(iter: T) -> Self {
672        Self(iter.into_iter().map(|(s, v)| (normalize_identifier(&s), v)).collect())
673    }
674}
675
676/// ComponentCompiler is deprecated, use [`Compiler`] instead
677#[deprecated(note = "Use slint_interpreter::Compiler instead")]
678pub struct ComponentCompiler {
679    config: i_slint_compiler::CompilerConfiguration,
680    diagnostics: Vec<Diagnostic>,
681}
682
683#[allow(deprecated)]
684impl Default for ComponentCompiler {
685    fn default() -> Self {
686        let mut config = i_slint_compiler::CompilerConfiguration::new(
687            i_slint_compiler::generator::OutputFormat::Interpreter,
688        );
689        config.components_to_generate = i_slint_compiler::ComponentSelection::LastExported;
690        Self { config, diagnostics: Vec::new() }
691    }
692}
693
694#[allow(deprecated)]
695impl ComponentCompiler {
696    /// Returns a new ComponentCompiler.
697    pub fn new() -> Self {
698        Self::default()
699    }
700
701    /// Sets the include paths used for looking up `.slint` imports to the specified vector of paths.
702    pub fn set_include_paths(&mut self, include_paths: Vec<std::path::PathBuf>) {
703        self.config.include_paths = include_paths;
704    }
705
706    /// Returns the include paths the component compiler is currently configured with.
707    pub fn include_paths(&self) -> &Vec<std::path::PathBuf> {
708        &self.config.include_paths
709    }
710
711    /// Sets the library paths used for looking up `@library` imports to the specified map of library names to paths.
712    pub fn set_library_paths(&mut self, library_paths: HashMap<String, PathBuf>) {
713        self.config.library_paths = library_paths;
714    }
715
716    /// Returns the library paths the component compiler is currently configured with.
717    pub fn library_paths(&self) -> &HashMap<String, PathBuf> {
718        &self.config.library_paths
719    }
720
721    /// Sets the style to be used for widgets.
722    ///
723    /// Use the "material" style as widget style when compiling:
724    /// ```rust
725    /// use slint_interpreter::{ComponentDefinition, ComponentCompiler, ComponentHandle};
726    ///
727    /// let mut compiler = ComponentCompiler::default();
728    /// compiler.set_style("material".into());
729    /// let definition =
730    ///     spin_on::spin_on(compiler.build_from_path("hello.slint"));
731    /// ```
732    pub fn set_style(&mut self, style: String) {
733        self.config.style = Some(style);
734    }
735
736    /// Returns the widget style the compiler is currently using when compiling .slint files.
737    pub fn style(&self) -> Option<&String> {
738        self.config.style.as_ref()
739    }
740
741    /// The domain used for translations
742    pub fn set_translation_domain(&mut self, domain: String) {
743        self.config.translation_domain = Some(domain);
744    }
745
746    /// Sets the callback that will be invoked when loading imported .slint files. The specified
747    /// `file_loader_callback` parameter will be called with a canonical file path as argument
748    /// and is expected to return a future that, when resolved, provides the source code of the
749    /// .slint file to be imported as a string.
750    /// If an error is returned, then the build will abort with that error.
751    /// If None is returned, it means the normal resolution algorithm will proceed as if the hook
752    /// was not in place (i.e: load from the file system following the include paths)
753    pub fn set_file_loader(
754        &mut self,
755        file_loader_fallback: impl Fn(
756            &Path,
757        ) -> core::pin::Pin<
758            Box<dyn Future<Output = Option<std::io::Result<String>>>>,
759        > + 'static,
760    ) {
761        self.config.open_import_callback =
762            Some(Rc::new(move |path| file_loader_fallback(Path::new(path.as_str()))));
763    }
764
765    /// Returns the diagnostics that were produced in the last call to [`Self::build_from_path`] or [`Self::build_from_source`].
766    pub fn diagnostics(&self) -> &Vec<Diagnostic> {
767        &self.diagnostics
768    }
769
770    /// Compile a .slint file into a ComponentDefinition
771    ///
772    /// Returns the compiled `ComponentDefinition` if there were no errors.
773    ///
774    /// Any diagnostics produced during the compilation, such as warnings or errors, are collected
775    /// in this ComponentCompiler and can be retrieved after the call using the [`Self::diagnostics()`]
776    /// function. The [`print_diagnostics`] function can be used to display the diagnostics
777    /// to the users.
778    ///
779    /// Diagnostics from previous calls are cleared when calling this function.
780    ///
781    /// If the path is `"-"`, the file will be read from stdin.
782    /// If the extension of the file .rs, the first `slint!` macro from a rust file will be extracted
783    ///
784    /// This function is `async` but in practice, this is only asynchronous if
785    /// [`Self::set_file_loader`] was called and its future is actually asynchronous.
786    /// If that is not used, then it is fine to use a very simple executor, such as the one
787    /// provided by the `spin_on` crate
788    pub async fn build_from_path<P: AsRef<Path>>(
789        &mut self,
790        path: P,
791    ) -> Option<ComponentDefinition> {
792        let path = path.as_ref();
793        let source = match i_slint_compiler::diagnostics::load_from_path(path) {
794            Ok(s) => s,
795            Err(d) => {
796                self.diagnostics = vec![d];
797                return None;
798            }
799        };
800
801        let r = crate::dynamic_item_tree::load(source, path.into(), self.config.clone()).await;
802        self.diagnostics = r.diagnostics.into_iter().collect();
803        r.components.into_values().next()
804    }
805
806    /// Compile some .slint code into a ComponentDefinition
807    ///
808    /// The `path` argument will be used for diagnostics and to compute relative
809    /// paths while importing.
810    ///
811    /// Any diagnostics produced during the compilation, such as warnings or errors, are collected
812    /// in this ComponentCompiler and can be retrieved after the call using the [`Self::diagnostics()`]
813    /// function. The [`print_diagnostics`] function can be used to display the diagnostics
814    /// to the users.
815    ///
816    /// Diagnostics from previous calls are cleared when calling this function.
817    ///
818    /// This function is `async` but in practice, this is only asynchronous if
819    /// [`Self::set_file_loader`] is set and its future is actually asynchronous.
820    /// If that is not used, then it is fine to use a very simple executor, such as the one
821    /// provided by the `spin_on` crate
822    pub async fn build_from_source(
823        &mut self,
824        source_code: String,
825        path: PathBuf,
826    ) -> Option<ComponentDefinition> {
827        let r = crate::dynamic_item_tree::load(source_code, path, self.config.clone()).await;
828        self.diagnostics = r.diagnostics.into_iter().collect();
829        r.components.into_values().next()
830    }
831}
832
833/// This is the entry point of the crate, it can be used to load a `.slint` file and
834/// compile it into a [`CompilationResult`].
835pub struct Compiler {
836    config: i_slint_compiler::CompilerConfiguration,
837}
838
839impl Default for Compiler {
840    fn default() -> Self {
841        let config = i_slint_compiler::CompilerConfiguration::new(
842            i_slint_compiler::generator::OutputFormat::Interpreter,
843        );
844        Self { config }
845    }
846}
847
848impl Compiler {
849    /// Returns a new Compiler.
850    pub fn new() -> Self {
851        Self::default()
852    }
853
854    #[cfg(feature = "internal-live-preview")]
855    pub(crate) fn set_embed_resources(
856        &mut self,
857        embed_resources: i_slint_compiler::EmbedResourcesKind,
858    ) {
859        self.config.embed_resources = embed_resources;
860    }
861
862    /// Allow access to the underlying `CompilerConfiguration`
863    ///
864    /// This is an internal function without and ABI or API stability guarantees.
865    #[doc(hidden)]
866    #[cfg(feature = "internal")]
867    pub fn compiler_configuration(
868        &mut self,
869        _: i_slint_core::InternalToken,
870    ) -> &mut i_slint_compiler::CompilerConfiguration {
871        &mut self.config
872    }
873
874    /// Sets the include paths used for looking up `.slint` imports to the specified vector of paths.
875    pub fn set_include_paths(&mut self, include_paths: Vec<std::path::PathBuf>) {
876        self.config.include_paths = include_paths;
877    }
878
879    /// Returns the include paths the component compiler is currently configured with.
880    pub fn include_paths(&self) -> &Vec<std::path::PathBuf> {
881        &self.config.include_paths
882    }
883
884    /// Sets the library paths used for looking up `@library` imports to the specified map of library names to paths.
885    pub fn set_library_paths(&mut self, library_paths: HashMap<String, PathBuf>) {
886        self.config.library_paths = library_paths;
887    }
888
889    /// Returns the library paths the component compiler is currently configured with.
890    pub fn library_paths(&self) -> &HashMap<String, PathBuf> {
891        &self.config.library_paths
892    }
893
894    /// Sets the style to be used for widgets.
895    ///
896    /// Use the "material" style as widget style when compiling:
897    /// ```rust
898    /// use slint_interpreter::{ComponentDefinition, Compiler, ComponentHandle};
899    ///
900    /// let mut compiler = Compiler::default();
901    /// compiler.set_style("material".into());
902    /// let result = spin_on::spin_on(compiler.build_from_path("hello.slint"));
903    /// ```
904    pub fn set_style(&mut self, style: String) {
905        self.config.style = Some(style);
906    }
907
908    /// Returns the widget style the compiler is currently using when compiling .slint files.
909    pub fn style(&self) -> Option<&String> {
910        self.config.style.as_ref()
911    }
912
913    /// The domain used for translations
914    pub fn set_translation_domain(&mut self, domain: String) {
915        self.config.translation_domain = Some(domain);
916    }
917
918    /// Unless explicitly specified with the `@tr("context" => ...)`, the default translation context is the component name.
919    /// Use this option with [`DefaultTranslationContext::None`] to disable the default translation context.
920    ///
921    /// The translation file must also not have context
922    /// (`--no-default-translation-context` argument of `slint-tr-extractor`)
923    pub fn set_default_translation_context(
924        &mut self,
925        default_translation_context: DefaultTranslationContext,
926    ) {
927        self.config.default_translation_context = default_translation_context;
928    }
929
930    /// Sets the callback that will be invoked when loading imported .slint files. The specified
931    /// `file_loader_callback` parameter will be called with a canonical file path as argument
932    /// and is expected to return a future that, when resolved, provides the source code of the
933    /// .slint file to be imported as a string.
934    /// If an error is returned, then the build will abort with that error.
935    /// If None is returned, it means the normal resolution algorithm will proceed as if the hook
936    /// was not in place (i.e: load from the file system following the include paths)
937    pub fn set_file_loader(
938        &mut self,
939        file_loader_fallback: impl Fn(
940            &Path,
941        ) -> core::pin::Pin<
942            Box<dyn Future<Output = Option<std::io::Result<String>>>>,
943        > + 'static,
944    ) {
945        self.config.open_import_callback =
946            Some(Rc::new(move |path| file_loader_fallback(Path::new(path.as_str()))));
947    }
948
949    /// Compile a .slint file
950    ///
951    /// Returns a structure that holds the diagnostics and the compiled components.
952    ///
953    /// Any diagnostics produced during the compilation, such as warnings or errors, can be retrieved
954    /// after the call using [`CompilationResult::diagnostics()`].
955    ///
956    /// If the file was compiled without error, the list of component names can be obtained with
957    /// [`CompilationResult::component_names`], and the compiled components themselves with
958    /// [`CompilationResult::component()`].
959    ///
960    /// If the path is `"-"`, the file will be read from stdin.
961    /// If the extension of the file .rs, the first `slint!` macro from a rust file will be extracted
962    ///
963    /// This function is `async` but in practice, this is only asynchronous if
964    /// [`Self::set_file_loader`] was called and its future is actually asynchronous.
965    /// If that is not used, then it is fine to use a very simple executor, such as the one
966    /// provided by the `spin_on` crate
967    pub async fn build_from_path<P: AsRef<Path>>(&self, path: P) -> CompilationResult {
968        let path = path.as_ref();
969        let source = match i_slint_compiler::diagnostics::load_from_path(path) {
970            Ok(s) => s,
971            Err(d) => {
972                let mut diagnostics = i_slint_compiler::diagnostics::BuildDiagnostics::default();
973                diagnostics.push_compiler_error(d);
974                return CompilationResult {
975                    components: HashMap::new(),
976                    diagnostics: diagnostics.into_iter().collect(),
977                    #[cfg(feature = "internal-file-watcher")]
978                    watch_paths: vec![i_slint_compiler::pathutils::clean_path(path)],
979                    #[cfg(feature = "internal")]
980                    structs_and_enums: Vec::new(),
981                    #[cfg(feature = "internal")]
982                    named_exports: Vec::new(),
983                };
984            }
985        };
986
987        crate::dynamic_item_tree::load(source, path.into(), self.config.clone()).await
988    }
989
990    /// Compile some .slint code
991    ///
992    /// The `path` argument will be used for diagnostics and to compute relative
993    /// paths while importing.
994    ///
995    /// Any diagnostics produced during the compilation, such as warnings or errors, can be retrieved
996    /// after the call using [`CompilationResult::diagnostics()`].
997    ///
998    /// This function is `async` but in practice, this is only asynchronous if
999    /// [`Self::set_file_loader`] is set and its future is actually asynchronous.
1000    /// If that is not used, then it is fine to use a very simple executor, such as the one
1001    /// provided by the `spin_on` crate
1002    pub async fn build_from_source(&self, source_code: String, path: PathBuf) -> CompilationResult {
1003        crate::dynamic_item_tree::load(source_code, path, self.config.clone()).await
1004    }
1005}
1006
1007/// The result of a compilation
1008///
1009/// If [`Self::has_errors()`] is true, then the compilation failed.
1010/// The [`Self::diagnostics()`] function can be used to retrieve the diagnostics (errors and/or warnings)
1011/// or [`Self::print_diagnostics()`] can be used to print them to stderr.
1012/// The components can be retrieved using [`Self::components()`]
1013#[derive(Clone)]
1014pub struct CompilationResult {
1015    pub(crate) components: HashMap<String, ComponentDefinition>,
1016    pub(crate) diagnostics: Vec<Diagnostic>,
1017    #[cfg(feature = "internal-file-watcher")]
1018    pub(crate) watch_paths: Vec<PathBuf>,
1019    #[cfg(feature = "internal")]
1020    pub(crate) structs_and_enums: Vec<LangType>,
1021    /// For `export { Foo as Bar }` this vec contains tuples of (`Foo`, `Bar`)
1022    #[cfg(feature = "internal")]
1023    pub(crate) named_exports: Vec<(String, String)>,
1024}
1025
1026impl core::fmt::Debug for CompilationResult {
1027    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1028        f.debug_struct("CompilationResult")
1029            .field("components", &self.components.keys())
1030            .field("diagnostics", &self.diagnostics)
1031            .finish()
1032    }
1033}
1034
1035impl CompilationResult {
1036    /// Returns true if the compilation failed.
1037    /// The errors can be retrieved using the [`Self::diagnostics()`] function.
1038    pub fn has_errors(&self) -> bool {
1039        self.diagnostics().any(|diag| diag.level() == DiagnosticLevel::Error)
1040    }
1041
1042    /// Return an iterator over the diagnostics.
1043    ///
1044    /// You can also call [`Self::print_diagnostics()`] to output the diagnostics to stderr
1045    pub fn diagnostics(&self) -> impl Iterator<Item = Diagnostic> + '_ {
1046        self.diagnostics.iter().cloned()
1047    }
1048
1049    /// Print the diagnostics to stderr
1050    ///
1051    /// The diagnostics are printed in the same style as rustc errors
1052    ///
1053    /// This function is available when the `display-diagnostics` is enabled.
1054    #[cfg(feature = "display-diagnostics")]
1055    pub fn print_diagnostics(&self) {
1056        print_diagnostics(&self.diagnostics)
1057    }
1058
1059    /// Returns an iterator over the compiled components.
1060    pub fn components(&self) -> impl Iterator<Item = ComponentDefinition> + '_ {
1061        self.components.values().cloned()
1062    }
1063
1064    /// Returns the names of the components that were compiled.
1065    pub fn component_names(&self) -> impl Iterator<Item = &str> + '_ {
1066        self.components.keys().map(|s| s.as_str())
1067    }
1068
1069    /// Return the component definition for the given name.
1070    /// If the component does not exist, then `None` is returned.
1071    pub fn component(&self, name: &str) -> Option<ComponentDefinition> {
1072        self.components.get(name).cloned()
1073    }
1074
1075    /// This is an internal function without API stability guarantees.
1076    #[doc(hidden)]
1077    #[cfg(feature = "internal-file-watcher")]
1078    pub fn watch_paths(&self, _: i_slint_core::InternalToken) -> &[PathBuf] {
1079        &self.watch_paths
1080    }
1081
1082    /// This is an internal function without API stability guarantees.
1083    #[doc(hidden)]
1084    #[cfg(feature = "internal")]
1085    pub fn structs_and_enums(
1086        &self,
1087        _: i_slint_core::InternalToken,
1088    ) -> impl Iterator<Item = &LangType> {
1089        self.structs_and_enums.iter()
1090    }
1091
1092    /// This is an internal function without API stability guarantees.
1093    /// Returns the list of named export aliases as tuples (`export { Foo as Bar}` is (`Foo`, `Bar` tuple)).
1094    #[doc(hidden)]
1095    #[cfg(feature = "internal")]
1096    pub fn named_exports(
1097        &self,
1098        _: i_slint_core::InternalToken,
1099    ) -> impl Iterator<Item = &(String, String)> {
1100        self.named_exports.iter()
1101    }
1102}
1103
1104/// ComponentDefinition is a representation of a compiled component from .slint markup.
1105///
1106/// It can be constructed from a .slint file using the [`Compiler::build_from_path`] or [`Compiler::build_from_source`] functions.
1107/// And then it can be instantiated with the [`Self::create`] function.
1108///
1109/// The ComponentDefinition acts as a factory to create new instances. When you've finished
1110/// creating the instances it is safe to drop the ComponentDefinition.
1111#[derive(Clone)]
1112pub struct ComponentDefinition {
1113    pub(crate) inner: crate::dynamic_item_tree::ErasedItemTreeDescription,
1114}
1115
1116impl ComponentDefinition {
1117    /// Set a `debug(...)` handler
1118    #[doc(hidden)]
1119    #[cfg(feature = "internal")]
1120    pub fn set_debug_handler(
1121        &self,
1122        handler: impl Fn(Option<&i_slint_compiler::diagnostics::SourceLocation>, &str) + 'static,
1123        _: i_slint_core::InternalToken,
1124    ) {
1125        let handler = Rc::new(handler);
1126
1127        generativity::make_guard!(guard);
1128        self.inner.unerase(guard).recursively_set_debug_handler(handler);
1129    }
1130    /// Creates a new instance of the component and returns a shared handle to it.
1131    pub fn create(&self) -> Result<ComponentInstance, PlatformError> {
1132        let instance = self.create_with_options(Default::default())?;
1133        // SystemTrayIcon-rooted components don't have a real WindowAdapter.
1134        // Skip the eager window creation and tree instantiation for them.
1135        if !instance.is_system_tray_rooted() {
1136            // Make sure the window adapter is created so call to `window()` do not panic later.
1137            instance.inner.window_adapter_ref()?;
1138            // Eagerly instantiate repeaters and conditionals so that layout
1139            // bindings can see all instances without calling ensure_updated.
1140            i_slint_core::window::WindowInner::from_pub(instance.window())
1141                .ensure_tree_instantiated();
1142        }
1143        Ok(instance)
1144    }
1145
1146    /// Creates a new instance of the component and returns a shared handle to it.
1147    #[doc(hidden)]
1148    #[cfg(feature = "internal")]
1149    pub fn create_embedded(&self, ctx: FactoryContext) -> Result<ComponentInstance, PlatformError> {
1150        self.create_with_options(WindowOptions::Embed {
1151            parent_item_tree: ctx.parent_item_tree,
1152            parent_item_tree_index: ctx.parent_item_tree_index,
1153        })
1154    }
1155
1156    /// Instantiate the component using an existing window.
1157    #[doc(hidden)]
1158    #[cfg(feature = "internal")]
1159    pub fn create_with_existing_window(
1160        &self,
1161        window: &Window,
1162    ) -> Result<ComponentInstance, PlatformError> {
1163        self.create_with_options(WindowOptions::UseExistingWindow(
1164            WindowInner::from_pub(window).window_adapter(),
1165        ))
1166    }
1167
1168    /// Private implementation of create
1169    pub(crate) fn create_with_options(
1170        &self,
1171        options: WindowOptions,
1172    ) -> Result<ComponentInstance, PlatformError> {
1173        generativity::make_guard!(guard);
1174        Ok(ComponentInstance { inner: self.inner.unerase(guard).clone().create(options)? })
1175    }
1176
1177    /// List of publicly declared properties or callback.
1178    ///
1179    /// This is internal because it exposes the `Type` from compilerlib.
1180    #[doc(hidden)]
1181    #[cfg(feature = "internal")]
1182    pub fn properties_and_callbacks(
1183        &self,
1184    ) -> impl Iterator<
1185        Item = (
1186            String,
1187            (i_slint_compiler::langtype::Type, i_slint_compiler::object_tree::PropertyVisibility),
1188        ),
1189    > + '_ {
1190        // We create here a 'static guard, because unfortunately the returned type would be restricted to the guard lifetime
1191        // which is not required, but this is safe because there is only one instance of the unerased type
1192        let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1193        self.inner.unerase(guard).properties().map(|(s, t, v)| (s.to_string(), (t, v)))
1194    }
1195
1196    /// Returns an iterator over all publicly declared properties. Each iterator item is a tuple of property name
1197    /// and property type for each of them.
1198    pub fn properties(&self) -> impl Iterator<Item = (String, ValueType)> + '_ {
1199        // We create here a 'static guard, because unfortunately the returned type would be restricted to the guard lifetime
1200        // which is not required, but this is safe because there is only one instance of the unerased type
1201        let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1202        self.inner.unerase(guard).properties().filter_map(|(prop_name, prop_type, _)| {
1203            if prop_type.is_property_type() {
1204                Some((prop_name.to_string(), prop_type.into()))
1205            } else {
1206                None
1207            }
1208        })
1209    }
1210
1211    /// Returns the names of all publicly declared callbacks.
1212    pub fn callbacks(&self) -> impl Iterator<Item = String> + '_ {
1213        // We create here a 'static guard, because unfortunately the returned type would be restricted to the guard lifetime
1214        // which is not required, but this is safe because there is only one instance of the unerased type
1215        let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1216        self.inner.unerase(guard).properties().filter_map(|(prop_name, prop_type, _)| {
1217            if matches!(prop_type, LangType::Callback { .. }) {
1218                Some(prop_name.to_string())
1219            } else {
1220                None
1221            }
1222        })
1223    }
1224
1225    /// Returns the names of all publicly declared functions.
1226    pub fn functions(&self) -> impl Iterator<Item = String> + '_ {
1227        // We create here a 'static guard, because unfortunately the returned type would be restricted to the guard lifetime
1228        // which is not required, but this is safe because there is only one instance of the unerased type
1229        let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1230        self.inner.unerase(guard).properties().filter_map(|(prop_name, prop_type, _)| {
1231            if matches!(prop_type, LangType::Function { .. }) {
1232                Some(prop_name.to_string())
1233            } else {
1234                None
1235            }
1236        })
1237    }
1238
1239    /// Returns the names of all exported global singletons
1240    ///
1241    /// **Note:** Only globals that are exported or re-exported from the main .slint file will
1242    /// be exposed in the API
1243    pub fn globals(&self) -> impl Iterator<Item = String> + '_ {
1244        // We create here a 'static guard, because unfortunately the returned type would be restricted to the guard lifetime
1245        // which is not required, but this is safe because there is only one instance of the unerased type
1246        let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1247        self.inner.unerase(guard).global_names().map(|s| s.to_string())
1248    }
1249
1250    /// List of publicly declared properties or callback in the exported global singleton specified by its name.
1251    ///
1252    /// This is internal because it exposes the `Type` from compilerlib.
1253    #[doc(hidden)]
1254    #[cfg(feature = "internal")]
1255    pub fn global_properties_and_callbacks(
1256        &self,
1257        global_name: &str,
1258    ) -> Option<
1259        impl Iterator<
1260            Item = (
1261                String,
1262                (
1263                    i_slint_compiler::langtype::Type,
1264                    i_slint_compiler::object_tree::PropertyVisibility,
1265                ),
1266            ),
1267        > + '_,
1268    > {
1269        // We create here a 'static guard, because unfortunately the returned type would be restricted to the guard lifetime
1270        // which is not required, but this is safe because there is only one instance of the unerased type
1271        let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1272        self.inner
1273            .unerase(guard)
1274            .global_properties(global_name)
1275            .map(|o| o.map(|(s, t, v)| (s.to_string(), (t, v))))
1276    }
1277
1278    /// List of publicly declared properties in the exported global singleton specified by its name.
1279    pub fn global_properties(
1280        &self,
1281        global_name: &str,
1282    ) -> Option<impl Iterator<Item = (String, ValueType)> + '_> {
1283        // We create here a 'static guard, because unfortunately the returned type would be restricted to the guard lifetime
1284        // which is not required, but this is safe because there is only one instance of the unerased type
1285        let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1286        self.inner.unerase(guard).global_properties(global_name).map(|iter| {
1287            iter.filter_map(|(prop_name, prop_type, _)| {
1288                if prop_type.is_property_type() {
1289                    Some((prop_name.to_string(), prop_type.into()))
1290                } else {
1291                    None
1292                }
1293            })
1294        })
1295    }
1296
1297    /// List of publicly declared callbacks in the exported global singleton specified by its name.
1298    pub fn global_callbacks(&self, global_name: &str) -> Option<impl Iterator<Item = String> + '_> {
1299        // We create here a 'static guard, because unfortunately the returned type would be restricted to the guard lifetime
1300        // which is not required, but this is safe because there is only one instance of the unerased type
1301        let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1302        self.inner.unerase(guard).global_properties(global_name).map(|iter| {
1303            iter.filter_map(|(prop_name, prop_type, _)| {
1304                if matches!(prop_type, LangType::Callback { .. }) {
1305                    Some(prop_name.to_string())
1306                } else {
1307                    None
1308                }
1309            })
1310        })
1311    }
1312
1313    /// List of publicly declared functions in the exported global singleton specified by its name.
1314    pub fn global_functions(&self, global_name: &str) -> Option<impl Iterator<Item = String> + '_> {
1315        // We create here a 'static guard, because unfortunately the returned type would be restricted to the guard lifetime
1316        // which is not required, but this is safe because there is only one instance of the unerased type
1317        let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1318        self.inner.unerase(guard).global_properties(global_name).map(|iter| {
1319            iter.filter_map(|(prop_name, prop_type, _)| {
1320                if matches!(prop_type, LangType::Function { .. }) {
1321                    Some(prop_name.to_string())
1322                } else {
1323                    None
1324                }
1325            })
1326        })
1327    }
1328
1329    /// The name of this Component as written in the .slint file
1330    pub fn name(&self) -> &str {
1331        // We create here a 'static guard, because unfortunately the returned type would be restricted to the guard lifetime
1332        // which is not required, but this is safe because there is only one instance of the unerased type
1333        let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1334        self.inner.unerase(guard).id()
1335    }
1336
1337    /// True if instances of this component expose a `slint::Window`-shaped API
1338    /// (i.e. calling [`ComponentInstance::window`] is meaningful). False for
1339    /// non-windowed roots such as `SystemTrayIcon`, where `window()` would panic.
1340    #[doc(hidden)]
1341    #[cfg(feature = "internal")]
1342    pub fn is_window(&self) -> bool {
1343        let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1344        !self.inner.unerase(guard).original.inherits_system_tray_icon()
1345    }
1346
1347    /// This gives access to the tree of Elements.
1348    #[cfg(feature = "internal")]
1349    #[doc(hidden)]
1350    pub fn root_component(&self) -> Rc<i_slint_compiler::object_tree::Component> {
1351        let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1352        self.inner.unerase(guard).original.clone()
1353    }
1354
1355    /// Return the `TypeLoader` used when parsing the code in the interpreter.
1356    ///
1357    /// WARNING: this is not part of the public API
1358    #[cfg(feature = "internal-highlight")]
1359    pub fn type_loader(&self) -> std::rc::Rc<i_slint_compiler::typeloader::TypeLoader> {
1360        let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1361        self.inner.unerase(guard).type_loader.get().unwrap().clone()
1362    }
1363
1364    /// Return the `TypeLoader` used when parsing the code in the interpreter in
1365    /// a state before most passes were applied by the compiler.
1366    ///
1367    /// Each returned type loader is a deep copy of the entire state connected to it,
1368    /// so this is a fairly expensive function!
1369    ///
1370    /// WARNING: this is not part of the public API
1371    #[cfg(feature = "internal-highlight")]
1372    pub fn raw_type_loader(&self) -> Option<i_slint_compiler::typeloader::TypeLoader> {
1373        let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1374        self.inner
1375            .unerase(guard)
1376            .raw_type_loader
1377            .get()
1378            .unwrap()
1379            .as_ref()
1380            .and_then(|tl| i_slint_compiler::typeloader::snapshot(tl))
1381    }
1382}
1383
1384/// Print the diagnostics to stderr
1385///
1386/// The diagnostics are printed in the same style as rustc errors
1387///
1388/// This function is available when the `display-diagnostics` is enabled.
1389#[cfg(feature = "display-diagnostics")]
1390pub fn print_diagnostics(diagnostics: &[Diagnostic]) {
1391    let mut build_diagnostics = i_slint_compiler::diagnostics::BuildDiagnostics::default();
1392    for d in diagnostics {
1393        build_diagnostics.push_compiler_error(d.clone())
1394    }
1395    build_diagnostics.print();
1396}
1397
1398/// This represents an instance of a dynamic component
1399///
1400/// You can create an instance with the [`ComponentDefinition::create`] function.
1401///
1402/// Properties and callback can be accessed using the associated functions.
1403///
1404/// An instance can be put on screen with the [`ComponentInstance::run`] function.
1405#[repr(C)]
1406pub struct ComponentInstance {
1407    pub(crate) inner: crate::dynamic_item_tree::DynamicComponentVRc,
1408}
1409
1410impl ComponentInstance {
1411    /// Return the [`ComponentDefinition`] that was used to create this instance.
1412    pub fn definition(&self) -> ComponentDefinition {
1413        generativity::make_guard!(guard);
1414        ComponentDefinition { inner: self.inner.unerase(guard).description().into() }
1415    }
1416
1417    fn is_system_tray_rooted(&self) -> bool {
1418        let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1419        self.inner.unerase(guard).description().original.inherits_system_tray_icon()
1420    }
1421
1422    /// Return the value for a public property of this component.
1423    ///
1424    /// ## Examples
1425    ///
1426    /// ```
1427    /// # i_slint_backend_testing::init_no_event_loop();
1428    /// use slint_interpreter::{ComponentDefinition, Compiler, Value, SharedString};
1429    /// let code = r#"
1430    ///     export component MyWin inherits Window {
1431    ///         in-out property <int> my_property: 42;
1432    ///     }
1433    /// "#;
1434    /// let mut compiler = Compiler::default();
1435    /// let result = spin_on::spin_on(
1436    ///     compiler.build_from_source(code.into(), Default::default()));
1437    /// assert_eq!(result.diagnostics().count(), 0, "{:?}", result.diagnostics().collect::<Vec<_>>());
1438    /// let instance = result.component("MyWin").unwrap().create().unwrap();
1439    /// assert_eq!(instance.get_property("my_property").unwrap(), Value::from(42));
1440    /// ```
1441    pub fn get_property(&self, name: &str) -> Result<Value, GetPropertyError> {
1442        generativity::make_guard!(guard);
1443        let comp = self.inner.unerase(guard);
1444        let name = normalize_identifier(name);
1445
1446        if comp
1447            .description()
1448            .original
1449            .root_element
1450            .borrow()
1451            .property_declarations
1452            .get(&name)
1453            .is_none_or(|d| !d.expose_in_public_api)
1454        {
1455            return Err(GetPropertyError::NoSuchProperty);
1456        }
1457
1458        comp.description()
1459            .get_property(comp.borrow(), &name)
1460            .map_err(|()| GetPropertyError::NoSuchProperty)
1461    }
1462
1463    /// Set the value for a public property of this component.
1464    pub fn set_property(&self, name: &str, value: Value) -> Result<(), SetPropertyError> {
1465        let name = normalize_identifier(name);
1466        generativity::make_guard!(guard);
1467        let comp = self.inner.unerase(guard);
1468        let d = comp.description();
1469        let elem = d.original.root_element.borrow();
1470        let decl = elem.property_declarations.get(&name).ok_or(SetPropertyError::NoSuchProperty)?;
1471
1472        if !decl.expose_in_public_api {
1473            return Err(SetPropertyError::NoSuchProperty);
1474        } else if decl.visibility == i_slint_compiler::object_tree::PropertyVisibility::Output {
1475            return Err(SetPropertyError::AccessDenied);
1476        }
1477
1478        d.set_property(comp.borrow(), &name, value)
1479    }
1480
1481    /// Set a handler for the callback with the given name. A callback with that
1482    /// name must be defined in the document otherwise an error will be returned.
1483    ///
1484    /// Note: Since the [`ComponentInstance`] holds the handler, the handler itself should not
1485    /// contain a strong reference to the instance. So if you need to capture the instance,
1486    /// you should use [`Self::as_weak`] to create a weak reference.
1487    ///
1488    /// ## Examples
1489    ///
1490    /// ```
1491    /// # i_slint_backend_testing::init_no_event_loop();
1492    /// use slint_interpreter::{Compiler, Value, SharedString, ComponentHandle};
1493    /// use core::convert::TryInto;
1494    /// let code = r#"
1495    ///     export component MyWin inherits Window {
1496    ///         callback foo(int) -> int;
1497    ///         in-out property <int> my_prop: 12;
1498    ///     }
1499    /// "#;
1500    /// let result = spin_on::spin_on(
1501    ///     Compiler::default().build_from_source(code.into(), Default::default()));
1502    /// assert_eq!(result.diagnostics().count(), 0, "{:?}", result.diagnostics().collect::<Vec<_>>());
1503    /// let instance = result.component("MyWin").unwrap().create().unwrap();
1504    /// let instance_weak = instance.as_weak();
1505    /// instance.set_callback("foo", move |args: &[Value]| -> Value {
1506    ///     let arg: u32 = args[0].clone().try_into().unwrap();
1507    ///     let my_prop = instance_weak.unwrap().get_property("my_prop").unwrap();
1508    ///     let my_prop : u32 = my_prop.try_into().unwrap();
1509    ///     Value::from(arg + my_prop)
1510    /// }).unwrap();
1511    ///
1512    /// let res = instance.invoke("foo", &[Value::from(500)]).unwrap();
1513    /// assert_eq!(res, Value::from(500+12));
1514    /// ```
1515    pub fn set_callback(
1516        &self,
1517        name: &str,
1518        callback: impl Fn(&[Value]) -> Value + 'static,
1519    ) -> Result<(), SetCallbackError> {
1520        generativity::make_guard!(guard);
1521        let comp = self.inner.unerase(guard);
1522        comp.description()
1523            .set_callback_handler(comp.borrow(), &normalize_identifier(name), Box::new(callback))
1524            .map_err(|()| SetCallbackError::NoSuchCallback)
1525    }
1526
1527    /// Call the given callback or function with the arguments
1528    ///
1529    /// ## Examples
1530    /// See the documentation of [`Self::set_callback`] for an example
1531    pub fn invoke(&self, name: &str, args: &[Value]) -> Result<Value, InvokeError> {
1532        generativity::make_guard!(guard);
1533        let comp = self.inner.unerase(guard);
1534        comp.description()
1535            .invoke(comp.borrow(), &normalize_identifier(name), args)
1536            .map_err(|()| InvokeError::NoSuchCallable)
1537    }
1538
1539    /// Return the value for a property within an exported global singleton used by this component.
1540    ///
1541    /// The `global` parameter is the exported name of the global singleton. The `property` argument
1542    /// is the name of the property
1543    ///
1544    /// ## Examples
1545    ///
1546    /// ```
1547    /// # i_slint_backend_testing::init_no_event_loop();
1548    /// use slint_interpreter::{Compiler, Value, SharedString};
1549    /// let code = r#"
1550    ///     global Glob {
1551    ///         in-out property <int> my_property: 42;
1552    ///     }
1553    ///     export { Glob as TheGlobal }
1554    ///     export component MyWin inherits Window {
1555    ///     }
1556    /// "#;
1557    /// let mut compiler = Compiler::default();
1558    /// let result = spin_on::spin_on(compiler.build_from_source(code.into(), Default::default()));
1559    /// assert_eq!(result.diagnostics().count(), 0, "{:?}", result.diagnostics().collect::<Vec<_>>());
1560    /// let instance = result.component("MyWin").unwrap().create().unwrap();
1561    /// assert_eq!(instance.get_global_property("TheGlobal", "my_property").unwrap(), Value::from(42));
1562    /// ```
1563    pub fn get_global_property(
1564        &self,
1565        global: &str,
1566        property: &str,
1567    ) -> Result<Value, GetPropertyError> {
1568        generativity::make_guard!(guard);
1569        let comp = self.inner.unerase(guard);
1570        comp.description()
1571            .get_global(comp.borrow(), &normalize_identifier(global))
1572            .map_err(|()| GetPropertyError::NoSuchProperty)? // FIXME: should there be a NoSuchGlobal error?
1573            .as_ref()
1574            .get_property(&normalize_identifier(property))
1575            .map_err(|()| GetPropertyError::NoSuchProperty)
1576    }
1577
1578    /// Set the value for a property within an exported global singleton used by this component.
1579    pub fn set_global_property(
1580        &self,
1581        global: &str,
1582        property: &str,
1583        value: Value,
1584    ) -> Result<(), SetPropertyError> {
1585        generativity::make_guard!(guard);
1586        let comp = self.inner.unerase(guard);
1587        comp.description()
1588            .get_global(comp.borrow(), &normalize_identifier(global))
1589            .map_err(|()| SetPropertyError::NoSuchProperty)? // FIXME: should there be a NoSuchGlobal error?
1590            .as_ref()
1591            .set_property(&normalize_identifier(property), value)
1592    }
1593
1594    /// Set a handler for the callback in the exported global singleton. A callback with that
1595    /// name must be defined in the specified global and the global must be exported from the
1596    /// main document otherwise an error will be returned.
1597    ///
1598    /// ## Examples
1599    ///
1600    /// ```
1601    /// # i_slint_backend_testing::init_no_event_loop();
1602    /// use slint_interpreter::{Compiler, Value, SharedString};
1603    /// use core::convert::TryInto;
1604    /// let code = r#"
1605    ///     export global Logic {
1606    ///         pure callback to_uppercase(string) -> string;
1607    ///     }
1608    ///     export component MyWin inherits Window {
1609    ///         out property <string> hello: Logic.to_uppercase("world");
1610    ///     }
1611    /// "#;
1612    /// let result = spin_on::spin_on(
1613    ///     Compiler::default().build_from_source(code.into(), Default::default()));
1614    /// let instance = result.component("MyWin").unwrap().create().unwrap();
1615    /// instance.set_global_callback("Logic", "to_uppercase", |args: &[Value]| -> Value {
1616    ///     let arg: SharedString = args[0].clone().try_into().unwrap();
1617    ///     Value::from(SharedString::from(arg.to_uppercase()))
1618    /// }).unwrap();
1619    ///
1620    /// let res = instance.get_property("hello").unwrap();
1621    /// assert_eq!(res, Value::from(SharedString::from("WORLD")));
1622    ///
1623    /// let abc = instance.invoke_global("Logic", "to_uppercase", &[
1624    ///     SharedString::from("abc").into()
1625    /// ]).unwrap();
1626    /// assert_eq!(abc, Value::from(SharedString::from("ABC")));
1627    /// ```
1628    pub fn set_global_callback(
1629        &self,
1630        global: &str,
1631        name: &str,
1632        callback: impl Fn(&[Value]) -> Value + 'static,
1633    ) -> Result<(), SetCallbackError> {
1634        generativity::make_guard!(guard);
1635        let comp = self.inner.unerase(guard);
1636        comp.description()
1637            .get_global(comp.borrow(), &normalize_identifier(global))
1638            .map_err(|()| SetCallbackError::NoSuchCallback)? // FIXME: should there be a NoSuchGlobal error?
1639            .as_ref()
1640            .set_callback_handler(&normalize_identifier(name), Box::new(callback))
1641            .map_err(|()| SetCallbackError::NoSuchCallback)
1642    }
1643
1644    /// Call the given callback or function within a global singleton with the arguments
1645    ///
1646    /// ## Examples
1647    /// See the documentation of [`Self::set_global_callback`] for an example
1648    pub fn invoke_global(
1649        &self,
1650        global: &str,
1651        callable_name: &str,
1652        args: &[Value],
1653    ) -> Result<Value, InvokeError> {
1654        generativity::make_guard!(guard);
1655        let comp = self.inner.unerase(guard);
1656        let g = comp
1657            .description()
1658            .get_global(comp.borrow(), &normalize_identifier(global))
1659            .map_err(|()| InvokeError::NoSuchCallable)?; // FIXME: should there be a NoSuchGlobal error?
1660        let callable_name = normalize_identifier(callable_name);
1661        if matches!(
1662            comp.description()
1663                .original
1664                .root_element
1665                .borrow()
1666                .lookup_property(&callable_name)
1667                .property_type,
1668            LangType::Function { .. }
1669        ) {
1670            g.as_ref()
1671                .eval_function(&callable_name, args.to_vec())
1672                .map_err(|()| InvokeError::NoSuchCallable)
1673        } else {
1674            g.as_ref()
1675                .invoke_callback(&callable_name, args)
1676                .map_err(|()| InvokeError::NoSuchCallable)
1677        }
1678    }
1679
1680    /// Find all positions of the components which are pointed by a given source location.
1681    ///
1682    /// WARNING: this is not part of the public API
1683    #[cfg(feature = "internal-highlight")]
1684    pub fn component_positions(
1685        &self,
1686        path: &Path,
1687        offset: u32,
1688    ) -> Vec<crate::highlight::HighlightedRect> {
1689        crate::highlight::component_positions(&self.inner, path, offset)
1690    }
1691
1692    /// Find the position of the `element`.
1693    ///
1694    /// WARNING: this is not part of the public API
1695    #[cfg(feature = "internal-highlight")]
1696    pub fn element_positions(
1697        &self,
1698        element: &i_slint_compiler::object_tree::ElementRc,
1699    ) -> Vec<crate::highlight::HighlightedRect> {
1700        crate::highlight::element_positions(
1701            &self.inner,
1702            element,
1703            crate::highlight::ElementPositionFilter::IncludeClipped,
1704        )
1705    }
1706
1707    /// Find the `element` that was defined at the text position.
1708    ///
1709    /// WARNING: this is not part of the public API
1710    #[cfg(feature = "internal-highlight")]
1711    pub fn element_node_at_source_code_position(
1712        &self,
1713        path: &Path,
1714        offset: u32,
1715    ) -> Vec<(i_slint_compiler::object_tree::ElementRc, usize)> {
1716        crate::highlight::element_node_at_source_code_position(&self.inner, path, offset)
1717    }
1718}
1719
1720impl StrongHandle for ComponentInstance {
1721    type WeakInner = vtable::VWeak<ItemTreeVTable, crate::dynamic_item_tree::ErasedItemTreeBox>;
1722
1723    fn upgrade_from_weak_inner(inner: &Self::WeakInner) -> Option<Self> {
1724        Some(Self { inner: inner.upgrade()? })
1725    }
1726}
1727
1728impl ComponentHandle for ComponentInstance {
1729    fn as_weak(&self) -> Weak<Self>
1730    where
1731        Self: Sized,
1732    {
1733        Weak::new(vtable::VRc::downgrade(&self.inner))
1734    }
1735
1736    fn clone_strong(&self) -> Self {
1737        Self { inner: self.inner.clone() }
1738    }
1739
1740    fn show(&self) -> Result<(), PlatformError> {
1741        if self.is_system_tray_rooted() {
1742            // Mirror what the Rust/C++ generators emit for tray-rooted public
1743            // components: toggle the `visible` property; the change-tracker on
1744            // the SystemTrayIcon native item dispatches to the platform handle.
1745            self.set_property("visible", Value::Bool(true)).expect(
1746                "setting `visible` on a SystemTrayIcon-rooted component should always succeed",
1747            );
1748            return Ok(());
1749        }
1750        self.inner.window_adapter_ref()?.window().show()
1751    }
1752
1753    fn hide(&self) -> Result<(), PlatformError> {
1754        if self.is_system_tray_rooted() {
1755            self.set_property("visible", Value::Bool(false)).expect(
1756                "setting `visible` on a SystemTrayIcon-rooted component should always succeed",
1757            );
1758            return Ok(());
1759        }
1760        self.inner.window_adapter_ref()?.window().hide()
1761    }
1762
1763    fn run(&self) -> Result<(), PlatformError> {
1764        self.show()?;
1765        run_event_loop()?;
1766        self.hide()
1767    }
1768
1769    fn window(&self) -> &Window {
1770        self.inner.window_adapter_ref().unwrap().window()
1771    }
1772
1773    fn global<'a, T: Global<'a, Self>>(&'a self) -> T
1774    where
1775        Self: Sized,
1776    {
1777        unreachable!()
1778    }
1779}
1780
1781impl From<ComponentInstance>
1782    for vtable::VRc<i_slint_core::item_tree::ItemTreeVTable, ErasedItemTreeBox>
1783{
1784    fn from(value: ComponentInstance) -> Self {
1785        value.inner
1786    }
1787}
1788
1789/// Error returned by [`ComponentInstance::get_property`]
1790#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Error, derive_more::Display)]
1791#[non_exhaustive]
1792pub enum GetPropertyError {
1793    /// There is no property with the given name
1794    #[display("no such property")]
1795    NoSuchProperty,
1796}
1797
1798/// Error returned by [`ComponentInstance::set_property`]
1799#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Error, derive_more::Display)]
1800#[non_exhaustive]
1801pub enum SetPropertyError {
1802    /// There is no property with the given name.
1803    #[display("no such property")]
1804    NoSuchProperty,
1805    /// The property exists but does not have a type matching the dynamic value.
1806    ///
1807    /// This happens for example when assigning a source struct value to a target
1808    /// struct property, where the source doesn't have all the fields the target struct
1809    /// requires.
1810    #[display("wrong type")]
1811    WrongType,
1812    /// Attempt to set an output property.
1813    #[display("access denied")]
1814    AccessDenied,
1815}
1816
1817/// Error returned by [`ComponentInstance::set_callback`]
1818#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Error, derive_more::Display)]
1819#[non_exhaustive]
1820pub enum SetCallbackError {
1821    /// There is no callback with the given name
1822    #[display("no such callback")]
1823    NoSuchCallback,
1824}
1825
1826/// Error returned by [`ComponentInstance::invoke`]
1827#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Error, derive_more::Display)]
1828#[non_exhaustive]
1829pub enum InvokeError {
1830    /// There is no callback or function with the given name
1831    #[display("no such callback or function")]
1832    NoSuchCallable,
1833}
1834
1835/// Enters the main event loop. This is necessary in order to receive
1836/// events from the windowing system in order to render to the screen
1837/// and react to user input.
1838pub fn run_event_loop() -> Result<(), PlatformError> {
1839    i_slint_backend_selector::with_platform(|b| b.run_event_loop())
1840}
1841
1842/// Spawns a [`Future`] to execute in the Slint event loop.
1843///
1844/// See the documentation of `slint::spawn_local()` for more info
1845pub fn spawn_local<F: Future + 'static>(fut: F) -> Result<JoinHandle<F::Output>, EventLoopError> {
1846    i_slint_backend_selector::with_global_context(|ctx| ctx.spawn_local(fut))
1847        .map_err(|_| EventLoopError::NoEventLoopProvider)?
1848}
1849
1850#[test]
1851fn component_definition_properties() {
1852    i_slint_backend_testing::init_no_event_loop();
1853    let mut compiler = Compiler::default();
1854    compiler.set_style("fluent".into());
1855    let comp_def = spin_on::spin_on(
1856        compiler.build_from_source(
1857            r#"
1858    export component Dummy {
1859        in-out property <string> test;
1860        in-out property <int> underscores-and-dashes_preserved: 44;
1861        callback hello;
1862    }"#
1863            .into(),
1864            "".into(),
1865        ),
1866    )
1867    .component("Dummy")
1868    .unwrap();
1869
1870    let props = comp_def.properties().collect::<Vec<(_, _)>>();
1871
1872    assert_eq!(props.len(), 2);
1873    assert_eq!(props[0].0, "test");
1874    assert_eq!(props[0].1, ValueType::String);
1875    assert_eq!(props[1].0, "underscores-and-dashes_preserved");
1876    assert_eq!(props[1].1, ValueType::Number);
1877
1878    let instance = comp_def.create().unwrap();
1879    assert_eq!(instance.get_property("underscores_and-dashes-preserved"), Ok(Value::Number(44.)));
1880    assert_eq!(
1881        instance.get_property("underscoresanddashespreserved"),
1882        Err(GetPropertyError::NoSuchProperty)
1883    );
1884    assert_eq!(
1885        instance.set_property("underscores-and_dashes-preserved", Value::Number(88.)),
1886        Ok(())
1887    );
1888    assert_eq!(
1889        instance.set_property("underscoresanddashespreserved", Value::Number(99.)),
1890        Err(SetPropertyError::NoSuchProperty)
1891    );
1892    assert_eq!(
1893        instance.set_property("underscores-and_dashes-preserved", Value::String("99".into())),
1894        Err(SetPropertyError::WrongType)
1895    );
1896    assert_eq!(instance.get_property("underscores-and-dashes-preserved"), Ok(Value::Number(88.)));
1897}
1898
1899#[test]
1900fn component_definition_properties2() {
1901    i_slint_backend_testing::init_no_event_loop();
1902    let mut compiler = Compiler::default();
1903    compiler.set_style("fluent".into());
1904    let comp_def = spin_on::spin_on(
1905        compiler.build_from_source(
1906            r#"
1907    export component Dummy {
1908        in-out property <string> sub-text <=> sub.text;
1909        sub := Text { property <int> private-not-exported; }
1910        out property <string> xreadonly: "the value";
1911        private property <string> xx: sub.text;
1912        callback hello;
1913    }"#
1914            .into(),
1915            "".into(),
1916        ),
1917    )
1918    .component("Dummy")
1919    .unwrap();
1920
1921    let props = comp_def.properties().collect::<Vec<(_, _)>>();
1922
1923    assert_eq!(props.len(), 2);
1924    assert_eq!(props[0].0, "sub-text");
1925    assert_eq!(props[0].1, ValueType::String);
1926    assert_eq!(props[1].0, "xreadonly");
1927
1928    let callbacks = comp_def.callbacks().collect::<Vec<_>>();
1929    assert_eq!(callbacks.len(), 1);
1930    assert_eq!(callbacks[0], "hello");
1931
1932    let instance = comp_def.create().unwrap();
1933    assert_eq!(
1934        instance.set_property("xreadonly", SharedString::from("XXX").into()),
1935        Err(SetPropertyError::AccessDenied)
1936    );
1937    assert_eq!(instance.get_property("xreadonly"), Ok(Value::String("the value".into())));
1938    assert_eq!(
1939        instance.set_property("xx", SharedString::from("XXX").into()),
1940        Err(SetPropertyError::NoSuchProperty)
1941    );
1942    assert_eq!(
1943        instance.set_property("background", Value::default()),
1944        Err(SetPropertyError::NoSuchProperty)
1945    );
1946
1947    assert_eq!(instance.get_property("background"), Err(GetPropertyError::NoSuchProperty));
1948    assert_eq!(instance.get_property("xx"), Err(GetPropertyError::NoSuchProperty));
1949}
1950
1951#[test]
1952fn globals() {
1953    i_slint_backend_testing::init_no_event_loop();
1954    let mut compiler = Compiler::default();
1955    compiler.set_style("fluent".into());
1956    let definition = spin_on::spin_on(
1957        compiler.build_from_source(
1958            r#"
1959    export global My-Super_Global {
1960        in-out property <int> the-property : 21;
1961        callback my-callback();
1962    }
1963    export { My-Super_Global as AliasedGlobal }
1964    export component Dummy {
1965        callback alias <=> My-Super_Global.my-callback;
1966    }"#
1967            .into(),
1968            "".into(),
1969        ),
1970    )
1971    .component("Dummy")
1972    .unwrap();
1973
1974    assert_eq!(definition.globals().collect::<Vec<_>>(), vec!["My-Super_Global", "AliasedGlobal"]);
1975
1976    assert!(definition.global_properties("not-there").is_none());
1977    {
1978        let expected_properties = vec![("the-property".to_string(), ValueType::Number)];
1979        let expected_callbacks = vec!["my-callback".to_string()];
1980
1981        let assert_properties_and_callbacks = |global_name| {
1982            assert_eq!(
1983                definition
1984                    .global_properties(global_name)
1985                    .map(|props| props.collect::<Vec<_>>())
1986                    .as_ref(),
1987                Some(&expected_properties)
1988            );
1989            assert_eq!(
1990                definition
1991                    .global_callbacks(global_name)
1992                    .map(|props| props.collect::<Vec<_>>())
1993                    .as_ref(),
1994                Some(&expected_callbacks)
1995            );
1996        };
1997
1998        assert_properties_and_callbacks("My-Super-Global");
1999        assert_properties_and_callbacks("My_Super-Global");
2000        assert_properties_and_callbacks("AliasedGlobal");
2001    }
2002
2003    let instance = definition.create().unwrap();
2004    assert_eq!(
2005        instance.set_global_property("My_Super-Global", "the_property", Value::Number(44.)),
2006        Ok(())
2007    );
2008    assert_eq!(
2009        instance.set_global_property("AliasedGlobal", "the_property", Value::Number(44.)),
2010        Ok(())
2011    );
2012    assert_eq!(
2013        instance.set_global_property("DontExist", "the-property", Value::Number(88.)),
2014        Err(SetPropertyError::NoSuchProperty)
2015    );
2016
2017    assert_eq!(
2018        instance.set_global_property("My_Super-Global", "theproperty", Value::Number(88.)),
2019        Err(SetPropertyError::NoSuchProperty)
2020    );
2021    assert_eq!(
2022        instance.set_global_property("AliasedGlobal", "theproperty", Value::Number(88.)),
2023        Err(SetPropertyError::NoSuchProperty)
2024    );
2025    assert_eq!(
2026        instance.set_global_property("My_Super-Global", "the_property", Value::String("88".into())),
2027        Err(SetPropertyError::WrongType)
2028    );
2029    assert_eq!(
2030        instance.get_global_property("My-Super_Global", "yoyo"),
2031        Err(GetPropertyError::NoSuchProperty)
2032    );
2033    assert_eq!(
2034        instance.get_global_property("My-Super_Global", "the-property"),
2035        Ok(Value::Number(44.))
2036    );
2037
2038    assert_eq!(
2039        instance.set_property("the-property", Value::Void),
2040        Err(SetPropertyError::NoSuchProperty)
2041    );
2042    assert_eq!(instance.get_property("the-property"), Err(GetPropertyError::NoSuchProperty));
2043
2044    assert_eq!(
2045        instance.set_global_callback("DontExist", "the-property", |_| panic!()),
2046        Err(SetCallbackError::NoSuchCallback)
2047    );
2048    assert_eq!(
2049        instance.set_global_callback("My_Super_Global", "the-property", |_| panic!()),
2050        Err(SetCallbackError::NoSuchCallback)
2051    );
2052    assert_eq!(
2053        instance.set_global_callback("My_Super_Global", "yoyo", |_| panic!()),
2054        Err(SetCallbackError::NoSuchCallback)
2055    );
2056
2057    assert_eq!(
2058        instance.invoke_global("DontExist", "the-property", &[]),
2059        Err(InvokeError::NoSuchCallable)
2060    );
2061    assert_eq!(
2062        instance.invoke_global("My_Super_Global", "the-property", &[]),
2063        Err(InvokeError::NoSuchCallable)
2064    );
2065    assert_eq!(
2066        instance.invoke_global("My_Super_Global", "yoyo", &[]),
2067        Err(InvokeError::NoSuchCallable)
2068    );
2069
2070    // Alias to global don't crash (#8238)
2071    assert_eq!(instance.get_property("alias"), Err(GetPropertyError::NoSuchProperty));
2072}
2073
2074#[test]
2075fn call_functions() {
2076    i_slint_backend_testing::init_no_event_loop();
2077    let mut compiler = Compiler::default();
2078    compiler.set_style("fluent".into());
2079    let definition = spin_on::spin_on(
2080        compiler.build_from_source(
2081            r#"
2082    export global Gl {
2083        out property<string> q;
2084        public function foo-bar(a-a: string, b-b:int) -> string {
2085            q = a-a;
2086            return a-a + b-b;
2087        }
2088    }
2089    export component Test {
2090        out property<int> p;
2091        public function foo-bar(a: int, b:int) -> int {
2092            p = a;
2093            return a + b;
2094        }
2095    }"#
2096            .into(),
2097            "".into(),
2098        ),
2099    )
2100    .component("Test")
2101    .unwrap();
2102
2103    assert_eq!(definition.functions().collect::<Vec<_>>(), ["foo-bar"]);
2104    assert_eq!(definition.global_functions("Gl").unwrap().collect::<Vec<_>>(), ["foo-bar"]);
2105
2106    let instance = definition.create().unwrap();
2107
2108    assert_eq!(
2109        instance.invoke("foo_bar", &[Value::Number(3.), Value::Number(4.)]),
2110        Ok(Value::Number(7.))
2111    );
2112    assert_eq!(instance.invoke("p", &[]), Err(InvokeError::NoSuchCallable));
2113    assert_eq!(instance.get_property("p"), Ok(Value::Number(3.)));
2114
2115    assert_eq!(
2116        instance.invoke_global(
2117            "Gl",
2118            "foo_bar",
2119            &[Value::String("Hello".into()), Value::Number(10.)]
2120        ),
2121        Ok(Value::String("Hello10".into()))
2122    );
2123    assert_eq!(instance.get_global_property("Gl", "q"), Ok(Value::String("Hello".into())));
2124}
2125
2126#[test]
2127fn component_definition_struct_properties() {
2128    i_slint_backend_testing::init_no_event_loop();
2129    let mut compiler = Compiler::default();
2130    compiler.set_style("fluent".into());
2131    let comp_def = spin_on::spin_on(
2132        compiler.build_from_source(
2133            r#"
2134    export struct Settings {
2135        string_value: string,
2136    }
2137    export component Dummy {
2138        in-out property <Settings> test;
2139    }"#
2140            .into(),
2141            "".into(),
2142        ),
2143    )
2144    .component("Dummy")
2145    .unwrap();
2146
2147    let props = comp_def.properties().collect::<Vec<(_, _)>>();
2148
2149    assert_eq!(props.len(), 1);
2150    assert_eq!(props[0].0, "test");
2151    assert_eq!(props[0].1, ValueType::Struct);
2152
2153    let instance = comp_def.create().unwrap();
2154
2155    let valid_struct: Struct =
2156        [("string_value".to_string(), Value::String("hello".into()))].iter().cloned().collect();
2157
2158    assert_eq!(instance.set_property("test", Value::Struct(valid_struct.clone())), Ok(()));
2159    assert_eq!(instance.get_property("test").unwrap().value_type(), ValueType::Struct);
2160
2161    assert_eq!(instance.set_property("test", Value::Number(42.)), Err(SetPropertyError::WrongType));
2162
2163    let mut invalid_struct = valid_struct.clone();
2164    invalid_struct.set_field("other".into(), Value::Number(44.));
2165    assert_eq!(
2166        instance.set_property("test", Value::Struct(invalid_struct)),
2167        Err(SetPropertyError::WrongType)
2168    );
2169    let mut invalid_struct = valid_struct;
2170    invalid_struct.set_field("string_value".into(), Value::Number(44.));
2171    assert_eq!(
2172        instance.set_property("test", Value::Struct(invalid_struct)),
2173        Err(SetPropertyError::WrongType)
2174    );
2175}
2176
2177#[test]
2178fn component_definition_model_properties() {
2179    use i_slint_core::model::*;
2180    i_slint_backend_testing::init_no_event_loop();
2181    let mut compiler = Compiler::default();
2182    compiler.set_style("fluent".into());
2183    let comp_def = spin_on::spin_on(compiler.build_from_source(
2184        "export component Dummy { in-out property <[int]> prop: [42, 12]; }".into(),
2185        "".into(),
2186    ))
2187    .component("Dummy")
2188    .unwrap();
2189
2190    let props = comp_def.properties().collect::<Vec<(_, _)>>();
2191    assert_eq!(props.len(), 1);
2192    assert_eq!(props[0].0, "prop");
2193    assert_eq!(props[0].1, ValueType::Model);
2194
2195    let instance = comp_def.create().unwrap();
2196
2197    let int_model =
2198        Value::Model([Value::Number(14.), Value::Number(15.), Value::Number(16.)].into());
2199    let empty_model = Value::Model(ModelRc::new(VecModel::<Value>::default()));
2200    let model_with_string = Value::Model(VecModel::from_slice(&[
2201        Value::Number(1000.),
2202        Value::String("foo".into()),
2203        Value::Number(1111.),
2204    ]));
2205
2206    #[track_caller]
2207    fn check_model(val: Value, r: &[f64]) {
2208        if let Value::Model(m) = val {
2209            assert_eq!(r.len(), m.row_count());
2210            for (i, v) in r.iter().enumerate() {
2211                assert_eq!(m.row_data(i).unwrap(), Value::Number(*v));
2212            }
2213        } else {
2214            panic!("{val:?} not a model");
2215        }
2216    }
2217
2218    assert_eq!(instance.get_property("prop").unwrap().value_type(), ValueType::Model);
2219    check_model(instance.get_property("prop").unwrap(), &[42., 12.]);
2220
2221    instance.set_property("prop", int_model).unwrap();
2222    check_model(instance.get_property("prop").unwrap(), &[14., 15., 16.]);
2223
2224    assert_eq!(instance.set_property("prop", Value::Number(42.)), Err(SetPropertyError::WrongType));
2225    check_model(instance.get_property("prop").unwrap(), &[14., 15., 16.]);
2226    assert_eq!(instance.set_property("prop", model_with_string), Err(SetPropertyError::WrongType));
2227    check_model(instance.get_property("prop").unwrap(), &[14., 15., 16.]);
2228
2229    assert_eq!(instance.set_property("prop", empty_model), Ok(()));
2230    check_model(instance.get_property("prop").unwrap(), &[]);
2231}
2232
2233#[test]
2234fn lang_type_to_value_type() {
2235    use i_slint_compiler::langtype::Struct as LangStruct;
2236    use std::collections::BTreeMap;
2237
2238    assert_eq!(ValueType::from(LangType::Void), ValueType::Void);
2239    assert_eq!(ValueType::from(LangType::Float32), ValueType::Number);
2240    assert_eq!(ValueType::from(LangType::Int32), ValueType::Number);
2241    assert_eq!(ValueType::from(LangType::Duration), ValueType::Number);
2242    assert_eq!(ValueType::from(LangType::Angle), ValueType::Number);
2243    assert_eq!(ValueType::from(LangType::PhysicalLength), ValueType::Number);
2244    assert_eq!(ValueType::from(LangType::LogicalLength), ValueType::Number);
2245    assert_eq!(ValueType::from(LangType::Percent), ValueType::Number);
2246    assert_eq!(ValueType::from(LangType::UnitProduct(Vec::new())), ValueType::Number);
2247    assert_eq!(ValueType::from(LangType::String), ValueType::String);
2248    assert_eq!(ValueType::from(LangType::Color), ValueType::Brush);
2249    assert_eq!(ValueType::from(LangType::Brush), ValueType::Brush);
2250    assert_eq!(ValueType::from(LangType::Array(Rc::new(LangType::Void))), ValueType::Model);
2251    assert_eq!(ValueType::from(LangType::Bool), ValueType::Bool);
2252    assert_eq!(
2253        ValueType::from(LangType::Struct(Rc::new(LangStruct {
2254            fields: BTreeMap::default(),
2255            name: i_slint_compiler::langtype::StructName::None,
2256        }))),
2257        ValueType::Struct
2258    );
2259    assert_eq!(ValueType::from(LangType::Image), ValueType::Image);
2260}
2261
2262#[test]
2263fn test_multi_components() {
2264    i_slint_backend_testing::init_no_event_loop();
2265    let result = spin_on::spin_on(
2266        Compiler::default().build_from_source(
2267            r#"
2268        export struct Settings {
2269            string_value: string,
2270        }
2271        export global ExpGlo { in-out property <int> test: 42; }
2272        component Common {
2273            in-out property <Settings> settings: { string_value: "Hello", };
2274        }
2275        export component Xyz inherits Window {
2276            in-out property <int> aaa: 8;
2277        }
2278        export component Foo {
2279
2280            in-out property <int> test: 42;
2281            c := Common {}
2282        }
2283        export component Bar inherits Window {
2284            in-out property <int> blah: 78;
2285            c := Common {}
2286        }
2287        "#
2288            .into(),
2289            PathBuf::from("hello.slint"),
2290        ),
2291    );
2292
2293    assert!(!result.has_errors(), "Error {:?}", result.diagnostics().collect::<Vec<_>>());
2294    let mut components = result.component_names().collect::<Vec<_>>();
2295    components.sort();
2296    assert_eq!(components, vec!["Bar", "Xyz"]);
2297    let diag = result.diagnostics().collect::<Vec<_>>();
2298    assert_eq!(diag.len(), 1);
2299    assert_eq!(diag[0].level(), DiagnosticLevel::Warning);
2300    assert_eq!(
2301        diag[0].message(),
2302        "Exported component 'Foo' doesn't inherit Window. No code will be generated for it"
2303    );
2304
2305    let comp1 = result.component("Xyz").unwrap();
2306    assert_eq!(comp1.name(), "Xyz");
2307    let instance1a = comp1.create().unwrap();
2308    let comp2 = result.component("Bar").unwrap();
2309    let instance2 = comp2.create().unwrap();
2310    let instance1b = comp1.create().unwrap();
2311
2312    // globals are not shared between instances
2313    assert_eq!(instance1a.get_global_property("ExpGlo", "test"), Ok(Value::Number(42.0)));
2314    assert_eq!(instance1a.set_global_property("ExpGlo", "test", Value::Number(88.0)), Ok(()));
2315    assert_eq!(instance2.get_global_property("ExpGlo", "test"), Ok(Value::Number(42.0)));
2316    assert_eq!(instance1b.get_global_property("ExpGlo", "test"), Ok(Value::Number(42.0)));
2317    assert_eq!(instance1a.get_global_property("ExpGlo", "test"), Ok(Value::Number(88.0)));
2318
2319    assert!(result.component("Settings").is_none());
2320    assert!(result.component("Foo").is_none());
2321    assert!(result.component("Common").is_none());
2322    assert!(result.component("ExpGlo").is_none());
2323    assert!(result.component("xyz").is_none());
2324}
2325
2326#[cfg(all(test, feature = "internal-highlight"))]
2327fn compile(code: &str) -> (ComponentInstance, PathBuf) {
2328    i_slint_backend_testing::init_no_event_loop();
2329    let mut compiler = Compiler::default();
2330    compiler.set_style("fluent".into());
2331    let path = PathBuf::from("/tmp/test.slint");
2332
2333    let compile_result =
2334        spin_on::spin_on(compiler.build_from_source(code.to_string(), path.clone()));
2335
2336    for d in &compile_result.diagnostics {
2337        eprintln!("{d}");
2338    }
2339
2340    assert!(!compile_result.has_errors());
2341
2342    let definition = compile_result.components().next().unwrap();
2343    let instance = definition.create().unwrap();
2344
2345    (instance, path)
2346}
2347
2348#[cfg(feature = "internal-highlight")]
2349#[test]
2350fn test_element_node_at_source_code_position() {
2351    let code = r#"
2352component Bar1 {}
2353
2354component Foo1 {
2355}
2356
2357export component Foo2 inherits Window  {
2358    Bar1 {}
2359    Foo1   {}
2360}"#;
2361
2362    let (handle, path) = compile(code);
2363
2364    for i in 0..code.len() as u32 {
2365        let elements = handle.element_node_at_source_code_position(&path, i);
2366        eprintln!("{i}: {}", code.as_bytes()[i as usize] as char);
2367        match i {
2368            16 => assert_eq!(elements.len(), 1),       // Bar1 (def)
2369            35 => assert_eq!(elements.len(), 1),       // Foo1 (def)
2370            71..=78 => assert_eq!(elements.len(), 1),  // Window + WS (from Foo2)
2371            85..=89 => assert_eq!(elements.len(), 1),  // Bar1 + WS (use)
2372            97..=103 => assert_eq!(elements.len(), 1), // Foo1 + WS (use)
2373            _ => assert!(elements.is_empty()),
2374        }
2375    }
2376}
2377
2378#[cfg(feature = "ffi")]
2379#[allow(missing_docs)]
2380#[path = "ffi.rs"]
2381pub(crate) mod ffi;