diff --git a/src/Example.cpp b/src/Example.cpp index 54e6dd3..4a214ba 100644 --- a/src/Example.cpp +++ b/src/Example.cpp @@ -13,6 +13,61 @@ namespace { constexpr int MAGIC_NUMBER = 42; + + class MyCallableCustom : public godot::CallableCustom + { + public: + virtual uint32_t hash() const + { + return 27; + } + + virtual godot::String get_as_text() const + { + return ""; + } + + static bool compare_equal_func( const CallableCustom *inA, const CallableCustom *inB ) + { + return inA == inB; + } + + virtual CompareEqualFunc get_compare_equal_func() const + { + return &MyCallableCustom::compare_equal_func; + } + + static bool compare_less_func( const CallableCustom *inA, const CallableCustom *inB ) + { + return reinterpret_cast( inA ) < reinterpret_cast( inB ); + } + + virtual CompareLessFunc get_compare_less_func() const + { + return &MyCallableCustom::compare_less_func; + } + + bool is_valid() const + { + return true; + } + + virtual godot::ObjectID get_object() const + { + return godot::ObjectID(); + } + + virtual void call( const godot::Variant **inArguments, int inArgcount, + godot::Variant &outReturnValue, + GDExtensionCallError &outCallError ) const + { + UNUSED( inArguments ); + UNUSED( inArgcount ); + + outReturnValue = "Hi"; + outCallError.error = GDEXTENSION_CALL_OK; + } + }; } //// ExampleRef @@ -38,6 +93,11 @@ ExampleRef::~ExampleRef() " destroyed, current instance count: ", godot::itos( sInstanceCount ) ); } +void ExampleRef::setId( int inID ) +{ + mID = inID; +} + int ExampleRef::getID() const { return mID; @@ -45,7 +105,19 @@ int ExampleRef::getID() const void ExampleRef::_bind_methods() { + godot::ClassDB::bind_method( godot::D_METHOD( "set_id", "id" ), &ExampleRef::setId ); godot::ClassDB::bind_method( godot::D_METHOD( "get_id" ), &ExampleRef::getID ); + + godot::ClassDB::bind_method( godot::D_METHOD( "was_post_initialized" ), + &ExampleRef::wasPostInitialized ); +} + +void ExampleRef::_notification( int inWhat ) +{ + if ( inWhat == NOTIFICATION_POSTINITIALIZE ) + { + mPostInitialized = true; + } } //// ExampleMin @@ -232,6 +304,32 @@ godot::String Example::testStringOps() const return s; } +godot::String Example::testStrUtility() const +{ + return godot::UtilityFunctions::str( "Hello, ", "World", "! The answer is ", 42 ); +} + +bool Example::testStringIsFortyTwo( const godot::String &inString ) const +{ + return strcmp( inString.utf8().ptr(), "forty two" ) == 0; +} + +godot::String Example::testStringResize( godot::String ioString ) const +{ + int64_t orig_len = ioString.length(); + + // This cast is to fix an issue with the API. + // See: https://github.com/godotengine/godot-cpp/issues/1338 + ioString.resize( static_cast( orig_len ) + 3 ); + + char32_t *data = ioString.ptrw(); + data[orig_len + 0] = '!'; + data[orig_len + 1] = '?'; + data[orig_len + 2] = '\0'; + + return ioString; +} + int Example::testVectorOps() const { godot::PackedInt32Array arr; @@ -249,13 +347,202 @@ int Example::testVectorOps() const return ret; } +bool Example::testObjectCastToNode( godot::Object *inObject ) const +{ + return godot::Object::cast_to( inObject ) != nullptr; +} + +bool Example::testObjectCastToControl( godot::Object *inObject ) const +{ + return godot::Object::cast_to( inObject ) != nullptr; +} + +bool Example::testObjectCastToExample( godot::Object *inObject ) const +{ + return godot::Object::cast_to( inObject ) != nullptr; +} + +godot::Vector2i Example::testVariantVector2iConversion( const godot::Variant &inVariant ) const +{ + return inVariant; +} + +int Example::testVariantIntConversion( const godot::Variant &inVariant ) const +{ + return inVariant; +} + +float Example::testVariantFloatConversion( const godot::Variant &inVariant ) const +{ + return inVariant; +} + +godot::Variant Example::testVariantCall( godot::Variant inVariant ) +{ + return inVariant.call( "test", "hello" ); +} + +godot::Variant Example::testVariantIterator( const godot::Variant &inVariant ) +{ + godot::Array output; + godot::Variant iter; + + bool is_init_valid = true; + + if ( !inVariant.iter_init( iter, is_init_valid ) ) + { + if ( !is_init_valid ) + { + return "iter_init: not valid"; + } + + return output; + } + + bool is_iter_next_valid = true; + bool is_iter_get_valid = true; + + do + { + if ( !is_iter_next_valid ) + { + return "iter_next: not valid"; + } + + godot::Variant value = inVariant.iter_get( iter, is_iter_get_valid ); + + if ( !is_iter_get_valid ) + { + return "iter_get: not valid"; + } + + output.push_back( ( static_cast( value ) ) + 5 ); + + } while ( inVariant.iter_next( iter, is_iter_next_valid ) ); + + if ( !is_iter_next_valid ) + { + return "iter_next: not valid"; + } + + return output; +} + +void Example::testAddChild( godot::Node *inNode ) +{ + add_child( inNode ); +} + +void Example::testSetTileset( godot::TileMap *inTilemap, + const godot::Ref &inTileset ) const +{ + inTilemap->set_tileset( inTileset ); +} + +godot::Callable Example::testCallableMP() +{ + return callable_mp( this, &Example::unboundMethod1 ); +} + +godot::Callable Example::testCallableMPRet() +{ + return callable_mp( this, &Example::unboundMethod2 ); +} + +godot::Callable Example::testCallableMPRetC() const +{ + return callable_mp( this, &Example::unboundMethod3 ); +} + +godot::Callable Example::testCallableMPStatic() const +{ + return callable_mp_static( &Example::unboundStaticMethod1 ); +} + +godot::Callable Example::testCallableMPStaticRet() const +{ + return callable_mp_static( &Example::unboundStaticMethod2 ); +} + +godot::Callable Example::testCustomCallable() const +{ + return godot::Callable( memnew( MyCallableCustom ) ); +} + +void Example::callableBind() +{ + godot::Callable c = godot::Callable( this, "emit_custom_signal" ).bind( "bound", 11 ); + c.call(); +} + +void Example::unboundMethod1( godot::Object *inObject, godot::String inString, int inInt ) +{ + godot::String test = "unbound_method1: "; + test += inObject->get_class(); + test += " - " + inString; + emitCustomSignal( test, inInt ); +} + +godot::String Example::unboundMethod2( godot::Object *inObject, godot::String inString, int inInt ) +{ + godot::String test = "unbound_method2: "; + test += inObject->get_class(); + test += " - " + inString; + test += " - " + godot::itos( inInt ); + return test; +} + +godot::String Example::unboundMethod3( godot::Object *inObject, godot::String inString, + int inInt ) const +{ + godot::String test = "unbound_method3: "; + test += inObject->get_class(); + test += " - " + inString; + test += " - " + godot::itos( inInt ); + return test; +} + +void Example::unboundStaticMethod1( Example *inObject, godot::String inString, int inInt ) +{ + godot::String test = "unbound_static_method1: "; + test += inObject->get_class(); + test += " - " + inString; + inObject->emitCustomSignal( test, inInt ); +} + +godot::String Example::unboundStaticMethod2( godot::Object *inObject, godot::String inString, + int inInt ) +{ + godot::String test = "unbound_static_method2: "; + test += inObject->get_class(); + test += " - " + inString; + test += " - " + godot::itos( inInt ); + return test; +} + godot::BitField Example::testBitfield( godot::BitField inFlags ) { godot::UtilityFunctions::print( " Got BitField: ", godot::String::num_int64( inFlags ) ); return inFlags; } -// Properties. +// RPC +void Example::testRPC( int inValue ) +{ + mLastRPCArg = inValue; +} + +void Example::testSendRPC( int inValue ) +{ + rpc( "test_rpc", inValue ); +} + +int Example::returnLastRPCArg() +{ + return mLastRPCArg; +} + +// Properties void Example::setCustomPosition( const godot::Vector2 &inPos ) { mCustomPosition = inPos; @@ -271,6 +558,15 @@ godot::Vector4 Example::getV4() const return { 1.2f, 3.4f, 5.6f, 7.8f }; } +bool Example::testPostInitialize() const +{ + godot::Ref new_example_ref; + + new_example_ref.instantiate(); + + return new_example_ref->wasPostInitialized(); +} + // Static methods int Example::testStatic( int inA, int inB ) { @@ -314,14 +610,65 @@ void Example::_bind_methods() godot::ClassDB::bind_method( godot::D_METHOD( "test_dictionary" ), &Example::testDictionary ); godot::ClassDB::bind_method( godot::D_METHOD( "test_node_argument" ), &Example::testNodeArgument ); + godot::ClassDB::bind_method( godot::D_METHOD( "test_string_ops" ), &Example::testStringOps ); + godot::ClassDB::bind_method( godot::D_METHOD( "test_str_utility" ), &Example::testStrUtility ); + godot::ClassDB::bind_method( godot::D_METHOD( "test_string_is_forty_two" ), + &Example::testStringIsFortyTwo ); + godot::ClassDB::bind_method( godot::D_METHOD( "test_string_resize" ), + &Example::testStringResize ); + godot::ClassDB::bind_method( godot::D_METHOD( "test_vector_ops" ), &Example::testVectorOps ); + godot::ClassDB::bind_method( godot::D_METHOD( "test_object_cast_to_node", "object" ), + &Example::testObjectCastToNode ); + godot::ClassDB::bind_method( godot::D_METHOD( "test_object_cast_to_control", "object" ), + &Example::testObjectCastToControl ); + godot::ClassDB::bind_method( godot::D_METHOD( "test_object_cast_to_example", "object" ), + &Example::testObjectCastToExample ); + + godot::ClassDB::bind_method( godot::D_METHOD( "test_variant_vector2i_conversion", "variant" ), + &Example::testVariantVector2iConversion ); + godot::ClassDB::bind_method( godot::D_METHOD( "test_variant_int_conversion", "variant" ), + &Example::testVariantIntConversion ); + godot::ClassDB::bind_method( godot::D_METHOD( "test_variant_float_conversion", "variant" ), + &Example::testVariantFloatConversion ); + godot::ClassDB::bind_method( godot::D_METHOD( "test_variant_call", "variant" ), + &Example::testVariantCall ); + godot::ClassDB::bind_method( godot::D_METHOD( "test_variant_iterator", "input" ), + &Example::testVariantIterator ); + + godot::ClassDB::bind_method( godot::D_METHOD( "test_add_child", "node" ), + &Example::testAddChild ); + godot::ClassDB::bind_method( godot::D_METHOD( "test_set_tileset", "tilemap", "tileset" ), + &Example::testSetTileset ); + + godot::ClassDB::bind_method( godot::D_METHOD( "test_callable_mp" ), &Example::testCallableMP ); + godot::ClassDB::bind_method( godot::D_METHOD( "test_callable_mp_ret" ), + &Example::testCallableMPRet ); + godot::ClassDB::bind_method( godot::D_METHOD( "test_callable_mp_retc" ), + &Example::testCallableMPRetC ); + godot::ClassDB::bind_method( godot::D_METHOD( "test_callable_mp_static" ), + &Example::testCallableMPStatic ); + godot::ClassDB::bind_method( godot::D_METHOD( "test_callable_mp_static_ret" ), + &Example::testCallableMPStaticRet ); + godot::ClassDB::bind_method( godot::D_METHOD( "test_custom_callable" ), + &Example::testCustomCallable ); + godot::ClassDB::bind_method( godot::D_METHOD( "test_bitfield", "flags" ), &Example::testBitfield ); + godot::ClassDB::bind_method( godot::D_METHOD( "test_rpc", "value" ), &Example::testRPC ); + godot::ClassDB::bind_method( godot::D_METHOD( "test_send_rpc", "value" ), + &Example::testSendRPC ); + godot::ClassDB::bind_method( godot::D_METHOD( "return_last_rpc_arg" ), + &Example::returnLastRPCArg ); + godot::ClassDB::bind_method( godot::D_METHOD( "def_args", "a", "b" ), &Example::defArgs, DEFVAL( 100 ), DEFVAL( 200 ) ); + godot::ClassDB::bind_method( godot::D_METHOD( "callable_bind" ), &Example::callableBind ); + godot::ClassDB::bind_method( godot::D_METHOD( "test_post_initialize" ), + &Example::testPostInitialize ); godot::ClassDB::bind_static_method( "Example", godot::D_METHOD( "test_static", "a", "b" ), &Example::testStatic ); @@ -469,6 +816,17 @@ bool Example::_property_get_revert( const godot::StringName &inName, return false; }; +void Example::_validate_property( godot::PropertyInfo &inProperty ) const +{ + godot::String name = inProperty.name; + + // Test hiding the "mouse_filter" property from the editor. + if ( name == "mouse_filter" ) + { + inProperty.usage = godot::PROPERTY_USAGE_NO_EDITOR; + } +} + godot::String Example::_to_string() const { return "[ GDExtension::Example <--> Instance ID:" + godot::uitos( get_instance_id() ) + " ]"; diff --git a/src/Example.h b/src/Example.h index 50804e6..dc876a8 100644 --- a/src/Example.h +++ b/src/Example.h @@ -3,8 +3,11 @@ #include "godot_cpp/classes/control.hpp" #include "godot_cpp/classes/global_constants.hpp" +#include "godot_cpp/classes/tile_map.hpp" +#include "godot_cpp/classes/tile_set.hpp" #include "godot_cpp/classes/viewport.hpp" #include "godot_cpp/core/binder_common.hpp" +#include "godot_cpp/variant/variant.hpp" class ExampleRef : public godot::RefCounted { @@ -14,16 +17,25 @@ class ExampleRef : public godot::RefCounted ExampleRef(); ~ExampleRef() override; + void setId( int inID ); int getID() const; + bool wasPostInitialized() const + { + return mPostInitialized; + } + protected: static void _bind_methods(); + void _notification( int inWhat ); + private: static int sInstanceCount; static int sLastID; int mID; + bool mPostInitialized = false; }; class ExampleMin : public godot::Control @@ -84,15 +96,56 @@ class Example : public godot::Control godot::Dictionary testDictionary() const; Example *testNodeArgument( Example *inNode ) const; godot::String testStringOps() const; + godot::String testStrUtility() const; + bool testStringIsFortyTwo( const godot::String &inString ) const; + godot::String testStringResize( godot::String ioString ) const; int testVectorOps() const; + bool testObjectCastToNode( godot::Object *inObject ) const; + bool testObjectCastToControl( godot::Object *inObject ) const; + bool testObjectCastToExample( godot::Object *inObject ) const; + + godot::Vector2i testVariantVector2iConversion( const godot::Variant &inVariant ) const; + int testVariantIntConversion( const godot::Variant &inVariant ) const; + float testVariantFloatConversion( const godot::Variant &inVariant ) const; + godot::Variant testVariantCall( godot::Variant inVariant ); + godot::Variant testVariantIterator( const godot::Variant &inVariant ); + + void testAddChild( godot::Node *inNode ); + void testSetTileset( godot::TileMap *inTilemap, + const godot::Ref &inTileset ) const; + + godot::Callable testCallableMP(); + godot::Callable testCallableMPRet(); + godot::Callable testCallableMPRetC() const; + godot::Callable testCallableMPStatic() const; + godot::Callable testCallableMPStaticRet() const; + godot::Callable testCustomCallable() const; + + void callableBind(); + + void unboundMethod1( godot::Object *inObject, godot::String inString, int inInt ); + godot::String unboundMethod2( godot::Object *inObject, godot::String inString, int inInt ); + godot::String unboundMethod3( godot::Object *inObject, godot::String inString, + int inInt ) const; + static void unboundStaticMethod1( Example *inObject, godot::String inString, int inInt ); + static godot::String unboundStaticMethod2( godot::Object *inObject, godot::String inString, + int inInt ); + godot::BitField testBitfield( godot::BitField inFlags ); - // Property. + // RPC + void testRPC( int inValue ); + void testSendRPC( int inValue ); + int returnLastRPCArg(); + + // Property void setCustomPosition( const godot::Vector2 &inPos ); godot::Vector2 getCustomPosition() const; godot::Vector4 getV4() const; + bool testPostInitialize() const; + // Static method. static int testStatic( int inA, int inB ); static void testStatic2(); @@ -109,6 +162,7 @@ class Example : public godot::Control void _get_property_list( godot::List *outList ) const; bool _property_can_revert( const godot::StringName &inName ) const; bool _property_get_revert( const godot::StringName &inName, godot::Variant &outProperty ) const; + void _validate_property( godot::PropertyInfo &inProperty ) const; godot::String _to_string() const; @@ -116,6 +170,8 @@ class Example : public godot::Control godot::Vector2 mCustomPosition; godot::Vector3 mPropertyFromList; godot::Vector2 mDProp[3]; + + int mLastRPCArg = 0; }; VARIANT_ENUM_CAST( Example::Constants );