diff options
159 files changed, 2007 insertions, 989 deletions
diff --git a/.travis.yml b/.travis.yml index 8d1dd1ef90..49d1059360 100644 --- a/.travis.yml +++ b/.travis.yml @@ -74,7 +74,7 @@ matrix: - name: macOS editor (debug, Clang) stage: build - env: PLATFORM=osx TOOLS=yes TARGET=debug CACHE_NAME=${PLATFORM}-tools-clang + env: PLATFORM=osx TOOLS=yes TARGET=debug CACHE_NAME=${PLATFORM}-tools-clang EXTRA_ARGS="warnings=extra werror=yes" os: osx compiler: clang diff --git a/core/SCsub b/core/SCsub index ed9a0a231d..b12c6a9e27 100644 --- a/core/SCsub +++ b/core/SCsub @@ -80,6 +80,8 @@ if env['builtin_zlib']: env_thirdparty.Prepend(CPPPATH=[thirdparty_zlib_dir]) # Needs to be available in main env too env.Prepend(CPPPATH=[thirdparty_zlib_dir]) + if (env['target'] == 'debug'): + env_thirdparty.Append(CPPDEFINES=['ZLIB_DEBUG']) env_thirdparty.add_source_files(env.core_sources, thirdparty_zlib_sources) diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index d07ba44788..1d451b2982 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -1136,6 +1136,16 @@ bool _OS::request_permission(const String &p_name) { return OS::get_singleton()->request_permission(p_name); } +bool _OS::request_permissions() { + + return OS::get_singleton()->request_permissions(); +} + +Vector<String> _OS::get_granted_permissions() const { + + return OS::get_singleton()->get_granted_permissions(); +} + _OS *_OS::singleton = NULL; void _OS::_bind_methods() { @@ -1319,6 +1329,8 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("get_power_percent_left"), &_OS::get_power_percent_left); ClassDB::bind_method(D_METHOD("request_permission", "name"), &_OS::request_permission); + ClassDB::bind_method(D_METHOD("request_permissions"), &_OS::request_permissions); + ClassDB::bind_method(D_METHOD("get_granted_permissions"), &_OS::get_granted_permissions); ADD_PROPERTY(PropertyInfo(Variant::STRING, "clipboard"), "set_clipboard", "get_clipboard"); ADD_PROPERTY(PropertyInfo(Variant::INT, "current_screen"), "set_current_screen", "get_current_screen"); diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index 693b85710a..1a4fd1d5cb 100644 --- a/core/bind/core_bind.h +++ b/core/bind/core_bind.h @@ -349,6 +349,8 @@ public: bool has_feature(const String &p_feature) const; bool request_permission(const String &p_name); + bool request_permissions(); + Vector<String> get_granted_permissions() const; static _OS *get_singleton() { return singleton; } diff --git a/core/compressed_translation.cpp b/core/compressed_translation.cpp index f102721470..d927b74897 100644 --- a/core/compressed_translation.cpp +++ b/core/compressed_translation.cpp @@ -136,6 +136,8 @@ void PHashTranslation::generate(const Ref<Translation> &p_from) { bucket_table_size += 2 + b.size() * 4; } + ERR_FAIL_COND(bucket_table_size == 0); + hash_table.resize(size); bucket_table.resize(bucket_table_size); diff --git a/core/hash_map.h b/core/hash_map.h index 38da1d59ab..edc67e7806 100644 --- a/core/hash_map.h +++ b/core/hash_map.h @@ -210,6 +210,7 @@ private: e->next = hash_table[index]; e->hash = hash; e->pair.key = p_key; + e->pair.data = TData(); hash_table[index] = e; elements++; diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp index 0ba84d0c8f..1426dbbd4d 100644 --- a/core/io/multiplayer_api.cpp +++ b/core/io/multiplayer_api.cpp @@ -602,7 +602,16 @@ void MultiplayerAPI::_add_peer(int p_id) { void MultiplayerAPI::_del_peer(int p_id) { connected_peers.erase(p_id); - path_get_cache.erase(p_id); // I no longer need your cache, sorry. + // Cleanup get cache. + path_get_cache.erase(p_id); + // Cleanup sent cache. + // Some refactoring is needed to make this faster and do paths GC. + List<NodePath> keys; + path_send_cache.get_key_list(&keys); + for (List<NodePath>::Element *E = keys.front(); E; E = E->next()) { + PathSentCache *psc = path_send_cache.getptr(E->get()); + psc->confirmed_peers.erase(p_id); + } emit_signal("network_peer_disconnected", p_id); } diff --git a/core/math/plane.cpp b/core/math/plane.cpp index b01853c4ac..b6bcac4b27 100644 --- a/core/math/plane.cpp +++ b/core/math/plane.cpp @@ -91,7 +91,7 @@ bool Plane::intersect_3(const Plane &p_plane1, const Plane &p_plane2, Vector3 *r real_t denom = vec3_cross(normal0, normal1).dot(normal2); - if (ABS(denom) <= CMP_EPSILON) + if (Math::is_zero_approx(denom)) return false; if (r_result) { diff --git a/core/os/main_loop.cpp b/core/os/main_loop.cpp index 5587e827ba..146a301995 100644 --- a/core/os/main_loop.cpp +++ b/core/os/main_loop.cpp @@ -65,6 +65,8 @@ void MainLoop::_bind_methods() { BIND_CONSTANT(NOTIFICATION_OS_IME_UPDATE); BIND_CONSTANT(NOTIFICATION_APP_RESUMED); BIND_CONSTANT(NOTIFICATION_APP_PAUSED); + + ADD_SIGNAL(MethodInfo("on_request_permissions_result", PropertyInfo(Variant::STRING, "permission"), PropertyInfo(Variant::BOOL, "granted"))); }; void MainLoop::set_init_script(const Ref<Script> &p_init_script) { diff --git a/core/os/os.h b/core/os/os.h index 9b46b43081..b5224c4f63 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -530,6 +530,8 @@ public: List<String> get_restart_on_exit_arguments() const; virtual bool request_permission(const String &p_name) { return true; } + virtual bool request_permissions() { return true; } + virtual Vector<String> get_granted_permissions() const { return Vector<String>(); } virtual void process_and_drop_events() {} OS(); diff --git a/core/resource.cpp b/core/resource.cpp index 87c92ca5b1..e0a40b6f3c 100644 --- a/core/resource.cpp +++ b/core/resource.cpp @@ -365,17 +365,38 @@ bool Resource::is_translation_remapped() const { //helps keep IDs same number when loading/saving scenes. -1 clears ID and it Returns -1 when no id stored void Resource::set_id_for_path(const String &p_path, int p_id) { if (p_id == -1) { - id_for_path.erase(p_path); + if (ResourceCache::path_cache_lock) { + ResourceCache::path_cache_lock->write_lock(); + } + ResourceCache::resource_path_cache[p_path].erase(get_path()); + if (ResourceCache::path_cache_lock) { + ResourceCache::path_cache_lock->write_unlock(); + } } else { - id_for_path[p_path] = p_id; + if (ResourceCache::path_cache_lock) { + ResourceCache::path_cache_lock->write_lock(); + } + ResourceCache::resource_path_cache[p_path][get_path()] = p_id; + if (ResourceCache::path_cache_lock) { + ResourceCache::path_cache_lock->write_unlock(); + } } } int Resource::get_id_for_path(const String &p_path) const { - - if (id_for_path.has(p_path)) { - return id_for_path[p_path]; + if (ResourceCache::path_cache_lock) { + ResourceCache::path_cache_lock->read_lock(); + } + if (ResourceCache::resource_path_cache[p_path].has(get_path())) { + int result = ResourceCache::resource_path_cache[p_path][get_path()]; + if (ResourceCache::path_cache_lock) { + ResourceCache::path_cache_lock->read_unlock(); + } + return result; } else { + if (ResourceCache::path_cache_lock) { + ResourceCache::path_cache_lock->read_unlock(); + } return -1; } } @@ -430,12 +451,21 @@ Resource::~Resource() { } HashMap<String, Resource *> ResourceCache::resources; +#ifdef TOOLS_ENABLED +HashMap<String, HashMap<String, int> > ResourceCache::resource_path_cache; +#endif RWLock *ResourceCache::lock = NULL; +#ifdef TOOLS_ENABLED +RWLock *ResourceCache::path_cache_lock = NULL; +#endif void ResourceCache::setup() { lock = RWLock::create(); +#ifdef TOOLS_ENABLED + path_cache_lock = RWLock::create(); +#endif } void ResourceCache::clear() { diff --git a/core/resource.h b/core/resource.h index 038b4f6278..3e1fe07137 100644 --- a/core/resource.h +++ b/core/resource.h @@ -84,9 +84,7 @@ protected: void _set_path(const String &p_path); void _take_over_path(const String &p_path); -#ifdef TOOLS_ENABLED - Map<String, int> id_for_path; -#endif + public: static Node *(*_get_local_scene_func)(); //used by editor @@ -152,6 +150,10 @@ class ResourceCache { friend class ResourceLoader; //need the lock static RWLock *lock; static HashMap<String, Resource *> resources; +#ifdef TOOLS_ENABLED + static HashMap<String, HashMap<String, int> > resource_path_cache; // each tscn has a set of resource paths and IDs + static RWLock *path_cache_lock; +#endif // TOOLS_ENABLED friend void unregister_core_types(); static void clear(); friend void register_core_types(); diff --git a/core/variant.cpp b/core/variant.cpp index 16bbf94c54..e0cc6685f4 100644 --- a/core/variant.cpp +++ b/core/variant.cpp @@ -3299,7 +3299,7 @@ String vformat(const String &p_text, const Variant &p1, const Variant &p2, const bool error = false; String fmt = p_text.sprintf(args, &error); - ERR_FAIL_COND_V(error, String()); + ERR_FAIL_COND_V_MSG(error, String(), fmt); return fmt; } diff --git a/core/vmap.h b/core/vmap.h index fde9723d71..ed66b46993 100644 --- a/core/vmap.h +++ b/core/vmap.h @@ -196,8 +196,7 @@ public: int pos = _find_exact(p_key); if (pos < 0) { - V val; - pos = insert(p_key, val); + pos = insert(p_key, V()); } return _cowdata.get_m(pos).value; diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml index bc64cfbb19..97da8c9980 100644 --- a/doc/classes/Animation.xml +++ b/doc/classes/Animation.xml @@ -34,7 +34,7 @@ <method name="animation_track_get_key_animation" qualifiers="const"> <return type="String"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="key_idx" type="int"> </argument> @@ -44,7 +44,7 @@ <method name="animation_track_insert_key"> <return type="int"> </return> - <argument index="0" name="track" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="time" type="float"> </argument> @@ -56,7 +56,7 @@ <method name="animation_track_set_key_animation"> <return type="void"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="key_idx" type="int"> </argument> @@ -68,7 +68,7 @@ <method name="audio_track_get_key_end_offset" qualifiers="const"> <return type="float"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="key_idx" type="int"> </argument> @@ -78,7 +78,7 @@ <method name="audio_track_get_key_start_offset" qualifiers="const"> <return type="float"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="key_idx" type="int"> </argument> @@ -88,7 +88,7 @@ <method name="audio_track_get_key_stream" qualifiers="const"> <return type="Resource"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="key_idx" type="int"> </argument> @@ -98,7 +98,7 @@ <method name="audio_track_insert_key"> <return type="int"> </return> - <argument index="0" name="track" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="time" type="float"> </argument> @@ -114,7 +114,7 @@ <method name="audio_track_set_key_end_offset"> <return type="void"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="key_idx" type="int"> </argument> @@ -126,7 +126,7 @@ <method name="audio_track_set_key_start_offset"> <return type="void"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="key_idx" type="int"> </argument> @@ -138,7 +138,7 @@ <method name="audio_track_set_key_stream"> <return type="void"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="key_idx" type="int"> </argument> @@ -150,7 +150,7 @@ <method name="bezier_track_get_key_in_handle" qualifiers="const"> <return type="Vector2"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="key_idx" type="int"> </argument> @@ -160,7 +160,7 @@ <method name="bezier_track_get_key_out_handle" qualifiers="const"> <return type="Vector2"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="key_idx" type="int"> </argument> @@ -170,7 +170,7 @@ <method name="bezier_track_get_key_value" qualifiers="const"> <return type="float"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="key_idx" type="int"> </argument> @@ -180,7 +180,7 @@ <method name="bezier_track_insert_key"> <return type="int"> </return> - <argument index="0" name="track" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="time" type="float"> </argument> @@ -196,7 +196,7 @@ <method name="bezier_track_interpolate" qualifiers="const"> <return type="float"> </return> - <argument index="0" name="track" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="time" type="float"> </argument> @@ -206,7 +206,7 @@ <method name="bezier_track_set_key_in_handle"> <return type="void"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="key_idx" type="int"> </argument> @@ -218,7 +218,7 @@ <method name="bezier_track_set_key_out_handle"> <return type="void"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="key_idx" type="int"> </argument> @@ -230,7 +230,7 @@ <method name="bezier_track_set_key_value"> <return type="void"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="key_idx" type="int"> </argument> @@ -249,7 +249,7 @@ <method name="copy_track"> <return type="void"> </return> - <argument index="0" name="track" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="to_animation" type="Animation"> </argument> @@ -276,7 +276,7 @@ <method name="method_track_get_key_indices" qualifiers="const"> <return type="PoolIntArray"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="time_sec" type="float"> </argument> @@ -289,7 +289,7 @@ <method name="method_track_get_name" qualifiers="const"> <return type="String"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="key_idx" type="int"> </argument> @@ -300,7 +300,7 @@ <method name="method_track_get_params" qualifiers="const"> <return type="Array"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="key_idx" type="int"> </argument> @@ -311,7 +311,7 @@ <method name="remove_track"> <return type="void"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <description> Removes a track by specifying the track index. @@ -320,7 +320,7 @@ <method name="track_find_key" qualifiers="const"> <return type="int"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="time" type="float"> </argument> @@ -333,7 +333,7 @@ <method name="track_get_interpolation_loop_wrap" qualifiers="const"> <return type="bool"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <description> Returns [code]true[/code] if the track at [code]idx[/code] wraps the interpolation loop. New tracks wrap the interpolation loop by default. @@ -342,7 +342,7 @@ <method name="track_get_interpolation_type" qualifiers="const"> <return type="int" enum="Animation.InterpolationType"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <description> Returns the interpolation type of a given track. @@ -351,7 +351,7 @@ <method name="track_get_key_count" qualifiers="const"> <return type="int"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <description> Returns the amount of keys in a given track. @@ -360,7 +360,7 @@ <method name="track_get_key_time" qualifiers="const"> <return type="float"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="key_idx" type="int"> </argument> @@ -371,7 +371,7 @@ <method name="track_get_key_transition" qualifiers="const"> <return type="float"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="key_idx" type="int"> </argument> @@ -382,7 +382,7 @@ <method name="track_get_key_value" qualifiers="const"> <return type="Variant"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="key_idx" type="int"> </argument> @@ -393,7 +393,7 @@ <method name="track_get_path" qualifiers="const"> <return type="NodePath"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <description> Gets the path of a track. For more information on the path format, see [method track_set_path]. @@ -402,7 +402,7 @@ <method name="track_get_type" qualifiers="const"> <return type="int" enum="Animation.TrackType"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <description> Gets the type of a track. @@ -411,7 +411,7 @@ <method name="track_insert_key"> <return type="void"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="time" type="float"> </argument> @@ -426,7 +426,7 @@ <method name="track_is_enabled" qualifiers="const"> <return type="bool"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <description> Returns [code]true[/code] if the track at index [code]idx[/code] is enabled. @@ -435,7 +435,7 @@ <method name="track_is_imported" qualifiers="const"> <return type="bool"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <description> Returns [code]true[/code] if the given track is imported. Else, return [code]false[/code]. @@ -444,7 +444,7 @@ <method name="track_move_down"> <return type="void"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <description> Moves a track down. @@ -453,7 +453,7 @@ <method name="track_move_to"> <return type="void"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="to_idx" type="int"> </argument> @@ -464,7 +464,7 @@ <method name="track_move_up"> <return type="void"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <description> Moves a track up. @@ -473,7 +473,7 @@ <method name="track_remove_key"> <return type="void"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="key_idx" type="int"> </argument> @@ -484,7 +484,7 @@ <method name="track_remove_key_at_position"> <return type="void"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="position" type="float"> </argument> @@ -495,7 +495,7 @@ <method name="track_set_enabled"> <return type="void"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="enabled" type="bool"> </argument> @@ -506,7 +506,7 @@ <method name="track_set_imported"> <return type="void"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="imported" type="bool"> </argument> @@ -517,7 +517,7 @@ <method name="track_set_interpolation_loop_wrap"> <return type="void"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="interpolation" type="bool"> </argument> @@ -528,7 +528,7 @@ <method name="track_set_interpolation_type"> <return type="void"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="interpolation" type="int" enum="Animation.InterpolationType"> </argument> @@ -539,7 +539,7 @@ <method name="track_set_key_time"> <return type="void"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="key_idx" type="int"> </argument> @@ -552,7 +552,7 @@ <method name="track_set_key_transition"> <return type="void"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="key_idx" type="int"> </argument> @@ -565,7 +565,7 @@ <method name="track_set_key_value"> <return type="void"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="key" type="int"> </argument> @@ -578,7 +578,7 @@ <method name="track_set_path"> <return type="void"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="path" type="NodePath"> </argument> @@ -590,7 +590,7 @@ <method name="track_swap"> <return type="void"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="with_idx" type="int"> </argument> @@ -601,7 +601,7 @@ <method name="transform_track_insert_key"> <return type="int"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="time" type="float"> </argument> @@ -618,7 +618,7 @@ <method name="transform_track_interpolate" qualifiers="const"> <return type="Array"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="time_sec" type="float"> </argument> @@ -629,7 +629,7 @@ <method name="value_track_get_key_indices" qualifiers="const"> <return type="PoolIntArray"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="time_sec" type="float"> </argument> @@ -642,7 +642,7 @@ <method name="value_track_get_update_mode" qualifiers="const"> <return type="int" enum="Animation.UpdateMode"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <description> Returns the update mode of a value track. @@ -651,7 +651,7 @@ <method name="value_track_set_update_mode"> <return type="void"> </return> - <argument index="0" name="idx" type="int"> + <argument index="0" name="track_idx" type="int"> </argument> <argument index="1" name="mode" type="int" enum="Animation.UpdateMode"> </argument> diff --git a/doc/classes/AnimationPlayer.xml b/doc/classes/AnimationPlayer.xml index 5bb4a6e3c7..60d04649a8 100644 --- a/doc/classes/AnimationPlayer.xml +++ b/doc/classes/AnimationPlayer.xml @@ -5,6 +5,7 @@ </brief_description> <description> An animation player is used for general-purpose playback of [Animation] resources. It contains a dictionary of animations (referenced by name) and custom blend times between their transitions. Additionally, animations can be played and blended in different channels. + Updating the target properties of animations occurs at process time. </description> <tutorials> <link>https://docs.godotengine.org/en/latest/getting_started/step_by_step/animations.html</link> @@ -28,7 +29,7 @@ <argument index="0" name="delta" type="float"> </argument> <description> - Shifts position in the animation timeline. Delta is the time in seconds to shift. Events between the current frame and [code]delta[/code] are handled. + Shifts position in the animation timeline and immediately updates the animation. [code]delta[/code] is the time in seconds to shift. Events between the current frame and [code]delta[/code] are handled. </description> </method> <method name="animation_get_next" qualifiers="const"> @@ -145,6 +146,7 @@ <description> Plays the animation with key [code]name[/code]. Custom speed and blend times can be set. If [code]custom_speed[/code] is negative and [code]from_end[/code] is [code]true[/code], the animation will play backwards. If the animation has been paused by [method stop], it will be resumed. Calling [method play] without arguments will also resume the animation. + [b]Note:[/b] The animation will be updated the next time the AnimationPlayer is processed. If other variables are updated at the same time this is called, they may be updated too early. To perform the update immediately, call [code]advance(0)[/code]. </description> </method> <method name="play_backwards"> @@ -157,6 +159,7 @@ <description> Plays the animation with key [code]name[/code] in reverse. If the animation has been paused by [code]stop(true)[/code], it will be resumed backwards. Calling [code]play_backwards()[/code] without arguments will also resume the animation backwards. + [b]Note:[/b] This is the same as [code]play[/code] in regards to process/update behavior. </description> </method> <method name="queue"> diff --git a/doc/classes/HTTPClient.xml b/doc/classes/HTTPClient.xml index 1a2d5cab81..52e4b94051 100644 --- a/doc/classes/HTTPClient.xml +++ b/doc/classes/HTTPClient.xml @@ -44,6 +44,7 @@ </return> <description> Returns the response's body length. + [b]Note:[/b] Some Web servers may not send a body length. In this case, the value returned will be [code]-1[/code]. If using chunked transfer encoding, the body length will also be [code]-1[/code]. </description> </method> <method name="get_response_code" qualifiers="const"> @@ -175,7 +176,7 @@ <argument index="0" name="bytes" type="int"> </argument> <description> - Sets the size of the buffer used and maximum bytes to read per iteration. see [method read_response_body_chunk] + Sets the size of the buffer used and maximum bytes to read per iteration. See [method read_response_body_chunk]. </description> </method> </methods> diff --git a/doc/classes/HTTPRequest.xml b/doc/classes/HTTPRequest.xml index 53ee0b6132..3a73d44a01 100644 --- a/doc/classes/HTTPRequest.xml +++ b/doc/classes/HTTPRequest.xml @@ -23,6 +23,7 @@ </return> <description> Returns the response body length. + [b]Note:[/b] Some Web servers may not send a body length. In this case, the value returned will be [code]-1[/code]. If using chunked transfer encoding, the body length will also be [code]-1[/code]. </description> </method> <method name="get_downloaded_bytes" qualifiers="const"> diff --git a/doc/classes/MainLoop.xml b/doc/classes/MainLoop.xml index 9e65da8eea..9457800825 100644 --- a/doc/classes/MainLoop.xml +++ b/doc/classes/MainLoop.xml @@ -167,6 +167,17 @@ </description> </method> </methods> + <signals> + <signal name="on_request_permissions_result"> + <argument index="0" name="permission" type="String"> + </argument> + <argument index="1" name="granted" type="bool"> + </argument> + <description> + Emitted when an user responds to permission request. + </description> + </signal> + </signals> <constants> <constant name="NOTIFICATION_WM_MOUSE_ENTER" value="1002"> Notification received from the OS when the mouse enters the game window. diff --git a/doc/classes/NavigationMesh.xml b/doc/classes/NavigationMesh.xml index f6c7a7d1b5..6528704bb5 100644 --- a/doc/classes/NavigationMesh.xml +++ b/doc/classes/NavigationMesh.xml @@ -107,6 +107,10 @@ </member> <member name="geometry/parsed_geometry_type" type="int" setter="set_parsed_geometry_type" getter="get_parsed_geometry_type" default="0"> </member> + <member name="geometry/source_geometry_mode" type="int" setter="set_source_geometry_mode" getter="get_source_geometry_mode" default="0"> + </member> + <member name="geometry/source_group_name" type="String" setter="set_source_group_name" getter="get_source_group_name"> + </member> <member name="polygon/verts_per_poly" type="float" setter="set_verts_per_poly" getter="get_verts_per_poly" default="6.0"> </member> <member name="region/merge_size" type="float" setter="set_region_merge_size" getter="get_region_merge_size" default="20.0"> diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index b206d4a4d2..1f685aab81 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -40,8 +40,9 @@ <return type="String"> </return> <description> - The string returned from this method is displayed as a warning in the "Scene Dock" if the script that overrides it is a [code]tool[/code] script. + The string returned from this method is displayed as a warning in the Scene Dock if the script that overrides it is a [code]tool[/code] script. Returning an empty string produces no warning. + Call [method update_configuration_warning] when the warning needs to be updated for this node. </description> </method> <method name="_input" qualifiers="virtual"> @@ -818,6 +819,14 @@ Sets whether this is an instance load placeholder. See [InstancePlaceholder]. </description> </method> + <method name="update_configuration_warning"> + <return type="void"> + </return> + <description> + Updates the warning displayed for this node in the Scene Dock. + Use [method _get_configuration_warning] to setup the warning message to display. + </description> + </method> </methods> <members> <member name="custom_multiplayer" type="MultiplayerAPI" setter="set_custom_multiplayer" getter="get_custom_multiplayer"> diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml index 9e1b25abe1..13a2fc2602 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -217,6 +217,13 @@ Returns the path to the current engine executable. </description> </method> + <method name="get_granted_permissions"> + <return type="PoolStringArray"> + </return> + <description> + With this function you can get the list of dangerous permissions that have been granted to the Android application. + </description> + </method> <method name="get_ime_selection" qualifiers="const"> <return type="Vector2"> </return> @@ -744,6 +751,13 @@ At the moment this function is only used by [code]AudioDriverOpenSL[/code] to request permission for [code]RECORD_AUDIO[/code] on Android. </description> </method> + <method name="request_permissions"> + <return type="bool"> + </return> + <description> + With this function you can request dangerous permissions since normal permissions are automatically granted at install time in Android application. + </description> + </method> <method name="set_icon"> <return type="void"> </return> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 9657982016..fa735b8918 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -6,6 +6,7 @@ <description> Contains global variables accessible from everywhere. Use [method get_setting], [method set_setting] or [method has_setting] to access them. Variables stored in [code]project.godot[/code] are also loaded into ProjectSettings, making this object very useful for reading custom game configuration options. When naming a Project Settings property, use the full path to the setting including the category. For example, [code]"application/config/name"[/code] for the project name. Category and property names can be viewed in the Project Settings dialog. + [b]Overriding:[/b] Any project setting can be overridden by creating a file named [code]override.cfg[/code] in the project's root directory. This can also be used in exported projects by placing this file in the same directory as the project binary. </description> <tutorials> </tutorials> @@ -201,6 +202,7 @@ </member> <member name="application/config/project_settings_override" type="String" setter="" getter="" default=""""> Specifies a file to override project settings. For example: [code]user://custom_settings.cfg[/code]. + [b]Note:[/b] Regardless of this setting's value, [code]res://override.cfg[/code] will still be read to override the project settings (see this class' description at the top). </member> <member name="application/config/use_custom_user_dir" type="bool" setter="" getter="" default="false"> If [code]true[/code], the project will save user data to its own user directory (see [member application/config/custom_user_dir_name]). This setting is only effective on desktop platforms. A name must be set in the [member application/config/custom_user_dir_name] setting for this to take effect. If [code]false[/code], the project will save user data to [code](OS user data directory)/Godot/app_userdata/(project name)[/code]. diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml index faf2ac1ff9..01c8ee4779 100644 --- a/doc/classes/RichTextLabel.xml +++ b/doc/classes/RichTextLabel.xml @@ -16,8 +16,13 @@ </return> <argument index="0" name="image" type="Texture"> </argument> + <argument index="1" name="width" type="int" default="0"> + </argument> + <argument index="2" name="height" type="int" default="0"> + </argument> <description> - Adds an image's opening and closing tags to the tag stack. + Adds an image's opening and closing tags to the tag stack, optionally providing a [code]width[/code] and [code]height[/code] to resize the image. + If [code]width[/code] or [code]height[/code] is set to 0, the image size will be adjusted in order to keep the original aspect ratio. </description> </method> <method name="add_text"> diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml index 8a114efd34..27eab85301 100644 --- a/doc/classes/TextEdit.xml +++ b/doc/classes/TextEdit.xml @@ -54,7 +54,7 @@ <return type="void"> </return> <description> - Clears all the syntax coloring information. + Clears all custom syntax coloring information previously added with [method add_color_region] or [method add_keyword_color]. </description> </method> <method name="clear_undo_history"> diff --git a/drivers/coreaudio/audio_driver_coreaudio.cpp b/drivers/coreaudio/audio_driver_coreaudio.cpp index 3b06c47244..9081fccd3a 100644 --- a/drivers/coreaudio/audio_driver_coreaudio.cpp +++ b/drivers/coreaudio/audio_driver_coreaudio.cpp @@ -186,15 +186,15 @@ OSStatus AudioDriverCoreAudio::output_callback(void *inRefCon, for (unsigned int i = 0; i < ioData->mNumberBuffers; i++) { AudioBuffer *abuf = &ioData->mBuffers[i]; - int frames_left = inNumberFrames; + unsigned int frames_left = inNumberFrames; int16_t *out = (int16_t *)abuf->mData; while (frames_left) { - int frames = MIN(frames_left, ad->buffer_frames); + unsigned int frames = MIN(frames_left, ad->buffer_frames); ad->audio_server_process(frames, ad->samples_in.ptrw()); - for (int j = 0; j < frames * ad->channels; j++) { + for (unsigned int j = 0; j < frames * ad->channels; j++) { out[j] = ad->samples_in[j] >> 16; } @@ -231,7 +231,7 @@ OSStatus AudioDriverCoreAudio::input_callback(void *inRefCon, OSStatus result = AudioUnitRender(ad->input_unit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, &bufferList); if (result == noErr) { - for (int i = 0; i < inNumberFrames * ad->capture_channels; i++) { + for (unsigned int i = 0; i < inNumberFrames * ad->capture_channels; i++) { int32_t sample = ad->input_buf[i] << 16; ad->capture_buffer_write(sample); diff --git a/drivers/coremidi/midi_driver_coremidi.cpp b/drivers/coremidi/midi_driver_coremidi.cpp index 7a92ac0702..28665b5190 100644 --- a/drivers/coremidi/midi_driver_coremidi.cpp +++ b/drivers/coremidi/midi_driver_coremidi.cpp @@ -39,7 +39,7 @@ void MIDIDriverCoreMidi::read(const MIDIPacketList *packet_list, void *read_proc_ref_con, void *src_conn_ref_con) { MIDIPacket *packet = const_cast<MIDIPacket *>(packet_list->packet); - for (int i = 0; i < packet_list->numPackets; i++) { + for (UInt32 i = 0; i < packet_list->numPackets; i++) { receive_input_packet(packet->timeStamp, packet->data, packet->length); packet = MIDIPacketNext(packet); } diff --git a/drivers/unix/net_socket_posix.cpp b/drivers/unix/net_socket_posix.cpp index da46b393c6..5f99a40c79 100644 --- a/drivers/unix/net_socket_posix.cpp +++ b/drivers/unix/net_socket_posix.cpp @@ -70,6 +70,7 @@ #define SOCK_CBUF(x) x #define SOCK_IOCTL ioctl #define SOCK_CLOSE ::close +#define SOCK_CONNECT(p_sock, p_addr, p_addr_len) ::connect(p_sock, p_addr, p_addr_len) /* Windows */ #elif defined(WINDOWS_ENABLED) @@ -83,6 +84,9 @@ #define SOCK_CBUF(x) (const char *)(x) #define SOCK_IOCTL ioctlsocket #define SOCK_CLOSE closesocket +// connect is broken on windows under certain conditions, reasons unknown: +// See https://github.com/godotengine/webrtc-native/issues/6 +#define SOCK_CONNECT(p_sock, p_addr, p_addr_len) ::WSAConnect(p_sock, p_addr, p_addr_len, NULL, NULL, NULL, NULL) // Workaround missing flag in MinGW #if defined(__MINGW32__) && !defined(SIO_UDP_NETRESET) @@ -409,7 +413,7 @@ Error NetSocketPosix::connect_to_host(IP_Address p_host, uint16_t p_port) { struct sockaddr_storage addr; size_t addr_size = _set_addr_storage(&addr, p_host, p_port, _ip_type); - if (::connect(_sock, (struct sockaddr *)&addr, addr_size) != 0) { + if (SOCK_CONNECT(_sock, (struct sockaddr *)&addr, addr_size) != 0) { NetError err = _get_socket_error(); diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index b3d98a0648..25dee6aedb 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -126,7 +126,9 @@ void OS_Unix::initialize_core() { RWLockDummy::make_default(); #else ThreadPosix::make_default(); +#if !defined(OSX_ENABLED) && !defined(IPHONE_ENABLED) SemaphorePosix::make_default(); +#endif MutexPosix::make_default(); RWLockPosix::make_default(); #endif diff --git a/drivers/unix/semaphore_posix.cpp b/drivers/unix/semaphore_posix.cpp index 5aa51d77d1..fc2d5b0dfe 100644 --- a/drivers/unix/semaphore_posix.cpp +++ b/drivers/unix/semaphore_posix.cpp @@ -30,7 +30,7 @@ #include "semaphore_posix.h" -#if defined(UNIX_ENABLED) || defined(PTHREAD_ENABLED) +#if (defined(UNIX_ENABLED) || defined(PTHREAD_ENABLED)) && !defined(OSX_ENABLED) && !defined(IPHONE_ENABLED) #include "core/os/memory.h" #include <errno.h> diff --git a/drivers/unix/semaphore_posix.h b/drivers/unix/semaphore_posix.h index 83e75c9a82..8aff01fc27 100644 --- a/drivers/unix/semaphore_posix.h +++ b/drivers/unix/semaphore_posix.h @@ -33,7 +33,7 @@ #include "core/os/semaphore.h" -#if defined(UNIX_ENABLED) || defined(PTHREAD_ENABLED) +#if (defined(UNIX_ENABLED) || defined(PTHREAD_ENABLED)) && !defined(OSX_ENABLED) && !defined(IPHONE_ENABLED) #include <semaphore.h> diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp index adc3cc8d65..336d5c5814 100644 --- a/drivers/wasapi/audio_driver_wasapi.cpp +++ b/drivers/wasapi/audio_driver_wasapi.cpp @@ -76,7 +76,7 @@ public: CMMNotificationClient() : _cRef(1), _pEnumerator(NULL) {} - ~CMMNotificationClient() { + virtual ~CMMNotificationClient() { if ((_pEnumerator) != NULL) { (_pEnumerator)->Release(); (_pEnumerator) = NULL; diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 74e8df60f9..7183d34d4f 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -5727,16 +5727,24 @@ void AnimationTrackEditor::_show_imported_anim_warning() const { } void AnimationTrackEditor::_select_all_tracks_for_copy() { + TreeItem *track = track_copy_select->get_root()->get_children(); + if (!track) + return; + + bool all_selected = true; + while (track) { + if (!track->is_checked(0)) + all_selected = false; + + track = track->get_next(); + } + + track = track_copy_select->get_root()->get_children(); while (track) { - track->set_checked(0, selected_all_tracks); + track->set_checked(0, !all_selected); track = track->get_next(); } - selected_all_tracks = !selected_all_tracks; - if (selected_all_tracks) - select_all_button->set_text(TTR("Select All")); - else - select_all_button->set_text(TTR("Select None")); } void AnimationTrackEditor::_bind_methods() { @@ -6067,25 +6075,22 @@ AnimationTrackEditor::AnimationTrackEditor() { track_copy_dialog = memnew(ConfirmationDialog); add_child(track_copy_dialog); - track_copy_dialog->set_title(TTR("Select tracks to copy:")); + track_copy_dialog->set_title(TTR("Select Tracks to Copy")); track_copy_dialog->get_ok()->set_text(TTR("Copy")); VBoxContainer *track_vbox = memnew(VBoxContainer); track_copy_dialog->add_child(track_vbox); - selected_all_tracks = true; + Button *select_all_button = memnew(Button); + select_all_button->set_text(TTR("Select All/None")); + select_all_button->connect("pressed", this, "_select_all_tracks_for_copy"); + track_vbox->add_child(select_all_button); track_copy_select = memnew(Tree); track_copy_select->set_h_size_flags(SIZE_EXPAND_FILL); track_copy_select->set_v_size_flags(SIZE_EXPAND_FILL); track_copy_select->set_hide_root(true); track_vbox->add_child(track_copy_select); - track_copy_options = memnew(HBoxContainer); - track_vbox->add_child(track_copy_options); - select_all_button = memnew(Button); - select_all_button->set_text(TTR("Select All")); - select_all_button->connect("pressed", this, "_select_all_tracks_for_copy"); - track_copy_options->add_child(select_all_button); track_copy_dialog->connect("confirmed", this, "_edit_menu_pressed", varray(EDIT_COPY_TRACKS_CONFIRM)); animation_changing_awaiting_update = false; } diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index 830d5b52d3..fd28d8f4d1 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -465,11 +465,8 @@ class AnimationTrackEditor : public VBoxContainer { void _selection_changed(); - bool selected_all_tracks; ConfirmationDialog *track_copy_dialog; Tree *track_copy_select; - HBoxContainer *track_copy_options; - Button *select_all_button; struct TrackClipboard { NodePath full_path; diff --git a/editor/animation_track_editor_plugins.cpp b/editor/animation_track_editor_plugins.cpp index 226eef9c1e..3e0b644b20 100644 --- a/editor/animation_track_editor_plugins.cpp +++ b/editor/animation_track_editor_plugins.cpp @@ -337,7 +337,7 @@ AnimationTrackEditAudio::AnimationTrackEditAudio() { AudioStreamPreviewGenerator::get_singleton()->connect("preview_updated", this, "_preview_changed"); } -/// SPRITE FRAME /// +/// SPRITE FRAME / FRAME_COORDS /// int AnimationTrackEditSpriteFrame::get_key_height() const { @@ -439,14 +439,24 @@ void AnimationTrackEditSpriteFrame::draw_key(int p_index, float p_pixels_sec, in if (Object::cast_to<Sprite>(object) || Object::cast_to<Sprite3D>(object)) { - int frame = get_animation()->track_get_key_value(get_track(), p_index); - texture = object->call("get_texture"); if (!texture.is_valid()) { AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right); return; } + int hframes = object->call("get_hframes"); + int vframes = object->call("get_vframes"); + + Vector2 coords; + if (is_coords) { + coords = get_animation()->track_get_key_value(get_track(), p_index); + } else { + int frame = get_animation()->track_get_key_value(get_track(), p_index); + coords.x = frame % hframes; + coords.y = frame / hframes; + } + region.size = texture->get_size(); if (bool(object->call("is_region"))) { @@ -454,9 +464,6 @@ void AnimationTrackEditSpriteFrame::draw_key(int p_index, float p_pixels_sec, in region = Rect2(object->call("get_region_rect")); } - int hframes = object->call("get_hframes"); - int vframes = object->call("get_vframes"); - if (hframes > 1) { region.size.x /= hframes; } @@ -464,8 +471,8 @@ void AnimationTrackEditSpriteFrame::draw_key(int p_index, float p_pixels_sec, in region.size.y /= vframes; } - region.position.x += region.size.x * (frame % hframes); - region.position.y += region.size.y * (frame / hframes); + region.position.x += region.size.x * coords.x; + region.position.y += region.size.y * coords.y; } else if (Object::cast_to<AnimatedSprite>(object) || Object::cast_to<AnimatedSprite3D>(object)) { @@ -532,6 +539,11 @@ void AnimationTrackEditSpriteFrame::set_node(Object *p_object) { id = p_object->get_instance_id(); } +void AnimationTrackEditSpriteFrame::set_as_coords() { + + is_coords = true; +} + /// SUB ANIMATION /// int AnimationTrackEditSubAnim::get_key_height() const { @@ -1297,6 +1309,14 @@ AnimationTrackEdit *AnimationTrackEditDefaultPlugin::create_value_track_edit(Obj return sprite; } + if (p_property == "frame_coords" && (p_object->is_class("Sprite") || p_object->is_class("Sprite3D"))) { + + AnimationTrackEditSpriteFrame *sprite = memnew(AnimationTrackEditSpriteFrame); + sprite->set_as_coords(); + sprite->set_node(p_object); + return sprite; + } + if (p_property == "current_animation" && (p_object->is_class("AnimationPlayer"))) { AnimationTrackEditSubAnim *player = memnew(AnimationTrackEditSubAnim); diff --git a/editor/animation_track_editor_plugins.h b/editor/animation_track_editor_plugins.h index 5f0ea6196c..1013cccf72 100644 --- a/editor/animation_track_editor_plugins.h +++ b/editor/animation_track_editor_plugins.h @@ -81,6 +81,7 @@ class AnimationTrackEditSpriteFrame : public AnimationTrackEdit { GDCLASS(AnimationTrackEditSpriteFrame, AnimationTrackEdit); ObjectID id; + bool is_coords; public: virtual int get_key_height() const; @@ -89,6 +90,9 @@ public: virtual void draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right); void set_node(Object *p_object); + void set_as_coords(); + + AnimationTrackEditSpriteFrame() { is_coords = false; } }; class AnimationTrackEditSubAnim : public AnimationTrackEdit { diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index 1e5eabc24e..f5a01dfb04 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -417,6 +417,7 @@ ConnectDialog::ConnectDialog() { dst_method = memnew(LineEdit); dst_method->set_h_size_flags(SIZE_EXPAND_FILL); + dst_method->connect("text_entered", this, "_builtin_text_entered"); dstm_hb->add_child(dst_method); advanced = memnew(CheckButton); diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp index 9510092a86..7ae8a9e0ce 100644 --- a/editor/editor_export.cpp +++ b/editor/editor_export.cpp @@ -376,6 +376,12 @@ Error EditorExportPlatform::_save_zip_file(void *p_userdata, const String &p_pat return OK; } +Ref<ImageTexture> EditorExportPlatform::get_option_icon(int p_index) const { + Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme(); + ERR_FAIL_COND_V(theme.is_null(), Ref<ImageTexture>()); + return theme->get_icon("Play", "EditorIcons"); +} + String EditorExportPlatform::find_export_template(String template_file_name, String *err) const { String current_version = VERSION_FULL_CONFIG; @@ -1403,7 +1409,7 @@ bool EditorExport::poll_export_platforms() { bool changed = false; for (int i = 0; i < export_platforms.size(); i++) { - if (export_platforms.write[i]->poll_devices()) { + if (export_platforms.write[i]->poll_export()) { changed = true; } } diff --git a/editor/editor_export.h b/editor/editor_export.h index 11dc464b5a..b0e27af629 100644 --- a/editor/editor_export.h +++ b/editor/editor_export.h @@ -243,10 +243,12 @@ public: Error save_pack(const Ref<EditorExportPreset> &p_preset, const String &p_path, Vector<SharedObject> *p_so_files = NULL, bool p_embed = false, int64_t *r_embedded_start = NULL, int64_t *r_embedded_size = NULL); Error save_zip(const Ref<EditorExportPreset> &p_preset, const String &p_path); - virtual bool poll_devices() { return false; } - virtual int get_device_count() const { return 0; } - virtual String get_device_name(int p_device) const { return ""; } - virtual String get_device_info(int p_device) const { return ""; } + virtual bool poll_export() { return false; } + virtual int get_options_count() const { return 0; } + virtual String get_options_tooltip() const { return ""; } + virtual Ref<ImageTexture> get_option_icon(int p_index) const; + virtual String get_option_label(int p_device) const { return ""; } + virtual String get_option_tooltip(int p_device) const { return ""; } enum DebugFlags { DEBUG_FLAG_DUMB_CLIENT = 1, diff --git a/editor/editor_fonts.cpp b/editor/editor_fonts.cpp index b6d27d84e0..97c796c707 100644 --- a/editor/editor_fonts.cpp +++ b/editor/editor_fonts.cpp @@ -64,7 +64,7 @@ Ref<DynamicFont> m_name; \ m_name.instance(); \ m_name->set_size(m_size); \ - if (CustomFont.is_valid()) { \ + if (CustomFontBold.is_valid()) { \ m_name->set_font_data(CustomFontBold); \ m_name->add_fallback(DefaultFontBold); \ } else { \ diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 4a1e93eaad..d2306abfd7 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -172,7 +172,9 @@ void EditorHelp::_class_desc_input(const Ref<InputEvent> &p_input) { void EditorHelp::_class_desc_resized() { // Add extra horizontal margins for better readability. // The margins increase as the width of the editor help container increases. - const int display_margin = MAX(30 * EDSCALE, get_parent_anchorable_rect().size.width - 900 * EDSCALE) * 0.5; + Ref<Font> doc_code_font = get_font("doc_source", "EditorFonts"); + real_t char_width = doc_code_font->get_char_size('x').width; + const int display_margin = MAX(30 * EDSCALE, get_parent_anchorable_rect().size.width - char_width * 120 * EDSCALE) * 0.5; Ref<StyleBox> class_desc_stylebox = EditorNode::get_singleton()->get_theme_base()->get_stylebox("normal", "RichTextLabel")->duplicate(); class_desc_stylebox->set_default_margin(MARGIN_LEFT, display_margin); @@ -1467,6 +1469,11 @@ void EditorHelp::_notification(int p_what) { _update_doc(); } break; + case NOTIFICATION_THEME_CHANGED: { + if (is_visible_in_tree()) { + _class_desc_resized(); + } + } break; default: break; } } diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 78e058eeaa..96b6a32914 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -644,7 +644,19 @@ void EditorProperty::_gui_input(const Ref<InputEvent> &p_event) { emit_signal("property_keyed", property, use_keying_next()); if (use_keying_next()) { - call_deferred("emit_changed", property, object->get(property).operator int64_t() + 1, "", false); + if (property == "frame_coords" && (object->is_class("Sprite") || object->is_class("Sprite3D"))) { + Vector2 new_coords = object->get(property); + new_coords.x++; + if (new_coords.x >= object->get("hframes").operator int64_t()) { + new_coords.x = 0; + new_coords.y++; + } + + call_deferred("emit_changed", property, new_coords, "", false); + } else { + call_deferred("emit_changed", property, object->get(property).operator int64_t() + 1, "", false); + } + call_deferred("update_property"); } } diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 2c3a84857a..9fa33044e1 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -33,6 +33,7 @@ #include "core/bind/core_bind.h" #include "core/class_db.h" #include "core/io/config_file.h" +#include "core/io/image_loader.h" #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" #include "core/io/stream_peer_ssl.h" @@ -56,6 +57,7 @@ #include "editor/editor_help.h" #include "editor/editor_properties.h" #include "editor/editor_settings.h" +#include "editor/editor_spin_slider.h" #include "editor/editor_themes.h" #include "editor/import/editor_import_collada.h" #include "editor/import/editor_scene_importer_gltf.h" @@ -370,7 +372,7 @@ void EditorNode::_notification(int p_what) { case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { scene_tabs->set_tab_close_display_policy((bool(EDITOR_GET("interface/scene_tabs/always_show_close_button")) ? Tabs::CLOSE_BUTTON_SHOW_ALWAYS : Tabs::CLOSE_BUTTON_SHOW_ACTIVE_ONLY)); - Ref<Theme> theme = create_editor_theme(theme_base->get_theme()); + theme = create_editor_theme(theme_base->get_theme()); theme_base->set_theme(theme); gui_base->set_theme(theme); @@ -1469,7 +1471,7 @@ void EditorNode::_dialog_action(String p_file) { config.instance(); Error err = config->load(EditorSettings::get_singleton()->get_editor_layouts_config()); - if (err == ERR_CANT_OPEN) { + if (err == ERR_FILE_CANT_OPEN || err == ERR_FILE_NOT_FOUND) { config.instance(); // new config } else if (err != OK) { show_warning(TTR("Error trying to save layout!")); @@ -3558,6 +3560,7 @@ void EditorNode::register_editor_types() { ClassDB::register_class<AnimationTrackEditPlugin>(); ClassDB::register_class<ScriptCreateDialog>(); ClassDB::register_class<EditorFeatureProfile>(); + ClassDB::register_class<EditorSpinSlider>(); // FIXME: Is this stuff obsolete, or should it be ported to new APIs? ClassDB::register_class<EditorScenePostImport>(); @@ -3637,6 +3640,20 @@ StringName EditorNode::get_object_custom_type_name(const Object *p_object) const return StringName(); } +Ref<ImageTexture> EditorNode::_load_custom_class_icon(const String &p_path) const { + if (p_path.length()) { + Ref<Image> img = memnew(Image); + Error err = ImageLoader::load_image(p_path, img); + if (err == OK) { + Ref<ImageTexture> icon = memnew(ImageTexture); + img->resize(16 * EDSCALE, 16 * EDSCALE, Image::INTERPOLATE_LANCZOS); + icon->create_from_image(img); + return icon; + } + } + return NULL; +} + Ref<Texture> EditorNode::get_object_icon(const Object *p_object, const String &p_fallback) const { ERR_FAIL_COND_V(!p_object || !gui_base, NULL); @@ -3650,8 +3667,10 @@ Ref<Texture> EditorNode::get_object_icon(const Object *p_object, const String &p while (base_script.is_valid()) { StringName name = EditorNode::get_editor_data().script_class_get_name(base_script->get_path()); String icon_path = EditorNode::get_editor_data().script_class_get_icon_path(name); - if (icon_path.length()) - return ResourceLoader::load(icon_path); + Ref<ImageTexture> icon = _load_custom_class_icon(icon_path); + if (icon.is_valid()) { + return icon; + } // should probably be deprecated in 4.x StringName base = base_script->get_instance_base_type(); @@ -3689,12 +3708,9 @@ Ref<Texture> EditorNode::get_class_icon(const String &p_class, const String &p_f if (ScriptServer::is_global_class(p_class)) { String icon_path = EditorNode::get_editor_data().script_class_get_icon_path(p_class); - RES icon; - - if (FileAccess::exists(icon_path)) { - icon = ResourceLoader::load(icon_path); - if (icon.is_valid()) - return icon; + Ref<ImageTexture> icon = _load_custom_class_icon(icon_path); + if (icon.is_valid()) { + return icon; } Ref<Script> script = ResourceLoader::load(ScriptServer::get_global_class_path(p_class), "Script"); @@ -3702,10 +3718,9 @@ Ref<Texture> EditorNode::get_class_icon(const String &p_class, const String &p_f while (script.is_valid()) { String current_icon_path; script->get_language()->get_global_class_name(script->get_path(), NULL, ¤t_icon_path); - if (FileAccess::exists(current_icon_path)) { - RES texture = ResourceLoader::load(current_icon_path); - if (texture.is_valid()) - return texture; + icon = _load_custom_class_icon(current_icon_path); + if (icon.is_valid()) { + return icon; } script = script->get_base_script(); } @@ -5638,6 +5653,9 @@ EditorNode::EditorNode() { editor_export = memnew(EditorExport); add_child(editor_export); + // Exporters might need the theme + theme = create_custom_theme(); + register_exporters(); GLOBAL_DEF("editor/main_run_args", ""); @@ -5679,7 +5697,6 @@ EditorNode::EditorNode() { theme_base->add_child(gui_base); gui_base->set_anchors_and_margins_preset(Control::PRESET_WIDE); - Ref<Theme> theme = create_custom_theme(); theme_base->set_theme(theme); gui_base->set_theme(theme); gui_base->add_style_override("panel", gui_base->get_stylebox("Background", "EditorStyles")); diff --git a/editor/editor_node.h b/editor/editor_node.h index 5ecb472e64..fb7e81d2d2 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -655,6 +655,7 @@ private: void _feature_profile_changed(); bool _is_class_editor_disabled_by_feature_profile(const StringName &p_class); + Ref<ImageTexture> _load_custom_class_icon(const String &p_path) const; protected: void _notification(int p_what); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index e978713c34..d3d91e6e0d 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -2205,7 +2205,14 @@ void EditorPropertyResource::_menu_option(int p_which) { case OBJ_MENU_NEW_SCRIPT: { if (Object::cast_to<Node>(get_edited_object())) { - EditorNode::get_singleton()->get_scene_tree_dock()->open_script_dialog(Object::cast_to<Node>(get_edited_object())); + EditorNode::get_singleton()->get_scene_tree_dock()->open_script_dialog(Object::cast_to<Node>(get_edited_object()), false); + } + + } break; + case OBJ_MENU_EXTEND_SCRIPT: { + + if (Object::cast_to<Node>(get_edited_object())) { + EditorNode::get_singleton()->get_scene_tree_dock()->open_script_dialog(Object::cast_to<Node>(get_edited_object()), true); } } break; @@ -2338,7 +2345,8 @@ void EditorPropertyResource::_update_menu_items() { menu->clear(); if (get_edited_property() == "script" && base_type == "Script" && Object::cast_to<Node>(get_edited_object())) { - menu->add_icon_item(get_icon("Script", "EditorIcons"), TTR("New Script"), OBJ_MENU_NEW_SCRIPT); + menu->add_icon_item(get_icon("ScriptCreate", "EditorIcons"), TTR("New Script"), OBJ_MENU_NEW_SCRIPT); + menu->add_icon_item(get_icon("ScriptExtend", "EditorIcons"), TTR("Extend Script"), OBJ_MENU_EXTEND_SCRIPT); menu->add_separator(); } else if (base_type != "") { int idx = 0; @@ -2399,19 +2407,11 @@ void EditorPropertyResource::_update_menu_items() { inheritors_array.push_back(t); - int id = TYPE_BASE_ID + idx; - - if (!icon.is_valid() && has_icon(t, "EditorIcons")) { - icon = get_icon(t, "EditorIcons"); - } - - if (icon.is_valid()) { + if (!icon.is_valid()) + icon = get_icon(has_icon(t, "EditorIcons") ? t : "Object", "EditorIcons"); - menu->add_icon_item(icon, vformat(TTR("New %s"), t), id); - } else { - - menu->add_item(vformat(TTR("New %s"), t), id); - } + int id = TYPE_BASE_ID + idx; + menu->add_icon_item(icon, vformat(TTR("New %s"), t), id); idx++; } @@ -2615,14 +2615,6 @@ void EditorPropertyResource::update_property() { get_tree()->call_deferred("call_group", "_editor_resource_properties", "_fold_other_editors", this); } opened_editor = true; - /* - Button *open_in_editor = memnew(Button); - open_in_editor->set_text(TTR("Open Editor")); - open_in_editor->set_icon(get_icon("Edit", "EditorIcons")); - sub_inspector_vbox->add_child(open_in_editor); - open_in_editor->connect("pressed", this, "_open_editor_pressed"); - open_in_editor->set_h_size_flags(SIZE_SHRINK_CENTER); - */ } } @@ -2649,10 +2641,9 @@ void EditorPropertyResource::update_property() { if (res == RES()) { assign->set_icon(Ref<Texture>()); assign->set_text(TTR("[empty]")); - assign->set_tooltip(""); } else { - assign->set_icon(EditorNode::get_singleton()->get_object_icon(res.operator->(), "Node")); + assign->set_icon(EditorNode::get_singleton()->get_object_icon(res.operator->(), "Object")); if (res->get_name() != String()) { assign->set_text(res->get_name()); @@ -2925,7 +2916,7 @@ void EditorInspectorDefaultPlugin::parse_begin(Object *p_object) { bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage) { - double default_float_step = EDITOR_GET("interface/inspector/default_float_step"); + float default_float_step = EDITOR_GET("interface/inspector/default_float_step"); switch (p_type) { diff --git a/editor/editor_properties.h b/editor/editor_properties.h index b8d6aa00c2..952b0447e2 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -550,7 +550,8 @@ class EditorPropertyResource : public EditorProperty { OBJ_MENU_COPY = 5, OBJ_MENU_PASTE = 6, OBJ_MENU_NEW_SCRIPT = 7, - OBJ_MENU_SHOW_IN_FILE_SYSTEM = 8, + OBJ_MENU_EXTEND_SCRIPT = 8, + OBJ_MENU_SHOW_IN_FILE_SYSTEM = 9, TYPE_BASE_ID = 100, CONVERT_BASE_ID = 1000 diff --git a/editor/editor_run_native.cpp b/editor/editor_run_native.cpp index 585ea0ec69..64e90f2488 100644 --- a/editor/editor_run_native.cpp +++ b/editor/editor_run_native.cpp @@ -75,20 +75,18 @@ void EditorRunNative::_notification(int p_what) { Ref<EditorExportPlatform> eep = EditorExport::get_singleton()->get_export_platform(E->key()); MenuButton *mb = E->get(); - int dc = eep->get_device_count(); + int dc = eep->get_options_count(); if (dc == 0) { mb->hide(); } else { mb->get_popup()->clear(); mb->show(); - if (dc == 1) { - mb->set_tooltip(eep->get_device_name(0) + "\n\n" + eep->get_device_info(0).strip_edges()); - } else { - mb->set_tooltip("Select device from the list"); + mb->set_tooltip(eep->get_options_tooltip()); + if (dc > 1) { for (int i = 0; i < dc; i++) { - mb->get_popup()->add_icon_item(get_icon("Play", "EditorIcons"), eep->get_device_name(i)); - mb->get_popup()->set_item_tooltip(mb->get_popup()->get_item_count() - 1, eep->get_device_info(i).strip_edges()); + mb->get_popup()->add_icon_item(eep->get_option_icon(i), eep->get_option_label(i)); + mb->get_popup()->set_item_tooltip(mb->get_popup()->get_item_count() - 1, eep->get_option_tooltip(i)); } } } @@ -111,7 +109,7 @@ void EditorRunNative::_run_native(int p_idx, int p_platform) { ERR_FAIL_COND(eep.is_null()); if (p_idx == -1) { - if (eep->get_device_count() == 1) { + if (eep->get_options_count() == 1) { menus[p_platform]->get_popup()->hide(); p_idx = 0; } else { diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 3ea59115b0..672844117d 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -559,8 +559,6 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { hints["editors/3d/freelook/freelook_base_speed"] = PropertyInfo(Variant::REAL, "editors/3d/freelook/freelook_base_speed", PROPERTY_HINT_RANGE, "0.0, 10, 0.01"); _initial_set("editors/3d/freelook/freelook_activation_modifier", 0); hints["editors/3d/freelook/freelook_activation_modifier"] = PropertyInfo(Variant::INT, "editors/3d/freelook/freelook_activation_modifier", PROPERTY_HINT_ENUM, "None,Shift,Alt,Meta,Ctrl"); - _initial_set("editors/3d/freelook/freelook_modifier_speed_factor", 3.0); - hints["editors/3d/freelook/freelook_modifier_speed_factor"] = PropertyInfo(Variant::REAL, "editors/3d/freelook/freelook_modifier_speed_factor", PROPERTY_HINT_RANGE, "0.0, 10.0, 0.1"); _initial_set("editors/3d/freelook/freelook_speed_zoom_link", false); // 2D diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp index 918b0ef96d..d80176f00d 100644 --- a/editor/editor_spin_slider.cpp +++ b/editor/editor_spin_slider.cpp @@ -228,9 +228,9 @@ void EditorSpinSlider::_notification(int p_what) { draw_style_box(focus, Rect2(Vector2(), get_size())); } - draw_string(font, Vector2(sb->get_offset().x, vofs), label, lc * Color(1, 1, 1, 0.5)); + draw_string(font, Vector2(Math::round(sb->get_offset().x), vofs), label, lc * Color(1, 1, 1, 0.5)); - draw_string(font, Vector2(sb->get_offset().x + string_width + sep, vofs), numstr, fc, number_width); + draw_string(font, Vector2(Math::round(sb->get_offset().x + string_width + sep), vofs), numstr, fc, number_width); if (get_step() == 1) { Ref<Texture> updown2 = get_icon("updown", "SpinBox"); diff --git a/editor/export_template_manager.cpp b/editor/export_template_manager.cpp index 1cdf9848ef..95c615afa3 100644 --- a/editor/export_template_manager.cpp +++ b/editor/export_template_manager.cpp @@ -155,38 +155,27 @@ void ExportTemplateManager::_uninstall_template(const String &p_version) { void ExportTemplateManager::_uninstall_template_confirm() { - DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - Error err = d->change_dir(EditorSettings::get_singleton()->get_templates_dir()); - - ERR_FAIL_COND(err != OK); - - err = d->change_dir(to_remove); - - ERR_FAIL_COND(err != OK); - - Vector<String> files; - d->list_dir_begin(); - String c = d->get_next(); - while (c != String()) { - if (!d->current_is_dir()) { - files.push_back(c); - } - c = d->get_next(); - } - d->list_dir_end(); + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + const String &templates_dir = EditorSettings::get_singleton()->get_templates_dir(); + Error err = da->change_dir(templates_dir); + ERR_FAIL_COND_MSG(err != OK, "Could not access templates directory at '" + templates_dir + "'."); + err = da->change_dir(to_remove); + ERR_FAIL_COND_MSG(err != OK, "Could not access templates directory at '" + templates_dir.plus_file(to_remove) + "'."); - for (int i = 0; i < files.size(); i++) { - d->remove(files[i]); - } + err = da->erase_contents_recursive(); + ERR_FAIL_COND_MSG(err != OK, "Could not remove all templates in '" + templates_dir.plus_file(to_remove) + "'."); - d->change_dir(".."); - d->remove(to_remove); + da->change_dir(".."); + da->remove(to_remove); + ERR_FAIL_COND_MSG(err != OK, "Could not remove templates directory at '" + templates_dir.plus_file(to_remove) + "'."); _update_template_list(); } bool ExportTemplateManager::_install_from_file(const String &p_file, bool p_use_progress) { + // unzClose() will take care of closing the file stored in the unzFile, + // so we don't need to `memdelete(fa)` in this method. FileAccess *fa = NULL; zlib_filefunc_def io = zipio_create_io_from_file(&fa); @@ -251,7 +240,7 @@ bool ExportTemplateManager::_install_from_file(const String &p_file, bool p_use_ String template_path = EditorSettings::get_singleton()->get_templates_dir().plus_file(version); - DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + DirAccessRef d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); Error err = d->make_dir_recursive(template_path); if (err != OK) { EditorNode::get_singleton()->show_warning(TTR("Error creating path for templates:") + "\n" + template_path); @@ -259,8 +248,6 @@ bool ExportTemplateManager::_install_from_file(const String &p_file, bool p_use_ return false; } - memdelete(d); - ret = unzGoToFirstFile(pkg); EditorProgress *p = NULL; @@ -316,7 +303,7 @@ bool ExportTemplateManager::_install_from_file(const String &p_file, bool p_use_ } String to_write = template_path.plus_file(file); - FileAccess *f = FileAccess::open(to_write, FileAccess::WRITE); + FileAccessRef f = FileAccess::open(to_write, FileAccess::WRITE); if (!f) { ret = unzGoToNextFile(pkg); @@ -326,8 +313,6 @@ bool ExportTemplateManager::_install_from_file(const String &p_file, bool p_use_ f->store_buffer(data.ptr(), data.size()); - memdelete(f); - #ifndef WINDOWS_ENABLED FileAccess::set_unix_permissions(to_write, (info.external_fa >> 16) & 0x01FF); #endif @@ -343,7 +328,6 @@ bool ExportTemplateManager::_install_from_file(const String &p_file, bool p_use_ unzClose(pkg); _update_template_list(); - return true; } diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index e3f0021fbc..be05183f92 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -333,10 +333,11 @@ void FileSystemDock::_notification(int p_what) { case NOTIFICATION_DRAG_BEGIN: { Dictionary dd = get_viewport()->gui_get_drag_data(); if (tree->is_visible_in_tree() && dd.has("type")) { - if ((String(dd["type"]) == "files") || (String(dd["type"]) == "files_and_dirs") || (String(dd["type"]) == "resource")) { + if (dd.has("favorite")) { + if ((String(dd["favorite"]) == "all")) + tree->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN); + } else if ((String(dd["type"]) == "files") || (String(dd["type"]) == "files_and_dirs") || (String(dd["type"]) == "resource")) { tree->set_drop_mode_flags(Tree::DROP_MODE_ON_ITEM | Tree::DROP_MODE_INBETWEEN); - } else if ((String(dd["type"]) == "favorite")) { - tree->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN); } } } break; @@ -1839,7 +1840,7 @@ Variant FileSystemDock::get_drag_data_fw(const Point2 &p_point, Control *p_from) all_not_favorites &= !is_favorite; selected = tree->get_next_selected(selected); } - if (all_favorites) { + if (!all_not_favorites) { paths = _tree_get_selected(false); } else { paths = _tree_get_selected(); @@ -1857,12 +1858,9 @@ Variant FileSystemDock::get_drag_data_fw(const Point2 &p_point, Control *p_from) if (paths.empty()) return Variant(); - if (!all_favorites && !all_not_favorites) - return Variant(); - Dictionary drag_data = EditorNode::get_singleton()->drag_files_and_dirs(paths, p_from); - if (all_favorites) { - drag_data["type"] = "favorite"; + if (!all_not_favorites) { + drag_data["favorite"] = all_favorites ? "all" : "mixed"; } return drag_data; } @@ -1870,7 +1868,11 @@ Variant FileSystemDock::get_drag_data_fw(const Point2 &p_point, Control *p_from) bool FileSystemDock::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { Dictionary drag_data = p_data; - if (drag_data.has("type") && String(drag_data["type"]) == "favorite") { + if (drag_data.has("favorite")) { + + if (String(drag_data["favorite"]) != "all") { + return false; + } // Moving favorite around. TreeItem *ti = tree->get_item_at_position(p_point); @@ -1937,7 +1939,11 @@ void FileSystemDock::drop_data_fw(const Point2 &p_point, const Variant &p_data, Vector<String> dirs = EditorSettings::get_singleton()->get_favorites(); - if (drag_data.has("type") && String(drag_data["type"]) == "favorite") { + if (drag_data.has("favorite")) { + + if (String(drag_data["favorite"]) != "all") { + return; + } // Moving favorite around. TreeItem *ti = tree->get_item_at_position(p_point); if (!ti) diff --git a/editor/icons/icon_crypto_key.svg b/editor/icons/icon_crypto_key.svg new file mode 100644 index 0000000000..45b53c815d --- /dev/null +++ b/editor/icons/icon_crypto_key.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 4.233 4.233" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2.397.34-.374.373-.375.374v.375l.188.187-1.497 1.496v.375l.374.374h.374l.187-.188.282-.092.092-.282.282-.093.093-.28.094-.28.28-.095.187-.187.187.187h.374l.375-.375.373-.373.001-.374-1.122-1.122zm.374.858a.264.264 0 1 1 .002.528.264.264 0 0 1 -.002-.528z" fill="#e0e0e0"/></svg>
\ No newline at end of file diff --git a/editor/icons/icon_x509_certificate.svg b/editor/icons/icon_x509_certificate.svg new file mode 100644 index 0000000000..e175fa3234 --- /dev/null +++ b/editor/icons/icon_x509_certificate.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 4.233 4.233" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3.967.263-3.704.001v2.646h1.427a.993.993 0 0 1 -.022-.096.993.993 0 0 1 -.012-.099.993.993 0 0 1 -.002-.07.993.993 0 0 1 .005-.1.993.993 0 0 1 .014-.097.993.993 0 0 1 .025-.096.993.993 0 0 1 .034-.093.993.993 0 0 1 .043-.09.993.993 0 0 1 .052-.085.993.993 0 0 1 .06-.079.993.993 0 0 1 .068-.072.993.993 0 0 1 .074-.066.993.993 0 0 1 .08-.057.993.993 0 0 1 .087-.05.993.993 0 0 1 .09-.04.993.993 0 0 1 .095-.031.993.993 0 0 1 .096-.022.993.993 0 0 1 .099-.012.993.993 0 0 1 .07-.003.993.993 0 0 1 .099.006.993.993 0 0 1 .098.014.993.993 0 0 1 .096.025.993.993 0 0 1 .094.034.993.993 0 0 1 .089.043.993.993 0 0 1 .084.052.993.993 0 0 1 .08.06.993.993 0 0 1 .072.068.993.993 0 0 1 .065.074.993.993 0 0 1 .058.08.993.993 0 0 1 .05.087.993.993 0 0 1 .04.09.993.993 0 0 1 .031.095.993.993 0 0 1 .022.096.993.993 0 0 1 .012.099.993.993 0 0 1 .002.07.993.993 0 0 1 -.004.1.993.993 0 0 1 -.015.097.993.993 0 0 1 -.017.068h.365z" fill="#e0e0e0"/><g fill="#ff8484"><path d="m2.116 3.175v.793l.53-.253.529.253v-.793z"/><circle cx="2.646" cy="2.645" r=".794"/></g></svg>
\ No newline at end of file diff --git a/editor/import/editor_scene_importer_gltf.cpp b/editor/import/editor_scene_importer_gltf.cpp index 49091dc812..fcf0e4af6f 100644 --- a/editor/import/editor_scene_importer_gltf.cpp +++ b/editor/import/editor_scene_importer_gltf.cpp @@ -1973,7 +1973,7 @@ Error EditorSceneImporterGLTF::_reparent_to_fake_joint(GLTFState &state, GLTFSke state.nodes.push_back(fake_joint); // We better not be a joint, or we messed up in our logic - if (node->joint == true) + if (node->joint) return FAILED; fake_joint->translation = node->translation; diff --git a/editor/node_dock.cpp b/editor/node_dock.cpp index d6df3bd369..7ba1796600 100644 --- a/editor/node_dock.cpp +++ b/editor/node_dock.cpp @@ -129,6 +129,7 @@ NodeDock::NodeDock() { select_a_node = memnew(Label); select_a_node->set_text(TTR("Select a single node to edit its signals and groups.")); + select_a_node->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); select_a_node->set_v_size_flags(SIZE_EXPAND_FILL); select_a_node->set_valign(Label::VALIGN_CENTER); select_a_node->set_align(Label::ALIGN_CENTER); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 117b88bc7e..7659363cdf 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -1197,7 +1197,7 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bo //Pan the viewport panning = true; } - } else if (panning) { + } else { if (!k->is_pressed()) { // Stop panning the viewport (for any mouse button press) panning = false; @@ -2274,7 +2274,7 @@ bool CanvasItemEditor::_gui_input_ruler_tool(const Ref<InputEvent> &p_event) { Point2 previous_origin = ruler_tool_origin; if (!ruler_tool_active) - ruler_tool_origin = snap_point(viewport->get_local_mouse_position() / zoom + view_offset) * zoom; + ruler_tool_origin = snap_point(viewport->get_local_mouse_position() / zoom + view_offset); if (b.is_valid() && b->get_button_index() == BUTTON_LEFT) { if (b->is_pressed()) { @@ -2287,9 +2287,7 @@ bool CanvasItemEditor::_gui_input_ruler_tool(const Ref<InputEvent> &p_event) { return true; } - bool is_snap_active = smart_snap_active ^ Input::get_singleton()->is_key_pressed(KEY_CONTROL); - - if (m.is_valid() && (ruler_tool_active || (is_snap_active && previous_origin != ruler_tool_origin))) { + if (m.is_valid() && (ruler_tool_active || (grid_snap_active && previous_origin != ruler_tool_origin))) { viewport->update(); return true; @@ -2687,19 +2685,17 @@ void CanvasItemEditor::_draw_ruler_tool() { if (tool != TOOL_RULER) return; - bool is_snap_active = smart_snap_active ^ Input::get_singleton()->is_key_pressed(KEY_CONTROL); - if (ruler_tool_active) { Color ruler_primary_color = get_color("accent_color", "Editor"); Color ruler_secondary_color = ruler_primary_color; ruler_secondary_color.a = 0.5; - Point2 begin = ruler_tool_origin - view_offset * zoom; + Point2 begin = (ruler_tool_origin - view_offset) * zoom; Point2 end = snap_point(viewport->get_local_mouse_position() / zoom + view_offset) * zoom - view_offset * zoom; Point2 corner = Point2(begin.x, end.y); Vector2 length_vector = (begin - end).abs() / zoom; - bool draw_secondary_lines = (begin.y != corner.y && end.x != corner.x); + bool draw_secondary_lines = !(Math::is_equal_approx(begin.y, corner.y) || Math::is_equal_approx(end.x, corner.x)); viewport->draw_line(begin, end, ruler_primary_color, Math::round(EDSCALE * 3), true); if (draw_secondary_lines) { @@ -2721,8 +2717,10 @@ void CanvasItemEditor::_draw_ruler_tool() { viewport->draw_string(font, text_pos, vformat("%.2f px", length_vector.length()), font_color); if (draw_secondary_lines) { - int horizontal_axis_angle = round(180 * atan2(length_vector.y, length_vector.x) / Math_PI); - int vertictal_axis_angle = 90 - horizontal_axis_angle; + const float horizontal_angle_rad = atan2(length_vector.y, length_vector.x); + const float vertical_angle_rad = Math_PI / 2.0 - horizontal_angle_rad; + const int horizontal_angle = round(180 * horizontal_angle_rad / Math_PI); + const int vertical_angle = round(180 * vertical_angle_rad / Math_PI); Point2 text_pos2 = text_pos; text_pos2.x = begin.x < text_pos.x ? MIN(text_pos.x - text_width, begin.x - text_width / 2) : MAX(text_pos.x + text_width, begin.x - text_width / 2); @@ -2731,7 +2729,7 @@ void CanvasItemEditor::_draw_ruler_tool() { Point2 v_angle_text_pos = Point2(); v_angle_text_pos.x = CLAMP(begin.x - angle_text_width / 2, angle_text_width / 2, viewport->get_rect().size.x - angle_text_width); v_angle_text_pos.y = begin.y < end.y ? MIN(text_pos2.y - 2 * text_height, begin.y - text_height * 0.5) : MAX(text_pos2.y + text_height * 3, begin.y + text_height * 1.5); - viewport->draw_string(font, v_angle_text_pos, vformat("%d deg", vertictal_axis_angle), font_secondary_color); + viewport->draw_string(font, v_angle_text_pos, vformat("%d deg", vertical_angle), font_secondary_color); text_pos2 = text_pos; text_pos2.y = end.y < text_pos.y ? MIN(text_pos.y - text_height * 2, end.y - text_height / 2) : MAX(text_pos.y + text_height * 2, end.y - text_height / 2); @@ -2742,20 +2740,48 @@ void CanvasItemEditor::_draw_ruler_tool() { if (begin.y < end.y) { h_angle_text_pos.y = end.y + text_height * 1.5; if (ABS(text_pos2.x - h_angle_text_pos.x) < text_width) { - int height_multiplier = 1.5 + (int)is_snap_active; + int height_multiplier = 1.5 + (int)grid_snap_active; h_angle_text_pos.y = MAX(text_pos.y + height_multiplier * text_height, MAX(end.y + text_height * 1.5, text_pos2.y + height_multiplier * text_height)); } } else { h_angle_text_pos.y = end.y - text_height * 0.5; if (ABS(text_pos2.x - h_angle_text_pos.x) < text_width) { - int height_multiplier = 1 + (int)is_snap_active; + int height_multiplier = 1 + (int)grid_snap_active; h_angle_text_pos.y = MIN(text_pos.y - height_multiplier * text_height, MIN(end.y - text_height * 0.5, text_pos2.y - height_multiplier * text_height)); } } - viewport->draw_string(font, h_angle_text_pos, vformat("%d deg", horizontal_axis_angle), font_secondary_color); + viewport->draw_string(font, h_angle_text_pos, vformat("%d deg", horizontal_angle), font_secondary_color); + + // Angle arcs + int arc_point_count = 8; + float arc_radius_max_length_percent = 0.1; + float ruler_length = length_vector.length() * zoom; + float arc_max_radius = 50.0; + float arc_line_width = 2.0; + + const Vector2 end_to_begin = (end - begin); + + float arc_1_start_angle = + end_to_begin.x < 0 ? + (end_to_begin.y < 0 ? 3.0 * Math_PI / 2.0 - vertical_angle_rad : Math_PI / 2.0) : + (end_to_begin.y < 0 ? 3.0 * Math_PI / 2.0 : Math_PI / 2.0 - vertical_angle_rad); + float arc_1_end_angle = arc_1_start_angle + vertical_angle_rad; + // Constrain arc to triangle height & max size + float arc_1_radius = MIN(MIN(arc_radius_max_length_percent * ruler_length, ABS(end_to_begin.y)), arc_max_radius); + + float arc_2_start_angle = + end_to_begin.x < 0 ? + (end_to_begin.y < 0 ? 0.0 : -horizontal_angle_rad) : + (end_to_begin.y < 0 ? Math_PI - horizontal_angle_rad : Math_PI); + float arc_2_end_angle = arc_2_start_angle + horizontal_angle_rad; + // Constrain arc to triangle width & max size + float arc_2_radius = MIN(MIN(arc_radius_max_length_percent * ruler_length, ABS(end_to_begin.x)), arc_max_radius); + + viewport->draw_arc(begin, arc_1_radius, arc_1_start_angle, arc_1_end_angle, arc_point_count, ruler_primary_color, Math::round(EDSCALE * arc_line_width)); + viewport->draw_arc(end, arc_2_radius, arc_2_start_angle, arc_2_end_angle, arc_point_count, ruler_primary_color, Math::round(EDSCALE * arc_line_width)); } - if (is_snap_active) { + if (grid_snap_active) { text_pos = (begin + end) / 2 + Vector2(-text_width / 2, text_height / 2); text_pos.x = CLAMP(text_pos.x, text_width / 2, viewport->get_rect().size.x - text_width * 1.5); @@ -2766,20 +2792,20 @@ void CanvasItemEditor::_draw_ruler_tool() { Point2 text_pos2 = text_pos; text_pos2.x = begin.x < text_pos.x ? MIN(text_pos.x - text_width, begin.x - text_width / 2) : MAX(text_pos.x + text_width, begin.x - text_width / 2); - viewport->draw_string(font, text_pos2, vformat("%d units", (int)(length_vector.y / grid_step.y)), font_secondary_color); + viewport->draw_string(font, text_pos2, vformat("%d units", roundf(length_vector.y / grid_step.y)), font_secondary_color); text_pos2 = text_pos; text_pos2.y = end.y < text_pos.y ? MIN(text_pos.y - text_height * 2, end.y + text_height / 2) : MAX(text_pos.y + text_height * 2, end.y + text_height / 2); - viewport->draw_string(font, text_pos2, vformat("%d units", (int)(length_vector.x / grid_step.x)), font_secondary_color); + viewport->draw_string(font, text_pos2, vformat("%d units", roundf(length_vector.x / grid_step.x)), font_secondary_color); } else { viewport->draw_string(font, text_pos, vformat("%d units", roundf((length_vector / grid_step).length())), font_color); } } } else { - if (is_snap_active) { + if (grid_snap_active) { Ref<Texture> position_icon = get_icon("EditorPosition", "EditorIcons"); - viewport->draw_texture(get_icon("EditorPosition", "EditorIcons"), ruler_tool_origin - view_offset * zoom - position_icon->get_size() / 2); + viewport->draw_texture(get_icon("EditorPosition", "EditorIcons"), (ruler_tool_origin - view_offset) * zoom - position_icon->get_size() / 2); } } } @@ -4894,7 +4920,7 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) { if (state.has("grid_snap_active")) { grid_snap_active = state["grid_snap_active"]; - grid_snap_button->set_pressed(smart_snap_active); + grid_snap_button->set_pressed(grid_snap_active); } if (state.has("snap_node_parent")) { diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index ecb2354aa1..5bccb36252 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -1186,7 +1186,7 @@ void ScriptTextEditor::_edit_option(int p_op) { if (expression.parse(line) == OK) { Variant result = expression.execute(Array(), Variant(), false); if (expression.get_error_text() == "") { - results.append(whitespace + (String)result); + results.append(whitespace + result.get_construct_string()); } else { results.append(line); } diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index 095350d0e1..1d8fd38858 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -2049,12 +2049,11 @@ void SpatialEditorViewport::_update_freelook(real_t delta) { return; } - Vector3 forward = camera->get_transform().basis.xform(Vector3(0, 0, -1)); - Vector3 right = camera->get_transform().basis.xform(Vector3(1, 0, 0)); - Vector3 up = camera->get_transform().basis.xform(Vector3(0, 1, 0)); + const Vector3 forward = camera->get_transform().basis.xform(Vector3(0, 0, -1)); + const Vector3 right = camera->get_transform().basis.xform(Vector3(1, 0, 0)); + const Vector3 up = camera->get_transform().basis.xform(Vector3(0, 1, 0)); Vector3 direction; - bool speed_modifier = false; if (is_shortcut_pressed("spatial_editor/freelook_left")) { direction -= right; @@ -2074,17 +2073,17 @@ void SpatialEditorViewport::_update_freelook(real_t delta) { if (is_shortcut_pressed("spatial_editor/freelook_down")) { direction -= up; } - if (is_shortcut_pressed("spatial_editor/freelook_speed_modifier")) { - speed_modifier = true; - } real_t speed = freelook_speed; - if (speed_modifier) { - real_t modifier_speed_factor = EditorSettings::get_singleton()->get("editors/3d/freelook/freelook_modifier_speed_factor"); - speed *= modifier_speed_factor; + + if (is_shortcut_pressed("spatial_editor/freelook_speed_modifier")) { + speed *= 3.0; + } + if (is_shortcut_pressed("spatial_editor/freelook_slow_modifier")) { + speed *= 0.333333; } - Vector3 motion = direction * speed * delta; + const Vector3 motion = direction * speed * delta; cursor.pos += motion; cursor.eye_pos += motion; } @@ -3588,6 +3587,7 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed ED_SHORTCUT("spatial_editor/freelook_up", TTR("Freelook Up"), KEY_E); ED_SHORTCUT("spatial_editor/freelook_down", TTR("Freelook Down"), KEY_Q); ED_SHORTCUT("spatial_editor/freelook_speed_modifier", TTR("Freelook Speed Modifier"), KEY_SHIFT); + ED_SHORTCUT("spatial_editor/freelook_slow_modifier", TTR("Freelook Slow Modifier"), KEY_ALT); preview_camera = memnew(CheckBox); preview_camera->set_text(TTR("Preview")); diff --git a/editor/plugins/style_box_editor_plugin.cpp b/editor/plugins/style_box_editor_plugin.cpp index defc0a40ea..c4a9803ff4 100644 --- a/editor/plugins/style_box_editor_plugin.cpp +++ b/editor/plugins/style_box_editor_plugin.cpp @@ -64,21 +64,24 @@ void StyleBoxPreview::edit(const Ref<StyleBox> &p_stylebox) { void StyleBoxPreview::_sb_changed() { preview->update(); +} + +void StyleBoxPreview::_redraw() { if (stylebox.is_valid()) { - Size2 ms = stylebox->get_minimum_size() * 4 / 3; - ms.height = MAX(ms.height, 150 * EDSCALE); - preview->set_custom_minimum_size(ms); + preview->draw_style_box(stylebox, preview->get_rect()); } } void StyleBoxPreview::_bind_methods() { ClassDB::bind_method("_sb_changed", &StyleBoxPreview::_sb_changed); + ClassDB::bind_method("_redraw", &StyleBoxPreview::_redraw); } StyleBoxPreview::StyleBoxPreview() { - - preview = memnew(Panel); + preview = memnew(Control); + preview->set_custom_minimum_size(Size2(0, 150 * EDSCALE)); + preview->connect("draw", this, "_redraw"); add_margin_child(TTR("Preview:"), preview); } diff --git a/editor/plugins/style_box_editor_plugin.h b/editor/plugins/style_box_editor_plugin.h index d31a28b3e4..fead8e0de8 100644 --- a/editor/plugins/style_box_editor_plugin.h +++ b/editor/plugins/style_box_editor_plugin.h @@ -41,10 +41,11 @@ class StyleBoxPreview : public VBoxContainer { GDCLASS(StyleBoxPreview, VBoxContainer); - Panel *preview; + Control *preview; Ref<StyleBox> stylebox; void _sb_changed(); + void _redraw(); protected: static void _bind_methods(); diff --git a/editor/project_export.cpp b/editor/project_export.cpp index 7456396460..adcbddfb04 100644 --- a/editor/project_export.cpp +++ b/editor/project_export.cpp @@ -286,11 +286,13 @@ void ProjectExportDialog::_edit_preset(int p_index) { export_templates_error->hide(); export_button->set_disabled(true); + get_ok()->set_disabled(true); } else { export_error->hide(); export_templates_error->hide(); export_button->set_disabled(false); + get_ok()->set_disabled(false); } custom_features->set_text(current->get_custom_features()); @@ -623,6 +625,7 @@ void ProjectExportDialog::_delete_preset_confirm() { int idx = presets->get_current(); _edit_preset(-1); export_button->set_disabled(true); + get_ok()->set_disabled(true); EditorExport::get_singleton()->remove_export_preset(idx); _update_presets(); } @@ -1249,6 +1252,7 @@ ProjectExportDialog::ProjectExportDialog() { export_button->connect("pressed", this, "_export_project"); // Disable initially before we select a valid preset export_button->set_disabled(true); + get_ok()->set_disabled(true); export_all_dialog = memnew(ConfirmationDialog); add_child(export_all_dialog); diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index d903e153a7..ab62a59be1 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -1023,6 +1023,7 @@ public: ProjectList(); ~ProjectList(); + void update_dock_menu(); void load_projects(); void set_search_term(String p_search_term); void set_order_option(ProjectListFilter::FilterOption p_option); @@ -1210,7 +1211,6 @@ void ProjectList::load_projects() { _projects.clear(); _last_clicked = ""; _selected_project_keys.clear(); - OS::get_singleton()->global_menu_clear("_dock"); // Load data // TODO Would be nice to change how projects and favourites are stored... it complicates things a bit. @@ -1248,14 +1248,38 @@ void ProjectList::load_projects() { create_project_item_control(i); } - OS::get_singleton()->global_menu_add_separator("_dock"); - OS::get_singleton()->global_menu_add_item("_dock", TTR("New Window"), GLOBAL_NEW_WINDOW, Variant()); - sort_projects(); set_v_scroll(0); update_icons_async(); + + update_dock_menu(); +} + +void ProjectList::update_dock_menu() { + OS::get_singleton()->global_menu_clear("_dock"); + + int favs_added = 0; + int total_added = 0; + for (int i = 0; i < _projects.size(); ++i) { + if (!_projects[i].grayed && !_projects[i].missing) { + if (_projects[i].favorite) { + favs_added++; + } else { + if (favs_added != 0) { + OS::get_singleton()->global_menu_add_separator("_dock"); + } + favs_added = 0; + } + OS::get_singleton()->global_menu_add_item("_dock", _projects[i].project_name + " ( " + _projects[i].path + " )", GLOBAL_OPEN_PROJECT, Variant(_projects[i].path.plus_file("project.godot"))); + total_added++; + } + } + if (total_added != 0) { + OS::get_singleton()->global_menu_add_separator("_dock"); + } + OS::get_singleton()->global_menu_add_item("_dock", TTR("New Window"), GLOBAL_NEW_WINDOW, Variant()); } void ProjectList::create_project_item_control(int p_index) { @@ -1341,7 +1365,6 @@ void ProjectList::create_project_item_control(int p_index) { fpath->set_clip_text(true); _scroll_children->add_child(hb); - OS::get_singleton()->global_menu_add_item("_dock", item.project_name + " ( " + item.path + " )", GLOBAL_OPEN_PROJECT, Variant(item.path.plus_file("project.godot"))); item.control = hb; } @@ -1394,6 +1417,8 @@ void ProjectList::sort_projects() { // Rewind the coroutine because order of projects changed update_icons_async(); + + update_dock_menu(); } const Set<String> &ProjectList::get_selected_project_keys() const { @@ -1470,6 +1495,8 @@ void ProjectList::remove_project(int p_index, bool p_update_settings) { EditorSettings::get_singleton()->erase("favorite_projects/" + item.project_key); // Not actually saving the file, in case you are doing more changes to settings } + + update_dock_menu(); } bool ProjectList::is_any_project_missing() const { @@ -1568,6 +1595,7 @@ int ProjectList::refresh_project(const String &dir_path) { ensure_project_visible(i); } load_project_icon(i); + index = i; break; } @@ -1642,6 +1670,8 @@ void ProjectList::erase_selected_projects() { _selected_project_keys.clear(); _last_clicked = ""; + + update_dock_menu(); } // Draws selected project highlight @@ -1725,6 +1755,8 @@ void ProjectList::_favorite_pressed(Node *p_hb) { } } } + + update_dock_menu(); } void ProjectList::_show_project(const String &p_path) { @@ -1929,6 +1961,8 @@ void ProjectManager::_on_projects_updated() { if (index != -1) { _project_list->ensure_project_visible(index); } + + _project_list->update_dock_menu(); } void ProjectManager::_on_project_created(const String &dir) { @@ -1937,6 +1971,8 @@ void ProjectManager::_on_project_created(const String &dir) { _project_list->select_project(i); _project_list->ensure_project_visible(i); _open_selected_projects_ask(); + + _project_list->update_dock_menu(); } void ProjectManager::_confirm_update_settings() { diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index 35187214a6..803c806028 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -1109,6 +1109,8 @@ void ProjectSettingsEditor::drop_data_fw(const Point2 &p_point, const Variant &p TreeItem *selected = input_editor->get_selected(); TreeItem *item = input_editor->get_item_at_position(p_point); + if (!item) + return; TreeItem *target = item->get_parent() == input_editor->get_root() ? item : item->get_parent(); String selected_name = "input/" + selected->get_text(0); diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp index ecb272876d..ce82d44164 100644 --- a/editor/property_editor.cpp +++ b/editor/property_editor.cpp @@ -246,7 +246,13 @@ void CustomPropertyEditor::_menu_option(int p_which) { case OBJ_MENU_NEW_SCRIPT: { if (Object::cast_to<Node>(owner)) - EditorNode::get_singleton()->get_scene_tree_dock()->open_script_dialog(Object::cast_to<Node>(owner)); + EditorNode::get_singleton()->get_scene_tree_dock()->open_script_dialog(Object::cast_to<Node>(owner), false); + + } break; + case OBJ_MENU_EXTEND_SCRIPT: { + + if (Object::cast_to<Node>(owner)) + EditorNode::get_singleton()->get_scene_tree_dock()->open_script_dialog(Object::cast_to<Node>(owner), true); } break; case OBJ_MENU_SHOW_IN_FILE_SYSTEM: { diff --git a/editor/property_editor.h b/editor/property_editor.h index 029c2211d5..b1c61c5e25 100644 --- a/editor/property_editor.h +++ b/editor/property_editor.h @@ -77,7 +77,8 @@ class CustomPropertyEditor : public Popup { OBJ_MENU_COPY = 4, OBJ_MENU_PASTE = 5, OBJ_MENU_NEW_SCRIPT = 6, - OBJ_MENU_SHOW_IN_FILE_SYSTEM = 7, + OBJ_MENU_EXTEND_SCRIPT = 7, + OBJ_MENU_SHOW_IN_FILE_SYSTEM = 8, TYPE_BASE_ID = 100, CONVERT_BASE_ID = 1000 }; diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 98ab1bfb54..0884620e5d 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -421,53 +421,11 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { create_dialog->popup_create(false, true, selected->get_class()); } break; + case TOOL_EXTEND_SCRIPT: { + attach_script_to_selected(true); + } break; case TOOL_ATTACH_SCRIPT: { - - if (!profile_allow_script_editing) { - break; - } - - List<Node *> selection = editor_selection->get_selected_node_list(); - if (selection.empty()) - break; - - Node *selected = scene_tree->get_selected(); - if (!selected) - selected = selection.front()->get(); - - Ref<Script> existing = selected->get_script(); - - String path = selected->get_filename(); - if (path == "") { - String root_path = editor_data->get_edited_scene_root()->get_filename(); - if (root_path == "") { - path = String("res://").plus_file(selected->get_name()); - } else { - path = root_path.get_base_dir().plus_file(selected->get_name()); - } - } - - String inherits = selected->get_class(); - if (existing.is_valid()) { - for (int i = 0; i < ScriptServer::get_language_count(); i++) { - ScriptLanguage *l = ScriptServer::get_language(i); - if (l->get_type() == existing->get_class()) { - String name = l->get_global_class_name(existing->get_path()); - if (ScriptServer::is_global_class(name) && EDITOR_GET("interface/editors/derive_script_globals_by_name").operator bool()) { - inherits = name; - } else if (l->can_inherit_from_file()) { - inherits = "\"" + existing->get_path() + "\""; - } - break; - } - } - } - script_create_dialog->connect("script_created", this, "_script_created"); - script_create_dialog->connect("popup_hide", this, "_script_creation_closed"); - script_create_dialog->set_inheritance_base_type("Node"); - script_create_dialog->config(inherits, path); - script_create_dialog->popup_centered(); - + attach_script_to_selected(false); } break; case TOOL_CLEAR_SCRIPT: { @@ -2482,10 +2440,9 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { if (profile_allow_script_editing) { if (selection.size() == 1) { - if (!existing_script.is_valid()) { - menu->add_icon_shortcut(get_icon("ScriptCreate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/attach_script"), TOOL_ATTACH_SCRIPT); - } else { - menu->add_icon_shortcut(get_icon("ScriptExtend", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/extend_script"), TOOL_ATTACH_SCRIPT); + menu->add_icon_shortcut(get_icon("ScriptCreate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/attach_script"), TOOL_ATTACH_SCRIPT); + if (existing_script.is_valid()) { + menu->add_icon_shortcut(get_icon("ScriptExtend", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/extend_script"), TOOL_EXTEND_SCRIPT); } } if (selection.size() > 1 || (existing_script.is_valid() && exisiting_script_removable)) { @@ -2595,10 +2552,64 @@ void SceneTreeDock::_focus_node() { } } -void SceneTreeDock::open_script_dialog(Node *p_for_node) { +void SceneTreeDock::attach_script_to_selected(bool p_extend) { + if (!profile_allow_script_editing) { + return; + } + + List<Node *> selection = editor_selection->get_selected_node_list(); + if (selection.empty()) + return; + + Node *selected = scene_tree->get_selected(); + if (!selected) + selected = selection.front()->get(); + + Ref<Script> existing = selected->get_script(); + + String path = selected->get_filename(); + if (path == "") { + String root_path = editor_data->get_edited_scene_root()->get_filename(); + if (root_path == "") { + path = String("res://").plus_file(selected->get_name()); + } else { + path = root_path.get_base_dir().plus_file(selected->get_name()); + } + } + + String inherits = selected->get_class(); + + if (p_extend && existing.is_valid()) { + for (int i = 0; i < ScriptServer::get_language_count(); i++) { + ScriptLanguage *l = ScriptServer::get_language(i); + if (l->get_type() == existing->get_class()) { + String name = l->get_global_class_name(existing->get_path()); + if (ScriptServer::is_global_class(name) && EDITOR_GET("interface/editors/derive_script_globals_by_name").operator bool()) { + inherits = name; + } else if (l->can_inherit_from_file()) { + inherits = "\"" + existing->get_path() + "\""; + } + break; + } + } + } + + script_create_dialog->connect("script_created", this, "_script_created"); + script_create_dialog->connect("popup_hide", this, "_script_creation_closed"); + script_create_dialog->set_inheritance_base_type("Node"); + script_create_dialog->config(inherits, path); + script_create_dialog->popup_centered(); +} + +void SceneTreeDock::open_script_dialog(Node *p_for_node, bool p_extend) { scene_tree->set_selected(p_for_node, false); - _tool_selected(TOOL_ATTACH_SCRIPT); + + if (p_extend) { + _tool_selected(TOOL_EXTEND_SCRIPT); + } else { + _tool_selected(TOOL_ATTACH_SCRIPT); + } } void SceneTreeDock::add_remote_tree_editor(Control *p_remote) { diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index 014ce58e88..4e78b84c53 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -64,6 +64,7 @@ class SceneTreeDock : public VBoxContainer { TOOL_RENAME, TOOL_BATCH_RENAME, TOOL_REPLACE, + TOOL_EXTEND_SCRIPT, TOOL_ATTACH_SCRIPT, TOOL_CLEAR_SCRIPT, TOOL_MOVE_UP, @@ -259,7 +260,8 @@ public: void replace_node(Node *p_node, Node *p_by_node, bool p_keep_properties = true, bool p_remove_old = true); - void open_script_dialog(Node *p_for_node); + void attach_script_to_selected(bool p_extend); + void open_script_dialog(Node *p_for_node, bool p_extend); ScriptCreateDialog *get_script_create_dialog() { return script_create_dialog; } diff --git a/editor/script_editor_debugger.cpp b/editor/script_editor_debugger.cpp index 89d275a90b..ccee38422c 100644 --- a/editor/script_editor_debugger.cpp +++ b/editor/script_editor_debugger.cpp @@ -597,7 +597,19 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da if (var.is_zero()) { var = RES(); } else if (var.get_type() == Variant::STRING) { - var = ResourceLoader::load(var); + String path = var; + if (path.find("::") != -1) { + // built-in resource + String base_path = path.get_slice("::", 0); + if (ResourceLoader::get_resource_type(base_path) == "PackedScene") { + if (!EditorNode::get_singleton()->is_scene_open(base_path)) { + EditorNode::get_singleton()->load_scene(base_path); + } + } else { + EditorNode::get_singleton()->load_resource(base_path); + } + } + var = ResourceLoader::load(path); if (pinfo.hint_string == "Script") debugObj->set_script(var); diff --git a/main/main.cpp b/main/main.cpp index 42c90fc027..fe0f5a0215 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1856,9 +1856,7 @@ bool Main::start() { // Hide console window if requested (Windows-only). bool hide_console = EditorSettings::get_singleton()->get_setting("interface/editor/hide_console_window"); OS::get_singleton()->set_console_visible(!hide_console); - } - if (project_manager || editor) { // Load SSL Certificates from Editor Settings (or builtin) Crypto::load_default_certificates(EditorSettings::get_singleton()->get_setting("network/ssl/editor_ssl_certificates").operator String()); } diff --git a/misc/travis/android-tools-linux.sh b/misc/travis/android-tools-linux.sh index fb6e2f0f9b..215b9fd159 100755 --- a/misc/travis/android-tools-linux.sh +++ b/misc/travis/android-tools-linux.sh @@ -24,12 +24,12 @@ ANDROID_SDK_URL=$ANDROID_BASE_URL/$ANDROID_SDK_FILENAME ANDROID_SDK_PATH=$GODOT_BUILD_TOOLS_PATH/$ANDROID_SDK_DIR ANDROID_SDK_SHA256=92ffee5a1d98d856634e8b71132e8a95d96c83a63fde1099be3d86df3106def9 -ANDROID_NDK_RELEASE=r18 +ANDROID_NDK_RELEASE=r20 ANDROID_NDK_DIR=android-ndk ANDROID_NDK_FILENAME=android-ndk-$ANDROID_NDK_RELEASE-linux-x86_64.zip ANDROID_NDK_URL=$ANDROID_BASE_URL/$ANDROID_NDK_FILENAME ANDROID_NDK_PATH=$GODOT_BUILD_TOOLS_PATH/$ANDROID_NDK_DIR -ANDROID_NDK_SHA1=2ac2e8e1ef73ed551cac3a1479bb28bd49369212 +ANDROID_NDK_SHA1=8665fc84a1b1f0d6ab3b5fdd1e30200cc7b9adff echo echo "Download and install Android development tools ..." @@ -75,7 +75,7 @@ mkdir -p ~/.android && echo "count=0" > ~/.android/repositories.cfg yes | $ANDROID_SDK_DIR/tools/bin/sdkmanager --licenses > /dev/null yes | $ANDROID_SDK_DIR/tools/bin/sdkmanager 'tools' > /dev/null yes | $ANDROID_SDK_DIR/tools/bin/sdkmanager 'platform-tools' > /dev/null -yes | $ANDROID_SDK_DIR/tools/bin/sdkmanager 'build-tools;28.0.1' > /dev/null +yes | $ANDROID_SDK_DIR/tools/bin/sdkmanager 'build-tools;28.0.3' > /dev/null echo EXPORT_VAL="export ANDROID_HOME=$ANDROID_SDK_PATH" diff --git a/misc/travis/android-tools-osx.sh b/misc/travis/android-tools-osx.sh deleted file mode 100755 index 96125a3a3f..0000000000 --- a/misc/travis/android-tools-osx.sh +++ /dev/null @@ -1,107 +0,0 @@ -#!/bin/bash - -# SDK -# https://dl.google.com/android/repository/sdk-tools-darwin-3859397.zip -# SHA-256 4a81754a760fce88cba74d69c364b05b31c53d57b26f9f82355c61d5fe4b9df9 -# latest version available here: https://developer.android.com/studio/index.html - -# NDK -# https://dl.google.com/android/repository/android-ndk-r15c-darwin-x86_64.zip -# SHA-1 ea4b5d76475db84745aa8828000d009625fc1f98 -# latest version available here: https://developer.android.com/ndk/downloads/index.html - -BASH_RC=~/.bashrc -GODOT_BUILD_TOOLS_PATH=./godot-dev/build-tools -mkdir -p $GODOT_BUILD_TOOLS_PATH -cd $GODOT_BUILD_TOOLS_PATH - -ANDROID_BASE_URL=http://dl.google.com/android/repository - -ANDROID_SDK_RELEASE=3859397 -ANDROID_SDK_DIR=android-sdk -ANDROID_SDK_FILENAME=sdk-tools-darwin-$ANDROID_SDK_RELEASE.zip -ANDROID_SDK_URL=$ANDROID_BASE_URL/$ANDROID_SDK_FILENAME -ANDROID_SDK_PATH=$GODOT_BUILD_TOOLS_PATH/$ANDROID_SDK_DIR -ANDROID_SDK_SHA256=4a81754a760fce88cba74d69c364b05b31c53d57b26f9f82355c61d5fe4b9df9 - -ANDROID_NDK_RELEASE=r15c -ANDROID_NDK_DIR=android-ndk -ANDROID_NDK_FILENAME=android-ndk-$ANDROID_NDK_RELEASE-darwin-x86_64.zip -ANDROID_NDK_URL=$ANDROID_BASE_URL/$ANDROID_NDK_FILENAME -ANDROID_NDK_PATH=$GODOT_BUILD_TOOLS_PATH/$ANDROID_NDK_DIR -ANDROID_NDK_SHA1=ea4b5d76475db84745aa8828000d009625fc1f98 - -echo -echo "Download and install Android development tools ..." -echo - -if [ ! -e $ANDROID_SDK_FILENAME ]; then - echo "Downloading: Android SDK ..." - curl -L -O $ANDROID_SDK_URL -else - echo $ANDROID_SDK_SHA1 $ANDROID_SDK_FILENAME > $ANDROID_SDK_FILENAME.sha1 - if [ $(shasum -a 256 < $ANDROID_SDK_FILENAME | awk '{print $1;}') != $ANDROID_SDK_SHA1 ]; then - echo "Downloading: Android SDK ..." - curl -L -O $ANDROID_SDK_URL - fi -fi - -if [ ! -d $ANDROID_SDK_DIR ]; then - echo "Extracting: Android SDK ..." - mkdir -p $ANDROID_SDK_DIR && tar -xf $ANDROID_SDK_FILENAME -C $ANDROID_SDK_DIR - echo -fi - -if [ ! -e $ANDROID_NDK_FILENAME ]; then - echo "Downloading: Android NDK ..." - curl -L -O $ANDROID_NDK_URL -else - echo $ANDROID_NDK_MD5 $ANDROID_NDK_FILENAME > $ANDROID_NDK_FILENAME.md5 - if [ $(shasum -a 1 < $ANDROID_NDK_FILENAME | awk '{print $1;}') != $ANDROID_NDK_SHA1 ]; then - echo "Downloading: Android NDK ..." - curl -L -O $ANDROID_NDK_URL - fi -fi - -if [ ! -d $ANDROID_NDK_DIR ]; then - echo "Extracting: Android NDK ..." - tar -xf $ANDROID_NDK_FILENAME - mv android-ndk-$ANDROID_NDK_RELEASE $ANDROID_NDK_DIR - echo -fi - -echo "Installing: Android Tools ..." -#$ANDROID_SDK_DIR/tools/bin/sdkmanager --all -yes | $ANDROID_SDK_DIR/tools/bin/sdkmanager --licenses > /dev/null -$ANDROID_SDK_DIR/tools/bin/sdkmanager 'tools' > /dev/null -$ANDROID_SDK_DIR/tools/bin/sdkmanager 'platform-tools' > /dev/null -$ANDROID_SDK_DIR/tools/bin/sdkmanager 'build-tools;26.0.2' > /dev/null -echo - -EXPORT_VAL="export ANDROID_HOME=$ANDROID_SDK_PATH" -if ! grep -q "^$EXPORT_VAL" $BASH_RC; then - echo $EXPORT_VAL >> $BASH_RC -fi -#eval $EXPORT_VAL - -EXPORT_VAL="export ANDROID_NDK_ROOT=$ANDROID_NDK_PATH" -if ! grep -q "^$EXPORT_VAL" $BASH_RC; then - echo $EXPORT_VAL >> $BASH_RC -fi -#eval $EXPORT_VAL - -EXPORT_VAL="export PATH=$PATH:$ANDROID_SDK_PATH/tools" -if ! grep -q "^export PATH=.*$ANDROID_SDK_PATH/tools.*" $BASH_RC; then - echo $EXPORT_VAL >> $BASH_RC -fi -#eval $EXPORT_VAL - -EXPORT_VAL="export PATH=$PATH:$ANDROID_SDK_PATH/tools/bin" -if ! grep -q "^export PATH=.*$ANDROID_SDK_PATH/tools/bin.*" $BASH_RC; then - echo $EXPORT_VAL >> $BASH_RC -fi -#eval $EXPORT_VAL - -echo -echo "Done!" -echo diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp index 89f3064d7a..f29c4cb9ca 100644 --- a/modules/bullet/rigid_body_bullet.cpp +++ b/modules/bullet/rigid_body_bullet.cpp @@ -473,7 +473,7 @@ void RigidBodyBullet::assert_no_constraints() { void RigidBodyBullet::set_activation_state(bool p_active) { if (p_active) { - btBody->setActivationState(ACTIVE_TAG); + btBody->activate(); } else { btBody->setActivationState(WANTS_DEACTIVATION); } diff --git a/modules/freetype/SCsub b/modules/freetype/SCsub index b47377cbc4..8f4a8de895 100644 --- a/modules/freetype/SCsub +++ b/modules/freetype/SCsub @@ -66,7 +66,7 @@ if env['builtin_freetype']: env.Prepend(CPPPATH=[thirdparty_dir + "/include"]) env_freetype.Append(CPPDEFINES=['FT2_BUILD_LIBRARY', 'FT_CONFIG_OPTION_USE_PNG']) - if (env['target'] != 'release'): + if (env['target'] == 'debug'): env_freetype.Append(CPPDEFINES=['ZLIB_DEBUG']) # Also requires libpng headers diff --git a/modules/gdnative/videodecoder/video_stream_gdnative.cpp b/modules/gdnative/videodecoder/video_stream_gdnative.cpp index f3c34fd5e0..14b7f9a2ef 100644 --- a/modules/gdnative/videodecoder/video_stream_gdnative.cpp +++ b/modules/gdnative/videodecoder/video_stream_gdnative.cpp @@ -279,7 +279,7 @@ void VideoStreamPlaybackGDNative::set_paused(bool p_paused) { paused = p_paused; } -Ref<Texture> VideoStreamPlaybackGDNative::get_texture() { +Ref<Texture> VideoStreamPlaybackGDNative::get_texture() const { return texture; } diff --git a/modules/gdnative/videodecoder/video_stream_gdnative.h b/modules/gdnative/videodecoder/video_stream_gdnative.h index 9aed1fd2a0..5ff7acb616 100644 --- a/modules/gdnative/videodecoder/video_stream_gdnative.h +++ b/modules/gdnative/videodecoder/video_stream_gdnative.h @@ -168,7 +168,7 @@ public: //virtual int mix(int16_t* p_buffer,int p_frames)=0; - virtual Ref<Texture> get_texture(); + virtual Ref<Texture> get_texture() const; virtual void update(float p_delta); virtual void set_mix_callback(AudioMixCallback p_callback, void *p_userdata); diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 9b3bf8ad5b..1d82735328 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -1944,11 +1944,10 @@ static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_MEMBER); r_result.insert(option.display, option); } - } else { - for (const Set<StringName>::Element *E = script->get_members().front(); E; E = E->next()) { - ScriptCodeCompletionOption option(E->get().operator String(), ScriptCodeCompletionOption::KIND_MEMBER); - r_result.insert(option.display, option); - } + } + for (const Set<StringName>::Element *E = script->get_members().front(); E; E = E->next()) { + ScriptCodeCompletionOption option(E->get().operator String(), ScriptCodeCompletionOption::KIND_MEMBER); + r_result.insert(option.display, option); } } if (!p_only_functions) { diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 857bcbb706..9d229adb2a 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -865,11 +865,23 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s if (current_function) { int arg_idx = current_function->arguments.find(identifier); if (arg_idx != -1) { - if (tokenizer->get_token() == GDScriptTokenizer::TK_OP_ASSIGN) { - // Assignment is not really usage - current_function->arguments_usage.write[arg_idx] = current_function->arguments_usage[arg_idx] - 1; - } else { - current_function->arguments_usage.write[arg_idx] = current_function->arguments_usage[arg_idx] + 1; + switch (tokenizer->get_token()) { + case GDScriptTokenizer::TK_OP_ASSIGN_ADD: + case GDScriptTokenizer::TK_OP_ASSIGN_BIT_AND: + case GDScriptTokenizer::TK_OP_ASSIGN_BIT_OR: + case GDScriptTokenizer::TK_OP_ASSIGN_BIT_XOR: + case GDScriptTokenizer::TK_OP_ASSIGN_DIV: + case GDScriptTokenizer::TK_OP_ASSIGN_MOD: + case GDScriptTokenizer::TK_OP_ASSIGN_MUL: + case GDScriptTokenizer::TK_OP_ASSIGN_SHIFT_LEFT: + case GDScriptTokenizer::TK_OP_ASSIGN_SHIFT_RIGHT: + case GDScriptTokenizer::TK_OP_ASSIGN_SUB: + case GDScriptTokenizer::TK_OP_ASSIGN: { + // Assignment is not really usage + } break; + default: { + current_function->arguments_usage.write[arg_idx] = current_function->arguments_usage[arg_idx] + 1; + } } } } @@ -2239,9 +2251,7 @@ void GDScriptParser::_parse_pattern_block(BlockNode *p_block, Vector<PatternBran break; // go back a level } - if (pending_newline != -1) { - pending_newline = -1; - } + pending_newline = -1; PatternBranchNode *branch = alloc_node<PatternBranchNode>(); branch->body = alloc_node<BlockNode>(); @@ -6135,12 +6145,18 @@ bool GDScriptParser::_is_type_compatible(const DataType &p_container, const Data break; } + // Some classes are prefixed with `_` internally + if (!ClassDB::class_exists(expr_native)) { + expr_native = "_" + expr_native; + } + switch (p_container.kind) { case DataType::NATIVE: { if (p_container.is_meta_type) { return ClassDB::is_parent_class(expr_native, GDScriptNativeClass::get_class_static()); } else { - return ClassDB::is_parent_class(expr_native, p_container.native_type); + StringName container_native = ClassDB::class_exists(p_container.native_type) ? p_container.native_type : StringName("_" + p_container.native_type); + return ClassDB::is_parent_class(expr_native, container_native); } } break; case DataType::SCRIPT: diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index c2e2c4c3c9..23a86f8d2b 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -823,12 +823,8 @@ void GDScriptTokenizerText::_advance() { _make_error("Unterminated String"); return; } - if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) { - _make_error("Malformed hex constant in string"); - return; - } - CharType v; + CharType v = 0; if (c >= '0' && c <= '9') { v = c - '0'; } else if (c >= 'a' && c <= 'f') { @@ -838,8 +834,8 @@ void GDScriptTokenizerText::_advance() { v = c - 'A'; v += 10; } else { - ERR_PRINT("BUG"); - v = 0; + _make_error("Malformed hex constant in string"); + return; } res <<= 4; diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp index cf360b5291..a048af88bb 100644 --- a/modules/gdscript/language_server/lsp.hpp +++ b/modules/gdscript/language_server/lsp.hpp @@ -496,7 +496,7 @@ struct TextDocumentSyncOptions { dict["willSave"] = willSave; dict["openClose"] = openClose; dict["change"] = change; - dict["change"] = save.to_json(); + dict["save"] = save.to_json(); return dict; } }; diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index c97524a54d..804c57fc6e 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -40,11 +40,8 @@ void GridMapEditor::_node_removed(Node *p_node) { - if (p_node == node) { + if (p_node == node) node = NULL; - hide(); - mesh_library_palette->hide(); - } } void GridMapEditor::_configure() { @@ -353,7 +350,14 @@ void GridMapEditor::_set_selection(bool p_active, const Vector3 &p_begin, const selection.click = p_begin; selection.current = p_end; - _update_selection_transform(); + if (is_visible_in_tree()) { + _update_selection_transform(); + } + + options->get_popup()->set_item_disabled(options->get_popup()->get_item_index(MENU_OPTION_SELECTION_CLEAR), !selection.active); + options->get_popup()->set_item_disabled(options->get_popup()->get_item_index(MENU_OPTION_SELECTION_CUT), !selection.active); + options->get_popup()->set_item_disabled(options->get_popup()->get_item_index(MENU_OPTION_SELECTION_DUPLICATE), !selection.active); + options->get_popup()->set_item_disabled(options->get_popup()->get_item_index(MENU_OPTION_SELECTION_FILL), !selection.active); } bool GridMapEditor::do_input_action(Camera *p_camera, const Point2 &p_point, bool p_click) { @@ -1465,7 +1469,7 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) { } } - selection.active = false; + _set_selection(false); updating = false; accumulated_floor_delta = 0.0; } diff --git a/modules/mono/glue/Managed/Files/Plane.cs b/modules/mono/glue/Managed/Files/Plane.cs index a13161d2e6..26e717e089 100644 --- a/modules/mono/glue/Managed/Files/Plane.cs +++ b/modules/mono/glue/Managed/Files/Plane.cs @@ -82,12 +82,12 @@ namespace Godot return Mathf.Abs(dist) <= epsilon; } - public Vector3 Intersect3(Plane b, Plane c) + public Vector3? Intersect3(Plane b, Plane c) { real_t denom = _normal.Cross(b._normal).Dot(c._normal); - if (Mathf.Abs(denom) <= Mathf.Epsilon) - return new Vector3(); + if (Mathf.IsZeroApprox(denom)) + return null; Vector3 result = b._normal.Cross(c._normal) * D + c._normal.Cross(_normal) * b.D + @@ -96,34 +96,35 @@ namespace Godot return result / denom; } - public Vector3 IntersectRay(Vector3 from, Vector3 dir) + public Vector3? IntersectRay(Vector3 from, Vector3 dir) { real_t den = _normal.Dot(dir); - if (Mathf.Abs(den) <= Mathf.Epsilon) - return new Vector3(); + if (Mathf.IsZeroApprox(den)) + return null; real_t dist = (_normal.Dot(from) - D) / den; // This is a ray, before the emitting pos (from) does not exist if (dist > Mathf.Epsilon) - return new Vector3(); + return null; return from + dir * -dist; } - public Vector3 IntersectSegment(Vector3 begin, Vector3 end) + public Vector3? IntersectSegment(Vector3 begin, Vector3 end) { Vector3 segment = begin - end; real_t den = _normal.Dot(segment); - if (Mathf.Abs(den) <= Mathf.Epsilon) - return new Vector3(); + if (Mathf.IsZeroApprox(den)) + return null; real_t dist = (_normal.Dot(begin) - D) / den; + // Only allow dist to be in the range of 0 to 1, with tolerance. if (dist < -Mathf.Epsilon || dist > 1.0f + Mathf.Epsilon) - return new Vector3(); + return null; return begin + segment * -dist; } diff --git a/modules/recast/navigation_mesh_generator.cpp b/modules/recast/navigation_mesh_generator.cpp index c5b60f2dca..320591cf7c 100644 --- a/modules/recast/navigation_mesh_generator.cpp +++ b/modules/recast/navigation_mesh_generator.cpp @@ -131,7 +131,7 @@ void EditorNavigationMeshGenerator::_add_faces(const PoolVector3Array &p_faces, } } -void EditorNavigationMeshGenerator::_parse_geometry(Transform p_accumulated_transform, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices, int p_generate_from, uint32_t p_collision_mask) { +void EditorNavigationMeshGenerator::_parse_geometry(Transform p_accumulated_transform, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices, int p_generate_from, uint32_t p_collision_mask, bool p_recurse_children) { if (Object::cast_to<MeshInstance>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) { @@ -263,8 +263,10 @@ void EditorNavigationMeshGenerator::_parse_geometry(Transform p_accumulated_tran p_accumulated_transform = p_accumulated_transform * spatial->get_transform(); } - for (int i = 0; i < p_node->get_child_count(); i++) { - _parse_geometry(p_accumulated_transform, p_node->get_child(i), p_verticies, p_indices, p_generate_from, p_collision_mask); + if (p_recurse_children) { + for (int i = 0; i < p_node->get_child_count(); i++) { + _parse_geometry(p_accumulated_transform, p_node->get_child(i), p_verticies, p_indices, p_generate_from, p_collision_mask, p_recurse_children); + } } } @@ -439,7 +441,21 @@ void EditorNavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p Vector<float> vertices; Vector<int> indices; - _parse_geometry(Object::cast_to<Spatial>(p_node)->get_transform().affine_inverse(), p_node, vertices, indices, p_nav_mesh->get_parsed_geometry_type(), p_nav_mesh->get_collision_mask()); + List<Node *> parse_nodes; + + if (p_nav_mesh->get_source_geometry_mode() == NavigationMesh::SOURCE_GEOMETRY_NAVMESH_CHILDREN) { + parse_nodes.push_back(p_node); + } else { + p_node->get_tree()->get_nodes_in_group(p_nav_mesh->get_source_group_name(), &parse_nodes); + } + + Transform navmesh_xform = Object::cast_to<Spatial>(p_node)->get_transform().affine_inverse(); + for (const List<Node *>::Element *E = parse_nodes.front(); E; E = E->next()) { + int geometry_type = p_nav_mesh->get_parsed_geometry_type(); + uint32_t collision_mask = p_nav_mesh->get_collision_mask(); + bool recurse_children = p_nav_mesh->get_source_geometry_mode() != NavigationMesh::SOURCE_GEOMETRY_GROUPS_EXPLICIT; + _parse_geometry(navmesh_xform, E->get(), vertices, indices, geometry_type, collision_mask, recurse_children); + } if (vertices.size() > 0 && indices.size() > 0) { diff --git a/modules/recast/navigation_mesh_generator.h b/modules/recast/navigation_mesh_generator.h index 30a6e3c835..f19622a4a9 100644 --- a/modules/recast/navigation_mesh_generator.h +++ b/modules/recast/navigation_mesh_generator.h @@ -47,7 +47,7 @@ protected: static void _add_vertex(const Vector3 &p_vec3, Vector<float> &p_verticies); static void _add_mesh(const Ref<Mesh> &p_mesh, const Transform &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices); static void _add_faces(const PoolVector3Array &p_faces, const Transform &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices); - static void _parse_geometry(Transform p_accumulated_transform, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices, int p_generate_from, uint32_t p_collision_mask); + static void _parse_geometry(Transform p_accumulated_transform, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices, int p_generate_from, uint32_t p_collision_mask, bool p_recurse_children); static void _convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref<NavigationMesh> p_nav_mesh); static void _build_recast_navigation_mesh(Ref<NavigationMesh> p_nav_mesh, EditorProgress *ep, diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp index 28a8b77283..ed1a7f682b 100644 --- a/modules/theora/video_stream_theora.cpp +++ b/modules/theora/video_stream_theora.cpp @@ -368,7 +368,7 @@ float VideoStreamPlaybackTheora::get_time() const { return time - AudioServer::get_singleton()->get_output_latency() - delay_compensation; //-((get_total())/(float)vi.rate); }; -Ref<Texture> VideoStreamPlaybackTheora::get_texture() { +Ref<Texture> VideoStreamPlaybackTheora::get_texture() const { return texture; } diff --git a/modules/theora/video_stream_theora.h b/modules/theora/video_stream_theora.h index 0c37d33358..b241722cd1 100644 --- a/modules/theora/video_stream_theora.h +++ b/modules/theora/video_stream_theora.h @@ -147,7 +147,7 @@ public: void set_file(const String &p_file); - virtual Ref<Texture> get_texture(); + virtual Ref<Texture> get_texture() const; virtual void update(float p_delta); virtual void set_mix_callback(AudioMixCallback p_callback, void *p_userdata); diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index 093901ad07..c1a4c58620 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -1916,8 +1916,6 @@ bool VisualScriptEditor::can_drop_data_fw(const Point2 &p_point, const Variant & return false; } -#ifdef TOOLS_ENABLED - static Node *_find_script_node(Node *p_edited_scene, Node *p_current_node, const Ref<Script> &script) { if (p_edited_scene != p_current_node && p_current_node->get_owner() != p_edited_scene) @@ -1937,8 +1935,6 @@ static Node *_find_script_node(Node *p_edited_scene, Node *p_current_node, const return NULL; } -#endif - void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) { if (p_from != graph) { @@ -2456,12 +2452,8 @@ Ref<Texture> VisualScriptEditor::get_icon() { } bool VisualScriptEditor::is_unsaved() { -#ifdef TOOLS_ENABLED return script->is_edited() || script->are_subnodes_edited(); -#else - return false; -#endif } Variant VisualScriptEditor::get_edit_state() { diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h index 5a00469eea..fbf021e7b6 100644 --- a/modules/visual_script/visual_script_editor.h +++ b/modules/visual_script/visual_script_editor.h @@ -228,7 +228,7 @@ class VisualScriptEditor : public ScriptEditorBase { void _update_node_size(int p_id); void _port_name_focus_out(const Node *p_name_box, int p_id, int p_port, bool is_input); - Vector2 _get_available_pos(bool centered = true, Vector2 pos = Vector2()) const; + Vector2 _get_available_pos(bool centered = true, Vector2 ofs = Vector2()) const; StringName _get_function_of_node(int p_id) const; void _move_nodes_with_rescan(const StringName &p_func_from, const StringName &p_func_to, int p_id); diff --git a/modules/visual_script/visual_script_property_selector.h b/modules/visual_script/visual_script_property_selector.h index 405949e8c1..3a7c8de6a2 100644 --- a/modules/visual_script/visual_script_property_selector.h +++ b/modules/visual_script/visual_script_property_selector.h @@ -45,7 +45,7 @@ class VisualScriptPropertySelector : public ConfirmationDialog { void create_visualscript_item(const String &name, TreeItem *const root, const String &search_input, const String &text); - void get_visual_node_names(const String &root_filter, const Set<String> &filter, bool &found, TreeItem *const root, LineEdit *const search_box); + void get_visual_node_names(const String &root_filter, const Set<String> &p_modifiers, bool &found, TreeItem *const root, LineEdit *const search_box); void _sbox_input(const Ref<InputEvent> &p_ie); diff --git a/modules/webm/video_stream_webm.cpp b/modules/webm/video_stream_webm.cpp index fa3602ad27..4ce0db3746 100644 --- a/modules/webm/video_stream_webm.cpp +++ b/modules/webm/video_stream_webm.cpp @@ -230,7 +230,7 @@ void VideoStreamPlaybackWebm::set_audio_track(int p_idx) { audio_track = p_idx; } -Ref<Texture> VideoStreamPlaybackWebm::get_texture() { +Ref<Texture> VideoStreamPlaybackWebm::get_texture() const { return texture; } diff --git a/modules/webm/video_stream_webm.h b/modules/webm/video_stream_webm.h index ddcbb1eb08..4f79d46cce 100644 --- a/modules/webm/video_stream_webm.h +++ b/modules/webm/video_stream_webm.h @@ -90,7 +90,7 @@ public: virtual void set_audio_track(int p_idx); - virtual Ref<Texture> get_texture(); + virtual Ref<Texture> get_texture() const; virtual void update(float p_delta); virtual void set_mix_callback(AudioMixCallback p_callback, void *p_userdata); diff --git a/modules/websocket/emws_client.cpp b/modules/websocket/emws_client.cpp index fad766ea5d..983db60d5e 100644 --- a/modules/websocket/emws_client.cpp +++ b/modules/websocket/emws_client.cpp @@ -64,9 +64,15 @@ EMSCRIPTEN_KEEPALIVE void _esws_on_close(void *obj, int code, char *reason, int } } -Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const PoolVector<String> p_protocols, const Vector<String> p_custom_headers) { +Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocols, const Vector<String> p_custom_headers) { + + String proto_string; + for (int i = 0; i < p_protocols.size(); i++) { + if (i != 0) + proto_string += ","; + proto_string += p_protocols[i]; + } - String proto_string = p_protocols.join(","); String str = "ws://"; if (p_custom_headers.size()) { diff --git a/modules/websocket/emws_client.h b/modules/websocket/emws_client.h index 2d35f7f0f6..67705891b2 100644 --- a/modules/websocket/emws_client.h +++ b/modules/websocket/emws_client.h @@ -50,7 +50,7 @@ public: bool _is_connecting; Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets); - Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const PoolVector<String> p_protocol = PoolVector<String>(), const Dictionary p_custom_headers = Dictionary()); + Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocol = Vector<String>(), const Vector<String> p_custom_headers = Vector<String>()); Ref<WebSocketPeer> get_peer(int p_peer_id) const; void disconnect_from_host(int p_code = 1000, String p_reason = ""); IP_Address get_connected_host() const; diff --git a/modules/websocket/emws_server.cpp b/modules/websocket/emws_server.cpp index c4bb459ad0..9a6a30d613 100644 --- a/modules/websocket/emws_server.cpp +++ b/modules/websocket/emws_server.cpp @@ -33,7 +33,7 @@ #include "emws_server.h" #include "core/os/os.h" -Error EMWSServer::listen(int p_port, PoolVector<String> p_protocols, bool gd_mp_api) { +Error EMWSServer::listen(int p_port, Vector<String> p_protocols, bool gd_mp_api) { return FAILED; } diff --git a/modules/websocket/emws_server.h b/modules/websocket/emws_server.h index a5e5b4090e..e8da8c26b4 100644 --- a/modules/websocket/emws_server.h +++ b/modules/websocket/emws_server.h @@ -43,7 +43,7 @@ class EMWSServer : public WebSocketServer { public: Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets); - Error listen(int p_port, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false); + Error listen(int p_port, Vector<String> p_protocols = Vector<String>(), bool gd_mp_api = false); void stop(); bool is_listening() const; bool has_peer(int p_id) const; diff --git a/modules/websocket/wsl_client.cpp b/modules/websocket/wsl_client.cpp index a422f65cfc..ad70c9c0e1 100644 --- a/modules/websocket/wsl_client.cpp +++ b/modules/websocket/wsl_client.cpp @@ -181,8 +181,12 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port, _connection = _tcp; _use_ssl = p_ssl; _host = p_host; - _protocols.clear(); - _protocols.append_array(p_protocols); + // Strip edges from protocols. + _protocols.resize(p_protocols.size()); + String *pw = _protocols.ptrw(); + for (int i = 0; i < p_protocols.size(); i++) { + pw[i] = p_protocols[i].strip_edges(); + } _key = WSLPeer::generate_key(); // TODO custom extra headers (allow overriding this too?) diff --git a/modules/websocket/wsl_server.cpp b/modules/websocket/wsl_server.cpp index 993dceafb9..2181775b99 100644 --- a/modules/websocket/wsl_server.cpp +++ b/modules/websocket/wsl_server.cpp @@ -80,11 +80,12 @@ bool WSLServer::PendingPeer::_parse_request(const Vector<String> p_protocols) { if (headers.has("sec-websocket-protocol")) { Vector<String> protos = headers["sec-websocket-protocol"].split(","); for (int i = 0; i < protos.size(); i++) { + String proto = protos[i].strip_edges(); // Check if we have the given protocol for (int j = 0; j < p_protocols.size(); j++) { - if (protos[i] != p_protocols[j]) + if (proto != p_protocols[j]) continue; - protocol = protos[i]; + protocol = proto; break; } // Found a protocol @@ -158,7 +159,12 @@ Error WSLServer::listen(int p_port, const Vector<String> p_protocols, bool gd_mp ERR_FAIL_COND_V(is_listening(), ERR_ALREADY_IN_USE); _is_multiplayer = gd_mp_api; - _protocols.append_array(p_protocols); + // Strip edges from protocols. + _protocols.resize(p_protocols.size()); + String *pw = _protocols.ptrw(); + for (int i = 0; i < p_protocols.size(); i++) { + pw[i] = p_protocols[i].strip_edges(); + } _server->listen(p_port); return OK; diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index a43f195b84..6d021ad33a 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -1345,7 +1345,7 @@ public: return logo; } - virtual bool poll_devices() { + virtual bool poll_export() { bool dc = devices_changed; if (dc) { @@ -1355,7 +1355,7 @@ public: return dc; } - virtual int get_device_count() const { + virtual int get_options_count() const { device_lock->lock(); int dc = devices.size(); @@ -1364,20 +1364,31 @@ public: return dc; } - virtual String get_device_name(int p_device) const { + virtual String get_options_tooltip() const { - ERR_FAIL_INDEX_V(p_device, devices.size(), ""); + return TTR("Select device from the list"); + } + + virtual String get_option_label(int p_index) const { + + ERR_FAIL_INDEX_V(p_index, devices.size(), ""); device_lock->lock(); - String s = devices[p_device].name; + String s = devices[p_index].name; device_lock->unlock(); return s; } - virtual String get_device_info(int p_device) const { + virtual String get_option_tooltip(int p_index) const { - ERR_FAIL_INDEX_V(p_device, devices.size(), ""); + ERR_FAIL_INDEX_V(p_index, devices.size(), ""); device_lock->lock(); - String s = devices[p_device].description; + String s = devices[p_index].description; + if (devices.size() == 1) { + // Tooltip will be: + // Name + // Description + s = devices[p_index].name + "\n\n" + s; + } device_lock->unlock(); return s; } diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java index 739aa285bf..4dae2dcc53 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java @@ -30,7 +30,6 @@ package org.godotengine.godot; -import android.Manifest; import android.annotation.SuppressLint; import android.app.Activity; import android.app.ActivityManager; @@ -56,12 +55,14 @@ import android.hardware.SensorManager; import android.os.Build; import android.os.Bundle; import android.os.Environment; +import android.os.Handler; +import android.os.Looper; import android.os.Messenger; import android.os.VibrationEffect; import android.os.Vibrator; import android.provider.Settings.Secure; import android.support.annotation.Keep; -import android.support.v4.content.ContextCompat; +import android.support.annotation.Nullable; import android.view.Display; import android.view.KeyEvent; import android.view.MotionEvent; @@ -95,14 +96,12 @@ import java.util.Locale; import javax.microedition.khronos.opengles.GL10; import org.godotengine.godot.input.GodotEditText; import org.godotengine.godot.payments.PaymentsManager; +import org.godotengine.godot.utils.PermissionsUtil; import org.godotengine.godot.xr.XRMode; public abstract class Godot extends Activity implements SensorEventListener, IDownloaderClient { static final int MAX_SINGLETONS = 64; - static final int REQUEST_RECORD_AUDIO_PERMISSION = 1; - static final int REQUEST_CAMERA_PERMISSION = 2; - static final int REQUEST_VIBRATE_PERMISSION = 3; private IStub mDownloaderClientStub; private TextView mStatusText; private TextView mProgressFraction; @@ -126,6 +125,9 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo private boolean activityResumed; private int mState; + // Used to dispatch events to the main thread. + private final Handler mainThreadHandler = new Handler(Looper.getMainLooper()); + static private Intent mCurrentIntent; @Override @@ -187,6 +189,20 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo Godot.singletons[Godot.singleton_count++] = this; } + /** + * Invoked once during the Godot Android initialization process after creation of the + * {@link GodotView} view. + * <p> + * This method should be overridden by descendants of this class that would like to add + * their view/layout to the Godot view hierarchy. + * + * @return the view to be included; null if no views should be included. + */ + @Nullable + protected View onMainCreateView(Activity activity) { + return null; + } + protected void onMainActivityResult(int requestCode, int resultCode, Intent data) { } @@ -306,6 +322,20 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo public void run() { GodotLib.setup(current_command_line); setKeepScreenOn("True".equals(GodotLib.getGlobal("display/window/energy_saving/keep_screen_on"))); + + // The Godot Android plugins are setup on completion of GodotLib.setup + mainThreadHandler.post(new Runnable() { + @Override + public void run() { + // Include the non-null views returned in the Godot view hierarchy. + for (int i = 0; i < singleton_count; i++) { + View view = singletons[i].onMainCreateView(Godot.this); + if (view != null) { + layout.addView(view); + } + } + } + }); } }); } @@ -973,32 +1003,15 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo } public boolean requestPermission(String p_name) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { - // Not necessary, asked on install already - return true; - } - - if (p_name.equals("RECORD_AUDIO")) { - if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { - requestPermissions(new String[] { Manifest.permission.RECORD_AUDIO }, REQUEST_RECORD_AUDIO_PERMISSION); - return false; - } - } + return PermissionsUtil.requestPermission(p_name, this); + } - if (p_name.equals("CAMERA")) { - if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { - requestPermissions(new String[] { Manifest.permission.CAMERA }, REQUEST_CAMERA_PERMISSION); - return false; - } - } + public boolean requestPermissions() { + return PermissionsUtil.requestManifestPermissions(this); + } - if (p_name.equals("VIBRATE")) { - if (ContextCompat.checkSelfPermission(this, Manifest.permission.VIBRATE) != PackageManager.PERMISSION_GRANTED) { - requestPermissions(new String[] { Manifest.permission.VIBRATE }, REQUEST_VIBRATE_PERMISSION); - return false; - } - } - return true; + public String[] getGrantedPermissions() { + return PermissionsUtil.getGrantedPermissions(this); } /** diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java b/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java new file mode 100644 index 0000000000..2c4a444e5a --- /dev/null +++ b/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java @@ -0,0 +1,157 @@ +package org.godotengine.godot.utils; + +import android.Manifest; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PermissionInfo; +import android.os.Build; +import android.support.v4.content.ContextCompat; +import java.util.ArrayList; +import java.util.List; +import org.godotengine.godot.Godot; + +/** + * This class includes utility functions for Android permissions related operations. + * @author Cagdas Caglak <cagdascaglak@gmail.com> + */ +public final class PermissionsUtil { + + static final int REQUEST_RECORD_AUDIO_PERMISSION = 1; + static final int REQUEST_CAMERA_PERMISSION = 2; + static final int REQUEST_VIBRATE_PERMISSION = 3; + static final int REQUEST_ALL_PERMISSION_REQ_CODE = 1001; + + private PermissionsUtil() { + } + + /** + * Request a dangerous permission. name must be specified in <a href="https://github.com/aosp-mirror/platform_frameworks_base/blob/master/core/res/AndroidManifest.xml">this</a> + * @param name the name of the requested permission. + * @param activity the caller activity for this method. + * @return true/false. "true" if permission was granted otherwise returns "false". + */ + public static boolean requestPermission(String name, Godot activity) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + // Not necessary, asked on install already + return true; + } + + if (name.equals("RECORD_AUDIO") && ContextCompat.checkSelfPermission(activity, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { + activity.requestPermissions(new String[] { Manifest.permission.RECORD_AUDIO }, REQUEST_RECORD_AUDIO_PERMISSION); + return false; + } + + if (name.equals("CAMERA") && ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { + activity.requestPermissions(new String[] { Manifest.permission.CAMERA }, REQUEST_CAMERA_PERMISSION); + return false; + } + + if (name.equals("VIBRATE") && ContextCompat.checkSelfPermission(activity, Manifest.permission.VIBRATE) != PackageManager.PERMISSION_GRANTED) { + activity.requestPermissions(new String[] { Manifest.permission.VIBRATE }, REQUEST_VIBRATE_PERMISSION); + return false; + } + return true; + } + + /** + * Request dangerous permissions which are defined in the Android manifest file from the user. + * @param activity the caller activity for this method. + * @return true/false. "true" if all permissions were granted otherwise returns "false". + */ + public static boolean requestManifestPermissions(Godot activity) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + return true; + } + + String[] manifestPermissions; + try { + manifestPermissions = getManifestPermissions(activity); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + return false; + } + + if (manifestPermissions == null || manifestPermissions.length == 0) + return true; + + List<String> dangerousPermissions = new ArrayList<>(); + for (String manifestPermission : manifestPermissions) { + try { + PermissionInfo permissionInfo = getPermissionInfo(activity, manifestPermission); + int protectionLevel = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P ? permissionInfo.getProtection() : permissionInfo.protectionLevel; + if (protectionLevel == PermissionInfo.PROTECTION_DANGEROUS && ContextCompat.checkSelfPermission(activity, manifestPermission) != PackageManager.PERMISSION_GRANTED) { + dangerousPermissions.add(manifestPermission); + } + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + return false; + } + } + + if (dangerousPermissions.isEmpty()) { + // If list is empty, all of dangerous permissions were granted. + return true; + } + + String[] requestedPermissions = dangerousPermissions.toArray(new String[0]); + activity.requestPermissions(requestedPermissions, REQUEST_ALL_PERMISSION_REQ_CODE); + return false; + } + + /** + * With this function you can get the list of dangerous permissions that have been granted to the Android application. + * @param activity the caller activity for this method. + * @return granted permissions list + */ + public static String[] getGrantedPermissions(Godot activity) { + String[] manifestPermissions; + try { + manifestPermissions = getManifestPermissions(activity); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + return new String[0]; + } + if (manifestPermissions == null || manifestPermissions.length == 0) + return new String[0]; + + List<String> dangerousPermissions = new ArrayList<>(); + for (String manifestPermission : manifestPermissions) { + try { + PermissionInfo permissionInfo = getPermissionInfo(activity, manifestPermission); + int protectionLevel = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P ? permissionInfo.getProtection() : permissionInfo.protectionLevel; + if (protectionLevel == PermissionInfo.PROTECTION_DANGEROUS && ContextCompat.checkSelfPermission(activity, manifestPermission) == PackageManager.PERMISSION_GRANTED) { + dangerousPermissions.add(manifestPermission); + } + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + return new String[0]; + } + } + + return dangerousPermissions.toArray(new String[0]); + } + + /** + * Returns the permissions defined in the AndroidManifest.xml file. + * @param activity the caller activity for this method. + * @return manifest permissions list + * @throws PackageManager.NameNotFoundException the exception is thrown when a given package, application, or component name cannot be found. + */ + private static String[] getManifestPermissions(Godot activity) throws PackageManager.NameNotFoundException { + PackageManager packageManager = activity.getPackageManager(); + PackageInfo packageInfo = packageManager.getPackageInfo(activity.getPackageName(), PackageManager.GET_PERMISSIONS); + return packageInfo.requestedPermissions; + } + + /** + * Returns the information of the desired permission. + * @param activity the caller activity for this method. + * @param permission the name of the permission. + * @return permission info object + * @throws PackageManager.NameNotFoundException the exception is thrown when a given package, application, or component name cannot be found. + */ + private static PermissionInfo getPermissionInfo(Godot activity, String permission) throws PackageManager.NameNotFoundException { + PackageManager packageManager = activity.getPackageManager(); + return packageManager.getPermissionInfo(permission, 0); + } +} diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index 7daea19961..a14e0a1960 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -1393,6 +1393,10 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResu if (permission == "android.permission.RECORD_AUDIO" && p_result) { AudioDriver::get_singleton()->capture_start(); } + + if (os_android->get_main_loop()) { + os_android->get_main_loop()->emit_signal("on_request_permissions_result", permission, p_result == JNI_TRUE); + } } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererResumed(JNIEnv *env, jclass clazz) { diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp index 8194ee6ecf..e3e613d30b 100644 --- a/platform/android/java_godot_wrapper.cpp +++ b/platform/android/java_godot_wrapper.cpp @@ -59,6 +59,8 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance) { _get_clipboard = p_env->GetMethodID(cls, "getClipboard", "()Ljava/lang/String;"); _set_clipboard = p_env->GetMethodID(cls, "setClipboard", "(Ljava/lang/String;)V"); _request_permission = p_env->GetMethodID(cls, "requestPermission", "(Ljava/lang/String;)Z"); + _request_permissions = p_env->GetMethodID(cls, "requestPermissions", "()Z"); + _get_granted_permissions = p_env->GetMethodID(cls, "getGrantedPermissions", "()[Ljava/lang/String;"); _init_input_devices = p_env->GetMethodID(cls, "initInputDevices", "()V"); _get_surface = p_env->GetMethodID(cls, "getSurface", "()Landroid/view/Surface;"); _is_activity_resumed = p_env->GetMethodID(cls, "isActivityResumed", "()Z"); @@ -199,6 +201,34 @@ bool GodotJavaWrapper::request_permission(const String &p_name) { } } +bool GodotJavaWrapper::request_permissions() { + if (_request_permissions) { + JNIEnv *env = ThreadAndroid::get_env(); + return env->CallBooleanMethod(godot_instance, _request_permissions); + } else { + return false; + } +} + +Vector<String> GodotJavaWrapper::get_granted_permissions() const { + Vector<String> permissions_list; + if (_get_granted_permissions) { + JNIEnv *env = ThreadAndroid::get_env(); + jobject permissions_object = env->CallObjectMethod(godot_instance, _get_granted_permissions); + jobjectArray *arr = reinterpret_cast<jobjectArray *>(&permissions_object); + + int i = 0; + jsize len = env->GetArrayLength(*arr); + for (i = 0; i < len; i++) { + jstring jstr = (jstring)env->GetObjectArrayElement(*arr, i); + String str = jstring_to_string(jstr, env); + permissions_list.push_back(str); + env->DeleteLocalRef(jstr); + } + } + return permissions_list; +} + void GodotJavaWrapper::init_input_devices() { if (_init_input_devices) { JNIEnv *env = ThreadAndroid::get_env(); diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h index b1bd9b7f48..d23ff273cb 100644 --- a/platform/android/java_godot_wrapper.h +++ b/platform/android/java_godot_wrapper.h @@ -54,6 +54,8 @@ private: jmethodID _get_clipboard = 0; jmethodID _set_clipboard = 0; jmethodID _request_permission = 0; + jmethodID _request_permissions = 0; + jmethodID _get_granted_permissions = 0; jmethodID _init_input_devices = 0; jmethodID _get_surface = 0; jmethodID _is_activity_resumed = 0; @@ -81,6 +83,8 @@ public: bool has_set_clipboard(); void set_clipboard(const String &p_text); bool request_permission(const String &p_name); + bool request_permissions(); + Vector<String> get_granted_permissions() const; void init_input_devices(); jobject get_surface(); bool is_activity_resumed(); diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index 91bd6cbdd2..defee8f1f1 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -220,6 +220,16 @@ bool OS_Android::request_permission(const String &p_name) { return godot_java->request_permission(p_name); } +bool OS_Android::request_permissions() { + + return godot_java->request_permissions(); +} + +Vector<String> OS_Android::get_granted_permissions() const { + + return godot_java->get_granted_permissions(); +} + Error OS_Android::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) { p_library_handle = dlopen(p_path.utf8().get_data(), RTLD_NOW); ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + p_path + ", error: " + dlerror() + "."); diff --git a/platform/android/os_android.h b/platform/android/os_android.h index 9bad9b2e01..a290c0cedd 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -125,6 +125,8 @@ public: virtual void alert(const String &p_alert, const String &p_title = "ALERT!"); virtual bool request_permission(const String &p_name); + virtual bool request_permissions(); + virtual Vector<String> get_granted_permissions() const; virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false); diff --git a/platform/iphone/gl_view.h b/platform/iphone/gl_view.h index 4fb721f159..e3c9d212f0 100644 --- a/platform/iphone/gl_view.h +++ b/platform/iphone/gl_view.h @@ -79,8 +79,6 @@ @property(strong, nonatomic) AVPlayer *avPlayer; @property(strong, nonatomic) AVPlayerLayer *avPlayerLayer; -// Old videoplayer properties -@property(strong, nonatomic) MPMoviePlayerController *moviePlayerController; @property(strong, nonatomic) UIWindow *backgroundWindow; @property(nonatomic) UITextAutocorrectionType autocorrectionType; diff --git a/platform/iphone/gl_view.mm b/platform/iphone/gl_view.mm index dfca2e3dd7..2ac158e6d3 100644 --- a/platform/iphone/gl_view.mm +++ b/platform/iphone/gl_view.mm @@ -729,40 +729,4 @@ static void clear_touches() { _stop_video(); } -/* -- (void)moviePlayBackDidFinish:(NSNotification*)notification { - - - NSNumber* reason = [[notification userInfo] objectForKey:MPMoviePlayerPlaybackDidFinishReasonUserInfoKey]; - switch ([reason intValue]) { - case MPMovieFinishReasonPlaybackEnded: - //NSLog(@"Playback Ended"); - break; - case MPMovieFinishReasonPlaybackError: - //NSLog(@"Playback Error"); - video_found_error = true; - break; - case MPMovieFinishReasonUserExited: - //NSLog(@"User Exited"); - video_found_error = true; - break; - default: - //NSLog(@"Unsupported reason!"); - break; - } - - MPMoviePlayerController *player = [notification object]; - - [[NSNotificationCenter defaultCenter] - removeObserver:self - name:MPMoviePlayerPlaybackDidFinishNotification - object:player]; - - [_instance.moviePlayerController stop]; - [_instance.moviePlayerController.view removeFromSuperview]; - - video_playing = false; -} -*/ - @end diff --git a/platform/iphone/godot_iphone.cpp b/platform/iphone/godot_iphone.cpp index db93db5021..992da2818b 100644 --- a/platform/iphone/godot_iphone.cpp +++ b/platform/iphone/godot_iphone.cpp @@ -47,7 +47,7 @@ int iphone_main(int, int, int, char **, String); int iphone_main(int width, int height, int argc, char **argv, String data_dir) { - int len = strlen(argv[0]); + size_t len = strlen(argv[0]); while (len--) { if (argv[0][len] == '/') break; diff --git a/platform/iphone/in_app_store.mm b/platform/iphone/in_app_store.mm index 490e84c571..8eef430621 100644 --- a/platform/iphone/in_app_store.mm +++ b/platform/iphone/in_app_store.mm @@ -92,7 +92,7 @@ void InAppStore::_bind_methods() { PoolStringArray localized_prices; PoolStringArray currency_codes; - for (int i = 0; i < [products count]; i++) { + for (NSUInteger i = 0; i < [products count]; i++) { SKProduct *product = [products objectAtIndex:i]; diff --git a/platform/iphone/ios.h b/platform/iphone/ios.h index 91c4725b35..ef45fc7ac3 100644 --- a/platform/iphone/ios.h +++ b/platform/iphone/ios.h @@ -42,6 +42,7 @@ class iOS : public Object { public: static void alert(const char *p_alert, const char *p_title); + String get_model() const; String get_rate_url(int p_app_id) const; iOS(); diff --git a/platform/iphone/ios.mm b/platform/iphone/ios.mm index 686422ceb2..6e986df493 100644 --- a/platform/iphone/ios.mm +++ b/platform/iphone/ios.mm @@ -29,6 +29,7 @@ /*************************************************************************/ #include "ios.h" +#include <sys/sysctl.h> #import <UIKit/UIKit.h> @@ -42,6 +43,21 @@ void iOS::alert(const char *p_alert, const char *p_title) { [alert show]; } +String iOS::get_model() const { + // [[UIDevice currentDevice] model] only returns "iPad" or "iPhone". + size_t size; + sysctlbyname("hw.machine", NULL, &size, NULL, 0); + char *model = (char *)malloc(size); + if (model == NULL) { + return ""; + } + sysctlbyname("hw.machine", model, &size, NULL, 0); + NSString *platform = [NSString stringWithCString:model encoding:NSUTF8StringEncoding]; + free(model); + const char *str = [platform UTF8String]; + return String(str != NULL ? str : ""); +} + String iOS::get_rate_url(int p_app_id) const { String templ = "itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=APP_ID"; String templ_iOS7 = "itms-apps://itunes.apple.com/app/idAPP_ID"; diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp index 353078c51c..83b0660ef7 100644 --- a/platform/iphone/os_iphone.cpp +++ b/platform/iphone/os_iphone.cpp @@ -47,8 +47,6 @@ #include "semaphore_iphone.h" -#include "ios.h" - #include <dlfcn.h> int OSIPhone::get_video_driver_count() const { @@ -184,7 +182,8 @@ Error OSIPhone::initialize(const VideoMode &p_desired, int p_video_driver, int p Engine::get_singleton()->add_singleton(Engine::Singleton("ICloud", icloud)); //icloud->connect(); #endif - Engine::get_singleton()->add_singleton(Engine::Singleton("iOS", memnew(iOS))); + ios = memnew(iOS); + Engine::get_singleton()->add_singleton(Engine::Singleton("iOS", ios)); return OK; }; @@ -507,6 +506,15 @@ String OSIPhone::get_name() const { return "iOS"; }; +String OSIPhone::get_model_name() const { + + String model = ios->get_model(); + if (model != "") + return model; + + return OS_Unix::get_model_name(); +} + Size2 OSIPhone::get_window_size() const { return Vector2(video_mode.width, video_mode.height); diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h index 804ba0b1af..63799bbae8 100644 --- a/platform/iphone/os_iphone.h +++ b/platform/iphone/os_iphone.h @@ -41,6 +41,7 @@ #include "game_center.h" #include "icloud.h" #include "in_app_store.h" +#include "ios.h" #include "main/input_default.h" #include "servers/audio_server.h" #include "servers/visual/rasterizer.h" @@ -72,6 +73,7 @@ private: #ifdef ICLOUD_ENABLED ICloud *icloud; #endif + iOS *ios; MainLoop *main_loop; @@ -178,6 +180,7 @@ public: void set_data_dir(String p_dir); virtual String get_name() const; + virtual String get_model_name() const; Error shell_open(String p_uri); diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp index 4de98f7039..f3e8d6911a 100644 --- a/platform/javascript/export/export.cpp +++ b/platform/javascript/export/export.cpp @@ -28,6 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#include "core/io/tcp_server.h" #include "core/io/zip_io.h" #include "editor/editor_export.h" #include "editor/editor_node.h" @@ -38,16 +39,153 @@ #define EXPORT_TEMPLATE_WEBASSEMBLY_RELEASE "webassembly_release.zip" #define EXPORT_TEMPLATE_WEBASSEMBLY_DEBUG "webassembly_debug.zip" +class EditorHTTPServer : public Reference { + +private: + Ref<TCP_Server> server; + Ref<StreamPeerTCP> connection; + uint64_t time; + uint8_t req_buf[4096]; + int req_pos; + + void _clear_client() { + connection = Ref<StreamPeerTCP>(); + memset(req_buf, 0, sizeof(req_buf)); + time = 0; + req_pos = 0; + } + +public: + EditorHTTPServer() { + server.instance(); + stop(); + } + + void stop() { + server->stop(); + _clear_client(); + } + + Error listen(int p_port, IP_Address p_address) { + return server->listen(p_port, p_address); + } + + bool is_listening() const { + return server->is_listening(); + } + + void _send_response() { + Vector<String> psa = String((char *)req_buf).split("\r\n"); + int len = psa.size(); + ERR_FAIL_COND_MSG(len < 4, "Not enough response headers, got: " + itos(len) + ", expected >= 4."); + + Vector<String> req = psa[0].split(" ", false); + ERR_FAIL_COND_MSG(req.size() < 2, "Invalid protocol or status code."); + + // Wrong protocol + ERR_FAIL_COND_MSG(req[0] != "GET" || req[2] != "HTTP/1.1", "Invalid method or HTTP version."); + + String filepath = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmp_js_export"); + String basereq = "/tmp_js_export"; + if (req[1] == basereq + ".html") { + filepath += ".html"; + } else if (req[1] == basereq + ".js") { + filepath += ".js"; + } else if (req[1] == basereq + ".pck") { + filepath += ".pck"; + } else if (req[1] == basereq + ".png") { + filepath += ".png"; + } else if (req[1] == basereq + ".wasm") { + filepath += ".wasm"; + } else { + String s = "HTTP/1.1 404 Not Found\r\n"; + s += "Connection: Close\r\n"; + s += "\r\n"; + CharString cs = s.utf8(); + connection->put_data((const uint8_t *)cs.get_data(), cs.size() - 1); + return; + } + FileAccess *f = FileAccess::open(filepath, FileAccess::READ); + ERR_FAIL_COND(!f); + String s = "HTTP/1.1 200 OK\r\n"; + s += "Connection: Close\r\n"; + s += "\r\n"; + CharString cs = s.utf8(); + Error err = connection->put_data((const uint8_t *)cs.get_data(), cs.size() - 1); + ERR_FAIL_COND(err != OK); + + while (true) { + uint8_t bytes[4096]; + int read = f->get_buffer(bytes, 4096); + if (read < 1) { + break; + } + err = connection->put_data(bytes, read); + ERR_FAIL_COND(err != OK); + } + } + + void poll() { + if (!server->is_listening()) + return; + if (connection.is_null()) { + if (!server->is_connection_available()) + return; + connection = server->take_connection(); + time = OS::get_singleton()->get_ticks_usec(); + } + if (OS::get_singleton()->get_ticks_usec() - time > 1000000) { + _clear_client(); + return; + } + if (connection->get_status() != StreamPeerTCP::STATUS_CONNECTED) + return; + + while (true) { + + char *r = (char *)req_buf; + int l = req_pos - 1; + if (l > 3 && r[l] == '\n' && r[l - 1] == '\r' && r[l - 2] == '\n' && r[l - 3] == '\r') { + _send_response(); + _clear_client(); + return; + } + + int read = 0; + ERR_FAIL_COND(req_pos >= 4096); + Error err = connection->get_partial_data(&req_buf[req_pos], 1, read); + if (err != OK) { + // Got an error + _clear_client(); + return; + } else if (read != 1) { + // Busy, wait next poll + return; + } + req_pos += read; + } + } +}; + class EditorExportPlatformJavaScript : public EditorExportPlatform { GDCLASS(EditorExportPlatformJavaScript, EditorExportPlatform); Ref<ImageTexture> logo; Ref<ImageTexture> run_icon; - bool runnable_when_last_polled; + Ref<ImageTexture> stop_icon; + int menu_options; void _fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug); +private: + Ref<EditorHTTPServer> server; + bool server_quit; + Mutex *server_lock; + Thread *server_thread; + + static void _server_thread_poll(void *data); + public: virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features); @@ -61,11 +199,12 @@ public: virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const; virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); - virtual bool poll_devices(); - virtual int get_device_count() const; - virtual String get_device_name(int p_device) const { return TTR("Run in Browser"); } - virtual String get_device_info(int p_device) const { return TTR("Run exported HTML in the system's default browser."); } - virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags); + virtual bool poll_export(); + virtual int get_options_count() const; + virtual String get_option_label(int p_index) const { return p_index ? TTR("Stop HTTP Server") : TTR("Run in Browser"); } + virtual String get_option_tooltip(int p_index) const { return p_index ? TTR("Stop HTTP Server") : TTR("Run exported HTML in the system's default browser."); } + virtual Ref<ImageTexture> get_option_icon(int p_index) const; + virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_option, int p_debug_flags); virtual Ref<Texture> get_run_icon() const; virtual void get_platform_features(List<String> *r_features) { @@ -78,6 +217,7 @@ public: } EditorExportPlatformJavaScript(); + ~EditorExportPlatformJavaScript(); }; void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug) { @@ -337,7 +477,7 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese return OK; } -bool EditorExportPlatformJavaScript::poll_devices() { +bool EditorExportPlatformJavaScript::poll_export() { Ref<EditorExportPreset> preset; @@ -350,17 +490,37 @@ bool EditorExportPlatformJavaScript::poll_devices() { } } - bool prev = runnable_when_last_polled; - runnable_when_last_polled = preset.is_valid(); - return runnable_when_last_polled != prev; + int prev = menu_options; + menu_options = preset.is_valid(); + if (server->is_listening()) { + if (menu_options == 0) { + server_lock->lock(); + server->stop(); + server_lock->unlock(); + } else { + menu_options += 1; + } + } + return menu_options != prev; +} + +Ref<ImageTexture> EditorExportPlatformJavaScript::get_option_icon(int p_index) const { + return p_index == 1 ? stop_icon : EditorExportPlatform::get_option_icon(p_index); } -int EditorExportPlatformJavaScript::get_device_count() const { +int EditorExportPlatformJavaScript::get_options_count() const { - return runnable_when_last_polled; + return menu_options; } -Error EditorExportPlatformJavaScript::run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) { +Error EditorExportPlatformJavaScript::run(const Ref<EditorExportPreset> &p_preset, int p_option, int p_debug_flags) { + + if (p_option == 1) { + server_lock->lock(); + server->stop(); + server_lock->unlock(); + return OK; + } String basepath = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmp_js_export"); String path = basepath + ".html"; @@ -374,7 +534,26 @@ Error EditorExportPlatformJavaScript::run(const Ref<EditorExportPreset> &p_prese DirAccess::remove_file_or_error(basepath + ".wasm"); return err; } - OS::get_singleton()->shell_open(String("file://") + path); + + IP_Address bind_ip; + uint16_t bind_port = EDITOR_GET("export/web/http_port"); + // Resolve host if needed. + String bind_host = EDITOR_GET("export/web/http_host"); + if (bind_host.is_valid_ip_address()) { + bind_ip = bind_host; + } else { + bind_ip = IP::get_singleton()->resolve_hostname(bind_host); + } + ERR_FAIL_COND_V_MSG(!bind_ip.is_valid(), ERR_INVALID_PARAMETER, "Invalid editor setting 'export/web/http_host': '" + bind_host + "'. Try using '127.0.0.1'."); + + // Restart server. + server_lock->lock(); + server->stop(); + err = server->listen(bind_port, bind_ip); + server_lock->unlock(); + ERR_FAIL_COND_V_MSG(err != OK, err, "Unable to start HTTP server."); + + OS::get_singleton()->shell_open(String("http://" + bind_host + ":" + itos(bind_port) + "/tmp_js_export.html")); // FIXME: Find out how to clean up export files after running the successfully // exported game. Might not be trivial. return OK; @@ -385,8 +564,23 @@ Ref<Texture> EditorExportPlatformJavaScript::get_run_icon() const { return run_icon; } +void EditorExportPlatformJavaScript::_server_thread_poll(void *data) { + EditorExportPlatformJavaScript *ej = (EditorExportPlatformJavaScript *)data; + while (!ej->server_quit) { + OS::get_singleton()->delay_usec(1000); + ej->server_lock->lock(); + ej->server->poll(); + ej->server_lock->unlock(); + } +} + EditorExportPlatformJavaScript::EditorExportPlatformJavaScript() { + server.instance(); + server_quit = false; + server_lock = Mutex::create(); + server_thread = Thread::create(_server_thread_poll, this); + Ref<Image> img = memnew(Image(_javascript_logo)); logo.instance(); logo->create_from_image(img); @@ -395,11 +589,29 @@ EditorExportPlatformJavaScript::EditorExportPlatformJavaScript() { run_icon.instance(); run_icon->create_from_image(img); - runnable_when_last_polled = false; + Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme(); + if (theme.is_valid()) + stop_icon = theme->get_icon("Stop", "EditorIcons"); + else + stop_icon.instance(); + + menu_options = 0; +} + +EditorExportPlatformJavaScript::~EditorExportPlatformJavaScript() { + server->stop(); + server_quit = true; + Thread::wait_to_finish(server_thread); + memdelete(server_lock); + memdelete(server_thread); } void register_javascript_exporter() { + EDITOR_DEF("export/web/http_host", "localhost"); + EDITOR_DEF("export/web/http_port", 8060); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "export/web/http_port", PROPERTY_HINT_RANGE, "1,65535,1")); + Ref<EditorExportPlatformJavaScript> platform; platform.instance(); EditorExport::get_singleton()->add_export_platform(platform); diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp index b0661cb4dd..652f6a1ce1 100644 --- a/platform/javascript/os_javascript.cpp +++ b/platform/javascript/os_javascript.cpp @@ -837,7 +837,7 @@ void OS_JavaScript::set_clipboard(const String &p_text) { var text = UTF8ToString($0); if (!navigator.clipboard || !navigator.clipboard.writeText) return 1; - navigator.clipboard.writeText(text).catch(e => { + navigator.clipboard.writeText(text).catch(function(e) { // Setting OS clipboard is only possible from an input callback. console.error("Setting OS clipboard is only possible from an input callback for the HTML5 plafrom. Exception:", e); }); diff --git a/platform/osx/camera_osx.mm b/platform/osx/camera_osx.mm index f13cf76beb..af09eec2eb 100644 --- a/platform/osx/camera_osx.mm +++ b/platform/osx/camera_osx.mm @@ -150,8 +150,8 @@ { // do Y - int new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0); - int new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0); + size_t new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0); + size_t new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0); if ((width[0] != new_width) || (height[0] != new_height)) { width[0] = new_width; @@ -168,8 +168,8 @@ { // do CbCr - int new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 1); - int new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 1); + size_t new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 1); + size_t new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 1); if ((width[1] != new_width) || (height[1] != new_height)) { width[1] = new_width; diff --git a/platform/osx/crash_handler_osx.mm b/platform/osx/crash_handler_osx.mm index e19fdf1b9f..015859b3c0 100644 --- a/platform/osx/crash_handler_osx.mm +++ b/platform/osx/crash_handler_osx.mm @@ -95,7 +95,7 @@ static void handle_crash(int sig) { if (strings) { void *load_addr = (void *)load_address(); - for (int i = 1; i < size; i++) { + for (size_t i = 1; i < size; i++) { char fname[1024]; Dl_info info; @@ -142,7 +142,7 @@ static void handle_crash(int sig) { } } - fprintf(stderr, "[%d] %ls\n", i, output.c_str()); + fprintf(stderr, "[%zu] %ls\n", i, output.c_str()); } free(strings); diff --git a/platform/osx/detect.py b/platform/osx/detect.py index 881ed05025..7882253e7a 100644 --- a/platform/osx/detect.py +++ b/platform/osx/detect.py @@ -91,6 +91,9 @@ def configure(env): env['RANLIB'] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/llvm-ranlib" env['AS'] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/llvm-as" env.Append(CPPDEFINES=['__MACPORTS__']) #hack to fix libvpx MM256_BROADCASTSI128_SI256 define + else: + env['CC'] = 'clang' + env['CXX'] = 'clang++' detect_darwin_sdk_path('osx', env) env.Append(CCFLAGS=['-isysroot', '$MACOS_SDK_PATH']) diff --git a/platform/osx/dir_access_osx.mm b/platform/osx/dir_access_osx.mm index ada142005b..75f50aaa28 100644 --- a/platform/osx/dir_access_osx.mm +++ b/platform/osx/dir_access_osx.mm @@ -48,18 +48,25 @@ String DirAccessOSX::fix_unicode_name(const char *p_name) const { } int DirAccessOSX::get_drive_count() { - NSArray *vols = [[NSWorkspace sharedWorkspace] mountedLocalVolumePaths]; + NSArray *res_keys = [NSArray arrayWithObjects:NSURLVolumeURLKey, NSURLIsSystemImmutableKey, nil]; + NSArray *vols = [[NSFileManager defaultManager] mountedVolumeURLsIncludingResourceValuesForKeys:res_keys options:NSVolumeEnumerationSkipHiddenVolumes]; + return [vols count]; } String DirAccessOSX::get_drive(int p_drive) { - NSArray *vols = [[NSWorkspace sharedWorkspace] mountedLocalVolumePaths]; + NSArray *res_keys = [NSArray arrayWithObjects:NSURLVolumeURLKey, NSURLIsSystemImmutableKey, nil]; + NSArray *vols = [[NSFileManager defaultManager] mountedVolumeURLsIncludingResourceValuesForKeys:res_keys options:NSVolumeEnumerationSkipHiddenVolumes]; int count = [vols count]; ERR_FAIL_INDEX_V(p_drive, count, ""); - NSString *path = vols[p_drive]; - return String([path UTF8String]); + String volname; + NSString *path = [vols[p_drive] path]; + + volname.parse_utf8([path UTF8String]); + + return volname; } #endif //posix_enabled diff --git a/platform/osx/joypad_osx.cpp b/platform/osx/joypad_osx.cpp index fa124dac11..4edf347f61 100644 --- a/platform/osx/joypad_osx.cpp +++ b/platform/osx/joypad_osx.cpp @@ -578,7 +578,7 @@ JoypadOSX::JoypadOSX() { const size_t n_elements = sizeof(vals) / sizeof(vals[0]); CFArrayRef array = okay ? CFArrayCreate(kCFAllocatorDefault, vals, n_elements, &kCFTypeArrayCallBacks) : NULL; - for (int i = 0; i < n_elements; i++) { + for (size_t i = 0; i < n_elements; i++) { if (vals[i]) { CFRelease((CFTypeRef)vals[i]); } diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h index f1f37e24d2..09a871f26c 100644 --- a/platform/osx/os_osx.h +++ b/platform/osx/os_osx.h @@ -31,6 +31,8 @@ #ifndef OS_OSX_H #define OS_OSX_H +#define BitMap _QDBitMap // Suppress deprecated QuickDraw definition. + #include "camera_osx.h" #include "core/os/input.h" #include "crash_handler_osx.h" @@ -50,6 +52,7 @@ #include <ApplicationServices/ApplicationServices.h> #include <CoreVideo/CoreVideo.h> +#undef BitMap #undef CursorShape class OS_OSX : public OS_Unix { diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index d30cb1c092..15885e1266 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -339,6 +339,8 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt CGFloat oldBackingScaleFactor = [[[notification userInfo] objectForKey:@"NSBackingPropertyOldScaleFactorKey"] doubleValue]; if (OS_OSX::singleton->is_hidpi_allowed()) { [OS_OSX::singleton->window_view setWantsBestResolutionOpenGLSurface:YES]; + } else { + [OS_OSX::singleton->window_view setWantsBestResolutionOpenGLSurface:NO]; } if (newBackingScaleFactor != oldBackingScaleFactor) { @@ -618,7 +620,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; NSArray *filenames = [pboard propertyListForType:NSFilenamesPboardType]; Vector<String> files; - for (int i = 0; i < filenames.count; i++) { + for (NSUInteger i = 0; i < filenames.count; i++) { NSString *ns = [filenames objectAtIndex:i]; char *utfs = strdup([ns UTF8String]); String ret; @@ -1492,13 +1494,15 @@ Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_a [window_view setWantsBestResolutionOpenGLSurface:YES]; //if (current_videomode.resizable) [window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; + } else { + [window_view setWantsBestResolutionOpenGLSurface:NO]; } //[window_object setTitle:[NSString stringWithUTF8String:"GodotEnginies"]]; [window_object setContentView:window_view]; [window_object setDelegate:window_delegate]; [window_object setAcceptsMouseMovedEvents:YES]; - [window_object center]; + [(NSWindow *)window_object center]; [window_object setRestorable:NO]; @@ -2334,12 +2338,12 @@ void OS_OSX::set_current_screen(int p_screen) { }; Point2 OS_OSX::get_native_screen_position(int p_screen) const { - if (p_screen == -1) { + if (p_screen < 0) { p_screen = get_current_screen(); } NSArray *screenArray = [NSScreen screens]; - if (p_screen < [screenArray count]) { + if ((NSUInteger)p_screen < [screenArray count]) { float display_scale = _display_scale([screenArray objectAtIndex:p_screen]); NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame]; // Return the top-left corner of the screen, for OS X the y starts at the bottom @@ -2358,12 +2362,12 @@ Point2 OS_OSX::get_screen_position(int p_screen) const { } int OS_OSX::get_screen_dpi(int p_screen) const { - if (p_screen == -1) { + if (p_screen < 0) { p_screen = get_current_screen(); } NSArray *screenArray = [NSScreen screens]; - if (p_screen < [screenArray count]) { + if ((NSUInteger)p_screen < [screenArray count]) { float displayScale = _display_scale([screenArray objectAtIndex:p_screen]); NSDictionary *description = [[screenArray objectAtIndex:p_screen] deviceDescription]; NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue]; @@ -2377,12 +2381,12 @@ int OS_OSX::get_screen_dpi(int p_screen) const { } Size2 OS_OSX::get_screen_size(int p_screen) const { - if (p_screen == -1) { + if (p_screen < 0) { p_screen = get_current_screen(); } NSArray *screenArray = [NSScreen screens]; - if (p_screen < [screenArray count]) { + if ((NSUInteger)p_screen < [screenArray count]) { float displayScale = _display_scale([screenArray objectAtIndex:p_screen]); // Note: Use frame to get the whole screen size NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame]; diff --git a/platform/windows/detect.py b/platform/windows/detect.py index cc9ba720a8..9a2b2bcb98 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -65,6 +65,8 @@ def get_opts(): BoolVariable('separate_debug_symbols', 'Create a separate file containing debugging symbols', False), ('msvc_version', 'MSVC version to use. Ignored if VCINSTALLDIR is set in shell env.', None), BoolVariable('use_mingw', 'Use the Mingw compiler, even if MSVC is installed. Only used on Windows.', False), + BoolVariable('use_llvm', 'Use the LLVM compiler', False), + BoolVariable('use_thinlto', 'Use ThinLTO', False), ] @@ -312,17 +314,33 @@ def configure_mingw(env): env.Append(LINKFLAGS=['-static']) mingw_prefix = env["mingw_prefix_64"] - env["CC"] = mingw_prefix + "gcc" - env['AS'] = mingw_prefix + "as" - env['CXX'] = mingw_prefix + "g++" - env['AR'] = mingw_prefix + "gcc-ar" - env['RANLIB'] = mingw_prefix + "gcc-ranlib" - env['LINK'] = mingw_prefix + "g++" + if env['use_llvm']: + env["CC"] = mingw_prefix + "clang" + env['AS'] = mingw_prefix + "as" + env["CXX"] = mingw_prefix + "clang++" + env['AR'] = mingw_prefix + "ar" + env['RANLIB'] = mingw_prefix + "ranlib" + env["LINK"] = mingw_prefix + "clang++" + else: + env["CC"] = mingw_prefix + "gcc" + env['AS'] = mingw_prefix + "as" + env['CXX'] = mingw_prefix + "g++" + env['AR'] = mingw_prefix + "gcc-ar" + env['RANLIB'] = mingw_prefix + "gcc-ranlib" + env['LINK'] = mingw_prefix + "g++" env["x86_libtheora_opt_gcc"] = True if env['use_lto']: - env.Append(CCFLAGS=['-flto']) - env.Append(LINKFLAGS=['-flto=' + str(env.GetOption("num_jobs"))]) + if not env['use_llvm'] and env.GetOption("num_jobs") > 1: + env.Append(CCFLAGS=['-flto']) + env.Append(LINKFLAGS=['-flto=' + str(env.GetOption("num_jobs"))]) + else: + if env['use_thinlto']: + env.Append(CCFLAGS=['-flto=thin']) + env.Append(LINKFLAGS=['-flto=thin']) + else: + env.Append(CCFLAGS=['-flto']) + env.Append(LINKFLAGS=['-flto']) ## Compile flags @@ -332,7 +350,7 @@ def configure_mingw(env): env.Append(CPPDEFINES=[('WINVER', env['target_win_version']), ('_WIN32_WINNT', env['target_win_version'])]) env.Append(LIBS=['mingw32', 'opengl32', 'dsound', 'ole32', 'd3d9', 'winmm', 'gdi32', 'iphlpapi', 'shlwapi', 'wsock32', 'ws2_32', 'kernel32', 'oleaut32', 'dinput8', 'dxguid', 'ksuser', 'imm32', 'bcrypt', 'avrt', 'uuid']) - env.Append(CPPDEFINES=['MINGW_ENABLED']) + env.Append(CPPDEFINES=['MINGW_ENABLED', ('MINGW_HAS_SECURE_API', 1)]) # resrc env.Append(BUILDERS={'RES': env.Builder(action=build_res_file, suffix='.o', src_suffix='.rc')}) diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 915d025e3b..683896886b 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -81,7 +81,9 @@ class OS_Windows : public OS { KEY_EVENT_BUFFER_SIZE = 512 }; +#ifdef STDOUT_FILE FILE *stdo; +#endif struct KeyEvent { @@ -107,7 +109,6 @@ class OS_Windows : public OS { VisualServer *visual_server; CameraWindows *camera_server; int pressrc; - HDC hDC; // Private GDI Device Context HINSTANCE hInstance; // Holds The Instance Of The Application HWND hWnd; Point2 last_pos; diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp index b38fbfe981..d584492737 100644 --- a/scene/2d/canvas_item.cpp +++ b/scene/2d/canvas_item.cpp @@ -735,6 +735,19 @@ void CanvasItem::draw_polyline_colors(const Vector<Point2> &p_points, const Vect VisualServer::get_singleton()->canvas_item_add_polyline(canvas_item, p_points, p_colors, p_width, p_antialiased); } +void CanvasItem::draw_arc(const Vector2 &p_center, float p_radius, float p_start_angle, float p_end_angle, int p_point_count, const Color &p_color, float p_width, bool p_antialiased) { + + Vector<Point2> points; + points.resize(p_point_count); + const float delta_angle = p_end_angle - p_start_angle; + for (int i = 0; i < p_point_count; i++) { + float theta = (i / (p_point_count - 1.0f)) * delta_angle + p_start_angle; + points.set(i, p_center + Vector2(Math::cos(theta), Math::sin(theta)) * p_radius); + } + + draw_polyline(points, p_color, p_width, p_antialiased); +} + void CanvasItem::draw_multiline(const Vector<Point2> &p_points, const Color &p_color, float p_width, bool p_antialiased) { ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); @@ -1152,6 +1165,7 @@ void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("draw_line", "from", "to", "color", "width", "antialiased"), &CanvasItem::draw_line, DEFVAL(1.0), DEFVAL(false)); ClassDB::bind_method(D_METHOD("draw_polyline", "points", "color", "width", "antialiased"), &CanvasItem::draw_polyline, DEFVAL(1.0), DEFVAL(false)); ClassDB::bind_method(D_METHOD("draw_polyline_colors", "points", "colors", "width", "antialiased"), &CanvasItem::draw_polyline_colors, DEFVAL(1.0), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("draw_arc", "center", "radius", "start_angle", "end_angle", "point_count", "color", "width", "antialiased"), &CanvasItem::draw_arc, DEFVAL(1.0), DEFVAL(false)); ClassDB::bind_method(D_METHOD("draw_multiline", "points", "color", "width", "antialiased"), &CanvasItem::draw_multiline, DEFVAL(1.0), DEFVAL(false)); ClassDB::bind_method(D_METHOD("draw_multiline_colors", "points", "colors", "width", "antialiased"), &CanvasItem::draw_multiline_colors, DEFVAL(1.0), DEFVAL(false)); ClassDB::bind_method(D_METHOD("draw_rect", "rect", "color", "filled", "width", "antialiased"), &CanvasItem::draw_rect, DEFVAL(true), DEFVAL(1.0), DEFVAL(false)); diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h index 9c6799a441..581adf1396 100644 --- a/scene/2d/canvas_item.h +++ b/scene/2d/canvas_item.h @@ -305,6 +305,7 @@ public: void draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width = 1.0, bool p_antialiased = false); void draw_polyline(const Vector<Point2> &p_points, const Color &p_color, float p_width = 1.0, bool p_antialiased = false); void draw_polyline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width = 1.0, bool p_antialiased = false); + void draw_arc(const Vector2 &p_center, float p_radius, float p_start_angle, float p_end_angle, int p_point_count, const Color &p_color, float p_width = 1.0, bool p_antialiased = false); void draw_multiline(const Vector<Point2> &p_points, const Color &p_color, float p_width = 1.0, bool p_antialiased = false); void draw_multiline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width = 1.0, bool p_antialiased = false); void draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled = true, float p_width = 1.0, bool p_antialiased = false); diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp index 85c423964b..07f3e10244 100644 --- a/scene/2d/cpu_particles_2d.cpp +++ b/scene/2d/cpu_particles_2d.cpp @@ -37,6 +37,9 @@ void CPUParticles2D::set_emitting(bool p_emitting) { + if (emitting == p_emitting) + return; + emitting = p_emitting; if (emitting) set_process_internal(true); @@ -257,8 +260,7 @@ void CPUParticles2D::restart() { inactive_time = 0; frame_remainder = 0; cycle = 0; - - set_emitting(true); + emitting = false; { int pc = particles.size(); @@ -268,6 +270,8 @@ void CPUParticles2D::restart() { w[i].active = false; } } + + set_emitting(true); } void CPUParticles2D::set_direction(Vector2 p_direction) { @@ -535,6 +539,74 @@ static float rand_from_seed(uint32_t &seed) { return float(seed % uint32_t(65536)) / 65535.0; } +void CPUParticles2D::_update_internal() { + + if (particles.size() == 0 || !is_visible_in_tree()) { + _set_redraw(false); + return; + } + + float delta = get_process_delta_time(); + if (emitting) { + inactive_time = 0; + } else { + inactive_time += delta; + if (inactive_time > lifetime * 1.2) { + set_process_internal(false); + _set_redraw(false); + + //reset variables + time = 0; + inactive_time = 0; + frame_remainder = 0; + cycle = 0; + return; + } + } + _set_redraw(true); + + if (time == 0 && pre_process_time > 0.0) { + + float frame_time; + if (fixed_fps > 0) + frame_time = 1.0 / fixed_fps; + else + frame_time = 1.0 / 30.0; + + float todo = pre_process_time; + + while (todo >= 0) { + _particles_process(frame_time); + todo -= frame_time; + } + } + + if (fixed_fps > 0) { + float frame_time = 1.0 / fixed_fps; + float decr = frame_time; + + float ldelta = delta; + if (ldelta > 0.1) { //avoid recursive stalls if fps goes below 10 + ldelta = 0.1; + } else if (ldelta <= 0.0) { //unlikely but.. + ldelta = 0.001; + } + float todo = frame_remainder + ldelta; + + while (todo >= frame_time) { + _particles_process(frame_time); + todo -= decr; + } + + frame_remainder = todo; + + } else { + _particles_process(delta); + } + + _update_particle_data_buffer(); +} + void CPUParticles2D::_particles_process(float p_delta) { p_delta *= speed_scale; @@ -1000,6 +1072,10 @@ void CPUParticles2D::_notification(int p_what) { } if (p_what == NOTIFICATION_DRAW) { + // first update before rendering to avoid one frame delay after emitting starts + if (emitting && (time == 0)) + _update_internal(); + if (!redraw) return; // don't add to render list @@ -1017,71 +1093,7 @@ void CPUParticles2D::_notification(int p_what) { } if (p_what == NOTIFICATION_INTERNAL_PROCESS) { - - if (particles.size() == 0 || !is_visible_in_tree()) { - _set_redraw(false); - return; - } - - float delta = get_process_delta_time(); - if (emitting) { - inactive_time = 0; - } else { - inactive_time += delta; - if (inactive_time > lifetime * 1.2) { - set_process_internal(false); - _set_redraw(false); - - //reset variables - time = 0; - inactive_time = 0; - frame_remainder = 0; - cycle = 0; - return; - } - } - _set_redraw(true); - - if (time == 0 && pre_process_time > 0.0) { - - float frame_time; - if (fixed_fps > 0) - frame_time = 1.0 / fixed_fps; - else - frame_time = 1.0 / 30.0; - - float todo = pre_process_time; - - while (todo >= 0) { - _particles_process(frame_time); - todo -= frame_time; - } - } - - if (fixed_fps > 0) { - float frame_time = 1.0 / fixed_fps; - float decr = frame_time; - - float ldelta = delta; - if (ldelta > 0.1) { //avoid recursive stalls if fps goes below 10 - ldelta = 0.1; - } else if (ldelta <= 0.0) { //unlikely but.. - ldelta = 0.001; - } - float todo = frame_remainder + ldelta; - - while (todo >= frame_time) { - _particles_process(frame_time); - todo -= decr; - } - - frame_remainder = todo; - - } else { - _particles_process(delta); - } - - _update_particle_data_buffer(); + _update_internal(); } if (p_what == NOTIFICATION_TRANSFORM_CHANGED) { @@ -1411,6 +1423,7 @@ CPUParticles2D::CPUParticles2D() { frame_remainder = 0; cycle = 0; redraw = false; + emitting = false; mesh = VisualServer::get_singleton()->mesh_create(); multimesh = VisualServer::get_singleton()->multimesh_create(); diff --git a/scene/2d/cpu_particles_2d.h b/scene/2d/cpu_particles_2d.h index da668664b9..47b4568dd4 100644 --- a/scene/2d/cpu_particles_2d.h +++ b/scene/2d/cpu_particles_2d.h @@ -174,6 +174,7 @@ private: Vector2 gravity; + void _update_internal(); void _particles_process(float p_delta); void _update_particle_data_buffer(); diff --git a/scene/2d/joints_2d.cpp b/scene/2d/joints_2d.cpp index d8156a0afe..847d08b025 100644 --- a/scene/2d/joints_2d.cpp +++ b/scene/2d/joints_2d.cpp @@ -37,8 +37,8 @@ void Joint2D::_update_joint(bool p_only_free) { if (joint.is_valid()) { - if (ba.is_valid() && bb.is_valid()) - Physics2DServer::get_singleton()->body_remove_collision_exception(ba, bb); + if (ba.is_valid() && bb.is_valid() && exclude_from_collision) + Physics2DServer::get_singleton()->joint_disable_collisions_between_bodies(joint, false); Physics2DServer::get_singleton()->free(joint); joint = RID(); @@ -61,8 +61,6 @@ void Joint2D::_update_joint(bool p_only_free) { if (!body_a || !body_b) return; - SWAP(body_a, body_b); - joint = _configure_joint(body_a, body_b); if (!joint.is_valid()) @@ -133,6 +131,8 @@ void Joint2D::set_exclude_nodes_from_collision(bool p_enable) { if (exclude_from_collision == p_enable) return; + + _update_joint(true); exclude_from_collision = p_enable; _update_joint(); } diff --git a/scene/2d/sprite.cpp b/scene/2d/sprite.cpp index d2e1e494e3..8cdfceea52 100644 --- a/scene/2d/sprite.cpp +++ b/scene/2d/sprite.cpp @@ -387,6 +387,10 @@ void Sprite::_validate_property(PropertyInfo &property) const { property.hint_string = "0," + itos(vframes * hframes - 1) + ",1"; property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS; } + + if (property.name == "frame_coords") { + property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS; + } } void Sprite::_texture_changed() { diff --git a/scene/2d/touch_screen_button.cpp b/scene/2d/touch_screen_button.cpp index 9a1a759e72..cf68528388 100644 --- a/scene/2d/touch_screen_button.cpp +++ b/scene/2d/touch_screen_button.cpp @@ -325,8 +325,12 @@ void TouchScreenButton::_release(bool p_exiting_tree) { } Rect2 TouchScreenButton::_edit_get_rect() const { - if (texture.is_null()) - return CanvasItem::_edit_get_rect(); + if (texture.is_null()) { + if (shape.is_valid()) + return shape->get_rect(); + else + return CanvasItem::_edit_get_rect(); + } return Rect2(Size2(), texture->get_size()); } diff --git a/scene/3d/cpu_particles.cpp b/scene/3d/cpu_particles.cpp index 93ff60bc4e..8766e30d0b 100644 --- a/scene/3d/cpu_particles.cpp +++ b/scene/3d/cpu_particles.cpp @@ -46,9 +46,17 @@ PoolVector<Face3> CPUParticles::get_faces(uint32_t p_usage_flags) const { void CPUParticles::set_emitting(bool p_emitting) { + if (emitting == p_emitting) + return; + emitting = p_emitting; - if (emitting) + if (emitting) { set_process_internal(true); + + // first update before rendering to avoid one frame delay after emitting starts + if (time == 0) + _update_internal(); + } } void CPUParticles::set_amount(int p_amount) { @@ -232,8 +240,7 @@ void CPUParticles::restart() { inactive_time = 0; frame_remainder = 0; cycle = 0; - - set_emitting(true); + emitting = false; { int pc = particles.size(); @@ -243,6 +250,8 @@ void CPUParticles::restart() { w[i].active = false; } } + + set_emitting(true); } void CPUParticles::set_direction(Vector3 p_direction) { @@ -508,6 +517,81 @@ static float rand_from_seed(uint32_t &seed) { return float(seed % uint32_t(65536)) / 65535.0; } +void CPUParticles::_update_internal() { + + if (particles.size() == 0 || !is_visible_in_tree()) { + _set_redraw(false); + return; + } + + float delta = get_process_delta_time(); + if (emitting) { + inactive_time = 0; + } else { + inactive_time += delta; + if (inactive_time > lifetime * 1.2) { + set_process_internal(false); + _set_redraw(false); + + //reset variables + time = 0; + inactive_time = 0; + frame_remainder = 0; + cycle = 0; + return; + } + } + _set_redraw(true); + + bool processed = false; + + if (time == 0 && pre_process_time > 0.0) { + + float frame_time; + if (fixed_fps > 0) + frame_time = 1.0 / fixed_fps; + else + frame_time = 1.0 / 30.0; + + float todo = pre_process_time; + + while (todo >= 0) { + _particles_process(frame_time); + processed = true; + todo -= frame_time; + } + } + + if (fixed_fps > 0) { + float frame_time = 1.0 / fixed_fps; + float decr = frame_time; + + float ldelta = delta; + if (ldelta > 0.1) { //avoid recursive stalls if fps goes below 10 + ldelta = 0.1; + } else if (ldelta <= 0.0) { //unlikely but.. + ldelta = 0.001; + } + float todo = frame_remainder + ldelta; + + while (todo >= frame_time) { + _particles_process(frame_time); + processed = true; + todo -= decr; + } + + frame_remainder = todo; + + } else { + _particles_process(delta); + processed = true; + } + + if (processed) { + _update_particle_data_buffer(); + } +} + void CPUParticles::_particles_process(float p_delta) { p_delta *= speed_scale; @@ -1068,85 +1152,24 @@ void CPUParticles::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { set_process_internal(emitting); + + // first update before rendering to avoid one frame delay after emitting starts + if (emitting && (time == 0)) + _update_internal(); } if (p_what == NOTIFICATION_EXIT_TREE) { _set_redraw(false); } - if (p_what == NOTIFICATION_INTERNAL_PROCESS) { - - if (particles.size() == 0 || !is_visible_in_tree()) { - _set_redraw(false); - return; - } - - float delta = get_process_delta_time(); - if (emitting) { - inactive_time = 0; - } else { - inactive_time += delta; - if (inactive_time > lifetime * 1.2) { - set_process_internal(false); - _set_redraw(false); - - //reset variables - time = 0; - inactive_time = 0; - frame_remainder = 0; - cycle = 0; - return; - } - } - _set_redraw(true); - - bool processed = false; - - if (time == 0 && pre_process_time > 0.0) { - - float frame_time; - if (fixed_fps > 0) - frame_time = 1.0 / fixed_fps; - else - frame_time = 1.0 / 30.0; - - float todo = pre_process_time; - - while (todo >= 0) { - _particles_process(frame_time); - processed = true; - todo -= frame_time; - } - } - - if (fixed_fps > 0) { - float frame_time = 1.0 / fixed_fps; - float decr = frame_time; - - float ldelta = delta; - if (ldelta > 0.1) { //avoid recursive stalls if fps goes below 10 - ldelta = 0.1; - } else if (ldelta <= 0.0) { //unlikely but.. - ldelta = 0.001; - } - float todo = frame_remainder + ldelta; - - while (todo >= frame_time) { - _particles_process(frame_time); - processed = true; - todo -= decr; - } - - frame_remainder = todo; - - } else { - _particles_process(delta); - processed = true; - } + if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { + // first update before rendering to avoid one frame delay after emitting starts + if (emitting && (time == 0)) + _update_internal(); + } - if (processed) { - _update_particle_data_buffer(); - } + if (p_what == NOTIFICATION_INTERNAL_PROCESS) { + _update_internal(); } if (p_what == NOTIFICATION_TRANSFORM_CHANGED) { @@ -1472,6 +1495,7 @@ CPUParticles::CPUParticles() { frame_remainder = 0; cycle = 0; redraw = false; + emitting = false; set_notify_transform(true); diff --git a/scene/3d/cpu_particles.h b/scene/3d/cpu_particles.h index 66b37f359a..635265be7f 100644 --- a/scene/3d/cpu_particles.h +++ b/scene/3d/cpu_particles.h @@ -173,6 +173,7 @@ private: Vector3 gravity; + void _update_internal(); void _particles_process(float p_delta); void _update_particle_data_buffer(); diff --git a/scene/3d/navigation_mesh.cpp b/scene/3d/navigation_mesh.cpp index f82543b789..496dc4b411 100644 --- a/scene/3d/navigation_mesh.cpp +++ b/scene/3d/navigation_mesh.cpp @@ -108,6 +108,24 @@ bool NavigationMesh::get_collision_mask_bit(int p_bit) const { return get_collision_mask() & (1 << p_bit); } +void NavigationMesh::set_source_geometry_mode(int p_geometry_mode) { + ERR_FAIL_INDEX(p_geometry_mode, SOURCE_GEOMETRY_MAX); + source_geometry_mode = static_cast<SourceGeometryMode>(p_geometry_mode); + _change_notify(); +} + +int NavigationMesh::get_source_geometry_mode() const { + return source_geometry_mode; +} + +void NavigationMesh::set_source_group_name(StringName p_group_name) { + source_group_name = p_group_name; +} + +StringName NavigationMesh::get_source_group_name() const { + return source_group_name; +} + void NavigationMesh::set_cell_size(float p_value) { cell_size = p_value; } @@ -387,6 +405,12 @@ void NavigationMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &NavigationMesh::set_collision_mask_bit); ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &NavigationMesh::get_collision_mask_bit); + ClassDB::bind_method(D_METHOD("set_source_geometry_mode", "mask"), &NavigationMesh::set_source_geometry_mode); + ClassDB::bind_method(D_METHOD("get_source_geometry_mode"), &NavigationMesh::get_source_geometry_mode); + + ClassDB::bind_method(D_METHOD("set_source_group_name", "mask"), &NavigationMesh::set_source_group_name); + ClassDB::bind_method(D_METHOD("get_source_group_name"), &NavigationMesh::get_source_group_name); + ClassDB::bind_method(D_METHOD("set_cell_size", "cell_size"), &NavigationMesh::set_cell_size); ClassDB::bind_method(D_METHOD("get_cell_size"), &NavigationMesh::get_cell_size); @@ -462,6 +486,8 @@ void NavigationMesh::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "sample_partition_type/sample_partition_type", PROPERTY_HINT_ENUM, "Watershed,Monotone,Layers"), "set_sample_partition_type", "get_sample_partition_type"); ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry/parsed_geometry_type", PROPERTY_HINT_ENUM, "Mesh Instances,Static Colliders,Both"), "set_parsed_geometry_type", "get_parsed_geometry_type"); ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry/collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry/source_geometry_mode", PROPERTY_HINT_ENUM, "Navmesh Children, Group With Children, Group Explicit"), "set_source_geometry_mode", "get_source_geometry_mode"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "geometry/source_group_name"), "set_source_group_name", "get_source_group_name"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "cell/size", PROPERTY_HINT_RANGE, "0.1,1.0,0.01,or_greater"), "set_cell_size", "get_cell_size"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "cell/height", PROPERTY_HINT_RANGE, "0.1,1.0,0.01,or_greater"), "set_cell_height", "get_cell_height"); @@ -489,6 +515,13 @@ void NavigationMesh::_validate_property(PropertyInfo &property) const { return; } } + + if (property.name == "geometry/source_group_name") { + if (source_geometry_mode == SOURCE_GEOMETRY_NAVMESH_CHILDREN) { + property.usage = 0; + return; + } + } } NavigationMesh::NavigationMesh() { @@ -509,6 +542,8 @@ NavigationMesh::NavigationMesh() { partition_type = SAMPLE_PARTITION_WATERSHED; parsed_geometry_type = PARSED_GEOMETRY_MESH_INSTANCES; collision_mask = 0xFFFFFFFF; + source_geometry_mode = SOURCE_GEOMETRY_NAVMESH_CHILDREN; + source_group_name = "navmesh"; filter_low_hanging_obstacles = false; filter_ledge_spans = false; filter_walkable_low_height_spans = false; diff --git a/scene/3d/navigation_mesh.h b/scene/3d/navigation_mesh.h index 5fbf3998ff..8467f80f0e 100644 --- a/scene/3d/navigation_mesh.h +++ b/scene/3d/navigation_mesh.h @@ -77,6 +77,13 @@ public: PARSED_GEOMETRY_MAX }; + enum SourceGeometryMode { + SOURCE_GEOMETRY_NAVMESH_CHILDREN = 0, + SOURCE_GEOMETRY_GROUPS_WITH_CHILDREN, + SOURCE_GEOMETRY_GROUPS_EXPLICIT, + SOURCE_GEOMETRY_MAX + }; + protected: float cell_size; float cell_height; @@ -96,6 +103,9 @@ protected: ParsedGeometryType parsed_geometry_type; uint32_t collision_mask; + SourceGeometryMode source_geometry_mode; + StringName source_group_name; + bool filter_low_hanging_obstacles; bool filter_ledge_spans; bool filter_walkable_low_height_spans; @@ -114,6 +124,12 @@ public: void set_collision_mask_bit(int p_bit, bool p_value); bool get_collision_mask_bit(int p_bit) const; + void set_source_geometry_mode(int p_source_mode); + int get_source_geometry_mode() const; + + void set_source_group_name(StringName p_group_name); + StringName get_source_group_name() const; + void set_cell_size(float p_value); float get_cell_size() const; diff --git a/scene/3d/spatial.cpp b/scene/3d/spatial.cpp index df831f92ef..9a659ef4af 100644 --- a/scene/3d/spatial.cpp +++ b/scene/3d/spatial.cpp @@ -690,11 +690,10 @@ void Spatial::look_at_from_position(const Vector3 &p_pos, const Vector3 &p_targe Transform lookat; lookat.origin = p_pos; - Vector3 original_scale(get_global_transform().basis.get_scale()); + Vector3 original_scale(get_scale()); lookat = lookat.looking_at(p_target, p_up); - // as basis was normalized, we just need to apply original scale back - lookat.basis.scale(original_scale); set_global_transform(lookat); + set_scale(original_scale); } Vector3 Spatial::to_local(Vector3 p_global) const { diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index aec672d2c7..adcd80b0ab 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -577,9 +577,8 @@ void Sprite3D::set_frame(int p_frame) { ERR_FAIL_INDEX(p_frame, int64_t(vframes) * hframes); - if (frame != p_frame) + frame = p_frame; - frame = p_frame; _queue_update(); _change_notify("frame"); @@ -663,6 +662,10 @@ void Sprite3D::_validate_property(PropertyInfo &property) const { property.hint_string = "0," + itos(vframes * hframes - 1) + ",1"; property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS; } + + if (property.name == "frame_coords") { + property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS; + } } void Sprite3D::_bind_methods() { diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index 1f9793190d..a7d936fcd3 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -281,7 +281,7 @@ void Tween::_bind_methods() { BIND_ENUM_CONSTANT(EASE_OUT_IN); } -Variant &Tween::_get_initial_val(InterpolateData &p_data) { +Variant Tween::_get_initial_val(const InterpolateData &p_data) const { // What type of data are we interpolating? switch (p_data.type) { @@ -299,7 +299,7 @@ Variant &Tween::_get_initial_val(InterpolateData &p_data) { ERR_FAIL_COND_V(object == NULL, p_data.initial_val); // Are we targeting a property or a method? - static Variant initial_val; + Variant initial_val; if (p_data.type == TARGETING_PROPERTY) { // Get the property from the target object bool valid = false; @@ -322,6 +322,41 @@ Variant &Tween::_get_initial_val(InterpolateData &p_data) { return p_data.delta_val; } +Variant Tween::_get_final_val(const InterpolateData &p_data) const { + switch (p_data.type) { + case FOLLOW_PROPERTY: + case FOLLOW_METHOD: { + // Get the object that is being followed + Object *target = ObjectDB::get_instance(p_data.target_id); + ERR_FAIL_COND_V(target == NULL, p_data.initial_val); + + // We want to figure out the final value + Variant final_val; + if (p_data.type == FOLLOW_PROPERTY) { + // Read the property as-is + bool valid = false; + final_val = target->get_indexed(p_data.target_key, &valid); + ERR_FAIL_COND_V(!valid, p_data.initial_val); + } else { + // We're looking at a method. Call the method on the target object + Variant::CallError error; + final_val = target->call(p_data.target_key[0], NULL, 0, error); + ERR_FAIL_COND_V(error.error != Variant::CallError::CALL_OK, p_data.initial_val); + } + + // If we're looking at an INT value, instead convert it to a REAL + // This is better for interpolation + if (final_val.get_type() == Variant::INT) final_val = final_val.operator real_t(); + + return final_val; + } + default: { + // If we're not following a final value/method, use the final value from the data + return p_data.final_val; + } + } +} + Variant &Tween::_get_delta_val(InterpolateData &p_data) { // What kind of data are we interpolating? @@ -384,7 +419,7 @@ Variant &Tween::_get_delta_val(InterpolateData &p_data) { Variant Tween::_run_equation(InterpolateData &p_data) { // Get the initial and delta values from the data - Variant &initial_val = _get_initial_val(p_data); + Variant initial_val = _get_initial_val(p_data); Variant &delta_val = _get_delta_val(p_data); Variant result; @@ -718,7 +753,8 @@ void Tween::_tween_process(float p_delta) { // Is the tween now finished? if (data.finish) { // Set it to the final value directly - _apply_tween_value(data, data.final_val); + Variant final_val = _get_final_val(data); + _apply_tween_value(data, final_val); // Mark the tween as completed and emit the signal data.elapsed = 0; diff --git a/scene/animation/tween.h b/scene/animation/tween.h index 64ce099ecd..574238f5c9 100644 --- a/scene/animation/tween.h +++ b/scene/animation/tween.h @@ -127,7 +127,8 @@ private: real_t _run_equation(TransitionType p_trans_type, EaseType p_ease_type, real_t t, real_t b, real_t c, real_t d); Variant &_get_delta_val(InterpolateData &p_data); - Variant &_get_initial_val(InterpolateData &p_data); + Variant _get_initial_val(const InterpolateData &p_data) const; + Variant _get_final_val(const InterpolateData &p_data) const; Variant _run_equation(InterpolateData &p_data); bool _calc_delta_val(const Variant &p_initial_val, const Variant &p_final_val, Variant &p_delta_val); bool _apply_tween_value(InterpolateData &p_data, Variant &value); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index fafbcf0c55..8b4d5d4980 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -2221,9 +2221,11 @@ void Control::_modal_stack_remove() { if (!data.MI) return; - get_viewport()->_gui_remove_from_modal_stack(data.MI, data.modal_prev_focus_owner); - + List<Control *>::Element *element = data.MI; data.MI = NULL; + + get_viewport()->_gui_remove_from_modal_stack(element, data.modal_prev_focus_owner); + data.modal_prev_focus_owner = 0; } diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 24c046457b..5cb4bcc64f 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -46,14 +46,31 @@ VBoxContainer *FileDialog::get_vbox() { void FileDialog::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE) { + if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { - dir_up->set_icon(get_icon("parent_folder")); - refresh->set_icon(get_icon("reload")); - show_hidden->set_icon(get_icon("toggle_hidden")); - } + if (p_what == NOTIFICATION_ENTER_TREE) { + dir_up->set_icon(get_icon("parent_folder")); + refresh->set_icon(get_icon("reload")); + show_hidden->set_icon(get_icon("toggle_hidden")); + } + + Color font_color = get_color("font_color", "ToolButton"); + Color font_color_hover = get_color("font_color_hover", "ToolButton"); + Color font_color_pressed = get_color("font_color_pressed", "ToolButton"); + + dir_up->add_color_override("icon_color_normal", font_color); + dir_up->add_color_override("icon_color_hover", font_color_hover); + dir_up->add_color_override("icon_color_pressed", font_color_pressed); + + refresh->add_color_override("icon_color_normal", font_color); + refresh->add_color_override("icon_color_hover", font_color_hover); + refresh->add_color_override("icon_color_pressed", font_color_pressed); + + show_hidden->add_color_override("icon_color_normal", font_color); + show_hidden->add_color_override("icon_color_hover", font_color_hover); + show_hidden->add_color_override("icon_color_pressed", font_color_pressed); - if (p_what == NOTIFICATION_POPUP_HIDE) { + } else if (p_what == NOTIFICATION_POPUP_HIDE) { set_process_unhandled_input(false); } diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index 510f1b18ad..4edd4b8530 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -452,6 +452,11 @@ void Label::regenerate_word_cache() { current_word_size += char_width; line_width += char_width; total_char_cache++; + + // allow autowrap to cut words when they exceed line width + if (autowrap && (current_word_size > width)) { + separatable = true; + } } if ((autowrap && (line_width >= width) && ((last && last->char_pos >= 0) || separatable)) || insert_newline) { diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 42cb89b2d6..8c19255fd0 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -555,12 +555,12 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & if (p_font_color_shadow.a > 0) { float x_ofs_shadow = align_ofs + pofs; float y_ofs_shadow = y + lh - line_descent; - font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + shadow_ofs, fx_char, c[i + 1], p_font_color_shadow); + font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + shadow_ofs + fx_offset, fx_char, c[i + 1], p_font_color_shadow); if (p_shadow_as_outline) { - font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, shadow_ofs.y), fx_char, c[i + 1], p_font_color_shadow); - font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(shadow_ofs.x, -shadow_ofs.y), fx_char, c[i + 1], p_font_color_shadow); - font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, -shadow_ofs.y), fx_char, c[i + 1], p_font_color_shadow); + font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, shadow_ofs.y) + fx_offset, fx_char, c[i + 1], p_font_color_shadow); + font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(shadow_ofs.x, -shadow_ofs.y) + fx_offset, fx_char, c[i + 1], p_font_color_shadow); + font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, -shadow_ofs.y) + fx_offset, fx_char, c[i + 1], p_font_color_shadow); } } @@ -624,19 +624,19 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & if (p_mode == PROCESS_POINTER && r_click_char) *r_click_char = 0; - ENSURE_WIDTH(img->image->get_width()); + ENSURE_WIDTH(img->size.width); - bool visible = visible_characters < 0 || (p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - font->get_descent() - img->image->get_height(), img->image->get_height())); + bool visible = visible_characters < 0 || (p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - font->get_descent() - img->size.height, img->size.height)); if (visible) line_is_blank = false; if (p_mode == PROCESS_DRAW && visible) { - img->image->draw(ci, p_ofs + Point2(align_ofs + wofs, y + lh - font->get_descent() - img->image->get_height())); + img->image->draw_rect(ci, Rect2(p_ofs + Point2(align_ofs + wofs, y + lh - font->get_descent() - img->size.height), img->size)); } p_char_count++; - ADVANCE(img->image->get_width()); - CHECK_HEIGHT((img->image->get_height() + font->get_descent())); + ADVANCE(img->size.width); + CHECK_HEIGHT((img->size.height + font->get_descent())); } break; case ITEM_NEWLINE: { @@ -859,7 +859,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & void RichTextLabel::_scroll_changed(double) { - if (updating_scroll) + if (updating_scroll || !scroll_active) return; if (scroll_follow && vscroll->get_value() >= (vscroll->get_max() - vscroll->get_page())) @@ -1634,7 +1634,7 @@ void RichTextLabel::_remove_item(Item *p_item, const int p_line, const int p_sub } } -void RichTextLabel::add_image(const Ref<Texture> &p_image) { +void RichTextLabel::add_image(const Ref<Texture> &p_image, const int p_width, const int p_height) { if (current->type == ITEM_TABLE) return; @@ -1643,6 +1643,30 @@ void RichTextLabel::add_image(const Ref<Texture> &p_image) { ItemImage *item = memnew(ItemImage); item->image = p_image; + + if (p_width > 0) { + // custom width + item->size.width = p_width; + if (p_height > 0) { + // custom height + item->size.height = p_height; + } else { + // calculate height to keep aspect ratio + item->size.height = p_image->get_height() * p_width / p_image->get_width(); + } + } else { + if (p_height > 0) { + // custom height + item->size.height = p_height; + // calculate width to keep aspect ratio + item->size.width = p_image->get_width() * p_height / p_image->get_height(); + } else { + // keep original width and height + item->size.height = p_image->get_height(); + item->size.width = p_image->get_width(); + } + } + _add_item(item, false); } @@ -2125,6 +2149,7 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { int end = p_bbcode.find("[", brk_end); if (end == -1) end = p_bbcode.length(); + String image = p_bbcode.substr(brk_end + 1, end - brk_end - 1); Ref<Texture> texture = ResourceLoader::load(image, "Texture"); @@ -2133,6 +2158,32 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { pos = end; tag_stack.push_front(tag); + } else if (tag.begins_with("img=")) { + + int width = 0; + int height = 0; + + String params = tag.substr(4, tag.length()); + int sep = params.find("x"); + if (sep == -1) { + width = params.to_int(); + } else { + width = params.substr(0, sep).to_int(); + height = params.substr(sep + 1, params.length()).to_int(); + } + + int end = p_bbcode.find("[", brk_end); + if (end == -1) + end = p_bbcode.length(); + + String image = p_bbcode.substr(brk_end + 1, end - brk_end - 1); + + Ref<Texture> texture = ResourceLoader::load(image, "Texture"); + if (texture.is_valid()) + add_image(texture, width, height); + + pos = end; + tag_stack.push_front("img"); } else if (tag.begins_with("color=")) { String col = tag.substr(6, tag.length()); @@ -2581,7 +2632,7 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("get_text"), &RichTextLabel::get_text); ClassDB::bind_method(D_METHOD("add_text", "text"), &RichTextLabel::add_text); ClassDB::bind_method(D_METHOD("set_text", "text"), &RichTextLabel::set_text); - ClassDB::bind_method(D_METHOD("add_image", "image"), &RichTextLabel::add_image); + ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0)); ClassDB::bind_method(D_METHOD("newline"), &RichTextLabel::add_newline); ClassDB::bind_method(D_METHOD("remove_line", "line"), &RichTextLabel::remove_line); ClassDB::bind_method(D_METHOD("push_font", "font"), &RichTextLabel::push_font); diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 1c90d974e4..6cd69b9187 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -148,6 +148,7 @@ private: struct ItemImage : public Item { Ref<Texture> image; + Size2 size; ItemImage() { type = ITEM_IMAGE; } }; @@ -406,7 +407,7 @@ protected: public: String get_text(); void add_text(const String &p_text); - void add_image(const Ref<Texture> &p_image); + void add_image(const Ref<Texture> &p_image, const int p_width = 0, const int p_height = 0); void add_newline(); bool remove_line(const int p_line); void push_font(const Ref<Font> &p_font); diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp index 172c366c41..bf067898e6 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -108,21 +108,21 @@ void SpinBox::_gui_input(const Ref<InputEvent> &p_event) { case BUTTON_LEFT: { + line_edit->grab_focus(); + set_value(get_value() + (up ? get_step() : -get_step())); range_click_timer->set_wait_time(0.6); range_click_timer->set_one_shot(true); range_click_timer->start(); - line_edit->grab_focus(); - drag.allowed = true; drag.capture_pos = mb->get_position(); } break; case BUTTON_RIGHT: { - set_value((up ? get_max() : get_min())); line_edit->grab_focus(); + set_value((up ? get_max() : get_min())); } break; case BUTTON_WHEEL_UP: { if (line_edit->has_focus()) { diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index a22ddb265b..a7e87a28ae 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -161,57 +161,58 @@ void TextEdit::Text::_update_line_cache(int p_line) const { /* BEGIN */ int lr = cr.begin_key.length(); - if (lr == 0 || lr > left) - continue; + const CharType *kc; + bool match; - const CharType *kc = cr.begin_key.c_str(); + if (lr != 0 && lr <= left) { + kc = cr.begin_key.c_str(); - bool match = true; + match = true; - for (int k = 0; k < lr; k++) { - if (kc[k] != str[i + k]) { - match = false; - break; + for (int k = 0; k < lr; k++) { + if (kc[k] != str[i + k]) { + match = false; + break; + } } - } - if (match) { + if (match) { - ColorRegionInfo cri; - cri.end = false; - cri.region = j; - text.write[p_line].region_info[i] = cri; - i += lr - 1; + ColorRegionInfo cri; + cri.end = false; + cri.region = j; + text.write[p_line].region_info[i] = cri; + i += lr - 1; - break; + break; + } } /* END */ lr = cr.end_key.length(); - if (lr == 0 || lr > left) - continue; - - kc = cr.end_key.c_str(); + if (lr != 0 && lr <= left) { + kc = cr.end_key.c_str(); - match = true; + match = true; - for (int k = 0; k < lr; k++) { - if (kc[k] != str[i + k]) { - match = false; - break; + for (int k = 0; k < lr; k++) { + if (kc[k] != str[i + k]) { + match = false; + break; + } } - } - if (match) { + if (match) { - ColorRegionInfo cri; - cri.end = true; - cri.region = j; - text.write[p_line].region_info[i] = cri; - i += lr - 1; + ColorRegionInfo cri; + cri.end = true; + cri.region = j; + text.write[p_line].region_info[i] = cri; + i += lr - 1; - break; + break; + } } } } @@ -935,7 +936,7 @@ void TextEdit::_notification(int p_what) { int minimap_line = (v_scroll->get_max() <= minimap_visible_lines) ? -1 : first_visible_line; if (minimap_line >= 0) { minimap_line -= num_lines_from_rows(first_visible_line, 0, -num_lines_before, wi); - minimap_line -= (smooth_scroll_enabled ? 1 : 0); + minimap_line -= (minimap_line > 0 && smooth_scroll_enabled ? 1 : 0); } int minimap_draw_amount = minimap_visible_lines + times_line_wraps(minimap_line + 1); @@ -1723,7 +1724,9 @@ void TextEdit::_notification(int p_what) { end = font->get_string_size(l.substr(0, l.rfind(String::chr(0xFFFF)))).x; } - draw_string(font, hint_ofs + sb->get_offset() + Vector2(0, font->get_ascent() + font->get_height() * i + spacing), l.replace(String::chr(0xFFFF), ""), font_color); + Point2 round_ofs = hint_ofs + sb->get_offset() + Vector2(0, font->get_ascent() + font->get_height() * i + spacing); + round_ofs = round_ofs.round(); + draw_string(font, round_ofs, l.replace(String::chr(0xFFFF), ""), font_color); if (end > 0) { Vector2 b = hint_ofs + sb->get_offset() + Vector2(begin, font->get_height() + font->get_height() * i + spacing - 1); draw_line(b, b + Vector2(end - begin, 0), font_color); @@ -2146,7 +2149,7 @@ void TextEdit::_get_minimap_mouse_row(const Point2i &p_mouse, int &r_row) const int minimap_line = (v_scroll->get_max() <= minimap_visible_lines) ? -1 : first_visible_line; if (first_visible_line > 0 && minimap_line >= 0) { minimap_line -= num_lines_from_rows(first_visible_line, 0, -num_lines_before, wi); - minimap_line -= (smooth_scroll_enabled ? 1 : 0); + minimap_line -= (minimap_line > 0 && smooth_scroll_enabled ? 1 : 0); } else { minimap_line = 0; } @@ -2843,19 +2846,30 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { // No need to indent if we are going upwards. if (auto_indent && !(k->get_command() && k->get_shift())) { - // Indent once again if previous line will end with ':' or '{' and the line is not a comment + // Indent once again if previous line will end with ':','{','[','(' and the line is not a comment // (i.e. colon/brace precedes current cursor position). - if (cursor.column > 0 && (text[cursor.line][cursor.column - 1] == ':' || text[cursor.line][cursor.column - 1] == '{') && !is_line_comment(cursor.line)) { - if (indent_using_spaces) { - ins += space_indent; - } else { - ins += "\t"; - } + if (cursor.column > 0) { + char prev_char = text[cursor.line][cursor.column - 1]; + switch (prev_char) { + case ':': + case '{': + case '[': + case '(': { + if (!is_line_comment(cursor.line)) { + if (indent_using_spaces) { + ins += space_indent; + } else { + ins += "\t"; + } - // No need to move the brace below if we are not taking the text with us. - if (text[cursor.line][cursor.column] == '}' && !k->get_command()) { - brace_indent = true; - ins += "\n" + ins.substr(1, ins.length() - 2); + // No need to move the brace below if we are not taking the text with us. + char closing_char = _get_right_pair_symbol(prev_char); + if ((closing_char != 0) && (closing_char == text[cursor.line][cursor.column]) && !k->get_command()) { + brace_indent = true; + ins += "\n" + ins.substr(1, ins.length() - 2); + } + } + } break; } } } @@ -4751,6 +4765,9 @@ void TextEdit::set_text(String p_text) { selection.active = false; } + cursor_set_line(0); + cursor_set_column(0); + update(); setting_text = false; }; @@ -5052,15 +5069,18 @@ Map<int, TextEdit::Text::ColorRegionInfo> TextEdit::_get_line_color_region_info( void TextEdit::clear_colors() { keywords.clear(); + member_keywords.clear(); color_regions.clear(); color_region_cache.clear(); syntax_highlighting_cache.clear(); text.clear_width_cache(); + update(); } void TextEdit::add_keyword_color(const String &p_keyword, const Color &p_color) { keywords[p_keyword] = p_color; + syntax_highlighting_cache.clear(); update(); } @@ -5077,12 +5097,14 @@ Color TextEdit::get_keyword_color(String p_keyword) const { void TextEdit::add_color_region(const String &p_begin_key, const String &p_end_key, const Color &p_color, bool p_line_only) { color_regions.push_back(ColorRegion(p_begin_key, p_end_key, p_color, p_line_only)); + syntax_highlighting_cache.clear(); text.clear_width_cache(); update(); } void TextEdit::add_member_keyword(const String &p_keyword, const Color &p_color) { member_keywords[p_keyword] = p_color; + syntax_highlighting_cache.clear(); update(); } @@ -5096,6 +5118,7 @@ Color TextEdit::get_member_color(String p_member) const { void TextEdit::clear_member_keywords() { member_keywords.clear(); + syntax_highlighting_cache.clear(); update(); } @@ -5915,7 +5938,7 @@ void TextEdit::unfold_line(int p_line) { if (!is_folded(p_line) && !is_line_hidden(p_line)) return; - int fold_start = p_line; + int fold_start; for (fold_start = p_line; fold_start > 0; fold_start--) { if (is_folded(fold_start)) break; @@ -6020,6 +6043,7 @@ void TextEdit::undo() { } } + _update_scrollbars(); if (undo_stack_pos->get().type == TextOperation::TYPE_REMOVE) { cursor_set_line(undo_stack_pos->get().to_line); cursor_set_column(undo_stack_pos->get().to_column); @@ -6055,6 +6079,8 @@ void TextEdit::redo() { break; } } + + _update_scrollbars(); cursor_set_line(undo_stack_pos->get().to_line); cursor_set_column(undo_stack_pos->get().to_column); undo_stack_pos = undo_stack_pos->next(); diff --git a/scene/gui/video_player.cpp b/scene/gui/video_player.cpp index 5768f58977..fd2d4a1a11 100644 --- a/scene/gui/video_player.cpp +++ b/scene/gui/video_player.cpp @@ -36,6 +36,10 @@ int VideoPlayer::sp_get_channel_count() const { + if (playback.is_null()) { + return 0; + } + return playback->get_channels(); } @@ -56,6 +60,9 @@ bool VideoPlayer::mix(AudioFrame *p_buffer, int p_frames) { // Called from main thread (eg VideoStreamPlaybackWebm::update) int VideoPlayer::_audio_mix_callback(void *p_udata, const float *p_data, int p_frames) { + ERR_FAIL_NULL_V(p_udata, 0); + ERR_FAIL_NULL_V(p_data, 0); + VideoPlayer *vp = (VideoPlayer *)p_udata; int todo = MIN(vp->resampler.get_writer_space(), p_frames); @@ -71,6 +78,12 @@ int VideoPlayer::_audio_mix_callback(void *p_udata, const float *p_data, int p_f return todo; } +void VideoPlayer::_mix_audios(void *p_self) { + + ERR_FAIL_NULL(p_self); + reinterpret_cast<VideoPlayer *>(p_self)->_mix_audio(); +} + // Called from audio thread void VideoPlayer::_mix_audio() { @@ -143,7 +156,7 @@ void VideoPlayer::_notification(int p_notification) { bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus); - if (stream.is_null() || paused || !playback->is_playing()) + if (stream.is_null() || paused || playback.is_null() || !playback->is_playing()) return; double audio_time = USEC_TO_SEC(OS::get_singleton()->get_ticks_usec()); @@ -358,7 +371,7 @@ void VideoPlayer::set_stream_position(float p_position) { playback->seek(p_position); } -Ref<Texture> VideoPlayer::get_video_texture() { +Ref<Texture> VideoPlayer::get_video_texture() const { if (playback.is_valid()) return playback->get_texture(); @@ -394,9 +407,9 @@ StringName VideoPlayer::get_bus() const { return "Master"; } -void VideoPlayer::_validate_property(PropertyInfo &property) const { +void VideoPlayer::_validate_property(PropertyInfo &p_property) const { - if (property.name == "bus") { + if (p_property.name == "bus") { String options; for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) { @@ -406,7 +419,7 @@ void VideoPlayer::_validate_property(PropertyInfo &property) const { options += name; } - property.hint_string = options; + p_property.hint_string = options; } } diff --git a/scene/gui/video_player.h b/scene/gui/video_player.h index 62fb7838b6..7d2821427e 100644 --- a/scene/gui/video_player.h +++ b/scene/gui/video_player.h @@ -55,7 +55,6 @@ class VideoPlayer : public Control { RID stream_rid; Ref<ImageTexture> texture; - Ref<Image> last_frame; AudioRBResampler resampler; Vector<AudioFrame> mix_buffer; @@ -75,19 +74,19 @@ class VideoPlayer : public Control { void _mix_audio(); static int _audio_mix_callback(void *p_udata, const float *p_data, int p_frames); - static void _mix_audios(void *self) { reinterpret_cast<VideoPlayer *>(self)->_mix_audio(); } + static void _mix_audios(void *p_self); protected: static void _bind_methods(); void _notification(int p_notification); - void _validate_property(PropertyInfo &property) const; + void _validate_property(PropertyInfo &p_property) const; public: Size2 get_minimum_size() const; void set_expand(bool p_expand); bool has_expand() const; - Ref<Texture> get_video_texture(); + Ref<Texture> get_video_texture() const; void set_stream(const Ref<VideoStream> &p_stream); Ref<VideoStream> get_stream() const; diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 7b6c90766f..217dacfbfe 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -2832,6 +2832,8 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("rset_unreliable", "property", "value"), &Node::rset_unreliable); ClassDB::bind_method(D_METHOD("rset_unreliable_id", "peer_id", "property", "value"), &Node::rset_unreliable_id); + ClassDB::bind_method(D_METHOD("update_configuration_warning"), &Node::update_configuration_warning); + BIND_CONSTANT(NOTIFICATION_ENTER_TREE); BIND_CONSTANT(NOTIFICATION_EXIT_TREE); BIND_CONSTANT(NOTIFICATION_MOVED_IN_PARENT); diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 3ee21ce38b..38ad6886b1 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -70,15 +70,25 @@ float SceneTreeTimer::get_time_left() const { } void SceneTreeTimer::set_pause_mode_process(bool p_pause_mode_process) { - if (process_pause != p_pause_mode_process) { - process_pause = p_pause_mode_process; - } + + process_pause = p_pause_mode_process; } bool SceneTreeTimer::is_pause_mode_process() { return process_pause; } +void SceneTreeTimer::release_connections() { + + List<Connection> connections; + get_all_signal_connections(&connections); + + for (List<Connection>::Element *E = connections.front(); E; E = E->next()) { + Connection const &connection = E->get(); + disconnect(connection.signal, connection.target, connection.method); + } +} + SceneTreeTimer::SceneTreeTimer() { time_left = 0; process_pause = true; @@ -612,6 +622,12 @@ void SceneTree::finish() { memdelete(root); //delete root root = NULL; } + + // cleanup timers + for (List<Ref<SceneTreeTimer> >::Element *E = timers.front(); E; E = E->next()) { + E->get()->release_connections(); + } + timers.clear(); } void SceneTree::quit() { diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index d387886d61..ef847ebb5b 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -61,6 +61,8 @@ public: void set_pause_mode_process(bool p_pause_mode_process); bool is_pause_mode_process(); + void release_connections(); + SceneTreeTimer(); }; diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 52ef225364..95536bbb23 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -2374,7 +2374,6 @@ void Viewport::_gui_remove_from_modal_stack(List<Control *>::Element *MI, Object List<Control *>::Element *next = MI->next(); gui.modal_stack.erase(MI); - MI = NULL; if (p_prev_focus_owner) { diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index 985b38f913..d932545da4 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -2741,77 +2741,77 @@ void Animation::copy_track(int p_track, Ref<Animation> p_to_animation) { void Animation::_bind_methods() { ClassDB::bind_method(D_METHOD("add_track", "type", "at_position"), &Animation::add_track, DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("remove_track", "idx"), &Animation::remove_track); + ClassDB::bind_method(D_METHOD("remove_track", "track_idx"), &Animation::remove_track); ClassDB::bind_method(D_METHOD("get_track_count"), &Animation::get_track_count); - ClassDB::bind_method(D_METHOD("track_get_type", "idx"), &Animation::track_get_type); - ClassDB::bind_method(D_METHOD("track_get_path", "idx"), &Animation::track_get_path); - ClassDB::bind_method(D_METHOD("track_set_path", "idx", "path"), &Animation::track_set_path); + ClassDB::bind_method(D_METHOD("track_get_type", "track_idx"), &Animation::track_get_type); + ClassDB::bind_method(D_METHOD("track_get_path", "track_idx"), &Animation::track_get_path); + ClassDB::bind_method(D_METHOD("track_set_path", "track_idx", "path"), &Animation::track_set_path); ClassDB::bind_method(D_METHOD("find_track", "path"), &Animation::find_track); - ClassDB::bind_method(D_METHOD("track_move_up", "idx"), &Animation::track_move_up); - ClassDB::bind_method(D_METHOD("track_move_down", "idx"), &Animation::track_move_down); - ClassDB::bind_method(D_METHOD("track_move_to", "idx", "to_idx"), &Animation::track_move_to); - ClassDB::bind_method(D_METHOD("track_swap", "idx", "with_idx"), &Animation::track_swap); + ClassDB::bind_method(D_METHOD("track_move_up", "track_idx"), &Animation::track_move_up); + ClassDB::bind_method(D_METHOD("track_move_down", "track_idx"), &Animation::track_move_down); + ClassDB::bind_method(D_METHOD("track_move_to", "track_idx", "to_idx"), &Animation::track_move_to); + ClassDB::bind_method(D_METHOD("track_swap", "track_idx", "with_idx"), &Animation::track_swap); - ClassDB::bind_method(D_METHOD("track_set_imported", "idx", "imported"), &Animation::track_set_imported); - ClassDB::bind_method(D_METHOD("track_is_imported", "idx"), &Animation::track_is_imported); + ClassDB::bind_method(D_METHOD("track_set_imported", "track_idx", "imported"), &Animation::track_set_imported); + ClassDB::bind_method(D_METHOD("track_is_imported", "track_idx"), &Animation::track_is_imported); - ClassDB::bind_method(D_METHOD("track_set_enabled", "idx", "enabled"), &Animation::track_set_enabled); - ClassDB::bind_method(D_METHOD("track_is_enabled", "idx"), &Animation::track_is_enabled); + ClassDB::bind_method(D_METHOD("track_set_enabled", "track_idx", "enabled"), &Animation::track_set_enabled); + ClassDB::bind_method(D_METHOD("track_is_enabled", "track_idx"), &Animation::track_is_enabled); - ClassDB::bind_method(D_METHOD("transform_track_insert_key", "idx", "time", "location", "rotation", "scale"), &Animation::transform_track_insert_key); - ClassDB::bind_method(D_METHOD("track_insert_key", "idx", "time", "key", "transition"), &Animation::track_insert_key, DEFVAL(1)); - ClassDB::bind_method(D_METHOD("track_remove_key", "idx", "key_idx"), &Animation::track_remove_key); - ClassDB::bind_method(D_METHOD("track_remove_key_at_position", "idx", "position"), &Animation::track_remove_key_at_position); - ClassDB::bind_method(D_METHOD("track_set_key_value", "idx", "key", "value"), &Animation::track_set_key_value); - ClassDB::bind_method(D_METHOD("track_set_key_transition", "idx", "key_idx", "transition"), &Animation::track_set_key_transition); - ClassDB::bind_method(D_METHOD("track_set_key_time", "idx", "key_idx", "time"), &Animation::track_set_key_time); - ClassDB::bind_method(D_METHOD("track_get_key_transition", "idx", "key_idx"), &Animation::track_get_key_transition); + ClassDB::bind_method(D_METHOD("transform_track_insert_key", "track_idx", "time", "location", "rotation", "scale"), &Animation::transform_track_insert_key); + ClassDB::bind_method(D_METHOD("track_insert_key", "track_idx", "time", "key", "transition"), &Animation::track_insert_key, DEFVAL(1)); + ClassDB::bind_method(D_METHOD("track_remove_key", "track_idx", "key_idx"), &Animation::track_remove_key); + ClassDB::bind_method(D_METHOD("track_remove_key_at_position", "track_idx", "position"), &Animation::track_remove_key_at_position); + ClassDB::bind_method(D_METHOD("track_set_key_value", "track_idx", "key", "value"), &Animation::track_set_key_value); + ClassDB::bind_method(D_METHOD("track_set_key_transition", "track_idx", "key_idx", "transition"), &Animation::track_set_key_transition); + ClassDB::bind_method(D_METHOD("track_set_key_time", "track_idx", "key_idx", "time"), &Animation::track_set_key_time); + ClassDB::bind_method(D_METHOD("track_get_key_transition", "track_idx", "key_idx"), &Animation::track_get_key_transition); - ClassDB::bind_method(D_METHOD("track_get_key_count", "idx"), &Animation::track_get_key_count); - ClassDB::bind_method(D_METHOD("track_get_key_value", "idx", "key_idx"), &Animation::track_get_key_value); - ClassDB::bind_method(D_METHOD("track_get_key_time", "idx", "key_idx"), &Animation::track_get_key_time); - ClassDB::bind_method(D_METHOD("track_find_key", "idx", "time", "exact"), &Animation::track_find_key, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("track_get_key_count", "track_idx"), &Animation::track_get_key_count); + ClassDB::bind_method(D_METHOD("track_get_key_value", "track_idx", "key_idx"), &Animation::track_get_key_value); + ClassDB::bind_method(D_METHOD("track_get_key_time", "track_idx", "key_idx"), &Animation::track_get_key_time); + ClassDB::bind_method(D_METHOD("track_find_key", "track_idx", "time", "exact"), &Animation::track_find_key, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("track_set_interpolation_type", "idx", "interpolation"), &Animation::track_set_interpolation_type); - ClassDB::bind_method(D_METHOD("track_get_interpolation_type", "idx"), &Animation::track_get_interpolation_type); + ClassDB::bind_method(D_METHOD("track_set_interpolation_type", "track_idx", "interpolation"), &Animation::track_set_interpolation_type); + ClassDB::bind_method(D_METHOD("track_get_interpolation_type", "track_idx"), &Animation::track_get_interpolation_type); - ClassDB::bind_method(D_METHOD("track_set_interpolation_loop_wrap", "idx", "interpolation"), &Animation::track_set_interpolation_loop_wrap); - ClassDB::bind_method(D_METHOD("track_get_interpolation_loop_wrap", "idx"), &Animation::track_get_interpolation_loop_wrap); + ClassDB::bind_method(D_METHOD("track_set_interpolation_loop_wrap", "track_idx", "interpolation"), &Animation::track_set_interpolation_loop_wrap); + ClassDB::bind_method(D_METHOD("track_get_interpolation_loop_wrap", "track_idx"), &Animation::track_get_interpolation_loop_wrap); - ClassDB::bind_method(D_METHOD("transform_track_interpolate", "idx", "time_sec"), &Animation::_transform_track_interpolate); - ClassDB::bind_method(D_METHOD("value_track_set_update_mode", "idx", "mode"), &Animation::value_track_set_update_mode); - ClassDB::bind_method(D_METHOD("value_track_get_update_mode", "idx"), &Animation::value_track_get_update_mode); + ClassDB::bind_method(D_METHOD("transform_track_interpolate", "track_idx", "time_sec"), &Animation::_transform_track_interpolate); + ClassDB::bind_method(D_METHOD("value_track_set_update_mode", "track_idx", "mode"), &Animation::value_track_set_update_mode); + ClassDB::bind_method(D_METHOD("value_track_get_update_mode", "track_idx"), &Animation::value_track_get_update_mode); - ClassDB::bind_method(D_METHOD("value_track_get_key_indices", "idx", "time_sec", "delta"), &Animation::_value_track_get_key_indices); + ClassDB::bind_method(D_METHOD("value_track_get_key_indices", "track_idx", "time_sec", "delta"), &Animation::_value_track_get_key_indices); - ClassDB::bind_method(D_METHOD("method_track_get_key_indices", "idx", "time_sec", "delta"), &Animation::_method_track_get_key_indices); - ClassDB::bind_method(D_METHOD("method_track_get_name", "idx", "key_idx"), &Animation::method_track_get_name); - ClassDB::bind_method(D_METHOD("method_track_get_params", "idx", "key_idx"), &Animation::method_track_get_params); + ClassDB::bind_method(D_METHOD("method_track_get_key_indices", "track_idx", "time_sec", "delta"), &Animation::_method_track_get_key_indices); + ClassDB::bind_method(D_METHOD("method_track_get_name", "track_idx", "key_idx"), &Animation::method_track_get_name); + ClassDB::bind_method(D_METHOD("method_track_get_params", "track_idx", "key_idx"), &Animation::method_track_get_params); - ClassDB::bind_method(D_METHOD("bezier_track_insert_key", "track", "time", "value", "in_handle", "out_handle"), &Animation::bezier_track_insert_key, DEFVAL(Vector2()), DEFVAL(Vector2())); + ClassDB::bind_method(D_METHOD("bezier_track_insert_key", "track_idx", "time", "value", "in_handle", "out_handle"), &Animation::bezier_track_insert_key, DEFVAL(Vector2()), DEFVAL(Vector2())); - ClassDB::bind_method(D_METHOD("bezier_track_set_key_value", "idx", "key_idx", "value"), &Animation::bezier_track_set_key_value); - ClassDB::bind_method(D_METHOD("bezier_track_set_key_in_handle", "idx", "key_idx", "in_handle"), &Animation::bezier_track_set_key_in_handle); - ClassDB::bind_method(D_METHOD("bezier_track_set_key_out_handle", "idx", "key_idx", "out_handle"), &Animation::bezier_track_set_key_out_handle); + ClassDB::bind_method(D_METHOD("bezier_track_set_key_value", "track_idx", "key_idx", "value"), &Animation::bezier_track_set_key_value); + ClassDB::bind_method(D_METHOD("bezier_track_set_key_in_handle", "track_idx", "key_idx", "in_handle"), &Animation::bezier_track_set_key_in_handle); + ClassDB::bind_method(D_METHOD("bezier_track_set_key_out_handle", "track_idx", "key_idx", "out_handle"), &Animation::bezier_track_set_key_out_handle); - ClassDB::bind_method(D_METHOD("bezier_track_get_key_value", "idx", "key_idx"), &Animation::bezier_track_get_key_value); - ClassDB::bind_method(D_METHOD("bezier_track_get_key_in_handle", "idx", "key_idx"), &Animation::bezier_track_get_key_in_handle); - ClassDB::bind_method(D_METHOD("bezier_track_get_key_out_handle", "idx", "key_idx"), &Animation::bezier_track_get_key_out_handle); + ClassDB::bind_method(D_METHOD("bezier_track_get_key_value", "track_idx", "key_idx"), &Animation::bezier_track_get_key_value); + ClassDB::bind_method(D_METHOD("bezier_track_get_key_in_handle", "track_idx", "key_idx"), &Animation::bezier_track_get_key_in_handle); + ClassDB::bind_method(D_METHOD("bezier_track_get_key_out_handle", "track_idx", "key_idx"), &Animation::bezier_track_get_key_out_handle); - ClassDB::bind_method(D_METHOD("bezier_track_interpolate", "track", "time"), &Animation::bezier_track_interpolate); + ClassDB::bind_method(D_METHOD("bezier_track_interpolate", "track_idx", "time"), &Animation::bezier_track_interpolate); - ClassDB::bind_method(D_METHOD("audio_track_insert_key", "track", "time", "stream", "start_offset", "end_offset"), &Animation::audio_track_insert_key, DEFVAL(0), DEFVAL(0)); - ClassDB::bind_method(D_METHOD("audio_track_set_key_stream", "idx", "key_idx", "stream"), &Animation::audio_track_set_key_stream); - ClassDB::bind_method(D_METHOD("audio_track_set_key_start_offset", "idx", "key_idx", "offset"), &Animation::audio_track_set_key_start_offset); - ClassDB::bind_method(D_METHOD("audio_track_set_key_end_offset", "idx", "key_idx", "offset"), &Animation::audio_track_set_key_end_offset); - ClassDB::bind_method(D_METHOD("audio_track_get_key_stream", "idx", "key_idx"), &Animation::audio_track_get_key_stream); - ClassDB::bind_method(D_METHOD("audio_track_get_key_start_offset", "idx", "key_idx"), &Animation::audio_track_get_key_start_offset); - ClassDB::bind_method(D_METHOD("audio_track_get_key_end_offset", "idx", "key_idx"), &Animation::audio_track_get_key_end_offset); + ClassDB::bind_method(D_METHOD("audio_track_insert_key", "track_idx", "time", "stream", "start_offset", "end_offset"), &Animation::audio_track_insert_key, DEFVAL(0), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("audio_track_set_key_stream", "track_idx", "key_idx", "stream"), &Animation::audio_track_set_key_stream); + ClassDB::bind_method(D_METHOD("audio_track_set_key_start_offset", "track_idx", "key_idx", "offset"), &Animation::audio_track_set_key_start_offset); + ClassDB::bind_method(D_METHOD("audio_track_set_key_end_offset", "track_idx", "key_idx", "offset"), &Animation::audio_track_set_key_end_offset); + ClassDB::bind_method(D_METHOD("audio_track_get_key_stream", "track_idx", "key_idx"), &Animation::audio_track_get_key_stream); + ClassDB::bind_method(D_METHOD("audio_track_get_key_start_offset", "track_idx", "key_idx"), &Animation::audio_track_get_key_start_offset); + ClassDB::bind_method(D_METHOD("audio_track_get_key_end_offset", "track_idx", "key_idx"), &Animation::audio_track_get_key_end_offset); - ClassDB::bind_method(D_METHOD("animation_track_insert_key", "track", "time", "animation"), &Animation::animation_track_insert_key); - ClassDB::bind_method(D_METHOD("animation_track_set_key_animation", "idx", "key_idx", "animation"), &Animation::animation_track_set_key_animation); - ClassDB::bind_method(D_METHOD("animation_track_get_key_animation", "idx", "key_idx"), &Animation::animation_track_get_key_animation); + ClassDB::bind_method(D_METHOD("animation_track_insert_key", "track_idx", "time", "animation"), &Animation::animation_track_insert_key); + ClassDB::bind_method(D_METHOD("animation_track_set_key_animation", "track_idx", "key_idx", "animation"), &Animation::animation_track_set_key_animation); + ClassDB::bind_method(D_METHOD("animation_track_get_key_animation", "track_idx", "key_idx"), &Animation::animation_track_get_key_animation); ClassDB::bind_method(D_METHOD("set_length", "time_sec"), &Animation::set_length); ClassDB::bind_method(D_METHOD("get_length"), &Animation::get_length); @@ -2823,7 +2823,7 @@ void Animation::_bind_methods() { ClassDB::bind_method(D_METHOD("get_step"), &Animation::get_step); ClassDB::bind_method(D_METHOD("clear"), &Animation::clear); - ClassDB::bind_method(D_METHOD("copy_track", "track", "to_animation"), &Animation::copy_track); + ClassDB::bind_method(D_METHOD("copy_track", "track_idx", "to_animation"), &Animation::copy_track); ADD_PROPERTY(PropertyInfo(Variant::REAL, "length", PROPERTY_HINT_RANGE, "0.001,99999,0.001"), "set_length", "get_length"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop"); diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp index b5354bc3e2..e6139dd707 100644 --- a/scene/resources/bit_map.cpp +++ b/scene/resources/bit_map.cpp @@ -534,7 +534,7 @@ void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) { return; } - bool bit_value = (p_pixels > 0) ? true : false; + bool bit_value = p_pixels > 0; p_pixels = Math::abs(p_pixels); Rect2i r = Rect2i(0, 0, width, height).clip(p_rect); diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 0dcc184a1d..1a053a18c9 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -432,7 +432,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_font("font", "TextEdit", default_font); - theme->set_color("background_color", "TextEdit", Color(0, 0, 0)); + theme->set_color("background_color", "TextEdit", Color(0, 0, 0, 0)); theme->set_color("completion_background_color", "TextEdit", Color(0.17, 0.16, 0.2)); theme->set_color("completion_selected_color", "TextEdit", Color(0.26, 0.26, 0.27)); theme->set_color("completion_existing_color", "TextEdit", Color(0.87, 0.87, 0.87, 0.13)); diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 0de462d616..31d294b7ca 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -1804,10 +1804,9 @@ RID SpatialMaterial::get_material_rid_for_2d(bool p_shaded, bool p_transparent, material->set_flag(FLAG_SRGB_VERTEX_COLOR, true); material->set_flag(FLAG_ALBEDO_FROM_VERTEX_COLOR, true); material->set_flag(FLAG_USE_ALPHA_SCISSOR, p_cut_alpha); - if (p_billboard) { - material->set_billboard_mode(BILLBOARD_ENABLED); - } else if (p_billboard_y) { - material->set_billboard_mode(BILLBOARD_FIXED_Y); + if (p_billboard || p_billboard_y) { + material->set_flag(FLAG_BILLBOARD_KEEP_SCALE, true); + material->set_billboard_mode(p_billboard_y ? BILLBOARD_FIXED_Y : BILLBOARD_ENABLED); } materials_for_2d[version] = material; diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp index ae18be1695..f1429a7d7b 100644 --- a/scene/resources/theme.cpp +++ b/scene/resources/theme.cpp @@ -37,6 +37,97 @@ void Theme::_emit_theme_changed() { emit_changed(); } +PoolVector<String> Theme::_get_icon_list(const String &p_type) const { + + PoolVector<String> ilret; + List<StringName> il; + + get_icon_list(p_type, &il); + ilret.resize(il.size()); + for (List<StringName>::Element *E = il.front(); E; E = E->next()) { + ilret.push_back(E->get()); + } + return ilret; +} + +PoolVector<String> Theme::_get_stylebox_list(const String &p_type) const { + + PoolVector<String> ilret; + List<StringName> il; + + get_stylebox_list(p_type, &il); + ilret.resize(il.size()); + for (List<StringName>::Element *E = il.front(); E; E = E->next()) { + ilret.push_back(E->get()); + } + return ilret; +} + +PoolVector<String> Theme::_get_stylebox_types(void) const { + + PoolVector<String> ilret; + List<StringName> il; + + get_stylebox_types(&il); + ilret.resize(il.size()); + for (List<StringName>::Element *E = il.front(); E; E = E->next()) { + ilret.push_back(E->get()); + } + return ilret; +} + +PoolVector<String> Theme::_get_font_list(const String &p_type) const { + + PoolVector<String> ilret; + List<StringName> il; + + get_font_list(p_type, &il); + ilret.resize(il.size()); + for (List<StringName>::Element *E = il.front(); E; E = E->next()) { + ilret.push_back(E->get()); + } + return ilret; +} + +PoolVector<String> Theme::_get_color_list(const String &p_type) const { + + PoolVector<String> ilret; + List<StringName> il; + + get_color_list(p_type, &il); + ilret.resize(il.size()); + for (List<StringName>::Element *E = il.front(); E; E = E->next()) { + ilret.push_back(E->get()); + } + return ilret; +} + +PoolVector<String> Theme::_get_constant_list(const String &p_type) const { + + PoolVector<String> ilret; + List<StringName> il; + + get_constant_list(p_type, &il); + ilret.resize(il.size()); + for (List<StringName>::Element *E = il.front(); E; E = E->next()) { + ilret.push_back(E->get()); + } + return ilret; +} + +PoolVector<String> Theme::_get_type_list(const String &p_type) const { + + PoolVector<String> ilret; + List<StringName> il; + + get_type_list(&il); + ilret.resize(il.size()); + for (List<StringName>::Element *E = il.front(); E; E = E->next()) { + ilret.push_back(E->get()); + } + return ilret; +} + bool Theme::_set(const StringName &p_name, const Variant &p_value) { String sname = p_name; @@ -300,6 +391,8 @@ void Theme::clear_icon(const StringName &p_name, const StringName &p_type) { void Theme::get_icon_list(StringName p_type, List<StringName> *p_list) const { + ERR_FAIL_NULL(p_list); + if (!icon_map.has(p_type)) return; @@ -344,6 +437,9 @@ void Theme::clear_shader(const StringName &p_name, const StringName &p_type) { } void Theme::get_shader_list(const StringName &p_type, List<StringName> *p_list) const { + + ERR_FAIL_NULL(p_list); + if (!shader_map.has(p_type)) return; @@ -408,6 +504,8 @@ void Theme::clear_stylebox(const StringName &p_name, const StringName &p_type) { void Theme::get_stylebox_list(StringName p_type, List<StringName> *p_list) const { + ERR_FAIL_NULL(p_list); + if (!style_map.has(p_type)) return; @@ -420,6 +518,8 @@ void Theme::get_stylebox_list(StringName p_type, List<StringName> *p_list) const } void Theme::get_stylebox_types(List<StringName> *p_list) const { + ERR_FAIL_NULL(p_list); + const StringName *key = NULL; while ((key = style_map.next(key))) { p_list->push_back(*key); @@ -478,6 +578,8 @@ void Theme::clear_font(const StringName &p_name, const StringName &p_type) { void Theme::get_font_list(StringName p_type, List<StringName> *p_list) const { + ERR_FAIL_NULL(p_list); + if (!font_map.has(p_type)) return; @@ -526,6 +628,8 @@ void Theme::clear_color(const StringName &p_name, const StringName &p_type) { void Theme::get_color_list(StringName p_type, List<StringName> *p_list) const { + ERR_FAIL_NULL(p_list); + if (!color_map.has(p_type)) return; @@ -574,6 +678,8 @@ void Theme::clear_constant(const StringName &p_name, const StringName &p_type) { void Theme::get_constant_list(StringName p_type, List<StringName> *p_list) const { + ERR_FAIL_NULL(p_list); + if (!constant_map.has(p_type)) return; @@ -637,6 +743,12 @@ void Theme::copy_default_theme() { void Theme::copy_theme(const Ref<Theme> &p_other) { + if (p_other.is_null()) { + clear(); + + return; + } + //these need reconnecting, so add normally { const StringName *K = NULL; @@ -680,8 +792,9 @@ void Theme::copy_theme(const Ref<Theme> &p_other) { void Theme::get_type_list(List<StringName> *p_list) const { - Set<StringName> types; + ERR_FAIL_NULL(p_list); + Set<StringName> types; const StringName *key = NULL; while ((key = icon_map.next(key))) { diff --git a/scene/resources/theme.h b/scene/resources/theme.h index 187694de65..471e5b1a51 100644 --- a/scene/resources/theme.h +++ b/scene/resources/theme.h @@ -52,6 +52,14 @@ class Theme : public Resource { HashMap<StringName, HashMap<StringName, Color> > color_map; HashMap<StringName, HashMap<StringName, int> > constant_map; + PoolVector<String> _get_icon_list(const String &p_type) const; + PoolVector<String> _get_stylebox_list(const String &p_type) const; + PoolVector<String> _get_stylebox_types(void) const; + PoolVector<String> _get_font_list(const String &p_type) const; + PoolVector<String> _get_color_list(const String &p_type) const; + PoolVector<String> _get_constant_list(const String &p_type) const; + PoolVector<String> _get_type_list(const String &p_type) const; + protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; @@ -65,70 +73,6 @@ protected: Ref<Font> default_theme_font; - PoolVector<String> _get_icon_list(const String &p_type) const { - PoolVector<String> ilret; - List<StringName> il; - get_icon_list(p_type, &il); - for (List<StringName>::Element *E = il.front(); E; E = E->next()) { - ilret.push_back(E->get()); - } - return ilret; - } - PoolVector<String> _get_stylebox_list(const String &p_type) const { - PoolVector<String> ilret; - List<StringName> il; - get_stylebox_list(p_type, &il); - for (List<StringName>::Element *E = il.front(); E; E = E->next()) { - ilret.push_back(E->get()); - } - return ilret; - } - PoolVector<String> _get_stylebox_types(void) const { - PoolVector<String> ilret; - List<StringName> il; - get_stylebox_types(&il); - for (List<StringName>::Element *E = il.front(); E; E = E->next()) { - ilret.push_back(E->get()); - } - return ilret; - } - PoolVector<String> _get_font_list(const String &p_type) const { - PoolVector<String> ilret; - List<StringName> il; - get_font_list(p_type, &il); - for (List<StringName>::Element *E = il.front(); E; E = E->next()) { - ilret.push_back(E->get()); - } - return ilret; - } - PoolVector<String> _get_color_list(const String &p_type) const { - PoolVector<String> ilret; - List<StringName> il; - get_color_list(p_type, &il); - for (List<StringName>::Element *E = il.front(); E; E = E->next()) { - ilret.push_back(E->get()); - } - return ilret; - } - PoolVector<String> _get_constant_list(const String &p_type) const { - PoolVector<String> ilret; - List<StringName> il; - get_constant_list(p_type, &il); - for (List<StringName>::Element *E = il.front(); E; E = E->next()) { - ilret.push_back(E->get()); - } - return ilret; - } - PoolVector<String> _get_type_list(const String &p_type) const { - PoolVector<String> ilret; - List<StringName> il; - get_type_list(&il); - for (List<StringName>::Element *E = il.front(); E; E = E->next()) { - ilret.push_back(E->get()); - } - return ilret; - } - static void _bind_methods(); public: diff --git a/scene/resources/video_stream.h b/scene/resources/video_stream.h index eb3bf6770f..81c7b062cc 100644 --- a/scene/resources/video_stream.h +++ b/scene/resources/video_stream.h @@ -63,7 +63,7 @@ public: //virtual int mix(int16_t* p_buffer,int p_frames)=0; - virtual Ref<Texture> get_texture() = 0; + virtual Ref<Texture> get_texture() const = 0; virtual void update(float p_delta) = 0; virtual void set_mix_callback(AudioMixCallback p_callback, void *p_userdata) = 0; diff --git a/servers/physics_2d/physics_2d_server_wrap_mt.cpp b/servers/physics_2d/physics_2d_server_wrap_mt.cpp index c698290fd9..9fc15d9660 100644 --- a/servers/physics_2d/physics_2d_server_wrap_mt.cpp +++ b/servers/physics_2d/physics_2d_server_wrap_mt.cpp @@ -139,6 +139,7 @@ void Physics2DServerWrapMT::finish() { segment_shape_free_cached_ids(); circle_shape_free_cached_ids(); rectangle_shape_free_cached_ids(); + capsule_shape_free_cached_ids(); convex_polygon_shape_free_cached_ids(); concave_polygon_shape_free_cached_ids(); |