@@ -18,6 +18,7 @@ use pyo3::gc::PyVisit;
1818use pyo3:: prelude:: * ;
1919use pyo3:: types:: PyTuple ;
2020use pyo3:: PyTraverseError ;
21+ use smol_str:: SmolStr ;
2122
2223use crate :: errors:: {
2324 PyGetPropertyError , PyInvokeError , PyPlatformError , PySetCallbackError , PySetPropertyError ,
@@ -255,6 +256,39 @@ impl ComponentDefinition {
255256 self . definition . globals ( ) . collect ( )
256257 }
257258
259+ fn property_infos ( & self ) -> Vec < PyPropertyInfo > {
260+ self . definition
261+ . properties_and_callbacks ( )
262+ . filter_map ( |( name, ( ty, _) ) | {
263+ if ty. is_property_type ( ) {
264+ Some ( PyPropertyInfo :: new ( name, & ty) )
265+ } else {
266+ None
267+ }
268+ } )
269+ . collect ( )
270+ }
271+
272+ fn callback_infos ( & self ) -> Vec < PyCallbackInfo > {
273+ self . definition
274+ . properties_and_callbacks ( )
275+ . filter_map ( |( name, ( ty, _) ) | match ty {
276+ Type :: Callback ( function) => Some ( PyCallbackInfo :: new ( name, & function) ) ,
277+ _ => None ,
278+ } )
279+ . collect ( )
280+ }
281+
282+ fn function_infos ( & self ) -> Vec < PyFunctionInfo > {
283+ self . definition
284+ . properties_and_callbacks ( )
285+ . filter_map ( |( name, ( ty, _) ) | match ty {
286+ Type :: Function ( function) => Some ( PyFunctionInfo :: new ( name, & function) ) ,
287+ _ => None ,
288+ } )
289+ . collect ( )
290+ }
291+
258292 fn global_properties ( & self , name : & str ) -> Option < IndexMap < String , PyValueType > > {
259293 self . definition . global_properties_and_callbacks ( name) . map ( |propiter| {
260294 propiter
@@ -271,6 +305,39 @@ impl ComponentDefinition {
271305 self . definition . global_functions ( name) . map ( |functioniter| functioniter. collect ( ) )
272306 }
273307
308+ fn global_property_infos ( & self , global_name : & str ) -> Option < Vec < PyPropertyInfo > > {
309+ self . definition . global_properties_and_callbacks ( global_name) . map ( |iter| {
310+ iter. filter_map ( |( name, ( ty, _) ) | {
311+ if ty. is_property_type ( ) {
312+ Some ( PyPropertyInfo :: new ( name, & ty) )
313+ } else {
314+ None
315+ }
316+ } )
317+ . collect ( )
318+ } )
319+ }
320+
321+ fn global_callback_infos ( & self , global_name : & str ) -> Option < Vec < PyCallbackInfo > > {
322+ self . definition . global_properties_and_callbacks ( global_name) . map ( |iter| {
323+ iter. filter_map ( |( name, ( ty, _) ) | match ty {
324+ Type :: Callback ( function) => Some ( PyCallbackInfo :: new ( name, & function) ) ,
325+ _ => None ,
326+ } )
327+ . collect ( )
328+ } )
329+ }
330+
331+ fn global_function_infos ( & self , global_name : & str ) -> Option < Vec < PyFunctionInfo > > {
332+ self . definition . global_properties_and_callbacks ( global_name) . map ( |iter| {
333+ iter. filter_map ( |( name, ( ty, _) ) | match ty {
334+ Type :: Function ( function) => Some ( PyFunctionInfo :: new ( name, & function) ) ,
335+ _ => None ,
336+ } )
337+ . collect ( )
338+ } )
339+ }
340+
274341 fn callback_returns_void ( & self , callback_name : & str ) -> bool {
275342 let callback_name = normalize_identifier ( callback_name) ;
276343 self . definition
@@ -357,6 +424,183 @@ impl From<i_slint_compiler::langtype::Type> for PyValueType {
357424 }
358425}
359426
427+ fn python_identifier ( name : & str ) -> String {
428+ if name. is_empty ( ) {
429+ return String :: new ( ) ;
430+ }
431+ let mut result = name. replace ( '-' , "_" ) ;
432+ if result. chars ( ) . next ( ) . is_some_and ( |c| c. is_ascii_digit ( ) ) {
433+ result. insert ( 0 , '_' ) ;
434+ }
435+ result
436+ }
437+
438+ fn type_to_python_hint ( ty : & i_slint_compiler:: langtype:: Type ) -> String {
439+ use i_slint_compiler:: langtype:: Type :: * ;
440+
441+ match ty {
442+ Void => "None" . into ( ) ,
443+ Bool => "bool" . into ( ) ,
444+ Int32 => "int" . into ( ) ,
445+ Float32 | Duration | PhysicalLength | LogicalLength | Rem | Angle | Percent
446+ | UnitProduct ( _) => "float" . into ( ) ,
447+ String => "str" . into ( ) ,
448+ Brush | Color => "slint.Brush" . into ( ) ,
449+ Image => "slint.Image" . into ( ) ,
450+ Model => "slint.Model" . into ( ) ,
451+ Array ( inner) => format ! ( "slint.ListModel[{}]" , type_to_python_hint( inner) ) ,
452+ Struct ( struct_ty) => struct_to_python_hint ( struct_ty) ,
453+ // Enumeration(enum_ty) => {
454+ // let name = enum_ty.name.as_str();
455+ // let tail = name.rsplit("::").next().unwrap_or(name);
456+ // format!("slint.{}", python_identifier(tail))
457+ // }
458+ Callback ( function) | Function ( function) => function_to_python_hint ( function) ,
459+ // ComponentFactory => "slint.ComponentFactory".into(), // TODO
460+ // PathData | Easing | ElementReference | LayoutCache | InferredProperty
461+ // | InferredCallback | Invalid => "Any".into(),
462+ _ => "Any" . into ( ) ,
463+ }
464+ }
465+
466+ fn struct_to_python_hint ( struct_ty : & Rc < i_slint_compiler:: langtype:: Struct > ) -> String {
467+ if let Some ( inner_ty) = optional_struct_inner ( struct_ty) {
468+ return format ! ( "Optional[{}]" , type_to_python_hint( inner_ty) ) ;
469+ }
470+
471+ if let Some ( name) = & struct_ty. name {
472+ let full = name. as_str ( ) ;
473+ let tail = full. rsplit ( "::" ) . next ( ) . unwrap_or ( full) ;
474+ if full. starts_with ( "slint::" ) {
475+ return format ! ( "slint.{}" , python_identifier( tail) ) ;
476+ }
477+ return python_identifier ( tail) ;
478+ }
479+
480+ "dict[str, Any]" . into ( )
481+ }
482+
483+ fn optional_struct_inner (
484+ struct_ty : & Rc < i_slint_compiler:: langtype:: Struct > ,
485+ ) -> Option < & i_slint_compiler:: langtype:: Type > {
486+ let name = struct_ty. name . as_ref ( ) ?;
487+ let tail = name. as_str ( ) . rsplit ( "::" ) . next ( ) . unwrap_or ( name. as_str ( ) ) ;
488+ let tail_lower = tail. to_ascii_lowercase ( ) ;
489+ if !tail_lower. starts_with ( "optional" ) {
490+ return None ;
491+ }
492+
493+ if let Some ( value_ty) =
494+ struct_ty. fields . get ( "value" ) . or_else ( || struct_ty. fields . get ( "maybe_value" ) )
495+ {
496+ return Some ( value_ty) ;
497+ }
498+
499+ struct_ty. fields . values ( ) . next ( )
500+ }
501+
502+ fn function_to_python_hint ( function : & Rc < i_slint_compiler:: langtype:: Function > ) -> String {
503+ let args: Vec < String > = function. args . iter ( ) . map ( type_to_python_hint) . collect ( ) ;
504+ let return_type = type_to_python_hint ( & function. return_type ) ;
505+
506+ if args. is_empty ( ) {
507+ if function. return_type == i_slint_compiler:: langtype:: Type :: Void {
508+ "Callable[..., Any]" . into ( )
509+ } else {
510+ format ! ( "Callable[[], {}]" , return_type)
511+ }
512+ } else {
513+ format ! ( "Callable[[{}], {}]" , args. join( ", " ) , return_type)
514+ }
515+ }
516+
517+ #[ gen_stub_pyclass]
518+ #[ pyclass( module = "slint" ) ]
519+ #[ derive( Clone ) ]
520+ pub struct PyPropertyInfo {
521+ #[ pyo3( get) ]
522+ pub name : String ,
523+ #[ pyo3( get) ]
524+ pub python_type : String ,
525+ }
526+
527+ impl PyPropertyInfo {
528+ fn new ( name : String , ty : & i_slint_compiler:: langtype:: Type ) -> Self {
529+ Self { name, python_type : type_to_python_hint ( ty) }
530+ }
531+ }
532+
533+ #[ gen_stub_pyclass]
534+ #[ pyclass( module = "slint" ) ]
535+ #[ derive( Clone ) ]
536+ pub struct PyCallbackParameter {
537+ #[ pyo3( get) ]
538+ pub name : Option < String > ,
539+ #[ pyo3( get) ]
540+ pub python_type : String ,
541+ }
542+
543+ impl PyCallbackParameter {
544+ fn new ( name : Option < SmolStr > , ty : & i_slint_compiler:: langtype:: Type ) -> Self {
545+ let name = name. and_then ( |n| if n. is_empty ( ) { None } else { Some ( n. into ( ) ) } ) ;
546+ Self { name, python_type : type_to_python_hint ( ty) }
547+ }
548+ }
549+
550+ #[ gen_stub_pyclass]
551+ #[ pyclass( module = "slint" ) ]
552+ #[ derive( Clone ) ]
553+ pub struct PyCallbackInfo {
554+ #[ pyo3( get) ]
555+ pub name : String ,
556+ #[ pyo3( get) ]
557+ pub parameters : Vec < PyCallbackParameter > ,
558+ #[ pyo3( get) ]
559+ pub return_type : String ,
560+ }
561+
562+ impl PyCallbackInfo {
563+ fn new ( name : String , function : & Rc < i_slint_compiler:: langtype:: Function > ) -> Self {
564+ let mut parameters = Vec :: with_capacity ( function. args . len ( ) ) ;
565+ for ( idx, arg_ty) in function. args . iter ( ) . enumerate ( ) {
566+ let arg_name = function. arg_names . get ( idx) . cloned ( ) ;
567+ parameters. push ( PyCallbackParameter :: new ( arg_name, arg_ty) ) ;
568+ }
569+ Self {
570+ name,
571+ parameters,
572+ return_type : type_to_python_hint ( & function. return_type ) ,
573+ }
574+ }
575+ }
576+
577+ #[ gen_stub_pyclass]
578+ #[ pyclass( module = "slint" ) ]
579+ #[ derive( Clone ) ]
580+ pub struct PyFunctionInfo {
581+ #[ pyo3( get) ]
582+ pub name : String ,
583+ #[ pyo3( get) ]
584+ pub parameters : Vec < PyCallbackParameter > ,
585+ #[ pyo3( get) ]
586+ pub return_type : String ,
587+ }
588+
589+ impl PyFunctionInfo {
590+ fn new ( name : String , function : & Rc < i_slint_compiler:: langtype:: Function > ) -> Self {
591+ let mut parameters = Vec :: with_capacity ( function. args . len ( ) ) ;
592+ for ( idx, arg_ty) in function. args . iter ( ) . enumerate ( ) {
593+ let arg_name = function. arg_names . get ( idx) . cloned ( ) ;
594+ parameters. push ( PyCallbackParameter :: new ( arg_name, arg_ty) ) ;
595+ }
596+ Self {
597+ name,
598+ parameters,
599+ return_type : type_to_python_hint ( & function. return_type ) ,
600+ }
601+ }
602+ }
603+
360604#[ gen_stub_pyclass]
361605#[ pyclass( unsendable, weakref) ]
362606pub struct ComponentInstance {
0 commit comments