gdnative_core/export/property/
hint.rs1use std::fmt::{self, Write};
4use std::ops::RangeInclusive;
5
6use crate::core_types::GodotString;
7use crate::core_types::VariantType;
8use crate::sys;
9
10use super::{Export, ExportInfo};
11
12#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
24pub struct RangeHint<T> {
25 pub min: T,
27 pub max: T,
29 pub step: Option<T>,
31 pub or_greater: bool,
33 pub or_lesser: bool,
35}
36
37impl<T> RangeHint<T>
38where
39 T: fmt::Display,
40{
41 #[inline]
43 pub fn new(min: T, max: T) -> Self {
44 RangeHint {
45 min,
46 max,
47 step: None,
48 or_greater: false,
49 or_lesser: false,
50 }
51 }
52
53 #[inline]
55 pub fn with_step(mut self, step: T) -> Self {
56 self.step.replace(step);
57 self
58 }
59
60 #[inline]
62 pub fn or_greater(mut self) -> Self {
63 self.or_greater = true;
64 self
65 }
66
67 #[inline]
69 pub fn or_lesser(mut self) -> Self {
70 self.or_lesser = true;
71 self
72 }
73
74 fn to_godot_hint_string(&self) -> GodotString {
76 let mut s = String::new();
77
78 write!(s, "{},{}", self.min, self.max).unwrap();
79 if let Some(step) = &self.step {
80 write!(s, ",{step}").unwrap();
81 }
82
83 if self.or_greater {
84 s.push_str(",or_greater");
85 }
86 if self.or_lesser {
87 s.push_str(",or_lesser");
88 }
89
90 s.into()
91 }
92}
93
94impl<T> From<RangeInclusive<T>> for RangeHint<T>
95where
96 T: fmt::Display,
97{
98 #[inline]
99 fn from(range: RangeInclusive<T>) -> Self {
100 let (min, max) = range.into_inner();
101 RangeHint::new(min, max)
102 }
103}
104
105#[derive(Clone, Eq, PartialEq, Debug, Default)]
118pub struct EnumHint {
119 values: Vec<String>,
120}
121
122impl EnumHint {
123 #[inline]
124 pub fn new(values: Vec<String>) -> Self {
125 EnumHint { values }
126 }
127
128 fn to_godot_hint_string(&self) -> GodotString {
130 let mut s = String::new();
131
132 let mut iter = self.values.iter();
133
134 if let Some(first) = iter.next() {
135 write!(s, "{first}").unwrap();
136 }
137
138 for rest in iter {
139 write!(s, ",{rest}").unwrap();
140 }
141
142 s.into()
143 }
144}
145
146#[derive(Clone, Debug)]
148pub enum IntHint<T> {
149 Range(RangeHint<T>),
151 ExpRange(RangeHint<T>),
153 Enum(EnumHint),
155 Flags(EnumHint),
157 Layers2DRender,
159 Layers2DPhysics,
161 Layers3DRender,
163 Layers3DPhysics,
165}
166
167impl<T> IntHint<T>
168where
169 T: fmt::Display,
170{
171 #[inline]
172 pub fn export_info(self) -> ExportInfo {
173 use IntHint as IH;
174
175 let hint_kind = match &self {
176 IH::Range(_) => sys::godot_property_hint_GODOT_PROPERTY_HINT_RANGE,
177 IH::ExpRange(_) => sys::godot_property_hint_GODOT_PROPERTY_HINT_EXP_RANGE,
178 IH::Enum(_) => sys::godot_property_hint_GODOT_PROPERTY_HINT_ENUM,
179 IH::Flags(_) => sys::godot_property_hint_GODOT_PROPERTY_HINT_FLAGS,
180 IH::Layers2DRender => sys::godot_property_hint_GODOT_PROPERTY_HINT_LAYERS_2D_RENDER,
181 IH::Layers2DPhysics => sys::godot_property_hint_GODOT_PROPERTY_HINT_LAYERS_2D_PHYSICS,
182 IH::Layers3DRender => sys::godot_property_hint_GODOT_PROPERTY_HINT_LAYERS_3D_RENDER,
183 IH::Layers3DPhysics => sys::godot_property_hint_GODOT_PROPERTY_HINT_LAYERS_3D_PHYSICS,
184 };
185
186 let hint_string = match self {
187 IH::Range(range) | IH::ExpRange(range) => range.to_godot_hint_string(),
188 IH::Enum(e) | IH::Flags(e) => e.to_godot_hint_string(),
189 _ => GodotString::new(),
190 };
191
192 ExportInfo {
193 variant_type: VariantType::I64,
194 hint_kind,
195 hint_string,
196 }
197 }
198}
199
200impl<T> From<RangeHint<T>> for IntHint<T>
201where
202 T: fmt::Display,
203{
204 #[inline]
205 fn from(hint: RangeHint<T>) -> Self {
206 Self::Range(hint)
207 }
208}
209
210impl<T> From<RangeInclusive<T>> for IntHint<T>
211where
212 T: fmt::Display,
213{
214 #[inline]
215 fn from(range: RangeInclusive<T>) -> Self {
216 Self::Range(range.into())
217 }
218}
219
220impl<T> From<EnumHint> for IntHint<T> {
221 #[inline]
222 fn from(hint: EnumHint) -> Self {
223 Self::Enum(hint)
224 }
225}
226
227#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
229pub struct ExpEasingHint {
230 pub is_attenuation: bool,
232 pub is_in_out: bool,
234}
235
236impl ExpEasingHint {
237 #[inline]
238 pub fn new() -> Self {
239 Self::default()
240 }
241
242 fn to_godot_hint_string(self) -> GodotString {
244 let mut s = String::new();
245
246 if self.is_attenuation {
247 s.push_str("attenuation");
248 }
249
250 if self.is_in_out {
251 if self.is_attenuation {
252 s.push(',');
253 }
254 s.push_str("inout");
255 }
256
257 s.into()
258 }
259}
260
261#[derive(Clone, Debug)]
263pub enum FloatHint<T> {
264 Range(RangeHint<T>),
266 ExpRange(RangeHint<T>),
268 ExpEasing(ExpEasingHint),
270}
271
272impl<T> FloatHint<T>
273where
274 T: fmt::Display,
275{
276 #[inline]
277 pub fn export_info(self) -> ExportInfo {
278 use FloatHint as FH;
279
280 let hint_kind = match &self {
281 FH::Range(_) => sys::godot_property_hint_GODOT_PROPERTY_HINT_RANGE,
282 FH::ExpRange(_) => sys::godot_property_hint_GODOT_PROPERTY_HINT_EXP_RANGE,
283 FH::ExpEasing(_) => sys::godot_property_hint_GODOT_PROPERTY_HINT_EXP_EASING,
284 };
285
286 let hint_string = match self {
287 FH::Range(range) | FH::ExpRange(range) => range.to_godot_hint_string(),
288 FH::ExpEasing(e) => e.to_godot_hint_string(),
289 };
290
291 ExportInfo {
292 variant_type: VariantType::F64,
293 hint_kind,
294 hint_string,
295 }
296 }
297}
298
299impl<T> From<RangeHint<T>> for FloatHint<T>
300where
301 T: fmt::Display,
302{
303 #[inline]
304 fn from(hint: RangeHint<T>) -> Self {
305 Self::Range(hint)
306 }
307}
308
309impl<T> From<RangeInclusive<T>> for FloatHint<T>
310where
311 T: fmt::Display,
312{
313 #[inline]
314 fn from(range: RangeInclusive<T>) -> Self {
315 Self::Range(range.into())
316 }
317}
318
319impl<T> From<ExpEasingHint> for FloatHint<T> {
320 #[inline]
321 fn from(hint: ExpEasingHint) -> Self {
322 Self::ExpEasing(hint)
323 }
324}
325
326#[derive(Clone, Debug)]
328pub enum StringHint {
329 Enum(EnumHint),
331 File(EnumHint),
333 GlobalFile(EnumHint),
335 Dir,
337 GlobalDir,
339 Multiline,
341 Placeholder { placeholder: String },
343}
344
345impl StringHint {
346 #[inline]
347 pub fn export_info(self) -> ExportInfo {
348 use StringHint as SH;
349
350 let hint_kind = match &self {
351 SH::Enum(_) => sys::godot_property_hint_GODOT_PROPERTY_HINT_ENUM,
352 SH::File(_) => sys::godot_property_hint_GODOT_PROPERTY_HINT_FILE,
353 SH::GlobalFile(_) => sys::godot_property_hint_GODOT_PROPERTY_HINT_GLOBAL_FILE,
354 SH::Dir => sys::godot_property_hint_GODOT_PROPERTY_HINT_DIR,
355 SH::GlobalDir => sys::godot_property_hint_GODOT_PROPERTY_HINT_GLOBAL_DIR,
356 SH::Multiline => sys::godot_property_hint_GODOT_PROPERTY_HINT_MULTILINE_TEXT,
357 SH::Placeholder { .. } => sys::godot_property_hint_GODOT_PROPERTY_HINT_PLACEHOLDER_TEXT,
358 };
359
360 let hint_string = match self {
361 SH::Enum(e) | SH::File(e) | SH::GlobalFile(e) => e.to_godot_hint_string(),
362 SH::Placeholder { placeholder } => placeholder.into(),
363 _ => GodotString::new(),
364 };
365
366 ExportInfo {
367 variant_type: VariantType::GodotString,
368 hint_kind,
369 hint_string,
370 }
371 }
372}
373
374#[derive(Clone, Debug)]
376pub enum ColorHint {
377 NoAlpha,
379}
380
381impl ColorHint {
382 #[inline]
383 pub fn export_info(self) -> ExportInfo {
384 ExportInfo {
385 variant_type: VariantType::Color,
386 hint_kind: match self {
387 ColorHint::NoAlpha => sys::godot_property_hint_GODOT_PROPERTY_HINT_COLOR_NO_ALPHA,
388 },
389 hint_string: GodotString::new(),
390 }
391 }
392}
393
394#[derive(Debug, Default)]
396pub struct ArrayHint {
397 element_hint: Option<ExportInfo>,
398}
399
400impl ArrayHint {
401 #[inline]
403 pub fn new() -> Self {
404 Self::default()
405 }
406
407 #[inline]
410 pub fn with_element<T: Export>() -> Self {
411 Self::with_maybe_element_hint::<T>(None)
412 }
413
414 #[inline]
416 pub fn with_element_hint<T: Export>(hint: T::Hint) -> Self {
417 Self::with_maybe_element_hint::<T>(Some(hint))
418 }
419
420 #[inline]
423 pub fn with_maybe_element_hint<T: Export>(hint: Option<T::Hint>) -> Self {
424 ArrayHint {
425 element_hint: Some(T::export_info(hint)),
426 }
427 }
428}
429
430impl ArrayHint {
431 #[inline]
432 pub fn export_info(self) -> ExportInfo {
433 if let Some(element_hint) = self.element_hint {
434 let hint_string = match (element_hint.variant_type, element_hint.hint_kind) {
435 (
438 VariantType::VariantArray,
439 sys::godot_property_hint_GODOT_PROPERTY_HINT_TYPE_STRING,
440 ) => format!(
441 "{}:{}",
442 VariantType::VariantArray as u32,
443 element_hint.hint_string
444 ),
445 (variant_type, sys::godot_property_hint_GODOT_PROPERTY_HINT_NONE) => {
446 format!("{}:{}", variant_type as u32, element_hint.hint_string)
447 }
448 (variant_type, hint_type) => format!(
449 "{}/{}:{}",
450 variant_type as u32, hint_type, element_hint.hint_string
451 ),
452 }
453 .into();
454 ExportInfo {
455 variant_type: VariantType::VariantArray,
456 hint_kind: sys::godot_property_hint_GODOT_PROPERTY_HINT_TYPE_STRING,
457 hint_string,
458 }
459 } else {
460 ExportInfo {
461 variant_type: VariantType::VariantArray,
462 hint_kind: sys::godot_property_hint_GODOT_PROPERTY_HINT_NONE,
463 hint_string: GodotString::new(),
464 }
465 }
466 }
467}