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