|
5 | 5 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
6 | 6 | */
|
7 | 7 |
|
8 |
| -use crate::builtin::{GString, Variant}; |
| 8 | +use crate::builtin::Variant; |
9 | 9 | use crate::meta::error::ConvertError;
|
10 | 10 | use crate::meta::{ClassName, FromGodot, GodotConvert, PropertyHintInfo, ToGodot};
|
11 | 11 | use crate::obj::guards::DynGdRef;
|
12 |
| -use crate::obj::{bounds, AsDyn, Bounds, DynGdMut, Gd, GodotClass, Inherits}; |
| 12 | +use crate::obj::{bounds, AsDyn, Bounds, DynGdMut, Gd, GodotClass, Inherits, OnEditor}; |
13 | 13 | use crate::registry::class::{get_dyn_property_hint_string, try_dynify_object};
|
14 | 14 | use crate::registry::property::{object_export_element_type_string, Export, Var};
|
15 | 15 | use crate::{meta, sys};
|
@@ -136,6 +136,25 @@ use std::{fmt, ops};
|
136 | 136 | /// godot-rust achieves this thanks to the registration done by `#[godot_dyn]`: the library knows for which classes `Health` is implemented,
|
137 | 137 | /// and it can query the dynamic type of the object. Based on that type, it can find the `impl Health` implementation matching the correct class.
|
138 | 138 | /// Behind the scenes, everything is wired up correctly so that you can restore the original `DynGd` even after it has passed through Godot.
|
| 139 | +/// |
| 140 | +/// # `#[export]` for `DynGd<T, D>` |
| 141 | +/// |
| 142 | +/// Exporting `DynGd<T, D>` is possible only via [`OnEditor`] or [`Option`]. |
| 143 | +/// `DynGd<T, D>` can also be exported directly as an element of an array such as `Array<DynGd<T, D>>`. |
| 144 | +/// |
| 145 | +/// Since `DynGd<T, D>` represents shared functionality `D` across classes inheriting from `T`, |
| 146 | +/// consider using `#[export] Gd<T>` instead of `#[export] DynGd<T, D>` |
| 147 | +/// in cases when `T` is a concrete Rust `GodotClass`. |
| 148 | +/// |
| 149 | +/// ## Node based classes |
| 150 | +/// |
| 151 | +/// `#[export]` for a `DynGd<T, D>` works identically to `#[export]` `Gd<T>` for `T` inheriting Node classes. |
| 152 | +/// Godot will report an error if the conversion fails, but it will only do so when accessing the given value. |
| 153 | +/// |
| 154 | +/// ## Resource based classes |
| 155 | +/// |
| 156 | +/// `#[export]` for a `DynGd<T, D>` allows you to limit the available choices to implementors of a given trait `D` whose base inherits the specified `T` |
| 157 | +/// (for example, `#[export] Option<DynGd<Resource, dyn MyTrait>>` won't include Rust classes with an Object base, even if they implement `MyTrait`). |
139 | 158 | pub struct DynGd<T, D>
|
140 | 159 | where
|
141 | 160 | // T does _not_ require AsDyn<D> here. Otherwise, it's impossible to upcast (without implementing the relation for all base classes).
|
@@ -499,18 +518,72 @@ where
|
499 | 518 | }
|
500 | 519 | }
|
501 | 520 |
|
502 |
| -impl<T, D> Export for DynGd<T, D> |
| 521 | +/// `#[export]` for `Option<DynGd<T, D>>` is available only for `T` being Engine class (such as Node or Resource). |
| 522 | +/// |
| 523 | +/// Consider exporting `Option<Gd<T>>` instead of `Option<DynGd<T, D>>` for user-declared GDExtension classes. |
| 524 | +impl<T, D> Export for Option<DynGd<T, D>> |
503 | 525 | where
|
504 | 526 | T: GodotClass + Bounds<Exportable = bounds::Yes>,
|
505 | 527 | D: ?Sized + 'static,
|
506 | 528 | {
|
507 | 529 | fn export_hint() -> PropertyHintInfo {
|
508 |
| - PropertyHintInfo { |
509 |
| - hint_string: GString::from(get_dyn_property_hint_string::<T, D>()), |
510 |
| - ..<Gd<T> as Export>::export_hint() |
511 |
| - } |
| 530 | + PropertyHintInfo::export_dyn_gd::<T, D>() |
| 531 | + } |
| 532 | + |
| 533 | + #[doc(hidden)] |
| 534 | + fn as_node_class() -> Option<ClassName> { |
| 535 | + PropertyHintInfo::object_as_node_class::<T>() |
512 | 536 | }
|
| 537 | +} |
| 538 | + |
| 539 | +impl<T, D> Default for OnEditor<DynGd<T, D>> |
| 540 | +where |
| 541 | + T: GodotClass, |
| 542 | + D: ?Sized + 'static, |
| 543 | +{ |
| 544 | + fn default() -> Self { |
| 545 | + OnEditor::gd_invalid() |
| 546 | + } |
| 547 | +} |
| 548 | + |
| 549 | +impl<T, D> GodotConvert for OnEditor<DynGd<T, D>> |
| 550 | +where |
| 551 | + T: GodotClass, |
| 552 | + D: ?Sized, |
| 553 | +{ |
| 554 | + type Via = Option<<DynGd<T, D> as GodotConvert>::Via>; |
| 555 | +} |
| 556 | + |
| 557 | +impl<T, D> Var for OnEditor<DynGd<T, D>> |
| 558 | +where |
| 559 | + T: GodotClass, |
| 560 | + D: ?Sized + 'static, |
| 561 | +{ |
| 562 | + fn get_property(&self) -> Self::Via { |
| 563 | + Self::get_property_inner(self) |
| 564 | + } |
| 565 | + |
| 566 | + fn set_property(&mut self, value: Self::Via) { |
| 567 | + // `set_property` can't be delegated to Gd<T>, since we have to set `erased_obj` as well. |
| 568 | + Self::set_property_inner(self, value) |
| 569 | + } |
| 570 | +} |
| 571 | + |
| 572 | +/// `#[export]` for `OnEditor<DynGd<T, D>>` is available only for `T` being Engine class (such as Node or Resource). |
| 573 | +/// |
| 574 | +/// Consider exporting `OnEditor<Gd<T>>` instead of `OnEditor<DynGd<T, D>>` for user-declared GDExtension classes. |
| 575 | +impl<T, D> Export for OnEditor<DynGd<T, D>> |
| 576 | +where |
| 577 | + Self: Var, |
| 578 | + T: GodotClass + Bounds<Exportable = bounds::Yes>, |
| 579 | + D: ?Sized + 'static, |
| 580 | +{ |
| 581 | + fn export_hint() -> PropertyHintInfo { |
| 582 | + PropertyHintInfo::export_dyn_gd::<T, D>() |
| 583 | + } |
| 584 | + |
| 585 | + #[doc(hidden)] |
513 | 586 | fn as_node_class() -> Option<ClassName> {
|
514 |
| - <Gd<T> as Export>::as_node_class() |
| 587 | + PropertyHintInfo::object_as_node_class::<T>() |
515 | 588 | }
|
516 | 589 | }
|
0 commit comments