1use crate::dynamic_item_tree::{ErasedItemTreeBox, WindowOptions};
6use i_slint_compiler::langtype::Type as LangType;
7use i_slint_core::PathData;
8use i_slint_core::component_factory::ComponentFactory;
9#[cfg(feature = "internal")]
10use i_slint_core::component_factory::FactoryContext;
11use i_slint_core::graphics::euclid::approxeq::ApproxEq as _;
12use i_slint_core::items::*;
13use i_slint_core::model::{Model, ModelExt, ModelRc};
14use i_slint_core::styled_text::StyledText;
15#[cfg(feature = "internal")]
16use i_slint_core::window::WindowInner;
17use smol_str::SmolStr;
18use std::collections::HashMap;
19use std::future::Future;
20use std::path::{Path, PathBuf};
21use std::rc::Rc;
22
23#[doc(inline)]
24pub use i_slint_compiler::diagnostics::{Diagnostic, DiagnosticLevel};
25
26pub use i_slint_backend_selector::api::*;
27pub use i_slint_core::api::*;
28
29pub use i_slint_compiler::DefaultTranslationContext;
32
33#[derive(Debug, Copy, Clone, PartialEq)]
36#[repr(i8)]
37#[non_exhaustive]
38pub enum ValueType {
39 Void,
41 Number,
43 String,
45 Bool,
47 Model,
49 Struct,
51 Brush,
53 Image,
55 #[doc(hidden)]
57 Other = -1,
58}
59
60impl From<LangType> for ValueType {
61 fn from(ty: LangType) -> Self {
62 match ty {
63 LangType::Float32
64 | LangType::Int32
65 | LangType::Duration
66 | LangType::Angle
67 | LangType::PhysicalLength
68 | LangType::LogicalLength
69 | LangType::Percent
70 | LangType::UnitProduct(_) => Self::Number,
71 LangType::String => Self::String,
72 LangType::Color => Self::Brush,
73 LangType::Brush => Self::Brush,
74 LangType::Array(_) => Self::Model,
75 LangType::Bool => Self::Bool,
76 LangType::Struct { .. } => Self::Struct,
77 LangType::Void => Self::Void,
78 LangType::Image => Self::Image,
79 _ => Self::Other,
80 }
81 }
82}
83
84#[derive(Clone, Default)]
96#[non_exhaustive]
97#[repr(u8)]
98pub enum Value {
99 #[default]
102 Void = 0,
103 Number(f64) = 1,
105 String(SharedString) = 2,
107 Bool(bool) = 3,
109 Image(Image) = 4,
111 Model(ModelRc<Value>) = 5,
113 Struct(Struct) = 6,
115 Brush(Brush) = 7,
117 #[doc(hidden)]
118 PathData(PathData) = 8,
120 #[doc(hidden)]
121 EasingCurve(i_slint_core::animations::EasingCurve) = 9,
123 #[doc(hidden)]
124 EnumerationValue(String, String) = 10,
127 #[doc(hidden)]
128 LayoutCache(SharedVector<f32>) = 11,
129 #[doc(hidden)]
130 ComponentFactory(ComponentFactory) = 12,
132 #[doc(hidden)] StyledText(StyledText) = 13,
135 #[doc(hidden)]
136 ArrayOfU16(SharedVector<u16>) = 14,
137 Keys(Keys) = 15,
139 DataTransfer(DataTransfer) = 16,
141 #[doc(hidden)]
142 MouseCursorInner(i_slint_core::cursor::MouseCursorInner) = 17,
144}
145
146impl Value {
147 pub fn value_type(&self) -> ValueType {
149 match self {
150 Value::Void => ValueType::Void,
151 Value::Number(_) => ValueType::Number,
152 Value::String(_) => ValueType::String,
153 Value::Bool(_) => ValueType::Bool,
154 Value::Model(_) => ValueType::Model,
155 Value::Struct(_) => ValueType::Struct,
156 Value::Brush(_) => ValueType::Brush,
157 Value::Image(_) => ValueType::Image,
158 _ => ValueType::Other,
159 }
160 }
161}
162
163impl PartialEq for Value {
164 fn eq(&self, other: &Self) -> bool {
165 match self {
166 Value::Void => matches!(other, Value::Void),
167 Value::Number(lhs) => matches!(other, Value::Number(rhs) if lhs.approx_eq(rhs)),
168 Value::String(lhs) => matches!(other, Value::String(rhs) if lhs == rhs),
169 Value::Bool(lhs) => matches!(other, Value::Bool(rhs) if lhs == rhs),
170 Value::Image(lhs) => matches!(other, Value::Image(rhs) if lhs == rhs),
171 Value::Model(lhs) => {
172 if let Value::Model(rhs) = other {
173 lhs == rhs
174 } else {
175 false
176 }
177 }
178 Value::Struct(lhs) => matches!(other, Value::Struct(rhs) if lhs == rhs),
179 Value::Brush(lhs) => matches!(other, Value::Brush(rhs) if lhs == rhs),
180 Value::PathData(lhs) => matches!(other, Value::PathData(rhs) if lhs == rhs),
181 Value::EasingCurve(lhs) => matches!(other, Value::EasingCurve(rhs) if lhs == rhs),
182 Value::EnumerationValue(lhs_name, lhs_value) => {
183 matches!(other, Value::EnumerationValue(rhs_name, rhs_value) if lhs_name == rhs_name && lhs_value == rhs_value)
184 }
185 Value::LayoutCache(lhs) => matches!(other, Value::LayoutCache(rhs) if lhs == rhs),
186 Value::ArrayOfU16(lhs) => matches!(other, Value::ArrayOfU16(rhs) if lhs == rhs),
187 Value::ComponentFactory(lhs) => {
188 matches!(other, Value::ComponentFactory(rhs) if lhs == rhs)
189 }
190 Value::StyledText(lhs) => {
191 matches!(other, Value::StyledText(rhs) if lhs == rhs)
192 }
193 Value::Keys(lhs) => {
194 matches!(other, Value::Keys(rhs) if lhs == rhs)
195 }
196 Value::DataTransfer(lhs) => {
197 matches!(other, Value::DataTransfer(rhs) if lhs == rhs)
198 }
199 Value::MouseCursorInner(lhs) => {
200 matches!(other, Value::MouseCursorInner(rhs) if lhs == rhs)
201 }
202 }
203 }
204}
205
206impl std::fmt::Debug for Value {
207 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208 match self {
209 Value::Void => write!(f, "Value::Void"),
210 Value::Number(n) => write!(f, "Value::Number({n:?})"),
211 Value::String(s) => write!(f, "Value::String({s:?})"),
212 Value::Bool(b) => write!(f, "Value::Bool({b:?})"),
213 Value::Image(i) => write!(f, "Value::Image({i:?})"),
214 Value::Model(m) => {
215 write!(f, "Value::Model(")?;
216 f.debug_list().entries(m.iter()).finish()?;
217 write!(f, "])")
218 }
219 Value::Struct(s) => write!(f, "Value::Struct({s:?})"),
220 Value::Brush(b) => write!(f, "Value::Brush({b:?})"),
221 Value::PathData(e) => write!(f, "Value::PathElements({e:?})"),
222 Value::EasingCurve(c) => write!(f, "Value::EasingCurve({c:?})"),
223 Value::EnumerationValue(n, v) => write!(f, "Value::EnumerationValue({n:?}, {v:?})"),
224 Value::LayoutCache(v) => write!(f, "Value::LayoutCache({v:?})"),
225 Value::ComponentFactory(factory) => write!(f, "Value::ComponentFactory({factory:?})"),
226 Value::StyledText(text) => write!(f, "Value::StyledText({text:?})"),
227 Value::ArrayOfU16(data) => {
228 write!(f, "Value::ArrayOfU16({data:?})")
229 }
230 Value::Keys(ks) => write!(f, "Value::Keys({ks:?})"),
231 Value::DataTransfer(cd) => write!(f, "Value::DataTransfer({cd:?})"),
232 Value::MouseCursorInner(m) => write!(f, "Value::MouseCursor({m:?})"),
233 }
234 }
235}
236
237macro_rules! declare_value_conversion {
246 ( $value:ident => [$($ty:ty),*] ) => {
247 $(
248 impl From<$ty> for Value {
249 fn from(v: $ty) -> Self {
250 Value::$value(v as _)
251 }
252 }
253 impl TryFrom<Value> for $ty {
254 type Error = Value;
255 fn try_from(v: Value) -> Result<$ty, Self::Error> {
256 match v {
257 Value::$value(x) => Ok(x as _),
258 _ => Err(v)
259 }
260 }
261 }
262 )*
263 };
264}
265declare_value_conversion!(Number => [u32, u64, i32, i64, f32, f64, usize, isize] );
266declare_value_conversion!(String => [SharedString] );
267declare_value_conversion!(Bool => [bool] );
268declare_value_conversion!(Image => [Image] );
269declare_value_conversion!(Struct => [Struct] );
270declare_value_conversion!(Brush => [Brush] );
271declare_value_conversion!(PathData => [PathData]);
272declare_value_conversion!(EasingCurve => [i_slint_core::animations::EasingCurve]);
273declare_value_conversion!(LayoutCache => [SharedVector<f32>] );
274declare_value_conversion!(ComponentFactory => [ComponentFactory] );
275declare_value_conversion!(StyledText => [StyledText] );
276declare_value_conversion!(ArrayOfU16 => [SharedVector<u16>] );
277declare_value_conversion!(Keys => [Keys]);
278declare_value_conversion!(DataTransfer => [DataTransfer]);
279declare_value_conversion!(MouseCursorInner => [i_slint_core::cursor::MouseCursorInner]);
280
281macro_rules! declare_value_struct_conversion {
283 (struct $name:path { $($field:ident),* $(, ..$extra:expr)? }) => {
284 impl From<$name> for Value {
285 fn from($name { $($field),* , .. }: $name) -> Self {
286 let mut struct_ = Struct::default();
287 $(struct_.set_field(stringify!($field).into(), $field.into());)*
288 Value::Struct(struct_)
289 }
290 }
291 impl TryFrom<Value> for $name {
292 type Error = ();
293 fn try_from(v: Value) -> Result<$name, Self::Error> {
294 #[allow(clippy::field_reassign_with_default)]
295 match v {
296 Value::Struct(x) => {
297 type Ty = $name;
298 #[allow(unused)]
299 let mut res: Ty = Ty::default();
300 $(let mut res: Ty = $extra;)?
301 $(res.$field = x.get_field(stringify!($field)).ok_or(())?.clone().try_into().map_err(|_|())?;)*
302 Ok(res)
303 }
304 _ => Err(()),
305 }
306 }
307 }
308 };
309 ($(
310 $(#[$struct_attr:meta])*
311 $vis:vis struct $Name:ident {
312 $( $(#[$field_attr:meta])* $field:ident : $field_type:ty $(= $field_default:expr)?, )*
313 }
314 )*) => {
315 $(
316 impl From<$Name> for Value {
317 fn from(item: $Name) -> Self {
318 let mut struct_ = Struct::default();
319 $(struct_.set_field(stringify!($field).into(), item.$field.into());)*
320 Value::Struct(struct_)
321 }
322 }
323 impl TryFrom<Value> for $Name {
324 type Error = ();
325 fn try_from(v: Value) -> Result<$Name, Self::Error> {
326 #[allow(clippy::field_reassign_with_default)]
327 match v {
328 Value::Struct(x) => {
329 type Ty = $Name;
330 #[allow(unused)]
331 let mut res: Ty = Ty::default();
332 $(res.$field = x.get_field(stringify!($field)).ok_or(())?.clone().try_into().map_err(|_|())?;)*
335 Ok(res)
336 }
337 _ => Err(()),
338 }
339 }
340 }
341 )*
342 };
343}
344
345declare_value_struct_conversion!(struct i_slint_core::layout::LayoutInfo { min, max, min_percent, max_percent, preferred, stretch });
346declare_value_struct_conversion!(struct i_slint_core::graphics::Point { x, y, ..Default::default()});
347declare_value_struct_conversion!(struct i_slint_core::api::LogicalPosition { x, y });
348declare_value_struct_conversion!(struct i_slint_core::api::LogicalSize { width, height });
349declare_value_struct_conversion!(struct i_slint_core::properties::StateInfo { current_state, previous_state, change_time });
350
351i_slint_common::for_each_builtin_structs!(declare_value_struct_conversion);
352
353macro_rules! declare_value_enum_conversion {
358 ($( $(#[$enum_doc:meta])* $vis:vis enum $Name:ident { $($body:tt)* })*) => { $(
359 impl From<i_slint_core::items::$Name> for Value {
360 fn from(v: i_slint_core::items::$Name) -> Self {
361 Value::EnumerationValue(stringify!($Name).to_owned(), v.to_string())
362 }
363 }
364 impl TryFrom<Value> for i_slint_core::items::$Name {
365 type Error = ();
366 fn try_from(v: Value) -> Result<i_slint_core::items::$Name, ()> {
367 use std::str::FromStr;
368 match v {
369 Value::EnumerationValue(enumeration, value) => {
370 if enumeration != stringify!($Name) {
371 return Err(());
372 }
373 i_slint_core::items::$Name::from_str(value.as_str()).map_err(|_| ())
374 }
375 _ => Err(()),
376 }
377 }
378 }
379 )*};
380}
381
382i_slint_common::for_each_enums!(declare_value_enum_conversion);
383
384impl From<i_slint_core::animations::Instant> for Value {
385 fn from(value: i_slint_core::animations::Instant) -> Self {
386 Value::Number(value.0 as _)
387 }
388}
389impl TryFrom<Value> for i_slint_core::animations::Instant {
390 type Error = ();
391 fn try_from(v: Value) -> Result<i_slint_core::animations::Instant, Self::Error> {
392 match v {
393 Value::Number(x) => Ok(i_slint_core::animations::Instant(x as _)),
394 _ => Err(()),
395 }
396 }
397}
398
399impl From<()> for Value {
400 #[inline]
401 fn from(_: ()) -> Self {
402 Value::Void
403 }
404}
405impl TryFrom<Value> for () {
406 type Error = ();
407 #[inline]
408 fn try_from(_: Value) -> Result<(), Self::Error> {
409 Ok(())
410 }
411}
412
413impl From<Color> for Value {
414 #[inline]
415 fn from(c: Color) -> Self {
416 Value::Brush(Brush::SolidColor(c))
417 }
418}
419impl TryFrom<Value> for Color {
420 type Error = Value;
421 #[inline]
422 fn try_from(v: Value) -> Result<Color, Self::Error> {
423 match v {
424 Value::Brush(Brush::SolidColor(c)) => Ok(c),
425 _ => Err(v),
426 }
427 }
428}
429
430impl From<i_slint_core::lengths::LogicalLength> for Value {
431 #[inline]
432 fn from(l: i_slint_core::lengths::LogicalLength) -> Self {
433 Value::Number(l.get() as _)
434 }
435}
436impl TryFrom<Value> for i_slint_core::lengths::LogicalLength {
437 type Error = Value;
438 #[inline]
439 fn try_from(v: Value) -> Result<i_slint_core::lengths::LogicalLength, Self::Error> {
440 match v {
441 Value::Number(n) => Ok(i_slint_core::lengths::LogicalLength::new(n as _)),
442 _ => Err(v),
443 }
444 }
445}
446
447impl From<i_slint_core::lengths::LogicalPoint> for Value {
448 #[inline]
449 fn from(pt: i_slint_core::lengths::LogicalPoint) -> Self {
450 Value::Struct(Struct::from_iter([
451 ("x".to_owned(), Value::Number(pt.x as _)),
452 ("y".to_owned(), Value::Number(pt.y as _)),
453 ]))
454 }
455}
456impl TryFrom<Value> for i_slint_core::lengths::LogicalPoint {
457 type Error = Value;
458 #[inline]
459 fn try_from(v: Value) -> Result<i_slint_core::lengths::LogicalPoint, Self::Error> {
460 match v {
461 Value::Struct(s) => {
462 let x = s
463 .get_field("x")
464 .cloned()
465 .unwrap_or_else(|| Value::Number(0 as _))
466 .try_into()?;
467 let y = s
468 .get_field("y")
469 .cloned()
470 .unwrap_or_else(|| Value::Number(0 as _))
471 .try_into()?;
472 Ok(i_slint_core::lengths::LogicalPoint::new(x, y))
473 }
474 _ => Err(v),
475 }
476 }
477}
478
479impl From<i_slint_core::lengths::LogicalSize> for Value {
480 #[inline]
481 fn from(s: i_slint_core::lengths::LogicalSize) -> Self {
482 Value::Struct(Struct::from_iter([
483 ("width".to_owned(), Value::Number(s.width as _)),
484 ("height".to_owned(), Value::Number(s.height as _)),
485 ]))
486 }
487}
488impl TryFrom<Value> for i_slint_core::lengths::LogicalSize {
489 type Error = Value;
490 #[inline]
491 fn try_from(v: Value) -> Result<i_slint_core::lengths::LogicalSize, Self::Error> {
492 match v {
493 Value::Struct(s) => {
494 let width = s
495 .get_field("width")
496 .cloned()
497 .unwrap_or_else(|| Value::Number(0 as _))
498 .try_into()?;
499 let height = s
500 .get_field("height")
501 .cloned()
502 .unwrap_or_else(|| Value::Number(0 as _))
503 .try_into()?;
504 Ok(i_slint_core::lengths::LogicalSize::new(width, height))
505 }
506 _ => Err(v),
507 }
508 }
509}
510
511impl From<i_slint_core::lengths::LogicalEdges> for Value {
512 #[inline]
513 fn from(s: i_slint_core::lengths::LogicalEdges) -> Self {
514 Value::Struct(Struct::from_iter([
515 ("left".to_owned(), Value::Number(s.left as _)),
516 ("right".to_owned(), Value::Number(s.right as _)),
517 ("top".to_owned(), Value::Number(s.top as _)),
518 ("bottom".to_owned(), Value::Number(s.bottom as _)),
519 ]))
520 }
521}
522impl TryFrom<Value> for i_slint_core::lengths::LogicalEdges {
523 type Error = Value;
524 #[inline]
525 fn try_from(v: Value) -> Result<i_slint_core::lengths::LogicalEdges, Self::Error> {
526 match v {
527 Value::Struct(s) => {
528 let left = s
529 .get_field("left")
530 .cloned()
531 .unwrap_or_else(|| Value::Number(0 as _))
532 .try_into()?;
533 let right = s
534 .get_field("right")
535 .cloned()
536 .unwrap_or_else(|| Value::Number(0 as _))
537 .try_into()?;
538 let top = s
539 .get_field("top")
540 .cloned()
541 .unwrap_or_else(|| Value::Number(0 as _))
542 .try_into()?;
543 let bottom = s
544 .get_field("bottom")
545 .cloned()
546 .unwrap_or_else(|| Value::Number(0 as _))
547 .try_into()?;
548 Ok(i_slint_core::lengths::LogicalEdges::new(left, right, top, bottom))
549 }
550 _ => Err(v),
551 }
552 }
553}
554
555impl<T: Into<Value> + TryFrom<Value> + 'static> From<ModelRc<T>> for Value {
556 fn from(m: ModelRc<T>) -> Self {
557 if let Some(v) = <dyn core::any::Any>::downcast_ref::<ModelRc<Value>>(&m) {
558 Value::Model(v.clone())
559 } else {
560 Value::Model(ModelRc::new(crate::value_model::ValueMapModel(m)))
561 }
562 }
563}
564impl<T: TryFrom<Value> + Default + 'static> TryFrom<Value> for ModelRc<T> {
565 type Error = Value;
566 #[inline]
567 fn try_from(v: Value) -> Result<ModelRc<T>, Self::Error> {
568 match v {
569 Value::Model(m) => {
570 if let Some(v) = <dyn core::any::Any>::downcast_ref::<ModelRc<T>>(&m) {
571 Ok(v.clone())
572 } else if let Some(v) =
573 m.as_any().downcast_ref::<crate::value_model::ValueMapModel<T>>()
574 {
575 Ok(v.0.clone())
576 } else {
577 Ok(ModelRc::new(m.map(|v| T::try_from(v).unwrap_or_default())))
578 }
579 }
580 _ => Err(v),
581 }
582 }
583}
584
585#[test]
586fn value_model_conversion() {
587 use i_slint_core::model::*;
588 let m = ModelRc::new(VecModel::from_slice(&[Value::Number(42.), Value::Number(12.)]));
589 let v = Value::from(m.clone());
590 assert_eq!(v, Value::Model(m.clone()));
591 let m2: ModelRc<Value> = v.clone().try_into().unwrap();
592 assert_eq!(m2, m);
593
594 let int_model: ModelRc<i32> = v.clone().try_into().unwrap();
595 assert_eq!(int_model.row_count(), 2);
596 assert_eq!(int_model.iter().collect::<Vec<_>>(), vec![42, 12]);
597
598 let Value::Model(m3) = int_model.clone().into() else { panic!("not a model?") };
599 assert_eq!(m3.row_count(), 2);
600 assert_eq!(m3.iter().collect::<Vec<_>>(), vec![Value::Number(42.), Value::Number(12.)]);
601
602 let str_model: ModelRc<SharedString> = v.clone().try_into().unwrap();
603 assert_eq!(str_model.row_count(), 2);
604 assert_eq!(str_model.iter().collect::<Vec<_>>(), vec!["", ""]);
606
607 let err: Result<ModelRc<Value>, _> = Value::Bool(true).try_into();
608 assert!(err.is_err());
609
610 let model =
611 Rc::new(VecModel::<SharedString>::from_iter(["foo".into(), "bar".into(), "baz".into()]));
612
613 let value: Value = ModelRc::from(model.clone()).into();
614 let value_model: ModelRc<Value> = value.clone().try_into().unwrap();
615 assert_eq!(value_model.row_data(2).unwrap(), Value::String("baz".into()));
616 value_model.set_row_data(1, Value::String("qux".into()));
617 value_model.set_row_data(0, Value::Bool(true));
618 assert_eq!(value_model.row_data(1).unwrap(), Value::String("qux".into()));
619 assert_eq!(value_model.row_data(0).unwrap(), Value::String("foo".into()));
621
622 assert_eq!(model.row_data(1).unwrap(), SharedString::from("qux"));
624 assert_eq!(model.row_data(0).unwrap(), SharedString::from("foo"));
625
626 let the_model: ModelRc<SharedString> = value.try_into().unwrap();
627 assert_eq!(the_model.row_data(1).unwrap(), SharedString::from("qux"));
628 assert_eq!(
629 model.as_ref() as *const VecModel<SharedString>,
630 the_model.as_any().downcast_ref::<VecModel<SharedString>>().unwrap()
631 as *const VecModel<SharedString>
632 );
633}
634
635pub(crate) fn normalize_identifier(ident: &str) -> SmolStr {
636 i_slint_compiler::parser::normalize_identifier(ident)
637}
638
639#[derive(Clone, PartialEq, Debug, Default)]
661pub struct Struct(pub(crate) HashMap<SmolStr, Value>);
662impl Struct {
663 pub fn get_field(&self, name: &str) -> Option<&Value> {
665 self.0.get(&*normalize_identifier(name))
666 }
667 pub fn set_field(&mut self, name: String, value: Value) {
669 self.0.insert(normalize_identifier(&name), value);
670 }
671
672 pub fn iter(&self) -> impl Iterator<Item = (&str, &Value)> {
674 self.0.iter().map(|(a, b)| (a.as_str(), b))
675 }
676}
677
678impl FromIterator<(String, Value)> for Struct {
679 fn from_iter<T: IntoIterator<Item = (String, Value)>>(iter: T) -> Self {
680 Self(iter.into_iter().map(|(s, v)| (normalize_identifier(&s), v)).collect())
681 }
682}
683
684#[deprecated(note = "Use slint_interpreter::Compiler instead")]
686pub struct ComponentCompiler {
687 config: i_slint_compiler::CompilerConfiguration,
688 diagnostics: Vec<Diagnostic>,
689}
690
691#[allow(deprecated)]
692impl Default for ComponentCompiler {
693 fn default() -> Self {
694 let mut config = i_slint_compiler::CompilerConfiguration::new(
695 i_slint_compiler::generator::OutputFormat::Interpreter,
696 );
697 config.components_to_generate = i_slint_compiler::ComponentSelection::LastExported;
698 Self { config, diagnostics: Vec::new() }
699 }
700}
701
702#[allow(deprecated)]
703impl ComponentCompiler {
704 pub fn new() -> Self {
706 Self::default()
707 }
708
709 pub fn set_include_paths(&mut self, include_paths: Vec<std::path::PathBuf>) {
711 self.config.include_paths = include_paths;
712 }
713
714 pub fn include_paths(&self) -> &Vec<std::path::PathBuf> {
716 &self.config.include_paths
717 }
718
719 pub fn set_library_paths(&mut self, library_paths: HashMap<String, PathBuf>) {
721 self.config.library_paths = library_paths;
722 }
723
724 pub fn library_paths(&self) -> &HashMap<String, PathBuf> {
726 &self.config.library_paths
727 }
728
729 pub fn set_style(&mut self, style: String) {
741 self.config.style = Some(style);
742 }
743
744 pub fn style(&self) -> Option<&String> {
746 self.config.style.as_ref()
747 }
748
749 pub fn set_translation_domain(&mut self, domain: String) {
751 self.config.translation_domain = Some(domain);
752 }
753
754 pub fn set_file_loader(
762 &mut self,
763 file_loader_fallback: impl Fn(
764 &Path,
765 ) -> core::pin::Pin<
766 Box<dyn Future<Output = Option<std::io::Result<String>>>>,
767 > + 'static,
768 ) {
769 self.config.open_import_callback =
770 Some(Rc::new(move |path| file_loader_fallback(Path::new(path.as_str()))));
771 }
772
773 pub fn diagnostics(&self) -> &Vec<Diagnostic> {
775 &self.diagnostics
776 }
777
778 pub async fn build_from_path<P: AsRef<Path>>(
797 &mut self,
798 path: P,
799 ) -> Option<ComponentDefinition> {
800 let path = path.as_ref();
801 let source = match i_slint_compiler::diagnostics::load_from_path(path) {
802 Ok(s) => s,
803 Err(d) => {
804 self.diagnostics = vec![d];
805 return None;
806 }
807 };
808
809 let r = crate::dynamic_item_tree::load(source, path.into(), self.config.clone()).await;
810 self.diagnostics = r.diagnostics.into_iter().collect();
811 r.components.into_values().next()
812 }
813
814 pub async fn build_from_source(
831 &mut self,
832 source_code: String,
833 path: PathBuf,
834 ) -> Option<ComponentDefinition> {
835 let r = crate::dynamic_item_tree::load(source_code, path, self.config.clone()).await;
836 self.diagnostics = r.diagnostics.into_iter().collect();
837 r.components.into_values().next()
838 }
839}
840
841pub struct Compiler {
844 config: i_slint_compiler::CompilerConfiguration,
845}
846
847impl Default for Compiler {
848 fn default() -> Self {
849 let config = i_slint_compiler::CompilerConfiguration::new(
850 i_slint_compiler::generator::OutputFormat::Interpreter,
851 );
852 Self { config }
853 }
854}
855
856impl Compiler {
857 pub fn new() -> Self {
859 Self::default()
860 }
861
862 #[doc(hidden)]
863 #[cfg(feature = "internal")]
864 pub fn set_embed_resources(&mut self, embed_resources: i_slint_compiler::EmbedResourcesKind) {
865 self.config.embed_resources = embed_resources;
866 }
867
868 #[doc(hidden)]
872 #[cfg(feature = "internal")]
873 pub fn compiler_configuration(
874 &mut self,
875 _: i_slint_core::InternalToken,
876 ) -> &mut i_slint_compiler::CompilerConfiguration {
877 &mut self.config
878 }
879
880 pub fn set_include_paths(&mut self, include_paths: Vec<std::path::PathBuf>) {
882 self.config.include_paths = include_paths;
883 }
884
885 pub fn include_paths(&self) -> &Vec<std::path::PathBuf> {
887 &self.config.include_paths
888 }
889
890 pub fn set_library_paths(&mut self, library_paths: HashMap<String, PathBuf>) {
892 self.config.library_paths = library_paths;
893 }
894
895 pub fn library_paths(&self) -> &HashMap<String, PathBuf> {
897 &self.config.library_paths
898 }
899
900 pub fn set_style(&mut self, style: String) {
911 self.config.style = Some(style);
912 }
913
914 pub fn style(&self) -> Option<&String> {
916 self.config.style.as_ref()
917 }
918
919 pub fn set_translation_domain(&mut self, domain: String) {
921 self.config.translation_domain = Some(domain);
922 }
923
924 pub fn set_default_translation_context(
930 &mut self,
931 default_translation_context: DefaultTranslationContext,
932 ) {
933 self.config.default_translation_context = default_translation_context;
934 }
935
936 pub fn set_file_loader(
944 &mut self,
945 file_loader_fallback: impl Fn(
946 &Path,
947 ) -> core::pin::Pin<
948 Box<dyn Future<Output = Option<std::io::Result<String>>>>,
949 > + 'static,
950 ) {
951 self.config.open_import_callback =
952 Some(Rc::new(move |path| file_loader_fallback(Path::new(path.as_str()))));
953 }
954
955 pub async fn build_from_path<P: AsRef<Path>>(&self, path: P) -> CompilationResult {
974 let path = path.as_ref();
975 let source = match i_slint_compiler::diagnostics::load_from_path(path) {
976 Ok(s) => s,
977 Err(d) => {
978 let mut diagnostics = i_slint_compiler::diagnostics::BuildDiagnostics::default();
979 diagnostics.push_compiler_error(d);
980 return CompilationResult {
981 components: HashMap::new(),
982 diagnostics: diagnostics.into_iter().collect(),
983 #[cfg(feature = "internal")]
984 watch_paths: vec![i_slint_compiler::pathutils::clean_path(path)],
985 #[cfg(feature = "internal")]
986 structs_and_enums: Vec::new(),
987 #[cfg(feature = "internal")]
988 named_exports: Vec::new(),
989 };
990 }
991 };
992
993 crate::dynamic_item_tree::load(source, path.into(), self.config.clone()).await
994 }
995
996 pub async fn build_from_source(&self, source_code: String, path: PathBuf) -> CompilationResult {
1009 crate::dynamic_item_tree::load(source_code, path, self.config.clone()).await
1010 }
1011}
1012
1013#[derive(Clone)]
1020pub struct CompilationResult {
1021 pub(crate) components: HashMap<String, ComponentDefinition>,
1022 pub(crate) diagnostics: Vec<Diagnostic>,
1023 #[cfg(feature = "internal")]
1024 pub(crate) watch_paths: Vec<PathBuf>,
1025 #[cfg(feature = "internal")]
1026 pub(crate) structs_and_enums: Vec<LangType>,
1027 #[cfg(feature = "internal")]
1029 pub(crate) named_exports: Vec<(String, String)>,
1030}
1031
1032impl core::fmt::Debug for CompilationResult {
1033 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1034 f.debug_struct("CompilationResult")
1035 .field("components", &self.components.keys())
1036 .field("diagnostics", &self.diagnostics)
1037 .finish()
1038 }
1039}
1040
1041impl CompilationResult {
1042 pub fn has_errors(&self) -> bool {
1045 self.diagnostics().any(|diag| diag.level() == DiagnosticLevel::Error)
1046 }
1047
1048 pub fn diagnostics(&self) -> impl Iterator<Item = Diagnostic> + '_ {
1052 self.diagnostics.iter().cloned()
1053 }
1054
1055 #[cfg(feature = "display-diagnostics")]
1061 pub fn print_diagnostics(&self) {
1062 print_diagnostics(&self.diagnostics)
1063 }
1064
1065 pub fn components(&self) -> impl Iterator<Item = ComponentDefinition> + '_ {
1067 self.components.values().cloned()
1068 }
1069
1070 pub fn component_names(&self) -> impl Iterator<Item = &str> + '_ {
1072 self.components.keys().map(|s| s.as_str())
1073 }
1074
1075 pub fn component(&self, name: &str) -> Option<ComponentDefinition> {
1078 self.components.get(name).cloned()
1079 }
1080
1081 #[doc(hidden)]
1083 #[cfg(feature = "internal")]
1084 pub fn watch_paths(&self, _: i_slint_core::InternalToken) -> &[PathBuf] {
1085 &self.watch_paths
1086 }
1087
1088 #[doc(hidden)]
1090 #[cfg(feature = "internal")]
1091 pub fn structs_and_enums(
1092 &self,
1093 _: i_slint_core::InternalToken,
1094 ) -> impl Iterator<Item = &LangType> {
1095 self.structs_and_enums.iter()
1096 }
1097
1098 #[doc(hidden)]
1101 #[cfg(feature = "internal")]
1102 pub fn named_exports(
1103 &self,
1104 _: i_slint_core::InternalToken,
1105 ) -> impl Iterator<Item = &(String, String)> {
1106 self.named_exports.iter()
1107 }
1108}
1109
1110#[derive(Clone)]
1118pub struct ComponentDefinition {
1119 pub(crate) inner: crate::dynamic_item_tree::ErasedItemTreeDescription,
1120}
1121
1122impl ComponentDefinition {
1123 pub fn create(&self) -> Result<ComponentInstance, PlatformError> {
1125 let instance = self.create_with_options(Default::default())?;
1126 if !instance.is_system_tray_rooted() {
1129 instance.inner.window_adapter_ref()?;
1131 i_slint_core::window::WindowInner::from_pub(instance.window())
1134 .ensure_tree_instantiated();
1135 }
1136 Ok(instance)
1137 }
1138
1139 #[doc(hidden)]
1141 #[cfg(feature = "internal")]
1142 pub fn create_embedded(&self, ctx: FactoryContext) -> Result<ComponentInstance, PlatformError> {
1143 self.create_with_options(WindowOptions::Embed {
1144 parent_item_tree: ctx.parent_item_tree,
1145 parent_item_tree_index: ctx.parent_item_tree_index,
1146 })
1147 }
1148
1149 #[doc(hidden)]
1151 #[cfg(feature = "internal")]
1152 pub fn create_with_existing_window(
1153 &self,
1154 window: &Window,
1155 ) -> Result<ComponentInstance, PlatformError> {
1156 self.create_with_options(WindowOptions::UseExistingWindow(
1157 WindowInner::from_pub(window).window_adapter(),
1158 ))
1159 }
1160
1161 pub(crate) fn create_with_options(
1163 &self,
1164 options: WindowOptions,
1165 ) -> Result<ComponentInstance, PlatformError> {
1166 generativity::make_guard!(guard);
1167 Ok(ComponentInstance { inner: self.inner.unerase(guard).clone().create(options)? })
1168 }
1169
1170 #[doc(hidden)]
1174 #[cfg(feature = "internal")]
1175 pub fn properties_and_callbacks(
1176 &self,
1177 ) -> impl Iterator<
1178 Item = (
1179 String,
1180 (i_slint_compiler::langtype::Type, i_slint_compiler::object_tree::PropertyVisibility),
1181 ),
1182 > + '_ {
1183 let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1186 self.inner.unerase(guard).properties().map(|(s, t, v)| (s.to_string(), (t, v)))
1187 }
1188
1189 pub fn properties(&self) -> impl Iterator<Item = (String, ValueType)> + '_ {
1192 let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1195 self.inner.unerase(guard).properties().filter_map(|(prop_name, prop_type, _)| {
1196 if prop_type.is_property_type() {
1197 Some((prop_name.to_string(), prop_type.into()))
1198 } else {
1199 None
1200 }
1201 })
1202 }
1203
1204 pub fn callbacks(&self) -> impl Iterator<Item = String> + '_ {
1206 let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1209 self.inner.unerase(guard).properties().filter_map(|(prop_name, prop_type, _)| {
1210 if matches!(prop_type, LangType::Callback { .. }) {
1211 Some(prop_name.to_string())
1212 } else {
1213 None
1214 }
1215 })
1216 }
1217
1218 pub fn functions(&self) -> impl Iterator<Item = String> + '_ {
1220 let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1223 self.inner.unerase(guard).properties().filter_map(|(prop_name, prop_type, _)| {
1224 if matches!(prop_type, LangType::Function { .. }) {
1225 Some(prop_name.to_string())
1226 } else {
1227 None
1228 }
1229 })
1230 }
1231
1232 pub fn globals(&self) -> impl Iterator<Item = String> + '_ {
1237 let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1240 self.inner.unerase(guard).global_names().map(|s| s.to_string())
1241 }
1242
1243 #[doc(hidden)]
1247 #[cfg(feature = "internal")]
1248 pub fn global_properties_and_callbacks(
1249 &self,
1250 global_name: &str,
1251 ) -> Option<
1252 impl Iterator<
1253 Item = (
1254 String,
1255 (
1256 i_slint_compiler::langtype::Type,
1257 i_slint_compiler::object_tree::PropertyVisibility,
1258 ),
1259 ),
1260 > + '_,
1261 > {
1262 let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1265 self.inner
1266 .unerase(guard)
1267 .global_properties(global_name)
1268 .map(|o| o.map(|(s, t, v)| (s.to_string(), (t, v))))
1269 }
1270
1271 pub fn global_properties(
1273 &self,
1274 global_name: &str,
1275 ) -> Option<impl Iterator<Item = (String, ValueType)> + '_> {
1276 let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1279 self.inner.unerase(guard).global_properties(global_name).map(|iter| {
1280 iter.filter_map(|(prop_name, prop_type, _)| {
1281 if prop_type.is_property_type() {
1282 Some((prop_name.to_string(), prop_type.into()))
1283 } else {
1284 None
1285 }
1286 })
1287 })
1288 }
1289
1290 pub fn global_callbacks(&self, global_name: &str) -> Option<impl Iterator<Item = String> + '_> {
1292 let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1295 self.inner.unerase(guard).global_properties(global_name).map(|iter| {
1296 iter.filter_map(|(prop_name, prop_type, _)| {
1297 if matches!(prop_type, LangType::Callback { .. }) {
1298 Some(prop_name.to_string())
1299 } else {
1300 None
1301 }
1302 })
1303 })
1304 }
1305
1306 pub fn global_functions(&self, global_name: &str) -> Option<impl Iterator<Item = String> + '_> {
1308 let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1311 self.inner.unerase(guard).global_properties(global_name).map(|iter| {
1312 iter.filter_map(|(prop_name, prop_type, _)| {
1313 if matches!(prop_type, LangType::Function { .. }) {
1314 Some(prop_name.to_string())
1315 } else {
1316 None
1317 }
1318 })
1319 })
1320 }
1321
1322 pub fn name(&self) -> &str {
1324 let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1327 self.inner.unerase(guard).id()
1328 }
1329
1330 #[doc(hidden)]
1334 #[cfg(feature = "internal")]
1335 pub fn is_window(&self) -> bool {
1336 let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1337 !self.inner.unerase(guard).original.inherits_system_tray_icon()
1338 }
1339
1340 #[cfg(feature = "internal")]
1342 #[doc(hidden)]
1343 pub fn root_component(&self) -> Rc<i_slint_compiler::object_tree::Component> {
1344 let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1345 self.inner.unerase(guard).original.clone()
1346 }
1347
1348 #[cfg(feature = "internal-highlight")]
1352 pub fn type_loader(&self) -> std::rc::Rc<i_slint_compiler::typeloader::TypeLoader> {
1353 let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1354 self.inner.unerase(guard).type_loader.get().unwrap().clone()
1355 }
1356
1357 #[cfg(feature = "internal-highlight")]
1365 pub fn raw_type_loader(&self) -> Option<i_slint_compiler::typeloader::TypeLoader> {
1366 let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1367 self.inner
1368 .unerase(guard)
1369 .raw_type_loader
1370 .get()
1371 .unwrap()
1372 .as_ref()
1373 .and_then(|tl| i_slint_compiler::typeloader::snapshot(tl))
1374 }
1375}
1376
1377#[cfg(feature = "display-diagnostics")]
1383pub fn print_diagnostics(diagnostics: &[Diagnostic]) {
1384 let mut build_diagnostics = i_slint_compiler::diagnostics::BuildDiagnostics::default();
1385 for d in diagnostics {
1386 build_diagnostics.push_compiler_error(d.clone())
1387 }
1388 build_diagnostics.print();
1389}
1390
1391#[repr(C)]
1399pub struct ComponentInstance {
1400 pub(crate) inner: crate::dynamic_item_tree::DynamicComponentVRc,
1401}
1402
1403impl ComponentInstance {
1404 pub fn definition(&self) -> ComponentDefinition {
1406 generativity::make_guard!(guard);
1407 ComponentDefinition { inner: self.inner.unerase(guard).description().into() }
1408 }
1409
1410 fn is_system_tray_rooted(&self) -> bool {
1411 let guard = unsafe { generativity::Guard::new(generativity::Id::new()) };
1412 self.inner.unerase(guard).description().original.inherits_system_tray_icon()
1413 }
1414
1415 fn set_tray_icon_visible(&self, visible: bool) {
1419 generativity::make_guard!(guard);
1420 let description = self.inner.unerase(guard).description();
1421 let item_info = &description.items[description.original.root_element.borrow().id.as_str()];
1422 let item_rc =
1423 ItemRc::new(vtable::VRc::into_dyn(self.inner.clone()), item_info.item_index());
1424 let tray = item_rc
1425 .downcast::<SystemTrayIcon>()
1426 .expect("the root item of a SystemTrayIcon-rooted component is a SystemTrayIcon");
1427 tray.as_pin_ref().visible.set(visible);
1428 }
1429
1430 pub fn get_property(&self, name: &str) -> Result<Value, GetPropertyError> {
1450 generativity::make_guard!(guard);
1451 let comp = self.inner.unerase(guard);
1452 let name = normalize_identifier(name);
1453
1454 if comp
1455 .description()
1456 .original
1457 .root_element
1458 .borrow()
1459 .property_declarations
1460 .get(&name)
1461 .is_none_or(|d| !d.expose_in_public_api)
1462 {
1463 return Err(GetPropertyError::NoSuchProperty);
1464 }
1465
1466 comp.description()
1467 .get_property(comp.borrow(), &name)
1468 .map_err(|()| GetPropertyError::NoSuchProperty)
1469 }
1470
1471 pub fn set_property(&self, name: &str, value: Value) -> Result<(), SetPropertyError> {
1473 let name = normalize_identifier(name);
1474 generativity::make_guard!(guard);
1475 let comp = self.inner.unerase(guard);
1476 let d = comp.description();
1477 let elem = d.original.root_element.borrow();
1478 let decl = elem.property_declarations.get(&name).ok_or(SetPropertyError::NoSuchProperty)?;
1479
1480 if !decl.expose_in_public_api {
1481 return Err(SetPropertyError::NoSuchProperty);
1482 } else if decl.visibility == i_slint_compiler::object_tree::PropertyVisibility::Output {
1483 return Err(SetPropertyError::AccessDenied);
1484 }
1485
1486 d.set_property(comp.borrow(), &name, value)
1487 }
1488
1489 pub fn set_callback(
1524 &self,
1525 name: &str,
1526 callback: impl Fn(&[Value]) -> Value + 'static,
1527 ) -> Result<(), SetCallbackError> {
1528 generativity::make_guard!(guard);
1529 let comp = self.inner.unerase(guard);
1530 comp.description()
1531 .set_callback_handler(comp.borrow(), &normalize_identifier(name), Box::new(callback))
1532 .map_err(|()| SetCallbackError::NoSuchCallback)
1533 }
1534
1535 pub fn invoke(&self, name: &str, args: &[Value]) -> Result<Value, InvokeError> {
1540 generativity::make_guard!(guard);
1541 let comp = self.inner.unerase(guard);
1542 comp.description()
1543 .invoke(comp.borrow(), &normalize_identifier(name), args)
1544 .map_err(|()| InvokeError::NoSuchCallable)
1545 }
1546
1547 pub fn get_global_property(
1572 &self,
1573 global: &str,
1574 property: &str,
1575 ) -> Result<Value, GetPropertyError> {
1576 generativity::make_guard!(guard);
1577 let comp = self.inner.unerase(guard);
1578 comp.description()
1579 .get_global(comp.borrow(), &normalize_identifier(global))
1580 .map_err(|()| GetPropertyError::NoSuchProperty)? .as_ref()
1582 .get_property(&normalize_identifier(property))
1583 .map_err(|()| GetPropertyError::NoSuchProperty)
1584 }
1585
1586 pub fn set_global_property(
1588 &self,
1589 global: &str,
1590 property: &str,
1591 value: Value,
1592 ) -> Result<(), SetPropertyError> {
1593 generativity::make_guard!(guard);
1594 let comp = self.inner.unerase(guard);
1595 comp.description()
1596 .get_global(comp.borrow(), &normalize_identifier(global))
1597 .map_err(|()| SetPropertyError::NoSuchProperty)? .as_ref()
1599 .set_property(&normalize_identifier(property), value)
1600 }
1601
1602 pub fn set_global_callback(
1637 &self,
1638 global: &str,
1639 name: &str,
1640 callback: impl Fn(&[Value]) -> Value + 'static,
1641 ) -> Result<(), SetCallbackError> {
1642 generativity::make_guard!(guard);
1643 let comp = self.inner.unerase(guard);
1644 comp.description()
1645 .get_global(comp.borrow(), &normalize_identifier(global))
1646 .map_err(|()| SetCallbackError::NoSuchCallback)? .as_ref()
1648 .set_callback_handler(&normalize_identifier(name), Box::new(callback))
1649 .map_err(|()| SetCallbackError::NoSuchCallback)
1650 }
1651
1652 pub fn invoke_global(
1657 &self,
1658 global: &str,
1659 callable_name: &str,
1660 args: &[Value],
1661 ) -> Result<Value, InvokeError> {
1662 generativity::make_guard!(guard);
1663 let comp = self.inner.unerase(guard);
1664 let g = comp
1665 .description()
1666 .get_global(comp.borrow(), &normalize_identifier(global))
1667 .map_err(|()| InvokeError::NoSuchCallable)?; let callable_name = normalize_identifier(callable_name);
1669 if matches!(
1670 comp.description()
1671 .original
1672 .root_element
1673 .borrow()
1674 .lookup_property(&callable_name)
1675 .property_type,
1676 LangType::Function { .. }
1677 ) {
1678 g.as_ref()
1679 .eval_function(&callable_name, args.to_vec())
1680 .map_err(|()| InvokeError::NoSuchCallable)
1681 } else {
1682 g.as_ref()
1683 .invoke_callback(&callable_name, args)
1684 .map_err(|()| InvokeError::NoSuchCallable)
1685 }
1686 }
1687
1688 #[cfg(feature = "internal-highlight")]
1692 pub fn component_positions(
1693 &self,
1694 path: &Path,
1695 offset: u32,
1696 ) -> Vec<crate::highlight::HighlightedRect> {
1697 crate::highlight::component_positions(&self.inner, path, offset)
1698 }
1699
1700 #[cfg(feature = "internal-highlight")]
1704 pub fn element_positions(
1705 &self,
1706 element: &i_slint_compiler::object_tree::ElementRc,
1707 ) -> Vec<crate::highlight::HighlightedRect> {
1708 crate::highlight::element_positions(
1709 &self.inner,
1710 element,
1711 crate::highlight::ElementPositionFilter::IncludeClipped,
1712 )
1713 }
1714
1715 #[cfg(feature = "internal-highlight")]
1719 pub fn element_node_at_source_code_position(
1720 &self,
1721 path: &Path,
1722 offset: u32,
1723 ) -> Vec<(i_slint_compiler::object_tree::ElementRc, usize)> {
1724 crate::highlight::element_node_at_source_code_position(&self.inner, path, offset)
1725 }
1726
1727 #[cfg(feature = "internal")]
1729 pub fn set_debug_hook_callback(&self, callback: Option<crate::debug_hook::DebugHookCallback>) {
1730 generativity::make_guard!(guard);
1731 let comp = self.inner.unerase(guard);
1732 crate::debug_hook::set_debug_hook_callback(comp, callback);
1733 }
1734}
1735
1736impl StrongHandle for ComponentInstance {
1737 type WeakInner = vtable::VWeak<ItemTreeVTable, crate::dynamic_item_tree::ErasedItemTreeBox>;
1738
1739 fn upgrade_from_weak_inner(inner: &Self::WeakInner) -> Option<Self> {
1740 Some(Self { inner: inner.upgrade()? })
1741 }
1742}
1743
1744impl ComponentHandle for ComponentInstance {
1745 fn as_weak(&self) -> Weak<Self>
1746 where
1747 Self: Sized,
1748 {
1749 Weak::new(vtable::VRc::downgrade(&self.inner))
1750 }
1751
1752 fn clone_strong(&self) -> Self {
1753 Self { inner: self.inner.clone() }
1754 }
1755
1756 fn show(&self) -> Result<(), PlatformError> {
1757 if self.is_system_tray_rooted() {
1758 self.set_tray_icon_visible(true);
1759 return Ok(());
1760 }
1761 self.inner.window_adapter_ref()?.window().show()
1762 }
1763
1764 fn hide(&self) -> Result<(), PlatformError> {
1765 if self.is_system_tray_rooted() {
1766 self.set_tray_icon_visible(false);
1767 return Ok(());
1768 }
1769 self.inner.window_adapter_ref()?.window().hide()
1770 }
1771
1772 fn run(&self) -> Result<(), PlatformError> {
1773 self.show()?;
1774 run_event_loop()?;
1775 self.hide()
1776 }
1777
1778 fn window(&self) -> &Window {
1779 self.inner.window_adapter_ref().unwrap().window()
1780 }
1781
1782 fn global<'a, T: Global<'a, Self>>(&'a self) -> T
1783 where
1784 Self: Sized,
1785 {
1786 unreachable!()
1787 }
1788}
1789
1790impl From<ComponentInstance>
1791 for vtable::VRc<i_slint_core::item_tree::ItemTreeVTable, ErasedItemTreeBox>
1792{
1793 fn from(value: ComponentInstance) -> Self {
1794 value.inner
1795 }
1796}
1797
1798#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Error, derive_more::Display)]
1800#[non_exhaustive]
1801pub enum GetPropertyError {
1802 #[display("no such property")]
1804 NoSuchProperty,
1805}
1806
1807#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Error, derive_more::Display)]
1809#[non_exhaustive]
1810pub enum SetPropertyError {
1811 #[display("no such property")]
1813 NoSuchProperty,
1814 #[display("wrong type")]
1820 WrongType,
1821 #[display("access denied")]
1823 AccessDenied,
1824}
1825
1826#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Error, derive_more::Display)]
1828#[non_exhaustive]
1829pub enum SetCallbackError {
1830 #[display("no such callback")]
1832 NoSuchCallback,
1833}
1834
1835#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Error, derive_more::Display)]
1837#[non_exhaustive]
1838pub enum InvokeError {
1839 #[display("no such callback or function")]
1841 NoSuchCallable,
1842}
1843
1844pub fn run_event_loop() -> Result<(), PlatformError> {
1848 i_slint_backend_selector::with_platform(|b| b.run_event_loop())
1849}
1850
1851pub fn spawn_local<F: Future + 'static>(fut: F) -> Result<JoinHandle<F::Output>, EventLoopError> {
1855 i_slint_backend_selector::with_global_context(|ctx| ctx.spawn_local(fut))
1856 .map_err(|_| EventLoopError::NoEventLoopProvider)?
1857}
1858
1859#[test]
1860fn component_definition_properties() {
1861 i_slint_backend_testing::init_no_event_loop();
1862 let mut compiler = Compiler::default();
1863 compiler.set_style("fluent".into());
1864 let comp_def = spin_on::spin_on(
1865 compiler.build_from_source(
1866 r#"
1867 export component Dummy {
1868 in-out property <string> test;
1869 in-out property <int> underscores-and-dashes_preserved: 44;
1870 callback hello;
1871 }"#
1872 .into(),
1873 "".into(),
1874 ),
1875 )
1876 .component("Dummy")
1877 .unwrap();
1878
1879 let props = comp_def.properties().collect::<Vec<(_, _)>>();
1880
1881 assert_eq!(props.len(), 2);
1882 assert_eq!(props[0].0, "test");
1883 assert_eq!(props[0].1, ValueType::String);
1884 assert_eq!(props[1].0, "underscores-and-dashes_preserved");
1885 assert_eq!(props[1].1, ValueType::Number);
1886
1887 let instance = comp_def.create().unwrap();
1888 assert_eq!(instance.get_property("underscores_and-dashes-preserved"), Ok(Value::Number(44.)));
1889 assert_eq!(
1890 instance.get_property("underscoresanddashespreserved"),
1891 Err(GetPropertyError::NoSuchProperty)
1892 );
1893 assert_eq!(
1894 instance.set_property("underscores-and_dashes-preserved", Value::Number(88.)),
1895 Ok(())
1896 );
1897 assert_eq!(
1898 instance.set_property("underscoresanddashespreserved", Value::Number(99.)),
1899 Err(SetPropertyError::NoSuchProperty)
1900 );
1901 assert_eq!(
1902 instance.set_property("underscores-and_dashes-preserved", Value::String("99".into())),
1903 Err(SetPropertyError::WrongType)
1904 );
1905 assert_eq!(instance.get_property("underscores-and-dashes-preserved"), Ok(Value::Number(88.)));
1906}
1907
1908#[test]
1909fn component_definition_properties2() {
1910 i_slint_backend_testing::init_no_event_loop();
1911 let mut compiler = Compiler::default();
1912 compiler.set_style("fluent".into());
1913 let comp_def = spin_on::spin_on(
1914 compiler.build_from_source(
1915 r#"
1916 export component Dummy {
1917 in-out property <string> sub-text <=> sub.text;
1918 sub := Text { property <int> private-not-exported; }
1919 out property <string> xreadonly: "the value";
1920 private property <string> xx: sub.text;
1921 callback hello;
1922 }"#
1923 .into(),
1924 "".into(),
1925 ),
1926 )
1927 .component("Dummy")
1928 .unwrap();
1929
1930 let props = comp_def.properties().collect::<Vec<(_, _)>>();
1931
1932 assert_eq!(props.len(), 2);
1933 assert_eq!(props[0].0, "sub-text");
1934 assert_eq!(props[0].1, ValueType::String);
1935 assert_eq!(props[1].0, "xreadonly");
1936
1937 let callbacks = comp_def.callbacks().collect::<Vec<_>>();
1938 assert_eq!(callbacks.len(), 1);
1939 assert_eq!(callbacks[0], "hello");
1940
1941 let instance = comp_def.create().unwrap();
1942 assert_eq!(
1943 instance.set_property("xreadonly", SharedString::from("XXX").into()),
1944 Err(SetPropertyError::AccessDenied)
1945 );
1946 assert_eq!(instance.get_property("xreadonly"), Ok(Value::String("the value".into())));
1947 assert_eq!(
1948 instance.set_property("xx", SharedString::from("XXX").into()),
1949 Err(SetPropertyError::NoSuchProperty)
1950 );
1951 assert_eq!(
1952 instance.set_property("background", Value::default()),
1953 Err(SetPropertyError::NoSuchProperty)
1954 );
1955
1956 assert_eq!(instance.get_property("background"), Err(GetPropertyError::NoSuchProperty));
1957 assert_eq!(instance.get_property("xx"), Err(GetPropertyError::NoSuchProperty));
1958}
1959
1960#[test]
1961fn globals() {
1962 i_slint_backend_testing::init_no_event_loop();
1963 let mut compiler = Compiler::default();
1964 compiler.set_style("fluent".into());
1965 let definition = spin_on::spin_on(
1966 compiler.build_from_source(
1967 r#"
1968 export global My-Super_Global {
1969 in-out property <int> the-property : 21;
1970 callback my-callback();
1971 }
1972 export { My-Super_Global as AliasedGlobal }
1973 export component Dummy {
1974 callback alias <=> My-Super_Global.my-callback;
1975 }"#
1976 .into(),
1977 "".into(),
1978 ),
1979 )
1980 .component("Dummy")
1981 .unwrap();
1982
1983 assert_eq!(definition.globals().collect::<Vec<_>>(), vec!["My-Super_Global", "AliasedGlobal"]);
1984
1985 assert!(definition.global_properties("not-there").is_none());
1986 {
1987 let expected_properties = vec![("the-property".to_string(), ValueType::Number)];
1988 let expected_callbacks = vec!["my-callback".to_string()];
1989
1990 let assert_properties_and_callbacks = |global_name| {
1991 assert_eq!(
1992 definition
1993 .global_properties(global_name)
1994 .map(|props| props.collect::<Vec<_>>())
1995 .as_ref(),
1996 Some(&expected_properties)
1997 );
1998 assert_eq!(
1999 definition
2000 .global_callbacks(global_name)
2001 .map(|props| props.collect::<Vec<_>>())
2002 .as_ref(),
2003 Some(&expected_callbacks)
2004 );
2005 };
2006
2007 assert_properties_and_callbacks("My-Super-Global");
2008 assert_properties_and_callbacks("My_Super-Global");
2009 assert_properties_and_callbacks("AliasedGlobal");
2010 }
2011
2012 let instance = definition.create().unwrap();
2013 assert_eq!(
2014 instance.set_global_property("My_Super-Global", "the_property", Value::Number(44.)),
2015 Ok(())
2016 );
2017 assert_eq!(
2018 instance.set_global_property("AliasedGlobal", "the_property", Value::Number(44.)),
2019 Ok(())
2020 );
2021 assert_eq!(
2022 instance.set_global_property("DontExist", "the-property", Value::Number(88.)),
2023 Err(SetPropertyError::NoSuchProperty)
2024 );
2025
2026 assert_eq!(
2027 instance.set_global_property("My_Super-Global", "theproperty", Value::Number(88.)),
2028 Err(SetPropertyError::NoSuchProperty)
2029 );
2030 assert_eq!(
2031 instance.set_global_property("AliasedGlobal", "theproperty", Value::Number(88.)),
2032 Err(SetPropertyError::NoSuchProperty)
2033 );
2034 assert_eq!(
2035 instance.set_global_property("My_Super-Global", "the_property", Value::String("88".into())),
2036 Err(SetPropertyError::WrongType)
2037 );
2038 assert_eq!(
2039 instance.get_global_property("My-Super_Global", "yoyo"),
2040 Err(GetPropertyError::NoSuchProperty)
2041 );
2042 assert_eq!(
2043 instance.get_global_property("My-Super_Global", "the-property"),
2044 Ok(Value::Number(44.))
2045 );
2046
2047 assert_eq!(
2048 instance.set_property("the-property", Value::Void),
2049 Err(SetPropertyError::NoSuchProperty)
2050 );
2051 assert_eq!(instance.get_property("the-property"), Err(GetPropertyError::NoSuchProperty));
2052
2053 assert_eq!(
2054 instance.set_global_callback("DontExist", "the-property", |_| panic!()),
2055 Err(SetCallbackError::NoSuchCallback)
2056 );
2057 assert_eq!(
2058 instance.set_global_callback("My_Super_Global", "the-property", |_| panic!()),
2059 Err(SetCallbackError::NoSuchCallback)
2060 );
2061 assert_eq!(
2062 instance.set_global_callback("My_Super_Global", "yoyo", |_| panic!()),
2063 Err(SetCallbackError::NoSuchCallback)
2064 );
2065
2066 assert_eq!(
2067 instance.invoke_global("DontExist", "the-property", &[]),
2068 Err(InvokeError::NoSuchCallable)
2069 );
2070 assert_eq!(
2071 instance.invoke_global("My_Super_Global", "the-property", &[]),
2072 Err(InvokeError::NoSuchCallable)
2073 );
2074 assert_eq!(
2075 instance.invoke_global("My_Super_Global", "yoyo", &[]),
2076 Err(InvokeError::NoSuchCallable)
2077 );
2078
2079 assert_eq!(instance.get_property("alias"), Err(GetPropertyError::NoSuchProperty));
2081}
2082
2083#[test]
2084fn call_functions() {
2085 i_slint_backend_testing::init_no_event_loop();
2086 let mut compiler = Compiler::default();
2087 compiler.set_style("fluent".into());
2088 let definition = spin_on::spin_on(
2089 compiler.build_from_source(
2090 r#"
2091 export global Gl {
2092 out property<string> q;
2093 public function foo-bar(a-a: string, b-b:int) -> string {
2094 q = a-a;
2095 return a-a + b-b;
2096 }
2097 }
2098 export component Test {
2099 out property<int> p;
2100 public function foo-bar(a: int, b:int) -> int {
2101 p = a;
2102 return a + b;
2103 }
2104 }"#
2105 .into(),
2106 "".into(),
2107 ),
2108 )
2109 .component("Test")
2110 .unwrap();
2111
2112 assert_eq!(definition.functions().collect::<Vec<_>>(), ["foo-bar"]);
2113 assert_eq!(definition.global_functions("Gl").unwrap().collect::<Vec<_>>(), ["foo-bar"]);
2114
2115 let instance = definition.create().unwrap();
2116
2117 assert_eq!(
2118 instance.invoke("foo_bar", &[Value::Number(3.), Value::Number(4.)]),
2119 Ok(Value::Number(7.))
2120 );
2121 assert_eq!(instance.invoke("p", &[]), Err(InvokeError::NoSuchCallable));
2122 assert_eq!(instance.get_property("p"), Ok(Value::Number(3.)));
2123
2124 assert_eq!(
2125 instance.invoke_global(
2126 "Gl",
2127 "foo_bar",
2128 &[Value::String("Hello".into()), Value::Number(10.)]
2129 ),
2130 Ok(Value::String("Hello10".into()))
2131 );
2132 assert_eq!(instance.get_global_property("Gl", "q"), Ok(Value::String("Hello".into())));
2133}
2134
2135#[test]
2136fn component_definition_struct_properties() {
2137 i_slint_backend_testing::init_no_event_loop();
2138 let mut compiler = Compiler::default();
2139 compiler.set_style("fluent".into());
2140 let comp_def = spin_on::spin_on(
2141 compiler.build_from_source(
2142 r#"
2143 export struct Settings {
2144 string_value: string,
2145 }
2146 export component Dummy {
2147 in-out property <Settings> test;
2148 }"#
2149 .into(),
2150 "".into(),
2151 ),
2152 )
2153 .component("Dummy")
2154 .unwrap();
2155
2156 let props = comp_def.properties().collect::<Vec<(_, _)>>();
2157
2158 assert_eq!(props.len(), 1);
2159 assert_eq!(props[0].0, "test");
2160 assert_eq!(props[0].1, ValueType::Struct);
2161
2162 let instance = comp_def.create().unwrap();
2163
2164 let valid_struct: Struct =
2165 [("string_value".to_string(), Value::String("hello".into()))].iter().cloned().collect();
2166
2167 assert_eq!(instance.set_property("test", Value::Struct(valid_struct.clone())), Ok(()));
2168 assert_eq!(instance.get_property("test").unwrap().value_type(), ValueType::Struct);
2169
2170 assert_eq!(instance.set_property("test", Value::Number(42.)), Err(SetPropertyError::WrongType));
2171
2172 let mut invalid_struct = valid_struct.clone();
2173 invalid_struct.set_field("other".into(), Value::Number(44.));
2174 assert_eq!(
2175 instance.set_property("test", Value::Struct(invalid_struct)),
2176 Err(SetPropertyError::WrongType)
2177 );
2178 let mut invalid_struct = valid_struct;
2179 invalid_struct.set_field("string_value".into(), Value::Number(44.));
2180 assert_eq!(
2181 instance.set_property("test", Value::Struct(invalid_struct)),
2182 Err(SetPropertyError::WrongType)
2183 );
2184}
2185
2186#[test]
2187fn component_definition_model_properties() {
2188 use i_slint_core::model::*;
2189 i_slint_backend_testing::init_no_event_loop();
2190 let mut compiler = Compiler::default();
2191 compiler.set_style("fluent".into());
2192 let comp_def = spin_on::spin_on(compiler.build_from_source(
2193 "export component Dummy { in-out property <[int]> prop: [42, 12]; }".into(),
2194 "".into(),
2195 ))
2196 .component("Dummy")
2197 .unwrap();
2198
2199 let props = comp_def.properties().collect::<Vec<(_, _)>>();
2200 assert_eq!(props.len(), 1);
2201 assert_eq!(props[0].0, "prop");
2202 assert_eq!(props[0].1, ValueType::Model);
2203
2204 let instance = comp_def.create().unwrap();
2205
2206 let int_model =
2207 Value::Model([Value::Number(14.), Value::Number(15.), Value::Number(16.)].into());
2208 let empty_model = Value::Model(ModelRc::new(VecModel::<Value>::default()));
2209 let model_with_string = Value::Model(VecModel::from_slice(&[
2210 Value::Number(1000.),
2211 Value::String("foo".into()),
2212 Value::Number(1111.),
2213 ]));
2214
2215 #[track_caller]
2216 fn check_model(val: Value, r: &[f64]) {
2217 if let Value::Model(m) = val {
2218 assert_eq!(r.len(), m.row_count());
2219 for (i, v) in r.iter().enumerate() {
2220 assert_eq!(m.row_data(i).unwrap(), Value::Number(*v));
2221 }
2222 } else {
2223 panic!("{val:?} not a model");
2224 }
2225 }
2226
2227 assert_eq!(instance.get_property("prop").unwrap().value_type(), ValueType::Model);
2228 check_model(instance.get_property("prop").unwrap(), &[42., 12.]);
2229
2230 instance.set_property("prop", int_model).unwrap();
2231 check_model(instance.get_property("prop").unwrap(), &[14., 15., 16.]);
2232
2233 assert_eq!(instance.set_property("prop", Value::Number(42.)), Err(SetPropertyError::WrongType));
2234 check_model(instance.get_property("prop").unwrap(), &[14., 15., 16.]);
2235 assert_eq!(instance.set_property("prop", model_with_string), Err(SetPropertyError::WrongType));
2236 check_model(instance.get_property("prop").unwrap(), &[14., 15., 16.]);
2237
2238 assert_eq!(instance.set_property("prop", empty_model), Ok(()));
2239 check_model(instance.get_property("prop").unwrap(), &[]);
2240}
2241
2242#[test]
2243fn lang_type_to_value_type() {
2244 use i_slint_compiler::langtype::Struct as LangStruct;
2245 use std::collections::BTreeMap;
2246
2247 assert_eq!(ValueType::from(LangType::Void), ValueType::Void);
2248 assert_eq!(ValueType::from(LangType::Float32), ValueType::Number);
2249 assert_eq!(ValueType::from(LangType::Int32), ValueType::Number);
2250 assert_eq!(ValueType::from(LangType::Duration), ValueType::Number);
2251 assert_eq!(ValueType::from(LangType::Angle), ValueType::Number);
2252 assert_eq!(ValueType::from(LangType::PhysicalLength), ValueType::Number);
2253 assert_eq!(ValueType::from(LangType::LogicalLength), ValueType::Number);
2254 assert_eq!(ValueType::from(LangType::Percent), ValueType::Number);
2255 assert_eq!(ValueType::from(LangType::UnitProduct(Vec::new())), ValueType::Number);
2256 assert_eq!(ValueType::from(LangType::String), ValueType::String);
2257 assert_eq!(ValueType::from(LangType::Color), ValueType::Brush);
2258 assert_eq!(ValueType::from(LangType::Brush), ValueType::Brush);
2259 assert_eq!(ValueType::from(LangType::Array(Rc::new(LangType::Void))), ValueType::Model);
2260 assert_eq!(ValueType::from(LangType::Bool), ValueType::Bool);
2261 assert_eq!(
2262 ValueType::from(LangType::Struct(Rc::new(LangStruct::new(
2263 BTreeMap::default(),
2264 i_slint_compiler::langtype::StructName::None
2265 )))),
2266 ValueType::Struct
2267 );
2268 assert_eq!(ValueType::from(LangType::Image), ValueType::Image);
2269}
2270
2271#[test]
2272fn test_multi_components() {
2273 i_slint_backend_testing::init_no_event_loop();
2274 let result = spin_on::spin_on(
2275 Compiler::default().build_from_source(
2276 r#"
2277 export struct Settings {
2278 string_value: string,
2279 }
2280 export global ExpGlo { in-out property <int> test: 42; }
2281 component Common {
2282 in-out property <Settings> settings: { string_value: "Hello", };
2283 }
2284 export component Xyz inherits Window {
2285 in-out property <int> aaa: 8;
2286 }
2287 export component Foo {
2288
2289 in-out property <int> test: 42;
2290 c := Common {}
2291 }
2292 export component Bar inherits Window {
2293 in-out property <int> blah: 78;
2294 c := Common {}
2295 }
2296 "#
2297 .into(),
2298 PathBuf::from("hello.slint"),
2299 ),
2300 );
2301
2302 assert!(!result.has_errors(), "Error {:?}", result.diagnostics().collect::<Vec<_>>());
2303 let mut components = result.component_names().collect::<Vec<_>>();
2304 components.sort();
2305 assert_eq!(components, vec!["Bar", "Xyz"]);
2306 let diag = result.diagnostics().collect::<Vec<_>>();
2307 assert_eq!(diag.len(), 1);
2308 assert_eq!(diag[0].level(), DiagnosticLevel::Warning);
2309 assert_eq!(
2310 diag[0].message(),
2311 "Exported component 'Foo' doesn't inherit Window. No code will be generated for it"
2312 );
2313
2314 let comp1 = result.component("Xyz").unwrap();
2315 assert_eq!(comp1.name(), "Xyz");
2316 let instance1a = comp1.create().unwrap();
2317 let comp2 = result.component("Bar").unwrap();
2318 let instance2 = comp2.create().unwrap();
2319 let instance1b = comp1.create().unwrap();
2320
2321 assert_eq!(instance1a.get_global_property("ExpGlo", "test"), Ok(Value::Number(42.0)));
2323 assert_eq!(instance1a.set_global_property("ExpGlo", "test", Value::Number(88.0)), Ok(()));
2324 assert_eq!(instance2.get_global_property("ExpGlo", "test"), Ok(Value::Number(42.0)));
2325 assert_eq!(instance1b.get_global_property("ExpGlo", "test"), Ok(Value::Number(42.0)));
2326 assert_eq!(instance1a.get_global_property("ExpGlo", "test"), Ok(Value::Number(88.0)));
2327
2328 assert!(result.component("Settings").is_none());
2329 assert!(result.component("Foo").is_none());
2330 assert!(result.component("Common").is_none());
2331 assert!(result.component("ExpGlo").is_none());
2332 assert!(result.component("xyz").is_none());
2333}
2334
2335#[cfg(all(test, feature = "internal-highlight"))]
2336fn compile(code: &str) -> (ComponentInstance, PathBuf) {
2337 i_slint_backend_testing::init_no_event_loop();
2338 let mut compiler = Compiler::default();
2339 compiler.set_style("fluent".into());
2340 let path = PathBuf::from("/tmp/test.slint");
2341
2342 let compile_result =
2343 spin_on::spin_on(compiler.build_from_source(code.to_string(), path.clone()));
2344
2345 for d in &compile_result.diagnostics {
2346 eprintln!("{d}");
2347 }
2348
2349 assert!(!compile_result.has_errors());
2350
2351 let definition = compile_result.components().next().unwrap();
2352 let instance = definition.create().unwrap();
2353
2354 (instance, path)
2355}
2356
2357#[cfg(feature = "internal-highlight")]
2358#[test]
2359fn test_element_node_at_source_code_position() {
2360 let code = r#"
2361component Bar1 {}
2362
2363component Foo1 {
2364}
2365
2366export component Foo2 inherits Window {
2367 Bar1 {}
2368 Foo1 {}
2369}"#;
2370
2371 let (handle, path) = compile(code);
2372
2373 for i in 0..code.len() as u32 {
2374 let elements = handle.element_node_at_source_code_position(&path, i);
2375 eprintln!("{i}: {}", code.as_bytes()[i as usize] as char);
2376 match i {
2377 16 => assert_eq!(elements.len(), 1), 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()),
2383 }
2384 }
2385}