1use i_slint_compiler::langtype::Type as LangType;
5use i_slint_core::component_factory::ComponentFactory;
6#[cfg(feature = "internal")]
7use i_slint_core::component_factory::FactoryContext;
8use i_slint_core::graphics::euclid::approxeq::ApproxEq as _;
9use i_slint_core::model::{Model, ModelExt, ModelRc};
10#[cfg(feature = "internal")]
11use i_slint_core::window::WindowInner;
12use i_slint_core::{PathData, SharedVector};
13use smol_str::SmolStr;
14use std::collections::HashMap;
15use std::future::Future;
16use std::path::{Path, PathBuf};
17use std::rc::Rc;
18
19#[doc(inline)]
20pub use i_slint_compiler::diagnostics::{Diagnostic, DiagnosticLevel};
21
22pub use i_slint_core::api::*;
23pub use i_slint_backend_selector::api::*;
25pub use i_slint_core::graphics::{
26 Brush, Color, Image, LoadImageError, Rgb8Pixel, Rgba8Pixel, RgbaColor, SharedPixelBuffer,
27};
28use i_slint_core::items::*;
29
30use crate::dynamic_item_tree::{ErasedItemTreeBox, WindowOptions};
31
32#[derive(Debug, Copy, Clone, PartialEq)]
35#[repr(i8)]
36#[non_exhaustive]
37pub enum ValueType {
38 Void,
40 Number,
42 String,
44 Bool,
46 Model,
48 Struct,
50 Brush,
52 Image,
54 #[doc(hidden)]
56 Other = -1,
57}
58
59impl From<LangType> for ValueType {
60 fn from(ty: LangType) -> Self {
61 match ty {
62 LangType::Float32
63 | LangType::Int32
64 | LangType::Duration
65 | LangType::Angle
66 | LangType::PhysicalLength
67 | LangType::LogicalLength
68 | LangType::Percent
69 | LangType::UnitProduct(_) => Self::Number,
70 LangType::String => Self::String,
71 LangType::Color => Self::Brush,
72 LangType::Brush => Self::Brush,
73 LangType::Array(_) => Self::Model,
74 LangType::Bool => Self::Bool,
75 LangType::Struct { .. } => Self::Struct,
76 LangType::Void => Self::Void,
77 LangType::Image => Self::Image,
78 _ => Self::Other,
79 }
80 }
81}
82
83#[derive(Clone, Default)]
95#[non_exhaustive]
96#[repr(u8)]
97pub enum Value {
98 #[default]
101 Void = 0,
102 Number(f64) = 1,
104 String(SharedString) = 2,
106 Bool(bool) = 3,
108 Image(Image) = 4,
110 Model(ModelRc<Value>) = 5,
112 Struct(Struct) = 6,
114 Brush(Brush) = 7,
116 #[doc(hidden)]
117 PathData(PathData) = 8,
119 #[doc(hidden)]
120 EasingCurve(i_slint_core::animations::EasingCurve) = 9,
122 #[doc(hidden)]
123 EnumerationValue(String, String) = 10,
126 #[doc(hidden)]
127 LayoutCache(SharedVector<f32>) = 11,
128 #[doc(hidden)]
129 ComponentFactory(ComponentFactory) = 12,
131}
132
133impl Value {
134 pub fn value_type(&self) -> ValueType {
136 match self {
137 Value::Void => ValueType::Void,
138 Value::Number(_) => ValueType::Number,
139 Value::String(_) => ValueType::String,
140 Value::Bool(_) => ValueType::Bool,
141 Value::Model(_) => ValueType::Model,
142 Value::Struct(_) => ValueType::Struct,
143 Value::Brush(_) => ValueType::Brush,
144 Value::Image(_) => ValueType::Image,
145 _ => ValueType::Other,
146 }
147 }
148}
149
150impl PartialEq for Value {
151 fn eq(&self, other: &Self) -> bool {
152 match self {
153 Value::Void => matches!(other, Value::Void),
154 Value::Number(lhs) => matches!(other, Value::Number(rhs) if lhs.approx_eq(rhs)),
155 Value::String(lhs) => matches!(other, Value::String(rhs) if lhs == rhs),
156 Value::Bool(lhs) => matches!(other, Value::Bool(rhs) if lhs == rhs),
157 Value::Image(lhs) => matches!(other, Value::Image(rhs) if lhs == rhs),
158 Value::Model(lhs) => {
159 if let Value::Model(rhs) = other {
160 lhs == rhs
161 } else {
162 false
163 }
164 }
165 Value::Struct(lhs) => matches!(other, Value::Struct(rhs) if lhs == rhs),
166 Value::Brush(lhs) => matches!(other, Value::Brush(rhs) if lhs == rhs),
167 Value::PathData(lhs) => matches!(other, Value::PathData(rhs) if lhs == rhs),
168 Value::EasingCurve(lhs) => matches!(other, Value::EasingCurve(rhs) if lhs == rhs),
169 Value::EnumerationValue(lhs_name, lhs_value) => {
170 matches!(other, Value::EnumerationValue(rhs_name, rhs_value) if lhs_name == rhs_name && lhs_value == rhs_value)
171 }
172 Value::LayoutCache(lhs) => matches!(other, Value::LayoutCache(rhs) if lhs == rhs),
173 Value::ComponentFactory(lhs) => {
174 matches!(other, Value::ComponentFactory(rhs) if lhs == rhs)
175 }
176 }
177 }
178}
179
180impl std::fmt::Debug for Value {
181 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
182 match self {
183 Value::Void => write!(f, "Value::Void"),
184 Value::Number(n) => write!(f, "Value::Number({n:?})"),
185 Value::String(s) => write!(f, "Value::String({s:?})"),
186 Value::Bool(b) => write!(f, "Value::Bool({b:?})"),
187 Value::Image(i) => write!(f, "Value::Image({i:?})"),
188 Value::Model(m) => {
189 write!(f, "Value::Model(")?;
190 f.debug_list().entries(m.iter()).finish()?;
191 write!(f, "])")
192 }
193 Value::Struct(s) => write!(f, "Value::Struct({s:?})"),
194 Value::Brush(b) => write!(f, "Value::Brush({b:?})"),
195 Value::PathData(e) => write!(f, "Value::PathElements({e:?})"),
196 Value::EasingCurve(c) => write!(f, "Value::EasingCurve({c:?})"),
197 Value::EnumerationValue(n, v) => write!(f, "Value::EnumerationValue({n:?}, {v:?})"),
198 Value::LayoutCache(v) => write!(f, "Value::LayoutCache({v:?})"),
199 Value::ComponentFactory(factory) => write!(f, "Value::ComponentFactory({factory:?})"),
200 }
201 }
202}
203
204macro_rules! declare_value_conversion {
213 ( $value:ident => [$($ty:ty),*] ) => {
214 $(
215 impl From<$ty> for Value {
216 fn from(v: $ty) -> Self {
217 Value::$value(v as _)
218 }
219 }
220 impl TryFrom<Value> for $ty {
221 type Error = Value;
222 fn try_from(v: Value) -> Result<$ty, Self::Error> {
223 match v {
224 Value::$value(x) => Ok(x as _),
225 _ => Err(v)
226 }
227 }
228 }
229 )*
230 };
231}
232declare_value_conversion!(Number => [u32, u64, i32, i64, f32, f64, usize, isize] );
233declare_value_conversion!(String => [SharedString] );
234declare_value_conversion!(Bool => [bool] );
235declare_value_conversion!(Image => [Image] );
236declare_value_conversion!(Struct => [Struct] );
237declare_value_conversion!(Brush => [Brush] );
238declare_value_conversion!(PathData => [PathData]);
239declare_value_conversion!(EasingCurve => [i_slint_core::animations::EasingCurve]);
240declare_value_conversion!(LayoutCache => [SharedVector<f32>] );
241declare_value_conversion!(ComponentFactory => [ComponentFactory] );
242
243macro_rules! declare_value_struct_conversion {
245 (struct $name:path { $($field:ident),* $(, ..$extra:expr)? }) => {
246 impl From<$name> for Value {
247 fn from($name { $($field),* , .. }: $name) -> Self {
248 let mut struct_ = Struct::default();
249 $(struct_.set_field(stringify!($field).into(), $field.into());)*
250 Value::Struct(struct_)
251 }
252 }
253 impl TryFrom<Value> for $name {
254 type Error = ();
255 fn try_from(v: Value) -> Result<$name, Self::Error> {
256 #[allow(clippy::field_reassign_with_default)]
257 match v {
258 Value::Struct(x) => {
259 type Ty = $name;
260 #[allow(unused)]
261 let mut res: Ty = Ty::default();
262 $(let mut res: Ty = $extra;)?
263 $(res.$field = x.get_field(stringify!($field)).ok_or(())?.clone().try_into().map_err(|_|())?;)*
264 Ok(res)
265 }
266 _ => Err(()),
267 }
268 }
269 }
270 };
271 ($(
272 $(#[$struct_attr:meta])*
273 struct $Name:ident {
274 @name = $inner_name:literal
275 export {
276 $( $(#[$pub_attr:meta])* $pub_field:ident : $pub_type:ty, )*
277 }
278 private {
279 $( $(#[$pri_attr:meta])* $pri_field:ident : $pri_type:ty, )*
280 }
281 }
282 )*) => {
283 $(
284 impl From<$Name> for Value {
285 fn from(item: $Name) -> Self {
286 let mut struct_ = Struct::default();
287 $(struct_.set_field(stringify!($pub_field).into(), item.$pub_field.into());)*
288 $(handle_private!(SET $Name $pri_field, struct_, item);)*
289 Value::Struct(struct_)
290 }
291 }
292 impl TryFrom<Value> for $Name {
293 type Error = ();
294 fn try_from(v: Value) -> Result<$Name, Self::Error> {
295 #[allow(clippy::field_reassign_with_default)]
296 match v {
297 Value::Struct(x) => {
298 type Ty = $Name;
299 #[allow(unused)]
300 let mut res: Ty = Ty::default();
301 $(res.$pub_field = x.get_field(stringify!($pub_field)).ok_or(())?.clone().try_into().map_err(|_|())?;)*
302 $(handle_private!(GET $Name $pri_field, x, res);)*
303 Ok(res)
304 }
305 _ => Err(()),
306 }
307 }
308 }
309 )*
310 };
311}
312
313macro_rules! handle_private {
314 (SET StateInfo $field:ident, $struct_:ident, $item:ident) => {
315 $struct_.set_field(stringify!($field).into(), $item.$field.into())
316 };
317 (SET $_:ident $field:ident, $struct_:ident, $item:ident) => {{}};
318 (GET StateInfo $field:ident, $struct_:ident, $item:ident) => {
319 $item.$field =
320 $struct_.get_field(stringify!($field)).ok_or(())?.clone().try_into().map_err(|_| ())?
321 };
322 (GET $_:ident $field:ident, $struct_:ident, $item:ident) => {{}};
323}
324
325declare_value_struct_conversion!(struct i_slint_core::layout::LayoutInfo { min, max, min_percent, max_percent, preferred, stretch });
326declare_value_struct_conversion!(struct i_slint_core::graphics::Point { x, y, ..Default::default()});
327declare_value_struct_conversion!(struct i_slint_core::api::LogicalPosition { x, y });
328
329i_slint_common::for_each_builtin_structs!(declare_value_struct_conversion);
330
331macro_rules! declare_value_enum_conversion {
336 ($( $(#[$enum_doc:meta])* enum $Name:ident { $($body:tt)* })*) => { $(
337 impl From<i_slint_core::items::$Name> for Value {
338 fn from(v: i_slint_core::items::$Name) -> Self {
339 Value::EnumerationValue(stringify!($Name).to_owned(), v.to_string())
340 }
341 }
342 impl TryFrom<Value> for i_slint_core::items::$Name {
343 type Error = ();
344 fn try_from(v: Value) -> Result<i_slint_core::items::$Name, ()> {
345 use std::str::FromStr;
346 match v {
347 Value::EnumerationValue(enumeration, value) => {
348 if enumeration != stringify!($Name) {
349 return Err(());
350 }
351 i_slint_core::items::$Name::from_str(value.as_str()).map_err(|_| ())
352 }
353 _ => Err(()),
354 }
355 }
356 }
357 )*};
358}
359
360i_slint_common::for_each_enums!(declare_value_enum_conversion);
361
362impl From<i_slint_core::animations::Instant> for Value {
363 fn from(value: i_slint_core::animations::Instant) -> Self {
364 Value::Number(value.0 as _)
365 }
366}
367impl TryFrom<Value> for i_slint_core::animations::Instant {
368 type Error = ();
369 fn try_from(v: Value) -> Result<i_slint_core::animations::Instant, Self::Error> {
370 match v {
371 Value::Number(x) => Ok(i_slint_core::animations::Instant(x as _)),
372 _ => Err(()),
373 }
374 }
375}
376
377impl From<()> for Value {
378 #[inline]
379 fn from(_: ()) -> Self {
380 Value::Void
381 }
382}
383impl TryFrom<Value> for () {
384 type Error = ();
385 #[inline]
386 fn try_from(_: Value) -> Result<(), Self::Error> {
387 Ok(())
388 }
389}
390
391impl From<Color> for Value {
392 #[inline]
393 fn from(c: Color) -> Self {
394 Value::Brush(Brush::SolidColor(c))
395 }
396}
397impl TryFrom<Value> for Color {
398 type Error = Value;
399 #[inline]
400 fn try_from(v: Value) -> Result<Color, Self::Error> {
401 match v {
402 Value::Brush(Brush::SolidColor(c)) => Ok(c),
403 _ => Err(v),
404 }
405 }
406}
407
408impl From<i_slint_core::lengths::LogicalLength> for Value {
409 #[inline]
410 fn from(l: i_slint_core::lengths::LogicalLength) -> Self {
411 Value::Number(l.get() as _)
412 }
413}
414impl TryFrom<Value> for i_slint_core::lengths::LogicalLength {
415 type Error = Value;
416 #[inline]
417 fn try_from(v: Value) -> Result<i_slint_core::lengths::LogicalLength, Self::Error> {
418 match v {
419 Value::Number(n) => Ok(i_slint_core::lengths::LogicalLength::new(n as _)),
420 _ => Err(v),
421 }
422 }
423}
424
425impl<T: Into<Value> + TryFrom<Value> + 'static> From<ModelRc<T>> for Value {
426 fn from(m: ModelRc<T>) -> Self {
427 if let Some(v) = <dyn core::any::Any>::downcast_ref::<ModelRc<Value>>(&m) {
428 Value::Model(v.clone())
429 } else {
430 Value::Model(ModelRc::new(crate::value_model::ValueMapModel(m)))
431 }
432 }
433}
434impl<T: TryFrom<Value> + Default + 'static> TryFrom<Value> for ModelRc<T> {
435 type Error = Value;
436 #[inline]
437 fn try_from(v: Value) -> Result<ModelRc<T>, Self::Error> {
438 match v {
439 Value::Model(m) => {
440 if let Some(v) = <dyn core::any::Any>::downcast_ref::<ModelRc<T>>(&m) {
441 Ok(v.clone())
442 } else if let Some(v) =
443 m.as_any().downcast_ref::<crate::value_model::ValueMapModel<T>>()
444 {
445 Ok(v.0.clone())
446 } else {
447 Ok(ModelRc::new(m.map(|v| T::try_from(v).unwrap_or_default())))
448 }
449 }
450 _ => Err(v),
451 }
452 }
453}
454
455#[test]
456fn value_model_conversion() {
457 use i_slint_core::model::*;
458 let m = ModelRc::new(VecModel::from_slice(&[Value::Number(42.), Value::Number(12.)]));
459 let v = Value::from(m.clone());
460 assert_eq!(v, Value::Model(m.clone()));
461 let m2: ModelRc<Value> = v.clone().try_into().unwrap();
462 assert_eq!(m2, m);
463
464 let int_model: ModelRc<i32> = v.clone().try_into().unwrap();
465 assert_eq!(int_model.row_count(), 2);
466 assert_eq!(int_model.iter().collect::<Vec<_>>(), vec![42, 12]);
467
468 let Value::Model(m3) = int_model.clone().into() else { panic!("not a model?") };
469 assert_eq!(m3.row_count(), 2);
470 assert_eq!(m3.iter().collect::<Vec<_>>(), vec![Value::Number(42.), Value::Number(12.)]);
471
472 let str_model: ModelRc<SharedString> = v.clone().try_into().unwrap();
473 assert_eq!(str_model.row_count(), 2);
474 assert_eq!(str_model.iter().collect::<Vec<_>>(), vec!["", ""]);
476
477 let err: Result<ModelRc<Value>, _> = Value::Bool(true).try_into();
478 assert!(err.is_err());
479
480 let model =
481 Rc::new(VecModel::<SharedString>::from_iter(["foo".into(), "bar".into(), "baz".into()]));
482
483 let value: Value = ModelRc::from(model.clone()).into();
484 let value_model: ModelRc<Value> = value.clone().try_into().unwrap();
485 assert_eq!(value_model.row_data(2).unwrap(), Value::String("baz".into()));
486 value_model.set_row_data(1, Value::String("qux".into()));
487 value_model.set_row_data(0, Value::Bool(true));
488 assert_eq!(value_model.row_data(1).unwrap(), Value::String("qux".into()));
489 assert_eq!(value_model.row_data(0).unwrap(), Value::String("foo".into()));
491
492 assert_eq!(model.row_data(1).unwrap(), SharedString::from("qux"));
494 assert_eq!(model.row_data(0).unwrap(), SharedString::from("foo"));
495
496 let the_model: ModelRc<SharedString> = value.try_into().unwrap();
497 assert_eq!(the_model.row_data(1).unwrap(), SharedString::from("qux"));
498 assert_eq!(
499 model.as_ref() as *const VecModel<SharedString>,
500 the_model.as_any().downcast_ref::<VecModel<SharedString>>().unwrap()
501 as *const VecModel<SharedString>
502 );
503}
504
505pub(crate) fn normalize_identifier(ident: &str) -> SmolStr {
506 i_slint_compiler::parser::normalize_identifier(ident)
507}
508
509#[derive(Clone, PartialEq, Debug, Default)]
531pub struct Struct(pub(crate) HashMap<SmolStr, Value>);
532impl Struct {
533 pub fn get_field(&self, name: &str) -> Option<&Value> {
535 self.0.get(&*normalize_identifier(name))
536 }
537 pub fn set_field(&mut self, name: String, value: Value) {
539 self.0.insert(normalize_identifier(&name), value);
540 }
541
542 pub fn iter(&self) -> impl Iterator<Item = (&str, &Value)> {
544 self.0.iter().map(|(a, b)| (a.as_str(), b))
545 }
546}
547
548impl FromIterator<(String, Value)> for Struct {
549 fn from_iter<T: IntoIterator<Item = (String, Value)>>(iter: T) -> Self {
550 Self(iter.into_iter().map(|(s, v)| (normalize_identifier(&s), v)).collect())
551 }
552}
553
554#[deprecated(note = "Use slint_interpreter::Compiler instead")]
556pub struct ComponentCompiler {
557 config: i_slint_compiler::CompilerConfiguration,
558 diagnostics: Vec<Diagnostic>,
559}
560
561#[allow(deprecated)]
562impl Default for ComponentCompiler {
563 fn default() -> Self {
564 let mut config = i_slint_compiler::CompilerConfiguration::new(
565 i_slint_compiler::generator::OutputFormat::Interpreter,
566 );
567 config.components_to_generate = i_slint_compiler::ComponentSelection::LastExported;
568 Self { config, diagnostics: vec![] }
569 }
570}
571
572#[allow(deprecated)]
573impl ComponentCompiler {
574 pub fn new() -> Self {
576 Self::default()
577 }
578
579 pub fn set_include_paths(&mut self, include_paths: Vec<std::path::PathBuf>) {
581 self.config.include_paths = include_paths;
582 }
583
584 pub fn include_paths(&self) -> &Vec<std::path::PathBuf> {
586 &self.config.include_paths
587 }
588
589 pub fn set_library_paths(&mut self, library_paths: HashMap<String, PathBuf>) {
591 self.config.library_paths = library_paths;
592 }
593
594 pub fn library_paths(&self) -> &HashMap<String, PathBuf> {
596 &self.config.library_paths
597 }
598
599 pub fn set_style(&mut self, style: String) {
611 self.config.style = Some(style);
612 }
613
614 pub fn style(&self) -> Option<&String> {
616 self.config.style.as_ref()
617 }
618
619 pub fn set_translation_domain(&mut self, domain: String) {
621 self.config.translation_domain = Some(domain);
622 }
623
624 pub fn set_file_loader(
632 &mut self,
633 file_loader_fallback: impl Fn(&Path) -> core::pin::Pin<Box<dyn Future<Output = Option<std::io::Result<String>>>>>
634 + 'static,
635 ) {
636 self.config.open_import_fallback =
637 Some(Rc::new(move |path| file_loader_fallback(Path::new(path.as_str()))));
638 }
639
640 pub fn diagnostics(&self) -> &Vec<Diagnostic> {
642 &self.diagnostics
643 }
644
645 pub async fn build_from_path<P: AsRef<Path>>(
664 &mut self,
665 path: P,
666 ) -> Option<ComponentDefinition> {
667 let path = path.as_ref();
668 let source = match i_slint_compiler::diagnostics::load_from_path(path) {
669 Ok(s) => s,
670 Err(d) => {
671 self.diagnostics = vec![d];
672 return None;
673 }
674 };
675
676 let r = crate::dynamic_item_tree::load(source, path.into(), self.config.clone()).await;
677 self.diagnostics = r.diagnostics.into_iter().collect();
678 r.components.into_values().next()
679 }
680
681 pub async fn build_from_source(
698 &mut self,
699 source_code: String,
700 path: PathBuf,
701 ) -> Option<ComponentDefinition> {
702 let r = crate::dynamic_item_tree::load(source_code, path, self.config.clone()).await;
703 self.diagnostics = r.diagnostics.into_iter().collect();
704 r.components.into_values().next()
705 }
706}
707
708pub struct Compiler {
711 config: i_slint_compiler::CompilerConfiguration,
712}
713
714impl Default for Compiler {
715 fn default() -> Self {
716 let config = i_slint_compiler::CompilerConfiguration::new(
717 i_slint_compiler::generator::OutputFormat::Interpreter,
718 );
719 Self { config }
720 }
721}
722
723impl Compiler {
724 pub fn new() -> Self {
726 Self::default()
727 }
728
729 #[doc(hidden)]
733 #[cfg(feature = "internal")]
734 pub fn compiler_configuration(
735 &mut self,
736 _: i_slint_core::InternalToken,
737 ) -> &mut i_slint_compiler::CompilerConfiguration {
738 &mut self.config
739 }
740
741 pub fn set_include_paths(&mut self, include_paths: Vec<std::path::PathBuf>) {
743 self.config.include_paths = include_paths;
744 }
745
746 pub fn include_paths(&self) -> &Vec<std::path::PathBuf> {
748 &self.config.include_paths
749 }
750
751 pub fn set_library_paths(&mut self, library_paths: HashMap<String, PathBuf>) {
753 self.config.library_paths = library_paths;
754 }
755
756 pub fn library_paths(&self) -> &HashMap<String, PathBuf> {
758 &self.config.library_paths
759 }
760
761 pub fn set_style(&mut self, style: String) {
772 self.config.style = Some(style);
773 }
774
775 pub fn style(&self) -> Option<&String> {
777 self.config.style.as_ref()
778 }
779
780 pub fn set_translation_domain(&mut self, domain: String) {
782 self.config.translation_domain = Some(domain);
783 }
784
785 pub fn set_file_loader(
793 &mut self,
794 file_loader_fallback: impl Fn(&Path) -> core::pin::Pin<Box<dyn Future<Output = Option<std::io::Result<String>>>>>
795 + 'static,
796 ) {
797 self.config.open_import_fallback =
798 Some(Rc::new(move |path| file_loader_fallback(Path::new(path.as_str()))));
799 }
800
801 pub async fn build_from_path<P: AsRef<Path>>(&self, path: P) -> CompilationResult {
820 let path = path.as_ref();
821 let source = match i_slint_compiler::diagnostics::load_from_path(path) {
822 Ok(s) => s,
823 Err(d) => {
824 let mut diagnostics = i_slint_compiler::diagnostics::BuildDiagnostics::default();
825 diagnostics.push_compiler_error(d);
826 return CompilationResult {
827 components: HashMap::new(),
828 diagnostics: diagnostics.into_iter().collect(),
829 #[cfg(feature = "internal")]
830 structs_and_enums: Vec::new(),
831 #[cfg(feature = "internal")]
832 named_exports: Vec::new(),
833 };
834 }
835 };
836
837 crate::dynamic_item_tree::load(source, path.into(), self.config.clone()).await
838 }
839
840 pub async fn build_from_source(&self, source_code: String, path: PathBuf) -> CompilationResult {
853 crate::dynamic_item_tree::load(source_code, path, self.config.clone()).await
854 }
855}
856
857#[derive(Clone)]
864pub struct CompilationResult {
865 pub(crate) components: HashMap<String, ComponentDefinition>,
866 pub(crate) diagnostics: Vec<Diagnostic>,
867 #[cfg(feature = "internal")]
868 pub(crate) structs_and_enums: Vec<LangType>,
869 #[cfg(feature = "internal")]
871 pub(crate) named_exports: Vec<(String, String)>,
872}
873
874impl core::fmt::Debug for CompilationResult {
875 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
876 f.debug_struct("CompilationResult")
877 .field("components", &self.components.keys())
878 .field("diagnostics", &self.diagnostics)
879 .finish()
880 }
881}
882
883impl CompilationResult {
884 pub fn has_errors(&self) -> bool {
887 self.diagnostics().any(|diag| diag.level() == DiagnosticLevel::Error)
888 }
889
890 pub fn diagnostics(&self) -> impl Iterator<Item = Diagnostic> + '_ {
894 self.diagnostics.iter().cloned()
895 }
896
897 #[cfg(feature = "display-diagnostics")]
903 pub fn print_diagnostics(&self) {
904 print_diagnostics(&self.diagnostics)
905 }
906
907 pub fn components(&self) -> impl Iterator<Item = ComponentDefinition> + '_ {
909 self.components.values().cloned()
910 }
911
912 pub fn component_names(&self) -> impl Iterator<Item = &str> + '_ {
914 self.components.keys().map(|s| s.as_str())
915 }
916
917 pub fn component(&self, name: &str) -> Option<ComponentDefinition> {
920 self.components.get(name).cloned()
921 }
922
923 #[doc(hidden)]
925 #[cfg(feature = "internal")]
926 pub fn structs_and_enums(
927 &self,
928 _: i_slint_core::InternalToken,
929 ) -> impl Iterator<Item = &LangType> {
930 self.structs_and_enums.iter()
931 }
932
933 #[doc(hidden)]
936 #[cfg(feature = "internal")]
937 pub fn named_exports(
938 &self,
939 _: i_slint_core::InternalToken,
940 ) -> impl Iterator<Item = &(String, String)> {
941 self.named_exports.iter()
942 }
943}
944
945#[derive(Clone)]
953pub struct ComponentDefinition {
954 pub(crate) inner: crate::dynamic_item_tree::ErasedItemTreeDescription,
955}
956
957impl ComponentDefinition {
958 #[doc(hidden)]
960 #[cfg(feature = "internal")]
961 pub fn set_debug_handler(
962 &self,
963 handler: impl Fn(Option<&i_slint_compiler::diagnostics::SourceLocation>, &str) + 'static,
964 _: i_slint_core::InternalToken,
965 ) {
966 let handler = Rc::new(handler);
967
968 generativity::make_guard!(guard);
969 self.inner.unerase(guard).recursively_set_debug_handler(handler);
970 }
971 pub fn create(&self) -> Result<ComponentInstance, PlatformError> {
973 let instance = self.create_with_options(Default::default())?;
974 instance.inner.window_adapter_ref()?;
976 Ok(instance)
977 }
978
979 #[doc(hidden)]
981 #[cfg(feature = "internal")]
982 pub fn create_embedded(&self, ctx: FactoryContext) -> Result<ComponentInstance, PlatformError> {
983 self.create_with_options(WindowOptions::Embed {
984 parent_item_tree: ctx.parent_item_tree,
985 parent_item_tree_index: ctx.parent_item_tree_index,
986 })
987 }
988
989 #[doc(hidden)]
991 #[cfg(feature = "internal")]
992 pub fn create_with_existing_window(
993 &self,
994 window: &Window,
995 ) -> Result<ComponentInstance, PlatformError> {
996 self.create_with_options(WindowOptions::UseExistingWindow(
997 WindowInner::from_pub(window).window_adapter(),
998 ))
999 }
1000
1001 pub(crate) fn create_with_options(
1003 &self,
1004 options: WindowOptions,
1005 ) -> Result<ComponentInstance, PlatformError> {
1006 generativity::make_guard!(guard);
1007 Ok(ComponentInstance { inner: self.inner.unerase(guard).clone().create(options)? })
1008 }
1009
1010 #[doc(hidden)]
1014 #[cfg(feature = "internal")]
1015 pub fn properties_and_callbacks(
1016 &self,
1017 ) -> impl Iterator<
1018 Item = (
1019 String,
1020 (i_slint_compiler::langtype::Type, i_slint_compiler::object_tree::PropertyVisibility),
1021 ),
1022 > + '_ {
1023 let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1026 self.inner.unerase(guard).properties().map(|(s, t, v)| (s.to_string(), (t, v)))
1027 }
1028
1029 pub fn properties(&self) -> impl Iterator<Item = (String, ValueType)> + '_ {
1032 let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1035 self.inner.unerase(guard).properties().filter_map(|(prop_name, prop_type, _)| {
1036 if prop_type.is_property_type() {
1037 Some((prop_name.to_string(), prop_type.into()))
1038 } else {
1039 None
1040 }
1041 })
1042 }
1043
1044 pub fn callbacks(&self) -> impl Iterator<Item = String> + '_ {
1046 let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1049 self.inner.unerase(guard).properties().filter_map(|(prop_name, prop_type, _)| {
1050 if matches!(prop_type, LangType::Callback { .. }) {
1051 Some(prop_name.to_string())
1052 } else {
1053 None
1054 }
1055 })
1056 }
1057
1058 pub fn functions(&self) -> impl Iterator<Item = String> + '_ {
1060 let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1063 self.inner.unerase(guard).properties().filter_map(|(prop_name, prop_type, _)| {
1064 if matches!(prop_type, LangType::Function { .. }) {
1065 Some(prop_name.to_string())
1066 } else {
1067 None
1068 }
1069 })
1070 }
1071
1072 pub fn globals(&self) -> impl Iterator<Item = String> + '_ {
1077 let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1080 self.inner.unerase(guard).global_names().map(|s| s.to_string())
1081 }
1082
1083 #[doc(hidden)]
1087 #[cfg(feature = "internal")]
1088 pub fn global_properties_and_callbacks(
1089 &self,
1090 global_name: &str,
1091 ) -> Option<
1092 impl Iterator<
1093 Item = (
1094 String,
1095 (
1096 i_slint_compiler::langtype::Type,
1097 i_slint_compiler::object_tree::PropertyVisibility,
1098 ),
1099 ),
1100 > + '_,
1101 > {
1102 let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1105 self.inner
1106 .unerase(guard)
1107 .global_properties(global_name)
1108 .map(|o| o.map(|(s, t, v)| (s.to_string(), (t, v))))
1109 }
1110
1111 pub fn global_properties(
1113 &self,
1114 global_name: &str,
1115 ) -> Option<impl Iterator<Item = (String, ValueType)> + '_> {
1116 let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1119 self.inner.unerase(guard).global_properties(global_name).map(|iter| {
1120 iter.filter_map(|(prop_name, prop_type, _)| {
1121 if prop_type.is_property_type() {
1122 Some((prop_name.to_string(), prop_type.into()))
1123 } else {
1124 None
1125 }
1126 })
1127 })
1128 }
1129
1130 pub fn global_callbacks(&self, global_name: &str) -> Option<impl Iterator<Item = String> + '_> {
1132 let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1135 self.inner.unerase(guard).global_properties(global_name).map(|iter| {
1136 iter.filter_map(|(prop_name, prop_type, _)| {
1137 if matches!(prop_type, LangType::Callback { .. }) {
1138 Some(prop_name.to_string())
1139 } else {
1140 None
1141 }
1142 })
1143 })
1144 }
1145
1146 pub fn global_functions(&self, global_name: &str) -> Option<impl Iterator<Item = String> + '_> {
1148 let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1151 self.inner.unerase(guard).global_properties(global_name).map(|iter| {
1152 iter.filter_map(|(prop_name, prop_type, _)| {
1153 if matches!(prop_type, LangType::Function { .. }) {
1154 Some(prop_name.to_string())
1155 } else {
1156 None
1157 }
1158 })
1159 })
1160 }
1161
1162 pub fn name(&self) -> &str {
1164 let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1167 self.inner.unerase(guard).id()
1168 }
1169
1170 #[cfg(feature = "internal")]
1172 #[doc(hidden)]
1173 pub fn root_component(&self) -> Rc<i_slint_compiler::object_tree::Component> {
1174 let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1175 self.inner.unerase(guard).original.clone()
1176 }
1177
1178 #[cfg(feature = "internal-highlight")]
1182 pub fn type_loader(&self) -> std::rc::Rc<i_slint_compiler::typeloader::TypeLoader> {
1183 let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1184 self.inner.unerase(guard).type_loader.get().unwrap().clone()
1185 }
1186
1187 #[cfg(feature = "internal-highlight")]
1195 pub fn raw_type_loader(&self) -> Option<i_slint_compiler::typeloader::TypeLoader> {
1196 let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1197 self.inner
1198 .unerase(guard)
1199 .raw_type_loader
1200 .get()
1201 .unwrap()
1202 .as_ref()
1203 .and_then(|tl| i_slint_compiler::typeloader::snapshot(tl))
1204 }
1205}
1206
1207#[cfg(feature = "display-diagnostics")]
1213pub fn print_diagnostics(diagnostics: &[Diagnostic]) {
1214 let mut build_diagnostics = i_slint_compiler::diagnostics::BuildDiagnostics::default();
1215 for d in diagnostics {
1216 build_diagnostics.push_compiler_error(d.clone())
1217 }
1218 build_diagnostics.print();
1219}
1220
1221#[repr(C)]
1229pub struct ComponentInstance {
1230 pub(crate) inner: crate::dynamic_item_tree::DynamicComponentVRc,
1231}
1232
1233impl ComponentInstance {
1234 pub fn definition(&self) -> ComponentDefinition {
1236 generativity::make_guard!(guard);
1237 ComponentDefinition { inner: self.inner.unerase(guard).description().into() }
1238 }
1239
1240 pub fn get_property(&self, name: &str) -> Result<Value, GetPropertyError> {
1260 generativity::make_guard!(guard);
1261 let comp = self.inner.unerase(guard);
1262 let name = normalize_identifier(name);
1263
1264 if comp
1265 .description()
1266 .original
1267 .root_element
1268 .borrow()
1269 .property_declarations
1270 .get(&name)
1271 .is_none_or(|d| !d.expose_in_public_api)
1272 {
1273 return Err(GetPropertyError::NoSuchProperty);
1274 }
1275
1276 comp.description()
1277 .get_property(comp.borrow(), &name)
1278 .map_err(|()| GetPropertyError::NoSuchProperty)
1279 }
1280
1281 pub fn set_property(&self, name: &str, value: Value) -> Result<(), SetPropertyError> {
1283 let name = normalize_identifier(name);
1284 generativity::make_guard!(guard);
1285 let comp = self.inner.unerase(guard);
1286 let d = comp.description();
1287 let elem = d.original.root_element.borrow();
1288 let decl = elem.property_declarations.get(&name).ok_or(SetPropertyError::NoSuchProperty)?;
1289
1290 if !decl.expose_in_public_api {
1291 return Err(SetPropertyError::NoSuchProperty);
1292 } else if decl.visibility == i_slint_compiler::object_tree::PropertyVisibility::Output {
1293 return Err(SetPropertyError::AccessDenied);
1294 }
1295
1296 d.set_property(comp.borrow(), &name, value)
1297 }
1298
1299 pub fn set_callback(
1334 &self,
1335 name: &str,
1336 callback: impl Fn(&[Value]) -> Value + 'static,
1337 ) -> Result<(), SetCallbackError> {
1338 generativity::make_guard!(guard);
1339 let comp = self.inner.unerase(guard);
1340 comp.description()
1341 .set_callback_handler(comp.borrow(), &normalize_identifier(name), Box::new(callback))
1342 .map_err(|()| SetCallbackError::NoSuchCallback)
1343 }
1344
1345 pub fn invoke(&self, name: &str, args: &[Value]) -> Result<Value, InvokeError> {
1350 generativity::make_guard!(guard);
1351 let comp = self.inner.unerase(guard);
1352 comp.description()
1353 .invoke(comp.borrow(), &normalize_identifier(name), args)
1354 .map_err(|()| InvokeError::NoSuchCallable)
1355 }
1356
1357 pub fn get_global_property(
1382 &self,
1383 global: &str,
1384 property: &str,
1385 ) -> Result<Value, GetPropertyError> {
1386 generativity::make_guard!(guard);
1387 let comp = self.inner.unerase(guard);
1388 comp.description()
1389 .get_global(comp.borrow(), &&normalize_identifier(global))
1390 .map_err(|()| GetPropertyError::NoSuchProperty)? .as_ref()
1392 .get_property(&normalize_identifier(property))
1393 .map_err(|()| GetPropertyError::NoSuchProperty)
1394 }
1395
1396 pub fn set_global_property(
1398 &self,
1399 global: &str,
1400 property: &str,
1401 value: Value,
1402 ) -> Result<(), SetPropertyError> {
1403 generativity::make_guard!(guard);
1404 let comp = self.inner.unerase(guard);
1405 comp.description()
1406 .get_global(comp.borrow(), &&normalize_identifier(global))
1407 .map_err(|()| SetPropertyError::NoSuchProperty)? .as_ref()
1409 .set_property(&&normalize_identifier(property), value)
1410 }
1411
1412 pub fn set_global_callback(
1447 &self,
1448 global: &str,
1449 name: &str,
1450 callback: impl Fn(&[Value]) -> Value + 'static,
1451 ) -> Result<(), SetCallbackError> {
1452 generativity::make_guard!(guard);
1453 let comp = self.inner.unerase(guard);
1454 comp.description()
1455 .get_global(comp.borrow(), &normalize_identifier(global))
1456 .map_err(|()| SetCallbackError::NoSuchCallback)? .as_ref()
1458 .set_callback_handler(&normalize_identifier(name), Box::new(callback))
1459 .map_err(|()| SetCallbackError::NoSuchCallback)
1460 }
1461
1462 pub fn invoke_global(
1467 &self,
1468 global: &str,
1469 callable_name: &str,
1470 args: &[Value],
1471 ) -> Result<Value, InvokeError> {
1472 generativity::make_guard!(guard);
1473 let comp = self.inner.unerase(guard);
1474 let g = comp
1475 .description()
1476 .get_global(comp.borrow(), &normalize_identifier(global))
1477 .map_err(|()| InvokeError::NoSuchCallable)?; let callable_name = normalize_identifier(callable_name);
1479 if matches!(
1480 comp.description()
1481 .original
1482 .root_element
1483 .borrow()
1484 .lookup_property(&callable_name)
1485 .property_type,
1486 LangType::Function { .. }
1487 ) {
1488 g.as_ref()
1489 .eval_function(&callable_name, args.to_vec())
1490 .map_err(|()| InvokeError::NoSuchCallable)
1491 } else {
1492 g.as_ref()
1493 .invoke_callback(&callable_name, args)
1494 .map_err(|()| InvokeError::NoSuchCallable)
1495 }
1496 }
1497
1498 #[cfg(feature = "internal-highlight")]
1502 pub fn component_positions(
1503 &self,
1504 path: &Path,
1505 offset: u32,
1506 ) -> Vec<i_slint_core::lengths::LogicalRect> {
1507 crate::highlight::component_positions(&self.inner, path, offset)
1508 }
1509
1510 #[cfg(feature = "internal-highlight")]
1514 pub fn element_positions(
1515 &self,
1516 element: &i_slint_compiler::object_tree::ElementRc,
1517 ) -> Vec<i_slint_core::lengths::LogicalRect> {
1518 crate::highlight::element_positions(
1519 &self.inner,
1520 element,
1521 crate::highlight::ElementPositionFilter::IncludeClipped,
1522 )
1523 }
1524
1525 #[cfg(feature = "internal-highlight")]
1529 pub fn element_node_at_source_code_position(
1530 &self,
1531 path: &Path,
1532 offset: u32,
1533 ) -> Vec<(i_slint_compiler::object_tree::ElementRc, usize)> {
1534 crate::highlight::element_node_at_source_code_position(&self.inner, path, offset)
1535 }
1536}
1537
1538impl ComponentHandle for ComponentInstance {
1539 type WeakInner = vtable::VWeak<ItemTreeVTable, crate::dynamic_item_tree::ErasedItemTreeBox>;
1540
1541 fn as_weak(&self) -> Weak<Self>
1542 where
1543 Self: Sized,
1544 {
1545 Weak::new(vtable::VRc::downgrade(&self.inner))
1546 }
1547
1548 fn clone_strong(&self) -> Self {
1549 Self { inner: self.inner.clone() }
1550 }
1551
1552 fn upgrade_from_weak_inner(inner: &Self::WeakInner) -> Option<Self> {
1553 Some(Self { inner: inner.upgrade()? })
1554 }
1555
1556 fn show(&self) -> Result<(), PlatformError> {
1557 self.inner.window_adapter_ref()?.window().show()
1558 }
1559
1560 fn hide(&self) -> Result<(), PlatformError> {
1561 self.inner.window_adapter_ref()?.window().hide()
1562 }
1563
1564 fn run(&self) -> Result<(), PlatformError> {
1565 self.show()?;
1566 run_event_loop()?;
1567 self.hide()
1568 }
1569
1570 fn window(&self) -> &Window {
1571 self.inner.window_adapter_ref().unwrap().window()
1572 }
1573
1574 fn global<'a, T: Global<'a, Self>>(&'a self) -> T
1575 where
1576 Self: Sized,
1577 {
1578 unreachable!()
1579 }
1580}
1581
1582impl From<ComponentInstance>
1583 for vtable::VRc<i_slint_core::item_tree::ItemTreeVTable, ErasedItemTreeBox>
1584{
1585 fn from(value: ComponentInstance) -> Self {
1586 value.inner
1587 }
1588}
1589
1590#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Error, derive_more::Display)]
1592#[non_exhaustive]
1593pub enum GetPropertyError {
1594 #[display("no such property")]
1596 NoSuchProperty,
1597}
1598
1599#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Error, derive_more::Display)]
1601#[non_exhaustive]
1602pub enum SetPropertyError {
1603 #[display("no such property")]
1605 NoSuchProperty,
1606 #[display("wrong type")]
1612 WrongType,
1613 #[display("access denied")]
1615 AccessDenied,
1616}
1617
1618#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Error, derive_more::Display)]
1620#[non_exhaustive]
1621pub enum SetCallbackError {
1622 #[display("no such callback")]
1624 NoSuchCallback,
1625}
1626
1627#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Error, derive_more::Display)]
1629#[non_exhaustive]
1630pub enum InvokeError {
1631 #[display("no such callback or function")]
1633 NoSuchCallable,
1634}
1635
1636pub fn run_event_loop() -> Result<(), PlatformError> {
1640 i_slint_backend_selector::with_platform(|b| b.run_event_loop())
1641}
1642
1643pub fn spawn_local<F: Future + 'static>(fut: F) -> Result<JoinHandle<F::Output>, EventLoopError> {
1647 i_slint_backend_selector::with_global_context(|ctx| ctx.spawn_local(fut))
1648 .map_err(|_| EventLoopError::NoEventLoopProvider)?
1649}
1650
1651#[doc(hidden)]
1653pub mod testing {
1654 use super::ComponentHandle;
1655 use i_slint_core::window::WindowInner;
1656
1657 pub fn send_mouse_click(comp: &super::ComponentInstance, x: f32, y: f32) {
1659 i_slint_core::tests::slint_send_mouse_click(
1660 x,
1661 y,
1662 &WindowInner::from_pub(comp.window()).window_adapter(),
1663 );
1664 }
1665
1666 pub fn send_keyboard_char(
1668 comp: &super::ComponentInstance,
1669 string: i_slint_core::SharedString,
1670 pressed: bool,
1671 ) {
1672 i_slint_core::tests::slint_send_keyboard_char(
1673 &string,
1674 pressed,
1675 &WindowInner::from_pub(comp.window()).window_adapter(),
1676 );
1677 }
1678 pub fn send_keyboard_string_sequence(
1680 comp: &super::ComponentInstance,
1681 string: i_slint_core::SharedString,
1682 ) {
1683 i_slint_core::tests::send_keyboard_string_sequence(
1684 &string,
1685 &WindowInner::from_pub(comp.window()).window_adapter(),
1686 );
1687 }
1688}
1689
1690#[test]
1691fn component_definition_properties() {
1692 i_slint_backend_testing::init_no_event_loop();
1693 let mut compiler = Compiler::default();
1694 compiler.set_style("fluent".into());
1695 let comp_def = spin_on::spin_on(
1696 compiler.build_from_source(
1697 r#"
1698 export component Dummy {
1699 in-out property <string> test;
1700 in-out property <int> underscores-and-dashes_preserved: 44;
1701 callback hello;
1702 }"#
1703 .into(),
1704 "".into(),
1705 ),
1706 )
1707 .component("Dummy")
1708 .unwrap();
1709
1710 let props = comp_def.properties().collect::<Vec<(_, _)>>();
1711
1712 assert_eq!(props.len(), 2);
1713 assert_eq!(props[0].0, "test");
1714 assert_eq!(props[0].1, ValueType::String);
1715 assert_eq!(props[1].0, "underscores-and-dashes_preserved");
1716 assert_eq!(props[1].1, ValueType::Number);
1717
1718 let instance = comp_def.create().unwrap();
1719 assert_eq!(instance.get_property("underscores_and-dashes-preserved"), Ok(Value::Number(44.)));
1720 assert_eq!(
1721 instance.get_property("underscoresanddashespreserved"),
1722 Err(GetPropertyError::NoSuchProperty)
1723 );
1724 assert_eq!(
1725 instance.set_property("underscores-and_dashes-preserved", Value::Number(88.)),
1726 Ok(())
1727 );
1728 assert_eq!(
1729 instance.set_property("underscoresanddashespreserved", Value::Number(99.)),
1730 Err(SetPropertyError::NoSuchProperty)
1731 );
1732 assert_eq!(
1733 instance.set_property("underscores-and_dashes-preserved", Value::String("99".into())),
1734 Err(SetPropertyError::WrongType)
1735 );
1736 assert_eq!(instance.get_property("underscores-and-dashes-preserved"), Ok(Value::Number(88.)));
1737}
1738
1739#[test]
1740fn component_definition_properties2() {
1741 i_slint_backend_testing::init_no_event_loop();
1742 let mut compiler = Compiler::default();
1743 compiler.set_style("fluent".into());
1744 let comp_def = spin_on::spin_on(
1745 compiler.build_from_source(
1746 r#"
1747 export component Dummy {
1748 in-out property <string> sub-text <=> sub.text;
1749 sub := Text { property <int> private-not-exported; }
1750 out property <string> xreadonly: "the value";
1751 private property <string> xx: sub.text;
1752 callback hello;
1753 }"#
1754 .into(),
1755 "".into(),
1756 ),
1757 )
1758 .component("Dummy")
1759 .unwrap();
1760
1761 let props = comp_def.properties().collect::<Vec<(_, _)>>();
1762
1763 assert_eq!(props.len(), 2);
1764 assert_eq!(props[0].0, "sub-text");
1765 assert_eq!(props[0].1, ValueType::String);
1766 assert_eq!(props[1].0, "xreadonly");
1767
1768 let callbacks = comp_def.callbacks().collect::<Vec<_>>();
1769 assert_eq!(callbacks.len(), 1);
1770 assert_eq!(callbacks[0], "hello");
1771
1772 let instance = comp_def.create().unwrap();
1773 assert_eq!(
1774 instance.set_property("xreadonly", SharedString::from("XXX").into()),
1775 Err(SetPropertyError::AccessDenied)
1776 );
1777 assert_eq!(instance.get_property("xreadonly"), Ok(Value::String("the value".into())));
1778 assert_eq!(
1779 instance.set_property("xx", SharedString::from("XXX").into()),
1780 Err(SetPropertyError::NoSuchProperty)
1781 );
1782 assert_eq!(
1783 instance.set_property("background", Value::default()),
1784 Err(SetPropertyError::NoSuchProperty)
1785 );
1786
1787 assert_eq!(instance.get_property("background"), Err(GetPropertyError::NoSuchProperty));
1788 assert_eq!(instance.get_property("xx"), Err(GetPropertyError::NoSuchProperty));
1789}
1790
1791#[test]
1792fn globals() {
1793 i_slint_backend_testing::init_no_event_loop();
1794 let mut compiler = Compiler::default();
1795 compiler.set_style("fluent".into());
1796 let definition = spin_on::spin_on(
1797 compiler.build_from_source(
1798 r#"
1799 export global My-Super_Global {
1800 in-out property <int> the-property : 21;
1801 callback my-callback();
1802 }
1803 export { My-Super_Global as AliasedGlobal }
1804 export component Dummy {
1805 callback alias <=> My-Super_Global.my-callback;
1806 }"#
1807 .into(),
1808 "".into(),
1809 ),
1810 )
1811 .component("Dummy")
1812 .unwrap();
1813
1814 assert_eq!(definition.globals().collect::<Vec<_>>(), vec!["My-Super_Global", "AliasedGlobal"]);
1815
1816 assert!(definition.global_properties("not-there").is_none());
1817 {
1818 let expected_properties = vec![("the-property".to_string(), ValueType::Number)];
1819 let expected_callbacks = vec!["my-callback".to_string()];
1820
1821 let assert_properties_and_callbacks = |global_name| {
1822 assert_eq!(
1823 definition
1824 .global_properties(global_name)
1825 .map(|props| props.collect::<Vec<_>>())
1826 .as_ref(),
1827 Some(&expected_properties)
1828 );
1829 assert_eq!(
1830 definition
1831 .global_callbacks(global_name)
1832 .map(|props| props.collect::<Vec<_>>())
1833 .as_ref(),
1834 Some(&expected_callbacks)
1835 );
1836 };
1837
1838 assert_properties_and_callbacks("My-Super-Global");
1839 assert_properties_and_callbacks("My_Super-Global");
1840 assert_properties_and_callbacks("AliasedGlobal");
1841 }
1842
1843 let instance = definition.create().unwrap();
1844 assert_eq!(
1845 instance.set_global_property("My_Super-Global", "the_property", Value::Number(44.)),
1846 Ok(())
1847 );
1848 assert_eq!(
1849 instance.set_global_property("AliasedGlobal", "the_property", Value::Number(44.)),
1850 Ok(())
1851 );
1852 assert_eq!(
1853 instance.set_global_property("DontExist", "the-property", Value::Number(88.)),
1854 Err(SetPropertyError::NoSuchProperty)
1855 );
1856
1857 assert_eq!(
1858 instance.set_global_property("My_Super-Global", "theproperty", Value::Number(88.)),
1859 Err(SetPropertyError::NoSuchProperty)
1860 );
1861 assert_eq!(
1862 instance.set_global_property("AliasedGlobal", "theproperty", Value::Number(88.)),
1863 Err(SetPropertyError::NoSuchProperty)
1864 );
1865 assert_eq!(
1866 instance.set_global_property("My_Super-Global", "the_property", Value::String("88".into())),
1867 Err(SetPropertyError::WrongType)
1868 );
1869 assert_eq!(
1870 instance.get_global_property("My-Super_Global", "yoyo"),
1871 Err(GetPropertyError::NoSuchProperty)
1872 );
1873 assert_eq!(
1874 instance.get_global_property("My-Super_Global", "the-property"),
1875 Ok(Value::Number(44.))
1876 );
1877
1878 assert_eq!(
1879 instance.set_property("the-property", Value::Void),
1880 Err(SetPropertyError::NoSuchProperty)
1881 );
1882 assert_eq!(instance.get_property("the-property"), Err(GetPropertyError::NoSuchProperty));
1883
1884 assert_eq!(
1885 instance.set_global_callback("DontExist", "the-property", |_| panic!()),
1886 Err(SetCallbackError::NoSuchCallback)
1887 );
1888 assert_eq!(
1889 instance.set_global_callback("My_Super_Global", "the-property", |_| panic!()),
1890 Err(SetCallbackError::NoSuchCallback)
1891 );
1892 assert_eq!(
1893 instance.set_global_callback("My_Super_Global", "yoyo", |_| panic!()),
1894 Err(SetCallbackError::NoSuchCallback)
1895 );
1896
1897 assert_eq!(
1898 instance.invoke_global("DontExist", "the-property", &[]),
1899 Err(InvokeError::NoSuchCallable)
1900 );
1901 assert_eq!(
1902 instance.invoke_global("My_Super_Global", "the-property", &[]),
1903 Err(InvokeError::NoSuchCallable)
1904 );
1905 assert_eq!(
1906 instance.invoke_global("My_Super_Global", "yoyo", &[]),
1907 Err(InvokeError::NoSuchCallable)
1908 );
1909
1910 assert_eq!(instance.get_property("alias"), Err(GetPropertyError::NoSuchProperty));
1912}
1913
1914#[test]
1915fn call_functions() {
1916 i_slint_backend_testing::init_no_event_loop();
1917 let mut compiler = Compiler::default();
1918 compiler.set_style("fluent".into());
1919 let definition = spin_on::spin_on(
1920 compiler.build_from_source(
1921 r#"
1922 export global Gl {
1923 out property<string> q;
1924 public function foo-bar(a-a: string, b-b:int) -> string {
1925 q = a-a;
1926 return a-a + b-b;
1927 }
1928 }
1929 export component Test {
1930 out property<int> p;
1931 public function foo-bar(a: int, b:int) -> int {
1932 p = a;
1933 return a + b;
1934 }
1935 }"#
1936 .into(),
1937 "".into(),
1938 ),
1939 )
1940 .component("Test")
1941 .unwrap();
1942
1943 assert_eq!(definition.functions().collect::<Vec<_>>(), ["foo-bar"]);
1944 assert_eq!(definition.global_functions("Gl").unwrap().collect::<Vec<_>>(), ["foo-bar"]);
1945
1946 let instance = definition.create().unwrap();
1947
1948 assert_eq!(
1949 instance.invoke("foo_bar", &[Value::Number(3.), Value::Number(4.)]),
1950 Ok(Value::Number(7.))
1951 );
1952 assert_eq!(instance.invoke("p", &[]), Err(InvokeError::NoSuchCallable));
1953 assert_eq!(instance.get_property("p"), Ok(Value::Number(3.)));
1954
1955 assert_eq!(
1956 instance.invoke_global(
1957 "Gl",
1958 "foo_bar",
1959 &[Value::String("Hello".into()), Value::Number(10.)]
1960 ),
1961 Ok(Value::String("Hello10".into()))
1962 );
1963 assert_eq!(instance.get_global_property("Gl", "q"), Ok(Value::String("Hello".into())));
1964}
1965
1966#[test]
1967fn component_definition_struct_properties() {
1968 i_slint_backend_testing::init_no_event_loop();
1969 let mut compiler = Compiler::default();
1970 compiler.set_style("fluent".into());
1971 let comp_def = spin_on::spin_on(
1972 compiler.build_from_source(
1973 r#"
1974 export struct Settings {
1975 string_value: string,
1976 }
1977 export component Dummy {
1978 in-out property <Settings> test;
1979 }"#
1980 .into(),
1981 "".into(),
1982 ),
1983 )
1984 .component("Dummy")
1985 .unwrap();
1986
1987 let props = comp_def.properties().collect::<Vec<(_, _)>>();
1988
1989 assert_eq!(props.len(), 1);
1990 assert_eq!(props[0].0, "test");
1991 assert_eq!(props[0].1, ValueType::Struct);
1992
1993 let instance = comp_def.create().unwrap();
1994
1995 let valid_struct: Struct =
1996 [("string_value".to_string(), Value::String("hello".into()))].iter().cloned().collect();
1997
1998 assert_eq!(instance.set_property("test", Value::Struct(valid_struct.clone())), Ok(()));
1999 assert_eq!(instance.get_property("test").unwrap().value_type(), ValueType::Struct);
2000
2001 assert_eq!(instance.set_property("test", Value::Number(42.)), Err(SetPropertyError::WrongType));
2002
2003 let mut invalid_struct = valid_struct.clone();
2004 invalid_struct.set_field("other".into(), Value::Number(44.));
2005 assert_eq!(
2006 instance.set_property("test", Value::Struct(invalid_struct)),
2007 Err(SetPropertyError::WrongType)
2008 );
2009 let mut invalid_struct = valid_struct;
2010 invalid_struct.set_field("string_value".into(), Value::Number(44.));
2011 assert_eq!(
2012 instance.set_property("test", Value::Struct(invalid_struct)),
2013 Err(SetPropertyError::WrongType)
2014 );
2015}
2016
2017#[test]
2018fn component_definition_model_properties() {
2019 use i_slint_core::model::*;
2020 i_slint_backend_testing::init_no_event_loop();
2021 let mut compiler = Compiler::default();
2022 compiler.set_style("fluent".into());
2023 let comp_def = spin_on::spin_on(compiler.build_from_source(
2024 "export component Dummy { in-out property <[int]> prop: [42, 12]; }".into(),
2025 "".into(),
2026 ))
2027 .component("Dummy")
2028 .unwrap();
2029
2030 let props = comp_def.properties().collect::<Vec<(_, _)>>();
2031 assert_eq!(props.len(), 1);
2032 assert_eq!(props[0].0, "prop");
2033 assert_eq!(props[0].1, ValueType::Model);
2034
2035 let instance = comp_def.create().unwrap();
2036
2037 let int_model =
2038 Value::Model([Value::Number(14.), Value::Number(15.), Value::Number(16.)].into());
2039 let empty_model = Value::Model(ModelRc::new(VecModel::<Value>::default()));
2040 let model_with_string = Value::Model(VecModel::from_slice(&[
2041 Value::Number(1000.),
2042 Value::String("foo".into()),
2043 Value::Number(1111.),
2044 ]));
2045
2046 #[track_caller]
2047 fn check_model(val: Value, r: &[f64]) {
2048 if let Value::Model(m) = val {
2049 assert_eq!(r.len(), m.row_count());
2050 for (i, v) in r.iter().enumerate() {
2051 assert_eq!(m.row_data(i).unwrap(), Value::Number(*v));
2052 }
2053 } else {
2054 panic!("{val:?} not a model");
2055 }
2056 }
2057
2058 assert_eq!(instance.get_property("prop").unwrap().value_type(), ValueType::Model);
2059 check_model(instance.get_property("prop").unwrap(), &[42., 12.]);
2060
2061 instance.set_property("prop", int_model).unwrap();
2062 check_model(instance.get_property("prop").unwrap(), &[14., 15., 16.]);
2063
2064 assert_eq!(instance.set_property("prop", Value::Number(42.)), Err(SetPropertyError::WrongType));
2065 check_model(instance.get_property("prop").unwrap(), &[14., 15., 16.]);
2066 assert_eq!(instance.set_property("prop", model_with_string), Err(SetPropertyError::WrongType));
2067 check_model(instance.get_property("prop").unwrap(), &[14., 15., 16.]);
2068
2069 assert_eq!(instance.set_property("prop", empty_model), Ok(()));
2070 check_model(instance.get_property("prop").unwrap(), &[]);
2071}
2072
2073#[test]
2074fn lang_type_to_value_type() {
2075 use i_slint_compiler::langtype::Struct as LangStruct;
2076 use std::collections::BTreeMap;
2077
2078 assert_eq!(ValueType::from(LangType::Void), ValueType::Void);
2079 assert_eq!(ValueType::from(LangType::Float32), ValueType::Number);
2080 assert_eq!(ValueType::from(LangType::Int32), ValueType::Number);
2081 assert_eq!(ValueType::from(LangType::Duration), ValueType::Number);
2082 assert_eq!(ValueType::from(LangType::Angle), ValueType::Number);
2083 assert_eq!(ValueType::from(LangType::PhysicalLength), ValueType::Number);
2084 assert_eq!(ValueType::from(LangType::LogicalLength), ValueType::Number);
2085 assert_eq!(ValueType::from(LangType::Percent), ValueType::Number);
2086 assert_eq!(ValueType::from(LangType::UnitProduct(vec![])), ValueType::Number);
2087 assert_eq!(ValueType::from(LangType::String), ValueType::String);
2088 assert_eq!(ValueType::from(LangType::Color), ValueType::Brush);
2089 assert_eq!(ValueType::from(LangType::Brush), ValueType::Brush);
2090 assert_eq!(ValueType::from(LangType::Array(Rc::new(LangType::Void))), ValueType::Model);
2091 assert_eq!(ValueType::from(LangType::Bool), ValueType::Bool);
2092 assert_eq!(
2093 ValueType::from(LangType::Struct(Rc::new(LangStruct {
2094 fields: BTreeMap::default(),
2095 name: None,
2096 node: None,
2097 rust_attributes: None
2098 }))),
2099 ValueType::Struct
2100 );
2101 assert_eq!(ValueType::from(LangType::Image), ValueType::Image);
2102}
2103
2104#[test]
2105fn test_multi_components() {
2106 let result = spin_on::spin_on(
2107 Compiler::default().build_from_source(
2108 r#"
2109 export struct Settings {
2110 string_value: string,
2111 }
2112 export global ExpGlo { in-out property <int> test: 42; }
2113 component Common {
2114 in-out property <Settings> settings: { string_value: "Hello", };
2115 }
2116 export component Xyz inherits Window {
2117 in-out property <int> aaa: 8;
2118 }
2119 export component Foo {
2120
2121 in-out property <int> test: 42;
2122 c := Common {}
2123 }
2124 export component Bar inherits Window {
2125 in-out property <int> blah: 78;
2126 c := Common {}
2127 }
2128 "#
2129 .into(),
2130 PathBuf::from("hello.slint"),
2131 ),
2132 );
2133
2134 assert!(!result.has_errors(), "Error {:?}", result.diagnostics().collect::<Vec<_>>());
2135 let mut components = result.component_names().collect::<Vec<_>>();
2136 components.sort();
2137 assert_eq!(components, vec!["Bar", "Xyz"]);
2138 let diag = result.diagnostics().collect::<Vec<_>>();
2139 assert_eq!(diag.len(), 1);
2140 assert_eq!(diag[0].level(), DiagnosticLevel::Warning);
2141 assert_eq!(
2142 diag[0].message(),
2143 "Exported component 'Foo' doesn't inherit Window. No code will be generated for it"
2144 );
2145
2146 let comp1 = result.component("Xyz").unwrap();
2147 assert_eq!(comp1.name(), "Xyz");
2148 let instance1a = comp1.create().unwrap();
2149 let comp2 = result.component("Bar").unwrap();
2150 let instance2 = comp2.create().unwrap();
2151 let instance1b = comp1.create().unwrap();
2152
2153 assert_eq!(instance1a.get_global_property("ExpGlo", "test"), Ok(Value::Number(42.0)));
2155 assert_eq!(instance1a.set_global_property("ExpGlo", "test", Value::Number(88.0)), Ok(()));
2156 assert_eq!(instance2.get_global_property("ExpGlo", "test"), Ok(Value::Number(42.0)));
2157 assert_eq!(instance1b.get_global_property("ExpGlo", "test"), Ok(Value::Number(42.0)));
2158 assert_eq!(instance1a.get_global_property("ExpGlo", "test"), Ok(Value::Number(88.0)));
2159
2160 assert!(result.component("Settings").is_none());
2161 assert!(result.component("Foo").is_none());
2162 assert!(result.component("Common").is_none());
2163 assert!(result.component("ExpGlo").is_none());
2164 assert!(result.component("xyz").is_none());
2165}
2166
2167#[cfg(all(test, feature = "internal-highlight"))]
2168fn compile(code: &str) -> (ComponentInstance, PathBuf) {
2169 i_slint_backend_testing::init_no_event_loop();
2170 let mut compiler = Compiler::default();
2171 compiler.set_style("fluent".into());
2172 let path = PathBuf::from("/tmp/test.slint");
2173
2174 let compile_result =
2175 spin_on::spin_on(compiler.build_from_source(code.to_string(), path.clone()));
2176
2177 for d in &compile_result.diagnostics {
2178 eprintln!("{d}");
2179 }
2180
2181 assert!(!compile_result.has_errors());
2182
2183 let definition = compile_result.components().next().unwrap();
2184 let instance = definition.create().unwrap();
2185
2186 (instance, path)
2187}
2188
2189#[cfg(feature = "internal-highlight")]
2190#[test]
2191fn test_element_node_at_source_code_position() {
2192 let code = r#"
2193component Bar1 {}
2194
2195component Foo1 {
2196}
2197
2198export component Foo2 inherits Window {
2199 Bar1 {}
2200 Foo1 {}
2201}"#;
2202
2203 let (handle, path) = compile(code);
2204
2205 for i in 0..code.len() as u32 {
2206 let elements = handle.element_node_at_source_code_position(&path, i);
2207 eprintln!("{i}: {}", code.as_bytes()[i as usize] as char);
2208 match i {
2209 16 => assert_eq!(elements.len(), 1), 35 => assert_eq!(elements.len(), 1), 71..=78 => assert_eq!(elements.len(), 1), 85..=89 => assert_eq!(elements.len(), 1), 97..=103 => assert_eq!(elements.len(), 1), _ => assert!(elements.is_empty()),
2215 }
2216 }
2217}
2218
2219#[cfg(feature = "ffi")]
2220#[allow(missing_docs)]
2221#[path = "ffi.rs"]
2222pub(crate) mod ffi;