@@ -17,6 +17,9 @@ use crate::global::PropertyHint;
17
17
use crate :: meta:: { ClassId , FromGodot , GodotConvert , GodotType , PropertyHintInfo , ToGodot } ;
18
18
use crate :: obj:: { EngineEnum , GodotClass } ;
19
19
20
+ // ----------------------------------------------------------------------------------------------------------------------------------------------
21
+ // Child modules
22
+
20
23
mod phantom_var;
21
24
22
25
pub use phantom_var:: PhantomVar ;
@@ -229,18 +232,22 @@ where
229
232
/// Each function is named the same as the equivalent Godot annotation.
230
233
/// For instance, `@export_range` in Godot is `fn export_range` here.
231
234
pub mod export_info_functions {
235
+ use std:: fmt;
236
+ use std:: fmt:: Write ;
237
+
232
238
use godot_ffi:: VariantType ;
233
239
234
240
use crate :: builtin:: GString ;
235
241
use crate :: global:: PropertyHint ;
236
242
use crate :: meta:: { GodotType , PropertyHintInfo , PropertyInfo } ;
237
243
use crate :: obj:: EngineEnum ;
238
244
use crate :: registry:: property:: Export ;
245
+ use crate :: { godot_str, sys} ;
239
246
240
247
/// Turn a list of variables into a comma separated string containing only the identifiers corresponding
241
248
/// to a true boolean variable.
242
249
macro_rules! comma_separate_boolean_idents {
243
- ( $( $ident: ident) ,* $( , ) ?) => {
250
+ ( $( $ident: ident ) ,* $( , ) ?) => {
244
251
{
245
252
let mut strings = Vec :: new( ) ;
246
253
@@ -261,24 +268,26 @@ pub mod export_info_functions {
261
268
/// You'll never call this function itself, but will instead use the macro `#[export(range=(...))]`, as below. The syntax is
262
269
/// very similar to Godot's [`@export_range`](https://docs.godotengine.org/en/stable/classes/class_%40gdscript.html#class-gdscript-annotation-export-range).
263
270
/// `min`, `max`, and `step` are `f32` positional arguments, with `step` being optional and defaulting to `1.0`. The rest of
264
- /// the arguments can be written in any order. The symbols of type `bool` just need to have those symbols written, and those of type `Option<T>` will be written as `{KEY}={VALUE}`, e.g. `suffix="px"`.
271
+ /// the arguments can be written in any order. The symbols of type `bool` just need to have those symbols written, and those of type
272
+ /// `Option<T>` will be written as `{KEY}={VALUE}`, e.g. `suffix="px"`.
265
273
///
266
274
/// ```
267
275
/// # use godot::prelude::*;
268
276
/// #[derive(GodotClass)]
269
277
/// #[class(init, base=Node)]
270
278
/// struct MyClassWithRangedValues {
271
- /// #[export(range=(0.0 , 400.0 , 1.0 , or_greater, suffix="px"))]
279
+ /// #[export(range=(0, 400, 1, or_greater, suffix="px"))]
272
280
/// icon_width: i32,
281
+ ///
273
282
/// #[export(range=(-180.0, 180.0, degrees))]
274
283
/// angle: f32,
275
284
/// }
276
285
/// ```
277
286
#[ allow( clippy:: too_many_arguments) ]
278
- pub fn export_range (
279
- min : f64 ,
280
- max : f64 ,
281
- step : Option < f64 > ,
287
+ pub fn export_range < T : Export + fmt :: Display > (
288
+ min : T ,
289
+ max : T ,
290
+ step : Option < T > ,
282
291
or_greater : bool ,
283
292
or_less : bool ,
284
293
exp : bool ,
@@ -307,10 +316,10 @@ pub mod export_info_functions {
307
316
308
317
let mut hint_string = hint_beginning;
309
318
if !rest. is_empty ( ) {
310
- hint_string . push_str ( & format ! ( ",{rest}" ) ) ;
319
+ write ! ( hint_string , ",{rest}" ) . unwrap ( ) ;
311
320
}
312
321
if let Some ( suffix) = suffix {
313
- hint_string . push_str ( & format ! ( ",suffix:{suffix}" ) ) ;
322
+ write ! ( hint_string , ",suffix:{suffix}" ) . unwrap ( ) ;
314
323
}
315
324
316
325
PropertyHintInfo {
@@ -319,6 +328,7 @@ pub mod export_info_functions {
319
328
}
320
329
}
321
330
331
+ // See also godot-macros > c_style_enum.rs; some code duplication.
322
332
#[ doc( hidden) ]
323
333
pub struct ExportValueWithKey < T > {
324
334
variant : String ,
@@ -339,23 +349,19 @@ pub mod export_info_functions {
339
349
where
340
350
for < ' a > & ' a V : Into < Self > ,
341
351
{
342
- let values = values
343
- . iter ( )
344
- . map ( |v| v. into ( ) . as_hint_string ( ) )
345
- . collect :: < Vec < _ > > ( ) ;
346
-
347
- values. join ( "," )
352
+ sys:: join_with ( values. iter ( ) , "," , |v| ( * v) . into ( ) . as_hint_string ( ) )
348
353
}
349
354
}
350
355
356
+ /// Allows syntax `("name", None)` and `("name", Some(10))`.
351
357
impl < T , S > From < & ( S , Option < T > ) > for ExportValueWithKey < T >
352
358
where
353
359
T : Clone ,
354
360
S : AsRef < str > ,
355
361
{
356
362
fn from ( ( variant, key) : & ( S , Option < T > ) ) -> Self {
357
363
Self {
358
- variant : variant. as_ref ( ) . into ( ) ,
364
+ variant : variant. as_ref ( ) . to_string ( ) ,
359
365
key : key. clone ( ) ,
360
366
}
361
367
}
@@ -493,14 +499,14 @@ pub mod export_info_functions {
493
499
494
500
PropertyHintInfo {
495
501
hint : PropertyHint :: TYPE_STRING ,
496
- hint_string : GString :: from ( & format ! ( "{hint_string}:{filter}" ) ) ,
502
+ hint_string : godot_str ! ( "{hint_string}:{filter}" ) ,
497
503
}
498
504
}
499
505
500
- pub fn export_placeholder < S : AsRef < str > > ( placeholder : S ) -> PropertyHintInfo {
506
+ pub fn export_placeholder ( placeholder : & str ) -> PropertyHintInfo {
501
507
PropertyHintInfo {
502
508
hint : PropertyHint :: PLACEHOLDER_TEXT ,
503
- hint_string : GString :: from ( placeholder. as_ref ( ) ) ,
509
+ hint_string : GString :: from ( placeholder) ,
504
510
}
505
511
}
506
512
@@ -544,6 +550,12 @@ mod export_impls {
544
550
impl_property_by_godot_convert!( @property $Ty) ;
545
551
} ;
546
552
553
+ ( $Ty: ty, limit_range) => {
554
+ impl_property_by_godot_convert!( @property $Ty) ;
555
+ impl_property_by_godot_convert!( @export_range $Ty) ;
556
+ impl_property_by_godot_convert!( @builtin $Ty) ;
557
+ } ;
558
+
547
559
( $Ty: ty) => {
548
560
impl_property_by_godot_convert!( @property $Ty) ;
549
561
impl_property_by_godot_convert!( @export $Ty) ;
@@ -570,6 +582,14 @@ mod export_impls {
570
582
}
571
583
} ;
572
584
585
+ ( @export_range $Ty: ty) => {
586
+ impl Export for $Ty {
587
+ fn export_hint( ) -> PropertyHintInfo {
588
+ PropertyHintInfo :: type_name_range( <$Ty>:: MIN , <$Ty>:: MAX )
589
+ }
590
+ }
591
+ } ;
592
+
573
593
( @builtin $Ty: ty) => {
574
594
impl BuiltinExport for $Ty { }
575
595
}
@@ -618,16 +638,14 @@ mod export_impls {
618
638
// then the property will just round the value or become inf.
619
639
impl_property_by_godot_convert ! ( f32 ) ;
620
640
621
- // Godot uses i64 internally for integers, and if Godot tries to pass an invalid integer into a property
622
- // accepting one of the below values then rust will panic. In the editor this will appear as the property
623
- // failing to be set to a value and an error printed in the console. During runtime this will crash the
624
- // program and print the panic from rust stating that the property cannot store the value.
625
- impl_property_by_godot_convert ! ( i32 ) ;
626
- impl_property_by_godot_convert ! ( i16 ) ;
627
- impl_property_by_godot_convert ! ( i8 ) ;
628
- impl_property_by_godot_convert ! ( u32 ) ;
629
- impl_property_by_godot_convert ! ( u16 ) ;
630
- impl_property_by_godot_convert ! ( u8 ) ;
641
+ // Godot uses i64 internally for integers. We use the @export_range hints to limit the integers to a sub-range of i64, so the editor UI
642
+ // can only set the respective values. If values are assigned in other ways (e.g. GDScript), a panic may occur, causing a Godot error.
643
+ impl_property_by_godot_convert ! ( i32 , limit_range) ;
644
+ impl_property_by_godot_convert ! ( i16 , limit_range) ;
645
+ impl_property_by_godot_convert ! ( i8 , limit_range) ;
646
+ impl_property_by_godot_convert ! ( u32 , limit_range) ;
647
+ impl_property_by_godot_convert ! ( u16 , limit_range) ;
648
+ impl_property_by_godot_convert ! ( u8 , limit_range) ;
631
649
632
650
// Callables and Signals are useless when exported to the editor, so we only need to make them available as
633
651
// properties.
0 commit comments