Skip to content

Commit 9cfe837

Browse files
committed
feat: Property support properties in derived classes
Previously, we weren't scanning parent classes for properties. Now we are. Additionally, add a super secret hidden property that adds the name of the class instead of the script file as the category header.
1 parent d9acf35 commit 9cfe837

4 files changed

Lines changed: 87 additions & 28 deletions

File tree

src/cpp/script/dart_script.cpp

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -216,19 +216,23 @@ godot::TypedArray<godot::Dictionary> DartScript::_get_script_property_list() con
216216

217217
godot::TypedArray<godot::Dictionary> ret_val;
218218

219-
// TODO: Look if there's a better way to store these strings as constants
220-
for (const auto &prop : _properties_cache) {
221-
const auto &prop_info = prop.second;
222-
godot::Dictionary info_dict;
223-
info_dict[godot::Variant(godot::String("type"))] = Variant(prop_info.type);
224-
info_dict[godot::Variant(godot::String("name"))] = Variant(*reinterpret_cast<godot::String *>(prop_info.name));
225-
info_dict[godot::Variant(godot::String("class_name"))] =
226-
Variant(*reinterpret_cast<godot::StringName *>(prop_info.name));
227-
info_dict[godot::Variant(godot::String("hint"))] = Variant(prop_info.hint);
228-
info_dict[godot::Variant(godot::String("hint_string"))] =
229-
Variant(*reinterpret_cast<godot::String *>(prop_info.hint_string));
230-
info_dict[godot::Variant(godot::String("usage"))] = Variant(prop_info.usage);
231-
ret_val.push_back(info_dict);
219+
const DartScript *top = this;
220+
while (top != nullptr) {
221+
const auto &properties_cache = top->get_properties();
222+
for (const auto &prop_info : properties_cache) {
223+
godot::Dictionary info_dict;
224+
info_dict[godot::Variant(godot::String("type"))] = Variant(prop_info.type);
225+
info_dict[godot::Variant(godot::String("name"))] =
226+
Variant(*reinterpret_cast<godot::StringName *>(prop_info.name));
227+
info_dict[godot::Variant(godot::String("class_name"))] =
228+
Variant(*reinterpret_cast<godot::StringName *>(prop_info.class_name));
229+
info_dict[godot::Variant(godot::String("hint"))] = Variant(prop_info.hint);
230+
info_dict[godot::Variant(godot::String("hint_string"))] =
231+
Variant(*reinterpret_cast<godot::String *>(prop_info.hint_string));
232+
info_dict[godot::Variant(godot::String("usage"))] = Variant(prop_info.usage);
233+
ret_val.push_back(info_dict);
234+
}
235+
top = top->_base_script.ptr();
232236
}
233237

234238
return ret_val;
@@ -433,7 +437,7 @@ void DartScript::dart_placeholder_erased(DartScriptInstance *p_placeholder) {
433437

434438
void DartScript::clear_property_cache() {
435439
for (auto &prop : _properties_cache) {
436-
gde_free_property_info_fields(&prop.second);
440+
gde_free_property_info_fields(&prop);
437441
}
438442
_properties_cache.clear();
439443
}
@@ -495,13 +499,27 @@ void DartScript::refresh_type(bool force) {
495499
intptr_t prop_count = 0;
496500
Dart_ListLength(properties_list, &prop_count);
497501

502+
// Always add a secret hidden property that tells Godot the name of our class
503+
{
504+
DART_CHECK(class_name, Dart_GetField(type_info, Dart_NewStringFromCString("className")),
505+
"Failed to get class name.");
506+
GDExtensionPropertyInfo property_info = {
507+
GDEXTENSION_VARIANT_TYPE_NIL,
508+
new godot::StringName(*reinterpret_cast<godot::StringName *>(get_object_address(class_name))),
509+
new godot::String(),
510+
PROPERTY_HINT_NONE,
511+
new godot::String(path),
512+
PROPERTY_USAGE_CATEGORY};
513+
_properties_cache.push_back(property_info);
514+
}
515+
498516
if (prop_count > 0) {
499517
for (auto i = 0; i < prop_count; ++i) {
500518
DART_CHECK(dart_property, Dart_ListGetAt(properties_list, i), "Failed to get property at index");
501519
GDExtensionPropertyInfo property_info;
502520
gde_property_info_from_dart(dart_property, &property_info);
503521
godot::StringName *prop_name = reinterpret_cast<godot::StringName *>(property_info.name);
504-
_properties_cache[*prop_name] = property_info;
522+
_properties_cache.push_back(property_info);
505523
}
506524
}
507525

src/cpp/script/dart_script.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,16 @@ class DartScript : public godot::ScriptExtension {
5252
void *_instance_create(Object *for_object) const override;
5353
void *_placeholder_instance_create(Object *for_object) const override;
5454

55-
const std::unordered_map<godot::StringName, GDExtensionPropertyInfo> &get_properties() const {
55+
const std::vector<GDExtensionPropertyInfo> &get_properties() const {
5656
return _properties_cache;
5757
}
5858

5959
// Create the Dart object represented by this script
6060
Dart_Handle create_dart_object(Object *for_object);
6161
Dart_Handle get_dart_type_info();
62+
godot::Ref<DartScript> get_base_dart_script() {
63+
return _base_script;
64+
}
6265

6366
// ScriptInstances in extensions are never of the type that calls
6467
// _placeholder_erased, so we handle this manually on instance free
@@ -73,7 +76,7 @@ class DartScript : public godot::ScriptExtension {
7376
void *create_script_instance_internal(Object *for_object, bool is_placeholder) const;
7477

7578
godot::String _source_code;
76-
std::unordered_map<godot::StringName, GDExtensionPropertyInfo> _properties_cache;
79+
std::vector<GDExtensionPropertyInfo> _properties_cache;
7780
godot::Variant _rpc_config;
7881

7982
mutable std::unordered_set<DartScriptInstance *> _placeholders;

src/cpp/script/dart_script_instance.cpp

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include "dart_script_instance.h"
22

3+
#include <algorithm>
4+
35
#include <dart_api.h>
46

57
#include <godot_cpp/classes/object.hpp>
@@ -163,15 +165,32 @@ bool DartScriptInstance::get_class_category(GDExtensionPropertyInfo *p_class_cat
163165

164166
const GDExtensionPropertyInfo *DartScriptInstance::get_property_list(uint32_t *r_count) {
165167
GDExtensionPropertyInfo *prop_list{nullptr};
166-
const auto &prop_map = _dart_script->get_properties();
167-
size_t prop_count = prop_map.size();
168+
169+
// First scan to get total size
170+
size_t prop_count = 0;
171+
{
172+
DartScript *top = _dart_script.ptr();
173+
while (top != nullptr) {
174+
const auto &properties = top->get_properties();
175+
prop_count += properties.size();
176+
177+
top = top->get_base_dart_script().ptr();
178+
}
179+
}
180+
168181
*r_count = prop_count;
169182
if (prop_count > 0) {
183+
// Now get all the properties
170184
prop_list = new GDExtensionPropertyInfo[prop_count];
171185
size_t index = 0;
172-
for (const auto &property : prop_map) {
173-
prop_list[index] = property.second;
174-
++index;
186+
DartScript *top = _dart_script.ptr();
187+
while (top != nullptr) {
188+
const auto &properties = top->get_properties();
189+
for (const auto &property_info : properties) {
190+
prop_list[index] = property_info;
191+
++index;
192+
}
193+
top = top->get_base_dart_script().ptr();
175194
}
176195
}
177196

@@ -184,11 +203,19 @@ void DartScriptInstance::free_property_list(const GDExtensionPropertyInfo *p_lis
184203

185204
GDExtensionVariantType DartScriptInstance::get_property_type(const godot::StringName &p_name,
186205
GDExtensionBool *r_is_valid) {
187-
const auto &properties = _dart_script->get_properties();
188-
const auto &prop_itr = properties.find(p_name);
189-
*r_is_valid = prop_itr != properties.end();
190-
if (r_is_valid) {
191-
return prop_itr->second.type;
206+
207+
DartScript *top = _dart_script.ptr();
208+
while (top != nullptr) {
209+
const auto &properties = top->get_properties();
210+
const auto &prop_itr = std::find_if(properties.begin(), properties.end(), [&](auto &e) {
211+
godot::StringName name(*reinterpret_cast<godot::StringName *>(e.name));
212+
return name == p_name;
213+
});
214+
*r_is_valid = prop_itr != properties.end();
215+
if (r_is_valid) {
216+
return prop_itr->type;
217+
}
218+
top = top->get_base_dart_script().ptr();
192219
}
193220
return GDExtensionVariantType{};
194221
}

src/dart/godot_dart/lib/src/core/type_info.dart

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,17 @@ class ExtensionTypeInfo<T> implements TypeInfo {
394394

395395
@pragma('vm:entry-point')
396396
PropertyInfo? getPropertyInfo(String propertyName) {
397-
return properties.firstWhereOrNull((e) => e.name == propertyName);
397+
ExtensionTypeInfo<dynamic>? searchTypeInfo = this;
398+
PropertyInfo? info;
399+
while (searchTypeInfo != null &&
400+
searchTypeInfo.className != searchTypeInfo.nativeTypeName) {
401+
info = searchTypeInfo.properties
402+
.firstWhereOrNull((e) => e.name == propertyName);
403+
if (info != null) {
404+
break;
405+
}
406+
searchTypeInfo = searchTypeInfo.parentTypeInfo;
407+
}
408+
return info;
398409
}
399410
}

0 commit comments

Comments
 (0)