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