summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--SConstruct21
-rw-r--r--core/io/config_file.cpp16
-rw-r--r--core/io/config_file.h3
-rw-r--r--core/ordered_hash_map.h2
-rw-r--r--core/version.h4
-rw-r--r--editor/export_template_manager.cpp226
-rw-r--r--editor/export_template_manager.h20
-rw-r--r--editor/filesystem_dock.cpp6
-rw-r--r--editor/icons/icon_editor_handle_add.svg6
-rw-r--r--editor/icons/icon_editor_handle_selected.svg6
-rw-r--r--editor/plugins/abstract_polygon_2d_editor.cpp425
-rw-r--r--editor/plugins/abstract_polygon_2d_editor.h35
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp6
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.cpp8
-rw-r--r--methods.py6
-rw-r--r--modules/gdnative/gdnative/basis.cpp2
-rw-r--r--modules/gdnative/gdnative/string.cpp24
-rw-r--r--modules/gdnative/gdnative_api.json10
-rw-r--r--modules/gdnative/include/gdnative/basis.h2
-rw-r--r--modules/gdnative/include/gdnative/string.h1
-rw-r--r--modules/mono/config.py1
-rw-r--r--platform/iphone/detect.py8
-rw-r--r--platform/windows/detect.py1
-rw-r--r--platform/x11/detect.py1
-rw-r--r--scene/animation/animation_player.cpp1
-rw-r--r--scene/gui/rich_text_label.cpp3
-rw-r--r--scene/main/http_request.cpp1
-rw-r--r--version.py1
28 files changed, 643 insertions, 203 deletions
diff --git a/SConstruct b/SConstruct
index c05a4332ab..9d536e0d16 100644
--- a/SConstruct
+++ b/SConstruct
@@ -11,7 +11,8 @@ import glob
import sys
import methods
-methods.update_version()
+# moved below to compensate with module version string
+# methods.update_version()
# scan possible build platforms
@@ -87,6 +88,7 @@ env_base.android_appattributes_chunk = ""
env_base.disabled_modules = []
env_base.use_ptrcall = False
env_base.split_drivers = False
+env_base.module_version_string = ""
# To decide whether to rebuild a file, use the MD5 sum only if the timestamp has changed.
# http://scons.org/doc/production/HTML/scons-user/ch06.html#idm139837621851792
@@ -111,6 +113,8 @@ env_base.__class__.android_add_gradle_plugin = methods.android_add_gradle_plugin
env_base.__class__.android_add_gradle_classpath = methods.android_add_gradle_classpath
env_base.__class__.disable_module = methods.disable_module
+env_base.__class__.add_module_version_string = methods.add_module_version_string
+
env_base.__class__.add_source_files = methods.add_source_files
env_base.__class__.use_windows_spawn_fix = methods.use_windows_spawn_fix
env_base.__class__.split_lib = methods.split_lib
@@ -139,6 +143,7 @@ opts.Add('p', "Platform (alias for 'platform')", '')
opts.Add('platform', "Target platform (%s)" % ('|'.join(platform_list), ), '')
opts.Add(EnumVariable('target', "Compilation target", 'debug', ('debug', 'release_debug', 'release')))
opts.Add(BoolVariable('tools', "Build the tools a.k.a. the Godot editor", True))
+opts.Add(BoolVariable('use_lto', 'Use linking time optimization', False))
# Components
opts.Add(BoolVariable('deprecated', "Enable deprecated features", True))
@@ -358,11 +363,6 @@ if selected_platform in platform_list:
suffix += env.extra_suffix
- env["PROGSUFFIX"] = suffix + env["PROGSUFFIX"]
- env["OBJSUFFIX"] = suffix + env["OBJSUFFIX"]
- env["LIBSUFFIX"] = suffix + env["LIBSUFFIX"]
- env["SHLIBSUFFIX"] = suffix + env["SHLIBSUFFIX"]
-
sys.path.remove("./platform/" + selected_platform)
sys.modules.pop('detect')
@@ -391,6 +391,15 @@ if selected_platform in platform_list:
sys.path.remove(tmppath)
sys.modules.pop('config')
+ methods.update_version(env.module_version_string)
+
+ suffix += env.module_version_string
+
+ env["PROGSUFFIX"] = suffix + env["PROGSUFFIX"]
+ env["OBJSUFFIX"] = suffix + env["OBJSUFFIX"]
+ env["LIBSUFFIX"] = suffix + env["LIBSUFFIX"]
+ env["SHLIBSUFFIX"] = suffix + env["SHLIBSUFFIX"]
+
if (env.use_ptrcall):
env.Append(CPPFLAGS=['-DPTRCALL_ENABLED'])
diff --git a/core/io/config_file.cpp b/core/io/config_file.cpp
index daa6b01d6e..b5d41bacbb 100644
--- a/core/io/config_file.cpp
+++ b/core/io/config_file.cpp
@@ -75,7 +75,7 @@ void ConfigFile::set_value(const String &p_section, const String &p_key, const V
} else {
if (!values.has(p_section)) {
- values[p_section] = Map<String, Variant>();
+ values[p_section] = OrderedHashMap<String, Variant>();
}
values[p_section][p_key] = p_value;
@@ -106,7 +106,7 @@ bool ConfigFile::has_section_key(const String &p_section, const String &p_key) c
void ConfigFile::get_sections(List<String> *r_sections) const {
- for (const Map<String, Map<String, Variant> >::Element *E = values.front(); E; E = E->next()) {
+ for (const Map<String, OrderedHashMap<String, Variant> >::Element *E = values.front(); E; E = E->next()) {
r_sections->push_back(E->key());
}
}
@@ -114,8 +114,8 @@ void ConfigFile::get_section_keys(const String &p_section, List<String> *r_keys)
ERR_FAIL_COND(!values.has(p_section));
- for (const Map<String, Variant>::Element *E = values[p_section].front(); E; E = E->next()) {
- r_keys->push_back(E->key());
+ for (OrderedHashMap<String, Variant>::ConstElement E = values[p_section].front(); E; E = E.next()) {
+ r_keys->push_back(E.key());
}
}
@@ -135,17 +135,17 @@ Error ConfigFile::save(const String &p_path) {
return err;
}
- for (Map<String, Map<String, Variant> >::Element *E = values.front(); E; E = E->next()) {
+ for (Map<String, OrderedHashMap<String, Variant> >::Element *E = values.front(); E; E = E->next()) {
if (E != values.front())
file->store_string("\n");
file->store_string("[" + E->key() + "]\n\n");
- for (Map<String, Variant>::Element *F = E->get().front(); F; F = F->next()) {
+ for (OrderedHashMap<String, Variant>::Element F = E->get().front(); F; F = F.next()) {
String vstr;
- VariantWriter::write_to_string(F->get(), vstr);
- file->store_string(F->key() + "=" + vstr + "\n");
+ VariantWriter::write_to_string(F.get(), vstr);
+ file->store_string(F.key() + "=" + vstr + "\n");
}
}
diff --git a/core/io/config_file.h b/core/io/config_file.h
index 8ed8a069e4..2be196faa2 100644
--- a/core/io/config_file.h
+++ b/core/io/config_file.h
@@ -30,13 +30,14 @@
#ifndef CONFIG_FILE_H
#define CONFIG_FILE_H
+#include "core/ordered_hash_map.h"
#include "reference.h"
class ConfigFile : public Reference {
GDCLASS(ConfigFile, Reference);
- Map<String, Map<String, Variant> > values;
+ Map<String, OrderedHashMap<String, Variant> > values;
PoolStringArray _get_sections() const;
PoolStringArray _get_section_keys(const String &p_section) const;
diff --git a/core/ordered_hash_map.h b/core/ordered_hash_map.h
index 9e95f963e1..62eeedb437 100644
--- a/core/ordered_hash_map.h
+++ b/core/ordered_hash_map.h
@@ -181,7 +181,7 @@ public:
};
ConstElement find(const K &p_key) const {
- typename InternalList::Element **list_element = map.getptr(p_key);
+ typename InternalList::Element *const *list_element = map.getptr(p_key);
if (list_element) {
return ConstElement(*list_element);
}
diff --git a/core/version.h b/core/version.h
index 436f30ef01..7d2c47df6a 100644
--- a/core/version.h
+++ b/core/version.h
@@ -30,8 +30,8 @@
#include "version_generated.gen.h"
#ifdef VERSION_PATCH
-#define VERSION_MKSTRING "" _MKSTR(VERSION_MAJOR) "." _MKSTR(VERSION_MINOR) "." _MKSTR(VERSION_PATCH) "." _MKSTR(VERSION_STATUS) "." _MKSTR(VERSION_REVISION)
+#define VERSION_MKSTRING "" _MKSTR(VERSION_MAJOR) "." _MKSTR(VERSION_MINOR) "." _MKSTR(VERSION_PATCH) "." _MKSTR(VERSION_STATUS) "." _MKSTR(VERSION_REVISION) VERSION_MODULE_CONFIG
#else
-#define VERSION_MKSTRING "" _MKSTR(VERSION_MAJOR) "." _MKSTR(VERSION_MINOR) "." _MKSTR(VERSION_STATUS) "." _MKSTR(VERSION_REVISION)
+#define VERSION_MKSTRING "" _MKSTR(VERSION_MAJOR) "." _MKSTR(VERSION_MINOR) "." _MKSTR(VERSION_STATUS) "." _MKSTR(VERSION_REVISION) VERSION_MODULE_CONFIG
#endif // VERSION_PATCH
#define VERSION_FULL_NAME "" _MKSTR(VERSION_NAME) " v" VERSION_MKSTRING
diff --git a/editor/export_template_manager.cpp b/editor/export_template_manager.cpp
index d867404f3d..164d02c580 100644
--- a/editor/export_template_manager.cpp
+++ b/editor/export_template_manager.cpp
@@ -31,10 +31,10 @@
#include "editor_node.h"
#include "editor_scale.h"
+#include "io/json.h"
#include "io/zip_io.h"
#include "os/dir_access.h"
#include "version.h"
-
void ExportTemplateManager::_update_template_list() {
while (current_hb->get_child_count()) {
@@ -66,7 +66,7 @@ void ExportTemplateManager::_update_template_list() {
memdelete(d);
- String current_version = itos(VERSION_MAJOR) + "." + itos(VERSION_MINOR) + "-" + _MKSTR(VERSION_STATUS);
+ String current_version = itos(VERSION_MAJOR) + "." + itos(VERSION_MINOR) + "-" + _MKSTR(VERSION_STATUS) + VERSION_MODULE_CONFIG;
Label *current = memnew(Label);
current->set_h_size_flags(SIZE_EXPAND_FILL);
@@ -120,6 +120,16 @@ void ExportTemplateManager::_update_template_list() {
void ExportTemplateManager::_download_template(const String &p_version) {
print_line("download " + p_version);
+ while (template_list->get_child_count()) {
+ memdelete(template_list->get_child(0));
+ }
+ template_downloader->popup_centered_minsize();
+ template_list_state->set_text(TTR("Retrieving mirrors, please wait.."));
+ template_download_progress->set_max(100);
+ template_download_progress->set_value(0);
+ request_mirror->request("https://www.godotengine.org/download_mirrors.php?version=" + p_version);
+ template_list_state->show();
+ template_download_progress->show();
}
void ExportTemplateManager::_uninstall_template(const String &p_version) {
@@ -307,12 +317,195 @@ void ExportTemplateManager::ok_pressed() {
template_open->popup_centered_ratio();
}
+void ExportTemplateManager::_http_download_mirror_completed(int p_status, int p_code, const PoolStringArray &headers, const PoolByteArray &p_data) {
+
+ print_line("mirror complete");
+ String mirror_str = "{ \"mirrors\":[{\"name\":\"Official\",\"url\":\"http://op.godotengine.org:81/downloads/2.1.4/Godot_v2.1.4-stable_linux_server.64.zip\"}] }";
+
+ template_list_state->hide();
+ template_download_progress->hide();
+
+ Variant r;
+ String errs;
+ int errline;
+ Error err = JSON::parse(mirror_str, r, errs, errline);
+ if (err != OK) {
+ EditorNode::get_singleton()->show_warning("Error parsing JSON with mirror list. Please report this issue!");
+ return;
+ }
+
+ bool mirrors_found = false;
+
+ Dictionary d = r;
+ print_line(r);
+ if (d.has("mirrors")) {
+ Array mirrors = d["mirrors"];
+ for (int i = 0; i < mirrors.size(); i++) {
+ Dictionary m = mirrors[i];
+ ERR_CONTINUE(!m.has("url") || !m.has("name"));
+ LinkButton *lb = memnew(LinkButton);
+ lb->set_text(m["name"]);
+ lb->connect("pressed", this, "_begin_template_download", varray(m["url"]));
+ template_list->add_child(lb);
+ mirrors_found = true;
+ }
+ }
+
+ if (!mirrors_found) {
+ EditorNode::get_singleton()->show_warning(TTR("No download links found for this version. Direct download is only available for official releases."));
+ return;
+ }
+}
+void ExportTemplateManager::_http_download_templates_completed(int p_status, int p_code, const PoolStringArray &headers, const PoolByteArray &p_data) {
+
+ switch (p_status) {
+
+ case HTTPRequest::RESULT_CANT_RESOLVE: {
+ template_list_state->set_text(TTR("Can't resolve."));
+ } break;
+ case HTTPRequest::RESULT_BODY_SIZE_LIMIT_EXCEEDED:
+ case HTTPRequest::RESULT_CONNECTION_ERROR:
+ case HTTPRequest::RESULT_CHUNKED_BODY_SIZE_MISMATCH: {
+ template_list_state->set_text(TTR("Can't connect."));
+ } break;
+ case HTTPRequest::RESULT_SSL_HANDSHAKE_ERROR:
+ case HTTPRequest::RESULT_CANT_CONNECT: {
+ template_list_state->set_text(TTR("Can't connect."));
+ } break;
+ case HTTPRequest::RESULT_NO_RESPONSE: {
+ template_list_state->set_text(TTR("No response."));
+ } break;
+ case HTTPRequest::RESULT_REQUEST_FAILED: {
+ template_list_state->set_text(TTR("Req. Failed."));
+ } break;
+ case HTTPRequest::RESULT_REDIRECT_LIMIT_REACHED: {
+ template_list_state->set_text(TTR("Redirect Loop."));
+ } break;
+ default: {
+ if (p_code != 200) {
+ template_list_state->set_text(TTR("Failed:") + " " + itos(p_code));
+ } else {
+ String path = EditorSettings::get_singleton()->get_settings_path().plus_file("tmp").plus_file("tmp_templates.tpz");
+ FileAccess *f = FileAccess::open(path, FileAccess::WRITE);
+ if (!f) {
+ template_list_state->set_text(TTR("Can't write file."));
+ } else {
+ int size = p_data.size();
+ PoolVector<uint8_t>::Read r = p_data.read();
+ f->store_buffer(r.ptr(), size);
+ memdelete(f);
+ template_list_state->set_text(TTR("Download Complete."));
+ template_downloader->hide();
+ _install_from_file(path);
+ }
+ }
+ } break;
+ }
+
+ set_process(false);
+}
+
+void ExportTemplateManager::_begin_template_download(const String &p_url) {
+
+ for (int i = 0; i < template_list->get_child_count(); i++) {
+ BaseButton *b = Object::cast_to<BaseButton>(template_list->get_child(0));
+ if (b) {
+ b->set_disabled(true);
+ }
+ }
+
+ download_data.clear();
+
+ Error err = download_templates->request(p_url);
+ if (err != OK) {
+ EditorNode::get_singleton()->show_warning(TTR("Error requesting url: ") + p_url);
+ return;
+ }
+
+ set_process(true);
+
+ template_list_state->show();
+ template_download_progress->set_max(100);
+ template_download_progress->set_value(0);
+ template_download_progress->show();
+ template_list_state->set_text(TTR("Connecting to Mirror.."));
+}
+
+void ExportTemplateManager::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_PROCESS) {
+
+ update_countdown -= get_process_delta_time();
+
+ if (update_countdown > 0) {
+ return;
+ }
+ update_countdown = 0.5;
+ String status;
+ bool errored = false;
+
+ switch (download_templates->get_http_client_status()) {
+ case HTTPClient::STATUS_DISCONNECTED:
+ status = TTR("Disconnected");
+ errored = true;
+ break;
+ case HTTPClient::STATUS_RESOLVING: status = TTR("Resolving"); break;
+ case HTTPClient::STATUS_CANT_RESOLVE:
+ status = TTR("Can't Resolve");
+ errored = true;
+ break;
+ case HTTPClient::STATUS_CONNECTING: status = TTR("Connecting.."); break;
+ case HTTPClient::STATUS_CANT_CONNECT:
+ status = TTR("Can't Conect");
+ errored = true;
+ break;
+ case HTTPClient::STATUS_CONNECTED: status = TTR("Connected"); break;
+ case HTTPClient::STATUS_REQUESTING: status = TTR("Requesting.."); break;
+ case HTTPClient::STATUS_BODY:
+ status = TTR("Downloading");
+ if (download_templates->get_body_size() > 0) {
+ status += " " + String::humanize_size(download_templates->get_downloaded_bytes()) + "/" + String::humanize_size(download_templates->get_body_size());
+ template_download_progress->set_max(download_templates->get_body_size());
+ template_download_progress->set_value(download_templates->get_downloaded_bytes());
+ } else {
+ status += " " + String::humanize_size(download_templates->get_downloaded_bytes());
+ }
+ break;
+ case HTTPClient::STATUS_CONNECTION_ERROR:
+ status = TTR("Connection Error");
+ errored = true;
+ break;
+ case HTTPClient::STATUS_SSL_HANDSHAKE_ERROR:
+ status = TTR("SSL Handshake Error");
+ errored = true;
+ break;
+ }
+
+ template_list_state->set_text(status);
+ if (errored) {
+ set_process(false);
+ ;
+ }
+ }
+
+ if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
+ if (!is_visible_in_tree()) {
+ print_line("closed");
+ download_templates->cancel_request();
+ set_process(false);
+ }
+ }
+}
+
void ExportTemplateManager::_bind_methods() {
ClassDB::bind_method("_download_template", &ExportTemplateManager::_download_template);
ClassDB::bind_method("_uninstall_template", &ExportTemplateManager::_uninstall_template);
ClassDB::bind_method("_uninstall_template_confirm", &ExportTemplateManager::_uninstall_template_confirm);
ClassDB::bind_method("_install_from_file", &ExportTemplateManager::_install_from_file);
+ ClassDB::bind_method("_http_download_mirror_completed", &ExportTemplateManager::_http_download_mirror_completed);
+ ClassDB::bind_method("_http_download_templates_completed", &ExportTemplateManager::_http_download_templates_completed);
+ ClassDB::bind_method("_begin_template_download", &ExportTemplateManager::_begin_template_download);
}
ExportTemplateManager::ExportTemplateManager() {
@@ -350,4 +543,33 @@ ExportTemplateManager::ExportTemplateManager() {
set_title(TTR("Export Template Manager"));
set_hide_on_ok(false);
+
+ request_mirror = memnew(HTTPRequest);
+ add_child(request_mirror);
+ request_mirror->connect("request_completed", this, "_http_download_mirror_completed");
+
+ download_templates = memnew(HTTPRequest);
+ add_child(download_templates);
+ download_templates->connect("request_completed", this, "_http_download_templates_completed");
+
+ template_downloader = memnew(AcceptDialog);
+ template_downloader->set_title(TTR("Download Templates"));
+ template_downloader->get_ok()->set_text(TTR("Close"));
+ add_child(template_downloader);
+
+ VBoxContainer *vbc = memnew(VBoxContainer);
+ template_downloader->add_child(vbc);
+ ScrollContainer *sc = memnew(ScrollContainer);
+ sc->set_custom_minimum_size(Size2(400, 200) * EDSCALE);
+ vbc->add_margin_child(TTR("Select mirror from list: "), sc);
+ template_list = memnew(VBoxContainer);
+ sc->add_child(template_list);
+ sc->set_enable_v_scroll(true);
+ sc->set_enable_h_scroll(false);
+ template_list_state = memnew(Label);
+ vbc->add_child(template_list_state);
+ template_download_progress = memnew(ProgressBar);
+ vbc->add_child(template_download_progress);
+
+ update_countdown = 0;
}
diff --git a/editor/export_template_manager.h b/editor/export_template_manager.h
index c77f85688f..644c6b466b 100644
--- a/editor/export_template_manager.h
+++ b/editor/export_template_manager.h
@@ -33,13 +33,20 @@
#include "editor/editor_settings.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/file_dialog.h"
+#include "scene/gui/progress_bar.h"
#include "scene/gui/scroll_container.h"
+#include "scene/main/http_request.h"
class ExportTemplateVersion;
class ExportTemplateManager : public ConfirmationDialog {
GDCLASS(ExportTemplateManager, ConfirmationDialog)
+ AcceptDialog *template_downloader;
+ VBoxContainer *template_list;
+ Label *template_list_state;
+ ProgressBar *template_download_progress;
+
ScrollContainer *installed_scroll;
VBoxContainer *installed_vb;
HBoxContainer *current_hb;
@@ -48,6 +55,13 @@ class ExportTemplateManager : public ConfirmationDialog {
ConfirmationDialog *remove_confirm;
String to_remove;
+ HTTPRequest *request_mirror;
+ HTTPRequest *download_templates;
+
+ Vector<uint8_t> download_data;
+
+ float update_countdown;
+
void _update_template_list();
void _download_template(const String &p_version);
@@ -57,7 +71,13 @@ class ExportTemplateManager : public ConfirmationDialog {
virtual void ok_pressed();
void _install_from_file(const String &p_file);
+ void _http_download_mirror_completed(int p_status, int p_code, const PoolStringArray &headers, const PoolByteArray &p_data);
+ void _http_download_templates_completed(int p_status, int p_code, const PoolStringArray &headers, const PoolByteArray &p_data);
+
+ void _begin_template_download(const String &p_url);
+
protected:
+ void _notification(int p_what);
static void _bind_methods();
public:
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index 62d77f56bc..00cbd9bb72 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -595,10 +595,10 @@ void FileSystemDock::_select_file(int p_idx) {
current_path->set_text(path);
_push_to_history();
} else {
- if (ResourceLoader::get_resource_type(path) == "PackedScene") {
- editor->open_request(path);
+ if (ResourceLoader::get_resource_type(fpath) == "PackedScene") {
+ editor->open_request(fpath);
} else {
- editor->load_resource(path);
+ editor->load_resource(fpath);
}
}
}
diff --git a/editor/icons/icon_editor_handle_add.svg b/editor/icons/icon_editor_handle_add.svg
new file mode 100644
index 0000000000..0e7fe7129a
--- /dev/null
+++ b/editor/icons/icon_editor_handle_add.svg
@@ -0,0 +1,6 @@
+<svg width="8" height="8" version="1.1" viewBox="0 0 8 8" xmlns="http://www.w3.org/2000/svg">
+ <g transform="translate(0 -1044.4)">
+ <ellipse cx="4" cy="1048.4" rx="4" ry="4" fill="#fff"/>
+ <ellipse cx="4" cy="1048.4" rx="2.8572" ry="2.8571" fill="#84ff84"/>
+ </g>
+</svg>
diff --git a/editor/icons/icon_editor_handle_selected.svg b/editor/icons/icon_editor_handle_selected.svg
new file mode 100644
index 0000000000..8d338c1fbd
--- /dev/null
+++ b/editor/icons/icon_editor_handle_selected.svg
@@ -0,0 +1,6 @@
+<svg width="8" height="8" version="1.1" viewBox="0 0 8 8" xmlns="http://www.w3.org/2000/svg">
+ <g transform="translate(0 -1044.4)">
+ <ellipse cx="4" cy="1048.4" rx="4" ry="4" fill="#fff"/>
+ <ellipse cx="4" cy="1048.4" rx="2.8572" ry="2.8571" fill="#8484ff"/>
+ </g>
+</svg>
diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp
index ffa4e36b5a..2f839b96cf 100644
--- a/editor/plugins/abstract_polygon_2d_editor.cpp
+++ b/editor/plugins/abstract_polygon_2d_editor.cpp
@@ -30,6 +30,49 @@
#include "abstract_polygon_2d_editor.h"
#include "canvas_item_editor_plugin.h"
+#include "core/os/keyboard.h"
+
+AbstractPolygon2DEditor::Vertex::Vertex()
+ : polygon(-1), vertex(-1) {
+ // invalid vertex
+}
+
+AbstractPolygon2DEditor::Vertex::Vertex(int p_vertex)
+ : polygon(-1), vertex(p_vertex) {
+ // vertex p_vertex of current wip polygon
+}
+
+AbstractPolygon2DEditor::Vertex::Vertex(int p_polygon, int p_vertex)
+ : polygon(p_polygon), vertex(p_vertex) {
+ // vertex p_vertex of polygon p_polygon
+}
+
+bool AbstractPolygon2DEditor::Vertex::operator==(const AbstractPolygon2DEditor::Vertex &p_vertex) const {
+
+ return polygon == p_vertex.polygon && vertex == p_vertex.vertex;
+}
+
+bool AbstractPolygon2DEditor::Vertex::operator!=(const AbstractPolygon2DEditor::Vertex &p_vertex) const {
+
+ return !(*this == p_vertex);
+}
+
+bool AbstractPolygon2DEditor::Vertex::valid() const {
+
+ return vertex >= 0;
+}
+
+AbstractPolygon2DEditor::PosVertex::PosVertex() {
+ // invalid vertex
+}
+
+AbstractPolygon2DEditor::PosVertex::PosVertex(const Vertex &p_vertex, const Vector2 &p_pos)
+ : Vertex(p_vertex.polygon, p_vertex.vertex), pos(p_pos) {
+}
+
+AbstractPolygon2DEditor::PosVertex::PosVertex(int p_polygon, int p_vertex, const Vector2 &p_pos)
+ : Vertex(p_polygon, p_vertex), pos(p_pos) {
+}
bool AbstractPolygon2DEditor::_is_empty() const {
@@ -171,7 +214,10 @@ void AbstractPolygon2DEditor::_wip_close() {
wip.clear();
wip_active = false;
- edited_point = -1;
+
+ edited_point = PosVertex();
+ hover_point = Vertex();
+ selected_point = Vertex();
}
bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
@@ -197,9 +243,6 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event)
Vector2 gpoint = mb->get_position();
Vector2 cpoint = _get_node()->get_global_transform().affine_inverse().xform(canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(mb->get_position())));
- //first check if a point is to be added (segment split)
- real_t grab_threshold = EDITOR_DEF("editors/poly_editor/point_grab_radius", 8);
-
if (mode == MODE_CREATE) {
if (mb->get_button_index() == BUTTON_LEFT && mb->is_pressed()) {
@@ -209,13 +252,16 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event)
wip.clear();
wip.push_back(cpoint);
wip_active = true;
- edited_point_pos = cpoint;
- edited_polygon = -1;
- edited_point = 1;
+ edited_point = PosVertex(-1, 1, cpoint);
canvas_item_editor->get_viewport_control()->update();
+ hover_point = Vertex();
+ selected_point = Vertex(0);
+ edge_point = PosVertex();
return true;
} else {
+ const real_t grab_threshold = EDITOR_DEF("editors/poly_editor/point_grab_radius", 8);
+
if (wip.size() > 1 && xform.xform(wip[0]).distance_to(gpoint) < grab_threshold) {
//wip closed
_wip_close();
@@ -224,7 +270,8 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event)
} else {
wip.push_back(cpoint);
- edited_point = wip.size();
+ edited_point = PosVertex(-1, wip.size(), cpoint);
+ selected_point = Vertex(wip.size() - 1);
canvas_item_editor->get_viewport_control()->update();
return true;
@@ -240,66 +287,31 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event)
if (mb->is_pressed()) {
- if (mb->get_control()) {
-
- const int n_polygons = _get_polygon_count();
+ const PosVertex insert = closest_edge_point(gpoint);
- if (n_polygons >= 1) {
+ if (insert.valid()) {
- Vector<Vector2> vertices = _get_polygon(n_polygons - 1);
+ Vector<Vector2> vertices = _get_polygon(insert.polygon);
- if (vertices.size() < 3) {
-
- vertices.push_back(cpoint);
- undo_redo->create_action(TTR("Edit Poly"));
- _action_set_polygon(n_polygons - 1, vertices);
- _commit_action();
- return true;
- }
- }
-
- //search edges
- int closest_poly = -1;
- int closest_idx = -1;
- Vector2 closest_pos;
- real_t closest_dist = 1e10;
-
- for (int j = 0; j < n_polygons; j++) {
-
- PoolVector<Vector2> points = _get_polygon(j);
- const Vector2 offset = _get_offset(j);
- const int n_points = points.size();
-
- for (int i = 0; i < n_points; i++) {
-
- Vector2 p[2] = { xform.xform(points[i] + offset),
- xform.xform(points[(i + 1) % n_points] + offset) };
-
- Vector2 cp = Geometry::get_closest_point_to_segment_2d(gpoint, p);
- if (cp.distance_squared_to(points[0]) < CMP_EPSILON2 || cp.distance_squared_to(points[1]) < CMP_EPSILON2)
- continue; //not valid to reuse point
-
- real_t d = cp.distance_to(gpoint);
- if (d < closest_dist && d < grab_threshold) {
- closest_poly = j;
- closest_dist = d;
- closest_pos = cp;
- closest_idx = i;
- }
- }
- }
+ if (vertices.size() < 3) {
- if (closest_idx >= 0) {
+ vertices.push_back(cpoint);
+ undo_redo->create_action(TTR("Edit Poly"));
+ selected_point = Vertex(insert.polygon, vertices.size());
+ _action_set_polygon(insert.polygon, vertices);
+ _commit_action();
+ return true;
+ } else {
- Vector<Vector2> vertices = _get_polygon(closest_poly);
+ Vector<Vector2> vertices = _get_polygon(insert.polygon);
pre_move_edit = vertices;
- vertices.insert(closest_idx + 1, xform.affine_inverse().xform(closest_pos));
- edited_point = closest_idx + 1;
- edited_polygon = closest_poly;
- edited_point_pos = xform.affine_inverse().xform(closest_pos);
+ edited_point = PosVertex(insert.polygon, insert.vertex + 1, xform.affine_inverse().xform(insert.pos));
+ vertices.insert(edited_point.vertex, edited_point.pos);
+ selected_point = edited_point;
+ edge_point = PosVertex();
undo_redo->create_action(TTR("Insert Point"));
- _action_set_polygon(closest_poly, vertices);
+ _action_set_polygon(insert.polygon, vertices);
_commit_action();
return true;
@@ -307,134 +319,120 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event)
} else {
//look for points to move
- int closest_poly = -1;
- int closest_idx = -1;
- Vector2 closest_pos;
- real_t closest_dist = 1e10;
-
- const int n_polygons = _get_polygon_count();
-
- for (int j = 0; j < n_polygons; j++) {
-
- PoolVector<Vector2> points = _get_polygon(j);
- const Vector2 offset = _get_offset(j);
- const int n_points = points.size();
-
- for (int i = 0; i < n_points; i++) {
-
- Vector2 cp = xform.xform(points[i] + offset);
-
- real_t d = cp.distance_to(gpoint);
- if (d < closest_dist && d < grab_threshold) {
- closest_poly = j;
- closest_dist = d;
- closest_pos = cp;
- closest_idx = i;
- }
- }
- }
+ const PosVertex closest = closest_point(gpoint);
- if (closest_idx >= 0) {
+ if (closest.valid()) {
- pre_move_edit = _get_polygon(closest_poly);
- edited_polygon = closest_poly;
- edited_point = closest_idx;
- edited_point_pos = xform.affine_inverse().xform(closest_pos);
+ pre_move_edit = _get_polygon(closest.polygon);
+ edited_point = PosVertex(closest, xform.affine_inverse().xform(closest.pos));
+ selected_point = closest;
+ edge_point = PosVertex();
canvas_item_editor->get_viewport_control()->update();
return true;
+ } else {
+
+ selected_point = Vertex();
}
}
} else {
- if (edited_point != -1) {
+ if (edited_point.valid()) {
//apply
- Vector<Vector2> vertices = _get_polygon(edited_polygon);
- ERR_FAIL_INDEX_V(edited_point, vertices.size(), false);
- vertices[edited_point] = edited_point_pos - _get_offset(edited_polygon);
+ Vector<Vector2> vertices = _get_polygon(edited_point.polygon);
+ ERR_FAIL_INDEX_V(edited_point.vertex, vertices.size(), false);
+ vertices[edited_point.vertex] = edited_point.pos - _get_offset(edited_point.polygon);
undo_redo->create_action(TTR("Edit Poly"));
- _action_set_polygon(edited_polygon, pre_move_edit, vertices);
+ _action_set_polygon(edited_point.polygon, pre_move_edit, vertices);
_commit_action();
- edited_point = -1;
+ edited_point = PosVertex();
return true;
}
}
- } else if (mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed() && edited_point == -1) {
+ } else if (mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed() && !edited_point.valid()) {
+
+ const PosVertex closest = closest_point(gpoint);
+
+ if (closest.valid()) {
+
+ remove_point(closest);
+ return true;
+ }
+ }
+ }
+ }
- int closest_poly = -1;
- int closest_idx = -1;
- Vector2 closest_pos;
- real_t closest_dist = 1e10;
- const int n_polygons = _get_polygon_count();
+ Ref<InputEventMouseMotion> mm = p_event;
- for (int j = 0; j < n_polygons; j++) {
+ if (mm.is_valid()) {
- PoolVector<Vector2> points = _get_polygon(j);
- const int n_points = points.size();
- const Vector2 offset = _get_offset(j);
+ Vector2 gpoint = mm->get_position();
- for (int i = 0; i < n_points; i++) {
+ if (edited_point.valid() && (wip_active || (mm->get_button_mask() & BUTTON_MASK_LEFT))) {
- Vector2 cp = xform.xform(points[i] + offset);
+ Vector2 cpoint = _get_node()->get_global_transform().affine_inverse().xform(canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint)));
+ edited_point = PosVertex(edited_point, cpoint);
- real_t d = cp.distance_to(gpoint);
- if (d < closest_dist && d < grab_threshold) {
- closest_poly = j;
- closest_dist = d;
- closest_pos = cp;
- closest_idx = i;
- }
- }
- }
+ if (!wip_active) {
- if (closest_idx >= 0) {
+ Vector<Vector2> vertices = _get_polygon(edited_point.polygon);
+ ERR_FAIL_INDEX_V(edited_point.vertex, vertices.size(), false);
+ vertices[edited_point.vertex] = cpoint - _get_offset(edited_point.polygon);
+ _set_polygon(edited_point.polygon, vertices);
+ }
- PoolVector<Vector2> vertices = _get_polygon(closest_poly);
+ canvas_item_editor->get_viewport_control()->update();
+ } else if (mode == MODE_EDIT) {
- if (vertices.size() > 3) {
+ const PosVertex onEdgeVertex = closest_edge_point(gpoint);
- vertices.remove(closest_idx);
+ if (onEdgeVertex.valid()) {
- undo_redo->create_action(TTR("Edit Poly (Remove Point)"));
- _action_set_polygon(closest_poly, vertices);
- _commit_action();
- } else {
+ hover_point = Vertex();
+ edge_point = onEdgeVertex;
+ canvas_item_editor->get_viewport_control()->update();
+ } else {
- undo_redo->create_action(TTR("Remove Poly And Point"));
- _action_remove_polygon(closest_poly);
- _commit_action();
- }
+ if (edge_point.valid()) {
- if (_is_empty())
- _menu_option(MODE_CREATE);
- return true;
+ edge_point = PosVertex();
+ canvas_item_editor->get_viewport_control()->update();
+ }
+
+ const PosVertex new_hover_point = closest_point(gpoint);
+ if (hover_point != new_hover_point) {
+
+ hover_point = new_hover_point;
+ canvas_item_editor->get_viewport_control()->update();
}
}
}
}
- Ref<InputEventMouseMotion> mm = p_event;
+ Ref<InputEventKey> k = p_event;
- if (mm.is_valid()) {
+ if (k.is_valid() && k->is_pressed() && (k->get_scancode() == KEY_DELETE || k->get_scancode() == KEY_BACKSPACE)) {
+ if (wip_active && selected_point.polygon == -1) {
- if (edited_point != -1 && (wip_active || (mm->get_button_mask() & BUTTON_MASK_LEFT))) {
+ if (wip.size() > selected_point.vertex) {
- Vector2 gpoint = mm->get_position();
- Vector2 cpoint = _get_node()->get_global_transform().affine_inverse().xform(canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(mm->get_position())));
- edited_point_pos = cpoint;
+ wip.remove(selected_point.vertex);
+ selected_point = wip.size() - 1;
+ canvas_item_editor->get_viewport_control()->update();
+ return true;
+ }
+ } else {
- if (!wip_active) {
+ const Vertex active_point = get_active_point();
- Vector<Vector2> vertices = _get_polygon(edited_polygon);
- ERR_FAIL_INDEX_V(edited_point, vertices.size(), false);
- vertices[edited_point] = cpoint - _get_offset(edited_polygon);
- _set_polygon(edited_polygon, vertices);
- }
+ if (active_point.valid()) {
- canvas_item_editor->get_viewport_control()->update();
+ remove_point(active_point);
+ return true;
+ }
}
}
@@ -448,7 +446,10 @@ void AbstractPolygon2DEditor::forward_draw_over_canvas(Control *p_canvas) {
Control *vpc = canvas_item_editor->get_viewport_control();
Transform2D xform = canvas_item_editor->get_canvas_transform() * _get_node()->get_global_transform();
- Ref<Texture> handle = get_icon("EditorHandle", "EditorIcons");
+ Ref<Texture> default_handle = get_icon("EditorHandle", "EditorIcons");
+ Ref<Texture> selected_handle = get_icon("EditorHandleSelected", "EditorIcons");
+
+ const Vertex active_point = get_active_point();
const int n_polygons = _get_polygon_count();
for (int j = -1; j < n_polygons; j++) {
@@ -459,7 +460,7 @@ void AbstractPolygon2DEditor::forward_draw_over_canvas(Control *p_canvas) {
PoolVector<Vector2> points;
Vector2 offset;
- if (wip_active && j == edited_polygon) {
+ if (wip_active && j == edited_point.polygon) {
points = Variant(wip);
offset = Vector2(0, 0);
@@ -471,7 +472,7 @@ void AbstractPolygon2DEditor::forward_draw_over_canvas(Control *p_canvas) {
offset = _get_offset(j);
}
- if (!wip_active && j == edited_polygon && edited_point >= 0 && EDITOR_DEF("editors/poly_editor/show_previous_outline", true)) {
+ if (!wip_active && j == edited_point.polygon && EDITOR_DEF("editors/poly_editor/show_previous_outline", true)) {
const Color col = Color(0.5, 0.5, 0.5); // FIXME polygon->get_outline_color();
const int n = pre_move_edit.size();
@@ -493,10 +494,12 @@ void AbstractPolygon2DEditor::forward_draw_over_canvas(Control *p_canvas) {
for (int i = 0; i < n_points; i++) {
+ const Vertex vertex(j, i);
+
Vector2 p, p2;
- p = (j == edited_polygon && i == edited_point) ? edited_point_pos : (points[i] + offset);
- if (j == edited_polygon && ((wip_active && i == n_points - 1) || (((i + 1) % n_points) == edited_point)))
- p2 = edited_point_pos;
+ p = (vertex == edited_point) ? edited_point.pos : (points[i] + offset);
+ if (j == edited_point.polygon && ((wip_active && i == n_points - 1) || (((i + 1) % n_points) == edited_point.vertex)))
+ p2 = edited_point.pos;
else
p2 = points[(i + 1) % n_points] + offset;
@@ -504,9 +507,16 @@ void AbstractPolygon2DEditor::forward_draw_over_canvas(Control *p_canvas) {
Vector2 next_point = xform.xform(p2);
vpc->draw_line(point, next_point, col, 2);
+ Ref<Texture> handle = vertex == active_point ? selected_handle : default_handle;
vpc->draw_texture(handle, point - handle->get_size() * 0.5);
}
}
+
+ if (edge_point.valid()) {
+
+ Ref<Texture> add_handle = get_icon("EditorHandleAdd", "EditorIcons");
+ vpc->draw_texture(add_handle, edge_point.pos - add_handle->get_size() * 0.5);
+ }
}
void AbstractPolygon2DEditor::edit(Node *p_polygon) {
@@ -525,7 +535,9 @@ void AbstractPolygon2DEditor::edit(Node *p_polygon) {
wip.clear();
wip_active = false;
- edited_point = -1;
+ edited_point = PosVertex();
+ hover_point = Vertex();
+ selected_point = Vertex();
canvas_item_editor->get_viewport_control()->update();
@@ -542,6 +554,107 @@ void AbstractPolygon2DEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_create_resource"), &AbstractPolygon2DEditor::_create_resource);
}
+void AbstractPolygon2DEditor::remove_point(const Vertex &p_vertex) {
+
+ PoolVector<Vector2> vertices = _get_polygon(p_vertex.polygon);
+
+ if (vertices.size() > 3) {
+
+ vertices.remove(p_vertex.vertex);
+
+ undo_redo->create_action(TTR("Edit Poly (Remove Point)"));
+ _action_set_polygon(p_vertex.polygon, vertices);
+ _commit_action();
+ } else {
+
+ undo_redo->create_action(TTR("Remove Poly And Point"));
+ _action_remove_polygon(p_vertex.polygon);
+ _commit_action();
+ }
+
+ if (_is_empty())
+ _menu_option(MODE_CREATE);
+
+ hover_point = Vertex();
+ if (selected_point == p_vertex)
+ selected_point = Vertex();
+}
+
+AbstractPolygon2DEditor::Vertex AbstractPolygon2DEditor::get_active_point() const {
+
+ return hover_point.valid() ? hover_point : selected_point;
+}
+
+AbstractPolygon2DEditor::PosVertex AbstractPolygon2DEditor::closest_point(const Vector2 &p_pos) const {
+
+ const real_t grab_threshold = EDITOR_DEF("editors/poly_editor/point_grab_radius", 8);
+
+ const int n_polygons = _get_polygon_count();
+ const Transform2D xform = canvas_item_editor->get_canvas_transform() * _get_node()->get_global_transform();
+
+ PosVertex closest;
+ real_t closest_dist = 1e10;
+
+ for (int j = 0; j < n_polygons; j++) {
+
+ PoolVector<Vector2> points = _get_polygon(j);
+ const Vector2 offset = _get_offset(j);
+ const int n_points = points.size();
+
+ for (int i = 0; i < n_points; i++) {
+
+ Vector2 cp = xform.xform(points[i] + offset);
+
+ real_t d = cp.distance_to(p_pos);
+ if (d < closest_dist && d < grab_threshold) {
+ closest_dist = d;
+ closest = PosVertex(j, i, cp);
+ }
+ }
+ }
+
+ return closest;
+}
+
+AbstractPolygon2DEditor::PosVertex AbstractPolygon2DEditor::closest_edge_point(const Vector2 &p_pos) const {
+
+ const real_t grab_threshold = EDITOR_DEF("editors/poly_editor/point_grab_radius", 8);
+ const real_t eps = grab_threshold * 2;
+ const real_t eps2 = eps * eps;
+
+ const int n_polygons = _get_polygon_count();
+ const Transform2D xform = canvas_item_editor->get_canvas_transform() * _get_node()->get_global_transform();
+
+ PosVertex closest;
+ real_t closest_dist = 1e10;
+
+ for (int j = 0; j < n_polygons; j++) {
+
+ PoolVector<Vector2> points = _get_polygon(j);
+ const Vector2 offset = _get_offset(j);
+ const int n_points = points.size();
+
+ for (int i = 0; i < n_points; i++) {
+
+ Vector2 segment[2] = { xform.xform(points[i] + offset),
+ xform.xform(points[(i + 1) % n_points] + offset) };
+
+ Vector2 cp = Geometry::get_closest_point_to_segment_2d(p_pos, segment);
+
+ if (cp.distance_squared_to(segment[0]) < eps2 || cp.distance_squared_to(segment[1]) < eps2)
+ continue; //not valid to reuse point
+
+ real_t d = cp.distance_to(p_pos);
+ if (d < closest_dist && d < grab_threshold) {
+ closest_dist = d;
+ closest = PosVertex(j, i, cp);
+ }
+ }
+ }
+
+ return closest;
+}
+
AbstractPolygon2DEditor::AbstractPolygon2DEditor(EditorNode *p_editor, bool p_wip_destructive) {
canvas_item_editor = NULL;
@@ -549,9 +662,13 @@ AbstractPolygon2DEditor::AbstractPolygon2DEditor(EditorNode *p_editor, bool p_wi
undo_redo = editor->get_undo_redo();
wip_active = false;
- edited_polygon = -1;
+ edited_point = PosVertex();
wip_destructive = p_wip_destructive;
+ hover_point = Vertex();
+ selected_point = Vertex();
+ edge_point = PosVertex();
+
add_child(memnew(VSeparator));
button_create = memnew(ToolButton);
add_child(button_create);
diff --git a/editor/plugins/abstract_polygon_2d_editor.h b/editor/plugins/abstract_polygon_2d_editor.h
index 3e3bff6d0d..8dd22958db 100644
--- a/editor/plugins/abstract_polygon_2d_editor.h
+++ b/editor/plugins/abstract_polygon_2d_editor.h
@@ -47,9 +47,33 @@ class AbstractPolygon2DEditor : public HBoxContainer {
ToolButton *button_create;
ToolButton *button_edit;
- int edited_polygon;
- int edited_point;
- Vector2 edited_point_pos;
+ struct Vertex {
+ Vertex();
+ Vertex(int p_vertex);
+ Vertex(int p_polygon, int p_vertex);
+
+ bool operator==(const Vertex &p_vertex) const;
+ bool operator!=(const Vertex &p_vertex) const;
+
+ bool valid() const;
+
+ int polygon;
+ int vertex;
+ };
+
+ struct PosVertex : public Vertex {
+ PosVertex();
+ PosVertex(const Vertex &p_vertex, const Vector2 &p_pos);
+ PosVertex(int p_polygon, int p_vertex, const Vector2 &p_pos);
+
+ Vector2 pos;
+ };
+
+ PosVertex edited_point;
+ Vertex hover_point; // point under mouse cursor
+ Vertex selected_point; // currently selected
+ PosVertex edge_point; // adding an edge point?
+
Vector<Vector2> pre_move_edit;
Vector<Vector2> wip;
bool wip_active;
@@ -80,6 +104,11 @@ protected:
void _node_removed(Node *p_node);
static void _bind_methods();
+ void remove_point(const Vertex &p_vertex);
+ Vertex get_active_point() const;
+ PosVertex closest_point(const Vector2 &p_pos) const;
+ PosVertex closest_edge_point(const Vector2 &p_pos) const;
+
bool _is_empty() const;
void _commit_action();
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 34c0a3d439..3807c8961a 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -248,7 +248,7 @@ void CanvasItemEditor::_snap_other_nodes(Point2 p_value, Point2 &r_current_snap,
if (canvas_item && (!p_to_snap || p_current != p_to_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(), 360.0) == 0.0) {
+ if (fmod(ci_transform.get_rotation() - to_snap_transform.get_rotation(), (real_t)360.0) == 0.0) {
Point2 begin = ci_transform.xform(canvas_item->get_item_rect().get_position());
Point2 end = ci_transform.xform(canvas_item->get_item_rect().get_position() + canvas_item->get_item_rect().get_size());
@@ -318,7 +318,7 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, const
_snap_other_nodes(p_target, output, snapped, get_tree()->get_edited_scene_root(), p_canvas_item);
}
- if (((snap_active && snap_guides && (p_modes & SNAP_GUIDES)) || (p_forced_modes & SNAP_GUIDES)) && fmod(rotation, 360.0) == 0.0) {
+ if (((snap_active && snap_guides && (p_modes & SNAP_GUIDES)) || (p_forced_modes & SNAP_GUIDES)) && fmod(rotation, (real_t)360.0) == 0.0) {
// Guides
if (EditorNode::get_singleton()->get_edited_scene() && EditorNode::get_singleton()->get_edited_scene()->has_meta("_edit_vertical_guides_")) {
Array vguides = EditorNode::get_singleton()->get_edited_scene()->get_meta("_edit_vertical_guides_");
@@ -335,7 +335,7 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, const
}
}
- if (((snap_active && snap_grid && (p_modes & SNAP_GRID)) || (p_forced_modes & SNAP_GRID)) && fmod(rotation, 360.0) == 0.0) {
+ if (((snap_active && snap_grid && (p_modes & SNAP_GRID)) || (p_forced_modes & SNAP_GRID)) && fmod(rotation, (real_t)360.0) == 0.0) {
// Grid
Point2 offset = grid_offset;
if (snap_relative) {
diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp
index b8274122f8..b3bb103577 100644
--- a/editor/plugins/sprite_frames_editor_plugin.cpp
+++ b/editor/plugins/sprite_frames_editor_plugin.cpp
@@ -365,13 +365,11 @@ void SpriteFramesEditor::_animation_name_edited() {
}
void SpriteFramesEditor::_animation_add() {
- String new_name = "New Anim";
-
- String name = new_name;
+ String name = "New Anim";
int counter = 0;
while (frames->has_animation(name)) {
counter++;
- name = new_name + " " + itos(counter);
+ name = "New Anim " + itos(counter);
}
List<Node *> nodes;
@@ -393,7 +391,7 @@ void SpriteFramesEditor::_animation_add() {
undo_redo->add_undo_method(E->get(), "set_animation", current);
}
- edited_anim = new_name;
+ edited_anim = name;
undo_redo->commit_action();
}
diff --git a/methods.py b/methods.py
index b62dfc6544..2f3dac7e42 100644
--- a/methods.py
+++ b/methods.py
@@ -1149,7 +1149,10 @@ def build_gles3_headers(target, source, env):
build_legacygl_header(str(x), include="drivers/gles3/shader_gles3.h", class_suffix="GLES3", output_attribs=True)
-def update_version():
+def add_module_version_string(self,s):
+ self.module_version_string+="."+s
+
+def update_version(module_version_string=""):
rev = "custom_build"
@@ -1167,6 +1170,7 @@ def update_version():
f.write("#define VERSION_PATCH " + str(version.patch) + "\n")
f.write("#define VERSION_REVISION " + str(rev) + "\n")
f.write("#define VERSION_STATUS " + str(version.status) + "\n")
+ f.write("#define VERSION_MODULE_CONFIG \"" + str(version.module_config) + module_version_string + "\"\n")
import datetime
f.write("#define VERSION_YEAR " + str(datetime.datetime.now().year) + "\n")
f.close()
diff --git a/modules/gdnative/gdnative/basis.cpp b/modules/gdnative/gdnative/basis.cpp
index b1327cdaef..28af93f942 100644
--- a/modules/gdnative/gdnative/basis.cpp
+++ b/modules/gdnative/gdnative/basis.cpp
@@ -172,7 +172,7 @@ void GDAPI godot_basis_new_with_euler_quat(godot_basis *r_dest, const godot_quat
}
// p_elements is a pointer to an array of 3 (!!) vector3
-void GDAPI godot_basis_get_elements(godot_basis *p_self, godot_vector3 *p_elements) {
+void GDAPI godot_basis_get_elements(const godot_basis *p_self, godot_vector3 *p_elements) {
const Basis *self = (const Basis *)p_self;
Vector3 *elements = (Vector3 *)p_elements;
elements[0] = self->elements[0];
diff --git a/modules/gdnative/gdnative/string.cpp b/modules/gdnative/gdnative/string.cpp
index 905c513d9d..619003083d 100644
--- a/modules/gdnative/gdnative/string.cpp
+++ b/modules/gdnative/gdnative/string.cpp
@@ -65,11 +65,20 @@ void GDAPI godot_string_new_unicode_data(godot_string *r_dest, const wchar_t *p_
void GDAPI godot_string_get_data(const godot_string *p_self, char *p_dest, int *p_size) {
String *self = (String *)p_self;
- if (p_size != NULL) {
- *p_size = self->utf8().length();
- }
- if (p_dest != NULL) {
- memcpy(p_dest, self->utf8().get_data(), *p_size);
+
+ if (p_size) {
+ // we have a length pointer, that means we either want to know
+ // the length or want to write *p_size bytes into a buffer
+
+ CharString utf8_string = self->utf8();
+
+ int len = utf8_string.length();
+
+ if (p_dest) {
+ memcpy(p_dest, utf8_string.get_data(), *p_size);
+ } else {
+ *p_size = len;
+ }
}
}
@@ -78,6 +87,11 @@ wchar_t GDAPI *godot_string_operator_index(godot_string *p_self, const godot_int
return &(self->operator[](p_idx));
}
+wchar_t GDAPI godot_string_operator_index_const(const godot_string *p_self, const godot_int p_idx) {
+ const String *self = (const String *)p_self;
+ return self->operator[](p_idx);
+}
+
const char GDAPI *godot_string_c_str(const godot_string *p_self) {
const String *self = (const String *)p_self;
return self->utf8().get_data();
diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json
index 3a59b9ca1c..3a92afd7ab 100644
--- a/modules/gdnative/gdnative_api.json
+++ b/modules/gdnative/gdnative_api.json
@@ -847,7 +847,7 @@
"name": "godot_basis_get_elements",
"return_type": "void",
"arguments": [
- ["godot_basis *", "p_self"],
+ ["const godot_basis *", "p_self"],
["godot_vector3 *", "p_elements"]
]
},
@@ -3927,6 +3927,14 @@
]
},
{
+ "name": "godot_string_operator_index_const",
+ "return_type": "wchar_t",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_int", "p_idx"]
+ ]
+ },
+ {
"name": "godot_string_c_str",
"return_type": "const char *",
"arguments": [
diff --git a/modules/gdnative/include/gdnative/basis.h b/modules/gdnative/include/gdnative/basis.h
index b86b1c17d8..49ca765a01 100644
--- a/modules/gdnative/include/gdnative/basis.h
+++ b/modules/gdnative/include/gdnative/basis.h
@@ -97,7 +97,7 @@ void GDAPI godot_basis_new(godot_basis *r_dest);
void GDAPI godot_basis_new_with_euler_quat(godot_basis *r_dest, const godot_quat *p_euler);
// p_elements is a pointer to an array of 3 (!!) vector3
-void GDAPI godot_basis_get_elements(godot_basis *p_self, godot_vector3 *p_elements);
+void GDAPI godot_basis_get_elements(const godot_basis *p_self, godot_vector3 *p_elements);
godot_vector3 GDAPI godot_basis_get_axis(const godot_basis *p_self, const godot_int p_axis);
diff --git a/modules/gdnative/include/gdnative/string.h b/modules/gdnative/include/gdnative/string.h
index f30fdb8dc7..29510313c9 100644
--- a/modules/gdnative/include/gdnative/string.h
+++ b/modules/gdnative/include/gdnative/string.h
@@ -66,6 +66,7 @@ void GDAPI godot_string_new_unicode_data(godot_string *r_dest, const wchar_t *p_
void GDAPI godot_string_get_data(const godot_string *p_self, char *p_dest, int *p_size);
wchar_t GDAPI *godot_string_operator_index(godot_string *p_self, const godot_int p_idx);
+wchar_t GDAPI godot_string_operator_index_const(const godot_string *p_self, const godot_int p_idx);
const char GDAPI *godot_string_c_str(const godot_string *p_self);
const wchar_t GDAPI *godot_string_unicode_str(const godot_string *p_self);
diff --git a/modules/mono/config.py b/modules/mono/config.py
index 44eef45f76..7ad135e0b9 100644
--- a/modules/mono/config.py
+++ b/modules/mono/config.py
@@ -47,6 +47,7 @@ def copy_file_no_replace(src_dir, dst_dir, name):
def configure(env):
env.use_ptrcall = True
+ env.add_module_version_string("mono")
envvars = Variables()
envvars.Add(BoolVariable('mono_static', 'Statically link mono', False))
diff --git a/platform/iphone/detect.py b/platform/iphone/detect.py
index 00d8a59f74..993a93ff89 100644
--- a/platform/iphone/detect.py
+++ b/platform/iphone/detect.py
@@ -47,8 +47,8 @@ def configure(env):
if (env["target"].startswith("release")):
env.Append(CPPFLAGS=['-DNDEBUG', '-DNS_BLOCK_ASSERTIONS=1'])
- env.Append(CPPFLAGS=['-O2', '-flto', '-ftree-vectorize', '-fomit-frame-pointer', '-ffast-math', '-funsafe-math-optimizations'])
- env.Append(LINKFLAGS=['-O2', '-flto'])
+ env.Append(CPPFLAGS=['-O2', '-ftree-vectorize', '-fomit-frame-pointer', '-ffast-math', '-funsafe-math-optimizations'])
+ env.Append(LINKFLAGS=['-O2'])
if env["target"] == "release_debug":
env.Append(CPPFLAGS=['-DDEBUG_ENABLED'])
@@ -56,6 +56,10 @@ def configure(env):
elif (env["target"] == "debug"):
env.Append(CPPFLAGS=['-D_DEBUG', '-DDEBUG=1', '-gdwarf-2', '-O0', '-DDEBUG_ENABLED', '-DDEBUG_MEMORY_ENABLED'])
+ if (env["use_lto"]):
+ env.Append(CPPFLAGS=['-flto'])
+ env.Append(LINKFLAGS=['-flto'])
+
## Architecture
if env["ios_sim"] or env["arch"] == "x86": # i386, simulator
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index bac5df5668..fbb02c9d1b 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -64,7 +64,6 @@ def get_opts():
return [
('mingw_prefix_32', 'MinGW prefix (Win32)', mingw32),
('mingw_prefix_64', 'MinGW prefix (Win64)', mingw64),
- BoolVariable('use_lto', 'Use link time optimization (when using MingW)', False),
EnumVariable('debug_symbols', 'Add debug symbols to release version', 'yes', ('yes', 'no', 'full')),
]
diff --git a/platform/x11/detect.py b/platform/x11/detect.py
index 56bc1d4c59..6bd0ac8317 100644
--- a/platform/x11/detect.py
+++ b/platform/x11/detect.py
@@ -52,7 +52,6 @@ def get_opts():
BoolVariable('use_static_cpp', 'Link stdc++ statically', False),
BoolVariable('use_sanitizer', 'Use LLVM compiler address sanitizer', False),
BoolVariable('use_leak_sanitizer', 'Use LLVM compiler memory leaks sanitizer (implies use_sanitizer)', False),
- BoolVariable('use_lto', 'Use link time optimization', False),
BoolVariable('pulseaudio', 'Detect & use pulseaudio', True),
BoolVariable('udev', 'Use udev for gamepad connection callbacks', False),
EnumVariable('debug_symbols', 'Add debug symbols to release version', 'yes', ('yes', 'no', 'full')),
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 0f631c69b6..80b7748078 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -202,7 +202,6 @@ void AnimationPlayer::_notification(int p_what) {
if (!Engine::get_singleton()->is_editor_hint() && animation_set.has(autoplay)) {
play(autoplay);
- set_autoplay(""); //this line is the fix for autoplay issues with animatio
_animation_process(0);
}
} break;
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index ad519d8d0c..65a8350bd3 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -1195,8 +1195,9 @@ void RichTextLabel::add_newline() {
return;
ItemNewline *item = memnew(ItemNewline);
item->line = current_frame->lines.size();
- current_frame->lines.resize(current_frame->lines.size() + 1);
_add_item(item, false);
+ current_frame->lines.resize(current_frame->lines.size() + 1);
+ _invalidate_current_line(current_frame);
}
bool RichTextLabel::remove_line(const int p_line) {
diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp
index 1e1e4f2d5f..672e893f1b 100644
--- a/scene/main/http_request.cpp
+++ b/scene/main/http_request.cpp
@@ -579,6 +579,7 @@ HTTPRequest::HTTPRequest() {
client.instance();
use_threads = false;
thread_done = false;
+ downloaded = 0;
body_size_limit = -1;
file = NULL;
status = HTTPClient::STATUS_DISCONNECTED;
diff --git a/version.py b/version.py
index f0da206837..38847d68f5 100644
--- a/version.py
+++ b/version.py
@@ -3,3 +3,4 @@ name = "Godot Engine"
major = 3
minor = 0
status = "alpha"
+module_config = ""