summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTechnoPorg <jonah.janzen@gmail.com>2022-01-25 08:37:41 -0700
committerTechnoPorg <jonah.janzen@gmail.com>2022-01-25 09:03:36 -0700
commit051ef479c93c0c830b60059e3dabed6fc381cdd6 (patch)
tree7e934e5aaa141e00de5d4a45ee7d92f4b9f6eb02
parent2f4d76f068b29783bde653406b51909b29a082a3 (diff)
Allow method binds to take Object subclasses as arguments
This commit adds a condition to VariantCaster that casts Variants of type OBJECT to any type T, if T is derived from Object. This change enables a fair bit of code cleanup. First, the Variant implicit cast operators for Node and Control can be removed, which allows for some invalid includes to be removed. Second, helper methods in Tree whose sole purpose was to cast arguments to TreeItem * are no longer necessary. A few small changes also had to be made to other files, due to the changes cascading down all the includes.
-rw-r--r--core/io/resource.h2
-rw-r--r--core/variant/binder_common.h50
-rw-r--r--core/variant/variant.cpp20
-rw-r--r--core/variant/variant.h4
-rw-r--r--doc/classes/Tree.xml8
-rw-r--r--editor/editor_properties.cpp2
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp2
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp4
-rw-r--r--editor/plugins/script_editor_plugin.cpp4
-rw-r--r--editor/rename_dialog.cpp2
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.cpp2
-rw-r--r--scene/debugger/scene_debugger.h1
-rw-r--r--scene/gui/tree.cpp8
-rw-r--r--scene/gui/tree.h17
-rw-r--r--tests/core/object/test_method_bind.h16
15 files changed, 64 insertions, 78 deletions
diff --git a/core/io/resource.h b/core/io/resource.h
index a0800c57cb..dea2160616 100644
--- a/core/io/resource.h
+++ b/core/io/resource.h
@@ -37,6 +37,8 @@
#include "core/templates/safe_refcount.h"
#include "core/templates/self_list.h"
+class Node;
+
#define RES_BASE_EXTENSION(m_ext) \
public: \
static void register_custom_data_to_otdb() { ClassDB::add_resource_base_extension(m_ext, get_class_static()); } \
diff --git a/core/variant/binder_common.h b/core/variant/binder_common.h
index 14f49d530c..b6fdb4d902 100644
--- a/core/variant/binder_common.h
+++ b/core/variant/binder_common.h
@@ -44,24 +44,42 @@
#include <stdio.h>
+// Variant cannot define an implicit cast operator for every Object subclass, so the
+// casting is done here, to allow binding methods with parameters more specific than Object *
+
template <class T>
struct VariantCaster {
static _FORCE_INLINE_ T cast(const Variant &p_variant) {
- return p_variant;
+ using TStripped = std::remove_pointer_t<T>;
+ if constexpr (std::is_base_of<Object, TStripped>::value) {
+ return Object::cast_to<TStripped>(p_variant);
+ } else {
+ return p_variant;
+ }
}
};
template <class T>
struct VariantCaster<T &> {
static _FORCE_INLINE_ T cast(const Variant &p_variant) {
- return p_variant;
+ using TStripped = std::remove_pointer_t<T>;
+ if constexpr (std::is_base_of<Object, TStripped>::value) {
+ return Object::cast_to<TStripped>(p_variant);
+ } else {
+ return p_variant;
+ }
}
};
template <class T>
struct VariantCaster<const T &> {
static _FORCE_INLINE_ T cast(const Variant &p_variant) {
- return p_variant;
+ using TStripped = std::remove_pointer_t<T>;
+ if constexpr (std::is_base_of<Object, TStripped>::value) {
+ return Object::cast_to<TStripped>(p_variant);
+ } else {
+ return p_variant;
+ }
}
};
@@ -135,7 +153,13 @@ struct PtrToArg<char32_t> {
template <typename T>
struct VariantObjectClassChecker {
static _FORCE_INLINE_ bool check(const Variant &p_variant) {
- return true;
+ using TStripped = std::remove_pointer_t<T>;
+ if constexpr (std::is_base_of<Object, TStripped>::value) {
+ Object *obj = p_variant;
+ return Object::cast_to<TStripped>(p_variant) || !obj;
+ } else {
+ return true;
+ }
}
};
@@ -151,24 +175,6 @@ struct VariantObjectClassChecker<const Ref<T> &> {
}
};
-template <>
-struct VariantObjectClassChecker<Node *> {
- static _FORCE_INLINE_ bool check(const Variant &p_variant) {
- Object *obj = p_variant;
- Node *node = p_variant;
- return node || !obj;
- }
-};
-
-template <>
-struct VariantObjectClassChecker<Control *> {
- static _FORCE_INLINE_ bool check(const Variant &p_variant) {
- Object *obj = p_variant;
- Control *control = p_variant;
- return control || !obj;
- }
-};
-
#ifdef DEBUG_METHODS_ENABLED
template <class T>
diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp
index db198da54a..b85d7641f0 100644
--- a/core/variant/variant.cpp
+++ b/core/variant/variant.cpp
@@ -38,8 +38,6 @@
#include "core/math/math_funcs.h"
#include "core/string/print_string.h"
#include "core/variant/variant_parser.h"
-#include "scene/gui/control.h"
-#include "scene/main/node.h"
String Variant::get_type_name(Variant::Type p_type) {
switch (p_type) {
@@ -2006,22 +2004,6 @@ Object *Variant::get_validated_object() const {
}
}
-Variant::operator Node *() const {
- if (type == OBJECT) {
- return Object::cast_to<Node>(_get_obj().obj);
- } else {
- return nullptr;
- }
-}
-
-Variant::operator Control *() const {
- if (type == OBJECT) {
- return Object::cast_to<Control>(_get_obj().obj);
- } else {
- return nullptr;
- }
-}
-
Variant::operator Dictionary() const {
if (type == DICTIONARY) {
return *reinterpret_cast<const Dictionary *>(_data._mem);
@@ -3416,7 +3398,7 @@ String Variant::get_call_error_text(Object *p_base, const StringName &p_method,
}
String class_name = p_base->get_class();
- Ref<Script> script = p_base->get_script();
+ Ref<Resource> script = p_base->get_script();
if (script.is_valid() && script->get_path().is_resource_file()) {
class_name += "(" + script->get_path().get_file() + ")";
}
diff --git a/core/variant/variant.h b/core/variant/variant.h
index 0860e7fdc3..ad7a3d0ef3 100644
--- a/core/variant/variant.h
+++ b/core/variant/variant.h
@@ -53,8 +53,6 @@
#include "core/variant/dictionary.h"
class Object;
-class Node; // helper
-class Control; // helper
struct PropertyInfo;
struct MethodInfo;
@@ -339,8 +337,6 @@ public:
operator ::RID() const;
operator Object *() const;
- operator Node *() const;
- operator Control *() const;
operator Callable() const;
operator Signal() const;
diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml
index 10bbdc0301..2eda8990ca 100644
--- a/doc/classes/Tree.xml
+++ b/doc/classes/Tree.xml
@@ -50,7 +50,7 @@
</method>
<method name="create_item">
<return type="TreeItem" />
- <argument index="0" name="parent" type="Object" default="null" />
+ <argument index="0" name="parent" type="TreeItem" default="null" />
<argument index="1" name="idx" type="int" default="-1" />
<description>
Creates an item in the tree and adds it as a child of [code]parent[/code].
@@ -170,7 +170,7 @@
</method>
<method name="get_item_area_rect" qualifiers="const">
<return type="Rect2" />
- <argument index="0" name="item" type="Object" />
+ <argument index="0" name="item" type="TreeItem" />
<argument index="1" name="column" type="int" default="-1" />
<description>
Returns the rectangle area for the specified item. If [code]column[/code] is specified, only get the position and size of that column, otherwise get the rectangle containing all columns.
@@ -185,7 +185,7 @@
</method>
<method name="get_next_selected">
<return type="TreeItem" />
- <argument index="0" name="from" type="Object" />
+ <argument index="0" name="from" type="TreeItem" />
<description>
Returns the next selected item after the given one, or [code]null[/code] if the end is reached.
If [code]from[/code] is [code]null[/code], this returns the first selected item.
@@ -239,7 +239,7 @@
</method>
<method name="scroll_to_item">
<return type="void" />
- <argument index="0" name="item" type="Object" />
+ <argument index="0" name="item" type="TreeItem" />
<description>
Causes the [Tree] to jump to the specified item.
</description>
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index 97a38b9200..e00dd698b6 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -2695,7 +2695,7 @@ void EditorPropertyNodePath::_node_selected(const NodePath &p_path) {
}
if (!base_node && get_edited_object()->has_method("get_root_path")) {
- base_node = get_edited_object()->call("get_root_path");
+ base_node = Object::cast_to<Node>(get_edited_object()->call("get_root_path"));
}
if (!base_node && Object::cast_to<RefCounted>(get_edited_object())) {
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index cb84e7ea65..f38426fbfe 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -3977,7 +3977,7 @@ void CanvasItemEditor::_selection_changed() {
void CanvasItemEditor::edit(CanvasItem *p_canvas_item) {
Array selection = editor_selection->get_selected_nodes();
- if (selection.size() != 1 || (Node *)selection[0] != p_canvas_item) {
+ if (selection.size() != 1 || Object::cast_to<Node>(selection[0]) != p_canvas_item) {
drag_type = DRAG_NONE;
// Clear the selection
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 6ea8fba9b5..6073c8669d 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -6623,7 +6623,7 @@ void Node3DEditor::snap_selected_nodes_to_floor() {
// For snapping to be performed, there must be solid geometry under at least one of the selected nodes.
// We need to check this before snapping to register the undo/redo action only if needed.
for (int i = 0; i < keys.size(); i++) {
- Node *node = keys[i];
+ Node *node = Object::cast_to<Node>(keys[i]);
Node3D *sp = Object::cast_to<Node3D>(node);
Dictionary d = snap_data[node];
Vector3 from = d["from"];
@@ -6645,7 +6645,7 @@ void Node3DEditor::snap_selected_nodes_to_floor() {
// Perform snapping if at least one node can be snapped
for (int i = 0; i < keys.size(); i++) {
- Node *node = keys[i];
+ Node *node = Object::cast_to<Node>(keys[i]);
Node3D *sp = Object::cast_to<Node3D>(node);
Dictionary d = snap_data[node];
Vector3 from = d["from"];
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 03ed0e0ef2..56ea072b51 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -2802,7 +2802,7 @@ bool ScriptEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data
}
if (String(d["type"]) == "script_list_element") {
- Node *node = d["script_list_element"];
+ Node *node = Object::cast_to<Node>(d["script_list_element"]);
ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(node);
if (se) {
@@ -2875,7 +2875,7 @@ void ScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Co
}
if (String(d["type"]) == "script_list_element") {
- Node *node = d["script_list_element"];
+ Node *node = Object::cast_to<Node>(d["script_list_element"]);
ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(node);
EditorHelp *eh = Object::cast_to<EditorHelp>(node);
diff --git a/editor/rename_dialog.cpp b/editor/rename_dialog.cpp
index 0e34d200f2..288fff7d41 100644
--- a/editor/rename_dialog.cpp
+++ b/editor/rename_dialog.cpp
@@ -362,7 +362,7 @@ void RenameDialog::_post_popup() {
Array selected_node_list = editor_selection->get_selected_nodes();
ERR_FAIL_COND(selected_node_list.size() == 0);
- preview_node = selected_node_list[0];
+ preview_node = Object::cast_to<Node>(selected_node_list[0]);
_update_preview();
_update_substitute();
diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp
index a944844226..a627eca45d 100644
--- a/modules/gdscript/language_server/gdscript_workspace.cpp
+++ b/modules/gdscript/language_server/gdscript_workspace.cpp
@@ -585,7 +585,7 @@ void GDScriptWorkspace::completion(const lsp::CompletionParams &p_params, List<S
stack.push_back(owner_scene_node);
while (!stack.is_empty()) {
- current = stack.pop_back();
+ current = Object::cast_to<Node>(stack.pop_back());
Ref<GDScript> script = current->get_script();
if (script.is_valid() && script->get_path() == path) {
break;
diff --git a/scene/debugger/scene_debugger.h b/scene/debugger/scene_debugger.h
index 432317a423..c8298391bb 100644
--- a/scene/debugger/scene_debugger.h
+++ b/scene/debugger/scene_debugger.h
@@ -37,6 +37,7 @@
#include "core/variant/array.h"
class Script;
+class Node;
class SceneDebugger {
public:
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index e46de43f1e..4f209db0f9 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -4752,7 +4752,7 @@ bool Tree::get_allow_reselect() const {
void Tree::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear"), &Tree::clear);
- ClassDB::bind_method(D_METHOD("create_item", "parent", "idx"), &Tree::_create_item, DEFVAL(Variant()), DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("create_item", "parent", "idx"), &Tree::create_item, DEFVAL(Variant()), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("get_root"), &Tree::get_root);
ClassDB::bind_method(D_METHOD("set_column_custom_minimum_width", "column", "min_width"), &Tree::set_column_custom_minimum_width);
@@ -4767,7 +4767,7 @@ void Tree::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_hide_root", "enable"), &Tree::set_hide_root);
ClassDB::bind_method(D_METHOD("is_root_hidden"), &Tree::is_root_hidden);
- ClassDB::bind_method(D_METHOD("get_next_selected", "from"), &Tree::_get_next_selected);
+ ClassDB::bind_method(D_METHOD("get_next_selected", "from"), &Tree::get_next_selected);
ClassDB::bind_method(D_METHOD("get_selected"), &Tree::get_selected);
ClassDB::bind_method(D_METHOD("get_selected_column"), &Tree::get_selected_column);
ClassDB::bind_method(D_METHOD("get_pressed_button"), &Tree::get_pressed_button);
@@ -4781,7 +4781,7 @@ void Tree::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_edited_column"), &Tree::get_edited_column);
ClassDB::bind_method(D_METHOD("edit_selected"), &Tree::edit_selected);
ClassDB::bind_method(D_METHOD("get_custom_popup_rect"), &Tree::get_custom_popup_rect);
- ClassDB::bind_method(D_METHOD("get_item_area_rect", "item", "column"), &Tree::_get_item_rect, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("get_item_area_rect", "item", "column"), &Tree::get_item_rect, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("get_item_at_position", "position"), &Tree::get_item_at_position);
ClassDB::bind_method(D_METHOD("get_column_at_position", "position"), &Tree::get_column_at_position);
ClassDB::bind_method(D_METHOD("get_drop_section_at_position", "position"), &Tree::get_drop_section_at_position);
@@ -4805,7 +4805,7 @@ void Tree::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_column_title_language", "column"), &Tree::get_column_title_language);
ClassDB::bind_method(D_METHOD("get_scroll"), &Tree::get_scroll);
- ClassDB::bind_method(D_METHOD("scroll_to_item", "item"), &Tree::_scroll_to_item);
+ ClassDB::bind_method(D_METHOD("scroll_to_item", "item"), &Tree::scroll_to_item);
ClassDB::bind_method(D_METHOD("set_h_scroll_enabled", "h_scroll"), &Tree::set_h_scroll_enabled);
ClassDB::bind_method(D_METHOD("is_h_scroll_enabled"), &Tree::is_h_scroll_enabled);
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index c60c87564e..1865bb4595 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -609,23 +609,6 @@ private:
protected:
static void _bind_methods();
- //bind helpers
- TreeItem *_create_item(Object *p_parent, int p_idx = -1) {
- return create_item(Object::cast_to<TreeItem>(p_parent), p_idx);
- }
-
- TreeItem *_get_next_selected(Object *p_item) {
- return get_next_selected(Object::cast_to<TreeItem>(p_item));
- }
-
- Rect2 _get_item_rect(Object *p_item, int p_column) const {
- return get_item_rect(Object::cast_to<TreeItem>(p_item), p_column);
- }
-
- void _scroll_to_item(Object *p_item) {
- scroll_to_item(Object::cast_to<TreeItem>(p_item));
- }
-
public:
virtual void gui_input(const Ref<InputEvent> &p_event) override;
diff --git a/tests/core/object/test_method_bind.h b/tests/core/object/test_method_bind.h
index 0c7e47fc89..350a08b6e2 100644
--- a/tests/core/object/test_method_bind.h
+++ b/tests/core/object/test_method_bind.h
@@ -51,9 +51,15 @@ public:
TEST_METHODRC,
TEST_METHODRC_ARGS,
TEST_METHOD_DEFARGS,
+ TEST_METHOD_OBJECT_CAST,
TEST_MAX
};
+ class ObjectSubclass : public Object {
+ public:
+ int value = 1;
+ };
+
int test_num = 0;
bool test_valid[TEST_MAX];
@@ -98,6 +104,10 @@ public:
test_valid[TEST_METHOD_DEFARGS] = p_arg1 == 1 && p_arg2 == 2 && p_arg3 == 3 && p_arg4 == 4 && p_arg5 == 5; //temporary
}
+ void test_method_object_cast(ObjectSubclass *p_object) {
+ test_valid[TEST_METHOD_OBJECT_CAST] = p_object->value == 1;
+ }
+
static void _bind_methods() {
ClassDB::bind_method(D_METHOD("test_method"), &MethodBindTester::test_method);
ClassDB::bind_method(D_METHOD("test_method_args"), &MethodBindTester::test_method_args);
@@ -108,6 +118,7 @@ public:
ClassDB::bind_method(D_METHOD("test_methodrc"), &MethodBindTester::test_methodrc);
ClassDB::bind_method(D_METHOD("test_methodrc_args"), &MethodBindTester::test_methodrc_args);
ClassDB::bind_method(D_METHOD("test_method_default_args"), &MethodBindTester::test_method_default_args, DEFVAL(9) /* wrong on purpose */, DEFVAL(4), DEFVAL(5));
+ ClassDB::bind_method(D_METHOD("test_method_object_cast", "object"), &MethodBindTester::test_method_object_cast);
}
virtual void run_tests() {
@@ -134,6 +145,10 @@ public:
test_valid[TEST_METHODRC_ARGS] = int(call("test_methodrc_args", test_num)) == test_num && test_valid[TEST_METHODRC_ARGS];
call("test_method_default_args", 1, 2, 3, 4);
+
+ ObjectSubclass *obj = memnew(ObjectSubclass);
+ call("test_method_object_cast", obj);
+ memdelete(obj);
}
};
@@ -152,6 +167,7 @@ TEST_CASE("[MethodBind] check all method binds") {
CHECK(mbt->test_valid[MethodBindTester::TEST_METHODRC]);
CHECK(mbt->test_valid[MethodBindTester::TEST_METHODRC_ARGS]);
CHECK(mbt->test_valid[MethodBindTester::TEST_METHOD_DEFARGS]);
+ CHECK(mbt->test_valid[MethodBindTester::TEST_METHOD_OBJECT_CAST]);
memdelete(mbt);
}