summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml38
-rw-r--r--COPYRIGHT.txt2
-rw-r--r--core/bind/core_bind.cpp9
-rw-r--r--core/bind/core_bind.h1
-rw-r--r--core/io/http_client.cpp6
-rw-r--r--core/math/math_funcs.h4
-rw-r--r--core/math/quat.cpp2
-rw-r--r--core/os/input.cpp1
-rw-r--r--core/os/input.h2
-rw-r--r--core/os/os.h12
-rw-r--r--core/ustring.cpp66
-rw-r--r--core/ustring.h7
-rw-r--r--core/variant_op.cpp3
-rw-r--r--doc/classes/@GDScript.xml3
-rw-r--r--doc/classes/ARVRServer.xml36
-rw-r--r--doc/classes/AnimationPlayer.xml6
-rw-r--r--doc/classes/Array.xml5
-rw-r--r--doc/classes/AudioServer.xml20
-rw-r--r--doc/classes/Basis.xml1
-rw-r--r--doc/classes/Camera.xml4
-rw-r--r--doc/classes/CanvasLayer.xml6
-rw-r--r--doc/classes/Control.xml6
-rw-r--r--doc/classes/Dictionary.xml2
-rw-r--r--doc/classes/EditorPlugin.xml24
-rw-r--r--doc/classes/File.xml4
-rw-r--r--doc/classes/Image.xml78
-rw-r--r--doc/classes/Input.xml2
-rw-r--r--doc/classes/ItemList.xml25
-rw-r--r--doc/classes/MultiplayerAPI.xml131
-rw-r--r--doc/classes/Navigation.xml22
-rw-r--r--doc/classes/Navigation2D.xml9
-rw-r--r--doc/classes/Node.xml29
-rw-r--r--doc/classes/OS.xml99
-rw-r--r--doc/classes/OptionButton.xml7
-rw-r--r--doc/classes/PopupMenu.xml73
-rw-r--r--doc/classes/Quat.xml1
-rw-r--r--doc/classes/SceneTree.xml4
-rw-r--r--doc/classes/ScrollContainer.xml12
-rw-r--r--doc/classes/TabContainer.xml16
-rw-r--r--doc/classes/Tabs.xml16
-rw-r--r--doc/classes/TextEdit.xml16
-rw-r--r--doc/classes/TextureProgress.xml6
-rw-r--r--doc/classes/TileSet.xml24
-rw-r--r--doc/classes/Transform.xml1
-rw-r--r--doc/classes/Vector2.xml4
-rw-r--r--doc/classes/VideoPlayer.xml6
-rw-r--r--doc/classes/WebSocketClient.xml4
-rw-r--r--drivers/coreaudio/audio_driver_coreaudio.h2
-rw-r--r--drivers/pulseaudio/audio_driver_pulseaudio.cpp55
-rw-r--r--drivers/pulseaudio/audio_driver_pulseaudio.h2
-rw-r--r--drivers/wasapi/audio_driver_wasapi.cpp11
-rw-r--r--editor/editor_node.cpp52
-rw-r--r--editor/editor_node.h1
-rw-r--r--editor/icons/icon_editor_position.svg69
-rw-r--r--editor/icons/icon_editor_position_previous.svg62
-rw-r--r--editor/icons/icon_editor_position_unselected.svg66
-rw-r--r--editor/plugins/asset_library_editor_plugin.cpp2
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp654
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h12
-rw-r--r--main/input_default.cpp18
-rw-r--r--main/input_default.h4
-rw-r--r--misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj34
-rw-r--r--misc/dist/ios_xcode/godot_ios/Default-568h@2x.pngbin564 -> 0 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Default-667h@2x.pngbin817 -> 0 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Default-Landscape-1366h@2x.pngbin32836 -> 0 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Default-Landscape-736h@3x.pngbin2582 -> 0 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Default-Landscape@2x.pngbin3131 -> 0 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Default-Portrait-1366h@2x.pngbin33309 -> 0 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Default-Portrait-736h@3x.pngbin1676 -> 0 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Default-Portrait@2x.pngbin33309 -> 0 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Contents.json102
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-480h@2x.pngbin0 -> 13558 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.pngbin0 -> 17911 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-667h@2x.pngbin0 -> 23513 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-736h@3x.pngbin0 -> 53789 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-X.pngbin0 -> 51462 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape.pngbin0 -> 18386 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape@2x.pngbin0 -> 61558 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-736h@3x.pngbin0 -> 56053 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-X.pngbin0 -> 53983 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait.pngbin0 -> 18753 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait@2x.pngbin0 -> 62336 bytes
-rwxr-xr-xmisc/travis/scons-local-osx.sh4
-rw-r--r--modules/bullet/area_bullet.cpp24
-rw-r--r--modules/bullet/area_bullet.h4
-rw-r--r--modules/bullet/collision_object_bullet.cpp2
-rw-r--r--modules/bullet/space_bullet.cpp7
-rw-r--r--modules/enet/networked_multiplayer_enet.cpp176
-rw-r--r--modules/enet/networked_multiplayer_enet.h7
-rw-r--r--modules/gdnative/doc_classes/GDNativeLibrary.xml8
-rw-r--r--modules/gdnative/gdnative/string.cpp18
-rw-r--r--modules/gdnative/gdnative_api.json14
-rw-r--r--modules/gdnative/include/gdnative/string.h7
-rw-r--r--modules/mono/glue/cs_files/Basis.cs4
-rw-r--r--modules/mono/glue/cs_files/Mathf.cs81
-rw-r--r--modules/mono/glue/cs_files/MathfEx.cs39
-rw-r--r--modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptMathConstant.xml4
-rw-r--r--modules/websocket/emws_peer.cpp6
-rw-r--r--modules/websocket/emws_server.cpp13
-rw-r--r--modules/websocket/emws_server.h3
-rw-r--r--modules/websocket/lws_peer.cpp39
-rw-r--r--modules/websocket/lws_server.cpp18
-rw-r--r--modules/websocket/lws_server.h3
-rw-r--r--modules/websocket/websocket_peer.cpp2
-rw-r--r--modules/websocket/websocket_server.cpp3
-rw-r--r--modules/websocket/websocket_server.h4
-rw-r--r--platform/android/java/src/org/godotengine/godot/GodotView.java4
-rw-r--r--platform/iphone/app_delegate.mm103
-rw-r--r--platform/iphone/export/export.cpp67
-rw-r--r--platform/iphone/gl_view.h2
-rw-r--r--platform/iphone/gl_view.mm15
-rw-r--r--platform/iphone/os_iphone.cpp6
-rw-r--r--platform/iphone/os_iphone.h1
-rw-r--r--platform/osx/os_osx.mm5
-rw-r--r--platform/windows/os_windows.cpp10
-rw-r--r--platform/x11/os_x11.cpp2
-rw-r--r--scene/2d/animated_sprite.cpp4
-rw-r--r--scene/2d/animated_sprite.h1
-rw-r--r--scene/2d/back_buffer_copy.cpp4
-rw-r--r--scene/2d/back_buffer_copy.h1
-rw-r--r--scene/2d/canvas_item.cpp11
-rw-r--r--scene/2d/canvas_item.h46
-rw-r--r--scene/2d/collision_polygon_2d.cpp4
-rw-r--r--scene/2d/collision_polygon_2d.h1
-rw-r--r--scene/2d/collision_shape_2d.cpp5
-rw-r--r--scene/2d/collision_shape_2d.h1
-rw-r--r--scene/2d/light_2d.cpp4
-rw-r--r--scene/2d/light_2d.h1
-rw-r--r--scene/2d/line_2d.cpp4
-rw-r--r--scene/2d/line_2d.h1
-rw-r--r--scene/2d/node_2d.cpp50
-rw-r--r--scene/2d/node_2d.h8
-rw-r--r--scene/2d/path_2d.cpp4
-rw-r--r--scene/2d/path_2d.h1
-rw-r--r--scene/2d/polygon_2d.cpp4
-rw-r--r--scene/2d/polygon_2d.h1
-rw-r--r--scene/2d/position_2d.cpp4
-rw-r--r--scene/2d/position_2d.h1
-rw-r--r--scene/2d/ray_cast_2d.cpp10
-rw-r--r--scene/2d/screen_button.cpp4
-rw-r--r--scene/2d/screen_button.h1
-rw-r--r--scene/2d/sprite.cpp8
-rw-r--r--scene/2d/sprite.h4
-rw-r--r--scene/2d/tile_map.cpp4
-rw-r--r--scene/2d/tile_map.h1
-rw-r--r--scene/2d/visibility_notifier_2d.cpp4
-rw-r--r--scene/2d/visibility_notifier_2d.h1
-rw-r--r--scene/3d/physics_body.cpp2
-rw-r--r--scene/3d/ray_cast.cpp10
-rw-r--r--scene/animation/animation_player.cpp4
-rw-r--r--scene/gui/base_button.cpp2
-rw-r--r--scene/gui/control.cpp10
-rw-r--r--scene/gui/control.h5
-rw-r--r--scene/gui/item_list.cpp4
-rw-r--r--scene/gui/line_edit.cpp20
-rw-r--r--scene/gui/line_edit.h1
-rw-r--r--scene/gui/option_button.cpp2
-rw-r--r--scene/gui/popup_menu.cpp7
-rw-r--r--scene/gui/popup_menu.h1
-rw-r--r--scene/gui/scroll_bar.cpp16
-rw-r--r--scene/gui/scroll_container.cpp80
-rw-r--r--scene/gui/scroll_container.h6
-rw-r--r--scene/gui/tab_container.cpp161
-rw-r--r--scene/gui/tab_container.h12
-rw-r--r--scene/gui/tabs.cpp118
-rw-r--r--scene/gui/tabs.h7
-rw-r--r--scene/gui/text_edit.cpp13
-rw-r--r--scene/gui/tree.cpp16
-rw-r--r--scene/main/node.cpp22
-rw-r--r--scene/main/node.h2
-rw-r--r--scene/main/viewport.cpp2
-rw-r--r--servers/arvr_server.cpp2
-rw-r--r--servers/visual/shader_language.cpp6
-rw-r--r--thirdparty/README.md6
-rw-r--r--thirdparty/fonts/Hack_Regular.ttfbin307420 -> 309408 bytes
-rw-r--r--thirdparty/fonts/LICENSE_Hack.md4
177 files changed, 2679 insertions, 768 deletions
diff --git a/.travis.yml b/.travis.yml
index 4bad241c53..57580c1c41 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,7 +8,8 @@ env:
global:
- SCONS_CACHE=$HOME/.scons_cache
- SCONS_CACHE_LIMIT=1024
- - OPTIONS="verbose=yes progress=no openmp=no gdnative_wrapper=yes"
+ - OPTIONS="verbose=yes progress=no gdnative_wrapper=yes"
+ - secure: AnjB84ZZYDxDQdWwsT9PJrD5tEnwcauzvVeSG+us2GxgfyJ2HFArkZ/IjlvbsfYIiiHghJ2Ssy9yPtUT921BtNNHoWuN/8YQziGrlwiSfLGmS/n8GFH22OYwzDSa7UC7ODts5La2I4+JzrdtF933TwE+4QzH4E3GyaKbznh402E=
cache:
directories:
@@ -18,7 +19,7 @@ matrix:
include:
- env: STATIC_CHECKS=yes
os: linux
- compiler: clang
+ compiler: gcc
- env: GODOT_TARGET=x11 TOOLS=yes CACHE_NAME=${GODOT_TARGET}-gcc-tools"
os: linux
compiler: gcc
@@ -31,12 +32,14 @@ matrix:
- env: GODOT_TARGET=android TOOLS=no CACHE_NAME=${GODOT_TARGET}-gcc
os: linux
compiler: gcc
- #- env: GODOT_TARGET=osx TOOLS=yes CACHE_NAME=${GODOT_TARGET}-clang-tools
- # os: osx
- # compiler: clang
- #- env: GODOT_TARGET=iphone TOOLS=no CACHE_NAME=${GODOT_TARGET}-clang
- # os: osx
- # compiler: clang
+ - env: GODOT_TARGET=osx TOOLS=yes CACHE_NAME=${GODOT_TARGET}-clang-tools
+ os: osx
+ osx_image: xcode9.3
+ compiler: clang
+ - env: GODOT_TARGET=iphone TOOLS=no CACHE_NAME=${GODOT_TARGET}-clang
+ os: osx
+ osx_image: xcode9.3
+ compiler: clang
addons:
apt:
@@ -69,6 +72,25 @@ addons:
# For style checks.
- clang-format-5.0
+ coverity_scan:
+ project:
+ name: "godotengine/godot"
+ description: "Godot Engine Coverity scans"
+ notification_email: coverity@godotengine.org
+ build_command_prepend: ""
+ build_command: "scons p=x11 -j2 $OPTIONS"
+ branch_pattern: coverity_scan
+
+before_install:
+ - if [ "$STATIC_CHECKS" = "yes" ]; then
+ unset SCONS_CACHE;
+ else
+ if [ "$TRAVIS_BRANCH" = "coverity_scan" ]; then
+ echo "This job runs in the Coverity Scan branch and is not the STATIC_CHECKS job meant for it, so aborting with exit code 0.";
+ travis_terminate 0;
+ fi;
+ fi
+
install:
- if [ "$TRAVIS_OS_NAME" = "linux" ] && [ "$GODOT_TARGET" = "android" ]; then
misc/travis/android-tools-linux.sh;
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index 0f421741b1..6b6ee4c509 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -157,7 +157,7 @@ License: OFL-1.1
Files: ./thirdparty/fonts/Hack_Regular.ttf
Comment: Hack font
-Copyright: 2017, Source Foundry Authors
+Copyright: 2018, Source Foundry Authors
2003, Bitstream Inc.
License: Expat and Bitstream Vera Fonts Copyright
diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp
index 20adf7ff02..85793f127d 100644
--- a/core/bind/core_bind.cpp
+++ b/core/bind/core_bind.cpp
@@ -288,6 +288,10 @@ void _OS::set_window_size(const Size2 &p_size) {
OS::get_singleton()->set_window_size(p_size);
}
+Rect2 _OS::get_window_safe_area() const {
+ return OS::get_singleton()->get_window_safe_area();
+}
+
void _OS::set_window_fullscreen(bool p_enabled) {
OS::get_singleton()->set_window_fullscreen(p_enabled);
}
@@ -1032,9 +1036,9 @@ void _OS::_bind_methods() {
//ClassDB::bind_method(D_METHOD("get_fullscreen_mode_list","screen"),&_OS::get_fullscreen_mode_list,DEFVAL(0));
ClassDB::bind_method(D_METHOD("get_video_driver_count"), &_OS::get_video_driver_count);
- ClassDB::bind_method(D_METHOD("get_video_driver_name"), &_OS::get_video_driver_name);
+ ClassDB::bind_method(D_METHOD("get_video_driver_name", "driver"), &_OS::get_video_driver_name);
ClassDB::bind_method(D_METHOD("get_audio_driver_count"), &_OS::get_audio_driver_count);
- ClassDB::bind_method(D_METHOD("get_audio_driver_name"), &_OS::get_audio_driver_name);
+ ClassDB::bind_method(D_METHOD("get_audio_driver_name", "driver"), &_OS::get_audio_driver_name);
ClassDB::bind_method(D_METHOD("get_screen_count"), &_OS::get_screen_count);
ClassDB::bind_method(D_METHOD("get_current_screen"), &_OS::get_current_screen);
@@ -1046,6 +1050,7 @@ void _OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_window_position", "position"), &_OS::set_window_position);
ClassDB::bind_method(D_METHOD("get_window_size"), &_OS::get_window_size);
ClassDB::bind_method(D_METHOD("set_window_size", "size"), &_OS::set_window_size);
+ ClassDB::bind_method(D_METHOD("get_window_safe_area"), &_OS::get_window_safe_area);
ClassDB::bind_method(D_METHOD("set_window_fullscreen", "enabled"), &_OS::set_window_fullscreen);
ClassDB::bind_method(D_METHOD("is_window_fullscreen"), &_OS::is_window_fullscreen);
ClassDB::bind_method(D_METHOD("set_window_resizable", "enabled"), &_OS::set_window_resizable);
diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h
index c40abd51f6..1790c68757 100644
--- a/core/bind/core_bind.h
+++ b/core/bind/core_bind.h
@@ -162,6 +162,7 @@ public:
virtual void set_window_position(const Point2 &p_position);
virtual Size2 get_window_size() const;
virtual Size2 get_real_window_size() const;
+ virtual Rect2 get_window_safe_area() const;
virtual void set_window_size(const Size2 &p_size);
virtual void set_window_fullscreen(bool p_enabled);
virtual bool is_window_fullscreen() const;
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp
index c787b7ec4c..9e301ccac5 100644
--- a/core/io/http_client.cpp
+++ b/core/io/http_client.cpp
@@ -618,14 +618,14 @@ String HTTPClient::query_string_from_dict(const Dictionary &p_dict) {
String query = "";
Array keys = p_dict.keys();
for (int i = 0; i < keys.size(); ++i) {
- String encoded_key = String(keys[i]).percent_encode();
+ String encoded_key = String(keys[i]).http_escape();
Variant value = p_dict[keys[i]];
switch (value.get_type()) {
case Variant::ARRAY: {
// Repeat the key with every values
Array values = value;
for (int j = 0; j < values.size(); ++j) {
- query += "&" + encoded_key + "=" + String(values[j]).percent_encode();
+ query += "&" + encoded_key + "=" + String(values[j]).http_escape();
}
break;
}
@@ -636,7 +636,7 @@ String HTTPClient::query_string_from_dict(const Dictionary &p_dict) {
}
default: {
// Add the key-value pair
- query += "&" + encoded_key + "=" + String(value).percent_encode();
+ query += "&" + encoded_key + "=" + String(value).http_escape();
}
}
}
diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h
index 26e87f009b..20001bb9a6 100644
--- a/core/math/math_funcs.h
+++ b/core/math/math_funcs.h
@@ -215,11 +215,11 @@ public:
}
static _ALWAYS_INLINE_ double wrapf(double value, double min, double max) {
double rng = max - min;
- return min + (value - min) - (rng * Math::floor((value - min) / rng));
+ return value - (rng * Math::floor((value - min) / rng));
}
static _ALWAYS_INLINE_ float wrapf(float value, float min, float max) {
float rng = max - min;
- return min + (value - min) - (rng * Math::floor((value - min) / rng));
+ return value - (rng * Math::floor((value - min) / rng));
}
// double only, as these functions are mainly used by the editor and not performance-critical,
diff --git a/core/math/quat.cpp b/core/math/quat.cpp
index 9aa8b537d2..4f61401ac7 100644
--- a/core/math/quat.cpp
+++ b/core/math/quat.cpp
@@ -89,7 +89,7 @@ void Quat::set_euler_yxz(const Vector3 &p_euler) {
set(sin_a1 * cos_a2 * sin_a3 + cos_a1 * sin_a2 * cos_a3,
sin_a1 * cos_a2 * cos_a3 - cos_a1 * sin_a2 * sin_a3,
- -sin_a1 * sin_a2 * cos_a3 + cos_a1 * sin_a2 * sin_a3,
+ -sin_a1 * sin_a2 * cos_a3 + cos_a1 * cos_a2 * sin_a3,
sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3);
}
diff --git a/core/os/input.cpp b/core/os/input.cpp
index fe24e86e1d..a5b0f91e63 100644
--- a/core/os/input.cpp
+++ b/core/os/input.cpp
@@ -86,6 +86,7 @@ void Input::_bind_methods() {
ClassDB::bind_method(D_METHOD("warp_mouse_position", "to"), &Input::warp_mouse_position);
ClassDB::bind_method(D_METHOD("action_press", "action"), &Input::action_press);
ClassDB::bind_method(D_METHOD("action_release", "action"), &Input::action_release);
+ ClassDB::bind_method(D_METHOD("set_default_cursor_shape", "shape"), &Input::set_default_cursor_shape, DEFVAL(CURSOR_ARROW));
ClassDB::bind_method(D_METHOD("set_custom_mouse_cursor", "image", "shape", "hotspot"), &Input::set_custom_mouse_cursor, DEFVAL(CURSOR_ARROW), DEFVAL(Vector2()));
ClassDB::bind_method(D_METHOD("parse_input_event", "event"), &Input::parse_input_event);
diff --git a/core/os/input.h b/core/os/input.h
index ae6b5f7f0a..027147b987 100644
--- a/core/os/input.h
+++ b/core/os/input.h
@@ -120,6 +120,8 @@ public:
virtual bool is_emulating_touchscreen() const = 0;
+ virtual CursorShape get_default_cursor_shape() = 0;
+ virtual void set_default_cursor_shape(CursorShape p_shape) = 0;
virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()) = 0;
virtual void set_mouse_in_window(bool p_in_window) = 0;
diff --git a/core/os/os.h b/core/os/os.h
index 5a2c998782..943c0498f1 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -205,6 +205,18 @@ public:
virtual void request_attention() {}
virtual void center_window();
+ // Returns window area free of hardware controls and other obstacles.
+ // The application should use this to determine where to place UI elements.
+ //
+ // Keep in mind the area returned is in window coordinates rather than
+ // viewport coordinates - you should perform the conversion on your own.
+ //
+ // The maximum size of the area is Rect2(0, 0, window_size.width, window_size.height).
+ virtual Rect2 get_window_safe_area() const {
+ Size2 window_size = get_window_size();
+ return Rect2(0, 0, window_size.width, window_size.height);
+ }
+
virtual void set_borderless_window(bool p_borderless) {}
virtual bool get_borderless_window() { return 0; }
diff --git a/core/ustring.cpp b/core/ustring.cpp
index d749146998..954f590218 100644
--- a/core/ustring.cpp
+++ b/core/ustring.cpp
@@ -3165,7 +3165,7 @@ String String::word_wrap(int p_chars_per_line) const {
return ret;
}
-String String::percent_encode() const {
+String String::http_escape() const {
const CharString temp = utf8();
String res;
for (int i = 0; i < temp.length(); ++i) {
@@ -3189,7 +3189,7 @@ String String::percent_encode() const {
return res;
}
-String String::percent_decode() const {
+String String::http_unescape() const {
String res;
for (int i = 0; i < length(); ++i) {
if (ord_at(i) == '%' && i + 2 < length()) {
@@ -3727,6 +3727,68 @@ String String::plus_file(const String &p_file) const {
return *this + "/" + p_file;
}
+String String::percent_encode() const {
+
+ CharString cs = utf8();
+ String encoded;
+ for (int i = 0; i < cs.length(); i++) {
+ uint8_t c = cs[i];
+ if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '~' || c == '.') {
+
+ char p[2] = { (char)c, 0 };
+ encoded += p;
+ } else {
+ char p[4] = { '%', 0, 0, 0 };
+ static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+ p[1] = hex[c >> 4];
+ p[2] = hex[c & 0xF];
+ encoded += p;
+ }
+ }
+
+ return encoded;
+}
+String String::percent_decode() const {
+
+ CharString pe;
+
+ CharString cs = utf8();
+ for (int i = 0; i < cs.length(); i++) {
+
+ uint8_t c = cs[i];
+ if (c == '%' && i < length() - 2) {
+
+ uint8_t a = LOWERCASE(cs[i + 1]);
+ uint8_t b = LOWERCASE(cs[i + 2]);
+
+ c = 0;
+ if (a >= '0' && a <= '9')
+ c = (a - '0') << 4;
+ else if (a >= 'a' && a <= 'f')
+ c = (a - 'a' + 10) << 4;
+ else
+ continue;
+
+ uint8_t d = 0;
+
+ if (b >= '0' && b <= '9')
+ d = (b - '0');
+ else if (b >= 'a' && b <= 'f')
+ d = (b - 'a' + 10);
+ else
+ continue;
+ c += d;
+ i += 2;
+ }
+ pe.push_back(c);
+ }
+
+ pe.push_back(0);
+
+ return String::utf8(pe.ptr());
+}
+
String String::get_basename() const {
int pos = find_last(".");
diff --git a/core/ustring.h b/core/ustring.h
index 8023c9b95d..bb676ce623 100644
--- a/core/ustring.h
+++ b/core/ustring.h
@@ -226,14 +226,17 @@ public:
String xml_escape(bool p_escape_quotes = false) const;
String xml_unescape() const;
- String percent_encode() const;
- String percent_decode() const;
+ String http_escape() const;
+ String http_unescape() const;
String c_escape() const;
String c_escape_multiline() const;
String c_unescape() const;
String json_escape() const;
String word_wrap(int p_chars_per_line) const;
+ String percent_encode() const;
+ String percent_decode() const;
+
bool is_valid_identifier() const;
bool is_valid_integer() const;
bool is_valid_float() const;
diff --git a/core/variant_op.cpp b/core/variant_op.cpp
index 8ad981ed0e..4e37593915 100644
--- a/core/variant_op.cpp
+++ b/core/variant_op.cpp
@@ -3728,8 +3728,9 @@ static const char *_op_names[Variant::OP_MAX] = {
"*",
"/",
"- (negation)",
+ "+ (positive)",
"%",
- "..",
+ "+ (concatenation)",
"<<",
">>",
"&",
diff --git a/doc/classes/@GDScript.xml b/doc/classes/@GDScript.xml
index 07e428faad..acece05510 100644
--- a/doc/classes/@GDScript.xml
+++ b/doc/classes/@GDScript.xml
@@ -326,7 +326,8 @@
<argument index="0" name="s" type="float">
</argument>
<description>
- Raises the Euler's constant [b]e[/b] to the power of [code]s[/code] and returns it. [b]e[/b] has an approximate value of 2.71828.
+ The natural exponential function. It raises the mathematical constant [b]e[/b] to the power of [code]s[/code] and returns it.
+ [b]e[/b] has an approximate value of 2.71828.
[codeblock]
a = exp(2) # approximately 7.39
[/codeblock]
diff --git a/doc/classes/ARVRServer.xml b/doc/classes/ARVRServer.xml
index 965fad1961..a7d3e46684 100644
--- a/doc/classes/ARVRServer.xml
+++ b/doc/classes/ARVRServer.xml
@@ -36,6 +36,13 @@
Find an interface by its name. Say that you're making a game that uses specific capabilities of an AR/VR platform you can find the interface for that platform by name and initialize it.
</description>
</method>
+ <method name="get_hmd_transform">
+ <return type="Transform">
+ </return>
+ <description>
+ Returns the primary interface's transformation.
+ </description>
+ </method>
<method name="get_interface" qualifiers="const">
<return type="ARVRInterface">
</return>
@@ -59,6 +66,24 @@
Returns a list of available interfaces with both id and name of the interface.
</description>
</method>
+ <method name="get_last_commit_usec">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_last_frame_usec">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_last_process_usec">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="get_reference_frame" qualifiers="const">
<return type="Transform">
</return>
@@ -82,17 +107,10 @@
Get the number of trackers currently registered.
</description>
</method>
- <method name="set_primary_interface">
- <return type="void">
- </return>
- <argument index="0" name="interface" type="ARVRInterface">
- </argument>
- <description>
- Changes the primary interface to the specified interface. Again mostly exposed for GDNative interfaces.
- </description>
- </method>
</methods>
<members>
+ <member name="primary_interface" type="ARVRInterface" setter="set_primary_interface" getter="get_primary_interface">
+ </member>
<member name="world_scale" type="float" setter="set_world_scale" getter="get_world_scale">
Allows you to adjust the scale to your game's units. Most AR/VR platforms assume a scale of 1 game world unit = 1 meter in the real world.
</member>
diff --git a/doc/classes/AnimationPlayer.xml b/doc/classes/AnimationPlayer.xml
index ed859169fd..673bbbea59 100644
--- a/doc/classes/AnimationPlayer.xml
+++ b/doc/classes/AnimationPlayer.xml
@@ -8,7 +8,7 @@
</description>
<tutorials>
http://docs.godotengine.org/en/3.0/getting_started/step_by_step/animations.html
- http://docs.godotengine.org/en/3.0/tutorials/animation/index.html
+ http://docs.godotengine.org/en/3.0/tutorials/animation/index.html
</tutorials>
<demos>
</demos>
@@ -110,7 +110,6 @@
Get the actual playing speed of current animation or 0 if not playing. This speed is the [code]playback_speed[/code] property multiplied by [code]custom_speed[/code] argument specified when calling the [code]play[/code] method.
</description>
</method>
-
<method name="has_animation" qualifiers="const">
<return type="bool">
</return>
@@ -139,8 +138,7 @@
<argument index="3" name="from_end" type="bool" default="false">
</argument>
<description>
- Play the animation with key [code]name[/code]. Custom speed and blend times can be set. If custom speed is negative (-1), 'from_end' being true can play the
- animation backwards.
+ Play the animation with key [code]name[/code]. Custom speed and blend times can be set. If custom speed is negative (-1), 'from_end' being true can play the animation backwards.
</description>
</method>
<method name="play_backwards">
diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml
index 2e2176743f..5f85751c13 100644
--- a/doc/classes/Array.xml
+++ b/doc/classes/Array.xml
@@ -131,8 +131,11 @@
<method name="duplicate">
<return type="Array">
</return>
+ <argument index="0" name="deep" type="bool" default="False">
+ </argument>
<description>
- Returns a copy of this [code]Array[/code].
+ Returns a copy of the array.
+ If [code]deep[/code] is [code]true[/code], a deep copy is be performed: all nested arrays and dictionaries are duplicated and will not be shared with the original array. If [code]false[/code], a shallow copy is made and references to the original nested arrays and dictionaries are kept, so that modifying a sub-array or dictionary in the copy will also impact those referenced in the source array.
</description>
</method>
<method name="empty">
diff --git a/doc/classes/AudioServer.xml b/doc/classes/AudioServer.xml
index 0be91427b1..7b3ba632cc 100644
--- a/doc/classes/AudioServer.xml
+++ b/doc/classes/AudioServer.xml
@@ -126,6 +126,18 @@
Returns the volume of the bus at index [code]bus_idx[/code] in dB.
</description>
</method>
+ <method name="get_device">
+ <return type="String">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_device_list">
+ <return type="Array">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="get_mix_rate" qualifiers="const">
<return type="float">
</return>
@@ -313,6 +325,14 @@
Sets the volume of the bus at index [code]bus_idx[/code] to [code]volume_db[/code].
</description>
</method>
+ <method name="set_device">
+ <return type="void">
+ </return>
+ <argument index="0" name="arg0" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="swap_bus_effects">
<return type="void">
</return>
diff --git a/doc/classes/Basis.xml b/doc/classes/Basis.xml
index f7270fa5f9..554ed99964 100644
--- a/doc/classes/Basis.xml
+++ b/doc/classes/Basis.xml
@@ -8,6 +8,7 @@
For such use, it is composed of a scaling and a rotation matrix, in that order (M = R.S).
</description>
<tutorials>
+ http://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/Camera.xml b/doc/classes/Camera.xml
index 9451ee959f..7f7f152ae9 100644
--- a/doc/classes/Camera.xml
+++ b/doc/classes/Camera.xml
@@ -14,8 +14,10 @@
<method name="clear_current">
<return type="void">
</return>
+ <argument index="0" name="enable_next" type="bool" default="true">
+ </argument>
<description>
- If this is the current Camera, remove it from being current. If it is inside the node tree, request to make the next Camera current, if any.
+ If this is the current Camera, remove it from being current. If [code]enable_next[/code] is true, request to make the next Camera current, if any.
</description>
</method>
<method name="get_camera_transform" qualifiers="const">
diff --git a/doc/classes/CanvasLayer.xml b/doc/classes/CanvasLayer.xml
index b2b0938822..3e4d1b29f7 100644
--- a/doc/classes/CanvasLayer.xml
+++ b/doc/classes/CanvasLayer.xml
@@ -13,11 +13,11 @@
<demos>
</demos>
<methods>
- <method name="get_world_2d" qualifiers="const">
- <return type="World2D">
+ <method name="get_canvas" qualifiers="const">
+ <return type="RID">
</return>
<description>
- Return the [World2D] used by this layer.
+ Returns the RID of the canvas used by this layer.
</description>
</method>
</methods>
diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml
index 95e8622cf4..4b6a9cca8a 100644
--- a/doc/classes/Control.xml
+++ b/doc/classes/Control.xml
@@ -773,6 +773,10 @@
<constant name="NOTIFICATION_MODAL_CLOSE" value="46">
Sent when an open modal dialog closes. See [member show_modal].
</constant>
+ <constant name="NOTIFICATION_SCROLL_BEGIN" value="47">
+ </constant>
+ <constant name="NOTIFICATION_SCROLL_END" value="48">
+ </constant>
<constant name="CURSOR_ARROW" value="0" enum="CursorShape">
Show the system's arrow mouse cursor when the user hovers the node. Use with [method set_default_cursor_shape].
</constant>
@@ -908,6 +912,8 @@
</constant>
<constant name="GROW_DIRECTION_END" value="1" enum="GrowDirection">
</constant>
+ <constant name="GROW_DIRECTION_BOTH" value="2" enum="GrowDirection">
+ </constant>
<constant name="ANCHOR_BEGIN" value="0" enum="Anchor">
Snaps one of the 4 anchor's sides to the origin of the node's [code]Rect[/code], in the top left. Use it with one of the [code]anchor_*[/code] member variables, like [member anchor_left]. To change all 4 anchors at once, use [method set_anchors_preset].
</constant>
diff --git a/doc/classes/Dictionary.xml b/doc/classes/Dictionary.xml
index d7118265f1..3c1413b028 100644
--- a/doc/classes/Dictionary.xml
+++ b/doc/classes/Dictionary.xml
@@ -19,6 +19,8 @@
<method name="duplicate">
<return type="Dictionary">
</return>
+ <argument index="0" name="deep" type="bool" default="False">
+ </argument>
<description>
</description>
</method>
diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml
index 0a8d95d25d..846d6f18ff 100644
--- a/doc/classes/EditorPlugin.xml
+++ b/doc/classes/EditorPlugin.xml
@@ -102,6 +102,21 @@
<description>
</description>
</method>
+ <method name="add_tool_menu_item">
+ <return type="void">
+ </return>
+ <argument index="0" name="name" type="String">
+ </argument>
+ <argument index="1" name="handler" type="Object">
+ </argument>
+ <argument index="2" name="callback" type="String">
+ </argument>
+ <argument index="3" name="ud" type="Variant" default="null">
+ </argument>
+ <description>
+ Adds a custom menu to 'Project > Tools' as [code]name[/code] that calls [code]callback[/code] on an instance of [code]handler[/code] with a parameter [code]ud[/code] when user activates it.
+ </description>
+ </method>
<method name="add_tool_submenu_item">
<return type="void">
</return>
@@ -347,6 +362,15 @@
<description>
</description>
</method>
+ <method name="remove_tool_menu_item">
+ <return type="void">
+ </return>
+ <argument index="0" name="name" type="String">
+ </argument>
+ <description>
+ Removes a menu [code]name[/code] from 'Project > Tools'.
+ </description>
+ </method>
<method name="save_external_data" qualifiers="virtual">
<return type="void">
</return>
diff --git a/doc/classes/File.xml b/doc/classes/File.xml
index 534323348e..bd368e967a 100644
--- a/doc/classes/File.xml
+++ b/doc/classes/File.xml
@@ -163,14 +163,14 @@
Returns a [String] saved in Pascal format from the file.
</description>
</method>
- <method name="get_path">
+ <method name="get_path" qualifiers="const">
<return type="String">
</return>
<description>
Returns the path as a [String] for the current open file.
</description>
</method>
- <method name="get_path_absolute">
+ <method name="get_path_absolute" qualifiers="const">
<return type="String">
</return>
<description>
diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml
index c1bd8d34eb..ea61aced83 100644
--- a/doc/classes/Image.xml
+++ b/doc/classes/Image.xml
@@ -67,6 +67,14 @@
Blits [code]src_rect[/code] area from [code]src[/code] image to this image at the coordinates given by [code]dst[/code]. [code]src[/code] pixel is copied onto [code]dst[/code] if the corresponding [code]mask[/code] pixel's alpha value is not 0. [code]src[/code] image and [code]mask[/code] image [b]must[/b] have the same size (width and height) but they can have different formats.
</description>
</method>
+ <method name="bumpmap_to_normalmap">
+ <return type="void">
+ </return>
+ <argument index="0" name="bump_scale" type="float" default="1.0">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="clear_mipmaps">
<return type="void">
</return>
@@ -436,109 +444,109 @@
<constant name="FORMAT_LA8" value="1" enum="Format">
</constant>
<constant name="FORMAT_R8" value="2" enum="Format">
- OpenGL texture format RED with a single component and a bitdepth of 8.
+ OpenGL texture format RED with a single component and a bitdepth of 8.
</constant>
<constant name="FORMAT_RG8" value="3" enum="Format">
- OpenGL texture format RG with two components and a bitdepth of 8 for each.
+ OpenGL texture format RG with two components and a bitdepth of 8 for each.
</constant>
<constant name="FORMAT_RGB8" value="4" enum="Format">
- OpenGL texture format RGB with three components, each with a bitdepth of 8.
+ OpenGL texture format RGB with three components, each with a bitdepth of 8.
</constant>
<constant name="FORMAT_RGBA8" value="5" enum="Format">
- OpenGL texture format RGBA with four components, each with a bitdepth of 8.
+ OpenGL texture format RGBA with four components, each with a bitdepth of 8.
</constant>
<constant name="FORMAT_RGBA4444" value="6" enum="Format">
- OpenGL texture format RGBA with four components, each with a bitdepth of 4.
+ OpenGL texture format RGBA with four components, each with a bitdepth of 4.
</constant>
<constant name="FORMAT_RGBA5551" value="7" enum="Format">
- OpenGL texture format GL_RGB5_A1 where 5 bits of depth for each component of RGB and one bit for alpha.
+ OpenGL texture format GL_RGB5_A1 where 5 bits of depth for each component of RGB and one bit for alpha.
</constant>
<constant name="FORMAT_RF" value="8" enum="Format">
- OpenGL texture format GL_R32F where there's one component, a 32-bit floating-point value.
+ OpenGL texture format GL_R32F where there's one component, a 32-bit floating-point value.
</constant>
<constant name="FORMAT_RGF" value="9" enum="Format">
- OpenGL texture format GL_RG32F where there are two components, each a 32-bit floating-point values.
+ OpenGL texture format GL_RG32F where there are two components, each a 32-bit floating-point values.
</constant>
<constant name="FORMAT_RGBF" value="10" enum="Format">
- OpenGL texture format GL_RGB32F where there are three components, each a 32-bit floating-point values.
+ OpenGL texture format GL_RGB32F where there are three components, each a 32-bit floating-point values.
</constant>
<constant name="FORMAT_RGBAF" value="11" enum="Format">
- OpenGL texture format GL_RGBA32F where there are four components, each a 32-bit floating-point values.
+ OpenGL texture format GL_RGBA32F where there are four components, each a 32-bit floating-point values.
</constant>
<constant name="FORMAT_RH" value="12" enum="Format">
- OpenGL texture format GL_R32F where there's one component, a 16-bit "half-precision" floating-point value.
+ OpenGL texture format GL_R32F where there's one component, a 16-bit "half-precision" floating-point value.
</constant>
<constant name="FORMAT_RGH" value="13" enum="Format">
- OpenGL texture format GL_RG32F where there's two components, each a 16-bit "half-precision" floating-point value.
+ OpenGL texture format GL_RG32F where there's two components, each a 16-bit "half-precision" floating-point value.
</constant>
<constant name="FORMAT_RGBH" value="14" enum="Format">
- OpenGL texture format GL_RGB32F where there's three components, each a 16-bit "half-precision" floating-point value.
+ OpenGL texture format GL_RGB32F where there's three components, each a 16-bit "half-precision" floating-point value.
</constant>
<constant name="FORMAT_RGBAH" value="15" enum="Format">
- OpenGL texture format GL_RGBA32F where there's four components, each a 16-bit "half-precision" floating-point value.
+ OpenGL texture format GL_RGBA32F where there's four components, each a 16-bit "half-precision" floating-point value.
</constant>
<constant name="FORMAT_RGBE9995" value="16" enum="Format">
- A special OpenGL texture format where the three color components have 9 bits of precision and all three share a single exponent.
+ A special OpenGL texture format where the three color components have 9 bits of precision and all three share a single exponent.
</constant>
<constant name="FORMAT_DXT1" value="17" enum="Format">
- The S3TC texture format that uses Block Compression 1, and is the smallest variation of S3TC, only providing 1 bit of alpha and color data being premultiplied with alpha. More information can be found at https://www.khronos.org/opengl/wiki/S3_Texture_Compression.
+ The S3TC texture format that uses Block Compression 1, and is the smallest variation of S3TC, only providing 1 bit of alpha and color data being premultiplied with alpha. More information can be found at https://www.khronos.org/opengl/wiki/S3_Texture_Compression.
</constant>
<constant name="FORMAT_DXT3" value="18" enum="Format">
- The S3TC texture format that uses Block Compression 2, and color data is interpreted as not having been premultiplied by alpha. Well suited for images with sharp alpha transitions between translucent and opaque areas.
+ The S3TC texture format that uses Block Compression 2, and color data is interpreted as not having been premultiplied by alpha. Well suited for images with sharp alpha transitions between translucent and opaque areas.
</constant>
<constant name="FORMAT_DXT5" value="19" enum="Format">
- The S3TC texture format also known as Block Compression 3 or BC3 that contains 64 bits of alpha channel data followed by 64 bits of DXT1-encoded color data. Color data is not premultiplied by alpha, same as DXT3. DXT5 generally produces superior results for transparency gradients than DXT3.
+ The S3TC texture format also known as Block Compression 3 or BC3 that contains 64 bits of alpha channel data followed by 64 bits of DXT1-encoded color data. Color data is not premultiplied by alpha, same as DXT3. DXT5 generally produces superior results for transparency gradients than DXT3.
</constant>
<constant name="FORMAT_RGTC_R" value="20" enum="Format">
- Texture format that uses Red Green Texture Compression, normalizing the red channel data using the same compression algorithm that DXT5 uses for the alpha channel. More information can be found here https://www.khronos.org/opengl/wiki/Red_Green_Texture_Compression.
+ Texture format that uses Red Green Texture Compression, normalizing the red channel data using the same compression algorithm that DXT5 uses for the alpha channel. More information can be found here https://www.khronos.org/opengl/wiki/Red_Green_Texture_Compression.
</constant>
<constant name="FORMAT_RGTC_RG" value="21" enum="Format">
- Texture format that uses Red Green Texture Compression, normalizing the red and green channel data using the same compression algorithm that DXT5 uses for the alpha channel.
+ Texture format that uses Red Green Texture Compression, normalizing the red and green channel data using the same compression algorithm that DXT5 uses for the alpha channel.
</constant>
<constant name="FORMAT_BPTC_RGBA" value="22" enum="Format">
- Texture format that uses BPTC compression with unsigned normalized RGBA components. More information can be found at https://www.khronos.org/opengl/wiki/BPTC_Texture_Compression.
+ Texture format that uses BPTC compression with unsigned normalized RGBA components. More information can be found at https://www.khronos.org/opengl/wiki/BPTC_Texture_Compression.
</constant>
<constant name="FORMAT_BPTC_RGBF" value="23" enum="Format">
- Texture format that uses BPTC compression with signed floating-point RGB components.
+ Texture format that uses BPTC compression with signed floating-point RGB components.
</constant>
<constant name="FORMAT_BPTC_RGBFU" value="24" enum="Format">
- Texture format that uses BPTC compression with unsigned floating-point RGB components.
+ Texture format that uses BPTC compression with unsigned floating-point RGB components.
</constant>
<constant name="FORMAT_PVRTC2" value="25" enum="Format">
- Texture format used on PowerVR-supported mobile platforms, uses 2 bit color depth with no alpha. More information on PVRTC can be found here https://en.wikipedia.org/wiki/PVRTC.
+ Texture format used on PowerVR-supported mobile platforms, uses 2 bit color depth with no alpha. More information on PVRTC can be found here https://en.wikipedia.org/wiki/PVRTC.
</constant>
<constant name="FORMAT_PVRTC2A" value="26" enum="Format">
- Same as PVRTC2, but with an alpha component.
+ Same as PVRTC2, but with an alpha component.
</constant>
<constant name="FORMAT_PVRTC4" value="27" enum="Format">
- Similar to PVRTC2, but with 4 bit color depth and no alpha.
+ Similar to PVRTC2, but with 4 bit color depth and no alpha.
</constant>
<constant name="FORMAT_PVRTC4A" value="28" enum="Format">
- Same as PVRTC4, but with an alpha component.
+ Same as PVRTC4, but with an alpha component.
</constant>
<constant name="FORMAT_ETC" value="29" enum="Format">
- Ericsson Texture Compression format, also referred to as 'ETC1', and is part of the OpenGL ES graphics standard. An overview of the format is given at https://en.wikipedia.org/wiki/Ericsson_Texture_Compression#ETC1.
+ Ericsson Texture Compression format, also referred to as 'ETC1', and is part of the OpenGL ES graphics standard. An overview of the format is given at https://en.wikipedia.org/wiki/Ericsson_Texture_Compression#ETC1.
</constant>
<constant name="FORMAT_ETC2_R11" value="30" enum="Format">
- Ericsson Texture Compression format 2 variant R11_EAC, which provides one channel of unsigned data.
+ Ericsson Texture Compression format 2 variant R11_EAC, which provides one channel of unsigned data.
</constant>
<constant name="FORMAT_ETC2_R11S" value="31" enum="Format">
- Ericsson Texture Compression format 2 variant SIGNED_R11_EAC, which provides one channel of signed data.
+ Ericsson Texture Compression format 2 variant SIGNED_R11_EAC, which provides one channel of signed data.
</constant>
<constant name="FORMAT_ETC2_RG11" value="32" enum="Format">
- Ericsson Texture Compression format 2 variant RG11_EAC, which provides two channels of unsigned data.
+ Ericsson Texture Compression format 2 variant RG11_EAC, which provides two channels of unsigned data.
</constant>
<constant name="FORMAT_ETC2_RG11S" value="33" enum="Format">
- Ericsson Texture Compression format 2 variant SIGNED_RG11_EAC, which provides two channels of signed data.
+ Ericsson Texture Compression format 2 variant SIGNED_RG11_EAC, which provides two channels of signed data.
</constant>
<constant name="FORMAT_ETC2_RGB8" value="34" enum="Format">
- Ericsson Texture Compression format 2 variant RGB8, which is a followup of ETC1 and compresses RGB888 data.
+ Ericsson Texture Compression format 2 variant RGB8, which is a followup of ETC1 and compresses RGB888 data.
</constant>
<constant name="FORMAT_ETC2_RGBA8" value="35" enum="Format">
- Ericsson Texture Compression format 2 variant RGBA8, which compresses RGBA8888 data with full alpha support.
+ Ericsson Texture Compression format 2 variant RGBA8, which compresses RGBA8888 data with full alpha support.
</constant>
<constant name="FORMAT_ETC2_RGB8A1" value="36" enum="Format">
- Ericsson Texture Compression format 2 variant RGB8_PUNCHTHROUGH_ALPHA1, which compresses RGBA data to make alpha either fully transparent or fully opaque.
+ Ericsson Texture Compression format 2 variant RGB8_PUNCHTHROUGH_ALPHA1, which compresses RGBA data to make alpha either fully transparent or fully opaque.
</constant>
<constant name="FORMAT_MAX" value="37" enum="Format">
</constant>
diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml
index f3bff5a146..f537908625 100644
--- a/doc/classes/Input.xml
+++ b/doc/classes/Input.xml
@@ -333,7 +333,7 @@
</methods>
<signals>
<signal name="joy_connection_changed">
- <argument index="0" name="index" type="int">
+ <argument index="0" name="device" type="int">
</argument>
<argument index="1" name="connected" type="bool">
</argument>
diff --git a/doc/classes/ItemList.xml b/doc/classes/ItemList.xml
index 0147887768..bd1d6be4f5 100644
--- a/doc/classes/ItemList.xml
+++ b/doc/classes/ItemList.xml
@@ -144,6 +144,13 @@
Returns the current vertical scroll bar for the List.
</description>
</method>
+ <method name="is_anything_selected">
+ <return type="bool">
+ </return>
+ <description>
+ Returns [code]true[/code] if one or more items are selected.
+ </description>
+ </method>
<method name="is_item_disabled" qualifiers="const">
<return type="bool">
</return>
@@ -180,6 +187,17 @@
Returns whether or not item at the specified index is currently selected.
</description>
</method>
+ <method name="move_item">
+ <return type="void">
+ </return>
+ <argument index="0" name="from_idx" type="int">
+ </argument>
+ <argument index="1" name="to_idx" type="int">
+ </argument>
+ <description>
+ Moves item at index [code]from_idx[/code] to [code]to_idx[/code].
+ </description>
+ </method>
<method name="remove_item">
<return type="void">
</return>
@@ -326,6 +344,13 @@
Ensure item at specified index is not selected.
</description>
</method>
+ <method name="unselect_all">
+ <return type="void">
+ </return>
+ <description>
+ Ensure there are no items selected.
+ </description>
+ </method>
</methods>
<members>
<member name="allow_reselect" type="bool" setter="set_allow_reselect" getter="get_allow_reselect">
diff --git a/doc/classes/MultiplayerAPI.xml b/doc/classes/MultiplayerAPI.xml
new file mode 100644
index 0000000000..591d715174
--- /dev/null
+++ b/doc/classes/MultiplayerAPI.xml
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="MultiplayerAPI" inherits="Reference" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ <method name="add_peer">
+ <return type="void">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="clear">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="connected_to_server">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="connection_failed">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="del_peer">
+ <return type="void">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_network_connected_peers" qualifiers="const">
+ <return type="PoolIntArray">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_network_unique_id" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_rpc_sender_id" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="has_network_peer" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="is_network_server" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="poll">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="server_disconnected">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="set_root_node">
+ <return type="void">
+ </return>
+ <argument index="0" name="node" type="Node">
+ </argument>
+ <description>
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="network_peer" type="NetworkedMultiplayerPeer" setter="set_network_peer" getter="get_network_peer">
+ </member>
+ <member name="refuse_new_network_connections" type="bool" setter="set_refuse_new_network_connections" getter="is_refusing_new_network_connections">
+ </member>
+ </members>
+ <signals>
+ <signal name="connected_to_server">
+ <description>
+ </description>
+ </signal>
+ <signal name="connection_failed">
+ <description>
+ </description>
+ </signal>
+ <signal name="network_peer_connected">
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ </description>
+ </signal>
+ <signal name="network_peer_disconnected">
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ </description>
+ </signal>
+ <signal name="server_disconnected">
+ <description>
+ </description>
+ </signal>
+ </signals>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/Navigation.xml b/doc/classes/Navigation.xml
index b3b9ada26c..08f22d49d3 100644
--- a/doc/classes/Navigation.xml
+++ b/doc/classes/Navigation.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Navigation" inherits="Spatial" category="Core" version="3.1">
<brief_description>
- A collection of [code]NavigationMesh[/code] resources and methods used for pathfinding.
+ Mesh-based navigation and pathfinding node.
</brief_description>
<description>
- The Navigation node is used for basic or advanced navigation. By default it will automatically collect all child [code]NavigationMesh[/code] resources, but they can also be added on the fly through scripting. It can be used for generating a simple path between two points or it can be used to ensure that a navigation agent is angled perfectly to the terrain it is navigating.
+ Provides navigation and pathfinding within a collection of [NavigationMesh]es. By default these will be automatically collected from child [NavigationMeshInstance] nodes, but they can also be added on the fly with [method navmesh_add]. In addition to basic pathfinding, this class also assists with aligning navigation agents with the meshes they are navigating on.
</description>
<tutorials>
</tutorials>
@@ -17,7 +17,7 @@
<argument index="0" name="to_point" type="Vector3">
</argument>
<description>
- Returns the closest navigation point to the point passed.
+ Returns the navigation point closest to the point given. Points are in local coordinate space.
</description>
</method>
<method name="get_closest_point_normal">
@@ -26,7 +26,7 @@
<argument index="0" name="to_point" type="Vector3">
</argument>
<description>
- Returns the surface normal of the navigation mesh at the point passed. For instance, if the point passed was at a 45 degree slope it would return something like (0.5,0.5,0). This is useful for rotating a navigation agent in accordance with the [code]NavigationMesh[/code].
+ Returns the surface normal at the navigation point closest to the point given. Useful for rotating a navigation agent according to the navigation mesh it moves on.
</description>
</method>
<method name="get_closest_point_owner">
@@ -35,7 +35,7 @@
<argument index="0" name="to_point" type="Vector3">
</argument>
<description>
- Returns the nearest [code]NavigationMeshInstance[/code] to the point passed.
+ Returns the owner of the [NavigationMesh] which contains the navigation point closest to the point given. This is usually a [NavigtionMeshInstance]. For meshes added via [method navmesh_add], returns the owner that was given (or [code]null[/code] if the [code]owner[/code] parameter was omitted).
</description>
</method>
<method name="get_closest_point_to_segment">
@@ -48,7 +48,7 @@
<argument index="2" name="use_collision" type="bool" default="false">
</argument>
<description>
- Returns the nearest point to the line segment passed. The third optional parameter takes collisions into account.
+ Returns the navigation point closest to the given line segment. When enabling [code]use_collision[/code], only considers intersection points between segment and navigation meshes. If multiple intersection points are found, the one closest to the segment start point is returned.
</description>
</method>
<method name="get_simple_path">
@@ -61,7 +61,7 @@
<argument index="2" name="optimize" type="bool" default="true">
</argument>
<description>
- Returns a path of points as a [code]PoolVector3Array[/code]. If [code]optimize[/code] is false the [code]NavigationMesh[/code] agent properties will be taken into account, otherwise it will return the nearest path and ignore agent radius, height, etc.
+ Returns the path between two given points. Points are in local coordinate space. If [code]optimize[/code] is [code]true[/code] (the default), the agent properties associated with each [NavigationMesh] (raidus, height, etc.) are considered in the path calculation, otherwise they are ignored.
</description>
</method>
<method name="navmesh_add">
@@ -74,7 +74,7 @@
<argument index="2" name="owner" type="Object" default="null">
</argument>
<description>
- Adds a [code]NavigationMesh[/code] to the list of NavigationMesh's in this node. Returns an id. Its position, rotation and scale are associated with the [code]Transform[/code] passed. The [code]Node[/code] (or [code]Object[/code]) that owns this node is an optional parameter.
+ Adds a [NavigationMesh]. Returns an ID for use with [method navmesh_remove] or [method navmesh_set_transform]. If given, a [Transform2D] is applied to the polygon. The optional [code]owner[/code] is used as return value for [method get_closest_point_owner].
</description>
</method>
<method name="navmesh_remove">
@@ -83,7 +83,7 @@
<argument index="0" name="id" type="int">
</argument>
<description>
- Removes a [code]NavigationMesh[/code] from the list of NavigationMesh's in this node.
+ Removes the [NavigationMesh] with the given ID.
</description>
</method>
<method name="navmesh_set_transform">
@@ -94,13 +94,13 @@
<argument index="1" name="xform" type="Transform">
</argument>
<description>
- Associates a [code]NavigationMesh[/code]'s id with a [code]Transform[/code]. Its position, rotation and scale are based on the [code]Transform[/code] passed.
+ Sets the transform applied to the [NavigationMesh] with the given ID.
</description>
</method>
</methods>
<members>
<member name="up_vector" type="Vector3" setter="set_up_vector" getter="get_up_vector">
- Defines which direction is up. The default defines 0,1,0 as up which is the world up direction. To make this a ceiling use 0,-1,0 to define down as up.
+ Defines which direction is up. By default this is [code](0, 1, 0)[/code], which is the world up direction.
</member>
</members>
<constants>
diff --git a/doc/classes/Navigation2D.xml b/doc/classes/Navigation2D.xml
index d2f4513372..364da55f99 100644
--- a/doc/classes/Navigation2D.xml
+++ b/doc/classes/Navigation2D.xml
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Navigation2D" inherits="Node2D" category="Core" version="3.1">
<brief_description>
- Class to assist with character navigation and pathfinding.
+ 2D navigation and pathfinding node.
</brief_description>
<description>
+ Navigation2D provides navigation and pathfinding within a 2D area, specified as a collection of [NavigationPolygon] resources. By default these are automatically collected from child [NavigationPolygonInstance] nodes, but they can also be added on the fly with [method navpoly_add].
</description>
<tutorials>
</tutorials>
@@ -16,6 +17,7 @@
<argument index="0" name="to_point" type="Vector2">
</argument>
<description>
+ Returns the navigation point closest to the point given. Points are in local coordinate space.
</description>
</method>
<method name="get_closest_point_owner">
@@ -24,6 +26,7 @@
<argument index="0" name="to_point" type="Vector2">
</argument>
<description>
+ Returns the owner of the [NavigationPolygon] which contains the navigation point closest to the point given. This is usually a [NavigtionPolygonInstance]. For polygons added via [method navpoly_add], returns the owner that was given (or [code]null[/code] if the [code]owner[/code] parameter was omitted).
</description>
</method>
<method name="get_simple_path">
@@ -36,6 +39,7 @@
<argument index="2" name="optimize" type="bool" default="true">
</argument>
<description>
+ Returns the path between two given points. Points are in local coordinate space. If [code]optimize[/code] is [code]true[/code] (the default), the path is smoothed by merging path segments where possible.
</description>
</method>
<method name="navpoly_add">
@@ -48,6 +52,7 @@
<argument index="2" name="owner" type="Object" default="null">
</argument>
<description>
+ Adds a [NavigationPolygon]. Returns an ID for use with [method navpoly_remove] or [method navpoly_set_transform]. If given, a [Transform2D] is applied to the polygon. The optional [code]owner[/code] is used as return value for [method get_closest_point_owner].
</description>
</method>
<method name="navpoly_remove">
@@ -56,6 +61,7 @@
<argument index="0" name="id" type="int">
</argument>
<description>
+ Removes the [NavigationPolygon] with the given ID.
</description>
</method>
<method name="navpoly_set_transform">
@@ -66,6 +72,7 @@
<argument index="1" name="xform" type="Transform2D">
</argument>
<description>
+ Sets the transform applied to the [NavigationPolygon] with the given ID.
</description>
</method>
</methods>
diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml
index 9129f64340..5d0f70bc59 100644
--- a/doc/classes/Node.xml
+++ b/doc/classes/Node.xml
@@ -453,7 +453,30 @@
<return type="void">
</return>
<description>
- Prints the scene hierarchy of this node and all it's children to stdout. Used mainly for debugging purposes.
+ Prints the tree to stdout. Used mainly for debugging purposes. This version displays the path relative to the current node, and is good for copy/pasting into the [method get_node] function. Example output:
+ [codeblock]
+ TheGame
+ TheGame/Menu
+ TheGame/Menu/Label
+ TheGame/Menu/Camera2D
+ TheGame/SplashScreen
+ TheGame/SplashScreen/Camera2D
+ [/codeblock]
+ </description>
+ </method>
+ <method name="print_tree_pretty">
+ <return type="void">
+ </return>
+ <description>
+ Similar to [method print_tree], this prints the tree to stdout. This version displays a more graphical representation similar to what is displayed in the scene inspector. It is useful for inspecting larger trees. Example output:
+ [codeblock]
+ ┖╴TheGame
+ ┠╴Menu
+ ┃ ┠╴Label
+ ┃ ┖╴Camera2D
+ ┖-SplashScreen
+ ┖╴Camera2D
+ [/codeblock]
</description>
</method>
<method name="propagate_call">
@@ -739,9 +762,13 @@
</method>
</methods>
<members>
+ <member name="custom_multiplayer_api" type="MultiplayerAPI" setter="set_custom_multiplayer_api" getter="get_custom_multiplayer_api">
+ </member>
<member name="filename" type="String" setter="set_filename" getter="get_filename">
When a scene is instanced from a file, its topmost node contains the filename from which it was loaded.
</member>
+ <member name="multiplayer_api" type="MultiplayerAPI" setter="" getter="get_multiplayer_api">
+ </member>
<member name="name" type="String" setter="set_name" getter="get_name">
The name of the node. This name is unique among the siblings (other child nodes from the same parent). When set to an existing name, the node will be automatically renamed
</member>
diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml
index a98385f1d2..d38a89874c 100644
--- a/doc/classes/OS.xml
+++ b/doc/classes/OS.xml
@@ -40,6 +40,7 @@
<return type="void">
</return>
<description>
+ Centers the window on the screen if in windowed mode.
</description>
</method>
<method name="delay_msec" qualifiers="const">
@@ -48,7 +49,7 @@
<argument index="0" name="msec" type="int">
</argument>
<description>
- Delay executing of the current thread by given milliseconds.
+ Delay execution of the current thread by given milliseconds.
</description>
</method>
<method name="delay_usec" qualifiers="const">
@@ -57,7 +58,7 @@
<argument index="0" name="usec" type="int">
</argument>
<description>
- Delay executing of the current thread by given microseconds.
+ Delay execution of the current thread by given microseconds.
</description>
</method>
<method name="dump_memory_to_file">
@@ -67,7 +68,7 @@
</argument>
<description>
Dumps the memory allocation ringlist to a file (only works in debug).
- Entry format per line: "Address - Size - Description"
+ Entry format per line: "Address - Size - Description".
</description>
</method>
<method name="dump_resources_to_file">
@@ -77,7 +78,7 @@
</argument>
<description>
Dumps all used resources to file (only works in debug).
- Entry format per line: "Resource Type : Resource Location"
+ Entry format per line: "Resource Type : Resource Location".
At the end of the file is a statistic of all used Resource Types.
</description>
</method>
@@ -116,6 +117,22 @@
Returns the scancode of the given string (e.g. "Escape")
</description>
</method>
+ <method name="get_audio_driver_count" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ Returns the total number of available audio drivers.
+ </description>
+ </method>
+ <method name="get_audio_driver_name" qualifiers="const">
+ <return type="String">
+ </return>
+ <argument index="0" name="driver" type="int">
+ </argument>
+ <description>
+ Returns the audio driver name for the given index.
+ </description>
+ </method>
<method name="get_cmdline_args">
<return type="PoolStringArray">
</return>
@@ -242,6 +259,7 @@
<return type="Vector2">
</return>
<description>
+ Returns the window size including decorations like window borders.
</description>
</method>
<method name="get_scancode_string" qualifiers="const">
@@ -366,7 +384,7 @@
<return type="int">
</return>
<description>
- Return the current unix timestamp.
+ Returns the current unix epoch timestamp.
</description>
</method>
<method name="get_unix_time_from_datetime" qualifiers="const">
@@ -391,10 +409,25 @@
If the project name is empty, [code]user://[/code] falls back to [code]res://[/code].
</description>
</method>
+ <method name="get_video_driver_count" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_video_driver_name" qualifiers="const">
+ <return type="String">
+ </return>
+ <argument index="0" name="driver" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="get_virtual_keyboard_height">
<return type="int">
</return>
<description>
+ Returns the on-screen keyboard's height in pixels. Returns 0 if there is no keyboard or it is currently hidden.
</description>
</method>
<method name="has_environment" qualifiers="const">
@@ -412,6 +445,7 @@
<argument index="0" name="tag_name" type="String">
</argument>
<description>
+ Returns [code]true[/code] if the feature for the given feature tag is supported in the currently running instance, depending on platform, build etc. Can be used to check whether you're currently running a debug build, on a certain platform or arch, etc. See feature tags documentation.
</description>
</method>
<method name="has_touchscreen_ui_hint" qualifiers="const">
@@ -478,6 +512,7 @@
<return type="bool">
</return>
<description>
+ Returns [code]true[/code] if the window should always be on top of other windows.
</description>
</method>
<method name="kill">
@@ -614,6 +649,7 @@
<argument index="0" name="enabled" type="bool">
</argument>
<description>
+ Sets whether the window should always be on top.
</description>
</method>
<method name="set_window_title">
@@ -654,27 +690,31 @@
The current screen index (starting from 0).
</member>
<member name="exit_code" type="int" setter="set_exit_code" getter="get_exit_code">
+ The exit code passed to the OS when the main loop exits.
</member>
<member name="keep_screen_on" type="bool" setter="set_keep_screen_on" getter="is_keep_screen_on">
+ If [code]true[/code] the engine tries to keep the screen on while the game is running. Useful on mobile.
</member>
<member name="low_processor_usage_mode" type="bool" setter="set_low_processor_usage_mode" getter="is_in_low_processor_usage_mode">
+ If [code]true[/code] the engine optimizes for low processor usage by only refreshing the screen if needed. Can improve battery consumption on mobile.
</member>
- <member name="screen_orientation" type="int" setter="set_screen_orientation" getter="get_screen_orientation" enum="_OS.ScreenOrientation">
+ <member name="screen_orientation" type="int" setter="set_screen_orientation" getter="get_screen_orientation" enum="OS.ScreenOrientation">
The current screen orientation.
</member>
<member name="vsync_enabled" type="bool" setter="set_use_vsync" getter="is_vsync_enabled">
+ If [code]true[/code] vertical synchronization (Vsync) is enabled.
</member>
<member name="window_borderless" type="bool" setter="set_borderless_window" getter="get_borderless_window">
- If [code]true[/code], removes the window frame.
+ If [code]true[/code] removes the window frame.
</member>
<member name="window_fullscreen" type="bool" setter="set_window_fullscreen" getter="is_window_fullscreen">
- If [code]true[/code], the window is fullscreen.
+ If [code]true[/code] the window is fullscreen.
</member>
<member name="window_maximized" type="bool" setter="set_window_maximized" getter="is_window_maximized">
- If [code]true[/code], the window is maximized.
+ If [code]true[/code] the window is maximized.
</member>
<member name="window_minimized" type="bool" setter="set_window_minimized" getter="is_window_minimized">
- If [code]true[/code], the window is minimized.
+ If [code]true[/code] the window is minimized.
</member>
<member name="window_position" type="Vector2" setter="set_window_position" getter="get_window_position">
The window position relative to the screen, the origin is the top left corner, +Y axis goes to the bottom and +X axis goes to the right.
@@ -688,82 +728,121 @@
</members>
<constants>
<constant name="DAY_SUNDAY" value="0" enum="Weekday">
+ Sunday.
</constant>
<constant name="DAY_MONDAY" value="1" enum="Weekday">
+ Monday.
</constant>
<constant name="DAY_TUESDAY" value="2" enum="Weekday">
+ Tuesday.
</constant>
<constant name="DAY_WEDNESDAY" value="3" enum="Weekday">
+ Wednesday.
</constant>
<constant name="DAY_THURSDAY" value="4" enum="Weekday">
+ Thursday.
</constant>
<constant name="DAY_FRIDAY" value="5" enum="Weekday">
+ Friday.
</constant>
<constant name="DAY_SATURDAY" value="6" enum="Weekday">
+ Saturday.
</constant>
<constant name="MONTH_JANUARY" value="1" enum="Month">
+ January.
</constant>
<constant name="MONTH_FEBRUARY" value="2" enum="Month">
+ February.
</constant>
<constant name="MONTH_MARCH" value="3" enum="Month">
+ March.
</constant>
<constant name="MONTH_APRIL" value="4" enum="Month">
+ April.
</constant>
<constant name="MONTH_MAY" value="5" enum="Month">
+ May.
</constant>
<constant name="MONTH_JUNE" value="6" enum="Month">
+ June.
</constant>
<constant name="MONTH_JULY" value="7" enum="Month">
+ July.
</constant>
<constant name="MONTH_AUGUST" value="8" enum="Month">
+ August.
</constant>
<constant name="MONTH_SEPTEMBER" value="9" enum="Month">
+ September.
</constant>
<constant name="MONTH_OCTOBER" value="10" enum="Month">
+ October.
</constant>
<constant name="MONTH_NOVEMBER" value="11" enum="Month">
+ November.
</constant>
<constant name="MONTH_DECEMBER" value="12" enum="Month">
+ December.
</constant>
<constant name="SCREEN_ORIENTATION_LANDSCAPE" value="0" enum="ScreenOrientation">
+ Landscape screen orientation.
</constant>
<constant name="SCREEN_ORIENTATION_PORTRAIT" value="1" enum="ScreenOrientation">
+ Portrait screen orientation.
</constant>
<constant name="SCREEN_ORIENTATION_REVERSE_LANDSCAPE" value="2" enum="ScreenOrientation">
+ Reverse landscape screen orientation.
</constant>
<constant name="SCREEN_ORIENTATION_REVERSE_PORTRAIT" value="3" enum="ScreenOrientation">
+ Reverse portrait screen orientation.
</constant>
<constant name="SCREEN_ORIENTATION_SENSOR_LANDSCAPE" value="4" enum="ScreenOrientation">
+ Uses landscape or reverse landscape based on the hardware sensor.
</constant>
<constant name="SCREEN_ORIENTATION_SENSOR_PORTRAIT" value="5" enum="ScreenOrientation">
+ Uses portrait or reverse portrait based on the hardware sensor.
</constant>
<constant name="SCREEN_ORIENTATION_SENSOR" value="6" enum="ScreenOrientation">
+ Uses most suitable orientation based on the hardware sensor.
</constant>
<constant name="SYSTEM_DIR_DESKTOP" value="0" enum="SystemDir">
+ Desktop directory path.
</constant>
<constant name="SYSTEM_DIR_DCIM" value="1" enum="SystemDir">
+ DCIM (Digital Camera Images) directory path.
</constant>
<constant name="SYSTEM_DIR_DOCUMENTS" value="2" enum="SystemDir">
+ Documents directory path.
</constant>
<constant name="SYSTEM_DIR_DOWNLOADS" value="3" enum="SystemDir">
+ Downloads directory path.
</constant>
<constant name="SYSTEM_DIR_MOVIES" value="4" enum="SystemDir">
+ Movies directory path.
</constant>
<constant name="SYSTEM_DIR_MUSIC" value="5" enum="SystemDir">
+ Music directory path.
</constant>
<constant name="SYSTEM_DIR_PICTURES" value="6" enum="SystemDir">
+ Pictures directory path.
</constant>
<constant name="SYSTEM_DIR_RINGTONES" value="7" enum="SystemDir">
+ Ringtones directory path.
</constant>
<constant name="POWERSTATE_UNKNOWN" value="0" enum="PowerState">
+ Unknown powerstate.
</constant>
<constant name="POWERSTATE_ON_BATTERY" value="1" enum="PowerState">
+ Unplugged, running on battery.
</constant>
<constant name="POWERSTATE_NO_BATTERY" value="2" enum="PowerState">
+ Plugged in, no battery available.
</constant>
<constant name="POWERSTATE_CHARGING" value="3" enum="PowerState">
+ Plugged in, battery charging.
</constant>
<constant name="POWERSTATE_CHARGED" value="4" enum="PowerState">
+ Plugged in, battery fully charged.
</constant>
</constants>
</class>
diff --git a/doc/classes/OptionButton.xml b/doc/classes/OptionButton.xml
index de6f60c384..8cb53dd98e 100644
--- a/doc/classes/OptionButton.xml
+++ b/doc/classes/OptionButton.xml
@@ -194,6 +194,13 @@
</member>
</members>
<signals>
+ <signal name="item_focused">
+ <argument index="0" name="ID" type="int">
+ </argument>
+ <description>
+ This signal is emitted when user navigated to an item using [code]ui_up[/code] or [code]ui_down[/code] action. ID of the item selected is passed as argument (if no IDs were added, ID will be just the item index).
+ </description>
+ </signal>
<signal name="item_selected">
<argument index="0" name="ID" type="int">
</argument>
diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml
index 6ead220236..166a4be2b0 100644
--- a/doc/classes/PopupMenu.xml
+++ b/doc/classes/PopupMenu.xml
@@ -24,19 +24,6 @@
Add a new checkable item with text "label". An id can optionally be provided, as well as an accelerator. If no id is provided, one will be created from the index. Note that checkable items just display a checkmark, but don't have any built-in checking behavior and must be checked/unchecked manually.
</description>
</method>
- <method name="add_radio_check_item">
- <return type="void">
- </return>
- <argument index="0" name="label" type="String">
- </argument>
- <argument index="1" name="id" type="int" default="-1">
- </argument>
- <argument index="2" name="accel" type="int" default="0">
- </argument>
- <description>
- The same as [method add_check_item] but the inserted item will look as a radio button. Remember this is just cosmetic and you have to add the logic for checking/unchecking items in radio groups.
- </description>
- </method>
<method name="add_check_shortcut">
<return type="void">
</return>
@@ -49,18 +36,6 @@
<description>
</description>
</method>
- <method name="add_radio_check_shortcut">
- <return type="void">
- </return>
- <argument index="0" name="shortcut" type="ShortCut">
- </argument>
- <argument index="1" name="id" type="int" default="-1">
- </argument>
- <argument index="2" name="global" type="bool" default="false">
- </argument>
- <description>
- </description>
- </method>
<method name="add_icon_check_item">
<return type="void">
</return>
@@ -133,6 +108,31 @@
Add a new item with text "label". An id can optionally be provided, as well as an accelerator keybinding. If no id is provided, one will be created from the index.
</description>
</method>
+ <method name="add_radio_check_item">
+ <return type="void">
+ </return>
+ <argument index="0" name="label" type="String">
+ </argument>
+ <argument index="1" name="id" type="int" default="-1">
+ </argument>
+ <argument index="2" name="accel" type="int" default="0">
+ </argument>
+ <description>
+ The same as [method add_check_item] but the inserted item will look as a radio button. Remember this is just cosmetic and you have to add the logic for checking/unchecking items in radio groups.
+ </description>
+ </method>
+ <method name="add_radio_check_shortcut">
+ <return type="void">
+ </return>
+ <argument index="0" name="shortcut" type="ShortCut">
+ </argument>
+ <argument index="1" name="id" type="int" default="-1">
+ </argument>
+ <argument index="2" name="global" type="bool" default="false">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="add_separator">
<return type="void">
</return>
@@ -267,31 +267,31 @@
Return whether the item at index "idx" is checkable in some way, i.e., whether has a checkbox or radio button. Note that checkable items just display a checkmark or radio button, but don't have any built-in checking behavior and must be checked/unchecked manually.
</description>
</method>
- <method name="is_item_radio_checkable" qualifiers="const">
+ <method name="is_item_checked" qualifiers="const">
<return type="bool">
</return>
<argument index="0" name="idx" type="int">
</argument>
<description>
- Return whether the item at index "idx" has radio-button-style checkability. Remember this is just cosmetic and you have to add the logic for checking/unchecking items in radio groups.
+ Return whether the item at index "idx" is checked.
</description>
</method>
- <method name="is_item_checked" qualifiers="const">
+ <method name="is_item_disabled" qualifiers="const">
<return type="bool">
</return>
<argument index="0" name="idx" type="int">
</argument>
<description>
- Return whether the item at index "idx" is checked.
+ Return whether the item at index "idx" is disabled. When it is disabled it can't be selected, or its action invoked.
</description>
</method>
- <method name="is_item_disabled" qualifiers="const">
+ <method name="is_item_radio_checkable" qualifiers="const">
<return type="bool">
</return>
<argument index="0" name="idx" type="int">
</argument>
<description>
- Return whether the item at index "idx" is disabled. When it is disabled it can't be selected, or its action invoked.
+ Return whether the item at index "idx" has radio-button-style checkability. Remember this is just cosmetic and you have to add the logic for checking/unchecking items in radio groups.
</description>
</method>
<method name="is_item_separator" qualifiers="const">
@@ -491,6 +491,13 @@
</member>
</members>
<signals>
+ <signal name="id_focused">
+ <argument index="0" name="ID" type="int">
+ </argument>
+ <description>
+ This event is emitted when user navigated to an item of some id using [code]ui_up[/code] or [code]ui_down[/code] action.
+ </description>
+ </signal>
<signal name="id_pressed">
<argument index="0" name="ID" type="int">
</argument>
@@ -529,6 +536,10 @@
</theme_item>
<theme_item name="panel_disabled" type="StyleBox">
</theme_item>
+ <theme_item name="radio_checked" type="Texture">
+ </theme_item>
+ <theme_item name="radio_unchecked" type="Texture">
+ </theme_item>
<theme_item name="separator" type="StyleBox">
</theme_item>
<theme_item name="submenu" type="Texture">
diff --git a/doc/classes/Quat.xml b/doc/classes/Quat.xml
index 3a258011b2..33f2b9758b 100644
--- a/doc/classes/Quat.xml
+++ b/doc/classes/Quat.xml
@@ -10,6 +10,7 @@
It can be used to perform SLERP (spherical-linear interpolation) between two rotations.
</description>
<tutorials>
+ http://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html#interpolating-with-quaternions
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/SceneTree.xml b/doc/classes/SceneTree.xml
index 18d868cff8..8846616851 100644
--- a/doc/classes/SceneTree.xml
+++ b/doc/classes/SceneTree.xml
@@ -266,6 +266,8 @@
<member name="edited_scene_root" type="Node" setter="set_edited_scene_root" getter="get_edited_scene_root">
The root of the edited scene.
</member>
+ <member name="multiplayer_api" type="MultiplayerAPI" setter="set_multiplayer_api" getter="get_multiplayer_api">
+ </member>
<member name="network_peer" type="NetworkedMultiplayerPeer" setter="set_network_peer" getter="get_network_peer">
The peer object to handle the RPC system (effectively enabling networking when set). Depending on the peer itself, the SceneTree will become a network server (check with [method is_network_server()]) and will set root node's network mode to master (see NETWORK_MODE_* constants in [Node]), or it will become a regular peer with root node set to slave. All child nodes are set to inherit the network mode by default. Handling of networking-related events (connection, disconnection, new clients) is done by connecting to SceneTree's signals.
</member>
@@ -359,7 +361,7 @@
</signal>
<signal name="tree_changed">
<description>
- Emitted whenever the SceneTree hierarchy changed (children being moved or renamed, etc.).
+ Emitted whenever the SceneTree hierarchy changed (children being moved or renamed, etc.).
</description>
</signal>
</signals>
diff --git a/doc/classes/ScrollContainer.xml b/doc/classes/ScrollContainer.xml
index 79b70fec99..28fbdea58d 100644
--- a/doc/classes/ScrollContainer.xml
+++ b/doc/classes/ScrollContainer.xml
@@ -13,6 +13,8 @@
<methods>
</methods>
<members>
+ <member name="scroll_deadzone" type="int" setter="set_deadzone" getter="get_deadzone">
+ </member>
<member name="scroll_horizontal" type="int" setter="set_h_scroll" getter="get_h_scroll">
The current horizontal scroll value.
</member>
@@ -26,6 +28,16 @@
If [code]true[/code], enables vertical scrolling.
</member>
</members>
+ <signals>
+ <signal name="scroll_ended">
+ <description>
+ </description>
+ </signal>
+ <signal name="scroll_started">
+ <description>
+ </description>
+ </signal>
+ </signals>
<constants>
</constants>
</class>
diff --git a/doc/classes/TabContainer.xml b/doc/classes/TabContainer.xml
index 330487adf7..11e94abc03 100644
--- a/doc/classes/TabContainer.xml
+++ b/doc/classes/TabContainer.xml
@@ -78,6 +78,12 @@
Returns the title of the tab at index [code]tab_idx[/code]. Tab titles default to the name of the indexed child node, but this can be overridden with [method set_tab_title].
</description>
</method>
+ <method name="get_tabs_rearrange_group" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="set_popup">
<return type="void">
</return>
@@ -120,11 +126,21 @@
Sets a title for the tab at index [code]tab_idx[/code]. Tab titles default to the name of the indexed child node, but this can be overridden with [method set_tab_title].
</description>
</method>
+ <method name="set_tabs_rearrange_group">
+ <return type="void">
+ </return>
+ <argument index="0" name="group_id" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
</methods>
<members>
<member name="current_tab" type="int" setter="set_current_tab" getter="get_current_tab">
The current tab index. When set, this index's [Control] node's [code]visible[/code] property is set to [code]true[/code] and all others are set to [code]false[/code].
</member>
+ <member name="drag_to_rearrange_enabled" type="bool" setter="set_drag_to_rearrange_enabled" getter="get_drag_to_rearrange_enabled">
+ </member>
<member name="tab_align" type="int" setter="set_tab_align" getter="get_tab_align" enum="TabContainer.TabAlign">
The alignment of all tabs in the tab container. See the [code]ALIGN_*[/code] constants for details.
</member>
diff --git a/doc/classes/Tabs.xml b/doc/classes/Tabs.xml
index 615bc83c2d..0d5f1f0ba9 100644
--- a/doc/classes/Tabs.xml
+++ b/doc/classes/Tabs.xml
@@ -80,6 +80,12 @@
<description>
</description>
</method>
+ <method name="get_tabs_rearrange_group" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="move_tab">
<return type="void">
</return>
@@ -129,10 +135,20 @@
<description>
</description>
</method>
+ <method name="set_tabs_rearrange_group">
+ <return type="void">
+ </return>
+ <argument index="0" name="group_id" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
</methods>
<members>
<member name="current_tab" type="int" setter="set_current_tab" getter="get_current_tab">
</member>
+ <member name="drag_to_rearrange_enabled" type="bool" setter="set_drag_to_rearrange_enabled" getter="get_drag_to_rearrange_enabled">
+ </member>
<member name="scrolling_enabled" type="bool" setter="set_scrolling_enabled" getter="get_scrolling_enabled">
</member>
<member name="tab_align" type="int" setter="set_tab_align" getter="get_tab_align" enum="Tabs.TabAlign">
diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml
index 8a71cb2059..76d94fbfbc 100644
--- a/doc/classes/TextEdit.xml
+++ b/doc/classes/TextEdit.xml
@@ -130,6 +130,14 @@
<description>
</description>
</method>
+ <method name="get_keyword_color" qualifiers="const">
+ <return type="Color">
+ </return>
+ <argument index="0" name="keyword" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="get_line" qualifiers="const">
<return type="String">
</return>
@@ -193,6 +201,14 @@
<description>
</description>
</method>
+ <method name="has_keyword_color" qualifiers="const">
+ <return type="bool">
+ </return>
+ <argument index="0" name="keyword" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="insert_text_at_cursor">
<return type="void">
</return>
diff --git a/doc/classes/TextureProgress.xml b/doc/classes/TextureProgress.xml
index 18bd886b0b..2ae4ef2cf8 100644
--- a/doc/classes/TextureProgress.xml
+++ b/doc/classes/TextureProgress.xml
@@ -51,6 +51,12 @@
<member name="texture_under" type="Texture" setter="set_under_texture" getter="get_under_texture">
[Texture] that draws under the progress bar. The bar's background.
</member>
+ <member name="tint_over" type="Color" setter="set_tint_over" getter="get_tint_over">
+ </member>
+ <member name="tint_progress" type="Color" setter="set_tint_progress" getter="get_tint_progress">
+ </member>
+ <member name="tint_under" type="Color" setter="set_tint_under" getter="get_tint_under">
+ </member>
</members>
<constants>
<constant name="FILL_LEFT_TO_RIGHT" value="0" enum="FillMode">
diff --git a/doc/classes/TileSet.xml b/doc/classes/TileSet.xml
index 10978970e9..4e218f5595 100644
--- a/doc/classes/TileSet.xml
+++ b/doc/classes/TileSet.xml
@@ -254,6 +254,14 @@
Return the texture offset of the tile.
</description>
</method>
+ <method name="tile_get_tile_mode" qualifiers="const">
+ <return type="int" enum="TileSet.TileMode">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="tile_set_light_occluder">
<return type="void">
</return>
@@ -410,6 +418,16 @@
Set the texture offset of the tile.
</description>
</method>
+ <method name="tile_set_tile_mode">
+ <return type="void">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <argument index="1" name="tilemode" type="int" enum="TileSet.TileMode">
+ </argument>
+ <description>
+ </description>
+ </method>
</methods>
<constants>
<constant name="BITMASK_2X2" value="0" enum="BitmaskMode">
@@ -432,5 +450,11 @@
</constant>
<constant name="BIND_BOTTOMRIGHT" value="256" enum="AutotileBindings">
</constant>
+ <constant name="SINGLE_TILE" value="0" enum="TileMode">
+ </constant>
+ <constant name="AUTO_TILE" value="1" enum="TileMode">
+ </constant>
+ <constant name="ANIMATED_TILE" value="2" enum="TileMode">
+ </constant>
</constants>
</class>
diff --git a/doc/classes/Transform.xml b/doc/classes/Transform.xml
index df5c2ab3ae..d9f9d8cc73 100644
--- a/doc/classes/Transform.xml
+++ b/doc/classes/Transform.xml
@@ -8,6 +8,7 @@
</description>
<tutorials>
http://docs.godotengine.org/en/3.0/tutorials/math/index.html
+ http://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/Vector2.xml b/doc/classes/Vector2.xml
index b293ec3368..5ffe807606 100644
--- a/doc/classes/Vector2.xml
+++ b/doc/classes/Vector2.xml
@@ -84,10 +84,10 @@
<method name="cross">
<return type="float">
</return>
- <argument index="0" name="b" type="Vector2">
+ <argument index="0" name="with" type="Vector2">
</argument>
<description>
- Returns the 2-dimensional analog of the cross product with [code]b[/code].
+ Returns the 2-dimensional analog of the cross product with the given Vector2.
</description>
</method>
<method name="cubic_interpolate">
diff --git a/doc/classes/VideoPlayer.xml b/doc/classes/VideoPlayer.xml
index 48d09ccd95..d2639590a1 100644
--- a/doc/classes/VideoPlayer.xml
+++ b/doc/classes/VideoPlayer.xml
@@ -72,6 +72,12 @@
<member name="volume_db" type="float" setter="set_volume_db" getter="get_volume_db">
</member>
</members>
+ <signals>
+ <signal name="finished">
+ <description>
+ </description>
+ </signal>
+ </signals>
<constants>
</constants>
</class>
diff --git a/doc/classes/WebSocketClient.xml b/doc/classes/WebSocketClient.xml
index 79313c90c7..496baa5787 100644
--- a/doc/classes/WebSocketClient.xml
+++ b/doc/classes/WebSocketClient.xml
@@ -28,6 +28,10 @@
</description>
</method>
</methods>
+ <members>
+ <member name="verify_ssl" type="bool" setter="set_verify_ssl_enabled" getter="is_verify_ssl_enabled">
+ </member>
+ </members>
<signals>
<signal name="connection_closed">
<description>
diff --git a/drivers/coreaudio/audio_driver_coreaudio.h b/drivers/coreaudio/audio_driver_coreaudio.h
index 1ef474ac19..9891920263 100644
--- a/drivers/coreaudio/audio_driver_coreaudio.h
+++ b/drivers/coreaudio/audio_driver_coreaudio.h
@@ -56,9 +56,11 @@ class AudioDriverCoreAudio : public AudioDriver {
Vector<int32_t> samples_in;
+#ifdef OSX_ENABLED
static OSStatus output_device_address_cb(AudioObjectID inObjectID,
UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses,
void *inClientData);
+#endif
static OSStatus output_callback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
index 5dddb1a900..733c7cc80c 100644
--- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp
+++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
@@ -60,7 +60,7 @@ void AudioDriverPulseAudio::pa_sink_info_cb(pa_context *c, const pa_sink_info *l
return;
}
- ad->pa_channels = l->channel_map.channels;
+ ad->pa_map = l->channel_map;
ad->pa_status++;
}
@@ -73,7 +73,7 @@ void AudioDriverPulseAudio::pa_server_info_cb(pa_context *c, const pa_server_inf
void AudioDriverPulseAudio::detect_channels() {
- pa_channels = 2;
+ pa_channel_map_init_stereo(&pa_map);
if (device_name == "Default") {
// Get the default output device name
@@ -129,56 +129,64 @@ Error AudioDriverPulseAudio::init_device() {
}
// Detect the amount of channels PulseAudio is using
- // Note: If using an even amount of channels (2, 4, etc) channels and pa_channels will be equal,
- // if not then pa_channels will have the real amount of channels PulseAudio is using and channels
- // will have the amount of channels Godot is using (in this case it's pa_channels + 1)
+ // Note: If using an even amount of channels (2, 4, etc) channels and pa_map.channels will be equal,
+ // if not then pa_map.channels will have the real amount of channels PulseAudio is using and channels
+ // will have the amount of channels Godot is using (in this case it's pa_map.channels + 1)
detect_channels();
- switch (pa_channels) {
+ switch (pa_map.channels) {
case 1: // Mono
case 3: // Surround 2.1
case 5: // Surround 5.0
case 7: // Surround 7.0
- channels = pa_channels + 1;
+ channels = pa_map.channels + 1;
break;
case 2: // Stereo
case 4: // Surround 4.0
case 6: // Surround 5.1
case 8: // Surround 7.1
- channels = pa_channels;
+ channels = pa_map.channels;
break;
default:
- ERR_PRINTS("PulseAudio: Unsupported number of channels: " + itos(pa_channels));
- ERR_FAIL_V(ERR_CANT_OPEN);
+ WARN_PRINTS("PulseAudio: Unsupported number of channels: " + itos(pa_map.channels));
+ pa_channel_map_init_stereo(&pa_map);
+ channels = 2;
break;
}
- pa_sample_spec spec;
- spec.format = PA_SAMPLE_S16LE;
- spec.channels = pa_channels;
- spec.rate = mix_rate;
-
int latency = GLOBAL_DEF("audio/output_latency", DEFAULT_OUTPUT_LATENCY);
buffer_frames = closest_power_of_2(latency * mix_rate / 1000);
- pa_buffer_size = buffer_frames * pa_channels;
+ pa_buffer_size = buffer_frames * pa_map.channels;
if (OS::get_singleton()->is_stdout_verbose()) {
- print_line("PulseAudio: detected " + itos(pa_channels) + " channels");
+ print_line("PulseAudio: detected " + itos(pa_map.channels) + " channels");
print_line("PulseAudio: audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms");
}
+ pa_sample_spec spec;
+ spec.format = PA_SAMPLE_S16LE;
+ spec.channels = pa_map.channels;
+ spec.rate = mix_rate;
+
+ pa_str = pa_stream_new(pa_ctx, "Sound", &spec, &pa_map);
+ if (pa_str == NULL) {
+ ERR_PRINTS("PulseAudio: pa_stream_new error: " + String(pa_strerror(pa_context_errno(pa_ctx))));
+ ERR_FAIL_V(ERR_CANT_OPEN);
+ }
+
pa_buffer_attr attr;
// set to appropriate buffer length (in bytes) from global settings
- attr.tlength = pa_buffer_size * sizeof(int16_t);
+ // Note: PulseAudio defaults to 4 fragments, which means that the actual
+ // latency is tlength / fragments. It seems that the PulseAudio has no way
+ // to get the fragments number so we're hardcoding this to the default of 4
+ const int fragments = 4;
+ attr.tlength = pa_buffer_size * sizeof(int16_t) * fragments;
// set them to be automatically chosen
attr.prebuf = (uint32_t)-1;
attr.maxlength = (uint32_t)-1;
attr.minreq = (uint32_t)-1;
- pa_str = pa_stream_new(pa_ctx, "Sound", &spec, NULL);
- ERR_FAIL_COND_V(pa_ctx == NULL, ERR_CANT_OPEN);
-
const char *dev = device_name == "Default" ? NULL : device_name.utf8().get_data();
pa_stream_flags flags = pa_stream_flags(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE);
int error_code = pa_stream_connect_playback(pa_str, dev, &attr, flags, NULL, NULL);
@@ -293,7 +301,7 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
ad->unlock();
- if (ad->channels == ad->pa_channels) {
+ if (ad->channels == ad->pa_map.channels) {
for (unsigned int i = 0; i < ad->pa_buffer_size; i++) {
ad->samples_out[i] = ad->samples_in[i] >> 16;
}
@@ -303,7 +311,7 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
unsigned int out_idx = 0;
for (unsigned int i = 0; i < ad->buffer_frames; i++) {
- for (unsigned int j = 0; j < ad->pa_channels - 1; j++) {
+ for (unsigned int j = 0; j < ad->pa_map.channels - 1; j++) {
ad->samples_out[out_idx++] = ad->samples_in[in_idx++] >> 16;
}
uint32_t l = ad->samples_in[in_idx++];
@@ -512,7 +520,6 @@ AudioDriverPulseAudio::AudioDriverPulseAudio() {
buffer_frames = 0;
pa_buffer_size = 0;
channels = 0;
- pa_channels = 0;
pa_ready = 0;
pa_status = 0;
diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.h b/drivers/pulseaudio/audio_driver_pulseaudio.h
index 934031e2a5..b471f5f9d5 100644
--- a/drivers/pulseaudio/audio_driver_pulseaudio.h
+++ b/drivers/pulseaudio/audio_driver_pulseaudio.h
@@ -47,6 +47,7 @@ class AudioDriverPulseAudio : public AudioDriver {
pa_mainloop *pa_ml;
pa_context *pa_ctx;
pa_stream *pa_str;
+ pa_channel_map pa_map;
String device_name;
String new_device;
@@ -59,7 +60,6 @@ class AudioDriverPulseAudio : public AudioDriver {
unsigned int buffer_frames;
unsigned int pa_buffer_size;
int channels;
- int pa_channels;
int pa_ready;
int pa_status;
Array pa_devices;
diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp
index 966b69a67e..e1680601ad 100644
--- a/drivers/wasapi/audio_driver_wasapi.cpp
+++ b/drivers/wasapi/audio_driver_wasapi.cpp
@@ -37,6 +37,17 @@
#include <functiondiscoverykeys.h>
+#ifndef PKEY_Device_FriendlyName
+
+#undef DEFINE_PROPERTYKEY
+/* clang-format off */
+#define DEFINE_PROPERTYKEY(id, a, b, c, d, e, f, g, h, i, j, k, l) \
+ const PROPERTYKEY id = { { a, b, c, { d, e, f, g, h, i, j, k, } }, l };
+/* clang-format on */
+
+DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14);
+#endif
+
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
const IID IID_IAudioClient = __uuidof(IAudioClient);
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 141769b16a..c31bec9a1b 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -3892,6 +3892,53 @@ void EditorNode::_update_dock_slots_visibility() {
}
}
+void EditorNode::_dock_tab_changed(int p_tab) {
+
+ // update visibility but dont set current tab
+ VSplitContainer *splits[DOCK_SLOT_MAX / 2] = {
+ left_l_vsplit,
+ left_r_vsplit,
+ right_l_vsplit,
+ right_r_vsplit,
+ };
+
+ if (!docks_visible) {
+
+ for (int i = 0; i < DOCK_SLOT_MAX; i++) {
+ dock_slot[i]->hide();
+ }
+
+ for (int i = 0; i < DOCK_SLOT_MAX / 2; i++) {
+ splits[i]->hide();
+ }
+
+ right_hsplit->hide();
+ bottom_panel->hide();
+ } else {
+ for (int i = 0; i < DOCK_SLOT_MAX; i++) {
+
+ if (dock_slot[i]->get_tab_count())
+ dock_slot[i]->show();
+ else
+ dock_slot[i]->hide();
+ }
+
+ for (int i = 0; i < DOCK_SLOT_MAX / 2; i++) {
+ bool in_use = dock_slot[i * 2 + 0]->get_tab_count() || dock_slot[i * 2 + 1]->get_tab_count();
+ if (in_use)
+ splits[i]->show();
+ else
+ splits[i]->hide();
+ }
+ bottom_panel->show();
+
+ if (right_l_vsplit->is_visible() || right_r_vsplit->is_visible())
+ right_hsplit->show();
+ else
+ right_hsplit->hide();
+ }
+}
+
void EditorNode::_load_docks_from_config(Ref<ConfigFile> p_layout, const String &p_section) {
for (int i = 0; i < DOCK_SLOT_MAX; i++) {
@@ -4762,6 +4809,7 @@ void EditorNode::_bind_methods() {
ClassDB::bind_method("_dock_popup_exit", &EditorNode::_dock_popup_exit);
ClassDB::bind_method("_dock_move_left", &EditorNode::_dock_move_left);
ClassDB::bind_method("_dock_move_right", &EditorNode::_dock_move_right);
+ ClassDB::bind_method("_dock_tab_changed", &EditorNode::_dock_tab_changed);
ClassDB::bind_method("_layout_menu_option", &EditorNode::_layout_menu_option);
@@ -5121,6 +5169,9 @@ EditorNode::EditorNode() {
dock_slot[i]->set_popup(dock_select_popup);
dock_slot[i]->connect("pre_popup_pressed", this, "_dock_pre_popup", varray(i));
dock_slot[i]->set_tab_align(TabContainer::ALIGN_LEFT);
+ dock_slot[i]->set_drag_to_rearrange_enabled(true);
+ dock_slot[i]->set_tabs_rearrange_group(1);
+ dock_slot[i]->connect("tab_changed", this, "_dock_tab_changed");
}
dock_drag_timer = memnew(Timer);
@@ -5158,6 +5209,7 @@ EditorNode::EditorNode() {
scene_tabs->set_tab_align(Tabs::ALIGN_LEFT);
scene_tabs->set_tab_close_display_policy((bool(EDITOR_DEF("interface/scene_tabs/always_show_close_button", false)) ? Tabs::CLOSE_BUTTON_SHOW_ALWAYS : Tabs::CLOSE_BUTTON_SHOW_ACTIVE_ONLY));
scene_tabs->set_min_width(int(EDITOR_DEF("interface/scene_tabs/minimum_width", 50)) * EDSCALE);
+ scene_tabs->set_drag_to_rearrange_enabled(true);
scene_tabs->connect("tab_changed", this, "_scene_tab_changed");
scene_tabs->connect("right_button_pressed", this, "_scene_tab_script_edited");
scene_tabs->connect("tab_close", this, "_scene_tab_closed");
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 90bebffca6..f774fa0a2e 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -569,6 +569,7 @@ private:
void _save_docks_to_config(Ref<ConfigFile> p_layout, const String &p_section);
void _load_docks_from_config(Ref<ConfigFile> p_layout, const String &p_section);
void _update_dock_slots_visibility();
+ void _dock_tab_changed(int p_tab);
bool restoring_scenes;
void _save_open_scenes_to_config(Ref<ConfigFile> p_layout, const String &p_section);
diff --git a/editor/icons/icon_editor_position.svg b/editor/icons/icon_editor_position.svg
new file mode 100644
index 0000000000..7cbce07fab
--- /dev/null
+++ b/editor/icons/icon_editor_position.svg
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg919"
+ sodipodi:docname="icon_editor_position.svg"
+ inkscape:version="0.92.2 5c3e80d, 2017-08-06">
+ <metadata
+ id="metadata925">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs923" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="2550"
+ inkscape:window-height="1414"
+ id="namedview921"
+ showgrid="false"
+ inkscape:zoom="64"
+ inkscape:cx="13.492036"
+ inkscape:cy="5.8518769"
+ inkscape:window-x="1370"
+ inkscape:window-y="20"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg919"
+ inkscape:snap-page="true" />
+ <g
+ id="g6479">
+ <path
+ id="path913"
+ d="M 6 0 L 6 4.4199219 A 4.2661548 4.0576186 0 0 0 4.2910156 6 L 0 6 L 0 10 L 4.2949219 10 A 4.2661548 4.0576186 0 0 0 6 11.582031 L 6 16 L 10 16 L 10 11.580078 A 4.2661548 4.0576186 0 0 0 11.708984 10 L 16 10 L 16 6 L 11.705078 6 A 4.2661548 4.0576186 0 0 0 10 4.4179688 L 10 0 L 6 0 z "
+ style="fill:#ffffff;fill-opacity:0.70588237" />
+ <path
+ id="path915"
+ d="M 7 1 L 7 4.0605469 A 4.2661548 4.0576186 0 0 1 8 3.9414062 A 4.2661548 4.0576186 0 0 1 9 4.0605469 L 9 1 L 7 1 z M 1 7 L 1 9 L 3.8691406 9 A 4.2661548 4.0576186 0 0 1 3.734375 8 A 4.2661548 4.0576186 0 0 1 3.8710938 7 L 1 7 z M 12.130859 7 A 4.2661548 4.0576186 0 0 1 12.265625 8 A 4.2661548 4.0576186 0 0 1 12.128906 9 L 15 9 L 15 7 L 12.130859 7 z M 7 11.939453 L 7 15 L 9 15 L 9 11.939453 A 4.2661548 4.0576186 0 0 1 8 12.058594 A 4.2661548 4.0576186 0 0 1 7 11.939453 z "
+ style="fill:#ff8484;stroke:none;fill-opacity:1" />
+ <circle
+ id="circle1517"
+ r="2.9201488"
+ cy="8"
+ cx="8"
+ style="fill:#ff8484;fill-opacity:1;stroke-width:0.97338283" />
+ </g>
+</svg>
diff --git a/editor/icons/icon_editor_position_previous.svg b/editor/icons/icon_editor_position_previous.svg
new file mode 100644
index 0000000000..d9eb7d7ce2
--- /dev/null
+++ b/editor/icons/icon_editor_position_previous.svg
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg919"
+ sodipodi:docname="icon_editor_position_previous.svg"
+ inkscape:version="0.92.2 5c3e80d, 2017-08-06">
+ <metadata
+ id="metadata925">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs923" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1356"
+ inkscape:window-height="742"
+ id="namedview921"
+ showgrid="false"
+ inkscape:zoom="11.313709"
+ inkscape:cx="14.523034"
+ inkscape:cy="-5.7323703"
+ inkscape:window-x="4"
+ inkscape:window-y="20"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg919"
+ inkscape:snap-page="true" />
+ <path
+ style="fill:#6699ff;stroke:none;fill-opacity:0.69803923"
+ d="M 7 1 L 7 4.0605469 A 4.2661548 4.0576186 0 0 1 8 3.9414062 A 4.2661548 4.0576186 0 0 1 9 4.0605469 L 9 1 L 7 1 z M 1 7 L 1 9 L 3.8691406 9 A 4.2661548 4.0576186 0 0 1 3.734375 8 A 4.2661548 4.0576186 0 0 1 3.8710938 7 L 1 7 z M 12.130859 7 A 4.2661548 4.0576186 0 0 1 12.265625 8 A 4.2661548 4.0576186 0 0 1 12.128906 9 L 15 9 L 15 7 L 12.130859 7 z M 7 11.939453 L 7 15 L 9 15 L 9 11.939453 A 4.2661548 4.0576186 0 0 1 8 12.058594 A 4.2661548 4.0576186 0 0 1 7 11.939453 z "
+ id="path915" />
+ <circle
+ style="fill:#6699ff;fill-opacity:0.69803923;stroke-width:0.97338283"
+ cx="8"
+ cy="8"
+ r="2.9201488"
+ id="circle1517" />
+</svg>
diff --git a/editor/icons/icon_editor_position_unselected.svg b/editor/icons/icon_editor_position_unselected.svg
new file mode 100644
index 0000000000..8f32c89f16
--- /dev/null
+++ b/editor/icons/icon_editor_position_unselected.svg
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg919"
+ sodipodi:docname="icon_editor_position_unselected.svg"
+ inkscape:version="0.92.2 5c3e80d, 2017-08-06">
+ <metadata
+ id="metadata925">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs923" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="2550"
+ inkscape:window-height="1414"
+ id="namedview921"
+ showgrid="false"
+ inkscape:zoom="64"
+ inkscape:cx="13.492036"
+ inkscape:cy="8.3518769"
+ inkscape:window-x="1370"
+ inkscape:window-y="20"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg919"
+ inkscape:snap-page="true" />
+ <path
+ style="fill:#000000;fill-opacity:0.41077441"
+ d="M 6 0 L 6 4.4199219 A 4.2661548 4.0576186 0 0 0 4.2910156 6 L 0 6 L 0 10 L 4.2949219 10 A 4.2661548 4.0576186 0 0 0 6 11.582031 L 6 16 L 10 16 L 10 11.580078 A 4.2661548 4.0576186 0 0 0 11.708984 10 L 16 10 L 16 6 L 11.705078 6 A 4.2661548 4.0576186 0 0 0 10 4.4179688 L 10 0 L 6 0 z "
+ id="path913" />
+ <path
+ style="fill:#d9d9d9;stroke:none;fill-opacity:1"
+ d="M 7 1 L 7 4.0605469 A 4.2661548 4.0576186 0 0 1 8 3.9414062 A 4.2661548 4.0576186 0 0 1 9 4.0605469 L 9 1 L 7 1 z M 1 7 L 1 9 L 3.8691406 9 A 4.2661548 4.0576186 0 0 1 3.734375 8 A 4.2661548 4.0576186 0 0 1 3.8710938 7 L 1 7 z M 12.130859 7 A 4.2661548 4.0576186 0 0 1 12.265625 8 A 4.2661548 4.0576186 0 0 1 12.128906 9 L 15 9 L 15 7 L 12.130859 7 z M 7 11.939453 L 7 15 L 9 15 L 9 11.939453 A 4.2661548 4.0576186 0 0 1 8 12.058594 A 4.2661548 4.0576186 0 0 1 7 11.939453 z "
+ id="path915" />
+ <circle
+ style="fill:#d9d9d9;fill-opacity:1;stroke-width:0.97338283"
+ cx="8"
+ cy="8"
+ r="2.9201488"
+ id="circle1517" />
+</svg>
diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp
index b72c9b25be..c203b4b81e 100644
--- a/editor/plugins/asset_library_editor_plugin.cpp
+++ b/editor/plugins/asset_library_editor_plugin.cpp
@@ -902,7 +902,7 @@ void EditorAssetLibrary::_search(int p_page) {
}
if (filter->get_text() != String()) {
- args += "&filter=" + filter->get_text().percent_encode();
+ args += "&filter=" + filter->get_text().http_escape();
}
if (p_page > 0) {
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index b8d0958c99..bb94aee462 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -196,11 +196,15 @@ void CanvasItemEditor::_snap_other_nodes(Point2 p_value, Point2 &r_current_snap,
Transform2D ci_transform = canvas_item->get_global_transform_with_canvas();
Transform2D to_snap_transform = p_to_snap ? p_to_snap->get_global_transform_with_canvas() : Transform2D();
if (fmod(ci_transform.get_rotation() - to_snap_transform.get_rotation(), (real_t)360.0) == 0.0) {
- Point2 begin = ci_transform.xform(canvas_item->_edit_get_rect().get_position());
- Point2 end = ci_transform.xform(canvas_item->_edit_get_rect().get_position() + canvas_item->_edit_get_rect().get_size());
-
- _snap_if_closer_point(p_value, begin, r_current_snap, r_snapped, ci_transform.get_rotation());
- _snap_if_closer_point(p_value, end, r_current_snap, r_snapped, ci_transform.get_rotation());
+ if (canvas_item->_edit_use_rect()) {
+ Point2 begin = ci_transform.xform(canvas_item->_edit_get_rect().get_position());
+ Point2 end = ci_transform.xform(canvas_item->_edit_get_rect().get_position() + canvas_item->_edit_get_rect().get_size());
+ _snap_if_closer_point(p_value, begin, r_current_snap, r_snapped, ci_transform.get_rotation());
+ _snap_if_closer_point(p_value, end, r_current_snap, r_snapped, ci_transform.get_rotation());
+ } else {
+ Point2 position = ci_transform.xform(Point2());
+ _snap_if_closer_point(p_value, position, r_current_snap, r_snapped, ci_transform.get_rotation());
+ }
}
}
for (int i = 0; i < p_current->get_child_count(); i++) {
@@ -221,23 +225,23 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, const
// Parent sides and center
if ((is_snap_active && snap_node_parent && (p_modes & SNAP_NODE_PARENT)) || (p_forced_modes & SNAP_NODE_PARENT)) {
- Point2 begin;
- Point2 end;
- bool can_snap = false;
if (const Control *c = Object::cast_to<Control>(p_canvas_item)) {
- begin = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(0, 0)));
- end = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(1, 1)));
- can_snap = true;
- } else if (const CanvasItem *parent_ci = Object::cast_to<CanvasItem>(p_canvas_item->get_parent())) {
- begin = p_canvas_item->get_transform().affine_inverse().xform(parent_ci->_edit_get_rect().get_position());
- end = p_canvas_item->get_transform().affine_inverse().xform(parent_ci->_edit_get_rect().get_position() + parent_ci->_edit_get_rect().get_size());
- can_snap = true;
- }
-
- if (can_snap) {
+ Point2 begin = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(0, 0)));
+ Point2 end = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(1, 1)));
_snap_if_closer_point(p_target, begin, output, snapped, rotation);
_snap_if_closer_point(p_target, (begin + end) / 2.0, output, snapped, rotation);
_snap_if_closer_point(p_target, end, output, snapped, rotation);
+ } else if (const CanvasItem *parent_ci = Object::cast_to<CanvasItem>(p_canvas_item->get_parent())) {
+ if (parent_ci->_edit_use_rect()) {
+ Point2 begin = p_canvas_item->get_transform().affine_inverse().xform(parent_ci->_edit_get_rect().get_position());
+ Point2 end = p_canvas_item->get_transform().affine_inverse().xform(parent_ci->_edit_get_rect().get_position() + parent_ci->_edit_get_rect().get_size());
+ _snap_if_closer_point(p_target, begin, output, snapped, rotation);
+ _snap_if_closer_point(p_target, (begin + end) / 2.0, output, snapped, rotation);
+ _snap_if_closer_point(p_target, end, output, snapped, rotation);
+ } else {
+ Point2 position = p_canvas_item->get_transform().affine_inverse().xform(Point2());
+ _snap_if_closer_point(p_target, position, output, snapped, rotation);
+ }
}
}
@@ -253,16 +257,23 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, const
// Self sides
if ((is_snap_active && snap_node_sides && (p_modes & SNAP_NODE_SIDES)) || (p_forced_modes & SNAP_NODE_SIDES)) {
- Point2 begin = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->_edit_get_rect().get_position());
- Point2 end = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->_edit_get_rect().get_position() + p_canvas_item->_edit_get_rect().get_size());
- _snap_if_closer_point(p_target, begin, output, snapped, rotation);
- _snap_if_closer_point(p_target, end, output, snapped, rotation);
+ if (p_canvas_item->_edit_use_rect()) {
+ Point2 begin = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->_edit_get_rect().get_position());
+ Point2 end = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->_edit_get_rect().get_position() + p_canvas_item->_edit_get_rect().get_size());
+ _snap_if_closer_point(p_target, begin, output, snapped, rotation);
+ _snap_if_closer_point(p_target, end, output, snapped, rotation);
+ }
}
// Self center
if ((is_snap_active && snap_node_center && (p_modes & SNAP_NODE_CENTER)) || (p_forced_modes & SNAP_NODE_CENTER)) {
- Point2 center = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->_edit_get_rect().get_position() + p_canvas_item->_edit_get_rect().get_size() / 2.0);
- _snap_if_closer_point(p_target, center, output, snapped, rotation);
+ if (p_canvas_item->_edit_use_rect()) {
+ Point2 center = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->_edit_get_rect().get_position() + p_canvas_item->_edit_get_rect().get_size() / 2.0);
+ _snap_if_closer_point(p_target, center, output, snapped, rotation);
+ } else {
+ Point2 position = p_canvas_item->get_global_transform_with_canvas().xform(Point2());
+ _snap_if_closer_point(p_target, position, output, snapped, rotation);
+ }
}
}
@@ -364,63 +375,85 @@ Rect2 CanvasItemEditor::_get_encompassing_rect_from_list(List<CanvasItem *> p_li
// Handles the first element
CanvasItem *canvas_item = p_list.front()->get();
- Rect2 rect = Rect2(canvas_item->get_global_transform_with_canvas().xform(canvas_item->_edit_get_rect().position + canvas_item->_edit_get_rect().size / 2), Size2());
+ Rect2 rect;
+ if (canvas_item->_edit_use_rect()) {
+ rect = Rect2(canvas_item->get_global_transform_with_canvas().xform(canvas_item->_edit_get_rect().position + canvas_item->_edit_get_rect().size / 2), Size2());
+ } else {
+ rect = Rect2(canvas_item->get_global_transform_with_canvas().xform(Point2()), Size2());
+ }
- // Handles the other ones
+ // Expand with the other ones
for (List<CanvasItem *>::Element *E = p_list.front(); E; E = E->next()) {
CanvasItem *canvas_item = E->get();
- Rect2 current_rect = canvas_item->_edit_get_rect();
Transform2D xform = canvas_item->get_global_transform_with_canvas();
- rect.expand_to(xform.xform(current_rect.position));
- rect.expand_to(xform.xform(current_rect.position + Vector2(current_rect.size.x, 0)));
- rect.expand_to(xform.xform(current_rect.position + current_rect.size));
- rect.expand_to(xform.xform(current_rect.position + Vector2(0, current_rect.size.y)));
+ if (canvas_item->_edit_use_rect()) {
+ Rect2 current_rect = canvas_item->_edit_get_rect();
+
+ rect.expand_to(xform.xform(current_rect.position));
+ rect.expand_to(xform.xform(current_rect.position + Vector2(current_rect.size.x, 0)));
+ rect.expand_to(xform.xform(current_rect.position + current_rect.size));
+ rect.expand_to(xform.xform(current_rect.position + Vector2(0, current_rect.size.y)));
+ } else {
+ rect.expand_to(xform.xform(Point2()));
+ }
}
return rect;
}
-void CanvasItemEditor::_expand_encompassing_rect_using_children(Rect2 &r_rect, Node *p_node, bool &r_first, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) {
+void CanvasItemEditor::_expand_encompassing_rect_using_children(Rect2 &r_rect, const Node *p_node, bool &r_first, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) {
if (!p_node)
return;
if (Object::cast_to<Viewport>(p_node))
return;
- CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node);
+ const CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node);
- bool inherited = p_node != get_tree()->get_edited_scene_root() && p_node->get_filename() != "";
- bool editable = inherited && EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(p_node);
+ /*bool inherited = p_node != get_tree()->get_edited_scene_root() && p_node->get_filename() != "";
+ bool editable = !inherited || EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(p_node);
bool lock_children = p_node->has_meta("_edit_group_") && p_node->get_meta("_edit_group_");
- if (!lock_children && (!inherited || editable)) {
- for (int i = p_node->get_child_count() - 1; i >= 0; i--) {
- if (canvas_item && !canvas_item->is_set_as_toplevel()) {
- _expand_encompassing_rect_using_children(r_rect, p_node->get_child(i), r_first, p_parent_xform * canvas_item->get_transform(), p_canvas_xform);
- } else {
- CanvasLayer *canvas_layer = Object::cast_to<CanvasLayer>(p_node);
- _expand_encompassing_rect_using_children(r_rect, p_node->get_child(i), r_first, Transform2D(), canvas_layer ? canvas_layer->get_transform() : p_canvas_xform);
- }
+ if (!lock_children && editable) {}*/
+
+ for (int i = p_node->get_child_count() - 1; i >= 0; i--) {
+ if (canvas_item && !canvas_item->is_set_as_toplevel()) {
+ _expand_encompassing_rect_using_children(r_rect, p_node->get_child(i), r_first, p_parent_xform * canvas_item->get_transform(), p_canvas_xform);
+ } else {
+ const CanvasLayer *canvas_layer = Object::cast_to<CanvasLayer>(p_node);
+ _expand_encompassing_rect_using_children(r_rect, p_node->get_child(i), r_first, Transform2D(), canvas_layer ? canvas_layer->get_transform() : p_canvas_xform);
}
}
if (canvas_item && canvas_item->is_visible_in_tree() && !canvas_item->has_meta("_edit_lock_")) {
- Rect2 rect = canvas_item->_edit_get_rect();
Transform2D xform = p_parent_xform * p_canvas_xform * canvas_item->get_transform();
- if (r_first) {
- r_rect = Rect2(xform.xform(rect.position + rect.size / 2), Size2());
- r_first = false;
+ if (canvas_item->_edit_use_rect()) {
+ Rect2 rect = canvas_item->_edit_get_rect();
+ if (r_first) {
+ r_rect = Rect2(xform.xform(rect.position + rect.size / 2), Size2());
+ r_first = false;
+ }
+ if (r_rect.size != Size2()) {
+ r_rect.expand_to(xform.xform(rect.position));
+ r_rect.expand_to(xform.xform(rect.position + Point2(rect.size.x, 0)));
+ r_rect.expand_to(xform.xform(rect.position + Point2(0, rect.size.y)));
+ r_rect.expand_to(xform.xform(rect.position + rect.size));
+ }
+ } else {
+ if (r_first) {
+ r_rect = Rect2(xform.xform(Point2()), Size2());
+ r_first = false;
+ } else {
+ r_rect.expand_to(xform.xform(Point2()));
+ }
}
- r_rect.expand_to(xform.xform(rect.position));
- r_rect.expand_to(xform.xform(rect.position + Point2(rect.size.x, 0)));
- r_rect.expand_to(xform.xform(rect.position + Point2(0, rect.size.y)));
- r_rect.expand_to(xform.xform(rect.position + rect.size));
}
}
-Rect2 CanvasItemEditor::_get_scene_encompassing_rect() {
+Rect2 CanvasItemEditor::_get_encompassing_rect(const Node *p_node) {
Rect2 rect;
bool first = true;
- _expand_encompassing_rect_using_children(rect, editor->get_edited_scene(), first);
+ _expand_encompassing_rect_using_children(rect, p_node, first);
+
return rect;
}
@@ -433,22 +466,23 @@ void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_no
const real_t grab_distance = EDITOR_DEF("editors/poly_editor/point_grab_radius", 8);
CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node);
- for (int i = p_node->get_child_count() - 1; i >= 0; i--) {
- if (canvas_item && !canvas_item->is_set_as_toplevel())
- _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, 0, p_parent_xform * canvas_item->get_transform(), p_canvas_xform);
- else {
- CanvasLayer *cl = Object::cast_to<CanvasLayer>(p_node);
- _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, 0, Transform2D(), cl ? cl->get_transform() : p_canvas_xform); //use base transform
+ Node *scene = editor->get_edited_scene();
+ if (!(p_node != scene && p_node->get_owner() != scene && (!scene->is_editable_instance(p_node) || p_node->has_meta("_edit_lock_")))) {
+ for (int i = p_node->get_child_count() - 1; i >= 0; i--) {
+ if (canvas_item && !canvas_item->is_set_as_toplevel()) {
+ _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, limit, p_parent_xform * canvas_item->get_transform(), p_canvas_xform);
+ } else {
+ CanvasLayer *cl = Object::cast_to<CanvasLayer>(p_node);
+ _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, limit, Transform2D(), cl ? cl->get_transform() : p_canvas_xform);
+ }
+ if (limit != 0 && r_items.size() >= limit)
+ return;
}
- if (limit != 0 && r_items.size() >= limit)
- return;
}
- if (canvas_item && canvas_item->is_visible_in_tree() && !canvas_item->has_meta("_edit_lock_")) {
-
+ if (canvas_item && canvas_item->is_visible_in_tree() && (canvas_item->get_owner() != editor->get_edited_scene() || !canvas_item->has_meta("_edit_lock_"))) {
Transform2D xform = (p_parent_xform * p_canvas_xform * canvas_item->get_transform()).affine_inverse();
const real_t local_grab_distance = xform.basis_xform(Vector2(grab_distance, 0)).length();
-
if (canvas_item->_edit_is_selected_on_click(xform.xform(p_pos), local_grab_distance)) {
Node2D *node = Object::cast_to<Node2D>(canvas_item);
@@ -463,7 +497,7 @@ void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_no
return;
}
-void CanvasItemEditor::_find_canvas_items_at_rect(const Rect2 &p_rect, Node *p_node, List<CanvasItem *> *r_items, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) {
+void CanvasItemEditor::_find_canvas_items_in_rect(const Rect2 &p_rect, Node *p_node, List<CanvasItem *> *r_items, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) {
if (!p_node)
return;
if (Object::cast_to<Viewport>(p_node))
@@ -472,31 +506,37 @@ void CanvasItemEditor::_find_canvas_items_at_rect(const Rect2 &p_rect, Node *p_n
CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node);
bool inherited = p_node != get_tree()->get_edited_scene_root() && p_node->get_filename() != "";
- bool editable = inherited && EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(p_node);
+ bool editable = !inherited || EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(p_node);
bool lock_children = p_node->has_meta("_edit_group_") && p_node->get_meta("_edit_group_");
+ bool locked = p_node->has_meta("_edit_lock_") && p_node->get_meta("_edit_lock_");
- if (!lock_children && (!inherited || editable)) {
+ if (!lock_children && !locked && editable) {
for (int i = p_node->get_child_count() - 1; i >= 0; i--) {
if (canvas_item && !canvas_item->is_set_as_toplevel()) {
- _find_canvas_items_at_rect(p_rect, p_node->get_child(i), r_items, p_parent_xform * canvas_item->get_transform(), p_canvas_xform);
+ _find_canvas_items_in_rect(p_rect, p_node->get_child(i), r_items, p_parent_xform * canvas_item->get_transform(), p_canvas_xform);
} else {
CanvasLayer *canvas_layer = Object::cast_to<CanvasLayer>(p_node);
- _find_canvas_items_at_rect(p_rect, p_node->get_child(i), r_items, Transform2D(), canvas_layer ? canvas_layer->get_transform() : p_canvas_xform);
+ _find_canvas_items_in_rect(p_rect, p_node->get_child(i), r_items, Transform2D(), canvas_layer ? canvas_layer->get_transform() : p_canvas_xform);
}
}
}
if (canvas_item && canvas_item->is_visible_in_tree() && !canvas_item->has_meta("_edit_lock_")) {
-
- Rect2 rect = canvas_item->_edit_get_rect();
Transform2D xform = p_parent_xform * p_canvas_xform * canvas_item->get_transform();
- if (p_rect.has_point(xform.xform(rect.position)) &&
- p_rect.has_point(xform.xform(rect.position + Vector2(rect.size.x, 0))) &&
- p_rect.has_point(xform.xform(rect.position + Vector2(rect.size.x, rect.size.y))) &&
- p_rect.has_point(xform.xform(rect.position + Vector2(0, rect.size.y)))) {
+ if (canvas_item->_edit_use_rect()) {
+ Rect2 rect = canvas_item->_edit_get_rect();
+ if (p_rect.has_point(xform.xform(rect.position)) &&
+ p_rect.has_point(xform.xform(rect.position + Vector2(rect.size.x, 0))) &&
+ p_rect.has_point(xform.xform(rect.position + Vector2(rect.size.x, rect.size.y))) &&
+ p_rect.has_point(xform.xform(rect.position + Vector2(0, rect.size.y)))) {
- r_items->push_back(canvas_item);
+ r_items->push_back(canvas_item);
+ }
+ } else {
+ if (p_rect.has_point(xform.xform(Point2()))) {
+ r_items->push_back(canvas_item);
+ }
}
}
}
@@ -575,8 +615,11 @@ void CanvasItemEditor::_save_canvas_item_state(List<CanvasItem *> p_canvas_items
if (se) {
se->undo_state = canvas_item->_edit_get_state();
se->pre_drag_xform = canvas_item->get_global_transform_with_canvas();
- se->pre_drag_rect = canvas_item->_edit_get_rect();
-
+ if (canvas_item->_edit_use_rect()) {
+ se->pre_drag_rect = canvas_item->_edit_get_rect();
+ } else {
+ se->pre_drag_rect = Rect2();
+ }
se->pre_drag_bones_length = List<float>();
se->pre_drag_bones_undo_state = List<Dictionary>();
@@ -1290,54 +1333,55 @@ bool CanvasItemEditor::_gui_input_resize(const Ref<InputEvent> &p_event) {
List<CanvasItem *> selection = _get_edited_canvas_items();
if (selection.size() == 1) {
CanvasItem *canvas_item = selection[0];
+ if (canvas_item->_edit_use_rect()) {
+ Rect2 rect = canvas_item->_edit_get_rect();
+ Transform2D xform = transform * canvas_item->get_global_transform_with_canvas();
+
+ Vector2 endpoints[4] = {
+ xform.xform(rect.position),
+ xform.xform(rect.position + Vector2(rect.size.x, 0)),
+ xform.xform(rect.position + rect.size),
+ xform.xform(rect.position + Vector2(0, rect.size.y))
+ };
- Rect2 rect = canvas_item->_edit_get_rect();
- Transform2D xform = transform * canvas_item->get_global_transform_with_canvas();
-
- Vector2 endpoints[4] = {
- xform.xform(rect.position),
- xform.xform(rect.position + Vector2(rect.size.x, 0)),
- xform.xform(rect.position + rect.size),
- xform.xform(rect.position + Vector2(0, rect.size.y))
- };
-
- DragType dragger[] = {
- DRAG_TOP_LEFT,
- DRAG_TOP,
- DRAG_TOP_RIGHT,
- DRAG_RIGHT,
- DRAG_BOTTOM_RIGHT,
- DRAG_BOTTOM,
- DRAG_BOTTOM_LEFT,
- DRAG_LEFT
- };
-
- DragType resize_drag = DRAG_NONE;
- float radius = (select_handle->get_size().width / 2) * 1.5;
-
- for (int i = 0; i < 4; i++) {
- int prev = (i + 3) % 4;
- int next = (i + 1) % 4;
+ DragType dragger[] = {
+ DRAG_TOP_LEFT,
+ DRAG_TOP,
+ DRAG_TOP_RIGHT,
+ DRAG_RIGHT,
+ DRAG_BOTTOM_RIGHT,
+ DRAG_BOTTOM,
+ DRAG_BOTTOM_LEFT,
+ DRAG_LEFT
+ };
- Vector2 ofs = ((endpoints[i] - endpoints[prev]).normalized() + ((endpoints[i] - endpoints[next]).normalized())).normalized();
- ofs *= (select_handle->get_size().width / 2);
- ofs += endpoints[i];
- if (ofs.distance_to(b->get_position()) < radius)
- resize_drag = dragger[i * 2];
+ DragType resize_drag = DRAG_NONE;
+ float radius = (select_handle->get_size().width / 2) * 1.5;
- ofs = (endpoints[i] + endpoints[next]) / 2;
- ofs += (endpoints[next] - endpoints[i]).tangent().normalized() * (select_handle->get_size().width / 2);
- if (ofs.distance_to(b->get_position()) < radius)
- resize_drag = dragger[i * 2 + 1];
- }
+ for (int i = 0; i < 4; i++) {
+ int prev = (i + 3) % 4;
+ int next = (i + 1) % 4;
+
+ Vector2 ofs = ((endpoints[i] - endpoints[prev]).normalized() + ((endpoints[i] - endpoints[next]).normalized())).normalized();
+ ofs *= (select_handle->get_size().width / 2);
+ ofs += endpoints[i];
+ if (ofs.distance_to(b->get_position()) < radius)
+ resize_drag = dragger[i * 2];
+
+ ofs = (endpoints[i] + endpoints[next]) / 2;
+ ofs += (endpoints[next] - endpoints[i]).tangent().normalized() * (select_handle->get_size().width / 2);
+ if (ofs.distance_to(b->get_position()) < radius)
+ resize_drag = dragger[i * 2 + 1];
+ }
- if (resize_drag != DRAG_NONE) {
- drag_type = resize_drag;
- drag_from = transform.affine_inverse().xform(b->get_position());
- drag_selection = List<CanvasItem *>();
- drag_selection.push_back(canvas_item);
- _save_canvas_item_state(drag_selection);
- return true;
+ if (resize_drag != DRAG_NONE) {
+ drag_type = resize_drag;
+ drag_from = transform.affine_inverse().xform(b->get_position());
+ drag_selection = List<CanvasItem *>();
+ drag_selection.push_back(canvas_item);
+ _save_canvas_item_state(drag_selection);
+ return true;
+ }
}
}
}
@@ -1535,61 +1579,64 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) {
_save_canvas_item_state(drag_selection, true);
}
- _restore_canvas_item_state(drag_selection, true);
-
- bool move_local_base = k->get_alt();
- bool move_local_base_rotated = k->get_control() || k->get_metakey();
-
- Vector2 dir;
- if (k->get_scancode() == KEY_UP)
- dir += Vector2(0, -1);
- else if (k->get_scancode() == KEY_DOWN)
- dir += Vector2(0, 1);
- else if (k->get_scancode() == KEY_LEFT)
- dir += Vector2(-1, 0);
- else if (k->get_scancode() == KEY_RIGHT)
- dir += Vector2(1, 0);
- if (k->get_shift())
- dir *= grid_step * Math::pow(2.0, grid_step_multiplier);
-
- drag_to += dir;
- if (k->get_shift())
- drag_to = drag_to.snapped(grid_step * Math::pow(2.0, grid_step_multiplier));
-
- Point2 previous_pos;
- if (drag_selection.size() == 1) {
- Transform2D xform = drag_selection[0]->get_global_transform_with_canvas() * drag_selection[0]->get_transform().affine_inverse();
- previous_pos = xform.xform(drag_selection[0]->_edit_get_position());
- } else {
- previous_pos = _get_encompassing_rect_from_list(drag_selection).position;
- }
+ if (drag_selection.size() > 0) {
- Point2 new_pos;
- if (drag_selection.size() == 1) {
- Node2D *node_2d = Object::cast_to<Node2D>(drag_selection[0]);
- if (node_2d && move_local_base_rotated) {
- Transform2D m;
- m.rotate(node_2d->get_rotation());
- new_pos += m.xform(drag_to);
- } else if (move_local_base) {
- new_pos += drag_to;
+ _restore_canvas_item_state(drag_selection, true);
+
+ bool move_local_base = k->get_alt();
+ bool move_local_base_rotated = k->get_control() || k->get_metakey();
+
+ Vector2 dir;
+ if (k->get_scancode() == KEY_UP)
+ dir += Vector2(0, -1);
+ else if (k->get_scancode() == KEY_DOWN)
+ dir += Vector2(0, 1);
+ else if (k->get_scancode() == KEY_LEFT)
+ dir += Vector2(-1, 0);
+ else if (k->get_scancode() == KEY_RIGHT)
+ dir += Vector2(1, 0);
+ if (k->get_shift())
+ dir *= grid_step * Math::pow(2.0, grid_step_multiplier);
+
+ drag_to += dir;
+ if (k->get_shift())
+ drag_to = drag_to.snapped(grid_step * Math::pow(2.0, grid_step_multiplier));
+
+ Point2 previous_pos;
+ if (drag_selection.size() == 1) {
+ Transform2D xform = drag_selection[0]->get_global_transform_with_canvas() * drag_selection[0]->get_transform().affine_inverse();
+ previous_pos = xform.xform(drag_selection[0]->_edit_get_position());
+ } else {
+ previous_pos = _get_encompassing_rect_from_list(drag_selection).position;
+ }
+
+ Point2 new_pos;
+ if (drag_selection.size() == 1) {
+ Node2D *node_2d = Object::cast_to<Node2D>(drag_selection[0]);
+ if (node_2d && move_local_base_rotated) {
+ Transform2D m;
+ m.rotate(node_2d->get_rotation());
+ new_pos += m.xform(drag_to);
+ } else if (move_local_base) {
+ new_pos += drag_to;
+ } else {
+ new_pos = previous_pos + (drag_to - drag_from);
+ }
} else {
new_pos = previous_pos + (drag_to - drag_from);
}
- } else {
- new_pos = previous_pos + (drag_to - drag_from);
- }
- for (List<CanvasItem *>::Element *E = drag_selection.front(); E; E = E->next()) {
- CanvasItem *canvas_item = E->get();
- CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
- Transform2D xform = canvas_item->get_global_transform_with_canvas().affine_inverse() * canvas_item->get_transform();
+ for (List<CanvasItem *>::Element *E = drag_selection.front(); E; E = E->next()) {
+ CanvasItem *canvas_item = E->get();
+ CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
+ Transform2D xform = canvas_item->get_global_transform_with_canvas().affine_inverse() * canvas_item->get_transform();
- Node2D *node2d = Object::cast_to<Node2D>(canvas_item);
- if (node2d && se->pre_drag_bones_undo_state.size() > 0) {
- _solve_IK(node2d, new_pos);
- } else {
- canvas_item->_edit_set_position(canvas_item->_edit_get_position() + xform.xform(new_pos) - xform.xform(previous_pos));
+ Node2D *node2d = Object::cast_to<Node2D>(canvas_item);
+ if (node2d && se->pre_drag_bones_undo_state.size() > 0) {
+ _solve_IK(node2d, new_pos);
+ } else {
+ canvas_item->_edit_set_position(canvas_item->_edit_get_position() + xform.xform(new_pos) - xform.xform(previous_pos));
+ }
}
}
return true;
@@ -1648,6 +1695,7 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
return true;
} else if (!selection_results.empty()) {
+ // Sorts items according the their z-index
selection_results.sort();
NodePath root_path = get_tree()->get_edited_scene_root()->get_path();
@@ -1707,16 +1755,17 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
canvas_item = selection[0].item;
// Check if the canvas item is in a group, and select the group instead if it is the case
- CanvasItem *canvas_item_tmp = canvas_item;
- while (canvas_item_tmp) {
- if (canvas_item->has_meta("_edit_group_")) {
+ Node *node = canvas_item;
+ while (node && node != scene) {
+ CanvasItem *canvas_item_tmp = Object::cast_to<CanvasItem>(node);
+ if (canvas_item_tmp && node->has_meta("_edit_group_")) {
canvas_item = canvas_item_tmp;
}
- canvas_item_tmp = canvas_item_tmp->get_parent_item();
+ node = node->get_parent();
}
// Make sure the selected node is in the current scene
- Node *node = canvas_item;
+ node = canvas_item;
while (node && ((node != scene && node->get_owner() != scene) || !node->is_class("CanvasItem"))) {
node = node->get_parent();
};
@@ -1766,7 +1815,7 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
if (bsfrom.y > bsto.y)
SWAP(bsfrom.y, bsto.y);
- _find_canvas_items_at_rect(Rect2(bsfrom, bsto - bsfrom), scene, &selitems);
+ _find_canvas_items_in_rect(Rect2(bsfrom, bsto - bsfrom), scene, &selitems);
for (List<CanvasItem *>::Element *E = selitems.front(); E; E = E->next()) {
editor_selection->add_node(E->get());
}
@@ -1800,6 +1849,42 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
return false;
}
+bool CanvasItemEditor::_gui_input_hover(const Ref<InputEvent> &p_event) {
+
+ Ref<InputEventMouseMotion> m = p_event;
+ if (m.is_valid()) {
+ if (drag_type == DRAG_NONE && tool == TOOL_SELECT) {
+ Point2 click = transform.affine_inverse().xform(m->get_position());
+ Node *scene = editor->get_edited_scene();
+
+ //Checks if the hovered items changed, update the viewport if so
+ Vector<_SelectResult> hovering_results_tmp;
+ _find_canvas_items_at_pos(click, scene, hovering_results_tmp);
+ hovering_results_tmp.sort();
+ bool changed = false;
+ if (hovering_results.size() == hovering_results_tmp.size()) {
+ for (int i = 0; i < hovering_results.size(); i++) {
+ if (hovering_results[i].item != hovering_results_tmp[i].item) {
+ changed = true;
+ break;
+ }
+ }
+ } else {
+ changed = true;
+ }
+
+ if (changed) {
+ hovering_results = hovering_results_tmp;
+ viewport->update();
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) {
bool accepted = false;
if ((accepted = _gui_input_rulers_and_guides(p_event))) {
@@ -1827,6 +1912,9 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) {
if (accepted)
accept_event();
+ // Handles the mouse hovering
+ _gui_input_hover(p_event);
+
// Change the cursor
CursorShape c = CURSOR_ARROW;
switch (drag_type) {
@@ -2093,6 +2181,9 @@ void CanvasItemEditor::_draw_grid() {
void CanvasItemEditor::_draw_selection() {
Ref<Texture> pivot_icon = get_icon("EditorPivot", "EditorIcons");
+ Ref<Texture> position_icon = get_icon("EditorPosition", "EditorIcons");
+ Ref<Texture> previous_position_icon = get_icon("EditorPositionPrevious", "EditorIcons");
+
RID ci = viewport->get_canvas_item();
List<CanvasItem *> selection = _get_edited_canvas_items(false, false);
@@ -2102,8 +2193,6 @@ void CanvasItemEditor::_draw_selection() {
CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->get());
CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
- Rect2 rect = canvas_item->_edit_get_rect();
-
// Draw the previous position if we are dragging the node
if (show_helpers &&
(drag_type == DRAG_ALL || drag_type == DRAG_ROTATE ||
@@ -2112,42 +2201,52 @@ void CanvasItemEditor::_draw_selection() {
const Transform2D pre_drag_xform = transform * se->pre_drag_xform;
const Color pre_drag_color = Color(0.4, 0.6, 1, 0.7);
- Vector2 pre_drag_endpoints[4] = {
+ if (canvas_item->_edit_use_rect()) {
+ Vector2 pre_drag_endpoints[4] = {
- pre_drag_xform.xform(se->pre_drag_rect.position),
- pre_drag_xform.xform(se->pre_drag_rect.position + Vector2(se->pre_drag_rect.size.x, 0)),
- pre_drag_xform.xform(se->pre_drag_rect.position + se->pre_drag_rect.size),
- pre_drag_xform.xform(se->pre_drag_rect.position + Vector2(0, se->pre_drag_rect.size.y))
- };
+ pre_drag_xform.xform(se->pre_drag_rect.position),
+ pre_drag_xform.xform(se->pre_drag_rect.position + Vector2(se->pre_drag_rect.size.x, 0)),
+ pre_drag_xform.xform(se->pre_drag_rect.position + se->pre_drag_rect.size),
+ pre_drag_xform.xform(se->pre_drag_rect.position + Vector2(0, se->pre_drag_rect.size.y))
+ };
- for (int i = 0; i < 4; i++) {
- viewport->draw_line(pre_drag_endpoints[i], pre_drag_endpoints[(i + 1) % 4], pre_drag_color, 2);
+ for (int i = 0; i < 4; i++) {
+ viewport->draw_line(pre_drag_endpoints[i], pre_drag_endpoints[(i + 1) % 4], pre_drag_color, 2);
+ }
+ } else {
+ viewport->draw_texture(previous_position_icon, (pre_drag_xform.xform(Point2()) - (previous_position_icon->get_size() / 2)).floor());
}
}
Transform2D xform = transform * canvas_item->get_global_transform_with_canvas();
- VisualServer::get_singleton()->canvas_item_add_set_transform(ci, xform);
-
- // Draw the selected items surrounding boxes
- Vector2 endpoints[4] = {
- xform.xform(rect.position),
- xform.xform(rect.position + Vector2(rect.size.x, 0)),
- xform.xform(rect.position + rect.size),
- xform.xform(rect.position + Vector2(0, rect.size.y))
- };
- Color c = Color(1, 0.6, 0.4, 0.7);
+ // Draw the selected items position / surrounding boxes
+ if (canvas_item->_edit_use_rect()) {
+ Rect2 rect = canvas_item->_edit_get_rect();
+ Vector2 endpoints[4] = {
+ xform.xform(rect.position),
+ xform.xform(rect.position + Vector2(rect.size.x, 0)),
+ xform.xform(rect.position + rect.size),
+ xform.xform(rect.position + Vector2(0, rect.size.y))
+ };
+
+ Color c = Color(1, 0.6, 0.4, 0.7);
- VisualServer::get_singleton()->canvas_item_add_set_transform(ci, Transform2D());
+ for (int i = 0; i < 4; i++) {
+ viewport->draw_line(endpoints[i], endpoints[(i + 1) % 4], c, 2);
+ }
+ } else {
- for (int i = 0; i < 4; i++) {
- viewport->draw_line(endpoints[i], endpoints[(i + 1) % 4], c, 2);
+ Transform2D transform = Transform2D(xform.get_rotation(), xform.get_origin());
+ viewport->draw_set_transform_matrix(transform);
+ viewport->draw_texture(position_icon, -(position_icon->get_size() / 2));
+ viewport->draw_set_transform_matrix(Transform2D());
}
if (single && (tool == TOOL_SELECT || tool == TOOL_MOVE || tool == TOOL_ROTATE || tool == TOOL_EDIT_PIVOT)) { //kind of sucks
// Draw the pivot
if (canvas_item->_edit_get_pivot() != Vector2() || drag_type == DRAG_PIVOT || tool == TOOL_EDIT_PIVOT) { // This is not really clean :/
- viewport->draw_texture(pivot_icon, xform.xform(canvas_item->_edit_get_pivot()) + (-pivot_icon->get_size() / 2).floor());
+ viewport->draw_texture(pivot_icon, (xform.xform(canvas_item->_edit_get_pivot()) - (pivot_icon->get_size() / 2)).floor());
}
Control *control = Object::cast_to<Control>(canvas_item);
@@ -2329,7 +2428,14 @@ void CanvasItemEditor::_draw_selection() {
}
}
- if (tool == TOOL_SELECT) {
+ if (tool == TOOL_SELECT && canvas_item->_edit_use_rect()) {
+ Rect2 rect = canvas_item->_edit_get_rect();
+ Vector2 endpoints[4] = {
+ xform.xform(rect.position),
+ xform.xform(rect.position + Vector2(rect.size.x, 0)),
+ xform.xform(rect.position + rect.size),
+ xform.xform(rect.position + Vector2(0, rect.size.y))
+ };
for (int i = 0; i < 4; i++) {
// Draw the resize handles
int prev = (i + 3) % 4;
@@ -2506,32 +2612,121 @@ void CanvasItemEditor::_draw_bones() {
}
}
-void CanvasItemEditor::_draw_locks_and_groups(Node *p_node, const Transform2D &p_xform) {
+void CanvasItemEditor::_draw_invisible_nodes_positions(Node *p_node, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) {
+ ERR_FAIL_COND(!p_node);
+
+ Node *scene = editor->get_edited_scene();
+ if (p_node != scene && p_node->get_owner() != scene && !scene->is_editable_instance(p_node))
+ return;
+ CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node);
+ if (canvas_item && !canvas_item->is_visible())
+ return;
+
+ Transform2D parent_xform = p_parent_xform;
+ Transform2D canvas_xform = p_canvas_xform;
+
+ if (canvas_item && !canvas_item->is_set_as_toplevel()) {
+ parent_xform = parent_xform * canvas_item->get_transform();
+ } else {
+ CanvasLayer *cl = Object::cast_to<CanvasLayer>(p_node);
+ parent_xform = Transform2D();
+ canvas_xform = cl ? cl->get_transform() : p_canvas_xform;
+ }
+
+ for (int i = p_node->get_child_count() - 1; i >= 0; i--) {
+ _draw_invisible_nodes_positions(p_node->get_child(i), parent_xform, canvas_xform);
+ }
+
+ if (canvas_item && !canvas_item->_edit_use_rect() && !editor_selection->is_selected(canvas_item)) {
+ Transform2D xform = transform * canvas_xform * parent_xform;
+
+ // Draw the node's position
+ Ref<Texture> position_icon = get_icon("EditorPositionUnselected", "EditorIcons");
+ Transform2D transform = Transform2D(xform.get_rotation(), xform.get_origin());
+ viewport->draw_set_transform_matrix(transform);
+ viewport->draw_texture(position_icon, -position_icon->get_size() / 2, Color(1.0, 1.0, 1.0, 0.5));
+ viewport->draw_set_transform_matrix(Transform2D());
+ }
+}
+
+void CanvasItemEditor::_draw_hover() {
+ List<Rect2> previous_rects;
+
+ for (int i = 0; i < hovering_results.size(); i++) {
+ // Draw the node's name and icon
+ CanvasItem *canvas_item = hovering_results[i].item;
+
+ if (canvas_item->_edit_use_rect())
+ continue;
+
+ Transform2D xform = transform * canvas_item->get_global_transform_with_canvas();
+
+ // Get the resources
+ Ref<Texture> node_icon;
+ if (has_icon(canvas_item->get_class(), "EditorIcons"))
+ node_icon = get_icon(canvas_item->get_class(), "EditorIcons");
+ else
+ node_icon = get_icon("Object", "EditorIcons");
+ Ref<Font> font = get_font("font", "Label");
+ String node_name = canvas_item->get_name();
+ Size2 node_name_size = font->get_string_size(node_name);
+ Size2 item_size = Size2(node_icon->get_size().x + 4 + node_name_size.x, MAX(node_icon->get_size().y, node_name_size.y - 3));
+
+ Point2 pos = xform.get_origin() - Point2(0, item_size.y) + (Point2(node_icon->get_size().x, -node_icon->get_size().y) / 4);
+ // Rectify the position to avoid overlaping items
+ for (List<Rect2>::Element *E = previous_rects.front(); E; E = E->next()) {
+ if (E->get().intersects(Rect2(pos, item_size))) {
+ pos.y = E->get().get_position().y - item_size.y;
+ }
+ }
+
+ previous_rects.push_back(Rect2(pos, item_size));
+
+ // Draw the node icon and name
+ viewport->draw_texture(node_icon, pos, Color(1.0, 1.0, 1.0, 0.5));
+ viewport->draw_string(font, pos + Point2(node_icon->get_size().x + 4, item_size.y - 3), node_name, Color(1.0, 1.0, 1.0, 0.5));
+ }
+}
+
+void CanvasItemEditor::_draw_locks_and_groups(Node *p_node, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) {
ERR_FAIL_COND(!p_node);
- RID viewport_ci = viewport->get_canvas_item();
+ Node *scene = editor->get_edited_scene();
+ if (p_node != scene && p_node->get_owner() != scene && !scene->is_editable_instance(p_node->get_owner()))
+ return;
+ CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node);
+ if (canvas_item && !canvas_item->is_visible())
+ return;
+
+ Transform2D parent_xform = p_parent_xform;
+ Transform2D canvas_xform = p_canvas_xform;
- Transform2D transform_ci = p_xform;
- CanvasItem *ci = Object::cast_to<CanvasItem>(p_node);
- if (ci)
- transform_ci = transform_ci * ci->get_transform();
+ if (canvas_item && !canvas_item->is_set_as_toplevel()) {
+ parent_xform = parent_xform * canvas_item->get_transform();
+ } else {
+ CanvasLayer *cl = Object::cast_to<CanvasLayer>(p_node);
+ parent_xform = Transform2D();
+ canvas_xform = cl ? cl->get_transform() : p_canvas_xform;
+ }
for (int i = p_node->get_child_count() - 1; i >= 0; i--) {
- _draw_locks_and_groups(p_node->get_child(i), transform_ci);
+ _draw_locks_and_groups(p_node->get_child(i), parent_xform, canvas_xform);
}
- if (ci) {
+ RID viewport_canvas_item = viewport->get_canvas_item();
+ if (canvas_item) {
+ float offset = 0;
+
Ref<Texture> lock = get_icon("LockViewport", "EditorIcons");
if (p_node->has_meta("_edit_lock_")) {
- lock->draw(viewport_ci, transform_ci.xform(Point2(0, 0)));
+ lock->draw(viewport_canvas_item, (transform * canvas_xform * parent_xform).xform(Point2(0, 0)) + Point2(offset, 0));
+ offset += lock->get_size().x;
}
Ref<Texture> group = get_icon("GroupViewport", "EditorIcons");
- if (ci->has_meta("_edit_group_")) {
- Vector2 ofs = transform_ci.xform(Point2(0, 0));
- if (ci->has_meta("_edit_lock_"))
- ofs = Point2(ofs.x + lock->get_size().x, ofs.y);
- group->draw(viewport_ci, ofs);
+ if (canvas_item->has_meta("_edit_group_")) {
+ group->draw(viewport_canvas_item, (transform * canvas_xform * parent_xform).xform(Point2(0, 0)) + Point2(offset, 0));
+ //offset += group->get_size().x;
}
}
}
@@ -2597,8 +2792,10 @@ void CanvasItemEditor::_draw_viewport() {
_draw_grid();
_draw_selection();
_draw_axis();
- if (editor->get_edited_scene())
- _draw_locks_and_groups(editor->get_edited_scene(), transform);
+ if (editor->get_edited_scene()) {
+ _draw_locks_and_groups(editor->get_edited_scene());
+ _draw_invisible_nodes_positions(editor->get_edited_scene());
+ }
RID ci = viewport->get_canvas_item();
VisualServer::get_singleton()->canvas_item_add_set_transform(ci, Transform2D());
@@ -2618,6 +2815,7 @@ void CanvasItemEditor::_draw_viewport() {
if (show_guides)
_draw_guides();
_draw_focus();
+ _draw_hover();
}
void CanvasItemEditor::_notification(int p_what) {
@@ -2633,12 +2831,17 @@ void CanvasItemEditor::_notification(int p_what) {
CanvasItem *canvas_item = E->get();
CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
- Rect2 r = canvas_item->_edit_get_rect();
+ Rect2 rect;
+ if (canvas_item->_edit_use_rect()) {
+ rect = canvas_item->_edit_get_rect();
+ } else {
+ rect = Rect2();
+ }
Transform2D xform = canvas_item->get_transform();
- if (r != se->prev_rect || xform != se->prev_xform) {
+ if (rect != se->prev_rect || xform != se->prev_xform) {
viewport->update();
- se->prev_rect = r;
+ se->prev_rect = rect;
se->prev_xform = xform;
}
@@ -2840,7 +3043,7 @@ void CanvasItemEditor::_update_scrollbars() {
// Calculate scrollable area
Rect2 canvas_item_rect = Rect2(Point2(), screen_rect);
if (editor->get_edited_scene()) {
- Rect2 content_rect = _get_scene_encompassing_rect();
+ Rect2 content_rect = _get_encompassing_rect(editor->get_edited_scene());
canvas_item_rect.expand_to(content_rect.position);
canvas_item_rect.expand_to(content_rect.position + content_rect.size);
}
@@ -3539,7 +3742,12 @@ void CanvasItemEditor::_focus_selection(int p_op) {
//if (!canvas_item->is_visible_in_tree()) continue;
++count;
- Rect2 item_rect = canvas_item->_edit_get_rect();
+ Rect2 item_rect;
+ if (canvas_item->_edit_use_rect()) {
+ item_rect = canvas_item->_edit_get_rect();
+ } else {
+ item_rect = Rect2();
+ }
Vector2 pos = canvas_item->get_global_transform().get_origin();
Vector2 scale = canvas_item->get_global_transform().get_scale();
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index da7ce9172a..5ca8a37610 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -258,6 +258,7 @@ class CanvasItemEditor : public VBoxContainer {
};
Vector<_SelectResult> selection_results;
+ Vector<_SelectResult> hovering_results;
struct BoneList {
@@ -336,7 +337,7 @@ class CanvasItemEditor : public VBoxContainer {
Ref<ShortCut> divide_grid_step_shortcut;
void _find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<_SelectResult> &r_items, int limit = 0, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
- void _find_canvas_items_at_rect(const Rect2 &p_rect, Node *p_node, List<CanvasItem *> *r_items, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
+ void _find_canvas_items_in_rect(const Rect2 &p_rect, Node *p_node, List<CanvasItem *> *r_items, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
bool _select_click_on_item(CanvasItem *item, Point2 p_click_pos, bool p_append);
ConfirmationDialog *snap_dialog;
@@ -366,8 +367,8 @@ class CanvasItemEditor : public VBoxContainer {
List<CanvasItem *> _get_edited_canvas_items(bool retreive_locked = false, bool remove_canvas_item_if_parent_in_selection = true);
Rect2 _get_encompassing_rect_from_list(List<CanvasItem *> p_list);
- void _expand_encompassing_rect_using_children(Rect2 &p_rect, Node *p_node, bool &r_first, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
- Rect2 _get_scene_encompassing_rect();
+ void _expand_encompassing_rect_using_children(Rect2 &p_rect, const Node *p_node, bool &r_first, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
+ Rect2 _get_encompassing_rect(const Node *p_node);
Object *_get_editor_data(Object *p_what);
@@ -387,7 +388,9 @@ class CanvasItemEditor : public VBoxContainer {
void _draw_selection();
void _draw_axis();
void _draw_bones();
- void _draw_locks_and_groups(Node *p_node, const Transform2D &p_xform);
+ void _draw_invisible_nodes_positions(Node *p_node, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
+ void _draw_locks_and_groups(Node *p_node, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
+ void _draw_hover();
void _draw_viewport();
@@ -400,6 +403,7 @@ class CanvasItemEditor : public VBoxContainer {
bool _gui_input_select(const Ref<InputEvent> &p_event);
bool _gui_input_zoom_or_pan(const Ref<InputEvent> &p_event);
bool _gui_input_rulers_and_guides(const Ref<InputEvent> &p_event);
+ bool _gui_input_hover(const Ref<InputEvent> &p_event);
void _gui_input_viewport(const Ref<InputEvent> &p_event);
diff --git a/main/input_default.cpp b/main/input_default.cpp
index 083386f3d5..3c40be5082 100644
--- a/main/input_default.cpp
+++ b/main/input_default.cpp
@@ -423,10 +423,6 @@ void InputDefault::set_mouse_position(const Point2 &p_posf) {
mouse_speed_track.update(p_posf - mouse_pos);
mouse_pos = p_posf;
- if (custom_cursor.is_valid()) {
- //removed, please insist that we implement hardware cursors
- // VisualServer::get_singleton()->cursor_set_pos(get_mouse_position());
- }
}
Point2 InputDefault::get_mouse_position() const {
@@ -509,15 +505,19 @@ bool InputDefault::is_emulating_touchscreen() const {
return emulate_touch;
}
+Input::CursorShape InputDefault::get_default_cursor_shape() {
+ return default_shape;
+}
+
+void InputDefault::set_default_cursor_shape(CursorShape p_shape) {
+ default_shape = p_shape;
+ OS::get_singleton()->set_cursor_shape((OS::CursorShape)p_shape);
+}
+
void InputDefault::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
if (Engine::get_singleton()->is_editor_hint())
return;
- if (custom_cursor == p_cursor)
- return;
-
- custom_cursor = p_cursor;
-
OS::get_singleton()->set_custom_mouse_cursor(p_cursor, (OS::CursorShape)p_shape, p_hotspot);
}
diff --git a/main/input_default.h b/main/input_default.h
index 322e7d149b..6dd88cd31e 100644
--- a/main/input_default.h
+++ b/main/input_default.h
@@ -116,7 +116,7 @@ class InputDefault : public Input {
SpeedTrack mouse_speed_track;
Map<int, Joypad> joy_names;
int fallback_mapping;
- RES custom_cursor;
+ CursorShape default_shape = CURSOR_ARROW;
public:
enum HatMask {
@@ -228,6 +228,8 @@ public:
void set_emulate_touch(bool p_emulate);
virtual bool is_emulating_touchscreen() const;
+ virtual CursorShape get_default_cursor_shape();
+ virtual void set_default_cursor_shape(CursorShape p_shape);
virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape = Input::CURSOR_ARROW, const Vector2 &p_hotspot = Vector2());
virtual void set_mouse_in_window(bool p_in_window);
diff --git a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
index ab15e35f63..a9a40c0508 100644
--- a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
+++ b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
@@ -22,14 +22,6 @@
1FF4C1851F584E3F00A41E41 /* GameKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FF4C1841F584E3F00A41E41 /* GameKit.framework */; };
1FF4C1871F584E5600A41E41 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FF4C1861F584E5600A41E41 /* StoreKit.framework */; };
1FF4C1871F584E7600A41E41 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FF4C1881F584E7600A41E41 /* StoreKit.framework */; };
- D07CD43F1C5D573600B7FB28 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD4331C5D573600B7FB28 /* Default-568h@2x.png */; };
- D07CD4411C5D573600B7FB28 /* Default-667h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD4351C5D573600B7FB28 /* Default-667h@2x.png */; };
- D07CD4421C5D573600B7FB28 /* Default-Portrait-736h@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD4361C5D573600B7FB28 /* Default-Portrait-736h@3x.png */; };
- D07CD4441C5D573600B7FB28 /* Default-Landscape-736h@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD4381C5D573600B7FB28 /* Default-Landscape-736h@3x.png */; };
- D07CD4451C5D573600B7FB28 /* Default-Landscape@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD4391C5D573600B7FB28 /* Default-Landscape@2x.png */; };
- D07CD4461C5D573600B7FB28 /* Default-Landscape-1366h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD43A1C5D573600B7FB28 /* Default-Landscape-1366h@2x.png */; };
- D07CD4471C5D573600B7FB28 /* Default-Portrait@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD43B1C5D573600B7FB28 /* Default-Portrait@2x.png */; };
- D07CD4481C5D573600B7FB28 /* Default-Portrait-1366h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD43C1C5D573600B7FB28 /* Default-Portrait-1366h@2x.png */; };
D07CD44E1C5D589C00B7FB28 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D07CD44D1C5D589C00B7FB28 /* Images.xcassets */; };
D0BCFE3818AEBDA2004A7AAE /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0BCFE3718AEBDA2004A7AAE /* Foundation.framework */; };
D0BCFE3A18AEBDA2004A7AAE /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0BCFE3918AEBDA2004A7AAE /* CoreGraphics.framework */; };
@@ -57,14 +49,6 @@
1FF4C1881F584E7600A41E41 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
1FF4C1881F584E6300A41E41 /* $binary.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = $binary.entitlements; sourceTree = "<group>"; };
1FF8DBB01FBA9DE1009DE660 /* dummy.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = dummy.cpp; sourceTree = "<group>"; };
- D07CD4331C5D573600B7FB28 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = "<group>"; };
- D07CD4351C5D573600B7FB28 /* Default-667h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-667h@2x.png"; sourceTree = "<group>"; };
- D07CD4361C5D573600B7FB28 /* Default-Portrait-736h@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait-736h@3x.png"; sourceTree = "<group>"; };
- D07CD4381C5D573600B7FB28 /* Default-Landscape-736h@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape-736h@3x.png"; sourceTree = "<group>"; };
- D07CD4391C5D573600B7FB28 /* Default-Landscape@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape@2x.png"; sourceTree = "<group>"; };
- D07CD43A1C5D573600B7FB28 /* Default-Landscape-1366h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape-1366h@2x.png"; sourceTree = "<group>"; };
- D07CD43B1C5D573600B7FB28 /* Default-Portrait@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait@2x.png"; sourceTree = "<group>"; };
- D07CD43C1C5D573600B7FB28 /* Default-Portrait-1366h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait-1366h@2x.png"; sourceTree = "<group>"; };
D07CD44D1C5D589C00B7FB28 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
D0BCFE3418AEBDA2004A7AAE /* $binary.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = $binary.app; sourceTree = BUILT_PRODUCTS_DIR; };
D0BCFE3718AEBDA2004A7AAE /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
@@ -159,14 +143,6 @@
isa = PBXGroup;
children = (
1FF4C1881F584E6300A41E41 /* $binary.entitlements */,
- D07CD4331C5D573600B7FB28 /* Default-568h@2x.png */,
- D07CD4351C5D573600B7FB28 /* Default-667h@2x.png */,
- D07CD4361C5D573600B7FB28 /* Default-Portrait-736h@3x.png */,
- D07CD4381C5D573600B7FB28 /* Default-Landscape-736h@3x.png */,
- D07CD4391C5D573600B7FB28 /* Default-Landscape@2x.png */,
- D07CD43A1C5D573600B7FB28 /* Default-Landscape-1366h@2x.png */,
- D07CD43B1C5D573600B7FB28 /* Default-Portrait@2x.png */,
- D07CD43C1C5D573600B7FB28 /* Default-Portrait-1366h@2x.png */,
D07CD44D1C5D589C00B7FB28 /* Images.xcassets */,
D0BCFE4218AEBDA2004A7AAE /* Supporting Files */,
1FF8DBB01FBA9DE1009DE660 /* dummy.cpp */,
@@ -254,14 +230,6 @@
1F1575721F582BE20003B888 /* dylibs in Resources */,
D07CD44E1C5D589C00B7FB28 /* Images.xcassets in Resources */,
D0BCFE7818AEBFEB004A7AAE /* $binary.pck in Resources */,
- D07CD4471C5D573600B7FB28 /* Default-Portrait@2x.png in Resources */,
- D07CD4461C5D573600B7FB28 /* Default-Landscape-1366h@2x.png in Resources */,
- D07CD4411C5D573600B7FB28 /* Default-667h@2x.png in Resources */,
- D07CD43F1C5D573600B7FB28 /* Default-568h@2x.png in Resources */,
- D07CD4451C5D573600B7FB28 /* Default-Landscape@2x.png in Resources */,
- D07CD4441C5D573600B7FB28 /* Default-Landscape-736h@3x.png in Resources */,
- D07CD4421C5D573600B7FB28 /* Default-Portrait-736h@3x.png in Resources */,
- D07CD4481C5D573600B7FB28 /* Default-Portrait-1366h@2x.png in Resources */,
D0BCFE4618AEBDA2004A7AAE /* InfoPlist.strings in Resources */,
$additional_pbx_resources_build
);
@@ -377,6 +345,7 @@
buildSettings = {
ARCHS = "$godot_archs";
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
CODE_SIGN_ENTITLEMENTS = $binary/$binary.entitlements;
CODE_SIGN_IDENTITY = "$code_sign_identity_debug";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "$code_sign_identity_debug";
@@ -401,6 +370,7 @@
buildSettings = {
ARCHS = "$godot_archs";
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
CODE_SIGN_ENTITLEMENTS = $binary/$binary.entitlements;
CODE_SIGN_IDENTITY = "$code_sign_identity_release";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "$code_sign_identity_release";
diff --git a/misc/dist/ios_xcode/godot_ios/Default-568h@2x.png b/misc/dist/ios_xcode/godot_ios/Default-568h@2x.png
deleted file mode 100644
index 1d5e472665..0000000000
--- a/misc/dist/ios_xcode/godot_ios/Default-568h@2x.png
+++ /dev/null
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Default-667h@2x.png b/misc/dist/ios_xcode/godot_ios/Default-667h@2x.png
deleted file mode 100644
index b51598fed0..0000000000
--- a/misc/dist/ios_xcode/godot_ios/Default-667h@2x.png
+++ /dev/null
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Default-Landscape-1366h@2x.png b/misc/dist/ios_xcode/godot_ios/Default-Landscape-1366h@2x.png
deleted file mode 100644
index ec5b4f7888..0000000000
--- a/misc/dist/ios_xcode/godot_ios/Default-Landscape-1366h@2x.png
+++ /dev/null
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Default-Landscape-736h@3x.png b/misc/dist/ios_xcode/godot_ios/Default-Landscape-736h@3x.png
deleted file mode 100644
index 2a025b745b..0000000000
--- a/misc/dist/ios_xcode/godot_ios/Default-Landscape-736h@3x.png
+++ /dev/null
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Default-Landscape@2x.png b/misc/dist/ios_xcode/godot_ios/Default-Landscape@2x.png
deleted file mode 100644
index 7099f3e18d..0000000000
--- a/misc/dist/ios_xcode/godot_ios/Default-Landscape@2x.png
+++ /dev/null
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Default-Portrait-1366h@2x.png b/misc/dist/ios_xcode/godot_ios/Default-Portrait-1366h@2x.png
deleted file mode 100644
index a6d054ba2a..0000000000
--- a/misc/dist/ios_xcode/godot_ios/Default-Portrait-1366h@2x.png
+++ /dev/null
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Default-Portrait-736h@3x.png b/misc/dist/ios_xcode/godot_ios/Default-Portrait-736h@3x.png
deleted file mode 100644
index 8c44edbccd..0000000000
--- a/misc/dist/ios_xcode/godot_ios/Default-Portrait-736h@3x.png
+++ /dev/null
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Default-Portrait@2x.png b/misc/dist/ios_xcode/godot_ios/Default-Portrait@2x.png
deleted file mode 100644
index a6d054ba2a..0000000000
--- a/misc/dist/ios_xcode/godot_ios/Default-Portrait@2x.png
+++ /dev/null
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Contents.json b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Contents.json
new file mode 100644
index 0000000000..ce81d76027
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Contents.json
@@ -0,0 +1,102 @@
+{
+ "images" : [
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "2436h",
+ "filename" : "Default-Portrait-X.png",
+ "minimum-system-version" : "11.0",
+ "orientation" : "portrait",
+ "scale" : "3x"
+ },
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "2436h",
+ "filename" : "Default-Landscape-X.png",
+ "minimum-system-version" : "11.0",
+ "orientation" : "landscape",
+ "scale" : "3x"
+ },
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "736h",
+ "filename" : "Default-Portrait-736h@3x.png",
+ "minimum-system-version" : "8.0",
+ "orientation" : "portrait",
+ "scale" : "3x"
+ },
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "736h",
+ "filename" : "Default-Landscape-736h@3x.png",
+ "minimum-system-version" : "8.0",
+ "orientation" : "landscape",
+ "scale" : "3x"
+ },
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "667h",
+ "filename" : "Default-667h@2x.png",
+ "minimum-system-version" : "8.0",
+ "orientation" : "portrait",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "iphone",
+ "filename" : "Default-480h@2x.png",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ },
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "retina4",
+ "filename" : "Default-568h@2x.png",
+ "minimum-system-version" : "7.0",
+ "orientation" : "portrait",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "filename" : "Default-Portrait.png",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "filename" : "Default-Landscape.png",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "filename" : "Default-Portrait@2x.png",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "filename" : "Default-Landscape@2x.png",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+} \ No newline at end of file
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-480h@2x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-480h@2x.png
new file mode 100644
index 0000000000..6b9b10daae
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-480h@2x.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png
new file mode 100644
index 0000000000..50497496b7
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-667h@2x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-667h@2x.png
new file mode 100644
index 0000000000..1c489de69f
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-667h@2x.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-736h@3x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-736h@3x.png
new file mode 100644
index 0000000000..d82dfce936
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-736h@3x.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-X.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-X.png
new file mode 100644
index 0000000000..5120595df8
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-X.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape.png
new file mode 100644
index 0000000000..cf6cf1347a
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png
new file mode 100644
index 0000000000..4eb167ae18
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-736h@3x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-736h@3x.png
new file mode 100644
index 0000000000..a9f951deac
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-736h@3x.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-X.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-X.png
new file mode 100644
index 0000000000..06d16412e2
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-X.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait.png
new file mode 100644
index 0000000000..d70cab17be
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png
new file mode 100644
index 0000000000..f934971074
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png
Binary files differ
diff --git a/misc/travis/scons-local-osx.sh b/misc/travis/scons-local-osx.sh
index d9d7d98b38..fe7b43aadc 100755
--- a/misc/travis/scons-local-osx.sh
+++ b/misc/travis/scons-local-osx.sh
@@ -5,10 +5,10 @@ echo "Download and install Scons local package ..."
echo
echo "Downloading sources ..."
-curl -L -O http://prdownloads.sourceforge.net/scons/scons-local-3.0.0.zip # latest version available here: http://scons.org/pages/download.html
+curl -L -O https://downloads.sourceforge.net/scons/scons-local-3.0.1.zip # latest version available here: http://scons.org/pages/download.html
echo "Extracting to build directory ..."
-unzip -qq -n scons-local-3.0.0.zip -d $TRAVIS_BUILD_DIR/scons-local
+unzip -qq -n scons-local-3.0.1.zip -d $TRAVIS_BUILD_DIR/scons-local
echo "Installing symlinks ..."
ln -s $TRAVIS_BUILD_DIR/scons-local/scons.py /usr/local/bin/scons
diff --git a/modules/bullet/area_bullet.cpp b/modules/bullet/area_bullet.cpp
index ec78cddb6a..bfb452d109 100644
--- a/modules/bullet/area_bullet.cpp
+++ b/modules/bullet/area_bullet.cpp
@@ -68,8 +68,9 @@ AreaBullet::AreaBullet() :
}
AreaBullet::~AreaBullet() {
- // Call "remove_all_overlapping_instantly();" is not necessary because the exit
- // signal are handled by godot, so just clear the array
+ // signal are handled by godot, so just clear without notify
+ for (int i = overlappingObjects.size() - 1; 0 <= i; --i)
+ overlappingObjects[i].object->on_exit_area(this);
}
void AreaBullet::dispatch_callbacks() {
@@ -122,24 +123,21 @@ void AreaBullet::scratch() {
isScratched = true;
}
-void AreaBullet::remove_all_overlapping_instantly() {
- CollisionObjectBullet *supportObject;
+void AreaBullet::clear_overlaps(bool p_notify) {
for (int i = overlappingObjects.size() - 1; 0 <= i; --i) {
- supportObject = overlappingObjects[i].object;
- call_event(supportObject, PhysicsServer::AREA_BODY_REMOVED);
- supportObject->on_exit_area(this);
+ if (p_notify)
+ call_event(overlappingObjects[i].object, PhysicsServer::AREA_BODY_REMOVED);
+ overlappingObjects[i].object->on_exit_area(this);
}
overlappingObjects.clear();
}
-void AreaBullet::remove_overlapping_instantly(CollisionObjectBullet *p_object, bool p_notify) {
- CollisionObjectBullet *supportObject;
+void AreaBullet::remove_overlap(CollisionObjectBullet *p_object, bool p_notify) {
for (int i = overlappingObjects.size() - 1; 0 <= i; --i) {
- supportObject = overlappingObjects[i].object;
- if (supportObject == p_object) {
+ if (overlappingObjects[i].object == p_object) {
if (p_notify)
- call_event(supportObject, PhysicsServer::AREA_BODY_REMOVED);
- supportObject->on_exit_area(this);
+ call_event(overlappingObjects[i].object, PhysicsServer::AREA_BODY_REMOVED);
+ overlappingObjects[i].object->on_exit_area(this);
overlappingObjects.remove(i);
break;
}
diff --git a/modules/bullet/area_bullet.h b/modules/bullet/area_bullet.h
index 4104780de9..b2046c684e 100644
--- a/modules/bullet/area_bullet.h
+++ b/modules/bullet/area_bullet.h
@@ -150,9 +150,9 @@ public:
void set_on_state_change(ObjectID p_id, const StringName &p_method, const Variant &p_udata = Variant());
void scratch();
- void remove_all_overlapping_instantly();
+ void clear_overlaps(bool p_notify);
// Dispatch the callbacks and removes from overlapping list
- void remove_overlapping_instantly(CollisionObjectBullet *p_object, bool p_notify);
+ void remove_overlap(CollisionObjectBullet *p_object, bool p_notify);
virtual void on_collision_filters_change();
virtual void on_collision_checker_start() {}
diff --git a/modules/bullet/collision_object_bullet.cpp b/modules/bullet/collision_object_bullet.cpp
index 77f8df34cb..05c0e653df 100644
--- a/modules/bullet/collision_object_bullet.cpp
+++ b/modules/bullet/collision_object_bullet.cpp
@@ -70,7 +70,7 @@ CollisionObjectBullet::CollisionObjectBullet(Type p_type) :
CollisionObjectBullet::~CollisionObjectBullet() {
// Remove all overlapping, notify is not required since godot take care of it
for (int i = areasOverlapped.size() - 1; 0 <= i; --i) {
- areasOverlapped[i]->remove_overlapping_instantly(this, /*Notify*/ false);
+ areasOverlapped[i]->remove_overlap(this, /*Notify*/ false);
}
destroyBulletCollisionObject();
diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp
index 51a76ff8c5..8450a66f65 100644
--- a/modules/bullet/space_bullet.cpp
+++ b/modules/bullet/space_bullet.cpp
@@ -996,7 +996,7 @@ public:
}
void reset() {
- result_collision_objects.empty();
+ result_collision_objects.clear();
}
};
@@ -1031,7 +1031,10 @@ bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTran
for (int i = recover_broad_result.result_collision_objects.size() - 1; 0 <= i; --i) {
btCollisionObject *otherObject = recover_broad_result.result_collision_objects[i];
- if (!p_body->get_bt_collision_object()->checkCollideWith(otherObject) || !otherObject->checkCollideWith(p_body->get_bt_collision_object()) || (p_infinite_inertia && !otherObject->isStaticOrKinematicObject()))
+ if (p_infinite_inertia && !otherObject->isStaticOrKinematicObject()) {
+ otherObject->activate(); // Force activation of hitten rigid, soft body
+ continue;
+ } else if (!p_body->get_bt_collision_object()->checkCollideWith(otherObject) || !otherObject->checkCollideWith(p_body->get_bt_collision_object()))
continue;
if (otherObject->getCollisionShape()->isCompound()) {
diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp
index f3f4acd768..95bb472c7b 100644
--- a/modules/enet/networked_multiplayer_enet.cpp
+++ b/modules/enet/networked_multiplayer_enet.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "networked_multiplayer_enet.h"
+#include "io/ip.h"
#include "io/marshalls.h"
#include "os/os.h"
@@ -92,32 +93,42 @@ Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int
connection_status = CONNECTION_CONNECTED;
return OK;
}
-Error NetworkedMultiplayerENet::create_client(const IP_Address &p_ip, int p_port, int p_in_bandwidth, int p_out_bandwidth) {
+Error NetworkedMultiplayerENet::create_client(const String &p_address, int p_port, int p_in_bandwidth, int p_out_bandwidth) {
ERR_FAIL_COND_V(active, ERR_ALREADY_IN_USE);
host = enet_host_create(NULL /* create a client host */,
1 /* only allow 1 outgoing connection */,
SYSCH_MAX /* allow up to SYSCH_MAX channels to be used */,
- p_in_bandwidth /* 56K modem with 56 Kbps downstream bandwidth */,
- p_out_bandwidth /* 56K modem with 14 Kbps upstream bandwidth */);
+ p_in_bandwidth /* limit incoming bandwith if > 0 */,
+ p_out_bandwidth /* limit outgoing bandwith if > 0 */);
ERR_FAIL_COND_V(!host, ERR_CANT_CREATE);
_setup_compressor();
+ IP_Address ip;
+ if (p_address.is_valid_ip_address()) {
+ ip = p_address;
+ } else {
+#ifdef GODOT_ENET
+ ip = IP::get_singleton()->resolve_hostname(p_address);
+#else
+ ip = IP::get_singleton()->resolve_hostname(p_address, IP::TYPE_IPV4);
+#endif
+
+ ERR_FAIL_COND_V(!ip.is_valid(), ERR_CANT_RESOLVE);
+ }
+
ENetAddress address;
#ifdef GODOT_ENET
- enet_address_set_ip(&address, p_ip.get_ipv6(), 16);
+ enet_address_set_ip(&address, ip.get_ipv6(), 16);
#else
- ERR_FAIL_COND_V(!p_ip.is_ipv4(), ERR_INVALID_PARAMETER);
- address.host = *(uint32_t *)p_ip.get_ipv4();
+ ERR_FAIL_COND_V(!ip.is_ipv4(), ERR_INVALID_PARAMETER);
+ address.host = *(uint32_t *)ip.get_ipv4();
#endif
address.port = p_port;
- //enet_address_set_host (& address, "localhost");
- //address.port = p_port;
-
unique_id = _gen_unique_id();
/* Initiate the connection, allocating the enough channels */
@@ -128,7 +139,7 @@ Error NetworkedMultiplayerENet::create_client(const IP_Address &p_ip, int p_port
ERR_FAIL_COND_V(!peer, ERR_CANT_CREATE);
}
- //technically safe to ignore the peer or anything else.
+ // Technically safe to ignore the peer or anything else.
connection_status = CONNECTION_CONNECTING;
active = true;
@@ -148,13 +159,13 @@ void NetworkedMultiplayerENet::poll() {
/* Wait up to 1000 milliseconds for an event. */
while (true) {
- if (!host || !active) //might have been disconnected while emitting a notification
+ if (!host || !active) // Might have been disconnected while emitting a notification
return;
int ret = enet_host_service(host, &event, 1);
if (ret < 0) {
- //error, do something?
+ // Error, do something?
break;
} else if (ret == 0) {
break;
@@ -172,7 +183,7 @@ void NetworkedMultiplayerENet::poll() {
int *new_id = memnew(int);
*new_id = event.data;
- if (*new_id == 0) { //data zero is sent by server (enet won't let you configure this). Server is always 1
+ if (*new_id == 0) { // Data zero is sent by server (enet won't let you configure this). Server is always 1
*new_id = 1;
}
@@ -180,22 +191,22 @@ void NetworkedMultiplayerENet::poll() {
peer_map[*new_id] = event.peer;
- connection_status = CONNECTION_CONNECTED; //if connecting, this means it connected t something!
+ connection_status = CONNECTION_CONNECTED; // If connecting, this means it connected to something!
emit_signal("peer_connected", *new_id);
if (server) {
- //someone connected, let it know of all the peers available
+ // Someone connected, notify all the peers available
for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) {
if (E->key() == *new_id)
continue;
- //send existing peers to new peer
+ // Send existing peers to new peer
ENetPacket *packet = enet_packet_create(NULL, 8, ENET_PACKET_FLAG_RELIABLE);
encode_uint32(SYSMSG_ADD_PEER, &packet->data[0]);
encode_uint32(E->key(), &packet->data[4]);
enet_peer_send(event.peer, SYSCH_CONFIG, packet);
- //send the new peer to existing peers
+ // Send the new peer to existing peers
packet = enet_packet_create(NULL, 8, ENET_PACKET_FLAG_RELIABLE);
encode_uint32(SYSMSG_ADD_PEER, &packet->data[0]);
encode_uint32(*new_id, &packet->data[4]);
@@ -220,12 +231,12 @@ void NetworkedMultiplayerENet::poll() {
} else {
if (server) {
- //someone disconnected, let it know to everyone else
+ // Someone disconnected, notify everyone else
for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) {
if (E->key() == *id)
continue;
- //send the new peer to existing peers
+
ENetPacket *packet = enet_packet_create(NULL, 8, ENET_PACKET_FLAG_RELIABLE);
encode_uint32(SYSMSG_REMOVE_PEER, &packet->data[0]);
encode_uint32(*id, &packet->data[4]);
@@ -246,7 +257,7 @@ void NetworkedMultiplayerENet::poll() {
case ENET_EVENT_TYPE_RECEIVE: {
if (event.channelID == SYSCH_CONFIG) {
- //some config message
+ // Some config message
ERR_CONTINUE(event.packet->dataLength < 8);
// Only server can send config messages
@@ -292,13 +303,13 @@ void NetworkedMultiplayerENet::poll() {
packet.from = *id;
if (target == 0) {
- //re-send the everyone but sender :|
+ // Re-send to everyone but sender :|
incoming_packets.push_back(packet);
- //and make copies for sending
+ // And make copies for sending
for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) {
- if (uint32_t(E->key()) == source) //do not resend to self
+ if (uint32_t(E->key()) == source) // Do not resend to self
continue;
ENetPacket *packet2 = enet_packet_create(packet.packet->data, packet.packet->dataLength, flags);
@@ -307,12 +318,12 @@ void NetworkedMultiplayerENet::poll() {
}
} else if (target < 0) {
- //to all but one
+ // To all but one
- //and make copies for sending
+ // And make copies for sending
for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) {
- if (uint32_t(E->key()) == source || E->key() == -target) //do not resend to self, also do not send to excluded
+ if (uint32_t(E->key()) == source || E->key() == -target) // Do not resend to self, also do not send to excluded
continue;
ENetPacket *packet2 = enet_packet_create(packet.packet->data, packet.packet->dataLength, flags);
@@ -321,18 +332,18 @@ void NetworkedMultiplayerENet::poll() {
}
if (-target != 1) {
- //server is not excluded
+ // Server is not excluded
incoming_packets.push_back(packet);
} else {
- //server is excluded, erase packet
+ // Server is excluded, erase packet
enet_packet_destroy(packet.packet);
}
} else if (target == 1) {
- //to myself and only myself
+ // To myself and only myself
incoming_packets.push_back(packet);
} else {
- //to someone else, specifically
+ // To someone else, specifically
ERR_CONTINUE(!peer_map.has(target));
enet_peer_send(peer_map[target], event.channelID, packet.packet);
}
@@ -341,14 +352,14 @@ void NetworkedMultiplayerENet::poll() {
incoming_packets.push_back(packet);
}
- //destroy packet later..
+ // Destroy packet later..
} else {
ERR_CONTINUE(true);
}
} break;
case ENET_EVENT_TYPE_NONE: {
- //do nothing
+ // Do nothing
} break;
}
}
@@ -377,16 +388,46 @@ void NetworkedMultiplayerENet::close_connection() {
if (peers_disconnected) {
enet_host_flush(host);
- OS::get_singleton()->delay_usec(100); //wait 100ms for disconnection packets to send
+ OS::get_singleton()->delay_usec(100); // Wait 100ms for disconnection packets to send
}
enet_host_destroy(host);
active = false;
incoming_packets.clear();
- unique_id = 1; //server is 1
+ unique_id = 1; // Server is 1
connection_status = CONNECTION_DISCONNECTED;
}
+void NetworkedMultiplayerENet::disconnect_peer(int p_peer, bool now) {
+
+ ERR_FAIL_COND(!active);
+ ERR_FAIL_COND(!is_server());
+ ERR_FAIL_COND(!peer_map.has(p_peer))
+
+ if (now) {
+ enet_peer_disconnect_now(peer_map[p_peer], 0);
+
+ // enet_peer_disconnect_now doesn't generate ENET_EVENT_TYPE_DISCONNECT,
+ // notify everyone else, send disconnect signal & remove from peer_map like in poll()
+
+ for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) {
+
+ if (E->key() == p_peer)
+ continue;
+
+ ENetPacket *packet = enet_packet_create(NULL, 8, ENET_PACKET_FLAG_RELIABLE);
+ encode_uint32(SYSMSG_REMOVE_PEER, &packet->data[0]);
+ encode_uint32(p_peer, &packet->data[4]);
+ enet_peer_send(E->get(), SYSCH_CONFIG, packet);
+ }
+
+ emit_signal("peer_disconnected", p_peer);
+ peer_map.erase(p_peer);
+ } else {
+ enet_peer_disconnect_later(peer_map[p_peer], 0);
+ }
+}
+
int NetworkedMultiplayerENet::get_available_packet_count() const {
return incoming_packets.size();
@@ -440,9 +481,9 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer
}
ENetPacket *packet = enet_packet_create(NULL, p_buffer_size + 12, packet_flags);
- encode_uint32(unique_id, &packet->data[0]); //source ID
- encode_uint32(target_peer, &packet->data[4]); //dest ID
- encode_uint32(packet_flags, &packet->data[8]); //dest ID
+ encode_uint32(unique_id, &packet->data[0]); // Source ID
+ encode_uint32(target_peer, &packet->data[4]); // Dest ID
+ encode_uint32(packet_flags, &packet->data[8]); // Dest ID
copymem(&packet->data[12], p_buffer, p_buffer_size);
if (server) {
@@ -450,14 +491,14 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer
if (target_peer == 0) {
enet_host_broadcast(host, channel, packet);
} else if (target_peer < 0) {
- //send to all but one
- //and make copies for sending
+ // Send to all but one
+ // and make copies for sending
int exclude = -target_peer;
for (Map<int, ENetPeer *>::Element *F = peer_map.front(); F; F = F->next()) {
- if (F->key() == exclude) // exclude packet
+ if (F->key() == exclude) // Exclude packet
continue;
ENetPacket *packet2 = enet_packet_create(packet->data, packet->dataLength, packet_flags);
@@ -465,14 +506,14 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer
enet_peer_send(F->get(), channel, packet2);
}
- enet_packet_destroy(packet); //original packet no longer needed
+ enet_packet_destroy(packet); // Original packet no longer needed
} else {
enet_peer_send(E->get(), channel, packet);
}
} else {
ERR_FAIL_COND_V(!peer_map.has(1), ERR_BUG);
- enet_peer_send(peer_map[1], channel, packet); //send to server for broadcast..
+ enet_peer_send(peer_map[1], channel, packet); // Send to server for broadcast..
}
enet_host_flush(host);
@@ -482,7 +523,7 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer
int NetworkedMultiplayerENet::get_max_packet_size() const {
- return 1 << 24; //anything is good
+ return 1 << 24; // Anything is good
}
void NetworkedMultiplayerENet::_pop_current_packet() {
@@ -511,16 +552,12 @@ uint32_t NetworkedMultiplayerENet::_gen_unique_id() const {
(uint32_t)OS::get_singleton()->get_unix_time(), hash);
hash = hash_djb2_one_32(
(uint32_t)OS::get_singleton()->get_user_data_dir().hash64(), hash);
- /*
hash = hash_djb2_one_32(
- (uint32_t)OS::get_singleton()->get_unique_id().hash64(), hash );
- */
+ (uint32_t)((uint64_t)this), hash); // Rely on ASLR heap
hash = hash_djb2_one_32(
- (uint32_t)((uint64_t)this), hash); //rely on aslr heap
- hash = hash_djb2_one_32(
- (uint32_t)((uint64_t)&hash), hash); //rely on aslr stack
+ (uint32_t)((uint64_t)&hash), hash); // Rely on ASLR stack
- hash = hash & 0x7FFFFFFF; // make it compatible with unsigned, since negatie id is used for exclusion
+ hash = hash & 0x7FFFFFFF; // Make it compatible with unsigned, since negative ID is used for exclusion
}
return hash;
@@ -596,7 +633,7 @@ size_t NetworkedMultiplayerENet::enet_compress(void *context, const ENetBuffer *
return 0;
if (ret > int(outLimit))
- return 0; //do not bother
+ return 0; // Do not bother
copymem(outData, enet->dst_compressor_mem.ptr(), ret);
@@ -651,17 +688,48 @@ void NetworkedMultiplayerENet::_setup_compressor() {
void NetworkedMultiplayerENet::enet_compressor_destroy(void *context) {
- //do none
+ // Nothing to do
+}
+
+IP_Address NetworkedMultiplayerENet::get_peer_address(int p_peer_id) const {
+
+ ERR_FAIL_COND_V(!peer_map.has(p_peer_id), IP_Address());
+ ERR_FAIL_COND_V(!is_server() && p_peer_id != 1, IP_Address());
+ ERR_FAIL_COND_V(peer_map[p_peer_id] == NULL, IP_Address());
+
+ IP_Address out;
+#ifdef GODOT_ENET
+ out.set_ipv6((uint8_t *)&(peer_map[p_peer_id]->address.host));
+#else
+ out.set_ipv4((uint8_t *)&(peer_map[p_peer_id]->address.host));
+#endif
+
+ return out;
+}
+
+int NetworkedMultiplayerENet::get_peer_port(int p_peer_id) const {
+
+ ERR_FAIL_COND_V(!peer_map.has(p_peer_id), 0);
+ ERR_FAIL_COND_V(!is_server() && p_peer_id != 1, 0);
+ ERR_FAIL_COND_V(peer_map[p_peer_id] == NULL, 0);
+#ifdef GODOT_ENET
+ return peer_map[p_peer_id]->address.port;
+#else
+ return peer_map[p_peer_id]->address.port;
+#endif
}
void NetworkedMultiplayerENet::_bind_methods() {
ClassDB::bind_method(D_METHOD("create_server", "port", "max_clients", "in_bandwidth", "out_bandwidth"), &NetworkedMultiplayerENet::create_server, DEFVAL(32), DEFVAL(0), DEFVAL(0));
- ClassDB::bind_method(D_METHOD("create_client", "ip", "port", "in_bandwidth", "out_bandwidth"), &NetworkedMultiplayerENet::create_client, DEFVAL(0), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("create_client", "address", "port", "in_bandwidth", "out_bandwidth"), &NetworkedMultiplayerENet::create_client, DEFVAL(0), DEFVAL(0));
ClassDB::bind_method(D_METHOD("close_connection"), &NetworkedMultiplayerENet::close_connection);
+ ClassDB::bind_method(D_METHOD("disconnect_peer", "id", "now"), &NetworkedMultiplayerENet::disconnect_peer, DEFVAL(false));
ClassDB::bind_method(D_METHOD("set_compression_mode", "mode"), &NetworkedMultiplayerENet::set_compression_mode);
ClassDB::bind_method(D_METHOD("get_compression_mode"), &NetworkedMultiplayerENet::get_compression_mode);
ClassDB::bind_method(D_METHOD("set_bind_ip", "ip"), &NetworkedMultiplayerENet::set_bind_ip);
+ ClassDB::bind_method(D_METHOD("get_peer_address"), &NetworkedMultiplayerENet::get_peer_address);
+ ClassDB::bind_method(D_METHOD("get_peer_port"), &NetworkedMultiplayerENet::get_peer_port);
ADD_PROPERTY(PropertyInfo(Variant::INT, "compression_mode", PROPERTY_HINT_ENUM, "None,Range Coder,FastLZ,ZLib,ZStd"), "set_compression_mode", "get_compression_mode");
@@ -696,7 +764,7 @@ NetworkedMultiplayerENet::~NetworkedMultiplayerENet() {
close_connection();
}
-// sets IP for ENet to bind when using create_server
+// Sets IP for ENet to bind when using create_server
// if no IP is set, then ENet bind to ENET_HOST_ANY
void NetworkedMultiplayerENet::set_bind_ip(const IP_Address &p_ip) {
ERR_FAIL_COND(!p_ip.is_valid() && !p_ip.is_wildcard());
diff --git a/modules/enet/networked_multiplayer_enet.h b/modules/enet/networked_multiplayer_enet.h
index 440e9b5400..678ae24135 100644
--- a/modules/enet/networked_multiplayer_enet.h
+++ b/modules/enet/networked_multiplayer_enet.h
@@ -115,11 +115,16 @@ public:
virtual int get_packet_peer() const;
+ virtual IP_Address get_peer_address(int p_peer_id) const;
+ virtual int get_peer_port(int p_peer_id) const;
+
Error create_server(int p_port, int p_max_clients = 32, int p_in_bandwidth = 0, int p_out_bandwidth = 0);
- Error create_client(const IP_Address &p_ip, int p_port, int p_in_bandwidth = 0, int p_out_bandwidth = 0);
+ Error create_client(const String &p_address, int p_port, int p_in_bandwidth = 0, int p_out_bandwidth = 0);
void close_connection();
+ void disconnect_peer(int p_peer, bool now = false);
+
virtual void poll();
virtual bool is_server() const;
diff --git a/modules/gdnative/doc_classes/GDNativeLibrary.xml b/modules/gdnative/doc_classes/GDNativeLibrary.xml
index 308d8defc3..754a6d2514 100644
--- a/modules/gdnative/doc_classes/GDNativeLibrary.xml
+++ b/modules/gdnative/doc_classes/GDNativeLibrary.xml
@@ -9,12 +9,6 @@
<demos>
</demos>
<methods>
- <method name="get_config_file">
- <return type="ConfigFile">
- </return>
- <description>
- </description>
- </method>
<method name="get_current_dependencies" qualifiers="const">
<return type="PoolStringArray">
</return>
@@ -29,6 +23,8 @@
</method>
</methods>
<members>
+ <member name="config_file" type="ConfigFile" setter="set_config_file" getter="get_config_file">
+ </member>
<member name="load_once" type="bool" setter="set_load_once" getter="should_load_once">
</member>
<member name="reloadable" type="bool" setter="set_reloadable" getter="is_reloadable">
diff --git a/modules/gdnative/gdnative/string.cpp b/modules/gdnative/gdnative/string.cpp
index ab0f0e0a50..7f5dbc12be 100644
--- a/modules/gdnative/gdnative/string.cpp
+++ b/modules/gdnative/gdnative/string.cpp
@@ -1168,6 +1168,24 @@ godot_string GDAPI godot_string_c_unescape(const godot_string *p_self) {
return result;
}
+godot_string GDAPI godot_string_http_escape(const godot_string *p_self) {
+ const String *self = (const String *)p_self;
+ godot_string result;
+ String return_value = self->http_escape();
+ memnew_placement(&result, String(return_value));
+
+ return result;
+}
+
+godot_string GDAPI godot_string_http_unescape(const godot_string *p_self) {
+ const String *self = (const String *)p_self;
+ godot_string result;
+ String return_value = self->http_unescape();
+ memnew_placement(&result, String(return_value));
+
+ return result;
+}
+
godot_string GDAPI godot_string_json_escape(const godot_string *p_self) {
const String *self = (const String *)p_self;
godot_string result;
diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json
index 9da2a69360..f41c3859bd 100644
--- a/modules/gdnative/gdnative_api.json
+++ b/modules/gdnative/gdnative_api.json
@@ -5468,6 +5468,20 @@
]
},
{
+ "name": "godot_string_http_escape",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_http_unescape",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
"name": "godot_string_json_escape",
"return_type": "godot_string",
"arguments": [
diff --git a/modules/gdnative/include/gdnative/string.h b/modules/gdnative/include/gdnative/string.h
index 8fc59e21da..73245160c1 100644
--- a/modules/gdnative/include/gdnative/string.h
+++ b/modules/gdnative/include/gdnative/string.h
@@ -228,14 +228,17 @@ godot_string GDAPI godot_string_simplify_path(const godot_string *p_self);
godot_string GDAPI godot_string_c_escape(const godot_string *p_self);
godot_string GDAPI godot_string_c_escape_multiline(const godot_string *p_self);
godot_string GDAPI godot_string_c_unescape(const godot_string *p_self);
-godot_string GDAPI godot_string_percent_decode(const godot_string *p_self);
-godot_string GDAPI godot_string_percent_encode(const godot_string *p_self);
+godot_string GDAPI godot_string_http_escape(const godot_string *p_self);
+godot_string GDAPI godot_string_http_unescape(const godot_string *p_self);
godot_string GDAPI godot_string_json_escape(const godot_string *p_self);
godot_string GDAPI godot_string_word_wrap(const godot_string *p_self, godot_int p_chars_per_line);
godot_string GDAPI godot_string_xml_escape(const godot_string *p_self);
godot_string GDAPI godot_string_xml_escape_with_quotes(const godot_string *p_self);
godot_string GDAPI godot_string_xml_unescape(const godot_string *p_self);
+godot_string GDAPI godot_string_percent_decode(const godot_string *p_self);
+godot_string GDAPI godot_string_percent_encode(const godot_string *p_self);
+
godot_bool GDAPI godot_string_is_valid_float(const godot_string *p_self);
godot_bool GDAPI godot_string_is_valid_hex_number(const godot_string *p_self, godot_bool p_with_prefix);
godot_bool GDAPI godot_string_is_valid_html_color(const godot_string *p_self);
diff --git a/modules/mono/glue/cs_files/Basis.cs b/modules/mono/glue/cs_files/Basis.cs
index 2e7e5404c4..aa07cf3999 100644
--- a/modules/mono/glue/cs_files/Basis.cs
+++ b/modules/mono/glue/cs_files/Basis.cs
@@ -206,13 +206,13 @@ namespace Godot
}
else
{
- euler.x = Mathf.PI * 0.5f;
+ euler.x = Mathf.Pi * 0.5f;
euler.y = -Mathf.Atan2(-m[0, 1], m[0, 0]);
}
}
else
{
- euler.x = -Mathf.PI * 0.5f;
+ euler.x = -Mathf.Pi * 0.5f;
euler.y = -Mathf.Atan2(-m[0, 1], m[0, 0]);
}
diff --git a/modules/mono/glue/cs_files/Mathf.cs b/modules/mono/glue/cs_files/Mathf.cs
index adbcc855ef..bbee106018 100644
--- a/modules/mono/glue/cs_files/Mathf.cs
+++ b/modules/mono/glue/cs_files/Mathf.cs
@@ -8,16 +8,14 @@ using real_t = System.Single;
namespace Godot
{
- public static class Mathf
+ public static partial class Mathf
{
- // Define constants with Decimal precision and cast down to double or float.
- public const real_t PI = (real_t) 3.1415926535897932384626433833M; // 3.1415927f and 3.14159265358979
+ // Define constants with Decimal precision and cast down to double or float.
- #if REAL_T_IS_DOUBLE
- public const real_t Epsilon = 1e-14; // Epsilon size should depend on the precision used.
- #else
- public const real_t Epsilon = 1e-06f;
- #endif
+ public const real_t Tau = (real_t) 6.2831853071795864769252867666M; // 6.2831855f and 6.28318530717959
+ public const real_t Pi = (real_t) 3.1415926535897932384626433833M; // 3.1415927f and 3.14159265358979
+ public const real_t Inf = real_t.PositiveInfinity;
+ public const real_t NaN = real_t.NaN;
private const real_t Deg2RadConst = (real_t) 0.0174532925199432957692369077M; // 0.0174532924f and 0.0174532925199433
private const real_t Rad2DegConst = (real_t) 57.295779513082320876798154814M; // 57.29578f and 57.2957795130823
@@ -27,6 +25,11 @@ namespace Godot
return Math.Abs(s);
}
+ public static int Abs(int s)
+ {
+ return Math.Abs(s);
+ }
+
public static real_t Acos(real_t s)
{
return (real_t)Math.Acos(s);
@@ -57,18 +60,14 @@ namespace Godot
return (real_t)Math.Ceiling(s);
}
- public static real_t Clamp(real_t val, real_t min, real_t max)
+ public static int Clamp(int value, int min, int max)
{
- if (val < min)
- {
- return min;
- }
- else if (val > max)
- {
- return max;
- }
+ return value < min ? min : value > max ? max : value;
+ }
- return val;
+ public static real_t Clamp(real_t value, real_t min, real_t max)
+ {
+ return value < min ? min : value > max ? max : value;
}
public static real_t Cos(real_t s)
@@ -151,6 +150,21 @@ namespace Godot
}
}
+ public static real_t InverseLerp(real_t from, real_t to, real_t weight)
+ {
+ return (Clamp(weight, 0f, 1f) - from) / (to - from);
+ }
+
+ public static bool IsInf(real_t s)
+ {
+ return real_t.IsInfinity(s);
+ }
+
+ public static bool IsNaN(real_t s)
+ {
+ return real_t.IsNaN(s);
+ }
+
public static real_t Lerp(real_t from, real_t to, real_t weight)
{
return from + (to - from) * Clamp(weight, 0f, 1f);
@@ -181,16 +195,16 @@ namespace Godot
return (a < b) ? a : b;
}
- public static int NearestPo2(int val)
+ public static int NearestPo2(int value)
{
- val--;
- val |= val >> 1;
- val |= val >> 2;
- val |= val >> 4;
- val |= val >> 8;
- val |= val >> 16;
- val++;
- return val;
+ value--;
+ value |= value >> 1;
+ value |= value >> 2;
+ value |= value >> 4;
+ value |= value >> 8;
+ value |= value >> 16;
+ value++;
+ return value;
}
public static Vector2 Polar2Cartesian(real_t r, real_t th)
@@ -218,6 +232,11 @@ namespace Godot
return (int)Math.Round(s);
}
+ public static int Sign(int s)
+ {
+ return (s < 0) ? -1 : 1;
+ }
+
public static real_t Sign(real_t s)
{
return (s < 0f) ? -1f : 1f;
@@ -258,16 +277,16 @@ namespace Godot
return (real_t)Math.Tanh(s);
}
- public static int Wrap(int val, int min, int max)
+ public static int Wrap(int value, int min, int max)
{
int rng = max - min;
- return min + ((((val - min) % rng) + rng) % rng);
+ return min + ((((value - min) % rng) + rng) % rng);
}
- public static real_t Wrap(real_t val, real_t min, real_t max)
+ public static real_t Wrap(real_t value, real_t min, real_t max)
{
real_t rng = max - min;
- return min + (val - min) - (rng * Floor((val - min) / rng));
+ return min + ((((value - min) % rng) + rng) % rng);
}
}
}
diff --git a/modules/mono/glue/cs_files/MathfEx.cs b/modules/mono/glue/cs_files/MathfEx.cs
new file mode 100644
index 0000000000..739b7fb568
--- /dev/null
+++ b/modules/mono/glue/cs_files/MathfEx.cs
@@ -0,0 +1,39 @@
+using System;
+
+#if REAL_T_IS_DOUBLE
+using real_t = System.Double;
+#else
+using real_t = System.Single;
+#endif
+
+namespace Godot
+{
+ public static partial class Mathf
+ {
+ // Define constants with Decimal precision and cast down to double or float.
+
+ public const real_t E = (real_t) 2.7182818284590452353602874714M; // 2.7182817f and 2.718281828459045
+ public const real_t Sqrt2 = (real_t) 1.4142135623730950488016887242M; // 1.4142136f and 1.414213562373095
+
+#if REAL_T_IS_DOUBLE
+ public const real_t Epsilon = 1e-14; // Epsilon size should depend on the precision used.
+#else
+ public const real_t Epsilon = 1e-06f;
+#endif
+
+ public static int CeilToInt(real_t s)
+ {
+ return (int)Math.Ceiling(s);
+ }
+
+ public static int FloorToInt(real_t s)
+ {
+ return (int)Math.Floor(s);
+ }
+
+ public static int RoundToInt(real_t s)
+ {
+ return (int)Math.Round(s);
+ }
+ }
+} \ No newline at end of file
diff --git a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml
index 0929b227d0..399ba8ef5d 100644
--- a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml
+++ b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml
@@ -80,7 +80,7 @@
Return the natural logarithm of the input. Note that this is not the typical base-10 logarithm function calculators use.
</constant>
<constant name="MATH_EXP" value="20" enum="BuiltinFunc">
- Return [b]e[/b] raised to the power of the input. [b]e[/b] sometimes called "Euler's number" is a mathematical constant whose value is approximately 2.71828.
+ Return the mathematical constant [b]e[/b] raised to the specified power of the input. [b]e[/b] has an approximate value of 2.71828.
</constant>
<constant name="MATH_ISNAN" value="21" enum="BuiltinFunc">
Return whether the input is NaN (Not a Number) or not. NaN is usually produced by dividing 0 by 0, though other ways exist.
diff --git a/modules/visual_script/doc_classes/VisualScriptMathConstant.xml b/modules/visual_script/doc_classes/VisualScriptMathConstant.xml
index df439f8794..d456e880b7 100644
--- a/modules/visual_script/doc_classes/VisualScriptMathConstant.xml
+++ b/modules/visual_script/doc_classes/VisualScriptMathConstant.xml
@@ -4,7 +4,7 @@
Commonly used mathematical constants.
</brief_description>
<description>
- Provides common math constants, such as Pi or Euler's constant, on an output Data port.
+ Provides common math constants, such as Pi, on an output Data port.
[b]Input Ports:[/b]
none
[b]Output Ports:[/b]
@@ -35,7 +35,7 @@
Tau: [code]6.283185[/code]
</constant>
<constant name="MATH_CONSTANT_E" value="4" enum="MathConstant">
- Natural log: [code]2.718282[/code]
+ Mathematical constant [code]e[/code], the natural log base: [code]2.718282[/code]
</constant>
<constant name="MATH_CONSTANT_SQRT2" value="5" enum="MathConstant">
Square root of two: [code]1.414214[/code]
diff --git a/modules/websocket/emws_peer.cpp b/modules/websocket/emws_peer.cpp
index d3330d683c..e0b987b4d7 100644
--- a/modules/websocket/emws_peer.cpp
+++ b/modules/websocket/emws_peer.cpp
@@ -148,12 +148,14 @@ void EMWSPeer::close() {
IP_Address EMWSPeer::get_connected_host() const {
- return IP_Address();
+ ERR_EXPLAIN("Not supported in HTML5 export");
+ ERR_FAIL_V(IP_Address());
};
uint16_t EMWSPeer::get_connected_port() const {
- return 1025;
+ ERR_EXPLAIN("Not supported in HTML5 export");
+ ERR_FAIL_V(0);
};
EMWSPeer::EMWSPeer() {
diff --git a/modules/websocket/emws_server.cpp b/modules/websocket/emws_server.cpp
index c9ddae0c8c..3eb93e4152 100644
--- a/modules/websocket/emws_server.cpp
+++ b/modules/websocket/emws_server.cpp
@@ -58,6 +58,19 @@ PoolVector<String> EMWSServer::get_protocols() const {
return out;
}
+IP_Address EMWSServer::get_peer_address(int p_peer_id) const {
+
+ return IP_Address();
+}
+
+int EMWSServer::get_peer_port(int p_peer_id) const {
+
+ return 0;
+}
+
+void EMWSServer::disconnect_peer(int p_peer_id) {
+}
+
EMWSServer::EMWSServer() {
}
diff --git a/modules/websocket/emws_server.h b/modules/websocket/emws_server.h
index aa089ea40d..9ec4ce72c8 100644
--- a/modules/websocket/emws_server.h
+++ b/modules/websocket/emws_server.h
@@ -46,6 +46,9 @@ public:
bool is_listening() const;
bool has_peer(int p_id) const;
Ref<WebSocketPeer> get_peer(int p_id) const;
+ IP_Address get_peer_address(int p_peer_id) const;
+ int get_peer_port(int p_peer_id) const;
+ void disconnect_peer(int p_peer_id);
virtual void poll();
virtual PoolVector<String> get_protocols() const;
diff --git a/modules/websocket/lws_peer.cpp b/modules/websocket/lws_peer.cpp
index ba45d7688f..8a064fb5a4 100644
--- a/modules/websocket/lws_peer.cpp
+++ b/modules/websocket/lws_peer.cpp
@@ -32,6 +32,13 @@
#include "lws_peer.h"
#include "core/io/ip.h"
+// Needed for socket_helpers on Android at least. UNIXes has it, just include if not windows
+#if !defined(WINDOWS_ENABLED)
+#include <netinet/in.h>
+#endif
+
+#include "drivers/unix/socket_helpers.h"
+
void LWSPeer::set_wsi(struct lws *p_wsi) {
wsi = p_wsi;
};
@@ -178,12 +185,40 @@ void LWSPeer::close() {
IP_Address LWSPeer::get_connected_host() const {
- return IP_Address();
+ ERR_FAIL_COND_V(!is_connected_to_host(), IP_Address());
+
+ IP_Address ip;
+ int port = 0;
+
+ socklen_t len;
+ struct sockaddr_storage addr;
+ int fd = lws_get_socket_fd(wsi);
+
+ int ret = getpeername(fd, (struct sockaddr *)&addr, &len);
+ ERR_FAIL_COND_V(ret != 0, IP_Address());
+
+ _set_ip_addr_port(ip, port, &addr);
+
+ return ip;
};
uint16_t LWSPeer::get_connected_port() const {
- return 1025;
+ ERR_FAIL_COND_V(!is_connected_to_host(), 0);
+
+ IP_Address ip;
+ int port = 0;
+
+ socklen_t len;
+ struct sockaddr_storage addr;
+ int fd = lws_get_socket_fd(wsi);
+
+ int ret = getpeername(fd, (struct sockaddr *)&addr, &len);
+ ERR_FAIL_COND_V(ret != 0, 0);
+
+ _set_ip_addr_port(ip, port, &addr);
+
+ return port;
};
LWSPeer::LWSPeer() {
diff --git a/modules/websocket/lws_server.cpp b/modules/websocket/lws_server.cpp
index 94fe4231ae..8d13dc7a98 100644
--- a/modules/websocket/lws_server.cpp
+++ b/modules/websocket/lws_server.cpp
@@ -164,6 +164,24 @@ Ref<WebSocketPeer> LWSServer::get_peer(int p_id) const {
return _peer_map[p_id];
}
+IP_Address LWSServer::get_peer_address(int p_peer_id) const {
+ ERR_FAIL_COND_V(!has_peer(p_peer_id), IP_Address());
+
+ return _peer_map[p_peer_id]->get_connected_host();
+}
+
+int LWSServer::get_peer_port(int p_peer_id) const {
+ ERR_FAIL_COND_V(!has_peer(p_peer_id), 0);
+
+ return _peer_map[p_peer_id]->get_connected_port();
+}
+
+void LWSServer::disconnect_peer(int p_peer_id) {
+ ERR_FAIL_COND(!has_peer(p_peer_id));
+
+ get_peer(p_peer_id)->close();
+}
+
LWSServer::LWSServer() {
context = NULL;
_lws_ref = NULL;
diff --git a/modules/websocket/lws_server.h b/modules/websocket/lws_server.h
index de8f59e5ae..9e3fb9b775 100644
--- a/modules/websocket/lws_server.h
+++ b/modules/websocket/lws_server.h
@@ -52,6 +52,9 @@ public:
bool is_listening() const;
bool has_peer(int p_id) const;
Ref<WebSocketPeer> get_peer(int p_id) const;
+ IP_Address get_peer_address(int p_peer_id) const;
+ int get_peer_port(int p_peer_id) const;
+ void disconnect_peer(int p_peer_id);
virtual void poll() { _lws_poll(); }
LWSServer();
diff --git a/modules/websocket/websocket_peer.cpp b/modules/websocket/websocket_peer.cpp
index 6324047846..61f783e377 100644
--- a/modules/websocket/websocket_peer.cpp
+++ b/modules/websocket/websocket_peer.cpp
@@ -43,6 +43,8 @@ void WebSocketPeer::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_connected_to_host"), &WebSocketPeer::is_connected_to_host);
ClassDB::bind_method(D_METHOD("was_string_packet"), &WebSocketPeer::was_string_packet);
ClassDB::bind_method(D_METHOD("close"), &WebSocketPeer::close);
+ ClassDB::bind_method(D_METHOD("get_connected_host"), &WebSocketPeer::get_connected_host);
+ ClassDB::bind_method(D_METHOD("get_connected_port"), &WebSocketPeer::get_connected_port);
BIND_ENUM_CONSTANT(WRITE_MODE_TEXT);
BIND_ENUM_CONSTANT(WRITE_MODE_BINARY);
diff --git a/modules/websocket/websocket_server.cpp b/modules/websocket/websocket_server.cpp
index 5746f61e10..2693b26e47 100644
--- a/modules/websocket/websocket_server.cpp
+++ b/modules/websocket/websocket_server.cpp
@@ -44,6 +44,9 @@ void WebSocketServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("listen", "port", "protocols", "gd_mp_api"), &WebSocketServer::listen, DEFVAL(PoolVector<String>()), DEFVAL(false));
ClassDB::bind_method(D_METHOD("stop"), &WebSocketServer::stop);
ClassDB::bind_method(D_METHOD("has_peer", "id"), &WebSocketServer::has_peer);
+ ClassDB::bind_method(D_METHOD("get_peer_address"), &WebSocketServer::get_peer_address);
+ ClassDB::bind_method(D_METHOD("get_peer_port"), &WebSocketServer::get_peer_port);
+ ClassDB::bind_method(D_METHOD("disconnect_peer"), &WebSocketServer::disconnect_peer);
ADD_SIGNAL(MethodInfo("client_disconnected", PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("client_connected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "protocol")));
diff --git a/modules/websocket/websocket_server.h b/modules/websocket/websocket_server.h
index 360ff9e6d4..64935f8a58 100644
--- a/modules/websocket/websocket_server.h
+++ b/modules/websocket/websocket_server.h
@@ -52,6 +52,10 @@ public:
virtual bool is_server() const;
ConnectionStatus get_connection_status() const;
+ virtual IP_Address get_peer_address(int p_peer_id) const = 0;
+ virtual int get_peer_port(int p_peer_id) const = 0;
+ virtual void disconnect_peer(int p_peer_id) = 0;
+
void _on_peer_packet(int32_t p_peer_id);
void _on_connect(int32_t p_peer_id, String p_protocol);
void _on_disconnect(int32_t p_peer_id);
diff --git a/platform/android/java/src/org/godotengine/godot/GodotView.java b/platform/android/java/src/org/godotengine/godot/GodotView.java
index 0222758c2b..23723c2696 100644
--- a/platform/android/java/src/org/godotengine/godot/GodotView.java
+++ b/platform/android/java/src/org/godotengine/godot/GodotView.java
@@ -261,7 +261,7 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener {
};
int source = event.getSource();
- if ((source & InputDevice.SOURCE_JOYSTICK) != 0 || (source & InputDevice.SOURCE_DPAD) != 0 || (source & InputDevice.SOURCE_GAMEPAD) != 0) {
+ if ((source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK || (source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD || (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
final int button = get_godot_button(keyCode);
final int device = find_joy_device(event.getDeviceId());
@@ -302,7 +302,7 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener {
int source = event.getSource();
//Log.e(TAG, String.format("Key down! source %d, device %d, joystick %d, %d, %d", event.getDeviceId(), source, (source & InputDevice.SOURCE_JOYSTICK), (source & InputDevice.SOURCE_DPAD), (source & InputDevice.SOURCE_GAMEPAD)));
- if ((source & InputDevice.SOURCE_JOYSTICK) != 0 || (source & InputDevice.SOURCE_DPAD) != 0 || (source & InputDevice.SOURCE_GAMEPAD) != 0) {
+ if ((source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK || (source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD || (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
if (event.getRepeatCount() > 0) // ignore key echo
return true;
diff --git a/platform/iphone/app_delegate.mm b/platform/iphone/app_delegate.mm
index 5c3799ab09..9e6bbff1d7 100644
--- a/platform/iphone/app_delegate.mm
+++ b/platform/iphone/app_delegate.mm
@@ -79,6 +79,7 @@ static ViewController *mainViewController = nil;
}
NSMutableDictionary *ios_joysticks = nil;
+NSMutableArray *pending_ios_joysticks = nil;
- (GCControllerPlayerIndex)getFreePlayerIndex {
bool have_player_1 = false;
@@ -115,6 +116,30 @@ NSMutableDictionary *ios_joysticks = nil;
};
};
+void _ios_add_joystick(GCController *controller, AppDelegate *delegate) {
+ // get a new id for our controller
+ int joy_id = OSIPhone::get_singleton()->get_unused_joy_id();
+ if (joy_id != -1) {
+ // assign our player index
+ if (controller.playerIndex == GCControllerPlayerIndexUnset) {
+ controller.playerIndex = [delegate getFreePlayerIndex];
+ };
+
+ // tell Godot about our new controller
+ OSIPhone::get_singleton()->joy_connection_changed(
+ joy_id, true, [controller.vendorName UTF8String]);
+
+ // add it to our dictionary, this will retain our controllers
+ [ios_joysticks setObject:controller
+ forKey:[NSNumber numberWithInt:joy_id]];
+
+ // set our input handler
+ [delegate setControllerInputHandler:controller];
+ } else {
+ printf("Couldn't retrieve new joy id\n");
+ };
+}
+
- (void)controllerWasConnected:(NSNotification *)notification {
// create our dictionary if we don't have one yet
if (ios_joysticks == nil) {
@@ -127,28 +152,12 @@ NSMutableDictionary *ios_joysticks = nil;
printf("Couldn't retrieve new controller\n");
} else if ([[ios_joysticks allKeysForObject:controller] count] != 0) {
printf("Controller is already registered\n");
+ } else if (frame_count > 1) {
+ _ios_add_joystick(controller, self);
} else {
- // get a new id for our controller
- int joy_id = OSIPhone::get_singleton()->get_unused_joy_id();
- if (joy_id != -1) {
- // assign our player index
- if (controller.playerIndex == GCControllerPlayerIndexUnset) {
- controller.playerIndex = [self getFreePlayerIndex];
- };
-
- // tell Godot about our new controller
- OSIPhone::get_singleton()->joy_connection_changed(
- joy_id, true, [controller.vendorName UTF8String]);
-
- // add it to our dictionary, this will retain our controllers
- [ios_joysticks setObject:controller
- forKey:[NSNumber numberWithInt:joy_id]];
-
- // set our input handler
- [self setControllerInputHandler:controller];
- } else {
- printf("Couldn't retrieve new joy id\n");
- };
+ if (pending_ios_joysticks == nil)
+ pending_ios_joysticks = [[NSMutableArray alloc] init];
+ [pending_ios_joysticks addObject:controller];
};
};
@@ -352,6 +361,27 @@ NSMutableDictionary *ios_joysticks = nil;
[ios_joysticks dealloc];
ios_joysticks = nil;
};
+
+ if (pending_ios_joysticks != nil) {
+ [pending_ios_joysticks dealloc];
+ pending_ios_joysticks = nil;
+ };
+};
+
+OS::VideoMode _get_video_mode() {
+ int backingWidth;
+ int backingHeight;
+ glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
+ GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
+ glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
+ GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
+
+ OS::VideoMode vm;
+ vm.fullscreen = true;
+ vm.width = backingWidth;
+ vm.height = backingHeight;
+ vm.resizable = false;
+ return vm;
};
static int frame_count = 0;
@@ -360,19 +390,7 @@ static int frame_count = 0;
switch (frame_count) {
case 0: {
- int backingWidth;
- int backingHeight;
- glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
- GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
- glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
- GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
-
- OS::VideoMode vm;
- vm.fullscreen = true;
- vm.width = backingWidth;
- vm.height = backingHeight;
- vm.resizable = false;
- OS::get_singleton()->set_video_mode(vm);
+ OS::get_singleton()->set_video_mode(_get_video_mode());
if (!OS::get_singleton()) {
exit(0);
@@ -410,6 +428,14 @@ static int frame_count = 0;
Main::setup2();
++frame_count;
+ if (pending_ios_joysticks != nil) {
+ for (GCController *controller in pending_ios_joysticks) {
+ _ios_add_joystick(controller, self);
+ }
+ [pending_ios_joysticks dealloc];
+ pending_ios_joysticks = nil;
+ }
+
// this might be necessary before here
NSDictionary *dict = [[NSBundle mainBundle] infoDictionary];
for (NSString *key in dict) {
@@ -562,18 +588,13 @@ static int frame_count = 0;
//[glView setAutoresizingMask:UIViewAutoresizingFlexibleWidth |
// UIViewAutoresizingFlexibleWidth];
- int backingWidth;
- int backingHeight;
- glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
- GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
- glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
- GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
+ OS::VideoMode vm = _get_video_mode();
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
- int err = iphone_main(backingWidth, backingHeight, gargc, gargv, String::utf8([documentsDirectory UTF8String]));
+ int err = iphone_main(vm.width, vm.height, gargc, gargv, String::utf8([documentsDirectory UTF8String]));
if (err != 0) {
// bail, things did not go very well for us, should probably output a message on screen with our error code...
exit(0);
diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp
index e3119814f4..4c1e02baf7 100644
--- a/platform/iphone/export/export.cpp
+++ b/platform/iphone/export/export.cpp
@@ -147,6 +147,26 @@ Vector<EditorExportPlatformIOS::ExportArchitecture> EditorExportPlatformIOS::_ge
return archs;
}
+struct LoadingScreenInfo {
+ const char *preset_key;
+ const char *export_name;
+};
+
+static const LoadingScreenInfo loading_screen_infos[] = {
+ { "landscape_launch_screens/iphone_2436x1125", "Default-Landscape-X.png" },
+ { "landscape_launch_screens/iphone_2208x1242", "Default-Landscape-736h@3x.png" },
+ { "landscape_launch_screens/ipad_1024x768", "Default-Landscape.png" },
+ { "landscape_launch_screens/ipad_2048x1536", "Default-Landscape@2x.png" },
+
+ { "portrait_launch_screens/iphone_640x960", "Default-480h@2x.png" },
+ { "portrait_launch_screens/iphone_640x1136", "Default-568h@2x.png" },
+ { "portrait_launch_screens/iphone_750x1334", "Default-667h@2x.png" },
+ { "portrait_launch_screens/iphone_1125x2436", "Default-Portrait-X.png" },
+ { "portrait_launch_screens/ipad_768x1024", "Default-Portrait.png" },
+ { "portrait_launch_screens/ipad_1536x2048", "Default-Portrait@2x.png" },
+ { "portrait_launch_screens/iphone_1242x2208", "Default-Portrait-736h@3x.png" }
+};
+
void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) {
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_package/debug", PROPERTY_HINT_GLOBAL_FILE, "zip"), ""));
@@ -172,6 +192,7 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/iphone_120x120", PROPERTY_HINT_FILE, "png"), "")); // Home screen on iPhone/iPod Touch with retina display
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/ipad_76x76", PROPERTY_HINT_FILE, "png"), "")); // Home screen on iPad
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/app_store_1024x1024", PROPERTY_HINT_FILE, "png"), "")); // App Store
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/iphone_180x180", PROPERTY_HINT_FILE, "png"), "")); // Home screen on iPhone with retina HD display
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/ipad_152x152", PROPERTY_HINT_FILE, "png"), "")); // Home screen on iPad with retina display
@@ -179,15 +200,9 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_40x40", PROPERTY_HINT_FILE, "png"), "")); // Spotlight
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_80x80", PROPERTY_HINT_FILE, "png"), "")); // Spotlight on devices with retina display
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "landscape_launch_screens/iphone_2208x1242", PROPERTY_HINT_FILE, "png"), "")); // iPhone 6 Plus, 6s Plus, 7 Plus
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "landscape_launch_screens/ipad_2732x2048", PROPERTY_HINT_FILE, "png"), "")); // 12.9-inch iPad Pro
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "landscape_launch_screens/ipad_2048x1536", PROPERTY_HINT_FILE, "png"), "")); // Other iPads
-
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "portrait_launch_screens/iphone_640x1136", PROPERTY_HINT_FILE, "png"), "")); // iPhone 5, 5s, SE
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "portrait_launch_screens/iphone_750x1334", PROPERTY_HINT_FILE, "png"), "")); // iPhone 6, 6s, 7
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "portrait_launch_screens/iphone_1242x2208", PROPERTY_HINT_FILE, "png"), "")); // iPhone 6 Plus, 6s Plus, 7 Plus
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "portrait_launch_screens/ipad_2048x2732", PROPERTY_HINT_FILE, "png"), "")); // 12.9-inch iPad Pro
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "portrait_launch_screens/ipad_1536x2048", PROPERTY_HINT_FILE, "png"), "")); // Other iPads
+ for (int i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) {
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, loading_screen_infos[i].preset_key, PROPERTY_HINT_FILE, "png"), ""));
+ }
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), false));
@@ -313,6 +328,7 @@ static const IconInfo icon_infos[] = {
{ "required_icons/iphone_120x120", "iphone", "Icon-120.png", "120", "3x", "40x40", true },
{ "required_icons/ipad_76x76", "ipad", "Icon-76.png", "76", "1x", "76x76", false },
+ { "required_icons/app_store_1024x1024", "ios-marketing", "Icon-1024.png", "1024", "1x", "1024x1024", false },
{ "optional_icons/iphone_180x180", "iphone", "Icon-180.png", "180", "3x", "60x60", false },
@@ -380,23 +396,6 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr
return OK;
}
-struct LoadingScreenInfo {
- const char *preset_key;
- const char *export_name;
-};
-
-static const LoadingScreenInfo loading_screen_infos[] = {
- { "landscape_launch_screens/iphone_2208x1242", "Default-Landscape-736h@3x.png" },
- { "landscape_launch_screens/ipad_2732x2048", "Default-Landscape-1366h@2x.png" },
- { "landscape_launch_screens/ipad_2048x1536", "Default-Landscape@2x.png" },
-
- { "portrait_launch_screens/iphone_640x1136", "Default-568h@2x.png" },
- { "portrait_launch_screens/iphone_750x1334", "Default-667h@2x.png" },
- { "portrait_launch_screens/iphone_1242x2208", "Default-Portrait-736h@3x.png" },
- { "portrait_launch_screens/ipad_2048x2732", "Default-Portrait-1366h@2x.png" },
- { "portrait_launch_screens/ipad_1536x2048", "Default-Portrait@2x.png" }
-};
-
Error EditorExportPlatformIOS::_export_loading_screens(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir) {
DirAccess *da = DirAccess::open(p_dest_dir);
ERR_FAIL_COND_V(!da, ERR_CANT_OPEN);
@@ -404,12 +403,14 @@ Error EditorExportPlatformIOS::_export_loading_screens(const Ref<EditorExportPre
for (int i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) {
LoadingScreenInfo info = loading_screen_infos[i];
String loading_screen_file = p_preset->get(info.preset_key);
- Error err = da->copy(loading_screen_file, p_dest_dir + info.export_name);
- if (err) {
- memdelete(da);
- String err_str = String("Failed to export loading screen (") + info.preset_key + ") from path: " + loading_screen_file;
- ERR_PRINT(err_str.utf8().get_data());
- return err;
+ if (loading_screen_file.size() > 0) {
+ Error err = da->copy(loading_screen_file, p_dest_dir + info.export_name);
+ if (err) {
+ memdelete(da);
+ String err_str = String("Failed to export loading screen (") + info.preset_key + ") from path: " + loading_screen_file;
+ ERR_PRINT(err_str.utf8().get_data());
+ return err;
+ }
}
}
memdelete(da);
@@ -887,7 +888,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
if (err)
return err;
- err = _export_loading_screens(p_preset, dest_dir + binary_name + "/");
+ err = _export_loading_screens(p_preset, dest_dir + binary_name + "/Images.xcassets/LaunchImage.launchimage/");
if (err)
return err;
diff --git a/platform/iphone/gl_view.h b/platform/iphone/gl_view.h
index 85376ebc08..0d101eb696 100644
--- a/platform/iphone/gl_view.h
+++ b/platform/iphone/gl_view.h
@@ -83,6 +83,8 @@
@property(strong, nonatomic) MPMoviePlayerController *moviePlayerController;
@property(strong, nonatomic) UIWindow *backgroundWindow;
+@property(nonatomic) UITextAutocorrectionType autocorrectionType;
+
- (void)startAnimation;
- (void)stopAnimation;
- (void)drawView;
diff --git a/platform/iphone/gl_view.mm b/platform/iphone/gl_view.mm
index 69116c64c6..ff95cf6e5f 100644
--- a/platform/iphone/gl_view.mm
+++ b/platform/iphone/gl_view.mm
@@ -78,6 +78,16 @@ void _hide_keyboard() {
keyboard_text = "";
};
+Rect2 _get_ios_window_safe_area(float p_window_width, float p_window_height) {
+ UIEdgeInsets insets = UIEdgeInsetsMake(0, 0, 0, 0);
+ if (_instance != nil && [_instance respondsToSelector:@selector(safeAreaInsets)]) {
+ insets = [_instance safeAreaInsets];
+ }
+ ERR_FAIL_COND_V(insets.left < 0 || insets.top < 0 || insets.right < 0 || insets.bottom < 0,
+ Rect2(0, 0, p_window_width, p_window_height));
+ return Rect2(insets.left, insets.top, p_window_width - insets.right - insets.left, p_window_height - insets.bottom - insets.top);
+}
+
bool _play_video(String p_path, float p_volume, String p_audio_track, String p_subtitle_track) {
p_path = ProjectSettings::get_singleton()->globalize_path(p_path);
@@ -326,9 +336,7 @@ static void clear_touches() {
// Generate IDs for a framebuffer object and a color renderbuffer
UIScreen *mainscr = [UIScreen mainScreen];
printf("******** screen size %i, %i\n", (int)mainscr.currentMode.size.width, (int)mainscr.currentMode.size.height);
- float minPointSize = MIN(mainscr.bounds.size.width, mainscr.bounds.size.height);
- float minScreenSize = MIN(mainscr.currentMode.size.width, mainscr.currentMode.size.height);
- self.contentScaleFactor = minScreenSize / minPointSize;
+ self.contentScaleFactor = mainscr.nativeScale;
glGenFramebuffersOES(1, &viewFramebuffer);
glGenRenderbuffersOES(1, &viewRenderbuffer);
@@ -627,6 +635,7 @@ static void clear_touches() {
}
init_touches();
self.multipleTouchEnabled = YES;
+ self.autocorrectionType = UITextAutocorrectionTypeNo;
printf("******** adding observer for sound routing changes\n");
[[NSNotificationCenter defaultCenter]
diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp
index c4c59431f2..97ebc19335 100644
--- a/platform/iphone/os_iphone.cpp
+++ b/platform/iphone/os_iphone.cpp
@@ -504,6 +504,12 @@ Size2 OSIPhone::get_window_size() const {
return Vector2(video_mode.width, video_mode.height);
}
+extern Rect2 _get_ios_window_safe_area(float p_window_width, float p_window_height);
+
+Rect2 OSIPhone::get_window_safe_area() const {
+ return _get_ios_window_safe_area(video_mode.width, video_mode.height);
+}
+
bool OSIPhone::has_touchscreen_ui_hint() const {
return true;
diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h
index 2e4458aeed..00d9efb01a 100644
--- a/platform/iphone/os_iphone.h
+++ b/platform/iphone/os_iphone.h
@@ -177,6 +177,7 @@ public:
virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot);
virtual Size2 get_window_size() const;
+ virtual Rect2 get_window_safe_area() const;
virtual bool has_touchscreen_ui_hint() const;
diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm
index 0c5524dd08..fbefd41bb7 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -1474,6 +1474,11 @@ void OS_OSX::set_cursor_shape(CursorShape p_shape) {
if (cursor_shape == p_shape)
return;
+ if (mouse_mode != MOUSE_MODE_VISIBLE) {
+ cursor_shape = p_shape;
+ return;
+ }
+
if (cursors[p_shape] != NULL) {
[cursors[p_shape] set];
} else {
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index f51d969c16..9c37b65d77 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -1299,7 +1299,9 @@ void OS_Windows::set_mouse_mode(MouseMode p_mode) {
if (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_HIDDEN) {
hCursor = SetCursor(NULL);
} else {
- SetCursor(hCursor);
+ CursorShape c = cursor_shape;
+ cursor_shape = CURSOR_MAX;
+ set_cursor_shape(c);
}
}
@@ -1863,6 +1865,11 @@ void OS_Windows::set_cursor_shape(CursorShape p_shape) {
if (cursor_shape == p_shape)
return;
+ if (mouse_mode != MOUSE_MODE_VISIBLE) {
+ cursor_shape = p_shape;
+ return;
+ }
+
static const LPCTSTR win_cursors[CURSOR_MAX] = {
IDC_ARROW,
IDC_IBEAM,
@@ -1888,6 +1895,7 @@ void OS_Windows::set_cursor_shape(CursorShape p_shape) {
} else {
SetCursor(LoadCursor(hInstance, win_cursors[p_shape]));
}
+
cursor_shape = p_shape;
}
diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp
index 338d13410f..1928800d8c 100644
--- a/platform/x11/os_x11.cpp
+++ b/platform/x11/os_x11.cpp
@@ -634,7 +634,7 @@ void OS_X11::set_mouse_mode(MouseMode p_mode) {
bool showCursor = (p_mode == MOUSE_MODE_VISIBLE || p_mode == MOUSE_MODE_CONFINED);
if (showCursor) {
- XUndefineCursor(x11_display, x11_window); // show cursor
+ XDefineCursor(x11_display, x11_window, cursors[current_cursor]); // show cursor
} else {
XDefineCursor(x11_display, x11_window, null_cursor); // hide cursor
}
diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp
index 824f50495b..447bd9a090 100644
--- a/scene/2d/animated_sprite.cpp
+++ b/scene/2d/animated_sprite.cpp
@@ -80,6 +80,10 @@ Rect2 AnimatedSprite::_edit_get_rect() const {
return Rect2(ofs, s);
}
+bool AnimatedSprite::_edit_use_rect() const {
+ return true;
+}
+
void SpriteFrames::add_frame(const StringName &p_anim, const Ref<Texture> &p_frame, int p_at_pos) {
Map<StringName, Anim>::Element *E = animations.find(p_anim);
diff --git a/scene/2d/animated_sprite.h b/scene/2d/animated_sprite.h
index a38adf792c..c7606d88aa 100644
--- a/scene/2d/animated_sprite.h
+++ b/scene/2d/animated_sprite.h
@@ -157,6 +157,7 @@ public:
virtual Point2 _edit_get_pivot() const;
virtual bool _edit_use_pivot() const;
virtual Rect2 _edit_get_rect() const;
+ virtual bool _edit_use_rect() const;
void set_sprite_frames(const Ref<SpriteFrames> &p_frames);
Ref<SpriteFrames> get_sprite_frames() const;
diff --git a/scene/2d/back_buffer_copy.cpp b/scene/2d/back_buffer_copy.cpp
index 446b367be0..caa1adebdb 100644
--- a/scene/2d/back_buffer_copy.cpp
+++ b/scene/2d/back_buffer_copy.cpp
@@ -55,6 +55,10 @@ Rect2 BackBufferCopy::_edit_get_rect() const {
return rect;
}
+bool BackBufferCopy::_edit_use_rect() const {
+ return true;
+}
+
void BackBufferCopy::set_rect(const Rect2 &p_rect) {
rect = p_rect;
diff --git a/scene/2d/back_buffer_copy.h b/scene/2d/back_buffer_copy.h
index b786c2b828..752d56de2b 100644
--- a/scene/2d/back_buffer_copy.h
+++ b/scene/2d/back_buffer_copy.h
@@ -54,6 +54,7 @@ protected:
public:
Rect2 _edit_get_rect() const;
+ virtual bool _edit_use_rect() const;
void set_rect(const Rect2 &p_rect);
Rect2 get_rect() const;
diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp
index 2b89062181..60368816a9 100644
--- a/scene/2d/canvas_item.cpp
+++ b/scene/2d/canvas_item.cpp
@@ -245,6 +245,14 @@ CanvasItemMaterial::~CanvasItemMaterial() {
///////////////////////////////////////////////////////////////////
+bool CanvasItem::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
+ if (_edit_use_rect()) {
+ return _edit_get_rect().has_point(p_point);
+ } else {
+ return p_point.length() < p_tolerance;
+ }
+}
+
bool CanvasItem::is_visible_in_tree() const {
if (!is_inside_tree())
@@ -980,7 +988,8 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("_edit_set_position", "position"), &CanvasItem::_edit_set_position);
ClassDB::bind_method(D_METHOD("_edit_get_position"), &CanvasItem::_edit_get_position);
- ClassDB::bind_method(D_METHOD("_edit_use_position"), &CanvasItem::_edit_use_position);
+ ClassDB::bind_method(D_METHOD("_edit_set_scale", "scale"), &CanvasItem::_edit_set_scale);
+ ClassDB::bind_method(D_METHOD("_edit_get_scale"), &CanvasItem::_edit_get_scale);
ClassDB::bind_method(D_METHOD("_edit_set_rect", "rect"), &CanvasItem::_edit_set_rect);
ClassDB::bind_method(D_METHOD("_edit_get_rect"), &CanvasItem::_edit_get_rect);
ClassDB::bind_method(D_METHOD("_edit_use_rect"), &CanvasItem::_edit_use_rect);
diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h
index 980fcb4109..85de0d2796 100644
--- a/scene/2d/canvas_item.h
+++ b/scene/2d/canvas_item.h
@@ -220,30 +220,46 @@ public:
/* EDITOR */
+ // Save and restore a CanvasItem state
virtual void _edit_set_state(const Dictionary &p_state){};
virtual Dictionary _edit_get_state() const { return Dictionary(); };
- // Used to move/select the node
- virtual void _edit_set_position(const Point2 &p_position){};
- virtual Point2 _edit_get_position() const { return Point2(); };
- virtual bool _edit_use_position() const { return false; };
+ // Used to move the node
+ virtual void _edit_set_position(const Point2 &p_position) = 0;
+ virtual Point2 _edit_get_position() const = 0;
- // Used to resize/move/select the node
+ // Used to scale the node
+ virtual void _edit_set_scale(const Size2 &p_scale) = 0;
+ virtual Size2 _edit_get_scale() const = 0;
+
+ // Used to resize/move the node
virtual void _edit_set_rect(const Rect2 &p_rect){};
- virtual Rect2 _edit_get_rect() const { return Rect2(-32, -32, 64, 64); };
- virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { return _edit_get_rect().has_point(p_point); }
- Rect2 _edit_get_item_and_children_rect() const;
+ virtual Rect2 _edit_get_rect() const { return Rect2(0, 0, 0, 0); };
virtual bool _edit_use_rect() const { return false; };
+ Rect2 _edit_get_item_and_children_rect() const;
+
+ // used to select the node
+ virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
+
// Used to rotate the node
- virtual void _edit_set_rotation(float p_rotation){};
- virtual float _edit_get_rotation() const { return 0.0; };
- virtual bool _edit_use_rotation() const { return false; };
+ virtual void
+ _edit_set_rotation(float p_rotation){};
+ virtual float _edit_get_rotation() const {
+ return 0.0;
+ };
+ virtual bool _edit_use_rotation() const {
+ return false;
+ };
// Used to set a pivot
virtual void _edit_set_pivot(const Point2 &p_pivot){};
- virtual Point2 _edit_get_pivot() const { return Point2(); };
- virtual bool _edit_use_pivot() const { return false; };
+ virtual Point2 _edit_get_pivot() const {
+ return Point2();
+ };
+ virtual bool _edit_use_pivot() const {
+ return false;
+ };
virtual Size2 _edit_get_minimum_size() const;
@@ -308,7 +324,9 @@ public:
virtual Transform2D get_global_transform_with_canvas() const;
CanvasItem *get_toplevel() const;
- _FORCE_INLINE_ RID get_canvas_item() const { return canvas_item; }
+ _FORCE_INLINE_ RID get_canvas_item() const {
+ return canvas_item;
+ }
void set_block_transform_notify(bool p_enable);
bool is_block_transform_notify_enabled() const;
diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp
index 7e2026d225..9d2a83fda7 100644
--- a/scene/2d/collision_polygon_2d.cpp
+++ b/scene/2d/collision_polygon_2d.cpp
@@ -264,6 +264,10 @@ Rect2 CollisionPolygon2D::_edit_get_rect() const {
return aabb;
}
+bool CollisionPolygon2D::_edit_use_rect() const {
+ return true;
+}
+
bool CollisionPolygon2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
return Geometry::is_point_in_polygon(p_point, Variant(polygon));
diff --git a/scene/2d/collision_polygon_2d.h b/scene/2d/collision_polygon_2d.h
index 4dafe7d1da..412a923292 100644
--- a/scene/2d/collision_polygon_2d.h
+++ b/scene/2d/collision_polygon_2d.h
@@ -73,6 +73,7 @@ public:
Vector<Point2> get_polygon() const;
virtual Rect2 _edit_get_rect() const;
+ virtual bool _edit_use_rect() const;
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
virtual String get_configuration_warning() const;
diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp
index 0eeb6dafe5..83ef4df8f4 100644
--- a/scene/2d/collision_shape_2d.cpp
+++ b/scene/2d/collision_shape_2d.cpp
@@ -173,11 +173,6 @@ Ref<Shape2D> CollisionShape2D::get_shape() const {
return shape;
}
-Rect2 CollisionShape2D::_edit_get_rect() const {
-
- return rect;
-}
-
bool CollisionShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
if (!shape.is_valid())
diff --git a/scene/2d/collision_shape_2d.h b/scene/2d/collision_shape_2d.h
index cdff595828..ed2c09d53d 100644
--- a/scene/2d/collision_shape_2d.h
+++ b/scene/2d/collision_shape_2d.h
@@ -54,7 +54,6 @@ protected:
static void _bind_methods();
public:
- virtual Rect2 _edit_get_rect() const;
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
void set_shape(const Ref<Shape2D> &p_shape);
diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp
index 1220ff299c..9a44eb31bb 100644
--- a/scene/2d/light_2d.cpp
+++ b/scene/2d/light_2d.cpp
@@ -65,6 +65,10 @@ Rect2 Light2D::_edit_get_rect() const {
return Rect2(texture_offset - s / 2.0, s);
}
+bool Light2D::_edit_use_rect() const {
+ return true;
+}
+
void Light2D::_update_light_visibility() {
if (!is_inside_tree())
diff --git a/scene/2d/light_2d.h b/scene/2d/light_2d.h
index 16d8c485d4..543805e329 100644
--- a/scene/2d/light_2d.h
+++ b/scene/2d/light_2d.h
@@ -92,6 +92,7 @@ public:
virtual Point2 _edit_get_pivot() const;
virtual bool _edit_use_pivot() const;
virtual Rect2 _edit_get_rect() const;
+ virtual bool _edit_use_rect() const;
void set_enabled(bool p_enabled);
bool is_enabled() const;
diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp
index ba4a5c5571..229c2c6fe8 100644
--- a/scene/2d/line_2d.cpp
+++ b/scene/2d/line_2d.cpp
@@ -62,6 +62,10 @@ Rect2 Line2D::_edit_get_rect() const {
return aabb;
}
+bool Line2D::_edit_use_rect() const {
+ return true;
+}
+
bool Line2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
const real_t d = _width / 2 + p_tolerance;
diff --git a/scene/2d/line_2d.h b/scene/2d/line_2d.h
index 0eba024555..24c48982cd 100644
--- a/scene/2d/line_2d.h
+++ b/scene/2d/line_2d.h
@@ -59,6 +59,7 @@ public:
Line2D();
virtual Rect2 _edit_get_rect() const;
+ virtual bool _edit_use_rect() const;
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
void set_points(const PoolVector<Vector2> &p_points);
diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp
index 95e24505be..2a52ade01d 100644
--- a/scene/2d/node_2d.cpp
+++ b/scene/2d/node_2d.cpp
@@ -58,16 +58,39 @@ void Node2D::_edit_set_state(const Dictionary &p_state) {
}
void Node2D::_edit_set_position(const Point2 &p_position) {
- pos = p_position;
- _update_transform();
- _change_notify("position");
+ set_position(p_position);
}
Point2 Node2D::_edit_get_position() const {
return pos;
}
+void Node2D::_edit_set_scale(const Size2 &p_scale) {
+ set_scale(p_scale);
+}
+
+Size2 Node2D::_edit_get_scale() const {
+ return _scale;
+}
+
+void Node2D::_edit_set_rotation(float p_rotation) {
+ angle = p_rotation;
+ _update_transform();
+ _change_notify("rotation");
+ _change_notify("rotation_degrees");
+}
+
+float Node2D::_edit_get_rotation() const {
+ return angle;
+}
+
+bool Node2D::_edit_use_rotation() const {
+ return true;
+}
+
void Node2D::_edit_set_rect(const Rect2 &p_edit_rect) {
+ ERR_FAIL_COND(!_edit_use_rect());
+
Rect2 r = _edit_get_rect();
Vector2 zero_offset;
@@ -83,7 +106,7 @@ void Node2D::_edit_set_rect(const Rect2 &p_edit_rect) {
if (r.size.y != 0)
new_scale.y = p_edit_rect.size.y / r.size.y;
- Point2 new_pos = p_edit_rect.position + p_edit_rect.size * zero_offset; //p_edit_rect.pos - r.pos;
+ Point2 new_pos = p_edit_rect.position + p_edit_rect.size * zero_offset;
Transform2D postxf;
postxf.set_rotation_and_scale(angle, _scale);
@@ -97,25 +120,6 @@ void Node2D::_edit_set_rect(const Rect2 &p_edit_rect) {
_change_notify("position");
}
-bool Node2D::_edit_use_rect() const {
- return true;
-}
-
-void Node2D::_edit_set_rotation(float p_rotation) {
- angle = p_rotation;
- _update_transform();
- _change_notify("rotation");
- _change_notify("rotation_degrees");
-}
-
-float Node2D::_edit_get_rotation() const {
- return angle;
-}
-
-bool Node2D::_edit_use_rotation() const {
- return true;
-}
-
void Node2D::_update_xform_values() {
pos = _mat.elements[2];
diff --git a/scene/2d/node_2d.h b/scene/2d/node_2d.h
index f817b214f8..725686cdf8 100644
--- a/scene/2d/node_2d.h
+++ b/scene/2d/node_2d.h
@@ -62,12 +62,16 @@ public:
virtual void _edit_set_position(const Point2 &p_position);
virtual Point2 _edit_get_position() const;
- virtual void _edit_set_rect(const Rect2 &p_edit_rect);
- virtual bool _edit_use_rect() const;
+
+ virtual void _edit_set_scale(const Size2 &p_scale);
+ virtual Size2 _edit_get_scale() const;
+
virtual void _edit_set_rotation(float p_rotation);
virtual float _edit_get_rotation() const;
virtual bool _edit_use_rotation() const;
+ virtual void _edit_set_rect(const Rect2 &p_edit_rect);
+
void set_position(const Point2 &p_pos);
void set_rotation(float p_radians);
void set_rotation_degrees(float p_degrees);
diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp
index 052a0ac026..7b77b34b26 100644
--- a/scene/2d/path_2d.cpp
+++ b/scene/2d/path_2d.cpp
@@ -57,6 +57,10 @@ Rect2 Path2D::_edit_get_rect() const {
return aabb;
}
+bool Path2D::_edit_use_rect() const {
+ return true;
+}
+
bool Path2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
for (int i = 0; i < curve->get_point_count(); i++) {
diff --git a/scene/2d/path_2d.h b/scene/2d/path_2d.h
index 735d289d74..64696442c3 100644
--- a/scene/2d/path_2d.h
+++ b/scene/2d/path_2d.h
@@ -48,6 +48,7 @@ protected:
public:
virtual Rect2 _edit_get_rect() const;
+ virtual bool _edit_use_rect() const;
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
void set_curve(const Ref<Curve2D> &p_curve);
diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp
index 2cb1e86f51..bf5bf29b2e 100644
--- a/scene/2d/polygon_2d.cpp
+++ b/scene/2d/polygon_2d.cpp
@@ -73,6 +73,10 @@ Rect2 Polygon2D::_edit_get_rect() const {
return item_rect;
}
+bool Polygon2D::_edit_use_rect() const {
+ return true;
+}
+
bool Polygon2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
return Geometry::is_point_in_polygon(p_point - get_offset(), Variant(polygon));
diff --git a/scene/2d/polygon_2d.h b/scene/2d/polygon_2d.h
index 3a24177548..262208f2f1 100644
--- a/scene/2d/polygon_2d.h
+++ b/scene/2d/polygon_2d.h
@@ -68,6 +68,7 @@ public:
virtual Point2 _edit_get_pivot() const;
virtual bool _edit_use_pivot() const;
virtual Rect2 _edit_get_rect() const;
+ virtual bool _edit_use_rect() const;
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
diff --git a/scene/2d/position_2d.cpp b/scene/2d/position_2d.cpp
index 37f6aaa2d6..64d23719e7 100644
--- a/scene/2d/position_2d.cpp
+++ b/scene/2d/position_2d.cpp
@@ -44,6 +44,10 @@ Rect2 Position2D::_edit_get_rect() const {
return Rect2(Point2(-10, -10), Size2(20, 20));
}
+bool Position2D::_edit_use_rect() const {
+ return false;
+}
+
void Position2D::_notification(int p_what) {
switch (p_what) {
diff --git a/scene/2d/position_2d.h b/scene/2d/position_2d.h
index 6f6a34c452..bff474cccd 100644
--- a/scene/2d/position_2d.h
+++ b/scene/2d/position_2d.h
@@ -44,6 +44,7 @@ protected:
public:
virtual Rect2 _edit_get_rect() const;
+ virtual bool _edit_use_rect() const;
Position2D();
};
diff --git a/scene/2d/ray_cast_2d.cpp b/scene/2d/ray_cast_2d.cpp
index 5a563143ad..255d2d38d5 100644
--- a/scene/2d/ray_cast_2d.cpp
+++ b/scene/2d/ray_cast_2d.cpp
@@ -101,7 +101,7 @@ void RayCast2D::set_enabled(bool p_enabled) {
enabled = p_enabled;
if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint())
- set_physics_process(p_enabled);
+ set_physics_process_internal(p_enabled);
if (!p_enabled)
collided = false;
}
@@ -141,9 +141,9 @@ void RayCast2D::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
if (enabled && !Engine::get_singleton()->is_editor_hint())
- set_physics_process(true);
+ set_physics_process_internal(true);
else
- set_physics_process(false);
+ set_physics_process_internal(false);
if (Object::cast_to<CollisionObject2D>(get_parent())) {
if (exclude_parent_body)
@@ -155,7 +155,7 @@ void RayCast2D::_notification(int p_what) {
case NOTIFICATION_EXIT_TREE: {
if (enabled)
- set_physics_process(false);
+ set_physics_process_internal(false);
} break;
@@ -183,7 +183,7 @@ void RayCast2D::_notification(int p_what) {
} break;
- case NOTIFICATION_PHYSICS_PROCESS: {
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (!enabled)
break;
diff --git a/scene/2d/screen_button.cpp b/scene/2d/screen_button.cpp
index b6cb734cee..9480f18176 100644
--- a/scene/2d/screen_button.cpp
+++ b/scene/2d/screen_button.cpp
@@ -335,6 +335,10 @@ Rect2 TouchScreenButton::_edit_get_rect() const {
return Rect2(Size2(), texture->get_size());
}
+bool TouchScreenButton::_edit_use_rect() const {
+ return true;
+}
+
void TouchScreenButton::set_visibility_mode(VisibilityMode p_mode) {
visibility = p_mode;
update();
diff --git a/scene/2d/screen_button.h b/scene/2d/screen_button.h
index e6f2a2f3cd..b2fafcc93d 100644
--- a/scene/2d/screen_button.h
+++ b/scene/2d/screen_button.h
@@ -104,6 +104,7 @@ public:
bool is_pressed() const;
Rect2 _edit_get_rect() const;
+ virtual bool _edit_use_rect() const;
TouchScreenButton();
};
diff --git a/scene/2d/sprite.cpp b/scene/2d/sprite.cpp
index 67f016ae79..ca1ac2cd7f 100644
--- a/scene/2d/sprite.cpp
+++ b/scene/2d/sprite.cpp
@@ -58,6 +58,14 @@ bool Sprite::_edit_use_pivot() const {
return true;
}
+Rect2 Sprite::_edit_get_rect() const {
+ return get_rect();
+}
+
+bool Sprite::_edit_use_rect() const {
+ return true;
+}
+
void Sprite::_get_rects(Rect2 &r_src_rect, Rect2 &r_dst_rect, bool &r_filter_clip) const {
Size2 s;
diff --git a/scene/2d/sprite.h b/scene/2d/sprite.h
index abd04515ec..609ad8bb34 100644
--- a/scene/2d/sprite.h
+++ b/scene/2d/sprite.h
@@ -74,7 +74,9 @@ public:
virtual Point2 _edit_get_pivot() const;
virtual bool _edit_use_pivot() const;
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
- virtual Rect2 _edit_get_rect() const { return get_rect(); }
+
+ virtual Rect2 _edit_get_rect() const;
+ virtual bool _edit_use_rect() const;
void set_texture(const Ref<Texture> &p_texture);
Ref<Texture> get_texture() const;
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index c126dd8f6b..14eb26b1ec 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -1119,6 +1119,10 @@ Rect2 TileMap::_edit_get_rect() const {
return rect_cache;
}
+bool TileMap::_edit_use_rect() const {
+ return true;
+}
+
void TileMap::set_collision_layer(uint32_t p_layer) {
collision_layer = p_layer;
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index 587bd3b684..07947004b3 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -245,6 +245,7 @@ public:
int get_cellv(const Vector2 &p_pos) const;
Rect2 _edit_get_rect() const;
+ virtual bool _edit_use_rect() const;
void make_bitmask_area_dirty(const Vector2 &p_pos);
void update_bitmask_area(const Vector2 &p_pos);
diff --git a/scene/2d/visibility_notifier_2d.cpp b/scene/2d/visibility_notifier_2d.cpp
index 4b38534d97..ddca97e60a 100644
--- a/scene/2d/visibility_notifier_2d.cpp
+++ b/scene/2d/visibility_notifier_2d.cpp
@@ -89,6 +89,10 @@ Rect2 VisibilityNotifier2D::_edit_get_rect() const {
return rect;
}
+bool VisibilityNotifier2D::_edit_use_rect() const {
+ return true;
+}
+
Rect2 VisibilityNotifier2D::get_rect() const {
return rect;
diff --git a/scene/2d/visibility_notifier_2d.h b/scene/2d/visibility_notifier_2d.h
index 93a35a709e..c4e12dfa22 100644
--- a/scene/2d/visibility_notifier_2d.h
+++ b/scene/2d/visibility_notifier_2d.h
@@ -56,6 +56,7 @@ protected:
public:
virtual Rect2 _edit_get_rect() const;
+ virtual bool _edit_use_rect() const;
void set_rect(const Rect2 &p_rect);
Rect2 get_rect() const;
diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp
index ff4a807de0..9aac391d80 100644
--- a/scene/3d/physics_body.cpp
+++ b/scene/3d/physics_body.cpp
@@ -781,7 +781,7 @@ String RigidBody::get_configuration_warning() const {
String warning = CollisionObject::get_configuration_warning();
- if ((get_mode() == MODE_RIGID || get_mode() == MODE_CHARACTER) && (ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(0).length() - 1.0) > 0.05)) {
+ if ((get_mode() == MODE_RIGID || get_mode() == MODE_CHARACTER) && (ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(2).length() - 1.0) > 0.05)) {
if (warning != String()) {
warning += "\n";
}
diff --git a/scene/3d/ray_cast.cpp b/scene/3d/ray_cast.cpp
index dd5ae8a999..7f83e2c3ea 100644
--- a/scene/3d/ray_cast.cpp
+++ b/scene/3d/ray_cast.cpp
@@ -103,7 +103,7 @@ void RayCast::set_enabled(bool p_enabled) {
enabled = p_enabled;
if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint())
- set_physics_process(p_enabled);
+ set_physics_process_internal(p_enabled);
if (!p_enabled)
collided = false;
@@ -150,12 +150,12 @@ void RayCast::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
if (enabled && !Engine::get_singleton()->is_editor_hint()) {
- set_physics_process(true);
+ set_physics_process_internal(true);
if (get_tree()->is_debugging_collisions_hint())
_update_debug_shape();
} else
- set_physics_process(false);
+ set_physics_process_internal(false);
if (Object::cast_to<CollisionObject>(get_parent())) {
if (exclude_parent_body)
@@ -168,14 +168,14 @@ void RayCast::_notification(int p_what) {
case NOTIFICATION_EXIT_TREE: {
if (enabled) {
- set_physics_process(false);
+ set_physics_process_internal(false);
}
if (debug_shape)
_clear_debug_shape();
} break;
- case NOTIFICATION_PHYSICS_PROCESS: {
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (!enabled)
break;
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index b5bba885c4..63580bcae6 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -182,8 +182,8 @@ void AnimationPlayer::_notification(int p_what) {
if (!processing) {
//make sure that a previous process state was not saved
//only process if "processing" is set
- set_physics_process(false);
- set_process(false);
+ set_physics_process_internal(false);
+ set_process_internal(false);
}
//_set_process(false);
clear_caches();
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index 562dd155f9..dbfb96697d 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -252,7 +252,7 @@ void BaseButton::_notification(int p_what) {
status.hovering = false;
update();
}
- if (p_what == NOTIFICATION_DRAG_BEGIN) {
+ if (p_what == NOTIFICATION_DRAG_BEGIN || p_what == NOTIFICATION_SCROLL_BEGIN) {
if (status.press_attempt) {
status.press_attempt = false;
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index c71320f207..b7c1d35fd7 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -94,6 +94,14 @@ Point2 Control::_edit_get_position() const {
return get_position();
};
+void Control::_edit_set_scale(const Size2 &p_scale) {
+ set_scale(p_scale);
+}
+
+Size2 Control::_edit_get_scale() const {
+ return data.scale;
+}
+
void Control::_edit_set_rect(const Rect2 &p_edit_rect) {
set_position((get_position() + get_transform().basis_xform(p_edit_rect.position)).snapped(Vector2(1, 1)));
set_size(p_edit_rect.size.snapped(Vector2(1, 1)));
@@ -2888,6 +2896,8 @@ void Control::_bind_methods() {
BIND_CONSTANT(NOTIFICATION_FOCUS_EXIT);
BIND_CONSTANT(NOTIFICATION_THEME_CHANGED);
BIND_CONSTANT(NOTIFICATION_MODAL_CLOSE);
+ BIND_CONSTANT(NOTIFICATION_SCROLL_BEGIN);
+ BIND_CONSTANT(NOTIFICATION_SCROLL_END);
BIND_ENUM_CONSTANT(CURSOR_ARROW);
BIND_ENUM_CONSTANT(CURSOR_IBEAM);
diff --git a/scene/gui/control.h b/scene/gui/control.h
index 65f75c8a66..b5453e60f5 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -272,6 +272,8 @@ public:
NOTIFICATION_FOCUS_EXIT = 44,
NOTIFICATION_THEME_CHANGED = 45,
NOTIFICATION_MODAL_CLOSE = 46,
+ NOTIFICATION_SCROLL_BEGIN = 47,
+ NOTIFICATION_SCROLL_END = 48,
};
@@ -281,6 +283,9 @@ public:
virtual void _edit_set_position(const Point2 &p_position);
virtual Point2 _edit_get_position() const;
+ virtual void _edit_set_scale(const Size2 &p_scale);
+ virtual Size2 _edit_get_scale() const;
+
virtual void _edit_set_rect(const Rect2 &p_edit_rect);
virtual Rect2 _edit_get_rect() const;
virtual bool _edit_use_rect() const;
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index fa7c9e091e..511dc248a0 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -678,7 +678,7 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) {
search_string = ""; //any mousepress cancels
- if (current % current_columns != (current_columns - 1)) {
+ if (current % current_columns != (current_columns - 1) && current + 1 < items.size()) {
set_current(current + 1);
ensure_current_is_visible();
if (select_mode == SELECT_SINGLE) {
@@ -1434,7 +1434,7 @@ void ItemList::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_selected", "idx"), &ItemList::is_selected);
ClassDB::bind_method(D_METHOD("get_selected_items"), &ItemList::get_selected_items);
- ClassDB::bind_method(D_METHOD("move_item", "p_from_idx", "p_to_idx"), &ItemList::move_item);
+ ClassDB::bind_method(D_METHOD("move_item", "from_idx", "to_idx"), &ItemList::move_item);
ClassDB::bind_method(D_METHOD("get_item_count"), &ItemList::get_item_count);
ClassDB::bind_method(D_METHOD("remove_item", "idx"), &ItemList::remove_item);
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 5c0e8fefc7..09c6a3b32c 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -602,6 +602,7 @@ void LineEdit::_notification(int p_what) {
}
int x_ofs = 0;
+ int cached_text_width = text.empty() ? cached_placeholder_width : cached_width;
switch (align) {
@@ -615,15 +616,15 @@ void LineEdit::_notification(int p_what) {
if (window_pos != 0)
x_ofs = style->get_offset().x;
else
- x_ofs = int(size.width - (cached_width)) / 2;
+ x_ofs = MAX(style->get_margin(MARGIN_LEFT), int(size.width - (cached_text_width)) / 2);
} break;
case ALIGN_RIGHT: {
- x_ofs = int(size.width - style->get_offset().x - (cached_width));
+ x_ofs = MAX(style->get_margin(MARGIN_LEFT), int(size.width - style->get_margin(MARGIN_RIGHT) - (cached_text_width)));
} break;
}
- int ofs_max = width - style->get_minimum_size().width;
+ int ofs_max = width - style->get_margin(MARGIN_RIGHT);
int char_ofs = window_pos;
int y_area = height - style->get_minimum_size().height;
@@ -881,7 +882,7 @@ void LineEdit::set_cursor_at_pixel_pos(int p_x) {
} break;
case ALIGN_RIGHT: {
- pixel_ofs = int(size.width - style->get_offset().x - (cached_width));
+ pixel_ofs = int(size.width - style->get_margin(MARGIN_RIGHT) - (cached_width));
} break;
}
@@ -1014,6 +1015,15 @@ String LineEdit::get_text() const {
void LineEdit::set_placeholder(String p_text) {
placeholder = tr(p_text);
+ if ((max_length <= 0) || (placeholder.length() <= max_length)) {
+ Ref<Font> font = get_font("font");
+ cached_placeholder_width = 0;
+ if (font != NULL) {
+ for (int i = 0; i < placeholder.length(); i++) {
+ cached_placeholder_width += font->get_char_size(placeholder[i]).width;
+ }
+ }
+ }
update();
}
@@ -1127,6 +1137,7 @@ void LineEdit::clear_internal() {
_clear_undo_stack();
cached_width = 0;
+ cached_placeholder_width = 0;
cursor_pos = 0;
window_pos = 0;
undo_text = "";
@@ -1468,6 +1479,7 @@ LineEdit::LineEdit() {
_create_undo_state();
align = ALIGN_LEFT;
cached_width = 0;
+ cached_placeholder_width = 0;
cursor_pos = 0;
window_pos = 0;
window_has_focus = true;
diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h
index e3ad3b17f1..c60ea36cc1 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -84,6 +84,7 @@ private:
int max_length; // 0 for no maximum
int cached_width;
+ int cached_placeholder_width;
struct Selection {
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index aaad10f579..a9402d6404 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -112,7 +112,7 @@ void OptionButton::pressed() {
void OptionButton::add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID) {
- popup->add_icon_check_item(p_icon, p_label, p_ID);
+ popup->add_icon_radio_check_item(p_icon, p_label, p_ID);
if (popup->get_item_count() == 1)
select(0);
}
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 9ff3bd6e81..fd2466407e 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -588,6 +588,13 @@ void PopupMenu::add_radio_check_item(const String &p_label, int p_ID, uint32_t p
update();
}
+void PopupMenu::add_icon_radio_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID, uint32_t p_accel) {
+
+ add_icon_check_item(p_icon, p_label, p_ID, p_accel);
+ items[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
+ update();
+}
+
void PopupMenu::add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) {
ERR_FAIL_COND(p_shortcut.is_null());
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index c7851969d0..fde91bd845 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -122,6 +122,7 @@ public:
void add_icon_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
void add_check_item(const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
void add_radio_check_item(const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
+ void add_icon_radio_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
void add_submenu_item(const String &p_label, const String &p_submenu, int p_ID = -1);
void add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false);
diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp
index e1cabd3f88..6ec67aca6b 100644
--- a/scene/gui/scroll_bar.cpp
+++ b/scene/gui/scroll_bar.cpp
@@ -114,7 +114,7 @@ void ScrollBar::_gui_input(Ref<InputEvent> p_event) {
if (smooth_scroll_enabled) {
scrolling = true;
- set_physics_process(true);
+ set_physics_process_internal(true);
} else {
set_value(target_scroll);
}
@@ -138,7 +138,7 @@ void ScrollBar::_gui_input(Ref<InputEvent> p_event) {
if (smooth_scroll_enabled) {
scrolling = true;
- set_physics_process(true);
+ set_physics_process_internal(true);
} else {
set_value(target_scroll);
}
@@ -322,7 +322,7 @@ void ScrollBar::_notification(int p_what) {
drag_slave = NULL;
}
- if (p_what == NOTIFICATION_PHYSICS_PROCESS) {
+ if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
if (scrolling) {
if (get_value() != target_scroll) {
@@ -337,7 +337,7 @@ void ScrollBar::_notification(int p_what) {
}
} else {
scrolling = false;
- set_physics_process(false);
+ set_physics_process_internal(false);
}
} else if (drag_slave_touching) {
@@ -397,7 +397,7 @@ void ScrollBar::_notification(int p_what) {
}
if (turnoff) {
- set_physics_process(false);
+ set_physics_process_internal(false);
drag_slave_touching = false;
drag_slave_touching_deaccel = false;
}
@@ -566,7 +566,7 @@ void ScrollBar::_drag_slave_input(const Ref<InputEvent> &p_input) {
if (mb->is_pressed()) {
if (drag_slave_touching) {
- set_physics_process(false);
+ set_physics_process_internal(false);
drag_slave_touching_deaccel = false;
drag_slave_touching = false;
drag_slave_speed = Vector2();
@@ -586,7 +586,7 @@ void ScrollBar::_drag_slave_input(const Ref<InputEvent> &p_input) {
drag_slave_touching_deaccel = false;
time_since_motion = 0;
if (drag_slave_touching) {
- set_physics_process(true);
+ set_physics_process_internal(true);
time_since_motion = 0;
}
}
@@ -598,7 +598,7 @@ void ScrollBar::_drag_slave_input(const Ref<InputEvent> &p_input) {
if (drag_slave_speed == Vector2()) {
drag_slave_touching_deaccel = false;
drag_slave_touching = false;
- set_physics_process(false);
+ set_physics_process_internal(false);
} else {
drag_slave_touching_deaccel = true;
diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp
index 33b3d46486..a1dcf3b002 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -68,13 +68,19 @@ Size2 ScrollContainer::get_minimum_size() const {
};
void ScrollContainer::_cancel_drag() {
- set_physics_process(false);
+ set_physics_process_internal(false);
drag_touching_deaccel = false;
drag_touching = false;
drag_speed = Vector2();
drag_accum = Vector2();
last_drag_accum = Vector2();
drag_from = Vector2();
+
+ if (beyond_deadzone) {
+ emit_signal("scroll_ended");
+ propagate_notification(NOTIFICATION_SCROLL_END);
+ beyond_deadzone = false;
+ }
}
void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
@@ -122,13 +128,7 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (mb->is_pressed()) {
if (drag_touching) {
- set_physics_process(false);
- drag_touching_deaccel = false;
- drag_touching = false;
- drag_speed = Vector2();
- drag_accum = Vector2();
- last_drag_accum = Vector2();
- drag_from = Vector2();
+ _cancel_drag();
}
if (true) {
@@ -138,9 +138,10 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
drag_from = Vector2(h_scroll->get_value(), v_scroll->get_value());
drag_touching = OS::get_singleton()->has_touchscreen_ui_hint();
drag_touching_deaccel = false;
+ beyond_deadzone = false;
time_since_motion = 0;
if (drag_touching) {
- set_physics_process(true);
+ set_physics_process_internal(true);
time_since_motion = 0;
}
}
@@ -149,9 +150,7 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (drag_touching) {
if (drag_speed == Vector2()) {
- drag_touching_deaccel = false;
- drag_touching = false;
- set_physics_process(false);
+ _cancel_drag();
} else {
drag_touching_deaccel = true;
@@ -168,17 +167,27 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
Vector2 motion = Vector2(mm->get_relative().x, mm->get_relative().y);
drag_accum -= motion;
- Vector2 diff = drag_from + drag_accum;
-
- if (scroll_h)
- h_scroll->set_value(diff.x);
- else
- drag_accum.x = 0;
- if (scroll_v)
- v_scroll->set_value(diff.y);
- else
- drag_accum.y = 0;
- time_since_motion = 0;
+
+ if (beyond_deadzone || scroll_h && Math::abs(drag_accum.x) > deadzone || scroll_v && Math::abs(drag_accum.y) > deadzone) {
+ if (!beyond_deadzone) {
+ propagate_notification(NOTIFICATION_SCROLL_BEGIN);
+ emit_signal("scroll_started");
+
+ beyond_deadzone = true;
+ // resetting drag_accum here ensures smooth scrolling after reaching deadzone
+ drag_accum = -motion;
+ }
+ Vector2 diff = drag_from + drag_accum;
+ if (scroll_h)
+ h_scroll->set_value(diff.x);
+ else
+ drag_accum.x = 0;
+ if (scroll_v)
+ v_scroll->set_value(diff.y);
+ else
+ drag_accum.y = 0;
+ time_since_motion = 0;
+ }
}
}
@@ -269,7 +278,7 @@ void ScrollContainer::_notification(int p_what) {
update_scrollbars();
}
- if (p_what == NOTIFICATION_PHYSICS_PROCESS) {
+ if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
if (drag_touching) {
@@ -323,9 +332,7 @@ void ScrollContainer::_notification(int p_what) {
drag_speed = Vector2(sgn_x * val_x, sgn_y * val_y);
if (turnoff_h && turnoff_v) {
- set_physics_process(false);
- drag_touching = false;
- drag_touching_deaccel = false;
+ _cancel_drag();
}
} else {
@@ -430,6 +437,14 @@ void ScrollContainer::set_h_scroll(int p_pos) {
_cancel_drag();
}
+int ScrollContainer::get_deadzone() const {
+ return deadzone;
+}
+
+void ScrollContainer::set_deadzone(int p_deadzone) {
+ deadzone = p_deadzone;
+}
+
String ScrollContainer::get_configuration_warning() const {
int found = 0;
@@ -466,12 +481,20 @@ void ScrollContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_h_scroll"), &ScrollContainer::get_h_scroll);
ClassDB::bind_method(D_METHOD("set_v_scroll", "value"), &ScrollContainer::set_v_scroll);
ClassDB::bind_method(D_METHOD("get_v_scroll"), &ScrollContainer::get_v_scroll);
+ ClassDB::bind_method(D_METHOD("set_deadzone", "deadzone"), &ScrollContainer::set_deadzone);
+ ClassDB::bind_method(D_METHOD("get_deadzone"), &ScrollContainer::get_deadzone);
+
+ ADD_SIGNAL(MethodInfo("scroll_started"));
+ ADD_SIGNAL(MethodInfo("scroll_ended"));
ADD_GROUP("Scroll", "scroll_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_horizontal_enabled"), "set_enable_h_scroll", "is_h_scroll_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_horizontal"), "set_h_scroll", "get_h_scroll");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_vertical_enabled"), "set_enable_v_scroll", "is_v_scroll_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_vertical"), "set_v_scroll", "get_v_scroll");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_deadzone"), "set_deadzone", "get_deadzone");
+
+ GLOBAL_DEF("gui/common/default_scroll_deadzone", 0);
};
ScrollContainer::ScrollContainer() {
@@ -490,8 +513,11 @@ ScrollContainer::ScrollContainer() {
drag_speed = Vector2();
drag_touching = false;
drag_touching_deaccel = false;
+ beyond_deadzone = false;
scroll_h = true;
scroll_v = true;
+ deadzone = GLOBAL_GET("gui/common/default_scroll_deadzone");
+
set_clip_contents(true);
};
diff --git a/scene/gui/scroll_container.h b/scene/gui/scroll_container.h
index 6e3387918b..3fe1ed447a 100644
--- a/scene/gui/scroll_container.h
+++ b/scene/gui/scroll_container.h
@@ -56,10 +56,13 @@ class ScrollContainer : public Container {
bool drag_touching;
bool drag_touching_deaccel;
bool click_handled;
+ bool beyond_deadzone;
bool scroll_h;
bool scroll_v;
+ int deadzone;
+
void _cancel_drag();
protected:
@@ -86,6 +89,9 @@ public:
void set_enable_v_scroll(bool p_enable);
bool is_v_scroll_enabled() const;
+ int get_deadzone() const;
+ void set_deadzone(int p_deadzone);
+
virtual bool clips_input() const;
virtual String get_configuration_warning() const;
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index 6e85ce5eb4..0363dd44c2 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -31,6 +31,9 @@
#include "tab_container.h"
#include "message_queue.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/label.h"
+#include "scene/gui/texture_rect.h"
int TabContainer::_get_top_margin() const {
@@ -492,6 +495,141 @@ void TabContainer::_update_current_tab() {
set_current_tab(current);
}
+Variant TabContainer::get_drag_data(const Point2 &p_point) {
+
+ if (!drag_to_rearrange_enabled)
+ return Variant();
+
+ int tab_over = get_tab_idx_at_point(p_point);
+
+ if (tab_over < 0)
+ return Variant();
+
+ HBoxContainer *drag_preview = memnew(HBoxContainer);
+
+ Ref<Texture> icon = get_tab_icon(tab_over);
+ if (!icon.is_null()) {
+ TextureRect *tf = memnew(TextureRect);
+ tf->set_texture(icon);
+ drag_preview->add_child(tf);
+ }
+ Label *label = memnew(Label(get_tab_title(tab_over)));
+ drag_preview->add_child(label);
+ set_drag_preview(drag_preview);
+
+ Dictionary drag_data;
+ drag_data["type"] = "tabc_element";
+ drag_data["tabc_element"] = tab_over;
+ drag_data["from_path"] = get_path();
+ return drag_data;
+}
+
+bool TabContainer::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
+
+ if (!drag_to_rearrange_enabled)
+ return false;
+
+ Dictionary d = p_data;
+ if (!d.has("type"))
+ return false;
+
+ if (String(d["type"]) == "tabc_element") {
+
+ NodePath from_path = d["from_path"];
+ NodePath to_path = get_path();
+ if (from_path == to_path) {
+ return true;
+ } else if (get_tabs_rearrange_group() != -1) {
+ // drag and drop between other TabContainers
+ Node *from_node = get_node(from_path);
+ TabContainer *from_tabc = Object::cast_to<TabContainer>(from_node);
+ if (from_tabc && from_tabc->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void TabContainer::drop_data(const Point2 &p_point, const Variant &p_data) {
+
+ if (!drag_to_rearrange_enabled)
+ return;
+
+ int hover_now = get_tab_idx_at_point(p_point);
+
+ Dictionary d = p_data;
+ if (!d.has("type"))
+ return;
+
+ if (String(d["type"]) == "tabc_element") {
+
+ int tab_from_id = d["tabc_element"];
+ NodePath from_path = d["from_path"];
+ NodePath to_path = get_path();
+ if (from_path == to_path) {
+ if (hover_now < 0)
+ hover_now = get_tab_count() - 1;
+ move_child(get_tab_control(tab_from_id), hover_now);
+ set_current_tab(hover_now);
+ } else if (get_tabs_rearrange_group() != -1) {
+ // drag and drop between TabContainers
+ Node *from_node = get_node(from_path);
+ TabContainer *from_tabc = Object::cast_to<TabContainer>(from_node);
+ if (from_tabc && from_tabc->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
+ Control *moving_tabc = from_tabc->get_tab_control(tab_from_id);
+ from_tabc->remove_child(moving_tabc);
+ add_child(moving_tabc);
+ if (hover_now < 0)
+ hover_now = get_tab_count() - 1;
+ move_child(moving_tabc, hover_now);
+ set_current_tab(hover_now);
+ emit_signal("tab_changed", hover_now);
+ }
+ }
+ }
+ update();
+}
+
+int TabContainer::get_tab_idx_at_point(const Point2 &p_point) const {
+
+ if (get_tab_count() == 0)
+ return -1;
+
+ // must be on tabs in the tab header area.
+ if (p_point.x < tabs_ofs_cache || p_point.y > _get_top_margin())
+ return -1;
+
+ Size2 size = get_size();
+ int right_ofs = 0;
+
+ if (popup) {
+ Ref<Texture> menu = get_icon("menu");
+ right_ofs += menu->get_width();
+ }
+ if (buttons_visible_cache) {
+ Ref<Texture> increment = get_icon("increment");
+ Ref<Texture> decrement = get_icon("decrement");
+ right_ofs += increment->get_width() + decrement->get_width();
+ }
+ if (p_point.x > size.width - right_ofs) {
+ return -1;
+ }
+
+ // get the tab at the point
+ Vector<Control *> tabs = _get_tabs();
+ int px = p_point.x;
+ px -= tabs_ofs_cache;
+ for (int i = first_tab_cache; i <= last_tab_cache; i++) {
+ int tab_width = _get_tab_width(i);
+ if (px < tab_width) {
+ return i;
+ }
+ px -= tab_width;
+ }
+ return -1;
+}
+
void TabContainer::set_tab_align(TabAlign p_align) {
ERR_FAIL_INDEX(p_align, 3);
@@ -500,6 +638,7 @@ void TabContainer::set_tab_align(TabAlign p_align) {
_change_notify("tab_align");
}
+
TabContainer::TabAlign TabContainer::get_tab_align() const {
return align;
@@ -643,6 +782,21 @@ Popup *TabContainer::get_popup() const {
return popup;
}
+void TabContainer::set_drag_to_rearrange_enabled(bool p_enabled) {
+ drag_to_rearrange_enabled = p_enabled;
+}
+
+bool TabContainer::get_drag_to_rearrange_enabled() const {
+ return drag_to_rearrange_enabled;
+}
+void TabContainer::set_tabs_rearrange_group(int p_group_id) {
+ tabs_rearrange_group = p_group_id;
+}
+
+int TabContainer::get_tabs_rearrange_group() const {
+ return tabs_rearrange_group;
+}
+
void TabContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("_gui_input"), &TabContainer::_gui_input);
@@ -664,6 +818,10 @@ void TabContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tab_disabled", "tab_idx"), &TabContainer::get_tab_disabled);
ClassDB::bind_method(D_METHOD("set_popup", "popup"), &TabContainer::set_popup);
ClassDB::bind_method(D_METHOD("get_popup"), &TabContainer::get_popup);
+ ClassDB::bind_method(D_METHOD("set_drag_to_rearrange_enabled", "enabled"), &TabContainer::set_drag_to_rearrange_enabled);
+ ClassDB::bind_method(D_METHOD("get_drag_to_rearrange_enabled"), &TabContainer::get_drag_to_rearrange_enabled);
+ ClassDB::bind_method(D_METHOD("set_tabs_rearrange_group", "group_id"), &TabContainer::set_tabs_rearrange_group);
+ ClassDB::bind_method(D_METHOD("get_tabs_rearrange_group"), &TabContainer::get_tabs_rearrange_group);
ClassDB::bind_method(D_METHOD("_child_renamed_callback"), &TabContainer::_child_renamed_callback);
ClassDB::bind_method(D_METHOD("_on_theme_changed"), &TabContainer::_on_theme_changed);
@@ -676,6 +834,7 @@ void TabContainer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_align", "get_tab_align");
ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1", PROPERTY_USAGE_EDITOR), "set_current_tab", "get_current_tab");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "tabs_visible"), "set_tabs_visible", "are_tabs_visible");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_to_rearrange_enabled"), "set_drag_to_rearrange_enabled", "get_drag_to_rearrange_enabled");
BIND_ENUM_CONSTANT(ALIGN_LEFT);
BIND_ENUM_CONSTANT(ALIGN_CENTER);
@@ -694,4 +853,6 @@ TabContainer::TabContainer() {
align = ALIGN_CENTER;
tabs_visible = true;
popup = NULL;
+ drag_to_rearrange_enabled = false;
+ tabs_rearrange_group = -1;
}
diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h
index 4bc6e00145..1afe5f7541 100644
--- a/scene/gui/tab_container.h
+++ b/scene/gui/tab_container.h
@@ -58,6 +58,8 @@ private:
Control *_get_tab(int p_idx) const;
int _get_top_margin() const;
Popup *popup;
+ bool drag_to_rearrange_enabled;
+ int tabs_rearrange_group;
Vector<Control *> _get_tabs() const;
int _get_tab_width(int p_index) const;
@@ -71,6 +73,11 @@ protected:
virtual void add_child_notify(Node *p_child);
virtual void remove_child_notify(Node *p_child);
+ Variant get_drag_data(const Point2 &p_point);
+ bool can_drop_data(const Point2 &p_point, const Variant &p_data) const;
+ void drop_data(const Point2 &p_point, const Variant &p_data);
+ int get_tab_idx_at_point(const Point2 &p_point) const;
+
static void _bind_methods();
public:
@@ -104,6 +111,11 @@ public:
void set_popup(Node *p_popup);
Popup *get_popup() const;
+ void set_drag_to_rearrange_enabled(bool p_enabled);
+ bool get_drag_to_rearrange_enabled() const;
+ void set_tabs_rearrange_group(int p_group_id);
+ int get_tabs_rearrange_group() const;
+
TabContainer();
};
diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp
index dee32aef7a..b114264de1 100644
--- a/scene/gui/tabs.cpp
+++ b/scene/gui/tabs.cpp
@@ -31,6 +31,9 @@
#include "tabs.h"
#include "message_queue.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/label.h"
+#include "scene/gui/texture_rect.h"
Size2 Tabs::get_minimum_size() const {
@@ -624,20 +627,105 @@ void Tabs::remove_tab(int p_idx) {
Variant Tabs::get_drag_data(const Point2 &p_point) {
- return get_tab_idx_at_point(p_point);
+ if (!drag_to_rearrange_enabled)
+ return Variant();
+
+ int tab_over = get_tab_idx_at_point(p_point);
+
+ if (tab_over < 0)
+ return Variant();
+
+ HBoxContainer *drag_preview = memnew(HBoxContainer);
+
+ if (!tabs[tab_over].icon.is_null()) {
+ TextureRect *tf = memnew(TextureRect);
+ tf->set_texture(tabs[tab_over].icon);
+ drag_preview->add_child(tf);
+ }
+ Label *label = memnew(Label(tabs[tab_over].text));
+ drag_preview->add_child(label);
+ if (!tabs[tab_over].right_button.is_null()) {
+ TextureRect *tf = memnew(TextureRect);
+ tf->set_texture(tabs[tab_over].right_button);
+ drag_preview->add_child(tf);
+ }
+ set_drag_preview(drag_preview);
+
+ Dictionary drag_data;
+ drag_data["type"] = "tab_element";
+ drag_data["tab_element"] = tab_over;
+ drag_data["from_path"] = get_path();
+ return drag_data;
}
bool Tabs::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
- return get_tab_idx_at_point(p_point) > -1;
+ if (!drag_to_rearrange_enabled)
+ return false;
+
+ Dictionary d = p_data;
+ if (!d.has("type"))
+ return false;
+
+ if (String(d["type"]) == "tab_element") {
+
+ NodePath from_path = d["from_path"];
+ NodePath to_path = get_path();
+ if (from_path == to_path) {
+ return true;
+ } else if (get_tabs_rearrange_group() != -1) {
+ // drag and drop between other Tabs
+ Node *from_node = get_node(from_path);
+ Tabs *from_tabs = Object::cast_to<Tabs>(from_node);
+ if (from_tabs && from_tabs->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
+ return true;
+ }
+ }
+ }
+ return false;
}
void Tabs::drop_data(const Point2 &p_point, const Variant &p_data) {
+ if (!drag_to_rearrange_enabled)
+ return;
+
int hover_now = get_tab_idx_at_point(p_point);
- ERR_FAIL_INDEX(hover_now, tabs.size());
- emit_signal("reposition_active_tab_request", hover_now);
+ Dictionary d = p_data;
+ if (!d.has("type"))
+ return;
+
+ if (String(d["type"]) == "tab_element") {
+
+ int tab_from_id = d["tab_element"];
+ NodePath from_path = d["from_path"];
+ NodePath to_path = get_path();
+ if (from_path == to_path) {
+ if (hover_now < 0)
+ hover_now = get_tab_count() - 1;
+ move_tab(tab_from_id, hover_now);
+ emit_signal("reposition_active_tab_request", hover_now);
+ set_current_tab(hover_now);
+ } else if (get_tabs_rearrange_group() != -1) {
+ // drag and drop between Tabs
+ Node *from_node = get_node(from_path);
+ Tabs *from_tabs = Object::cast_to<Tabs>(from_node);
+ if (from_tabs && from_tabs->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
+ if (tab_from_id >= from_tabs->get_tab_count())
+ return;
+ Tab moving_tab = from_tabs->tabs[tab_from_id];
+ if (hover_now < 0)
+ hover_now = get_tab_count();
+ tabs.insert(hover_now, moving_tab);
+ from_tabs->remove_tab(tab_from_id);
+ set_current_tab(hover_now);
+ emit_signal("tab_changed", hover_now);
+ _update_cache();
+ }
+ }
+ }
+ update();
}
int Tabs::get_tab_idx_at_point(const Point2 &p_point) const {
@@ -817,6 +905,21 @@ bool Tabs::get_scrolling_enabled() const {
return scrolling_enabled;
}
+void Tabs::set_drag_to_rearrange_enabled(bool p_enabled) {
+ drag_to_rearrange_enabled = p_enabled;
+}
+
+bool Tabs::get_drag_to_rearrange_enabled() const {
+ return drag_to_rearrange_enabled;
+}
+void Tabs::set_tabs_rearrange_group(int p_group_id) {
+ tabs_rearrange_group = p_group_id;
+}
+
+int Tabs::get_tabs_rearrange_group() const {
+ return tabs_rearrange_group;
+}
+
void Tabs::_bind_methods() {
ClassDB::bind_method(D_METHOD("_gui_input"), &Tabs::_gui_input);
@@ -842,6 +945,10 @@ void Tabs::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tab_close_display_policy"), &Tabs::get_tab_close_display_policy);
ClassDB::bind_method(D_METHOD("set_scrolling_enabled", "enabled"), &Tabs::set_scrolling_enabled);
ClassDB::bind_method(D_METHOD("get_scrolling_enabled"), &Tabs::get_scrolling_enabled);
+ ClassDB::bind_method(D_METHOD("set_drag_to_rearrange_enabled", "enabled"), &Tabs::set_drag_to_rearrange_enabled);
+ ClassDB::bind_method(D_METHOD("get_drag_to_rearrange_enabled"), &Tabs::get_drag_to_rearrange_enabled);
+ ClassDB::bind_method(D_METHOD("set_tabs_rearrange_group", "group_id"), &Tabs::set_tabs_rearrange_group);
+ ClassDB::bind_method(D_METHOD("get_tabs_rearrange_group"), &Tabs::get_tabs_rearrange_group);
ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("right_button_pressed", PropertyInfo(Variant::INT, "tab")));
@@ -854,6 +961,7 @@ void Tabs::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_align", "get_tab_align");
ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "tab_close_display_policy", PROPERTY_HINT_ENUM, "Show Never,Show Active Only,Show Always"), "set_tab_close_display_policy", "get_tab_close_display_policy");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scrolling_enabled"), "set_scrolling_enabled", "get_scrolling_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_to_rearrange_enabled"), "set_drag_to_rearrange_enabled", "get_drag_to_rearrange_enabled");
BIND_ENUM_CONSTANT(ALIGN_LEFT);
BIND_ENUM_CONSTANT(ALIGN_CENTER);
@@ -884,4 +992,6 @@ Tabs::Tabs() {
scrolling_enabled = true;
buttons_visible = false;
hover = -1;
+ drag_to_rearrange_enabled = false;
+ tabs_rearrange_group = -1;
}
diff --git a/scene/gui/tabs.h b/scene/gui/tabs.h
index 246b3cba67..3b38e7f2cb 100644
--- a/scene/gui/tabs.h
+++ b/scene/gui/tabs.h
@@ -90,6 +90,8 @@ private:
int hover; // hovered tab
int min_width;
bool scrolling_enabled;
+ bool drag_to_rearrange_enabled;
+ int tabs_rearrange_group;
int get_tab_width(int p_idx) const;
void _ensure_no_over_offset();
@@ -143,6 +145,11 @@ public:
void set_scrolling_enabled(bool p_enabled);
bool get_scrolling_enabled() const;
+ void set_drag_to_rearrange_enabled(bool p_enabled);
+ bool get_drag_to_rearrange_enabled() const;
+ void set_tabs_rearrange_group(int p_group_id);
+ int get_tabs_rearrange_group() const;
+
void ensure_tab_visible(int p_idx);
void set_min_width(int p_width);
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index cc6a677ec8..068ea9d4f5 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -539,7 +539,7 @@ void TextEdit::_notification(int p_what) {
draw_caret = false;
update();
} break;
- case NOTIFICATION_PHYSICS_PROCESS: {
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (scrolling && v_scroll->get_value() != target_v_scroll) {
double target_y = target_v_scroll - v_scroll->get_value();
double dist = sqrt(target_y * target_y);
@@ -548,13 +548,13 @@ void TextEdit::_notification(int p_what) {
if (Math::abs(vel) >= dist) {
v_scroll->set_value(target_v_scroll);
scrolling = false;
- set_physics_process(false);
+ set_physics_process_internal(false);
} else {
v_scroll->set_value(v_scroll->get_value() + vel);
}
} else {
scrolling = false;
- set_physics_process(false);
+ set_physics_process_internal(false);
}
} break;
case NOTIFICATION_DRAW: {
@@ -3027,7 +3027,7 @@ void TextEdit::_scroll_up(real_t p_delta) {
v_scroll->set_value(target_v_scroll);
} else {
scrolling = true;
- set_physics_process(true);
+ set_physics_process_internal(true);
}
} else {
v_scroll->set_value(target_v_scroll);
@@ -3060,7 +3060,7 @@ void TextEdit::_scroll_down(real_t p_delta) {
v_scroll->set_value(target_v_scroll);
} else {
scrolling = true;
- set_physics_process(true);
+ set_physics_process_internal(true);
}
} else {
v_scroll->set_value(target_v_scroll);
@@ -4023,6 +4023,9 @@ int TextEdit::_is_line_in_region(int p_line) {
// calculate up to line we need and update the cache along the way.
int in_region = color_region_cache[previous_line];
+ if (previous_line == -1) {
+ in_region = -1;
+ }
for (int i = previous_line; i < p_line; i++) {
const Map<int, Text::ColorRegionInfo> &cri_map = _get_line_color_region_info(i);
for (const Map<int, Text::ColorRegionInfo>::Element *E = cri_map.front(); E; E = E->next()) {
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index e7f63997f2..1d27612766 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -2545,7 +2545,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
if (drag_speed == 0) {
drag_touching_deaccel = false;
drag_touching = false;
- set_physics_process(false);
+ set_physics_process_internal(false);
} else {
drag_touching_deaccel = true;
@@ -2611,7 +2611,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
break;
if (drag_touching) {
- set_physics_process(false);
+ set_physics_process_internal(false);
drag_touching_deaccel = false;
drag_touching = false;
drag_speed = 0;
@@ -2626,7 +2626,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
drag_touching = OS::get_singleton()->has_touchscreen_ui_hint();
drag_touching_deaccel = false;
if (drag_touching) {
- set_physics_process(true);
+ set_physics_process_internal(true);
}
if (b->get_button_index() == BUTTON_LEFT) {
@@ -2829,7 +2829,7 @@ void Tree::_notification(int p_what) {
drop_mode_flags = 0;
scrolling = false;
- set_physics_process(false);
+ set_physics_process_internal(false);
update();
}
if (p_what == NOTIFICATION_DRAG_BEGIN) {
@@ -2837,10 +2837,10 @@ void Tree::_notification(int p_what) {
single_select_defer = NULL;
if (cache.scroll_speed > 0 && get_rect().has_point(get_viewport()->get_mouse_position() - get_global_position())) {
scrolling = true;
- set_physics_process(true);
+ set_physics_process_internal(true);
}
}
- if (p_what == NOTIFICATION_PHYSICS_PROCESS) {
+ if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
if (drag_touching) {
@@ -2853,7 +2853,7 @@ void Tree::_notification(int p_what) {
if (pos < 0) {
pos = 0;
turnoff = true;
- set_physics_process(false);
+ set_physics_process_internal(false);
drag_touching = false;
drag_touching_deaccel = false;
}
@@ -2873,7 +2873,7 @@ void Tree::_notification(int p_what) {
drag_speed = sgn * val;
if (turnoff) {
- set_physics_process(false);
+ set_physics_process_internal(false);
drag_touching = false;
drag_touching_deaccel = false;
}
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index ec01490ae5..fcf8768094 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -1708,11 +1708,18 @@ bool Node::has_persistent_groups() const {
return false;
}
-void Node::_print_tree(const Node *p_node) {
+void Node::_print_tree_pretty(const String prefix, const bool last) {
- print_line(String(p_node->get_path_to(this)));
- for (int i = 0; i < data.children.size(); i++)
- data.children[i]->_print_tree(p_node);
+ String new_prefix = last ? String::utf8(" ┖╴") : String::utf8(" ┠╴");
+ print_line(prefix + new_prefix + String(get_name()));
+ for (int i = 0; i < data.children.size(); i++) {
+ new_prefix = last ? String::utf8(" ") : String::utf8(" ┃ ");
+ data.children[i]->_print_tree_pretty(prefix + new_prefix, i == data.children.size() - 1);
+ }
+}
+
+void Node::print_tree_pretty() {
+ _print_tree_pretty("", true);
}
void Node::print_tree() {
@@ -1720,6 +1727,12 @@ void Node::print_tree() {
_print_tree(this);
}
+void Node::_print_tree(const Node *p_node) {
+ print_line(String(p_node->get_path_to(this)));
+ for (int i = 0; i < data.children.size(); i++)
+ data.children[i]->_print_tree(p_node);
+}
+
void Node::_propagate_reverse_notification(int p_notification) {
data.blocked++;
@@ -2668,6 +2681,7 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_and_skip"), &Node::remove_and_skip);
ClassDB::bind_method(D_METHOD("get_index"), &Node::get_index);
ClassDB::bind_method(D_METHOD("print_tree"), &Node::print_tree);
+ ClassDB::bind_method(D_METHOD("print_tree_pretty"), &Node::print_tree_pretty);
ClassDB::bind_method(D_METHOD("set_filename", "filename"), &Node::set_filename);
ClassDB::bind_method(D_METHOD("get_filename"), &Node::get_filename);
ClassDB::bind_method(D_METHOD("propagate_notification", "what"), &Node::propagate_notification);
diff --git a/scene/main/node.h b/scene/main/node.h
index 2e8716cbd1..b9bafb1ed1 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -153,6 +153,7 @@ private:
Ref<MultiplayerAPI> multiplayer_api;
+ void _print_tree_pretty(const String prefix, const bool last);
void _print_tree(const Node *p_node);
Node *_get_node(const NodePath &p_path) const;
@@ -289,6 +290,7 @@ public:
int get_index() const;
void print_tree();
+ void print_tree_pretty();
void set_filename(const String &p_filename);
String get_filename() const;
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 45a969eeda..568a765420 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -1814,7 +1814,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
if (!over) {
- OS::get_singleton()->set_cursor_shape(OS::CURSOR_ARROW);
+ OS::get_singleton()->set_cursor_shape((OS::CursorShape)Input::get_singleton()->get_default_cursor_shape());
return;
}
diff --git a/servers/arvr_server.cpp b/servers/arvr_server.cpp
index f9d402fe7b..f48bedbdac 100644
--- a/servers/arvr_server.cpp
+++ b/servers/arvr_server.cpp
@@ -58,6 +58,8 @@ void ARVRServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_primary_interface"), &ARVRServer::get_primary_interface);
ClassDB::bind_method(D_METHOD("set_primary_interface", "interface"), &ARVRServer::set_primary_interface);
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "primary_interface"), "set_primary_interface", "get_primary_interface");
+
ClassDB::bind_method(D_METHOD("get_last_process_usec"), &ARVRServer::get_last_process_usec);
ClassDB::bind_method(D_METHOD("get_last_commit_usec"), &ARVRServer::get_last_commit_usec);
ClassDB::bind_method(D_METHOD("get_last_frame_usec"), &ARVRServer::get_last_frame_usec);
diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp
index 37aeef8999..7aca497881 100644
--- a/servers/visual/shader_language.cpp
+++ b/servers/visual/shader_language.cpp
@@ -3008,7 +3008,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
}
if (next_op + 2 >= expression.size() || !expression[next_op + 2].is_op || expression[next_op + 2].op != OP_SELECT_ELSE) {
- _set_error("Mising matching ':' for select operator");
+ _set_error("Missing matching ':' for select operator");
return NULL;
}
@@ -3213,7 +3213,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
precision = get_token_precision(tk.type);
tk = _get_token();
if (!is_token_nonvoid_datatype(tk.type)) {
- _set_error("Expected datatype after precission");
+ _set_error("Expected datatype after precision");
return ERR_PARSE_ERROR;
}
}
@@ -3511,7 +3511,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
if (!p_can_break) {
//all is good
- _set_error("Contiuning is not allowed here");
+ _set_error("Continuing is not allowed here");
}
ControlFlowNode *flow = alloc_node<ControlFlowNode>();
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 394d6098b0..2f765a4000 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -89,7 +89,7 @@ Use UI font variant if available, because it has tight vertical metrics and good
### Hack Regular
- Upstream: https://github.com/source-foundry/Hack
-- Version: 3.000
+- Version: 3.003
- License: MIT + Bitstream Vera License
### DroidSans*.ttf
@@ -241,14 +241,14 @@ File extracted from upstream source:
- Everything in `lib/` except `minilex.c`, `http2/`, `event-libs/`.
- From `misc/` exclude `lws-genhash.c`, `lws-ring.c`, `romfs.{c,h}`, `smtp.c`.
- From `plat/` exclude `lws-plat-{esp*,optee}.c`.
- - From `server/` exclude `access-log.c`, `cgi.c`, `daemonize.c`, `lws-spa.c`,
+ - From `server/` exclude `access-log.c`, `cgi.c`, `daemonize.c`, `lws-spa.c`,
`peer-limits.c`, `rewrite.c`
- Also copy `win32helpers/` from `win32port/`
- `mbedtls_wrapper/include/platform/ssl_port.h` has a small change to check for OSX and FreeBSD (missing `malloc.h`).
The bug is fixed in upstream master via `LWS_HAVE_MALLOC_H`, but not in the 2.4.1 branch (as the file structure has changed).
- You might need to apply the patch in `thirdparty/lws/mbedtls_verify.diff` (port of PR 1215) to future `2.4.x` releases if it does not get cherry picked.
-Important: `lws_config.h` and `lws_config_private.h` contains custom
+Important: `lws_config.h` and `lws_config_private.h` contains custom
Godot build configurations, check them out when updating.
## mbedTLS
diff --git a/thirdparty/fonts/Hack_Regular.ttf b/thirdparty/fonts/Hack_Regular.ttf
index f342700811..92a90cb06e 100644
--- a/thirdparty/fonts/Hack_Regular.ttf
+++ b/thirdparty/fonts/Hack_Regular.ttf
Binary files differ
diff --git a/thirdparty/fonts/LICENSE_Hack.md b/thirdparty/fonts/LICENSE_Hack.md
index ddd23a2b81..08927e504f 100644
--- a/thirdparty/fonts/LICENSE_Hack.md
+++ b/thirdparty/fonts/LICENSE_Hack.md
@@ -1,4 +1,4 @@
-The work in the Hack project is Copyright 2017 Source Foundry Authors and licensed under the MIT License
+The work in the Hack project is Copyright 2018 Source Foundry Authors and licensed under the MIT License
The work in the DejaVu project was committed to the public domain.
@@ -6,7 +6,7 @@ Bitstream Vera Sans Mono Copyright 2003 Bitstream Inc. and licensed under the Bi
### MIT License
-Copyright (c) 2017 Source Foundry Authors
+Copyright (c) 2018 Source Foundry Authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal