summaryrefslogtreecommitdiff
path: root/editor
diff options
context:
space:
mode:
Diffstat (limited to 'editor')
-rw-r--r--editor/connections_dialog.cpp16
-rw-r--r--editor/create_dialog.cpp49
-rw-r--r--editor/editor_export.cpp5
-rw-r--r--editor/editor_file_system.cpp104
-rw-r--r--editor/editor_file_system.h14
-rw-r--r--editor/editor_inspector.cpp213
-rw-r--r--editor/editor_inspector.h32
-rw-r--r--editor/editor_node.cpp154
-rw-r--r--editor/editor_node.h16
-rw-r--r--editor/editor_properties.cpp299
-rw-r--r--editor/editor_properties.h31
-rw-r--r--editor/editor_sectioned_inspector.cpp306
-rw-r--r--editor/editor_sectioned_inspector.h42
-rw-r--r--editor/editor_settings.cpp26
-rw-r--r--editor/editor_settings.h7
-rw-r--r--editor/editor_spin_slider.cpp74
-rw-r--r--editor/editor_spin_slider.h9
-rw-r--r--editor/editor_themes.cpp35
-rw-r--r--editor/export_template_manager.cpp13
-rw-r--r--editor/filesystem_dock.cpp2
-rw-r--r--editor/icons/icon_GUI_tree_arrow_up.svg60
-rw-r--r--editor/icons/icon_animated_texture.svg74
-rw-r--r--editor/icons/icon_expand_bottom_dock.svg70
-rw-r--r--editor/icons/icon_g_l_e_s_2.svg69
-rw-r--r--editor/icons/icon_g_l_e_s_3.svg67
-rw-r--r--editor/icons/icon_new_root.svg69
-rw-r--r--editor/icons/icon_shrink_bottom_dock.svg71
-rw-r--r--editor/icons/icon_visual_shader.svg105
-rw-r--r--editor/icons/icon_vulkan.svg127
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.cpp2
-rw-r--r--editor/plugins/audio_stream_editor_plugin.cpp284
-rw-r--r--editor/plugins/audio_stream_editor_plugin.h93
-rw-r--r--editor/plugins/path_2d_editor_plugin.cpp55
-rw-r--r--editor/plugins/path_2d_editor_plugin.h12
-rw-r--r--editor/plugins/path_editor_plugin.cpp65
-rw-r--r--editor/plugins/path_editor_plugin.h19
-rw-r--r--editor/plugins/script_editor_plugin.cpp1
-rw-r--r--editor/plugins/shader_editor_plugin.cpp4
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp1217
-rw-r--r--editor/plugins/visual_shader_editor_plugin.h187
-rw-r--r--editor/project_export.cpp7
-rw-r--r--editor/project_settings_editor.cpp84
-rw-r--r--editor/project_settings_editor.h15
-rw-r--r--editor/scene_tree_dock.cpp121
-rw-r--r--editor/scene_tree_dock.h19
-rw-r--r--editor/settings_config_dialog.cpp70
-rw-r--r--editor/settings_config_dialog.h18
-rw-r--r--editor/spatial_editor_gizmos.cpp8
48 files changed, 4229 insertions, 211 deletions
diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp
index 7f93917744..8933fd7fe8 100644
--- a/editor/connections_dialog.cpp
+++ b/editor/connections_dialog.cpp
@@ -428,6 +428,13 @@ void ConnectionsDock::_make_or_edit_connection() {
bool oshot = connect_dialog->get_oneshot();
cToMake.flags = CONNECT_PERSIST | (defer ? CONNECT_DEFERRED : 0) | (oshot ? CONNECT_ONESHOT : 0);
+ bool add_script_function = connect_dialog->get_make_callback();
+ PoolStringArray script_function_args;
+ if (add_script_function) {
+ // pick up args here before "it" is deleted by update_tree
+ script_function_args = it->get_metadata(0).operator Dictionary()["args"];
+ }
+
if (connect_dialog->is_editing()) {
_disconnect(*it);
_connect(cToMake);
@@ -435,9 +442,12 @@ void ConnectionsDock::_make_or_edit_connection() {
_connect(cToMake);
}
- if (connect_dialog->get_make_callback()) {
- PoolStringArray args = it->get_metadata(0).operator Dictionary()["args"];
- editor->emit_signal("script_add_function_request", target, cToMake.method, args);
+ // IMPORTANT NOTE: _disconnect and _connect cause an update_tree,
+ // which will delete the object "it" is pointing to
+ it = NULL;
+
+ if (add_script_function) {
+ editor->emit_signal("script_add_function_request", target, cToMake.method, script_function_args);
hide();
}
diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp
index a8cbf52cd2..6b2a072e20 100644
--- a/editor/create_dialog.cpp
+++ b/editor/create_dialog.cpp
@@ -243,6 +243,18 @@ void CreateDialog::_update_search() {
_parse_fs(EditorFileSystem::get_singleton()->get_filesystem());
*/
+ List<StringName> global_classes;
+ ScriptServer::get_global_class_list(&global_classes);
+
+ Map<String, List<String> > global_class_map;
+ for (List<StringName>::Element *E = global_classes.front(); E; E = E->next()) {
+ String base = ScriptServer::get_global_class_base(E->get());
+ if (!global_class_map.has(base)) {
+ global_class_map[base] = List<String>();
+ }
+ global_class_map[base].push_back(E->get());
+ }
+
HashMap<String, TreeItem *> types;
TreeItem *root = search_options->create_item();
@@ -293,6 +305,32 @@ void CreateDialog::_update_search() {
add_type(I->get(), types, root, &to_select);
}
+ if (global_class_map.has(type) && ClassDB::is_parent_class(type, base_type)) {
+ for (List<String>::Element *J = global_class_map[type].front(); J; J = J->next()) {
+ bool show = search_box->get_text().is_subsequence_ofi(J->get());
+
+ if (!show)
+ continue;
+
+ if (!types.has(type))
+ add_type(type, types, root, &to_select);
+
+ TreeItem *ti;
+ if (types.has(type))
+ ti = types[type];
+ else
+ ti = search_options->get_root();
+
+ TreeItem *item = search_options->create_item(ti);
+ item->set_metadata(0, J->get());
+ item->set_text(0, J->get() + " (" + ScriptServer::get_global_class_path(J->get()).get_file() + ")");
+ item->set_icon(0, _get_editor_icon(type));
+ if (!to_select || J->get() == search_box->get_text()) {
+ to_select = item;
+ }
+ }
+ }
+
if (EditorNode::get_editor_data().get_custom_types().has(type) && ClassDB::is_parent_class(type, base_type)) {
//there are custom types based on this... cool.
@@ -444,6 +482,17 @@ Object *CreateDialog::instance_selected() {
custom = md;
if (custom != String()) {
+
+ if (ScriptServer::is_global_class(custom)) {
+ RES script = ResourceLoader::load(ScriptServer::get_global_class_path(custom));
+ ERR_FAIL_COND_V(!script.is_valid(), NULL);
+
+ Object *obj = ClassDB::instance(ScriptServer::get_global_class_base(custom));
+ ERR_FAIL_COND_V(!obj, NULL);
+
+ obj->set_script(script.get_ref_ptr());
+ return obj;
+ }
return EditorNode::get_editor_data().instance_custom_type(selected->get_text(0), custom);
} else {
return ClassDB::instance(selected->get_text(0));
diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp
index 7739b08eff..317fffad64 100644
--- a/editor/editor_export.cpp
+++ b/editor/editor_export.cpp
@@ -409,6 +409,7 @@ void EditorExportPlatform::_edit_files_with_filter(DirAccess *da, const Vector<S
String cur_dir = da->get_current_dir().replace("\\", "/");
if (!cur_dir.ends_with("/"))
cur_dir += "/";
+ String cur_dir_no_prefix = cur_dir.replace("res://", "");
Vector<String> dirs;
String f;
@@ -417,8 +418,10 @@ void EditorExportPlatform::_edit_files_with_filter(DirAccess *da, const Vector<S
dirs.push_back(f);
else {
String fullpath = cur_dir + f;
+ // Test also against path without res:// so that filters like `file.txt` can work.
+ String fullpath_no_prefix = cur_dir_no_prefix + f;
for (int i = 0; i < p_filters.size(); ++i) {
- if (fullpath.matchn(p_filters[i])) {
+ if (fullpath.matchn(p_filters[i]) || fullpath_no_prefix.matchn(p_filters[i])) {
if (!exclude) {
r_list.insert(fullpath);
} else {
diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp
index d8ae1da72e..d8ab41fa05 100644
--- a/editor/editor_file_system.cpp
+++ b/editor/editor_file_system.cpp
@@ -125,6 +125,14 @@ bool EditorFileSystemDirectory::get_file_import_is_valid(int p_idx) const {
return files[p_idx]->import_valid;
}
+String EditorFileSystemDirectory::get_file_script_class_name(int p_idx) const {
+ return files[p_idx]->script_class_name;
+}
+
+String EditorFileSystemDirectory::get_file_script_class_extends(int p_idx) const {
+ return files[p_idx]->script_class_extends;
+}
+
StringName EditorFileSystemDirectory::get_file_type(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, files.size(), "");
@@ -149,6 +157,8 @@ void EditorFileSystemDirectory::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_file", "idx"), &EditorFileSystemDirectory::get_file);
ClassDB::bind_method(D_METHOD("get_file_path", "idx"), &EditorFileSystemDirectory::get_file_path);
ClassDB::bind_method(D_METHOD("get_file_type", "idx"), &EditorFileSystemDirectory::get_file_type);
+ ClassDB::bind_method(D_METHOD("get_file_script_class_name", "idx"), &EditorFileSystemDirectory::get_file_script_class_name);
+ ClassDB::bind_method(D_METHOD("get_file_script_class_extends", "idx"), &EditorFileSystemDirectory::get_file_script_class_extends);
ClassDB::bind_method(D_METHOD("get_file_import_is_valid", "idx"), &EditorFileSystemDirectory::get_file_import_is_valid);
ClassDB::bind_method(D_METHOD("get_name"), &EditorFileSystemDirectory::get_name);
ClassDB::bind_method(D_METHOD("get_path"), &EditorFileSystemDirectory::get_path);
@@ -189,7 +199,7 @@ void EditorFileSystem::_scan_filesystem() {
String project = ProjectSettings::get_singleton()->get_resource_path();
- String fscache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_cache3");
+ String fscache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_cache4");
FileAccess *f = FileAccess::open(fscache, FileAccess::READ);
if (f) {
@@ -209,7 +219,7 @@ void EditorFileSystem::_scan_filesystem() {
} else {
Vector<String> split = l.split("::");
- ERR_CONTINUE(split.size() != 6);
+ ERR_CONTINUE(split.size() != 7);
String name = split[0];
String file;
@@ -221,8 +231,10 @@ void EditorFileSystem::_scan_filesystem() {
fc.modification_time = split[2].to_int64();
fc.import_modification_time = split[3].to_int64();
fc.import_valid = split[4].to_int64() != 0;
+ fc.script_class_name = split[5].get_slice("<>", 0);
+ fc.script_class_extends = split[5].get_slice("<>", 1);
- String deps = split[5].strip_edges();
+ String deps = split[6].strip_edges();
if (deps.length()) {
Vector<String> dp = deps.split("<>");
for (int i = 0; i < dp.size(); i++) {
@@ -239,7 +251,7 @@ void EditorFileSystem::_scan_filesystem() {
memdelete(f);
}
- String update_cache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_update3");
+ String update_cache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_update4");
if (FileAccess::exists(update_cache)) {
{
@@ -287,7 +299,7 @@ void EditorFileSystem::_scan_filesystem() {
}
void EditorFileSystem::_save_filesystem_cache() {
- String fscache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_cache3");
+ String fscache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_cache4");
FileAccess *f = FileAccess::open(fscache, FileAccess::WRITE);
if (f == NULL) {
@@ -563,6 +575,7 @@ void EditorFileSystem::scan() {
scanning = false;
emit_signal("filesystem_changed");
emit_signal("sources_changed", sources_changed.size() > 0);
+ _queue_update_script_classes();
} else {
@@ -706,6 +719,9 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess
fi->modified_time = fc->modification_time;
fi->import_modified_time = fc->import_modification_time;
fi->import_valid = fc->import_valid;
+ fi->script_class_name = fc->script_class_name;
+ fi->script_class_extends = fc->script_class_extends;
+
if (fc->type == String()) {
fi->type = ResourceLoader::get_resource_type(path);
//there is also the chance that file type changed due to reimport, must probably check this somehow here (or kind of note it for next time in another file?)
@@ -715,6 +731,7 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess
} else {
fi->type = ResourceFormatImporter::get_singleton()->get_resource_type(path);
+ fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends);
fi->modified_time = 0;
fi->import_modified_time = 0;
fi->import_valid = ResourceLoader::is_import_valid(path);
@@ -734,9 +751,12 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess
fi->deps = fc->deps;
fi->import_modified_time = 0;
fi->import_valid = true;
+ fi->script_class_name = fc->script_class_name;
+ fi->script_class_extends = fc->script_class_extends;
} else {
//new or modified time
fi->type = ResourceLoader::get_resource_type(path);
+ fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends);
fi->deps = _get_dependencies(path);
fi->modified_time = mt;
fi->import_modified_time = 0;
@@ -835,6 +855,7 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, const
fi->modified_time = FileAccess::get_modified_time(path);
fi->import_modified_time = 0;
fi->type = ResourceLoader::get_resource_type(path);
+ fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends);
fi->import_valid = ResourceLoader::is_import_valid(path);
{
@@ -1044,6 +1065,7 @@ void EditorFileSystem::_notification(int p_what) {
if (_update_scan_actions())
emit_signal("filesystem_changed");
emit_signal("sources_changed", sources_changed.size() > 0);
+ _queue_update_script_classes();
}
} else if (!scanning) {
@@ -1059,6 +1081,7 @@ void EditorFileSystem::_notification(int p_what) {
_update_scan_actions();
emit_signal("filesystem_changed");
emit_signal("sources_changed", sources_changed.size() > 0);
+ _queue_update_script_classes();
}
}
} break;
@@ -1087,7 +1110,7 @@ void EditorFileSystem::_save_filesystem_cache(EditorFileSystemDirectory *p_dir,
for (int i = 0; i < p_dir->files.size(); i++) {
- String s = p_dir->files[i]->file + "::" + p_dir->files[i]->type + "::" + itos(p_dir->files[i]->modified_time) + "::" + itos(p_dir->files[i]->import_modified_time) + "::" + itos(p_dir->files[i]->import_valid);
+ String s = p_dir->files[i]->file + "::" + p_dir->files[i]->type + "::" + itos(p_dir->files[i]->modified_time) + "::" + itos(p_dir->files[i]->import_modified_time) + "::" + itos(p_dir->files[i]->import_valid) + "::" + p_dir->files[i]->script_class_name + "<>" + p_dir->files[i]->script_class_extends;
s += "::";
for (int j = 0; j < p_dir->files[i]->deps.size(); j++) {
@@ -1268,7 +1291,7 @@ EditorFileSystemDirectory *EditorFileSystem::get_filesystem_path(const String &p
void EditorFileSystem::_save_late_updated_files() {
//files that already existed, and were modified, need re-scanning for dependencies upon project restart. This is done via saving this special file
- String fscache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_update3");
+ String fscache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_update4");
FileAccessRef f = FileAccess::open(fscache, FileAccess::WRITE);
for (Set<String>::Element *E = late_update_files.front(); E; E = E->next()) {
f->store_line(E->get());
@@ -1293,6 +1316,67 @@ Vector<String> EditorFileSystem::_get_dependencies(const String &p_path) {
return ret;
}
+String EditorFileSystem::_get_global_script_class(const String &p_type, const String &p_path, String *r_extends) const {
+
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ if (ScriptServer::get_language(i)->handles_global_class_type(p_type)) {
+ String global_name;
+ String extends;
+
+ global_name = ScriptServer::get_language(i)->get_global_class_name(p_path, &extends);
+ *r_extends = extends;
+ return global_name;
+ }
+ }
+ *r_extends = String();
+ return String();
+}
+
+void EditorFileSystem::_scan_script_classes(EditorFileSystemDirectory *p_dir) {
+ int filecount = p_dir->files.size();
+ const EditorFileSystemDirectory::FileInfo *const *files = p_dir->files.ptr();
+ for (int i = 0; i < filecount; i++) {
+ if (files[i]->script_class_name == String()) {
+ continue;
+ }
+
+ String lang;
+ for (int j = 0; j < ScriptServer::get_language_count(); j++) {
+ if (ScriptServer::get_language(j)->handles_global_class_type(files[i]->type)) {
+ lang = ScriptServer::get_language(j)->get_name();
+ }
+ }
+
+ ScriptServer::add_global_class(files[i]->script_class_name, files[i]->script_class_extends, lang, p_dir->get_file_path(i));
+ }
+ for (int i = 0; i < p_dir->get_subdir_count(); i++) {
+ _scan_script_classes(p_dir->get_subdir(i));
+ }
+}
+
+void EditorFileSystem::update_script_classes() {
+
+ if (!update_script_classes_queued)
+ return;
+
+ update_script_classes_queued = false;
+ ScriptServer::global_classes_clear();
+ if (get_filesystem()) {
+ _scan_script_classes(get_filesystem());
+ }
+
+ ScriptServer::save_global_classes();
+}
+
+void EditorFileSystem::_queue_update_script_classes() {
+ if (update_script_classes_queued) {
+ return;
+ }
+
+ update_script_classes_queued = true;
+ call_deferred("update_script_classes");
+}
+
void EditorFileSystem::update_file(const String &p_file) {
EditorFileSystemDirectory *fs = NULL;
@@ -1311,7 +1395,9 @@ void EditorFileSystem::update_file(const String &p_file) {
memdelete(fs->files[cpos]);
fs->files.remove(cpos);
}
+
call_deferred("emit_signal", "filesystem_changed"); //update later
+ _queue_update_script_classes();
return;
}
@@ -1351,6 +1437,7 @@ void EditorFileSystem::update_file(const String &p_file) {
}
fs->files[cpos]->type = type;
+ fs->files[cpos]->script_class_name = _get_global_script_class(type, p_file, &fs->files[cpos]->script_class_extends);
fs->files[cpos]->modified_time = FileAccess::get_modified_time(p_file);
fs->files[cpos]->deps = _get_dependencies(p_file);
fs->files[cpos]->import_valid = ResourceLoader::is_import_valid(p_file);
@@ -1359,6 +1446,7 @@ void EditorFileSystem::update_file(const String &p_file) {
EditorResourcePreview::get_singleton()->check_for_invalidation(p_file);
call_deferred("emit_signal", "filesystem_changed"); //update later
+ _queue_update_script_classes();
}
void EditorFileSystem::_reimport_file(const String &p_file) {
@@ -1611,6 +1699,7 @@ void EditorFileSystem::_bind_methods() {
ClassDB::bind_method(D_METHOD("update_file", "path"), &EditorFileSystem::update_file);
ClassDB::bind_method(D_METHOD("get_filesystem_path", "path"), &EditorFileSystem::get_filesystem_path);
ClassDB::bind_method(D_METHOD("get_file_type", "path"), &EditorFileSystem::get_file_type);
+ ClassDB::bind_method(D_METHOD("update_script_classes"), &EditorFileSystem::update_script_classes);
ADD_SIGNAL(MethodInfo("filesystem_changed"));
ADD_SIGNAL(MethodInfo("sources_changed", PropertyInfo(Variant::BOOL, "exist")));
@@ -1664,6 +1753,7 @@ EditorFileSystem::EditorFileSystem() {
memdelete(da);
scan_total = 0;
+ update_script_classes_queued = false;
}
EditorFileSystem::~EditorFileSystem() {
diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h
index a587d2879a..1aa35f4782 100644
--- a/editor/editor_file_system.h
+++ b/editor/editor_file_system.h
@@ -58,6 +58,8 @@ class EditorFileSystemDirectory : public Object {
bool import_valid;
Vector<String> deps;
bool verified; //used for checking changes
+ String script_class_name;
+ String script_class_extends;
};
struct FileInfoSort {
@@ -86,6 +88,8 @@ public:
StringName get_file_type(int p_idx) const;
Vector<String> get_file_deps(int p_idx) const;
bool get_file_import_is_valid(int p_idx) const;
+ String get_file_script_class_name(int p_idx) const; //used for scripts
+ String get_file_script_class_extends(int p_idx) const; //used for scripts
EditorFileSystemDirectory *get_parent();
@@ -157,6 +161,8 @@ class EditorFileSystem : public Node {
uint64_t import_modification_time;
Vector<String> deps;
bool import_valid;
+ String script_class_name;
+ String script_class_extends;
};
HashMap<String, FileCache> file_cache;
@@ -215,6 +221,12 @@ class EditorFileSystem : public Node {
}
};
+ void _scan_script_classes(EditorFileSystemDirectory *p_dir);
+ volatile bool update_script_classes_queued;
+ void _queue_update_script_classes();
+
+ String _get_global_script_class(const String &p_type, const String &p_path, String *r_extends) const;
+
protected:
void _notification(int p_what);
static void _bind_methods();
@@ -237,6 +249,8 @@ public:
void reimport_files(const Vector<String> &p_files);
+ void update_script_classes();
+
EditorFileSystem();
~EditorFileSystem();
};
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index d8ce2bc024..8d5c320743 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -91,8 +91,11 @@ void EditorProperty::_notification(int p_what) {
Rect2 rect;
Rect2 bottom_rect;
+ right_child_rect = Rect2();
+ bottom_child_rect = Rect2();
+
{
- int child_room = size.width / 2;
+ int child_room = size.width * (1.0 - split_ratio);
Ref<Font> font = get_font("font", "Tree");
int height = font->get_height();
@@ -118,7 +121,8 @@ void EditorProperty::_notification(int p_what) {
if (bottom_editor) {
- int m = get_constant("item_margin", "Tree");
+ int m = 0; //get_constant("item_margin", "Tree");
+
bottom_rect = Rect2(m, rect.size.height + get_constant("vseparation", "Tree"), size.width - m, bottom_editor->get_combined_minimum_size().height);
}
}
@@ -147,10 +151,12 @@ void EditorProperty::_notification(int p_what) {
continue;
fit_child_in_rect(c, rect);
+ right_child_rect = rect;
}
if (bottom_editor) {
fit_child_in_rect(bottom_editor, bottom_rect);
+ bottom_child_rect = bottom_rect;
}
update(); //need to redraw text
@@ -158,6 +164,7 @@ void EditorProperty::_notification(int p_what) {
if (p_what == NOTIFICATION_DRAW) {
Ref<Font> font = get_font("font", "Tree");
+ Color dark_color = get_color("dark_color_2", "Editor");
Size2 size = get_size();
if (bottom_editor) {
@@ -171,6 +178,13 @@ void EditorProperty::_notification(int p_what) {
draw_style_box(sb, Rect2(Vector2(), size));
}
+ if (right_child_rect != Rect2()) {
+ draw_rect(right_child_rect, dark_color);
+ }
+ if (bottom_child_rect != Rect2()) {
+ draw_rect(bottom_child_rect, dark_color);
+ }
+
Color color;
if (draw_red) {
color = get_color("error_color", "Editor");
@@ -251,7 +265,7 @@ void EditorProperty::_notification(int p_what) {
//int vs = get_constant("vseparation", "Tree");
Color guide_color = get_color("guide_color", "Tree");
int vs_height = get_size().height; // vs / 2;
- draw_line(Point2(0, vs_height), Point2(get_size().width, vs_height), guide_color);
+ // draw_line(Point2(0, vs_height), Point2(get_size().width, vs_height), guide_color);
}
}
@@ -691,6 +705,15 @@ bool EditorProperty::is_selectable() const {
return selectable;
}
+void EditorProperty::set_name_split_ratio(float p_ratio) {
+ split_ratio = p_ratio;
+}
+
+float EditorProperty::get_name_split_ratio() const {
+
+ return split_ratio;
+}
+
void EditorProperty::set_object_and_property(Object *p_object, const StringName &p_property) {
object = p_object;
property = p_property;
@@ -744,6 +767,7 @@ void EditorProperty::_bind_methods() {
EditorProperty::EditorProperty() {
+ split_ratio = 0.5;
selectable = true;
text_size = 0;
read_only = false;
@@ -916,6 +940,14 @@ EditorInspectorCategory::EditorInspectorCategory() {
////////////////////////////////////////////////
////////////////////////////////////////////////
+void EditorInspectorSection::_test_unfold() {
+
+ if (!vbox_added) {
+ add_child(vbox);
+ vbox_added = true;
+ }
+}
+
void EditorInspectorSection::_notification(int p_what) {
if (p_what == NOTIFICATION_SORT_CHILDREN) {
@@ -926,9 +958,9 @@ void EditorInspectorSection::_notification(int p_what) {
#ifdef TOOLS_ENABLED
if (foldable) {
if (object->editor_is_section_unfolded(section)) {
- arrow = get_icon("arrow", "Tree");
+ arrow = get_icon("arrow_up", "Tree");
} else {
- arrow = get_icon("arrow_collapsed", "Tree");
+ arrow = get_icon("arrow", "Tree");
}
}
#endif
@@ -941,7 +973,7 @@ void EditorInspectorSection::_notification(int p_what) {
}
offset.y += get_constant("vseparation", "Tree");
- offset.x += get_constant("item_margin", "Tree");
+ offset.x += get_constant("inspector_margin", "Editor");
Rect2 rect(offset, size - offset);
@@ -969,9 +1001,9 @@ void EditorInspectorSection::_notification(int p_what) {
#ifdef TOOLS_ENABLED
if (foldable) {
if (object->editor_is_section_unfolded(section)) {
- arrow = get_icon("arrow", "Tree");
+ arrow = get_icon("arrow_up", "Tree");
} else {
- arrow = get_icon("arrow_collapsed", "Tree");
+ arrow = get_icon("arrow", "Tree");
}
}
#endif
@@ -988,14 +1020,14 @@ void EditorInspectorSection::_notification(int p_what) {
int hs = get_constant("hseparation", "Tree");
+ Color color = get_color("font_color", "Tree");
+ draw_string(font, Point2(hs, font->get_ascent() + (h - font->get_height()) / 2).floor(), label, color, get_size().width);
+
int ofs = 0;
if (arrow.is_valid()) {
- draw_texture(arrow, Point2(ofs, (h - arrow->get_height()) / 2).floor());
+ draw_texture(arrow, Point2(get_size().width - arrow->get_width(), (h - arrow->get_height()) / 2).floor());
ofs += hs + arrow->get_width();
}
-
- Color color = get_color("font_color", "Tree");
- draw_string(font, Point2(ofs, font->get_ascent() + (h - font->get_height()) / 2).floor(), label, color, get_size().width);
}
}
@@ -1017,8 +1049,8 @@ Size2 EditorInspectorSection::get_minimum_size() const {
}
Ref<Font> font = get_font("font", "Tree");
- ms.height += font->get_ascent() + get_constant("vseparation", "Tree");
- ms.width += get_constant("item_margin", "Tree");
+ ms.height += font->get_height() + get_constant("vseparation", "Tree");
+ ms.width += get_constant("inspector_margin", "Editor");
return ms;
}
@@ -1031,16 +1063,20 @@ void EditorInspectorSection::setup(const String &p_section, const String &p_labe
bg_color = p_bg_color;
foldable = p_foldable;
+ if (!foldable && !vbox_added) {
+ add_child(vbox);
+ vbox_added = true;
+ }
+
#ifdef TOOLS_ENABLED
if (foldable) {
+ _test_unfold();
if (object->editor_is_section_unfolded(section)) {
vbox->show();
} else {
vbox->hide();
}
}
- // void editor_set_section_unfold(const String &p_section, bool p_unfolded);
-
#endif
}
@@ -1053,6 +1089,9 @@ void EditorInspectorSection::_gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+
+ _test_unfold();
+
bool unfold = !object->editor_is_section_unfolded(section);
object->editor_set_section_unfold(section, unfold);
if (unfold) {
@@ -1072,6 +1111,9 @@ void EditorInspectorSection::unfold() {
if (!foldable)
return;
+
+ _test_unfold();
+
#ifdef TOOLS_ENABLED
object->editor_set_section_unfold(section, true);
@@ -1084,6 +1126,8 @@ void EditorInspectorSection::fold() {
if (!foldable)
return;
+ if (!vbox_added)
+ return; //kinda pointless
#ifdef TOOLS_ENABLED
object->editor_set_section_unfold(section, false);
@@ -1105,7 +1149,14 @@ EditorInspectorSection::EditorInspectorSection() {
object = NULL;
foldable = false;
vbox = memnew(VBoxContainer);
- add_child(vbox);
+ vbox_added = false;
+ //add_child(vbox);
+}
+
+EditorInspectorSection::~EditorInspectorSection() {
+ if (!vbox_added) {
+ memdelete(vbox);
+ }
}
////////////////////////////////////////////////
@@ -1114,6 +1165,30 @@ EditorInspectorSection::EditorInspectorSection() {
Ref<EditorInspectorPlugin> EditorInspector::inspector_plugins[MAX_PLUGINS];
int EditorInspector::inspector_plugin_count = 0;
+EditorProperty *EditorInspector::instantiate_property_editor(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage) {
+
+ for (int i = inspector_plugin_count - 1; i >= 0; i--) {
+
+ inspector_plugins[i]->parse_property(p_object, p_type, p_path, p_hint, p_hint_text, p_usage);
+ if (inspector_plugins[i]->added_editors.size()) {
+ for (int j = 1; j < inspector_plugins[i]->added_editors.size(); j++) { //only keep first one
+ memdelete(inspector_plugins[i]->added_editors[j].property_editor);
+ }
+
+ EditorProperty *prop = Object::cast_to<EditorProperty>(inspector_plugins[i]->added_editors[0].property_editor);
+ if (prop) {
+
+ inspector_plugins[i]->added_editors.clear();
+ return prop;
+ } else {
+ memdelete(inspector_plugins[i]->added_editors[0].property_editor);
+ inspector_plugins[i]->added_editors.clear();
+ }
+ }
+ }
+ return NULL;
+}
+
void EditorInspector::add_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin) {
ERR_FAIL_COND(inspector_plugin_count == MAX_PLUGINS);
@@ -1242,8 +1317,10 @@ void EditorInspector::update_tree() {
String filter = search_box ? search_box->get_text() : "";
String group;
String group_base;
+ VBoxContainer *category_vbox = NULL;
- List<PropertyInfo> plist;
+ List<PropertyInfo>
+ plist;
object->get_property_list(&plist, true);
HashMap<String, VBoxContainer *> item_path;
@@ -1295,6 +1372,7 @@ void EditorInspector::update_tree() {
EditorInspectorCategory *category = memnew(EditorInspectorCategory);
main_vbox->add_child(category);
+ category_vbox = NULL; //reset
String type = p.name;
if (has_icon(type, "EditorIcons"))
@@ -1380,6 +1458,11 @@ void EditorInspector::update_tree() {
continue;
}
+ if (category_vbox == NULL) {
+ category_vbox = memnew(VBoxContainer);
+ main_vbox->add_child(category_vbox);
+ }
+
VBoxContainer *current_vbox = main_vbox;
{
@@ -1407,6 +1490,14 @@ void EditorInspector::update_tree() {
current_vbox = item_path[acc_path];
level = (MIN(level + 1, 4));
}
+
+ if (current_vbox == main_vbox) {
+ //do not add directly to the main vbox, given it has no spacing
+ if (category_vbox == NULL) {
+ category_vbox = memnew(VBoxContainer);
+ }
+ current_vbox = category_vbox;
+ }
}
bool checkable = false;
@@ -1416,12 +1507,19 @@ void EditorInspector::update_tree() {
checked = p.usage & PROPERTY_USAGE_CHECKED;
}
+ if (p.usage & PROPERTY_USAGE_RESTART_IF_CHANGED) {
+ restart_request_props.insert(p.name);
+ }
+
String doc_hint;
if (use_doc_hints) {
StringName classname = object->get_class_name();
- StringName propname = p.name;
+ if (object_class != String()) {
+ classname = object_class;
+ }
+ StringName propname = property_prefix + p.name;
String descr;
bool found = false;
@@ -1489,9 +1587,9 @@ void EditorInspector::update_tree() {
ep->connect("resource_selected", this, "_resource_selected", varray(), CONNECT_DEFERRED);
ep->connect("object_id_selected", this, "_object_id_selected", varray(), CONNECT_DEFERRED);
if (doc_hint != String()) {
- ep->set_tooltip(TTR("Property: ") + p.name + "\n\n" + doc_hint);
+ ep->set_tooltip(TTR("Property:") + " " + property_prefix + p.name + "\n\n" + doc_hint);
} else {
- ep->set_tooltip(TTR("Property: ") + p.name);
+ ep->set_tooltip(TTR("Property:") + " " + property_prefix + p.name);
}
ep->set_draw_red(draw_red);
ep->set_use_folding(use_folding);
@@ -1568,6 +1666,7 @@ void EditorInspector::_clear() {
editor_property_map.clear();
sections.clear();
pending.clear();
+ restart_request_props.clear();
}
void EditorInspector::refresh() {
@@ -1593,6 +1692,10 @@ void EditorInspector::edit(Object *p_object) {
object = p_object;
if (object) {
+ update_scroll_request = 0; //reset
+ if (scroll_cache.has(object->get_instance_id())) { //if exists, set something else
+ update_scroll_request = scroll_cache[object->get_instance_id()]; //done this way because wait until full size is accomodated
+ }
object->add_change_receptor(this);
update_tree();
}
@@ -1698,6 +1801,19 @@ int EditorInspector::get_scroll_offset() const {
return get_v_scroll();
}
+void EditorInspector::set_use_sub_inspector_bg(bool p_enable) {
+
+ use_sub_inspector_bg = p_enable;
+ if (!is_inside_tree())
+ return;
+
+ if (use_sub_inspector_bg) {
+ add_style_override("bg", get_stylebox("sub_inspector_bg", "Editor"));
+ } else {
+ add_style_override("bg", get_stylebox("bg", "Tree"));
+ }
+}
+
void EditorInspector::_edit_request_change(Object *p_object, const String &p_property) {
if (object != p_object) //may be undoing/redoing for a non edited object, so ignore
@@ -1794,6 +1910,10 @@ void EditorInspector::_property_changed(const String &p_path, const Variant &p_v
if (changing)
this->changing--;
+
+ if (restart_request_props.has(p_path)) {
+ emit_signal("restart_requested");
+ }
}
void EditorInspector::_property_changed_update_all(const String &p_path, const Variant &p_value) {
@@ -1813,6 +1933,9 @@ void EditorInspector::_multiple_properties_changed(Vector<String> p_paths, Array
undo_redo->create_action(TTR("Set Multiple:") + " " + names, UndoRedo::MERGE_ENDS);
for (int i = 0; i < p_paths.size(); i++) {
_edit_set(p_paths[i], p_values[i], false, "");
+ if (restart_request_props.has(p_paths[i])) {
+ emit_signal("restart_requested");
+ }
}
changing++;
undo_redo->commit_action();
@@ -1885,6 +2008,8 @@ void EditorInspector::_property_selected(const String &p_path, int p_focusable)
E->get()->deselect();
}
}
+
+ emit_signal("property_selected", p_path);
}
void EditorInspector::_object_id_selected(const String &p_path, ObjectID p_id) {
@@ -1908,7 +2033,11 @@ void EditorInspector::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
get_tree()->connect("node_removed", this, "_node_removed");
- add_style_override("bg", get_stylebox("bg", "Tree"));
+ if (use_sub_inspector_bg) {
+ add_style_override("bg", get_stylebox("sub_inspector_bg", "Editor"));
+ } else if (is_inside_tree()) {
+ add_style_override("bg", get_stylebox("bg", "Tree"));
+ }
}
if (p_what == NOTIFICATION_EXIT_TREE) {
@@ -1918,6 +2047,10 @@ void EditorInspector::_notification(int p_what) {
if (p_what == NOTIFICATION_PROCESS) {
+ if (update_scroll_request >= 0) {
+ get_v_scrollbar()->call_deferred("set_value", update_scroll_request);
+ update_scroll_request = -1;
+ }
if (refresh_countdown > 0) {
refresh_countdown -= get_process_delta_time();
if (refresh_countdown <= 0) {
@@ -1965,6 +2098,32 @@ void EditorInspector::_changed_callback(Object *p_changed, const char *p_prop) {
_edit_request_change(p_changed, p_prop);
}
+void EditorInspector::_vscroll_changed(double p_offset) {
+
+ if (update_scroll_request >= 0) //waiting, do nothing
+ return;
+
+ if (object) {
+ scroll_cache[object->get_instance_id()] = p_offset;
+ }
+}
+
+void EditorInspector::set_property_prefix(const String &p_prefix) {
+ property_prefix = p_prefix;
+}
+
+String EditorInspector::get_property_prefix() const {
+ return property_prefix;
+}
+
+void EditorInspector::set_object_class(const String &p_class) {
+ object_class = p_class;
+}
+
+String EditorInspector::get_object_class() const {
+ return object_class;
+}
+
void EditorInspector::_bind_methods() {
ClassDB::bind_method("_property_changed", &EditorInspector::_property_changed, DEFVAL(false));
@@ -1980,11 +2139,16 @@ void EditorInspector::_bind_methods() {
ClassDB::bind_method("_property_selected", &EditorInspector::_property_selected);
ClassDB::bind_method("_resource_selected", &EditorInspector::_resource_selected);
ClassDB::bind_method("_object_id_selected", &EditorInspector::_object_id_selected);
+ ClassDB::bind_method("_vscroll_changed", &EditorInspector::_vscroll_changed);
+
ClassDB::bind_method("refresh", &EditorInspector::refresh);
+ ADD_SIGNAL(MethodInfo("property_selected", PropertyInfo(Variant::STRING, "property")));
ADD_SIGNAL(MethodInfo("property_keyed", PropertyInfo(Variant::STRING, "property")));
ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::OBJECT, "res"), PropertyInfo(Variant::STRING, "prop")));
ADD_SIGNAL(MethodInfo("object_id_selected", PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("property_edited", PropertyInfo(Variant::STRING, "property")));
+ ADD_SIGNAL(MethodInfo("restart_requested"));
}
EditorInspector::EditorInspector() {
@@ -1992,6 +2156,7 @@ EditorInspector::EditorInspector() {
undo_redo = NULL;
main_vbox = memnew(VBoxContainer);
main_vbox->set_h_size_flags(SIZE_EXPAND_FILL);
+ main_vbox->add_constant_override("separation", 0);
add_child(main_vbox);
set_enable_h_scroll(false);
set_enable_v_scroll(true);
@@ -2013,4 +2178,8 @@ EditorInspector::EditorInspector() {
_prop_edited = "property_edited";
set_process(true);
property_focusable = -1;
+ use_sub_inspector_bg = false;
+
+ get_v_scrollbar()->connect("value_changed", this, "_vscroll_changed");
+ update_scroll_request = -1;
}
diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h
index 383cb458ec..d9b66b05b2 100644
--- a/editor/editor_inspector.h
+++ b/editor/editor_inspector.h
@@ -55,6 +55,9 @@ private:
bool draw_red;
bool keying;
+ Rect2 right_child_rect;
+ Rect2 bottom_child_rect;
+
Rect2 keying_rect;
bool keying_hover;
Rect2 revert_rect;
@@ -76,6 +79,8 @@ private:
bool selected;
int selected_focusable;
+ float split_ratio;
+
Vector<Control *> focusables;
Control *label_reference;
Control *bottom_editor;
@@ -134,6 +139,9 @@ public:
void set_selectable(bool p_selectable);
bool is_selectable() const;
+ void set_name_split_ratio(float p_ratio);
+ float get_name_split_ratio() const;
+
void set_object_and_property(Object *p_object, const StringName &p_property);
EditorProperty();
};
@@ -189,9 +197,12 @@ class EditorInspectorSection : public Container {
String section;
Object *object;
VBoxContainer *vbox;
+ bool vbox_added; //optimization
Color bg_color;
bool foldable;
+ void _test_unfold();
+
protected:
void _notification(int p_what);
static void _bind_methods();
@@ -208,6 +219,7 @@ public:
Object *get_edited_object();
EditorInspectorSection();
+ ~EditorInspectorSection();
};
class EditorInspector : public ScrollContainer {
@@ -244,15 +256,23 @@ class EditorInspector : public ScrollContainer {
bool update_all_pending;
bool read_only;
bool keying;
+ bool use_sub_inspector_bg;
float refresh_countdown;
bool update_tree_pending;
StringName _prop_edited;
StringName property_selected;
int property_focusable;
+ int update_scroll_request;
Map<StringName, Map<StringName, String> > descr_cache;
Map<StringName, String> class_descr_cache;
+ Set<StringName> restart_request_props;
+
+ Map<ObjectID, int> scroll_cache;
+
+ String property_prefix; //used for sectioned inspector
+ String object_class;
void _edit_set(const String &p_name, const Variant &p_value, bool p_refresh_all, const String &p_changed_field);
@@ -276,6 +296,8 @@ class EditorInspector : public ScrollContainer {
void _filter_changed(const String &p_text);
void _parse_added_editors(VBoxContainer *current_vbox, Ref<EditorInspectorPlugin> ped);
+ void _vscroll_changed(double);
+
protected:
static void _bind_methods();
void _notification(int p_what);
@@ -285,6 +307,8 @@ public:
static void remove_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin);
static void cleanup_plugins();
+ static EditorProperty *instantiate_property_editor(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage);
+
void set_undo_redo(UndoRedo *p_undo_redo);
String get_selected_path() const;
@@ -323,6 +347,14 @@ public:
void set_scroll_offset(int p_offset);
int get_scroll_offset() const;
+ void set_property_prefix(const String &p_prefix);
+ String get_property_prefix() const;
+
+ void set_object_class(const String &p_class);
+ String get_object_class() const;
+
+ void set_use_sub_inspector_bg(bool p_enable);
+
EditorInspector();
};
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 8d039f8cc0..88266f468a 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -73,6 +73,7 @@
#include "editor/plugins/animation_state_machine_editor.h"
#include "editor/plugins/animation_tree_editor_plugin.h"
#include "editor/plugins/asset_library_editor_plugin.h"
+#include "editor/plugins/audio_stream_editor_plugin.h"
#include "editor/plugins/baked_lightmap_editor_plugin.h"
#include "editor/plugins/camera_editor_plugin.h"
#include "editor/plugins/canvas_item_editor_plugin.h"
@@ -116,6 +117,7 @@
#include "editor/plugins/theme_editor_plugin.h"
#include "editor/plugins/tile_map_editor_plugin.h"
#include "editor/plugins/tile_set_editor_plugin.h"
+#include "editor/plugins/visual_shader_editor_plugin.h"
#include "editor/pvrtc_compress.h"
#include "editor/register_exporters.h"
#include "editor/script_editor_debugger.h"
@@ -1066,6 +1068,32 @@ void EditorNode::_save_scene(String p_file, int idx) {
}
}
+void EditorNode::save_all_scenes_and_restart() {
+
+ _menu_option_confirm(RUN_STOP, true);
+ exiting = true;
+
+ _save_all_scenes();
+
+ String to_reopen;
+ if (get_tree()->get_edited_scene_root()) {
+ to_reopen = get_tree()->get_edited_scene_root()->get_filename();
+ }
+
+ get_tree()->quit();
+ String exec = OS::get_singleton()->get_executable_path();
+
+ List<String> args;
+ args.push_back("--path");
+ args.push_back(ProjectSettings::get_singleton()->get_resource_path());
+ args.push_back("-e");
+ if (to_reopen != String()) {
+ args.push_back(to_reopen);
+ }
+
+ OS::get_singleton()->set_restart_on_exit(true, args);
+}
+
void EditorNode::_save_all_scenes() {
for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
@@ -1854,10 +1882,6 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
} break;
- case SETTINGS_EXPORT_PREFERENCES: {
-
- //project_export_settings->popup_centered_ratio();
- } break;
case FILE_IMPORT_SUBSCENE: {
if (!editor_data.get_edited_scene_root()) {
@@ -2206,6 +2230,13 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
about->popup_centered_minsize(Size2(780, 500) * EDSCALE);
} break;
+ case SET_VIDEO_DRIVER_SAVE_AND_RESTART: {
+
+ ProjectSettings::get_singleton()->set("rendering/quality/driver/driver_name", video_driver_request);
+ ProjectSettings::get_singleton()->save();
+
+ save_all_scenes_and_restart();
+ } break;
default: {
if (p_option >= IMPORT_PLUGIN_BASE) {
}
@@ -3860,7 +3891,7 @@ ToolButton *EditorNode::add_bottom_panel_item(String p_text, Control *p_item) {
tb->set_focus_mode(Control::FOCUS_NONE);
bottom_panel_vb->add_child(p_item);
bottom_panel_hb->raise();
- bottom_panel_hb->add_child(tb);
+ bottom_panel_hb_editors->add_child(tb);
p_item->set_v_size_flags(Control::SIZE_EXPAND_FILL);
p_item->hide();
BottomPanelItem bpi;
@@ -3924,7 +3955,7 @@ void EditorNode::remove_bottom_panel_item(Control *p_item) {
_bottom_panel_switch(false, 0);
}
bottom_panel_vb->remove_child(bottom_panel_items[i].control);
- bottom_panel_hb->remove_child(bottom_panel_items[i].button);
+ bottom_panel_hb_editors->remove_child(bottom_panel_items[i].button);
memdelete(bottom_panel_items[i].button);
bottom_panel_items.remove(i);
break;
@@ -3954,6 +3985,11 @@ void EditorNode::_bottom_panel_switch(bool p_enable, int p_idx) {
}
center_split->set_dragger_visibility(SplitContainer::DRAGGER_VISIBLE);
center_split->set_collapsed(false);
+ if (bottom_panel_raise->is_pressed()) {
+ top_split->hide();
+ }
+ bottom_panel_raise->show();
+
} else {
bottom_panel->add_style_override("panel", gui_base->get_stylebox("panel", "TabContainer"));
for (int i = 0; i < bottom_panel_items.size(); i++) {
@@ -3963,6 +3999,10 @@ void EditorNode::_bottom_panel_switch(bool p_enable, int p_idx) {
}
center_split->set_dragger_visibility(SplitContainer::DRAGGER_HIDDEN);
center_split->set_collapsed(true);
+ bottom_panel_raise->hide();
+ if (bottom_panel_raise->is_pressed()) {
+ top_split->show();
+ }
}
}
@@ -4179,7 +4219,7 @@ void EditorNode::_dropped_files(const Vector<String> &p_files, int p_screen) {
for (int i = 0; i < p_files.size(); i++) {
String from = p_files[i];
- if (!ResourceFormatImporter::get_singleton()->can_be_imported(from) && (just_copy.find(from.get_extension().to_lower()) != -1)) {
+ if (!ResourceFormatImporter::get_singleton()->can_be_imported(from) && (just_copy.find(from.get_extension().to_lower()) == -1)) {
continue;
}
String to = to_path.plus_file(from.get_file());
@@ -4372,6 +4412,32 @@ Vector<Ref<EditorResourceConversionPlugin> > EditorNode::find_resource_conversio
return ret;
}
+void EditorNode::_bottom_panel_raise_toggled(bool p_pressed) {
+
+ if (p_pressed) {
+ top_split->hide();
+ bottom_panel_raise->set_icon(gui_base->get_icon("ShrinkBottomDock", "EditorIcons"));
+ } else {
+ top_split->show();
+ bottom_panel_raise->set_icon(gui_base->get_icon("ExpandBottomDock", "EditorIcons"));
+ }
+}
+
+void EditorNode::_video_driver_selected(int p_which) {
+
+ String driver = video_driver->get_item_metadata(p_which);
+
+ String current = OS::get_singleton()->get_video_driver_name(OS::get_singleton()->get_current_video_driver());
+
+ if (driver == current) {
+ return;
+ }
+
+ video_driver_request = driver;
+ video_restart_dialog->popup_centered_minsize();
+ video_driver->select(video_driver_current);
+}
+
void EditorNode::_bind_methods() {
ClassDB::bind_method("_menu_option", &EditorNode::_menu_option);
@@ -4440,6 +4506,9 @@ void EditorNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("_dim_timeout"), &EditorNode::_dim_timeout);
ClassDB::bind_method(D_METHOD("_resources_reimported"), &EditorNode::_resources_reimported);
+ ClassDB::bind_method(D_METHOD("_bottom_panel_raise_toggled"), &EditorNode::_bottom_panel_raise_toggled);
+
+ ClassDB::bind_method(D_METHOD("_video_driver_selected"), &EditorNode::_video_driver_selected);
ADD_SIGNAL(MethodInfo("play_pressed"));
ADD_SIGNAL(MethodInfo("pause_pressed"));
@@ -4605,6 +4674,10 @@ EditorNode::EditorNode() {
Ref<EditorInspectorRootMotionPlugin> rmp;
rmp.instance();
EditorInspector::add_inspector_plugin(rmp);
+
+ Ref<EditorInspectorShaderModePlugin> smp;
+ smp.instance();
+ EditorInspector::add_inspector_plugin(smp);
}
_pvrtc_register_compressors();
@@ -4633,20 +4706,21 @@ EditorNode::EditorNode() {
ClassDB::set_class_enabled("RootMotionView", true);
//defs here, use EDITOR_GET in logic
- EDITOR_DEF("interface/scene_tabs/always_show_close_button", false);
- EDITOR_DEF("interface/scene_tabs/resize_if_many_tabs", true);
- EDITOR_DEF("interface/scene_tabs/minimum_width", 50);
+ EDITOR_DEF_RST("interface/scene_tabs/always_show_close_button", false);
+ EDITOR_DEF_RST("interface/scene_tabs/resize_if_many_tabs", true);
+ EDITOR_DEF_RST("interface/scene_tabs/minimum_width", 50);
EDITOR_DEF("run/output/always_clear_output_on_play", true);
EDITOR_DEF("run/output/always_open_output_on_play", true);
EDITOR_DEF("run/output/always_close_output_on_stop", true);
EDITOR_DEF("run/auto_save/save_before_running", true);
- EDITOR_DEF("interface/editor/save_each_scene_on_quit", true);
+ EDITOR_DEF_RST("interface/editor/save_each_scene_on_quit", true);
EDITOR_DEF("interface/editor/quit_confirmation", true);
- EDITOR_DEF("interface/scene_tabs/restore_scenes_on_load", false);
- EDITOR_DEF("interface/scene_tabs/show_thumbnail_on_hover", true);
- EDITOR_DEF("interface/inspector/capitalize_properties", true);
- EDITOR_DEF("interface/inspector/disable_folding", false);
- EDITOR_DEF("interface/inspector/open_resources_in_new_inspector", false);
+ EDITOR_DEF_RST("interface/scene_tabs/restore_scenes_on_load", false);
+ EDITOR_DEF_RST("interface/scene_tabs/show_thumbnail_on_hover", true);
+ EDITOR_DEF_RST("interface/inspector/capitalize_properties", true);
+ EDITOR_DEF_RST("interface/inspector/disable_folding", false);
+ EDITOR_DEF("interface/inspector/open_resources_in_current_inspector", true);
+ EDITOR_DEF("interface/inspector/resources_types_to_open_in_new_inspector", "SpatialMaterial");
EDITOR_DEF("run/auto_save/save_before_running", true);
theme_base = memnew(Control);
@@ -5167,6 +5241,37 @@ EditorNode::EditorNode() {
play_custom_scene_button->set_shortcut(ED_SHORTCUT("editor/play_custom_scene", TTR("Play Custom Scene"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F5));
#endif
+ video_driver = memnew(OptionButton);
+ video_driver->set_flat(true);
+ video_driver->set_focus_mode(Control::FOCUS_NONE);
+ video_driver->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
+ String video_drivers = ProjectSettings::get_singleton()->get_custom_property_info()["rendering/quality/driver/driver_name"].hint_string;
+ String current_video_driver = OS::get_singleton()->get_video_driver_name(OS::get_singleton()->get_current_video_driver());
+ menu_hb->add_child(video_driver);
+ video_driver_current = 0;
+ for (int i = 0; i < video_drivers.get_slice_count(","); i++) {
+ String driver = video_drivers.get_slice(",", i);
+ if (gui_base->has_icon(driver, "EditorIcons")) {
+ video_driver->add_icon_item(gui_base->get_icon(driver, "EditorIcons"), "");
+ } else {
+ video_driver->add_item(driver);
+ }
+
+ video_driver->set_item_metadata(i, driver);
+
+ if (current_video_driver == driver) {
+ video_driver->select(i);
+ video_driver_current = i;
+ }
+ }
+
+ video_driver->connect("item_selected", this, "_video_driver_selected");
+ video_restart_dialog = memnew(ConfirmationDialog);
+ video_restart_dialog->set_text(TTR("Changing the video driver requires restarting the editor."));
+ video_restart_dialog->get_ok()->set_text(TTR("Save & Restart"));
+ video_restart_dialog->connect("confirmed", this, "_menu_option", varray(SET_VIDEO_DRIVER_SAVE_AND_RESTART));
+ add_child(video_restart_dialog);
+
progress_hb = memnew(BackgroundProgress);
HBoxContainer *right_menu_hb = memnew(HBoxContainer);
@@ -5263,6 +5368,19 @@ EditorNode::EditorNode() {
bottom_panel_hb = memnew(HBoxContainer);
bottom_panel_vb->add_child(bottom_panel_hb);
+ bottom_panel_hb_editors = memnew(HBoxContainer);
+ bottom_panel_hb_editors->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ bottom_panel_hb->add_child(bottom_panel_hb_editors);
+ bottom_panel_raise = memnew(ToolButton);
+ bottom_panel_raise->set_icon(gui_base->get_icon("ExpandBottomDock", "EditorIcons"));
+
+ bottom_panel_raise->set_shortcut(ED_SHORTCUT("editor/bottom_panel_expand", TTR("Expand Bottom Panel"), KEY_MASK_SHIFT | KEY_F12));
+
+ bottom_panel_hb->add_child(bottom_panel_raise);
+ bottom_panel_raise->hide();
+ bottom_panel_raise->set_toggle_mode(true);
+ bottom_panel_raise->connect("toggled", this, "_bottom_panel_raise_toggled");
+
log = memnew(EditorLog);
ToolButton *output_button = add_bottom_panel_item(TTR("Output"), log);
log->set_tool_button(output_button);
@@ -5367,8 +5485,7 @@ EditorNode::EditorNode() {
raise_bottom_panel_item(AnimationPlayerEditor::singleton);
add_editor_plugin(memnew(ShaderEditorPlugin(this)));
- // FIXME: Disabled for Godot 3.0 as made incompatible, it needs to be ported to the new API.
- //add_editor_plugin(memnew(ShaderGraphEditorPlugin(this)));
+ add_editor_plugin(memnew(VisualShaderEditorPlugin(this)));
add_editor_plugin(memnew(AnimationNodeBlendTreeEditorPlugin(this)));
add_editor_plugin(memnew(AnimationNodeBlendSpace1DEditorPlugin(this)));
add_editor_plugin(memnew(AnimationNodeBlendSpace2DEditorPlugin(this)));
@@ -5406,6 +5523,7 @@ EditorNode::EditorNode() {
add_editor_plugin(memnew(CollisionShape2DEditorPlugin(this)));
add_editor_plugin(memnew(CurveEditorPlugin(this)));
add_editor_plugin(memnew(TextureEditorPlugin(this)));
+ add_editor_plugin(memnew(AudioStreamEditorPlugin(this)));
add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor)));
add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor)));
add_editor_plugin(memnew(SkeletonEditorPlugin(this)));
diff --git a/editor/editor_node.h b/editor/editor_node.h
index a5f975784c..38e68b2e09 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -164,7 +164,6 @@ private:
SETTINGS_UPDATE_ALWAYS,
SETTINGS_UPDATE_CHANGES,
SETTINGS_UPDATE_SPINNER_HIDE,
- SETTINGS_EXPORT_PREFERENCES,
SETTINGS_PREFERENCES,
SETTINGS_LAYOUT_SAVE,
SETTINGS_LAYOUT_DELETE,
@@ -183,6 +182,8 @@ private:
HELP_COMMUNITY,
HELP_ABOUT,
+ SET_VIDEO_DRIVER_SAVE_AND_RESTART,
+
IMPORT_PLUGIN_BASE = 100,
TOOL_MENU_BASE = 1000
@@ -195,6 +196,13 @@ private:
Control *gui_base;
VBoxContainer *main_vbox;
PanelContainer *play_button_panel;
+ OptionButton *video_driver;
+
+ ConfirmationDialog *video_restart_dialog;
+
+ int video_driver_current;
+ String video_driver_request;
+ void _video_driver_selected(int);
//split
@@ -377,7 +385,11 @@ private:
PanelContainer *bottom_panel;
HBoxContainer *bottom_panel_hb;
+ HBoxContainer *bottom_panel_hb_editors;
VBoxContainer *bottom_panel_vb;
+ ToolButton *bottom_panel_raise;
+
+ void _bottom_panel_raise_toggled(bool);
EditorInterface *editor_interface;
@@ -742,6 +754,8 @@ public:
void add_tool_submenu_item(const String &p_name, PopupMenu *p_submenu);
void remove_tool_menu_item(const String &p_name);
+ void save_all_scenes_and_restart();
+
void dim_editor(bool p_dimming);
void edit_current() { _edit_current(); };
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index 064569dea0..0b49b5a801 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -178,6 +178,8 @@ void EditorPropertyTextEnum::_bind_methods() {
EditorPropertyTextEnum::EditorPropertyTextEnum() {
options = memnew(OptionButton);
options->set_clip_text(true);
+ options->set_flat(true);
+
add_child(options);
add_focusable(options);
options->connect("item_selected", this, "_option_selected");
@@ -390,13 +392,37 @@ EditorPropertyCheck::EditorPropertyCheck() {
void EditorPropertyEnum::_option_selected(int p_which) {
- emit_signal("property_changed", get_edited_property(), p_which);
+ String text = options->get_item_text(p_which);
+ Vector<String> text_split = text.split(":");
+ if (text_split.size() == 1) {
+ emit_signal("property_changed", get_edited_property(), p_which);
+ return;
+ }
+ String name = text_split[1];
+ emit_signal("property_changed", get_edited_property(), name.to_int());
}
void EditorPropertyEnum::update_property() {
int which = get_edited_object()->get(get_edited_property());
- options->select(which);
+ if (which == 0) {
+ options->select(which);
+ return;
+ }
+
+ for (int i = 0; i < options->get_item_count(); i++) {
+ String text = options->get_item_text(i);
+ Vector<String> text_split = text.split(":");
+ if (text_split.size() == 1) {
+ options->select(which);
+ return;
+ }
+ String name = text_split[1];
+ if (itos(which) == name) {
+ options->select(i);
+ return;
+ }
+ }
}
void EditorPropertyEnum::setup(const Vector<String> &p_options) {
@@ -405,6 +431,10 @@ void EditorPropertyEnum::setup(const Vector<String> &p_options) {
}
}
+void EditorPropertyEnum::set_option_button_clip(bool p_enable) {
+ options->set_clip_text(p_enable);
+}
+
void EditorPropertyEnum::_bind_methods() {
ClassDB::bind_method(D_METHOD("_option_selected"), &EditorPropertyEnum::_option_selected);
@@ -413,6 +443,7 @@ void EditorPropertyEnum::_bind_methods() {
EditorPropertyEnum::EditorPropertyEnum() {
options = memnew(OptionButton);
options->set_clip_text(true);
+ options->set_flat(true);
add_child(options);
add_focusable(options);
options->connect("item_selected", this, "_option_selected");
@@ -613,9 +644,11 @@ void EditorPropertyLayers::setup(LayerType p_layer_type) {
}
if (name == "") {
- name = "Layer " + itos(i + 1);
+ name = TTR("Layer") + " " + itos(i + 1);
}
+ name += "\n" + vformat(TTR("Bit %d, value %d"), i, 1 << i);
+
names.push_back(name);
}
@@ -705,6 +738,7 @@ void EditorPropertyInteger::setup(int p_min, int p_max, bool p_allow_greater, bo
EditorPropertyInteger::EditorPropertyInteger() {
spin = memnew(EditorSpinSlider);
+ spin->set_flat(true);
add_child(spin);
add_focusable(spin);
spin->connect("value_changed", this, "_value_changed");
@@ -791,6 +825,7 @@ void EditorPropertyFloat::setup(double p_min, double p_max, double p_step, bool
EditorPropertyFloat::EditorPropertyFloat() {
spin = memnew(EditorSpinSlider);
+ spin->set_flat(true);
add_child(spin);
add_focusable(spin);
spin->connect("value_changed", this, "_value_changed");
@@ -801,6 +836,12 @@ EditorPropertyFloat::EditorPropertyFloat() {
void EditorPropertyEasing::_drag_easing(const Ref<InputEvent> &p_ev) {
+ Ref<InputEventMouseButton> mb = p_ev;
+ if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_RIGHT) {
+ preset->set_global_position(easing_draw->get_global_transform().xform(mb->get_position()));
+ preset->popup();
+ }
+
Ref<InputEventMouseMotion> mm = p_ev;
if (mm.is_valid() && mm->get_button_mask() & BUTTON_MASK_LEFT) {
@@ -838,7 +879,7 @@ void EditorPropertyEasing::_draw_easing() {
Size2 s = easing_draw->get_size();
Rect2 r(Point2(), s);
r = r.grow(3);
- get_stylebox("normal", "LineEdit")->draw(ci, r);
+ //get_stylebox("normal", "LineEdit")->draw(ci, r);
int points = 48;
@@ -848,6 +889,7 @@ void EditorPropertyEasing::_draw_easing() {
Ref<Font> f = get_font("font", "Label");
Color color = get_color("font_color", "Label");
+ Vector<Point2> lines;
for (int i = 1; i <= points; i++) {
float ifl = i / float(points);
@@ -860,10 +902,12 @@ void EditorPropertyEasing::_draw_easing() {
iflp = 1.0 - iflp;
}
- VisualServer::get_singleton()->canvas_item_add_line(ci, Point2(iflp * s.width, prev * s.height), Point2(ifl * s.width, h * s.height), color);
+ lines.push_back(Point2(ifl * s.width, h * s.height));
+ lines.push_back(Point2(iflp * s.width, prev * s.height));
prev = h;
}
+ easing_draw->draw_multiline(lines, color, 1.0, true);
f->draw(ci, Point2(10, 10 + f->get_ascent()), String::num(exp, 2), color);
}
@@ -871,29 +915,17 @@ void EditorPropertyEasing::update_property() {
easing_draw->update();
}
-void EditorPropertyEasing::_set_preset(float p_val) {
- emit_signal("property_changed", get_edited_property(), p_val);
+void EditorPropertyEasing::_set_preset(int p_preset) {
+ static const float preset_value[EASING_MAX] = { 0.0, 1.0, 2.0, 0.5, -2.0, -0.5 };
+
+ emit_signal("property_changed", get_edited_property(), preset_value[p_preset]);
easing_draw->update();
}
void EditorPropertyEasing::setup(bool p_full, bool p_flip) {
flip = p_flip;
- if (p_full) {
- HBoxContainer *hb2 = memnew(HBoxContainer);
- vb->add_child(hb2);
- button_out_in = memnew(ToolButton);
- button_out_in->set_tooltip(TTR("Out-In"));
- button_out_in->set_h_size_flags(SIZE_EXPAND_FILL);
- button_out_in->connect("pressed", this, "_set_preset", varray(-0.5));
- hb2->add_child(button_out_in);
-
- button_in_out = memnew(ToolButton);
- button_in_out->set_tooltip(TTR("In"));
- button_in_out->set_h_size_flags(SIZE_EXPAND_FILL);
- button_in_out->connect("pressed", this, "_set_preset", varray(-2));
- hb2->add_child(button_in_out);
- }
+ full = p_full;
}
void EditorPropertyEasing::_notification(int p_what) {
@@ -901,15 +933,19 @@ void EditorPropertyEasing::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED:
case NOTIFICATION_ENTER_TREE: {
+ preset->clear();
+ preset->add_icon_item(get_icon("CurveConstant", "EditorIcons"), "Zero", EASING_ZERO);
+ preset->add_icon_item(get_icon("CurveLinear", "EditorIcons"), "Linear", EASING_LINEAR);
+ preset->add_icon_item(get_icon("CurveIn", "EditorIcons"), "In", EASING_IN);
+ preset->add_icon_item(get_icon("CurveOut", "EditorIcons"), "Out", EASING_OUT);
+ if (full) {
+ preset->add_icon_item(get_icon("CurveInOut", "EditorIcons"), "In-Out", EASING_IN_OUT);
+ preset->add_icon_item(get_icon("CurveOutIn", "EditorIcons"), "Out-In", EASING_OUT_IN);
+ }
easing_draw->set_custom_minimum_size(Size2(0, get_font("font", "Label")->get_height() * 2));
- button_linear->set_icon(get_icon("CurveLinear", "EditorIcons"));
- button_out->set_icon(get_icon("CurveOut", "EditorIcons"));
- button_in->set_icon(get_icon("CurveIn", "EditorIcons"));
- button_constant->set_icon(get_icon("CurveConstant", "EditorIcons"));
- if (button_out_in)
- button_out_in->set_icon(get_icon("CurveOutIn", "EditorIcons"));
- if (button_in_out)
- button_in_out->set_icon(get_icon("CurveInOut", "EditorIcons"));
+ } break;
+ case NOTIFICATION_RESIZED: {
+
} break;
}
}
@@ -923,47 +959,18 @@ void EditorPropertyEasing::_bind_methods() {
EditorPropertyEasing::EditorPropertyEasing() {
- vb = memnew(VBoxContainer);
- add_child(vb);
- HBoxContainer *hb = memnew(HBoxContainer);
- set_label_reference(hb);
-
- vb->add_child(hb);
-
- button_linear = memnew(ToolButton);
- button_linear->set_tooltip(TTR("Linear"));
- button_linear->set_h_size_flags(SIZE_EXPAND_FILL);
- button_linear->connect("pressed", this, "_set_preset", varray(1));
- hb->add_child(button_linear);
-
- button_constant = memnew(ToolButton);
- button_constant->set_tooltip(TTR("Linear"));
- button_constant->set_h_size_flags(SIZE_EXPAND_FILL);
- button_constant->connect("pressed", this, "_set_preset", varray(0));
- hb->add_child(button_constant);
-
- button_out = memnew(ToolButton);
- button_out->set_tooltip(TTR("Out"));
- button_out->set_h_size_flags(SIZE_EXPAND_FILL);
- button_out->connect("pressed", this, "_set_preset", varray(0.5));
- hb->add_child(button_out);
-
- button_in = memnew(ToolButton);
- button_in->set_tooltip(TTR("In"));
- button_in->set_h_size_flags(SIZE_EXPAND_FILL);
- button_in->connect("pressed", this, "_set_preset", varray(2));
- hb->add_child(button_in);
-
- button_in_out = NULL;
- button_out_in = NULL;
-
easing_draw = memnew(Control);
easing_draw->connect("draw", this, "_draw_easing");
easing_draw->connect("gui_input", this, "_drag_easing");
easing_draw->set_default_cursor_shape(Control::CURSOR_MOVE);
- vb->add_child(easing_draw);
+ add_child(easing_draw);
+
+ preset = memnew(PopupMenu);
+ add_child(preset);
+ preset->connect("id_pressed", this, "_set_preset");
flip = false;
+ full = false;
}
///////////////////// VECTOR2 /////////////////////////
@@ -986,6 +993,18 @@ void EditorPropertyVector2::update_property() {
setting = false;
}
+void EditorPropertyVector2::_notification(int p_what) {
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ Color base = get_color("accent_color", "Editor");
+ for (int i = 0; i < 2; i++) {
+
+ Color c = base;
+ c.set_hsv(float(i) / 3.0 + 0.05, c.get_s() * 0.75, c.get_v());
+ spin[i]->set_custom_label_color(true, c);
+ }
+ }
+}
+
void EditorPropertyVector2::_bind_methods() {
ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyVector2::_value_changed);
@@ -1006,6 +1025,7 @@ EditorPropertyVector2::EditorPropertyVector2() {
static const char *desc[2] = { "x", "y" };
for (int i = 0; i < 2; i++) {
spin[i] = memnew(EditorSpinSlider);
+ spin[i]->set_flat(true);
spin[i]->set_label(desc[i]);
vb->add_child(spin[i]);
add_focusable(spin[i]);
@@ -1038,7 +1058,17 @@ void EditorPropertyRect2::update_property() {
spin[3]->set_value(val.size.y);
setting = false;
}
+void EditorPropertyRect2::_notification(int p_what) {
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ Color base = get_color("accent_color", "Editor");
+ for (int i = 0; i < 4; i++) {
+ Color c = base;
+ c.set_hsv(float(i % 2) / 3.0 + 0.05, c.get_s() * 0.75, c.get_v());
+ spin[i]->set_custom_label_color(true, c);
+ }
+ }
+}
void EditorPropertyRect2::_bind_methods() {
ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyRect2::_value_changed);
@@ -1060,6 +1090,8 @@ EditorPropertyRect2::EditorPropertyRect2() {
for (int i = 0; i < 4; i++) {
spin[i] = memnew(EditorSpinSlider);
spin[i]->set_label(desc[i]);
+ spin[i]->set_flat(true);
+
vb->add_child(spin[i]);
add_focusable(spin[i]);
spin[i]->connect("value_changed", this, "_value_changed");
@@ -1088,7 +1120,17 @@ void EditorPropertyVector3::update_property() {
spin[2]->set_value(val.z);
setting = false;
}
+void EditorPropertyVector3::_notification(int p_what) {
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ Color base = get_color("accent_color", "Editor");
+ for (int i = 0; i < 3; i++) {
+ Color c = base;
+ c.set_hsv(float(i) / 3.0 + 0.05, c.get_s() * 0.75, c.get_v());
+ spin[i]->set_custom_label_color(true, c);
+ }
+ }
+}
void EditorPropertyVector3::_bind_methods() {
ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyVector3::_value_changed);
@@ -1110,6 +1152,8 @@ EditorPropertyVector3::EditorPropertyVector3() {
for (int i = 0; i < 3; i++) {
spin[i] = memnew(EditorSpinSlider);
spin[i]->set_label(desc[i]);
+ spin[i]->set_flat(true);
+
vb->add_child(spin[i]);
add_focusable(spin[i]);
spin[i]->connect("value_changed", this, "_value_changed");
@@ -1140,7 +1184,17 @@ void EditorPropertyPlane::update_property() {
spin[3]->set_value(val.d);
setting = false;
}
+void EditorPropertyPlane::_notification(int p_what) {
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ Color base = get_color("accent_color", "Editor");
+ for (int i = 0; i < 3; i++) {
+ Color c = base;
+ c.set_hsv(float(i) / 3.0 + 0.05, c.get_s() * 0.75, c.get_v());
+ spin[i]->set_custom_label_color(true, c);
+ }
+ }
+}
void EditorPropertyPlane::_bind_methods() {
ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyPlane::_value_changed);
@@ -1162,6 +1216,7 @@ EditorPropertyPlane::EditorPropertyPlane() {
for (int i = 0; i < 4; i++) {
spin[i] = memnew(EditorSpinSlider);
spin[i]->set_label(desc[i]);
+ spin[i]->set_flat(true);
vb->add_child(spin[i]);
add_focusable(spin[i]);
spin[i]->connect("value_changed", this, "_value_changed");
@@ -1193,7 +1248,17 @@ void EditorPropertyQuat::update_property() {
spin[3]->set_value(val.w);
setting = false;
}
+void EditorPropertyQuat::_notification(int p_what) {
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ Color base = get_color("accent_color", "Editor");
+ for (int i = 0; i < 3; i++) {
+ Color c = base;
+ c.set_hsv(float(i) / 3.0 + 0.05, c.get_s() * 0.75, c.get_v());
+ spin[i]->set_custom_label_color(true, c);
+ }
+ }
+}
void EditorPropertyQuat::_bind_methods() {
ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyQuat::_value_changed);
@@ -1215,6 +1280,8 @@ EditorPropertyQuat::EditorPropertyQuat() {
for (int i = 0; i < 4; i++) {
spin[i] = memnew(EditorSpinSlider);
spin[i]->set_label(desc[i]);
+ spin[i]->set_flat(true);
+
vb->add_child(spin[i]);
add_focusable(spin[i]);
spin[i]->connect("value_changed", this, "_value_changed");
@@ -1252,7 +1319,17 @@ void EditorPropertyAABB::update_property() {
setting = false;
}
+void EditorPropertyAABB::_notification(int p_what) {
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ Color base = get_color("accent_color", "Editor");
+ for (int i = 0; i < 6; i++) {
+ Color c = base;
+ c.set_hsv(float(i % 3) / 3.0 + 0.05, c.get_s() * 0.75, c.get_v());
+ spin[i]->set_custom_label_color(true, c);
+ }
+ }
+}
void EditorPropertyAABB::_bind_methods() {
ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyAABB::_value_changed);
@@ -1276,6 +1353,8 @@ EditorPropertyAABB::EditorPropertyAABB() {
for (int i = 0; i < 6; i++) {
spin[i] = memnew(EditorSpinSlider);
spin[i]->set_label(desc[i]);
+ spin[i]->set_flat(true);
+
g->add_child(spin[i]);
spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
add_focusable(spin[i]);
@@ -1314,7 +1393,17 @@ void EditorPropertyTransform2D::update_property() {
setting = false;
}
+void EditorPropertyTransform2D::_notification(int p_what) {
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ Color base = get_color("accent_color", "Editor");
+ for (int i = 0; i < 6; i++) {
+ Color c = base;
+ c.set_hsv(float(i % 2) / 3.0 + 0.05, c.get_s() * 0.75, c.get_v());
+ spin[i]->set_custom_label_color(true, c);
+ }
+ }
+}
void EditorPropertyTransform2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyTransform2D::_value_changed);
@@ -1334,10 +1423,11 @@ EditorPropertyTransform2D::EditorPropertyTransform2D() {
g->set_columns(2);
add_child(g);
- static const char *desc[6] = { "xx", "xy", "yx", "yy", "ox", "oy" };
+ static const char *desc[6] = { "x", "y", "x", "y", "x", "y" };
for (int i = 0; i < 6; i++) {
spin[i] = memnew(EditorSpinSlider);
spin[i]->set_label(desc[i]);
+ spin[i]->set_flat(true);
g->add_child(spin[i]);
spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
add_focusable(spin[i]);
@@ -1382,7 +1472,17 @@ void EditorPropertyBasis::update_property() {
setting = false;
}
+void EditorPropertyBasis::_notification(int p_what) {
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ Color base = get_color("accent_color", "Editor");
+ for (int i = 0; i < 9; i++) {
+ Color c = base;
+ c.set_hsv(float(i % 3) / 3.0 + 0.05, c.get_s() * 0.75, c.get_v());
+ spin[i]->set_custom_label_color(true, c);
+ }
+ }
+}
void EditorPropertyBasis::_bind_methods() {
ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyBasis::_value_changed);
@@ -1402,10 +1502,11 @@ EditorPropertyBasis::EditorPropertyBasis() {
g->set_columns(3);
add_child(g);
- static const char *desc[9] = { "xx", "xy", "xz", "yx", "yy", "yz", "zx", "zy", "zz" };
+ static const char *desc[9] = { "x", "y", "z", "x", "y", "z", "x", "y", "z" };
for (int i = 0; i < 9; i++) {
spin[i] = memnew(EditorSpinSlider);
spin[i]->set_label(desc[i]);
+ spin[i]->set_flat(true);
g->add_child(spin[i]);
spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
add_focusable(spin[i]);
@@ -1456,7 +1557,17 @@ void EditorPropertyTransform::update_property() {
setting = false;
}
+void EditorPropertyTransform::_notification(int p_what) {
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ Color base = get_color("accent_color", "Editor");
+ for (int i = 0; i < 12; i++) {
+ Color c = base;
+ c.set_hsv(float(i % 3) / 3.0 + 0.05, c.get_s() * 0.75, c.get_v());
+ spin[i]->set_custom_label_color(true, c);
+ }
+ }
+}
void EditorPropertyTransform::_bind_methods() {
ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyTransform::_value_changed);
@@ -1476,10 +1587,11 @@ EditorPropertyTransform::EditorPropertyTransform() {
g->set_columns(3);
add_child(g);
- static const char *desc[12] = { "xx", "xy", "xz", "yx", "yy", "yz", "zx", "zy", "zz", "ox", "oy", "oz" };
+ static const char *desc[12] = { "x", "y", "z", "x", "y", "z", "x", "y", "z", "x", "y", "z" };
for (int i = 0; i < 12; i++) {
spin[i] = memnew(EditorSpinSlider);
spin[i]->set_label(desc[i]);
+ spin[i]->set_flat(true);
g->add_child(spin[i]);
spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
add_focusable(spin[i]);
@@ -1826,7 +1938,19 @@ void EditorPropertyResource::_resource_preview(const String &p_path, const Ref<T
RES p = get_edited_object()->get(get_edited_property());
if (p.is_valid() && p->get_instance_id() == p_obj) {
if (p_preview.is_valid()) {
- assign->set_icon(p_preview);
+ String type = p->get_class_name();
+ preview->set_margin(MARGIN_LEFT, assign->get_icon()->get_width() + assign->get_stylebox("normal")->get_default_margin(MARGIN_LEFT) + get_constant("hseparation", "Button"));
+ if (type == "GradientTexture") {
+ preview->set_stretch_mode(TextureRect::STRETCH_SCALE);
+ assign->set_custom_minimum_size(Size2(1, 1));
+ } else {
+ preview->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
+ int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size");
+ thumbnail_size *= EDSCALE;
+ assign->set_custom_minimum_size(Size2(1, thumbnail_size));
+ }
+ preview->set_texture(p_preview);
+ assign->set_text("");
}
}
}
@@ -2000,7 +2124,7 @@ void EditorPropertyResource::_sub_inspector_object_id_selected(int p_id) {
void EditorPropertyResource::_open_editor_pressed() {
RES res = get_edited_object()->get(get_edited_property());
if (res.is_valid()) {
- EditorNode::get_singleton()->edit_item(res.ptr());
+ EditorNode::get_singleton()->edit_resource(res.ptr());
}
}
@@ -2020,6 +2144,9 @@ void EditorPropertyResource::update_property() {
sub_inspector = memnew(EditorInspector);
sub_inspector->set_enable_v_scroll(false);
+ sub_inspector->set_use_sub_inspector_bg(true);
+ sub_inspector->set_enable_capitalize_paths(true);
+
sub_inspector->connect("property_keyed", this, "_sub_inspector_property_keyed");
sub_inspector->connect("resource_selected", this, "_sub_inspector_resource_selected");
sub_inspector->connect("object_id_selected", this, "_sub_inspector_object_id_selected");
@@ -2067,6 +2194,7 @@ void EditorPropertyResource::update_property() {
#endif
}
+ preview->set_texture(Ref<Texture>());
if (res == RES()) {
assign->set_icon(Ref<Texture>());
assign->set_text(TTR("[empty]"));
@@ -2266,6 +2394,10 @@ void EditorPropertyResource::drop_data_fw(const Point2 &p_point, const Variant &
}
}
+void EditorPropertyResource::set_use_sub_inspector(bool p_enable) {
+ use_sub_inspector = p_enable;
+}
+
void EditorPropertyResource::_bind_methods() {
ClassDB::bind_method(D_METHOD("_file_selected"), &EditorPropertyResource::_file_selected);
@@ -2288,7 +2420,8 @@ EditorPropertyResource::EditorPropertyResource() {
sub_inspector = NULL;
sub_inspector_vbox = NULL;
- use_sub_inspector = !bool(EDITOR_GET("interface/inspector/open_resources_in_new_inspector"));
+ use_sub_inspector = bool(EDITOR_GET("interface/inspector/open_resources_in_current_inspector"));
+
HBoxContainer *hbc = memnew(HBoxContainer);
add_child(hbc);
assign = memnew(Button);
@@ -2300,6 +2433,14 @@ EditorPropertyResource::EditorPropertyResource() {
assign->connect("draw", this, "_button_draw");
hbc->add_child(assign);
+ preview = memnew(TextureRect);
+ preview->set_expand(true);
+ preview->set_anchors_and_margins_preset(PRESET_WIDE);
+ preview->set_margin(MARGIN_TOP, 1);
+ preview->set_margin(MARGIN_BOTTOM, -1);
+ preview->set_margin(MARGIN_RIGHT, -1);
+ assign->add_child(preview);
+
menu = memnew(PopupMenu);
add_child(menu);
edit = memnew(Button);
@@ -2691,6 +2832,22 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ
case Variant::OBJECT: {
EditorPropertyResource *editor = memnew(EditorPropertyResource);
editor->setup(p_hint == PROPERTY_HINT_RESOURCE_TYPE ? p_hint_text : "Resource");
+
+ if (p_hint == PROPERTY_HINT_RESOURCE_TYPE) {
+ String open_in_new = EDITOR_GET("interface/inspector/resources_types_to_open_in_new_inspector");
+ for (int i = 0; i < open_in_new.get_slice_count(","); i++) {
+ String type = open_in_new.get_slicec(',', i).strip_edges();
+ for (int j = 0; j < p_hint_text.get_slice_count(","); j++) {
+ String inherits = p_hint_text.get_slicec(',', j);
+
+ if (ClassDB::is_parent_class(inherits, type)) {
+
+ editor->set_use_sub_inspector(false);
+ }
+ }
+ }
+ }
+
add_property_editor(p_path, editor);
} break;
diff --git a/editor/editor_properties.h b/editor/editor_properties.h
index c67eccb60e..0afb1bf955 100644
--- a/editor/editor_properties.h
+++ b/editor/editor_properties.h
@@ -178,6 +178,7 @@ protected:
public:
void setup(const Vector<String> &p_options);
virtual void update_property();
+ void set_option_button_clip(bool p_enable);
EditorPropertyEnum();
};
@@ -277,16 +278,26 @@ public:
class EditorPropertyEasing : public EditorProperty {
GDCLASS(EditorPropertyEasing, EditorProperty)
Control *easing_draw;
- ToolButton *button_out, *button_in, *button_linear, *button_constant;
- ToolButton *button_in_out, *button_out_in;
- VBoxContainer *vb;
+ PopupMenu *preset;
+ bool full;
+
+ enum {
+ EASING_ZERO,
+ EASING_LINEAR,
+ EASING_IN,
+ EASING_OUT,
+ EASING_IN_OUT,
+ EASING_OUT_IN,
+ EASING_MAX
+
+ };
bool flip;
void _drag_easing(const Ref<InputEvent> &p_ev);
void _draw_easing();
void _notification(int p_what);
- void _set_preset(float p_val);
+ void _set_preset(int);
protected:
static void _bind_methods();
@@ -304,6 +315,7 @@ class EditorPropertyVector2 : public EditorProperty {
void _value_changed(double p_val);
protected:
+ void _notification(int p_what);
static void _bind_methods();
public:
@@ -319,6 +331,7 @@ class EditorPropertyRect2 : public EditorProperty {
void _value_changed(double p_val);
protected:
+ void _notification(int p_what);
static void _bind_methods();
public:
@@ -334,6 +347,7 @@ class EditorPropertyVector3 : public EditorProperty {
void _value_changed(double p_val);
protected:
+ void _notification(int p_what);
static void _bind_methods();
public:
@@ -349,6 +363,7 @@ class EditorPropertyPlane : public EditorProperty {
void _value_changed(double p_val);
protected:
+ void _notification(int p_what);
static void _bind_methods();
public:
@@ -364,6 +379,7 @@ class EditorPropertyQuat : public EditorProperty {
void _value_changed(double p_val);
protected:
+ void _notification(int p_what);
static void _bind_methods();
public:
@@ -379,6 +395,7 @@ class EditorPropertyAABB : public EditorProperty {
void _value_changed(double p_val);
protected:
+ void _notification(int p_what);
static void _bind_methods();
public:
@@ -394,6 +411,7 @@ class EditorPropertyTransform2D : public EditorProperty {
void _value_changed(double p_val);
protected:
+ void _notification(int p_what);
static void _bind_methods();
public:
@@ -409,6 +427,7 @@ class EditorPropertyBasis : public EditorProperty {
void _value_changed(double p_val);
protected:
+ void _notification(int p_what);
static void _bind_methods();
public:
@@ -424,6 +443,7 @@ class EditorPropertyTransform : public EditorProperty {
void _value_changed(double p_val);
protected:
+ void _notification(int p_what);
static void _bind_methods();
public:
@@ -487,6 +507,7 @@ class EditorPropertyResource : public EditorProperty {
};
Button *assign;
+ TextureRect *preview;
Button *edit;
PopupMenu *menu;
EditorFileDialog *file;
@@ -531,6 +552,8 @@ public:
void collapse_all_folding();
void expand_all_folding();
+ void set_use_sub_inspector(bool p_enable);
+
EditorPropertyResource();
};
diff --git a/editor/editor_sectioned_inspector.cpp b/editor/editor_sectioned_inspector.cpp
new file mode 100644
index 0000000000..72050cd79b
--- /dev/null
+++ b/editor/editor_sectioned_inspector.cpp
@@ -0,0 +1,306 @@
+#include "editor_sectioned_inspector.h"
+#include "editor_scale.h"
+class SectionedInspectorFilter : public Object {
+
+ GDCLASS(SectionedInspectorFilter, Object);
+
+ Object *edited;
+ String section;
+ bool allow_sub;
+
+ bool _set(const StringName &p_name, const Variant &p_value) {
+
+ if (!edited)
+ return false;
+
+ String name = p_name;
+ if (section != "") {
+ name = section + "/" + name;
+ }
+
+ bool valid;
+ edited->set(name, p_value, &valid);
+ return valid;
+ }
+
+ bool _get(const StringName &p_name, Variant &r_ret) const {
+
+ if (!edited)
+ return false;
+
+ String name = p_name;
+ if (section != "") {
+ name = section + "/" + name;
+ }
+
+ bool valid = false;
+
+ r_ret = edited->get(name, &valid);
+ return valid;
+ }
+ void _get_property_list(List<PropertyInfo> *p_list) const {
+
+ if (!edited)
+ return;
+
+ List<PropertyInfo> pinfo;
+ edited->get_property_list(&pinfo);
+ for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
+
+ PropertyInfo pi = E->get();
+ int sp = pi.name.find("/");
+
+ if (pi.name == "resource_path" || pi.name == "resource_name" || pi.name == "resource_local_to_scene" || pi.name.begins_with("script/")) //skip resource stuff
+ continue;
+
+ if (sp == -1) {
+ pi.name = "global/" + pi.name;
+ }
+
+ if (pi.name.begins_with(section + "/")) {
+ pi.name = pi.name.replace_first(section + "/", "");
+ if (!allow_sub && pi.name.find("/") != -1)
+ continue;
+ p_list->push_back(pi);
+ }
+ }
+ }
+
+ bool property_can_revert(const String &p_name) {
+
+ return edited->call("property_can_revert", section + "/" + p_name);
+ }
+
+ Variant property_get_revert(const String &p_name) {
+
+ return edited->call("property_get_revert", section + "/" + p_name);
+ }
+
+protected:
+ static void _bind_methods() {
+
+ ClassDB::bind_method("property_can_revert", &SectionedInspectorFilter::property_can_revert);
+ ClassDB::bind_method("property_get_revert", &SectionedInspectorFilter::property_get_revert);
+ }
+
+public:
+ void set_section(const String &p_section, bool p_allow_sub) {
+
+ section = p_section;
+ allow_sub = p_allow_sub;
+ _change_notify();
+ }
+
+ void set_edited(Object *p_edited) {
+ edited = p_edited;
+ _change_notify();
+ }
+
+ SectionedInspectorFilter() {
+ edited = NULL;
+ }
+};
+
+void SectionedInspector::_bind_methods() {
+
+ ClassDB::bind_method("_section_selected", &SectionedInspector::_section_selected);
+ ClassDB::bind_method("_search_changed", &SectionedInspector::_search_changed);
+
+ ClassDB::bind_method("update_category_list", &SectionedInspector::update_category_list);
+}
+
+void SectionedInspector::_section_selected() {
+
+ if (!sections->get_selected())
+ return;
+
+ filter->set_section(sections->get_selected()->get_metadata(0), sections->get_selected()->get_children() == NULL);
+ inspector->set_property_prefix(String(sections->get_selected()->get_metadata(0)) + "/");
+}
+
+void SectionedInspector::set_current_section(const String &p_section) {
+
+ if (section_map.has(p_section)) {
+ section_map[p_section]->select(0);
+ }
+}
+
+String SectionedInspector::get_current_section() const {
+
+ if (sections->get_selected())
+ return sections->get_selected()->get_metadata(0);
+ else
+ return "";
+}
+
+String SectionedInspector::get_full_item_path(const String &p_item) {
+
+ String base = get_current_section();
+
+ if (base != "")
+ return base + "/" + p_item;
+ else
+ return p_item;
+}
+
+void SectionedInspector::edit(Object *p_object) {
+
+ if (!p_object) {
+ obj = -1;
+ sections->clear();
+
+ filter->set_edited(NULL);
+ inspector->edit(NULL);
+
+ return;
+ }
+
+ ObjectID id = p_object->get_instance_id();
+
+ inspector->set_object_class(p_object->get_class());
+
+ if (obj != id) {
+
+ obj = id;
+ update_category_list();
+
+ filter->set_edited(p_object);
+ inspector->edit(filter);
+
+ if (sections->get_root()->get_children()) {
+ sections->get_root()->get_children()->select(0);
+ }
+ } else {
+
+ update_category_list();
+ }
+}
+
+void SectionedInspector::update_category_list() {
+
+ String selected_category = get_current_section();
+ sections->clear();
+
+ Object *o = ObjectDB::get_instance(obj);
+
+ if (!o)
+ return;
+
+ List<PropertyInfo> pinfo;
+ o->get_property_list(&pinfo);
+
+ section_map.clear();
+
+ TreeItem *root = sections->create_item();
+ section_map[""] = root;
+
+ for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
+
+ PropertyInfo pi = E->get();
+
+ if (pi.usage & PROPERTY_USAGE_CATEGORY)
+ continue;
+ else if (!(pi.usage & PROPERTY_USAGE_EDITOR))
+ continue;
+
+ if (pi.name.find(":") != -1 || pi.name == "script" || pi.name == "resource_name" || pi.name == "resource_path" || pi.name == "resource_local_to_scene")
+ continue;
+
+ if (search_box && search_box->get_text() != String() && pi.name.findn(search_box->get_text()) == -1)
+ continue;
+
+ int sp = pi.name.find("/");
+ if (sp == -1)
+ pi.name = "Global/" + pi.name;
+
+ Vector<String> sectionarr = pi.name.split("/");
+ String metasection;
+
+ int sc = MIN(2, sectionarr.size() - 1);
+
+ for (int i = 0; i < sc; i++) {
+
+ TreeItem *parent = section_map[metasection];
+ parent->set_custom_bg_color(0, get_color("prop_subsection", "Editor"));
+
+ if (i > 0) {
+ metasection += "/" + sectionarr[i];
+ } else {
+ metasection = sectionarr[i];
+ }
+
+ if (!section_map.has(metasection)) {
+ TreeItem *ms = sections->create_item(parent);
+ section_map[metasection] = ms;
+ ms->set_text(0, sectionarr[i].capitalize());
+ ms->set_metadata(0, metasection);
+ ms->set_selectable(0, false);
+ }
+
+ if (i == sc - 1) {
+ //if it has children, make selectable
+ section_map[metasection]->set_selectable(0, true);
+ }
+ }
+ }
+
+ if (section_map.has(selected_category)) {
+ section_map[selected_category]->select(0);
+ }
+
+ inspector->update_tree();
+}
+
+void SectionedInspector::register_search_box(LineEdit *p_box) {
+
+ search_box = p_box;
+ inspector->register_text_enter(p_box);
+ search_box->connect("text_changed", this, "_search_changed");
+}
+
+void SectionedInspector::_search_changed(const String &p_what) {
+
+ update_category_list();
+}
+
+EditorInspector *SectionedInspector::get_inspector() {
+
+ return inspector;
+}
+
+SectionedInspector::SectionedInspector() {
+
+ obj = -1;
+
+ search_box = NULL;
+
+ add_constant_override("autohide", 1); // Fixes the dragger always showing up
+
+ VBoxContainer *left_vb = memnew(VBoxContainer);
+ left_vb->set_custom_minimum_size(Size2(170, 0) * EDSCALE);
+ add_child(left_vb);
+
+ sections = memnew(Tree);
+ sections->set_v_size_flags(SIZE_EXPAND_FILL);
+ sections->set_hide_root(true);
+
+ left_vb->add_child(sections, true);
+
+ VBoxContainer *right_vb = memnew(VBoxContainer);
+ right_vb->set_custom_minimum_size(Size2(300, 0) * EDSCALE);
+ right_vb->set_h_size_flags(SIZE_EXPAND_FILL);
+ add_child(right_vb);
+
+ filter = memnew(SectionedInspectorFilter);
+ inspector = memnew(EditorInspector);
+ inspector->set_v_size_flags(SIZE_EXPAND_FILL);
+ right_vb->add_child(inspector, true);
+ inspector->set_use_doc_hints(true);
+
+ sections->connect("cell_selected", this, "_section_selected");
+}
+
+SectionedInspector::~SectionedInspector() {
+
+ memdelete(filter);
+}
diff --git a/editor/editor_sectioned_inspector.h b/editor/editor_sectioned_inspector.h
new file mode 100644
index 0000000000..75b51a1581
--- /dev/null
+++ b/editor/editor_sectioned_inspector.h
@@ -0,0 +1,42 @@
+#ifndef EDITOR_SECTIONED_INSPECTOR_H
+#define EDITOR_SECTIONED_INSPECTOR_H
+
+#include "editor/editor_inspector.h"
+#include "scene/gui/split_container.h"
+#include "scene/gui/tree.h"
+
+class SectionedInspectorFilter;
+
+class SectionedInspector : public HSplitContainer {
+
+ GDCLASS(SectionedInspector, HSplitContainer);
+
+ ObjectID obj;
+
+ Tree *sections;
+ SectionedInspectorFilter *filter;
+
+ Map<String, TreeItem *> section_map;
+ EditorInspector *inspector;
+ LineEdit *search_box;
+
+ static void _bind_methods();
+ void _section_selected();
+
+ void _search_changed(const String &p_what);
+
+public:
+ void register_search_box(LineEdit *p_box);
+ EditorInspector *get_inspector();
+ void edit(Object *p_object);
+ String get_full_item_path(const String &p_item);
+
+ void set_current_section(const String &p_section);
+ String get_current_section() const;
+
+ void update_category_list();
+
+ SectionedInspector();
+ ~SectionedInspector();
+};
+#endif // EDITOR_SECTIONED_INSPECTOR_H
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index 4045d6c3d3..4cfdb6f6d3 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -165,6 +165,7 @@ struct _EVCSort {
Variant::Type type;
int order;
bool save;
+ bool restart_if_changed;
bool operator<(const _EVCSort &p_vcs) const { return order < p_vcs.order; }
};
@@ -188,6 +189,7 @@ void EditorSettings::_get_property_list(List<PropertyInfo> *p_list) const {
vc.order = v->order;
vc.type = v->variant.get_type();
vc.save = v->save;
+ vc.restart_if_changed = v->restart_if_changed;
vclist.insert(vc);
}
@@ -210,6 +212,10 @@ void EditorSettings::_get_property_list(List<PropertyInfo> *p_list) const {
if (hints.has(E->get().name))
pi = hints[E->get().name];
+ if (E->get().restart_if_changed) {
+ pi.usage |= PROPERTY_USAGE_RESTART_IF_CHANGED;
+ }
+
p_list->push_back(pi);
}
@@ -280,6 +286,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
}
_initial_set("interface/editor/editor_language", best);
+ set_restart_if_changed("interface/editor/editor_language", true);
hints["interface/editor/editor_language"] = PropertyInfo(Variant::STRING, "interface/editor/editor_language", PROPERTY_HINT_ENUM, lang_hint, PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
}
@@ -1017,6 +1024,14 @@ void EditorSettings::raise_order(const String &p_setting) {
props[p_setting].order = ++last_order;
}
+void EditorSettings::set_restart_if_changed(const StringName &p_setting, bool p_restart) {
+ _THREAD_SAFE_METHOD_
+
+ if (!props.has(p_setting))
+ return;
+ props[p_setting].restart_if_changed = p_restart;
+}
+
void EditorSettings::set_initial_value(const StringName &p_setting, const Variant &p_value, bool p_update_current) {
_THREAD_SAFE_METHOD_
@@ -1030,16 +1045,19 @@ void EditorSettings::set_initial_value(const StringName &p_setting, const Varian
}
}
-Variant _EDITOR_DEF(const String &p_setting, const Variant &p_default) {
+Variant _EDITOR_DEF(const String &p_setting, const Variant &p_default, bool p_restart_if_changed) {
Variant ret = p_default;
- if (EditorSettings::get_singleton()->has_setting(p_setting))
+ if (EditorSettings::get_singleton()->has_setting(p_setting)) {
ret = EditorSettings::get_singleton()->get(p_setting);
- else
+ } else {
EditorSettings::get_singleton()->set_manually(p_setting, p_default);
+ EditorSettings::get_singleton()->set_restart_if_changed(p_setting, p_restart_if_changed);
+ }
- if (!EditorSettings::get_singleton()->has_default_value(p_setting))
+ if (!EditorSettings::get_singleton()->has_default_value(p_setting)) {
EditorSettings::get_singleton()->set_initial_value(p_setting, p_default);
+ }
return ret;
}
diff --git a/editor/editor_settings.h b/editor/editor_settings.h
index 420e067cad..e5b61abf54 100644
--- a/editor/editor_settings.h
+++ b/editor/editor_settings.h
@@ -70,6 +70,7 @@ private:
bool has_default_value;
bool hide_from_editor;
bool save;
+ bool restart_if_changed;
VariantContainer() {
variant = Variant();
initial = Variant();
@@ -77,6 +78,7 @@ private:
hide_from_editor = false;
has_default_value = false;
save = false;
+ restart_if_changed = false;
}
VariantContainer(const Variant &p_variant, int p_order) {
variant = p_variant;
@@ -85,6 +87,7 @@ private:
hide_from_editor = false;
has_default_value = false;
save = false;
+ restart_if_changed = false;
}
};
@@ -145,6 +148,7 @@ public:
void erase(const String &p_setting);
void raise_order(const String &p_setting);
void set_initial_value(const StringName &p_setting, const Variant &p_value, bool p_update_current = false);
+ void set_restart_if_changed(const StringName &p_setting, bool p_restart);
void set_manually(const StringName &p_setting, const Variant &p_value, bool p_emit_signal = false) {
if (p_emit_signal)
_set(p_setting, p_value);
@@ -200,7 +204,8 @@ public:
//not a macro any longer
#define EDITOR_DEF(m_var, m_val) _EDITOR_DEF(m_var, Variant(m_val))
-Variant _EDITOR_DEF(const String &p_setting, const Variant &p_default);
+#define EDITOR_DEF_RST(m_var, m_val) _EDITOR_DEF(m_var, Variant(m_val), true)
+Variant _EDITOR_DEF(const String &p_setting, const Variant &p_default, bool p_restart_if_changed = false);
#define EDITOR_GET(m_var) _EDITOR_GET(m_var)
Variant _EDITOR_GET(const String &p_setting);
diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp
index c7a33de3f1..0e6d81d13b 100644
--- a/editor/editor_spin_slider.cpp
+++ b/editor/editor_spin_slider.cpp
@@ -90,10 +90,12 @@ void EditorSpinSlider::_gui_input(const Ref<InputEvent> &p_event) {
}
grabbing_spinner_dist_cache += diff_x;
- if (!grabbing_spinner && ABS(grabbing_spinner_dist_cache) > 4) {
+ if (!grabbing_spinner && ABS(grabbing_spinner_dist_cache) > 4 * EDSCALE) {
Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED);
grabbing_spinner = true;
- } else {
+ }
+
+ if (grabbing_spinner) {
if (mm->get_control() || updown_offset != -1) {
set_value(Math::round(get_value()));
if (ABS(grabbing_spinner_dist_cache) > 6) {
@@ -159,20 +161,20 @@ void EditorSpinSlider::_notification(int p_what) {
updown_offset = -1;
Ref<StyleBox> sb = get_stylebox("normal", "LineEdit");
- draw_style_box(sb, Rect2(Vector2(), get_size()));
+ if (!flat) {
+ draw_style_box(sb, Rect2(Vector2(), get_size()));
+ }
Ref<Font> font = get_font("font", "LineEdit");
+ int sep_base = 4 * EDSCALE;
+ int sep = sep_base + sb->get_offset().x; //make it have the same margin on both sides, looks better
+
+ int string_width = font->get_string_size(label).width;
+ int number_width = get_size().width - sb->get_minimum_size().width - string_width - sep;
- int avail_width = get_size().width - sb->get_minimum_size().width;
- avail_width -= font->get_string_size(label).width;
Ref<Texture> updown = get_icon("updown", "SpinBox");
if (get_step() == 1) {
- avail_width -= updown->get_width();
- }
-
- if (has_focus()) {
- Ref<StyleBox> focus = get_stylebox("focus", "LineEdit");
- draw_style_box(focus, Rect2(Vector2(), get_size()));
+ number_width -= updown->get_width();
}
String numstr = get_text_value();
@@ -180,10 +182,26 @@ void EditorSpinSlider::_notification(int p_what) {
int vofs = (get_size().height - font->get_height()) / 2 + font->get_ascent();
Color fc = get_color("font_color", "LineEdit");
+ Color lc;
+ if (use_custom_label_color) {
+ lc = custom_label_color;
+ } else {
+ lc = fc;
+ }
- int label_ofs = sb->get_offset().x + avail_width;
- draw_string(font, Vector2(label_ofs, vofs), label, fc * Color(1, 1, 1, 0.5));
- draw_string(font, Vector2(sb->get_offset().x, vofs), numstr, fc, avail_width);
+ if (flat && label != String()) {
+ Color label_bg_color = get_color("dark_color_3", "Editor");
+ draw_rect(Rect2(Vector2(), Vector2(sb->get_offset().x * 2 + string_width, get_size().height)), label_bg_color);
+ }
+
+ if (has_focus()) {
+ Ref<StyleBox> focus = get_stylebox("focus", "LineEdit");
+ draw_style_box(focus, Rect2(Vector2(), get_size()));
+ }
+
+ draw_string(font, Vector2(sb->get_offset().x, vofs), label, lc * Color(1, 1, 1, 0.5));
+
+ draw_string(font, Vector2(sb->get_offset().x + string_width + sep, vofs), numstr, fc, number_width);
if (get_step() == 1) {
Ref<Texture> updown = get_icon("updown", "SpinBox");
@@ -250,9 +268,12 @@ void EditorSpinSlider::_notification(int p_what) {
update();
}
if (p_what == NOTIFICATION_FOCUS_ENTER) {
- if (!Input::get_singleton()->is_mouse_button_pressed(BUTTON_LEFT) && !value_input_just_closed) {
+ /* Sorry, I dont like this, it makes navigating the different fields with arrows more difficult.
+ * Just press enter to edit.
+ * if (!Input::get_singleton()->is_mouse_button_pressed(BUTTON_LEFT) && !value_input_just_closed) {
_focus_entered();
- }
+ }*/
+
value_input_just_closed = false;
}
}
@@ -334,6 +355,21 @@ bool EditorSpinSlider::is_read_only() const {
return read_only;
}
+void EditorSpinSlider::set_flat(bool p_enable) {
+
+ flat = p_enable;
+ update();
+}
+
+bool EditorSpinSlider::is_flat() const {
+ return flat;
+}
+
+void EditorSpinSlider::set_custom_label_color(bool p_use_custom_label_color, Color p_custom_label_color) {
+ use_custom_label_color = p_use_custom_label_color;
+ custom_label_color = p_custom_label_color;
+}
+
void EditorSpinSlider::_focus_entered() {
Rect2 gr = get_global_rect();
value_input->set_text(get_text_value());
@@ -353,6 +389,9 @@ void EditorSpinSlider::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_read_only", "read_only"), &EditorSpinSlider::set_read_only);
ClassDB::bind_method(D_METHOD("is_read_only"), &EditorSpinSlider::is_read_only);
+ ClassDB::bind_method(D_METHOD("set_flat", "flat"), &EditorSpinSlider::set_flat);
+ ClassDB::bind_method(D_METHOD("is_flat"), &EditorSpinSlider::is_flat);
+
ClassDB::bind_method(D_METHOD("_gui_input"), &EditorSpinSlider::_gui_input);
ClassDB::bind_method(D_METHOD("_grabber_mouse_entered"), &EditorSpinSlider::_grabber_mouse_entered);
ClassDB::bind_method(D_METHOD("_grabber_mouse_exited"), &EditorSpinSlider::_grabber_mouse_exited);
@@ -363,10 +402,12 @@ void EditorSpinSlider::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "label"), "set_label", "get_label");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "read_only"), "set_read_only", "is_read_only");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat");
}
EditorSpinSlider::EditorSpinSlider() {
+ flat = false;
grabbing_spinner_attempt = false;
grabbing_spinner = false;
grabbing_spinner_dist_cache = 0;
@@ -395,4 +436,5 @@ EditorSpinSlider::EditorSpinSlider() {
value_input_just_closed = false;
hide_slider = false;
read_only = false;
+ use_custom_label_color = false;
}
diff --git a/editor/editor_spin_slider.h b/editor/editor_spin_slider.h
index 5316c0264a..fb32534ef4 100644
--- a/editor/editor_spin_slider.h
+++ b/editor/editor_spin_slider.h
@@ -68,6 +68,10 @@ class EditorSpinSlider : public Range {
void _value_input_entered(const String &);
void _value_focus_exited();
bool hide_slider;
+ bool flat;
+
+ bool use_custom_label_color;
+ Color custom_label_color;
protected:
void _notification(int p_what);
@@ -88,6 +92,11 @@ public:
void set_read_only(bool p_enable);
bool is_read_only() const;
+ void set_flat(bool p_enable);
+ bool is_flat() const;
+
+ void set_custom_label_color(bool p_use_custom_label_color, Color p_custom_label_color);
+
virtual Size2 get_minimum_size() const;
EditorSpinSlider();
};
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 98402d8df5..084caff083 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -72,10 +72,11 @@ static Ref<StyleBoxFlat> make_flat_stylebox(Color p_color, float p_margin_left =
return style;
}
-static Ref<StyleBoxLine> make_line_stylebox(Color p_color, int p_thickness = 1, float p_grow = 1, bool p_vertical = false) {
+static Ref<StyleBoxLine> make_line_stylebox(Color p_color, int p_thickness = 1, float p_grow_begin = 1, float p_grow_end = 1, bool p_vertical = false) {
Ref<StyleBoxLine> style(memnew(StyleBoxLine));
style->set_color(p_color);
- style->set_grow(p_grow);
+ style->set_grow_begin(p_grow_begin);
+ style->set_grow_end(p_grow_end);
style->set_thickness(p_thickness);
style->set_vertical(p_vertical);
return style;
@@ -462,9 +463,20 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
Ref<StyleBoxLine> style_popup_separator(memnew(StyleBoxLine));
style_popup_separator->set_color(separator_color);
- style_popup_separator->set_grow(popup_margin_size - MAX(EDSCALE, border_width));
+ style_popup_separator->set_grow_begin(popup_margin_size - MAX(EDSCALE, border_width));
+ style_popup_separator->set_grow_end(popup_margin_size - MAX(EDSCALE, border_width));
style_popup_separator->set_thickness(MAX(EDSCALE, border_width));
+ Ref<StyleBoxLine> style_popup_labeled_separator_left(memnew(StyleBoxLine));
+ style_popup_labeled_separator_left->set_grow_begin(popup_margin_size - MAX(EDSCALE, border_width));
+ style_popup_labeled_separator_left->set_color(separator_color);
+ style_popup_labeled_separator_left->set_thickness(MAX(EDSCALE, border_width));
+
+ Ref<StyleBoxLine> style_popup_labeled_separator_right(memnew(StyleBoxLine));
+ style_popup_labeled_separator_right->set_grow_end(popup_margin_size - MAX(EDSCALE, border_width));
+ style_popup_labeled_separator_right->set_color(separator_color);
+ style_popup_labeled_separator_right->set_thickness(MAX(EDSCALE, border_width));
+
Ref<StyleBoxEmpty> style_empty = make_empty_stylebox(default_margin_size, default_margin_size, default_margin_size, default_margin_size);
// Tabs
@@ -578,6 +590,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_icon("arrow", "OptionButton", theme->get_icon("GuiOptionArrow", "EditorIcons"));
theme->set_constant("arrow_margin", "OptionButton", default_margin_size * EDSCALE);
theme->set_constant("modulate_arrow", "OptionButton", true);
+ theme->set_constant("hseparation", "OptionButton", 4 * EDSCALE);
// CheckButton
theme->set_stylebox("normal", "CheckButton", style_menu);
@@ -626,6 +639,9 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
Ref<StyleBoxFlat> style_popup_menu = style_popup;
theme->set_stylebox("panel", "PopupMenu", style_popup_menu);
theme->set_stylebox("separator", "PopupMenu", style_popup_separator);
+ theme->set_stylebox("labeled_separator_left", "PopupMenu", style_popup_labeled_separator_left);
+ theme->set_stylebox("labeled_separator_right", "PopupMenu", style_popup_labeled_separator_right);
+
theme->set_color("font_color", "PopupMenu", font_color);
theme->set_color("font_color_hover", "PopupMenu", font_color_hl);
theme->set_color("font_color_accel", "PopupMenu", font_color_disabled);
@@ -640,6 +656,14 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_icon("visibility_xray", "PopupMenu", theme->get_icon("GuiVisibilityXray", "EditorIcons"));
theme->set_constant("vseparation", "PopupMenu", (extra_spacing + default_margin_size) * EDSCALE);
+ Ref<StyleBoxFlat> sub_inspector_bg = make_flat_stylebox(dark_color_1, 2, 0, 0, 0);
+ sub_inspector_bg->set_border_width(MARGIN_LEFT, 2);
+ sub_inspector_bg->set_border_color(MARGIN_LEFT, accent_color * Color(1, 1, 1, 0.3));
+ sub_inspector_bg->set_draw_center(true);
+
+ theme->set_stylebox("sub_inspector_bg", "Editor", sub_inspector_bg);
+ theme->set_constant("inspector_margin", "Editor", 8 * EDSCALE);
+
// Tree & ItemList background
Ref<StyleBoxFlat> style_tree_bg = style_default->duplicate();
style_tree_bg->set_bg_color(dark_color_1);
@@ -650,6 +674,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
// Tree
theme->set_icon("checked", "Tree", theme->get_icon("GuiChecked", "EditorIcons"));
theme->set_icon("unchecked", "Tree", theme->get_icon("GuiUnchecked", "EditorIcons"));
+ theme->set_icon("arrow_up", "Tree", theme->get_icon("GuiTreeArrowUp", "EditorIcons"));
theme->set_icon("arrow", "Tree", theme->get_icon("GuiTreeArrowDown", "EditorIcons"));
theme->set_icon("arrow_collapsed", "Tree", theme->get_icon("GuiTreeArrowRight", "EditorIcons"));
theme->set_icon("updown", "Tree", theme->get_icon("GuiTreeUpdown", "EditorIcons"));
@@ -780,7 +805,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
// Separators
theme->set_stylebox("separator", "HSeparator", make_line_stylebox(separator_color, border_width));
- theme->set_stylebox("separator", "VSeparator", make_line_stylebox(separator_color, border_width, 0, true));
+ theme->set_stylebox("separator", "VSeparator", make_line_stylebox(separator_color, border_width, 0, 0, true));
// Debugger
@@ -1005,6 +1030,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_constant("title_h_offset", "GraphNode", -16 * EDSCALE);
theme->set_constant("close_h_offset", "GraphNode", 20 * EDSCALE);
theme->set_constant("close_offset", "GraphNode", 20 * EDSCALE);
+ theme->set_constant("separation", "GraphNode", 1 * EDSCALE);
+
theme->set_icon("close", "GraphNode", theme->get_icon("GuiCloseCustomizable", "EditorIcons"));
theme->set_icon("resizer", "GraphNode", theme->get_icon("GuiResizer", "EditorIcons"));
theme->set_icon("port", "GraphNode", theme->get_icon("GuiGraphNodePort", "EditorIcons"));
diff --git a/editor/export_template_manager.cpp b/editor/export_template_manager.cpp
index 541c848ca3..931785333f 100644
--- a/editor/export_template_manager.cpp
+++ b/editor/export_template_manager.cpp
@@ -123,7 +123,6 @@ 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));
}
@@ -228,7 +227,10 @@ void ExportTemplateManager::_install_from_file(const String &p_file, bool p_use_
version = data_str;
}
- fc++;
+ if (file.get_file().size() != 0) {
+ fc++;
+ }
+
ret = unzGoToNextFile(pkg);
}
@@ -268,6 +270,11 @@ void ExportTemplateManager::_install_from_file(const String &p_file, bool p_use_
String file = String(fname).get_file();
+ if (file.size() == 0) {
+ ret = unzGoToNextFile(pkg);
+ continue;
+ }
+
Vector<uint8_t> data;
data.resize(info.uncompressed_size);
@@ -344,7 +351,6 @@ void ExportTemplateManager::_http_download_mirror_completed(int p_status, int p_
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++) {
@@ -499,7 +505,6 @@ void ExportTemplateManager::_notification(int p_what) {
if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
if (!is_visible_in_tree()) {
- print_line("closed");
set_process(false);
}
}
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index eebf1b6ab8..f65fb5365b 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -1862,7 +1862,7 @@ void FileSystemDock::_bind_methods() {
ClassDB::bind_method(D_METHOD("_file_option"), &FileSystemDock::_file_option);
ClassDB::bind_method(D_METHOD("_folder_option"), &FileSystemDock::_folder_option);
ClassDB::bind_method(D_METHOD("_make_dir_confirm"), &FileSystemDock::_make_dir_confirm);
- ClassDB::bind_method(D_METHOD("_move_operation_confirm"), &FileSystemDock::_move_operation_confirm);
+ ClassDB::bind_method(D_METHOD("_move_operation_confirm", "to_path", "overwrite"), &FileSystemDock::_move_operation_confirm, DEFVAL(false));
ClassDB::bind_method(D_METHOD("_move_with_overwrite"), &FileSystemDock::_move_with_overwrite);
ClassDB::bind_method(D_METHOD("_rename_operation_confirm"), &FileSystemDock::_rename_operation_confirm);
ClassDB::bind_method(D_METHOD("_duplicate_operation_confirm"), &FileSystemDock::_duplicate_operation_confirm);
diff --git a/editor/icons/icon_GUI_tree_arrow_up.svg b/editor/icons/icon_GUI_tree_arrow_up.svg
new file mode 100644
index 0000000000..4e6e8e9e29
--- /dev/null
+++ b/editor/icons/icon_GUI_tree_arrow_up.svg
@@ -0,0 +1,60 @@
+<?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="12"
+ height="12"
+ version="1.1"
+ viewBox="0 0 12 12"
+ id="svg6"
+ sodipodi:docname="icon_GUI_tree_arrow_up.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata12">
+ <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="defs10" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1673"
+ inkscape:window-height="594"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="19.666667"
+ inkscape:cx="-4.3220338"
+ inkscape:cy="6.0000001"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg6" />
+ <g
+ transform="rotate(180,6,526.08476)"
+ id="g4">
+ <path
+ d="m 3,1045.4 3,3 3,-3"
+ id="path2"
+ inkscape:connector-curvature="0"
+ style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.39216003" />
+ </g>
+</svg>
diff --git a/editor/icons/icon_animated_texture.svg b/editor/icons/icon_animated_texture.svg
new file mode 100644
index 0000000000..dd039df6a7
--- /dev/null
+++ b/editor/icons/icon_animated_texture.svg
@@ -0,0 +1,74 @@
+<?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="svg6"
+ sodipodi:docname="icon_animated_texture.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata12">
+ <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="defs10">
+ <filter
+ inkscape:collect="always"
+ style="color-interpolation-filters:sRGB"
+ id="filter822"
+ x="-0.012"
+ width="1.024"
+ y="-0.012"
+ height="1.024">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.07"
+ id="feGaussianBlur824" />
+ </filter>
+ </defs>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="836"
+ inkscape:window-height="480"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="14.75"
+ inkscape:cx="8"
+ inkscape:cy="8"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="g4" />
+ <g
+ transform="translate(0 -1036.4)"
+ id="g4">
+ <path
+ d="m1 1037.4v14h1.1667v-2h1.8333v2h8v-2h2v2h1v-14h-1v2h-2v-2h-8v2h-1.8333v-2zm1.1667 4h1.8333v2h-1.8333zm9.8333 0h2v2h-2zm-9.8333 4h1.8333v2h-1.8333zm9.8333 0h2v2h-2z"
+ fill="#cea4f1"
+ id="path2"
+ style="fill:#e0e0e0;fill-opacity:1;filter:url(#filter822)" />
+ </g>
+</svg>
diff --git a/editor/icons/icon_expand_bottom_dock.svg b/editor/icons/icon_expand_bottom_dock.svg
new file mode 100644
index 0000000000..5a1760f377
--- /dev/null
+++ b/editor/icons/icon_expand_bottom_dock.svg
@@ -0,0 +1,70 @@
+<?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="svg6"
+ sodipodi:docname="icon_expand_bottom_dock.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata12">
+ <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="defs10" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1853"
+ inkscape:window-height="1016"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="20.85965"
+ inkscape:cx="9.4509357"
+ inkscape:cy="6.016355"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg6" />
+ <path
+ style="fill:#e0e0e0"
+ d="M 4.2130251,4.516057 0.6774912,8.0515909 H 3.2131761 V 13.025308 H 5.2130155 V 8.0515909 H 7.7487004 L 4.2131665,4.516057 Z"
+ id="path829"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccc" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path831"
+ d="M 11.907306,4.6119359 8.3717718,8.1474698 h 2.5356852 v 4.9737172 h 1.999839 V 8.1474698 h 2.535685 L 11.907447,4.6119359 Z"
+ style="fill:#e0e0e0"
+ sodipodi:nodetypes="ccccccccc" />
+ <rect
+ style="fill:#e0e0e0;fill-opacity:1"
+ id="rect855"
+ width="14"
+ height="1.8305085"
+ x="1.2881356"
+ y="1.3700738" />
+</svg>
diff --git a/editor/icons/icon_g_l_e_s_2.svg b/editor/icons/icon_g_l_e_s_2.svg
new file mode 100644
index 0000000000..efc4f01e4f
--- /dev/null
+++ b/editor/icons/icon_g_l_e_s_2.svg
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<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"
+ version="1.1"
+ id="svg2"
+ width="48"
+ height="16"
+ viewBox="0 0 47.999999 16"
+ sodipodi:docname="icon_g_l_e_s_2.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata8">
+ <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="defs6" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1484"
+ inkscape:window-height="697"
+ id="namedview4"
+ showgrid="false"
+ inkscape:zoom="13.520979"
+ inkscape:cx="20.549976"
+ inkscape:cy="7.9399684"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg2" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path835"
+ d="m 19.839879,15.499154 c -0.0652,-0.0268 -0.141743,-0.1098 -0.170113,-0.184417 -0.03304,-0.08688 -0.05158,-0.95731 -0.05158,-5.912028 V 3.1830459 l 0.108486,-0.1379162 c 0.150269,-0.1910365 0.41814,-0.1907342 0.568677,6.436e-4 l 0.10899,0.1385579 -0.01358,6.2990785 c -0.01194,6.8660953 -0.0921,5.3381383 -0.0921,5.9327083 -0.106573,0.104434 -0.315006,0.142158 -0.458762,0.08303 z M 5.3808767,14.575188 C 4.5309456,14.518738 3.6260357,14.196602 2.9750499,13.718734 2.5767564,13.42636 2.0035795,12.787236 1.747789,12.350269 1.2385669,11.480363 1.0170768,10.580508 1.0213778,9.399057 1.0293972,7.2009406 1.9726797,5.5285643 3.6891526,4.6693537 4.7813316,4.1226444 6.2246017,4.0371807 7.4330177,4.4476602 8.1309525,4.6847376 8.4685433,4.8972607 9.0207129,5.4471587 9.4063328,5.8311907 9.5338898,6.0004852 9.7108978,6.3631718 9.8335428,6.6144683 9.9681328,6.9987435 10.020175,7.2461971 10.145759,7.8433551 10.170431,7.8289765 9.0218356,7.828057 8.5307356,7.8276009 8.0769363,7.8134035 8.0133918,7.7963663 7.9392662,7.7764919 7.8757344,7.6970176 7.8361313,7.5746239 7.5012661,6.5397183 6.6297764,6.0267536 5.4889128,6.193037 4.244092,6.3744711 3.4980921,7.3344965 3.343357,8.9541432 3.2260083,10.182472 3.5434132,11.329338 4.1781352,11.97041 c 0.46237,0.466997 0.9869175,0.673904 1.7084683,0.673904 1.2025378,0 1.9439704,-0.533034 2.1862936,-1.57178 0.055989,-0.240028 0.059178,-0.324448 0.012859,-0.341503 -0.033838,-0.01246 -0.5090516,-0.02871 -1.0560342,-0.03612 L 6.0352096,10.681458 V 9.8178001 8.9541431 l 1.9890278,-0.014575 c 1.0939663,-0.00802 2.0422396,-0.00163 2.1072756,0.014201 l 0.118246,0.028779 -0.01356,2.6814549 -0.01356,2.681455 -0.7170922,0.01455 c -0.8295927,0.01682 -0.7753286,0.05076 -0.8815155,-0.55106 -0.036825,-0.208719 -0.077853,-0.379487 -0.091164,-0.379487 -0.013311,0 -0.16916,0.135437 -0.3463303,0.300972 -0.3894417,0.363866 -0.8188673,0.600316 -1.3418506,0.738852 -0.4725114,0.125166 -0.8081647,0.149449 -1.4638111,0.10591 z M 32.49721,14.469781 c -0.928547,-0.194854 -1.630354,-0.56605 -2.174913,-1.150343 -0.515384,-0.552992 -0.832054,-1.344249 -0.800629,-2.000518 l 0.01549,-0.323408 1.060826,-0.01418 1.060825,-0.01418 0.05146,0.135352 c 0.0283,0.07444 0.0517,0.198593 0.05197,0.275887 8.54e-4,0.230559 0.229434,0.649361 0.479979,0.879354 0.347226,0.318744 0.735307,0.44853 1.431019,0.478576 1.267096,0.05472 2.007349,-0.393206 1.947849,-1.178652 -0.0456,-0.601928 -0.471503,-0.860841 -2.12876,-1.294103 C 32.881626,10.103917 32.242852,9.9264243 32.07283,9.8691486 30.95902,9.4939337 30.283515,8.9537559 29.97948,8.195172 29.836139,7.8375288 29.784025,7.0484225 29.874852,6.6109088 30.100606,5.5234588 31.071976,4.6456053 32.416011,4.314394 33.01697,4.1662997 34.128873,4.156633 34.77144,4.293917 c 1.67335,0.3575071 2.584333,1.270761 2.774448,2.7813655 0.0543,0.4314615 0.0347,0.4394334 -1.080484,0.4394334 -0.521251,0 -0.9851,-0.023133 -1.038665,-0.051802 C 35.367672,7.4313026 35.307808,7.3078793 35.273143,7.1462409 35.195527,6.7843357 35.099156,6.6147944 34.849667,6.4012402 34.543832,6.1394568 34.14764,6.029069 33.515213,6.0294329 c -0.428465,2.111e-4 -0.570793,0.021517 -0.784491,0.1172346 -0.47592,0.2131691 -0.654939,0.4628549 -0.654939,0.9134748 0,0.5904894 0.225799,0.7059322 2.58195,1.3200619 1.350552,0.3520209 1.903346,0.598685 2.415601,1.0778741 0.591219,0.5530567 0.852295,1.2543747 0.796412,2.1393787 -0.07929,1.255762 -0.891416,2.255747 -2.192274,2.699402 -0.885807,0.302103 -2.21918,0.374602 -3.180262,0.172924 z M 11.476954,14.306572 c -0.0138,-0.03619 -0.019,-2.268126 -0.01158,-4.9598581 l 0.0135,-4.8940567 1.066335,-0.01419 c 0.742348,-0.00988 1.088249,0.00399 1.138458,0.045665 0.06009,0.049873 0.07211,0.7135739 0.07211,3.9791612 v 3.9193056 h 2.293081 c 1.756352,0 2.314103,0.01538 2.382892,0.06567 0.07993,0.05845 0.08822,0.166396 0.07543,0.981428 l -0.01437,0.915757 -3.495384,0.01345 c -2.768549,0.0107 -3.500605,-1.69e-4 -3.520473,-0.05234 z m 10.193414,0.0026 c -0.04842,-0.04842 -0.06297,-1.193838 -0.06236,-4.9074882 4.61e-4,-2.6643823 0.01959,-4.8739347 0.04256,-4.9101166 0.03301,-0.05201 0.813774,-0.062971 3.728627,-0.052342 l 3.686862,0.013441 V 5.3948518 6.337024 l -2.5648,0.026171 -2.5648,0.026172 v 0.9421438 0.9421716 l 2.313597,0.026171 c 1.548367,0.017515 2.332217,0.044804 2.36989,0.082507 0.03673,0.036745 0.05127,0.3461819 0.04183,0.889829 l -0.01446,0.8334926 -2.355428,0.02617 -2.355429,0.02617 v 1.0992 1.099199 l 2.617143,0.0274 c 1.439428,0.01507 2.623562,0.03274 2.63141,0.03926 0.0078,0.0065 0.0078,0.441727 0,0.967118 l -0.01427,0.955257 -3.718613,0.01343 c -2.848812,0.01027 -3.733388,-0.0013 -3.781773,-0.04973 z m 17.753791,-0.378679 c -0.04061,-0.105824 0.0759,-0.828141 0.198829,-1.232689 0.288088,-0.948035 0.88431,-1.590368 2.319422,-2.498804 1.100465,-0.6965999 1.86374,-1.2293374 2.17747,-1.5198007 0.515251,-0.477031 0.731074,-1.0868265 0.620161,-1.7522036 -0.126353,-0.7579473 -0.607483,-1.1395723 -1.436711,-1.1395723 -0.930964,0 -1.401324,0.4507271 -1.481617,1.4197789 l -0.03634,0.4383927 h -1.099202 -1.099196 l -0.01524,-0.3725124 c -0.03408,-0.8332648 0.288934,-1.6827799 0.855164,-2.2490093 0.399774,-0.3997734 1.09283,-0.7574546 1.70958,-0.8822975 0.580047,-0.1174131 1.71432,-0.1077309 2.332892,0.019914 1.258364,0.2596698 2.203978,1.0560413 2.520675,2.1228587 0.104477,0.3519131 0.117355,0.4871812 0.09657,1.0144101 -0.01959,0.4962935 -0.04847,0.667451 -0.157022,0.9292002 -0.313508,0.7560998 -0.900391,1.3802206 -1.888823,2.0086882 -1.507571,0.958543 -1.915442,1.243322 -2.230808,1.557578 -0.26352,0.262604 -0.32016,0.345357 -0.261709,0.382352 0.04123,0.0261 1.061246,0.04757 2.280484,0.04802 1.96272,7.11e-4 2.209393,0.0099 2.237659,0.0836 0.01749,0.04554 0.03178,0.408703 0.03178,0.807033 0,0.398331 -0.0143,0.761495 -0.03178,0.807033 -0.0286,0.07445 -0.414152,0.0828 -3.822672,0.0828 -3.236429,0 -3.795092,-0.01093 -3.819578,-0.07475 z"
+ style="fill:#5586a4;fill-opacity:1;stroke-width:0.05234285"
+ sodipodi:nodetypes="ccscccccccccsscsscccccscccsccsccccccccccssscccccccccccccccscsccsccccsssscscccccccscscscccccccscccccccscccccccscccccccccccscccccsssccccccccscscc" />
+ <path
+ style="fill:#000000;stroke-width:1.06666672"
+ d=""
+ id="path819"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#000000;stroke-width:1.06666672"
+ d=""
+ id="path817"
+ inkscape:connector-curvature="0" />
+</svg>
diff --git a/editor/icons/icon_g_l_e_s_3.svg b/editor/icons/icon_g_l_e_s_3.svg
new file mode 100644
index 0000000000..dfa3c26b38
--- /dev/null
+++ b/editor/icons/icon_g_l_e_s_3.svg
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<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"
+ version="1.1"
+ id="svg2"
+ width="48"
+ height="16"
+ viewBox="0 0 47.999999 16"
+ sodipodi:docname="icon_g_l_e_s_3.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata8">
+ <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="defs6" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1484"
+ inkscape:window-height="697"
+ id="namedview4"
+ showgrid="false"
+ inkscape:zoom="13.520979"
+ inkscape:cx="20.549976"
+ inkscape:cy="7.9399684"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg2" />
+ <path
+ style="fill:#6aa455;fill-opacity:1;stroke-width:0.05234285"
+ d="M 20.011719 2.9023438 C 19.90715 2.9022255 19.801697 2.9494036 19.726562 3.0449219 L 19.619141 3.1835938 L 19.619141 9.4023438 C 19.619141 14.357062 19.636882 15.227573 19.669922 15.314453 C 19.698292 15.38907 19.774644 15.4732 19.839844 15.5 C 19.9836 15.559128 20.192255 15.52045 20.298828 15.416016 C 20.298828 14.821446 20.378685 16.35047 20.390625 9.484375 L 20.404297 3.1835938 L 20.294922 3.0449219 C 20.219653 2.949233 20.116287 2.902462 20.011719 2.9023438 z M 33.578125 4.1972656 C 33.144709 4.2010336 32.716495 4.240406 32.416016 4.3144531 C 31.071981 4.6456644 30.100754 5.5238781 29.875 6.6113281 C 29.784173 7.0488418 29.835175 7.8376693 29.978516 8.1953125 C 30.282551 8.9538964 30.958456 9.4939257 32.072266 9.8691406 C 32.242288 9.9264163 32.881487 10.104023 33.492188 10.263672 C 35.149445 10.696934 35.575494 10.956666 35.621094 11.558594 C 35.680594 12.34404 34.940924 12.791048 33.673828 12.736328 C 32.978116 12.706282 32.589413 12.576557 32.242188 12.257812 C 31.991643 12.02782 31.762573 11.609465 31.761719 11.378906 C 31.761449 11.301612 31.739238 11.176002 31.710938 11.101562 L 31.658203 10.966797 L 30.597656 10.980469 L 29.537109 10.996094 L 29.521484 11.318359 C 29.490059 11.974628 29.806882 12.767321 30.322266 13.320312 C 30.866825 13.904606 31.5695 14.275849 32.498047 14.470703 C 33.459129 14.672381 34.791927 14.598978 35.677734 14.296875 C 36.978592 13.85322 37.789851 12.853418 37.869141 11.597656 C 37.925024 10.712652 37.665438 10.012041 37.074219 9.4589844 C 36.561964 8.9797953 36.008755 8.7328803 34.658203 8.3808594 C 32.302052 7.7667297 32.076172 7.6510363 32.076172 7.0605469 C 32.076172 6.609927 32.254549 6.3596535 32.730469 6.1464844 C 32.944167 6.0507668 33.08716 6.029508 33.515625 6.0292969 C 34.148052 6.028933 34.543774 6.1386072 34.849609 6.4003906 C 35.099098 6.6139448 35.195822 6.7845792 35.273438 7.1464844 C 35.308103 7.3081228 35.366714 7.4312793 35.425781 7.4628906 C 35.479346 7.4915596 35.943593 7.515625 36.464844 7.515625 C 37.580028 7.515625 37.599222 7.5076334 37.544922 7.0761719 C 37.354807 5.5655674 36.444834 4.6504759 34.771484 4.2929688 C 34.450201 4.2243268 34.011541 4.1934977 33.578125 4.1972656 z M 5.5175781 4.1992188 C 4.8691862 4.2376134 4.2355426 4.3965672 3.6894531 4.6699219 C 1.9729802 5.5291325 1.0295038 7.2003211 1.0214844 9.3984375 C 1.0171834 10.579889 1.2388248 11.479703 1.7480469 12.349609 C 2.0038374 12.786576 2.5763159 13.426376 2.9746094 13.71875 C 3.6255952 14.196618 4.5309283 14.517769 5.3808594 14.574219 C 6.0365058 14.617758 6.3712386 14.593916 6.84375 14.46875 C 7.3667333 14.330214 7.7980583 14.094335 8.1875 13.730469 C 8.3646703 13.564934 8.5198921 13.429688 8.5332031 13.429688 C 8.5465141 13.429688 8.588175 13.599875 8.625 13.808594 C 8.7311869 14.410414 8.6762667 14.376195 9.5058594 14.359375 L 10.222656 14.345703 L 10.236328 11.664062 L 10.25 8.9824219 L 10.130859 8.953125 C 10.065823 8.937294 9.1174038 8.9314331 8.0234375 8.9394531 L 6.0351562 8.9550781 L 6.0351562 9.8183594 L 6.0351562 10.681641 L 7.0292969 10.695312 C 7.5762795 10.702722 8.0520995 10.718009 8.0859375 10.730469 C 8.1322565 10.747524 8.1282546 10.832238 8.0722656 11.072266 C 7.8299424 12.111012 7.0892565 12.644531 5.8867188 12.644531 C 5.1651679 12.644531 4.6401044 12.4377 4.1777344 11.970703 C 3.5430124 11.329631 3.2264013 10.183407 3.34375 8.9550781 C 3.4984851 7.3354314 4.2434605 6.3747935 5.4882812 6.1933594 C 6.6291449 6.027076 7.5010723 6.5393131 7.8359375 7.5742188 C 7.8755406 7.6966124 7.9395463 7.7770006 8.0136719 7.796875 C 8.0772164 7.8139122 8.5303844 7.8276689 9.0214844 7.828125 C 10.17008 7.8290445 10.145115 7.8432518 10.019531 7.2460938 C 9.967489 6.9986401 9.8335825 6.6145778 9.7109375 6.3632812 C 9.5339295 6.0005947 9.4071043 5.8312976 9.0214844 5.4472656 C 8.4693148 4.8973676 8.1315285 4.684343 7.4335938 4.4472656 C 6.8293858 4.2420259 6.16597 4.1608241 5.5175781 4.1992188 z M 42.03125 4.2617188 L 41.537109 4.4335938 C 40.933232 4.6433398 40.657695 4.8014669 40.300781 5.1386719 C 39.969225 5.4519119 39.761404 5.8046136 39.621094 6.2910156 C 39.502474 6.7023596 39.433137 7.3494687 39.498047 7.4492188 C 39.531044 7.4999487 39.783863 7.5127831 40.576172 7.5019531 L 41.611328 7.4863281 L 41.691406 7.0703125 C 41.808812 6.4678105 41.927622 6.2685471 42.265625 6.0957031 C 42.510424 5.9705181 42.604184 5.953125 43.019531 5.953125 C 43.426321 5.953125 43.533311 5.9721266 43.765625 6.0878906 C 44.253715 6.3311276 44.455638 6.904517 44.273438 7.53125 C 44.105442 8.109131 43.697334 8.363965 42.791016 8.453125 C 42.521874 8.479605 42.288464 8.51424 42.271484 8.53125 C 42.225224 8.577174 42.232777 9.7874244 42.279297 9.8339844 C 42.301291 9.8559744 42.598053 9.8907794 42.939453 9.9121094 C 43.836652 9.9681724 44.239534 10.166191 44.525391 10.691406 C 44.916028 11.409137 44.561069 12.318315 43.787109 12.582031 C 43.476088 12.688024 42.767292 12.688624 42.470703 12.583984 C 42.00204 12.418633 41.795632 12.174325 41.642578 11.597656 L 41.560547 11.285156 L 40.46875 11.285156 L 39.376953 11.285156 L 39.361328 11.527344 C 39.352678 11.660649 39.384791 11.918152 39.431641 12.099609 C 39.739925 13.294376 40.783209 14.156157 42.212891 14.396484 C 42.284425 14.408514 42.682741 14.422181 43.097656 14.425781 C 44.074936 14.434074 44.653306 14.320796 45.308594 13.996094 C 46.07786 13.61492 46.610204 13.058412 46.847656 12.382812 C 47.087412 11.700564 47.08166 10.999125 46.833984 10.333984 C 46.695621 9.962377 46.130198 9.3782416 45.6875 9.1503906 C 45.523031 9.0657476 45.386773 8.9810006 45.386719 8.9628906 C 45.386654 8.9447846 45.539488 8.8195027 45.724609 8.6835938 C 46.129744 8.3861558 46.390215 8.064434 46.53125 7.6875 C 46.963216 6.532963 46.370297 5.2063894 45.166016 4.6308594 C 44.482944 4.3044164 44.23589 4.2611938 43.072266 4.2617188 L 42.03125 4.2617188 z M 12.544922 4.4375 L 11.478516 4.453125 L 11.464844 9.3476562 C 11.457424 12.039388 11.462763 14.270451 11.476562 14.306641 C 11.49643 14.358812 12.229498 14.370075 14.998047 14.359375 L 18.492188 14.345703 L 18.507812 13.429688 C 18.520602 12.614656 18.511571 12.507669 18.431641 12.449219 C 18.362852 12.398929 17.80518 12.382812 16.048828 12.382812 L 13.755859 12.382812 L 13.755859 8.4628906 C 13.755859 5.1973033 13.743684 4.534248 13.683594 4.484375 C 13.633385 4.4427 13.28727 4.42762 12.544922 4.4375 z M 25.378906 4.4394531 C 22.464053 4.4288241 21.683401 4.4401775 21.650391 4.4921875 C 21.627421 4.5283694 21.607883 6.7379615 21.607422 9.4023438 C 21.606812 13.115994 21.621502 14.260174 21.669922 14.308594 C 21.718307 14.357024 22.60236 14.369645 25.451172 14.359375 L 29.169922 14.345703 L 29.185547 13.390625 C 29.193347 12.865234 29.193347 12.430328 29.185547 12.423828 C 29.177699 12.417308 27.992162 12.399836 26.552734 12.384766 L 23.935547 12.355469 L 23.935547 11.257812 L 23.935547 10.158203 L 26.291016 10.132812 L 28.646484 10.105469 L 28.662109 9.2714844 C 28.671549 8.7278373 28.655871 8.4195575 28.619141 8.3828125 C 28.581468 8.3451095 27.798367 8.3182962 26.25 8.3007812 L 23.935547 8.2734375 L 23.935547 7.3320312 L 23.935547 6.3886719 L 26.501953 6.3632812 L 29.066406 6.3378906 L 29.066406 5.3945312 L 29.066406 4.453125 L 25.378906 4.4394531 z "
+ id="path3424" />
+ <path
+ style="fill:#000000;stroke-width:1.06666672"
+ d=""
+ id="path819"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#000000;stroke-width:1.06666672"
+ d=""
+ id="path817"
+ inkscape:connector-curvature="0" />
+</svg>
diff --git a/editor/icons/icon_new_root.svg b/editor/icons/icon_new_root.svg
new file mode 100644
index 0000000000..51c79f038d
--- /dev/null
+++ b/editor/icons/icon_new_root.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="svg8"
+ sodipodi:docname="icon_new_root.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata14">
+ <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="defs12" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1474"
+ inkscape:window-height="755"
+ id="namedview10"
+ showgrid="false"
+ inkscape:zoom="29.5"
+ inkscape:cx="9.9306919"
+ inkscape:cy="7.2213369"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg8" />
+ <path
+ style="fill:#e0e0e0"
+ d="m 2,4.7813475 v 2.0494746 c -0.6177049,0.3566305 -0.998733,1.0152377 -1,1.7285 0,1.1045694 0.8954305,1.9999999 2,1.9999999 0.7139771,-5.54e-4 1.3735116,-0.381678 1.7305,-0.9999995 h 1.3545593 c 0.3566306,0.6177035 1.0152377,0.9987325 1.7285,0.9999995 1.1045696,0 1.9999996,-0.8954305 1.9999996,-1.9999999 0,-1.1045695 -0.89543,-2 -1.9999996,-2 -0.7139771,5.537e-4 -1.3735116,0.3816774 -1.7305,1 H 4.7285 C 4.5537191,7.2563119 4.3025219,7.0044423 3.99998,6.8288521 V 4.7793775 C 3.4615087,4.8084067 2.7017179,4.8161838 2,4.7813475 Z"
+ id="path2"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccscccccc" />
+ <path
+ style="fill:#e0e0e0"
+ d="m 6.8474576,9.6288045 v 1.2020165 c -0.617705,0.35663 -0.998733,1.015237 -1,1.7285 0,1.104569 0.89543,2 2,2 0.713977,-5.54e-4 1.373512,-0.381678 1.7305,-1 h 1.2867634 c 0.35663,0.617704 1.015237,0.998733 1.7285,1 1.104569,0 1.999999,-0.895431 1.999999,-2 0,-1.10457 -0.89543,-2 -1.999999,-2 -0.713977,5.53e-4 -1.373512,0.381677 -1.7305,1 H 9.5759576 c -0.174781,-0.303011 -0.425978,-0.55488 -0.72852,-0.73047 V 9.6268345 c 0,0 -1.264363,0.03681 -1.99998,0.002 z"
+ id="path827"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccsccccccc" />
+ <path
+ sodipodi:nodetypes="ccccccc"
+ inkscape:connector-curvature="0"
+ id="path829"
+ d="m 2.7966098,1.3559322 c -1.104569,0 -2.00000003,0.8954305 -2.00000003,2 5.54e-4,0.7139771 0.38167803,1.3735116 1.00000003,1.7305 0.757716,0.266212 0.949133,0.2840609 1.99998,-0.00197 0.617705,-0.3566306 0.998733,-1.0152377 1,-1.7285 0,-1.1045695 -0.89543,-2 -2,-2 z"
+ style="fill:#84ffb1;fill-opacity:1" />
+</svg>
diff --git a/editor/icons/icon_shrink_bottom_dock.svg b/editor/icons/icon_shrink_bottom_dock.svg
new file mode 100644
index 0000000000..c1e8c1bfdb
--- /dev/null
+++ b/editor/icons/icon_shrink_bottom_dock.svg
@@ -0,0 +1,71 @@
+<?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="svg6"
+ sodipodi:docname="icon_shrink_bottom_dock.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata12">
+ <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="defs10" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1853"
+ inkscape:window-height="1016"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="20.85965"
+ inkscape:cx="9.4509357"
+ inkscape:cy="6.016355"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg6" />
+ <path
+ style="fill:#e0e0e0"
+ d="M 11.907447,9.9752038 15.442981,6.4396699 H 12.907296 V 1.4659528 h -1.999839 l 0,4.9737171 -2.5356852,0 3.5355342,3.5355339 z"
+ id="path829"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccc" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path831"
+ d="M 4.2131662,9.8793249 7.7487004,6.343791 H 5.2130152 V 1.3700738 H 3.2131762 V 6.343791 h -2.535685 l 3.535534,3.5355339 z"
+ style="fill:#e0e0e0"
+ sodipodi:nodetypes="ccccccccc" />
+ <rect
+ style="fill:#e0e0e0;fill-opacity:1"
+ id="rect855"
+ width="14"
+ height="1.8305085"
+ x="-14.832336"
+ y="-13.121187"
+ transform="scale(-1)" />
+</svg>
diff --git a/editor/icons/icon_visual_shader.svg b/editor/icons/icon_visual_shader.svg
new file mode 100644
index 0000000000..e2c4f128b2
--- /dev/null
+++ b/editor/icons/icon_visual_shader.svg
@@ -0,0 +1,105 @@
+<?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="svg20"
+ sodipodi:docname="icon_visual_shader.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata26">
+ <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="defs24" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="640"
+ inkscape:window-height="480"
+ id="namedview22"
+ showgrid="false"
+ inkscape:zoom="14.75"
+ inkscape:cx="8"
+ inkscape:cy="8"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg20" />
+ <g
+ id="g18"
+ transform="matrix(1,0,0,0.50605327,0,0.49394673)">
+ <path
+ d="M 2,1 C 1.44774,1.0001 1.00006,1.4477 1,2 v 12 c 5.52e-5,0.5523 0.44774,0.9999 1,1 h 12 c 0.55226,-10e-5 0.99994,-0.4477 1,-1 V 6 L 10,1 Z m 1,2 h 6 v 3 c 0,0.554 0.44599,1 1,1 h 3 v 6 H 3 Z"
+ id="path2"
+ inkscape:connector-curvature="0"
+ style="fill:#e0e0e0" />
+ <path
+ d="m 10,11 h 2 v 1 h -2 z"
+ id="path4"
+ inkscape:connector-curvature="0"
+ style="fill:#9f70ff" />
+ <path
+ d="M 4,6 H 6 V 7 H 4 Z"
+ id="path6"
+ inkscape:connector-curvature="0"
+ style="fill:#ffeb70" />
+ <path
+ d="m 8,8 h 4 V 9 H 8 Z"
+ id="path8"
+ inkscape:connector-curvature="0"
+ style="fill:#9dff70" />
+ <path
+ d="M 7,6 H 8 V 7 H 7 Z"
+ id="path10"
+ inkscape:connector-curvature="0"
+ style="fill:#70deff" />
+ <path
+ d="m 4,11 h 5 v 1 H 4 Z"
+ id="path12"
+ inkscape:connector-curvature="0"
+ style="fill:#ff70ac" />
+ <path
+ d="M 4,4 H 7 V 5 H 4 Z"
+ id="path14"
+ inkscape:connector-curvature="0"
+ style="fill:#ff7070" />
+ <path
+ d="M 4,8 H 7 V 9 H 4 Z"
+ id="path16"
+ inkscape:connector-curvature="0"
+ style="fill:#70ffb9" />
+ </g>
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#e0e0e0"
+ d="m 2.8642321,9 v 6 h 2 a 3,3 0 0 0 3,-3 V 9 h -2 v 3 a 1,1 0 0 1 -1,1 V 9 Z"
+ id="path1394" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#e0e0e0"
+ d="m 10.864232,9 a 2,2 0 0 0 -1.7323999,1 2,2 0 0 0 0,2 2,2 0 0 0 1.7323999,1 H 8.8642321 v 2 h 1.9999999 a 2,2 0 0 0 1.7324,-1 2,2 0 0 0 0,-2 2,2 0 0 0 -1.7324,-1 h 2 V 9 Z"
+ id="path30" />
+</svg>
diff --git a/editor/icons/icon_vulkan.svg b/editor/icons/icon_vulkan.svg
new file mode 100644
index 0000000000..1d5fed0305
--- /dev/null
+++ b/editor/icons/icon_vulkan.svg
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<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"
+ version="1.1"
+ id="svg2"
+ width="48"
+ height="16"
+ viewBox="0 0 47.999999 16"
+ sodipodi:docname="icon_vulkan.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata8">
+ <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="defs6" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1853"
+ inkscape:window-height="1016"
+ id="namedview4"
+ showgrid="false"
+ inkscape:zoom="10.24"
+ inkscape:cx="9.4970674"
+ inkscape:cy="11.192118"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="g8" />
+ <path
+ style="fill:#000000;stroke-width:1.06666672"
+ d=""
+ id="path819"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#000000;stroke-width:1.06666672"
+ d=""
+ id="path817"
+ inkscape:connector-curvature="0" />
+ <g
+ transform="matrix(0.04333868,0,0,0.04333868,-4.0493236,-3.7704963)"
+ id="g8">
+ <path
+ inkscape:connector-curvature="0"
+ d="m 724.1,432.41989 h -40.6 c 0,0 0,-99 0,-129.7 13,7.2 30.1,20.5 40.6,33.3 z"
+ id="path10"
+ style="fill:#e6555a;fill-opacity:1" />
+ <g
+ id="g12"
+ style="fill:#e6555a;fill-opacity:1"
+ transform="translate(0,47.319882)">
+ <path
+ inkscape:connector-curvature="0"
+ d="m 381.8,385.1 h -50.6 l -66,-204 h 46 l 45.4,143.5 h 0.6 l 46,-143.5 h 46.3 z"
+ id="path14"
+ style="fill:#e6555a;fill-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ d="M 585.5,385.1 H 546.9 V 364.5 H 546 c -5.1,8.6 -11.8,14.8 -20,18.6 -8.2,3.8 -16.6,5.7 -25.1,5.7 -10.9,0 -19.8,-1.4 -26.7,-4.3 -7,-2.9 -12.4,-6.9 -16.4,-12.1 -4,-5.2 -6.8,-11.6 -8.4,-19.1 -1.6,-7.5 -2.4,-15.9 -2.4,-25 v -90.9 h 40.6 v 83.4 c 0,12.2 1.9,21.3 5.7,27.3 3.8,6 10.6,9 20.3,9 11,0 19.1,-3.3 24,-9.9 5,-6.6 7.4,-17.4 7.4,-32.4 v -77.4 h 40.6 v 147.7 z"
+ id="path16"
+ style="fill:#e6555a;fill-opacity:1" />
+ </g>
+ <polygon
+ points="730.8,296.2 730.7,290.5 781.9,237.3 829.9,237.3 774.2,291.6 836.2,385.1 787,385.1 "
+ id="polygon18"
+ style="fill:#e6555a;fill-opacity:1"
+ transform="translate(0,47.319882)" />
+ <path
+ inkscape:connector-curvature="0"
+ d="m 843.6,330.11989 c 0.6,-9.5 3,-17.4 7.2,-23.7 4.2,-6.3 9.5,-11.3 16,-15.1 6.5,-3.8 13.8,-6.5 21.9,-8.1 8.1,-1.6 16.2,-2.4 24.4,-2.4 7.4,0 15,0.5 22.6,1.6 7.6,1.1 14.6,3.1 20.9,6.1 6.3,3.1 11.4,7.3 15.4,12.7 4,5.4 6,12.6 6,21.6 v 76.9 c 0,6.7 0.4,13.1 1.1,19.1 0.8,6.1 2.1,10.7 4,13.7 h -41.2 c -0.8,-2.3 -1.4,-4.6 -1.9,-7 -0.5,-2.4 -0.8,-4.8 -1,-7.3 -6.5,6.7 -14.1,11.3 -22.9,14 -8.8,2.7 -17.7,4 -26.9,4 -7,0 -13.6,-0.9 -19.7,-2.6 -6.1,-1.7 -11.4,-4.4 -16,-8 -4.6,-3.6 -8.2,-8.2 -10.7,-13.7 -2.6,-5.5 -3.9,-12.1 -3.9,-19.7 0,-8.4 1.5,-15.3 4.4,-20.7 3,-5.4 6.8,-9.8 11.4,-13 4.7,-3.2 10,-5.7 16,-7.3 6,-1.6 12,-2.9 18.1,-3.9 6.1,-0.9 12.1,-1.7 18,-2.3 5.9,-0.6 11.1,-1.4 15.7,-2.6 4.6,-1.1 8.2,-2.8 10.9,-5 2.7,-2.2 3.9,-5.4 3.7,-9.6 0,-4.4 -0.7,-7.9 -2.2,-10.4 -1.4,-2.6 -3.3,-4.6 -5.7,-6 -2.4,-1.4 -5.1,-2.4 -8.3,-2.9 -3.1,-0.5 -6.5,-0.7 -10.1,-0.7 -8,0 -14.3,1.7 -18.9,5.1 -4.6,3.4 -7.2,9.1 -8,17.1 h -40.3 z m 93.8,30 c -1.7,1.5 -3.9,2.7 -6.4,3.6 -2.6,0.9 -5.3,1.6 -8.3,2.2 -2.9,0.6 -6,1 -9.3,1.4 -3.2,0.4 -6.5,0.9 -9.7,1.4 -3,0.6 -6,1.3 -9,2.3 -3,1 -5.5,2.2 -7.7,3.9 -2.2,1.6 -4,3.7 -5.3,6.1 -1.3,2.5 -2,5.6 -2,9.4 0,3.6 0.7,6.7 2,9.1 1.3,2.5 3.1,4.4 5.4,5.9 2.3,1.4 5,2.4 8,3 3.1,0.6 6.2,0.9 9.4,0.9 8,0 14.2,-1.3 18.6,-4 4.4,-2.7 7.6,-5.9 9.7,-9.6 2.1,-3.7 3.4,-7.5 3.9,-11.3 0.5,-3.8 0.7,-6.9 0.7,-9.1 z"
+ id="path20"
+ style="fill:#e6555a;fill-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ d="m 1004.2,284.61989 h 38.6 v 20.6 h 0.9 c 5.1,-8.6 11.8,-14.8 20,-18.7 8.2,-3.9 16.6,-5.9 25.1,-5.9 10.9,0 19.8,1.5 26.7,4.4 7,3 12.4,7.1 16.4,12.3 4,5.2 6.8,11.6 8.4,19.1 1.6,7.5 2.4,15.9 2.4,25 v 90.9 h -40.6 v -83.4 c 0,-12.2 -1.9,-21.3 -5.7,-27.3 -3.8,-6 -10.6,-9 -20.3,-9 -11,0 -19,3.3 -24,9.9 -5,6.6 -7.4,17.4 -7.4,32.4 v 77.4 h -40.6 v -147.7 z"
+ id="path22"
+ style="fill:#e6555a;fill-opacity:1" />
+ <g
+ id="g24"
+ style="fill:#e6555a;fill-opacity:1"
+ transform="translate(0,47.319882)">
+ <path
+ inkscape:connector-curvature="0"
+ d="M 612.4,211.8 V 385 H 653 V 234.2 c -13.1,-8 -26.6,-15.5 -40.6,-22.4 z"
+ id="path26"
+ style="fill:#e6555a;fill-opacity:1" />
+ </g>
+ <path
+ inkscape:connector-curvature="0"
+ d="m 198.4,266.51989 c 23.5,-68.9 164.2,-94.2 314.1,-56.4 90,22.6 163.5,66.5 211.5,109.9 -21.7,-57.6 -127.3,-139.6 -272.8,-167.7 -164.5,-31.8 -326.7,-3.9 -346.8,69.1 -14.5,52.7 49.2,114.5 147.7,156.7 -44.3,-35.8 -65.8,-76 -53.7,-111.6 z"
+ id="path28"
+ style="fill:#e6555a;fill-opacity:1" />
+ <g
+ id="g30"
+ style="fill:#e6555a;fill-opacity:1"
+ transform="translate(0,47.319882)">
+ <path
+ inkscape:connector-curvature="0"
+ d="M 724.2,247.6 V 181 h -40.6 v 20.2 c 17.3,15.5 31,31.2 40.6,46.4 z"
+ id="path32"
+ style="fill:#e6555a;fill-opacity:1" />
+ </g>
+ </g>
+</svg>
diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp
index 3efb2736b5..c00ad451fa 100644
--- a/editor/plugins/animation_blend_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp
@@ -246,7 +246,7 @@ void AnimationNodeBlendTreeEditor::_add_node(int p_idx) {
Point2 instance_pos = graph->get_scroll_ofs() + graph->get_size() * 0.5;
- anode->set_position(instance_pos);
+ anode->set_position(instance_pos / EDSCALE);
String base_name = add_options[p_idx].name;
int base = 1;
diff --git a/editor/plugins/audio_stream_editor_plugin.cpp b/editor/plugins/audio_stream_editor_plugin.cpp
new file mode 100644
index 0000000000..ddb03d0250
--- /dev/null
+++ b/editor/plugins/audio_stream_editor_plugin.cpp
@@ -0,0 +1,284 @@
+/*************************************************************************/
+/* audio_stream_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "audio_stream_editor_plugin.h"
+
+#include "editor/editor_settings.h"
+#include "io/resource_loader.h"
+#include "project_settings.h"
+
+void AudioStreamEditor::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_READY) {
+ AudioStreamPreviewGenerator::get_singleton()->connect("preview_updated", this, "_preview_changed");
+ }
+
+ if (p_what == NOTIFICATION_THEME_CHANGED || p_what == NOTIFICATION_ENTER_TREE) {
+ _play_button->set_icon(get_icon("MainPlay", "EditorIcons"));
+ _stop_button->set_icon(get_icon("Stop", "EditorIcons"));
+ _preview->set_frame_color(get_color("dark_color_2", "Editor"));
+ set_frame_color(get_color("dark_color_1", "Editor"));
+
+ _indicator->update();
+ _preview->update();
+ }
+
+ if (p_what == NOTIFICATION_PROCESS) {
+ _current = _player->get_playback_position();
+ _indicator->update();
+ _preview->update();
+ }
+
+ if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
+ if (!is_visible_in_tree()) {
+ _stop();
+ }
+ }
+}
+
+void AudioStreamEditor::_draw_preview() {
+ Rect2 rect = _preview->get_rect();
+ Size2 size = get_size();
+
+ Ref<AudioStreamPreview> preview = AudioStreamPreviewGenerator::get_singleton()->generate_preview(stream);
+ float preview_len = preview->get_length();
+
+ Vector<Vector2> lines;
+ lines.resize(size.width * 2);
+
+ for (int i = 0; i < size.width; i++) {
+
+ float ofs = i * preview_len / size.width;
+ float ofs_n = (i + 1) * preview_len / size.width;
+ float max = preview->get_max(ofs, ofs_n) * 0.5 + 0.5;
+ float min = preview->get_min(ofs, ofs_n) * 0.5 + 0.5;
+
+ int idx = i;
+ lines[idx * 2 + 0] = Vector2(i + 1, rect.position.y + min * rect.size.y);
+ lines[idx * 2 + 1] = Vector2(i + 1, rect.position.y + max * rect.size.y);
+ }
+
+ Vector<Color> color;
+ color.push_back(get_color("contrast_color_2", "Editor"));
+
+ VS::get_singleton()->canvas_item_add_multiline(_preview->get_canvas_item(), lines, color);
+}
+
+void AudioStreamEditor::_preview_changed(ObjectID p_which) {
+
+ if (stream.is_valid() && stream->get_instance_id() == p_which) {
+ _preview->update();
+ }
+}
+
+void AudioStreamEditor::_changed_callback(Object *p_changed, const char *p_prop) {
+
+ if (!is_visible())
+ return;
+ update();
+}
+
+void AudioStreamEditor::_play() {
+
+ if (_player->is_playing()) {
+ _player->stop();
+ _play_button->set_icon(get_icon("MainPlay", "EditorIcons"));
+ set_process(false);
+ } else {
+ _player->play(_current);
+ _play_button->set_icon(get_icon("Pause", "EditorIcons"));
+ set_process(true);
+ }
+}
+
+void AudioStreamEditor::_stop() {
+
+ _player->stop();
+ _on_finished();
+}
+
+void AudioStreamEditor::_on_finished() {
+
+ _play_button->set_icon(get_icon("MainPlay", "EditorIcons"));
+ _current = 0;
+ _indicator->update();
+ set_process(false);
+}
+
+void AudioStreamEditor::_draw_indicator() {
+
+ if (!stream.is_valid()) {
+ return;
+ }
+
+ Rect2 rect = _preview->get_rect();
+ float len = stream->get_length();
+ float ofs_x = _current / len * rect.size.width;
+ _indicator->draw_line(Point2(ofs_x, 0), Point2(ofs_x, rect.size.height), get_color("accent_color", "Editor"), 1);
+
+ _current_label->set_text(String::num(_current, 2).pad_decimals(2) + " /");
+}
+
+void AudioStreamEditor::_on_input_indicator(Ref<InputEvent> p_event) {
+ Ref<InputEventMouseButton> mb = p_event;
+
+ if (mb.is_valid()) {
+ if (mb->is_pressed()) {
+ _seek_to(mb->get_position().x);
+ }
+ _dragging = mb->is_pressed();
+ }
+
+ Ref<InputEventMouseMotion> mm = p_event;
+
+ if (mm.is_valid()) {
+ if (_dragging) {
+ _seek_to(mm->get_position().x);
+ }
+ }
+}
+
+void AudioStreamEditor::_seek_to(real_t p_x) {
+ _current = p_x / _preview->get_rect().size.x * stream->get_length();
+ _current = CLAMP(_current, 0, stream->get_length());
+ _player->seek(_current);
+ _indicator->update();
+}
+
+void AudioStreamEditor::edit(Ref<AudioStream> p_stream) {
+
+ if (!stream.is_null())
+ stream->remove_change_receptor(this);
+
+ stream = p_stream;
+ _player->set_stream(stream);
+ _current = 0;
+ String text = String::num(stream->get_length(), 2).pad_decimals(2) + "s";
+ _duration_label->set_text(text);
+
+ if (!stream.is_null()) {
+ stream->add_change_receptor(this);
+ update();
+ } else {
+ hide();
+ }
+}
+
+void AudioStreamEditor::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_preview_changed"), &AudioStreamEditor::_preview_changed);
+ ClassDB::bind_method(D_METHOD("_play"), &AudioStreamEditor::_play);
+ ClassDB::bind_method(D_METHOD("_stop"), &AudioStreamEditor::_stop);
+ ClassDB::bind_method(D_METHOD("_on_finished"), &AudioStreamEditor::_on_finished);
+ ClassDB::bind_method(D_METHOD("_draw_preview"), &AudioStreamEditor::_draw_preview);
+ ClassDB::bind_method(D_METHOD("_draw_indicator"), &AudioStreamEditor::_draw_indicator);
+ ClassDB::bind_method(D_METHOD("_on_input_indicator"), &AudioStreamEditor::_on_input_indicator);
+}
+
+AudioStreamEditor::AudioStreamEditor() {
+
+ set_custom_minimum_size(Size2(1, 100));
+ _current = 0;
+ _dragging = false;
+
+ _player = memnew(AudioStreamPlayer);
+ _player->connect("finished", this, "_on_finished");
+ add_child(_player);
+
+ VBoxContainer *vbox = memnew(VBoxContainer);
+ vbox->set_anchors_and_margins_preset(PRESET_WIDE, PRESET_MODE_MINSIZE, 0);
+ add_child(vbox);
+
+ _preview = memnew(ColorRect);
+ _preview->set_v_size_flags(SIZE_EXPAND_FILL);
+ _preview->connect("draw", this, "_draw_preview");
+ vbox->add_child(_preview);
+
+ _indicator = memnew(Control);
+ _indicator->set_anchors_and_margins_preset(PRESET_WIDE);
+ _indicator->connect("draw", this, "_draw_indicator");
+ _indicator->connect("gui_input", this, "_on_input_indicator");
+ _preview->add_child(_indicator);
+
+ HBoxContainer *hbox = memnew(HBoxContainer);
+ hbox->add_constant_override("separation", 0);
+ vbox->add_child(hbox);
+
+ _play_button = memnew(ToolButton);
+ hbox->add_child(_play_button);
+ _play_button->set_focus_mode(Control::FOCUS_NONE);
+ _play_button->connect("pressed", this, "_play");
+
+ _stop_button = memnew(ToolButton);
+ hbox->add_child(_stop_button);
+ _stop_button->set_focus_mode(Control::FOCUS_NONE);
+ _stop_button->connect("pressed", this, "_stop");
+
+ _current_label = memnew(Label);
+ _current_label->set_align(Label::ALIGN_RIGHT);
+ _current_label->set_h_size_flags(SIZE_EXPAND_FILL);
+ _current_label->add_font_override("font", EditorNode::get_singleton()->get_gui_base()->get_font("status_source", "EditorFonts"));
+ _current_label->set_modulate(Color(1, 1, 1, 0.5));
+ hbox->add_child(_current_label);
+
+ _duration_label = memnew(Label);
+ _duration_label->add_font_override("font", EditorNode::get_singleton()->get_gui_base()->get_font("status_source", "EditorFonts"));
+ hbox->add_child(_duration_label);
+}
+
+void AudioStreamEditorPlugin::edit(Object *p_object) {
+
+ AudioStream *s = Object::cast_to<AudioStream>(p_object);
+ if (!s)
+ return;
+
+ audio_editor->edit(Ref<AudioStream>(s));
+}
+
+bool AudioStreamEditorPlugin::handles(Object *p_object) const {
+
+ return p_object->is_class("AudioStream");
+}
+
+void AudioStreamEditorPlugin::make_visible(bool p_visible) {
+
+ audio_editor->set_visible(p_visible);
+}
+
+AudioStreamEditorPlugin::AudioStreamEditorPlugin(EditorNode *p_node) {
+
+ editor = p_node;
+ audio_editor = memnew(AudioStreamEditor);
+ add_control_to_container(CONTAINER_PROPERTY_EDITOR_BOTTOM, audio_editor);
+ audio_editor->hide();
+}
+
+AudioStreamEditorPlugin::~AudioStreamEditorPlugin() {
+}
diff --git a/editor/plugins/audio_stream_editor_plugin.h b/editor/plugins/audio_stream_editor_plugin.h
new file mode 100644
index 0000000000..1887874b74
--- /dev/null
+++ b/editor/plugins/audio_stream_editor_plugin.h
@@ -0,0 +1,93 @@
+/*************************************************************************/
+/* audio_stream_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef AUDIO_STREAM_EDITOR_PLUGIN_H
+#define AUDIO_STREAM_EDITOR_PLUGIN_H
+
+#include "editor/editor_node.h"
+#include "editor/editor_plugin.h"
+#include "scene/audio/audio_player.h"
+#include "scene/gui/color_rect.h"
+#include "scene/resources/texture.h"
+
+class AudioStreamEditor : public ColorRect {
+
+ GDCLASS(AudioStreamEditor, ColorRect);
+
+ Ref<AudioStream> stream;
+ AudioStreamPlayer *_player;
+ ColorRect *_preview;
+ Control *_indicator;
+ Label *_current_label;
+ Label *_duration_label;
+
+ ToolButton *_play_button;
+ ToolButton *_stop_button;
+
+ float _current;
+ bool _dragging;
+
+protected:
+ void _notification(int p_what);
+ void _preview_changed(ObjectID p_which);
+ void _play();
+ void _stop();
+ void _on_finished();
+ void _draw_preview();
+ void _draw_indicator();
+ void _on_input_indicator(Ref<InputEvent> p_event);
+ void _seek_to(real_t p_x);
+ void _changed_callback(Object *p_changed, const char *p_prop);
+ static void _bind_methods();
+
+public:
+ void edit(Ref<AudioStream> p_stream);
+ AudioStreamEditor();
+};
+
+class AudioStreamEditorPlugin : public EditorPlugin {
+
+ GDCLASS(AudioStreamEditorPlugin, EditorPlugin);
+
+ AudioStreamEditor *audio_editor;
+ EditorNode *editor;
+
+public:
+ virtual String get_name() const { return "Audio"; }
+ bool has_main_screen() const { return false; }
+ virtual void edit(Object *p_object);
+ virtual bool handles(Object *p_object) const;
+ virtual void make_visible(bool p_visible);
+
+ AudioStreamEditorPlugin(EditorNode *p_node);
+ ~AudioStreamEditorPlugin();
+};
+
+#endif // AUDIO_STREAM_EDITOR_PLUGIN_H
diff --git a/editor/plugins/path_2d_editor_plugin.cpp b/editor/plugins/path_2d_editor_plugin.cpp
index 5ec42b07aa..33e182faef 100644
--- a/editor/plugins/path_2d_editor_plugin.cpp
+++ b/editor/plugins/path_2d_editor_plugin.cpp
@@ -109,6 +109,7 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
action_point = i;
moving_from = curve->get_point_out(i);
moving_screen_from = gpoint;
+ orig_in_length = curve->get_point_in(action_point).length();
return true;
} else if (dist_to_p_in < grab_threshold && i > 0) {
@@ -116,6 +117,7 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
action_point = i;
moving_from = curve->get_point_in(i);
moving_screen_from = gpoint;
+ orig_out_length = curve->get_point_out(action_point).length();
return true;
}
}
@@ -205,6 +207,11 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
undo_redo->create_action(TTR("Move In-Control in Curve"));
undo_redo->add_do_method(curve.ptr(), "set_point_in", action_point, new_pos);
undo_redo->add_undo_method(curve.ptr(), "set_point_in", action_point, moving_from);
+
+ if (mirror_handle_angle) {
+ undo_redo->add_do_method(curve.ptr(), "set_point_out", action_point, mirror_handle_length ? -new_pos : (-new_pos.normalized() * orig_out_length));
+ undo_redo->add_undo_method(curve.ptr(), "set_point_out", action_point, mirror_handle_length ? -moving_from : (-moving_from.normalized() * orig_out_length));
+ }
undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update");
undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update");
undo_redo->commit_action();
@@ -216,6 +223,11 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
undo_redo->create_action(TTR("Move Out-Control in Curve"));
undo_redo->add_do_method(curve.ptr(), "set_point_out", action_point, new_pos);
undo_redo->add_undo_method(curve.ptr(), "set_point_out", action_point, moving_from);
+
+ if (mirror_handle_angle) {
+ undo_redo->add_do_method(curve.ptr(), "set_point_in", action_point, mirror_handle_length ? -new_pos : (-new_pos.normalized() * orig_in_length));
+ undo_redo->add_undo_method(curve.ptr(), "set_point_in", action_point, mirror_handle_length ? -moving_from : (-moving_from.normalized() * orig_in_length));
+ }
undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update");
undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update");
undo_redo->commit_action();
@@ -255,10 +267,16 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
case ACTION_MOVING_IN: {
curve->set_point_in(action_point, new_pos);
+
+ if (mirror_handle_angle)
+ curve->set_point_out(action_point, mirror_handle_length ? -new_pos : (-new_pos.normalized() * orig_out_length));
} break;
case ACTION_MOVING_OUT: {
curve->set_point_out(action_point, new_pos);
+
+ if (mirror_handle_angle)
+ curve->set_point_in(action_point, mirror_handle_length ? -new_pos : (-new_pos.normalized() * orig_in_length));
} break;
}
@@ -342,6 +360,7 @@ void Path2DEditor::_bind_methods() {
//ClassDB::bind_method(D_METHOD("_menu_option"),&Path2DEditor::_menu_option);
ClassDB::bind_method(D_METHOD("_node_visibility_changed"), &Path2DEditor::_node_visibility_changed);
ClassDB::bind_method(D_METHOD("_mode_selected"), &Path2DEditor::_mode_selected);
+ ClassDB::bind_method(D_METHOD("_handle_option_pressed"), &Path2DEditor::_handle_option_pressed);
}
void Path2DEditor::_mode_selected(int p_mode) {
@@ -396,11 +415,33 @@ void Path2DEditor::_mode_selected(int p_mode) {
mode = Mode(p_mode);
}
+void Path2DEditor::_handle_option_pressed(int p_option) {
+
+ PopupMenu *pm;
+ pm = handle_menu->get_popup();
+
+ switch (p_option) {
+ case HANDLE_OPTION_ANGLE: {
+ bool is_checked = pm->is_item_checked(HANDLE_OPTION_ANGLE);
+ mirror_handle_angle = !is_checked;
+ pm->set_item_checked(HANDLE_OPTION_ANGLE, mirror_handle_angle);
+ pm->set_item_disabled(HANDLE_OPTION_LENGTH, !mirror_handle_angle);
+ } break;
+ case HANDLE_OPTION_LENGTH: {
+ bool is_checked = pm->is_item_checked(HANDLE_OPTION_LENGTH);
+ mirror_handle_length = !is_checked;
+ pm->set_item_checked(HANDLE_OPTION_LENGTH, mirror_handle_length);
+ } break;
+ }
+}
+
Path2DEditor::Path2DEditor(EditorNode *p_editor) {
canvas_item_editor = NULL;
editor = p_editor;
undo_redo = editor->get_undo_redo();
+ mirror_handle_angle = true;
+ mirror_handle_length = true;
mode = MODE_EDIT;
action = ACTION_NONE;
@@ -444,6 +485,20 @@ Path2DEditor::Path2DEditor(EditorNode *p_editor) {
curve_close->set_tooltip(TTR("Close Curve"));
curve_close->connect("pressed", this, "_mode_selected", varray(ACTION_CLOSE));
base_hb->add_child(curve_close);
+
+ PopupMenu *menu;
+
+ handle_menu = memnew(MenuButton);
+ handle_menu->set_text(TTR("Options"));
+ base_hb->add_child(handle_menu);
+
+ menu = handle_menu->get_popup();
+ menu->add_check_item(TTR("Mirror Handle Angles"));
+ menu->set_item_checked(HANDLE_OPTION_ANGLE, mirror_handle_angle);
+ menu->add_check_item(TTR("Mirror Handle Lengths"));
+ menu->set_item_checked(HANDLE_OPTION_LENGTH, mirror_handle_length);
+ menu->connect("id_pressed", this, "_handle_option_pressed");
+
base_hb->hide();
curve_edit->set_pressed(true);
diff --git a/editor/plugins/path_2d_editor_plugin.h b/editor/plugins/path_2d_editor_plugin.h
index c92a696967..1e3955f84f 100644
--- a/editor/plugins/path_2d_editor_plugin.h
+++ b/editor/plugins/path_2d_editor_plugin.h
@@ -69,6 +69,15 @@ class Path2DEditor : public HBoxContainer {
ToolButton *curve_edit_curve;
ToolButton *curve_del;
ToolButton *curve_close;
+ MenuButton *handle_menu;
+
+ bool mirror_handle_angle;
+ bool mirror_handle_length;
+
+ enum HandleOption {
+ HANDLE_OPTION_ANGLE,
+ HANDLE_OPTION_LENGTH
+ };
enum Action {
@@ -82,8 +91,11 @@ class Path2DEditor : public HBoxContainer {
int action_point;
Point2 moving_from;
Point2 moving_screen_from;
+ float orig_in_length;
+ float orig_out_length;
void _mode_selected(int p_mode);
+ void _handle_option_pressed(int p_option);
void _node_visibility_changed();
friend class Path2DEditorPlugin;
diff --git a/editor/plugins/path_editor_plugin.cpp b/editor/plugins/path_editor_plugin.cpp
index 6dde639c54..72a8b55a52 100644
--- a/editor/plugins/path_editor_plugin.cpp
+++ b/editor/plugins/path_editor_plugin.cpp
@@ -128,11 +128,22 @@ void PathSpatialGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_p
if (p.intersects_ray(ray_from, ray_dir, &inters)) {
+ if (!PathEditorPlugin::singleton->is_handle_clicked()) {
+ orig_in_length = c->get_point_in(idx).length();
+ orig_out_length = c->get_point_out(idx).length();
+ PathEditorPlugin::singleton->set_handle_clicked(true);
+ }
+
Vector3 local = gi.xform(inters) - base;
if (t == 0) {
c->set_point_in(idx, local);
+
+ if (PathEditorPlugin::singleton->mirror_angle_enabled())
+ c->set_point_out(idx, PathEditorPlugin::singleton->mirror_length_enabled() ? -local : (-local.normalized() * orig_out_length));
} else {
c->set_point_out(idx, local);
+ if (PathEditorPlugin::singleton->mirror_angle_enabled())
+ c->set_point_in(idx, PathEditorPlugin::singleton->mirror_length_enabled() ? -local : (-local.normalized() * orig_in_length));
}
}
}
@@ -165,8 +176,6 @@ void PathSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p
int idx = p_idx / 2;
int t = p_idx % 2;
- Vector3 ofs;
-
if (t == 0) {
if (p_cancel) {
c->set_point_in(p_idx, p_restore);
@@ -176,6 +185,11 @@ void PathSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p
ur->create_action(TTR("Set Curve In Position"));
ur->add_do_method(c.ptr(), "set_point_in", idx, c->get_point_in(idx));
ur->add_undo_method(c.ptr(), "set_point_in", idx, p_restore);
+
+ if (PathEditorPlugin::singleton->mirror_angle_enabled()) {
+ ur->add_do_method(c.ptr(), "set_point_out", idx, PathEditorPlugin::singleton->mirror_length_enabled() ? -c->get_point_in(idx) : (-c->get_point_in(idx).normalized() * orig_out_length));
+ ur->add_undo_method(c.ptr(), "set_point_out", idx, PathEditorPlugin::singleton->mirror_length_enabled() ? -static_cast<Vector3>(p_restore) : (-static_cast<Vector3>(p_restore).normalized() * orig_out_length));
+ }
ur->commit_action();
} else {
@@ -188,6 +202,11 @@ void PathSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p
ur->create_action(TTR("Set Curve Out Position"));
ur->add_do_method(c.ptr(), "set_point_out", idx, c->get_point_out(idx));
ur->add_undo_method(c.ptr(), "set_point_out", idx, p_restore);
+
+ if (PathEditorPlugin::singleton->mirror_angle_enabled()) {
+ ur->add_do_method(c.ptr(), "set_point_in", idx, PathEditorPlugin::singleton->mirror_length_enabled() ? -c->get_point_out(idx) : (-c->get_point_out(idx).normalized() * orig_in_length));
+ ur->add_undo_method(c.ptr(), "set_point_in", idx, PathEditorPlugin::singleton->mirror_length_enabled() ? -static_cast<Vector3>(p_restore) : (-static_cast<Vector3>(p_restore).normalized() * orig_in_length));
+ }
ur->commit_action();
}
}
@@ -291,6 +310,9 @@ bool PathEditorPlugin::forward_spatial_gui_input(Camera *p_camera, const Ref<Inp
Point2 mbpos(mb->get_position().x, mb->get_position().y);
+ if (!mb->is_pressed())
+ set_handle_clicked(false);
+
if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT && (curve_create->is_pressed() || (curve_edit->is_pressed() && mb->get_control()))) {
//click into curve, break it down
PoolVector<Vector3> v3a = c->tessellate();
@@ -459,6 +481,7 @@ void PathEditorPlugin::make_visible(bool p_visible) {
curve_edit->show();
curve_del->show();
curve_close->show();
+ handle_menu->show();
sep->show();
} else {
@@ -466,6 +489,7 @@ void PathEditorPlugin::make_visible(bool p_visible) {
curve_edit->hide();
curve_del->hide();
curve_close->hide();
+ handle_menu->hide();
sep->hide();
{
@@ -495,6 +519,26 @@ void PathEditorPlugin::_close_curve() {
c->add_point(c->get_point_position(0), c->get_point_in(0), c->get_point_out(0));
}
+void PathEditorPlugin::_handle_option_pressed(int p_option) {
+
+ PopupMenu *pm;
+ pm = handle_menu->get_popup();
+
+ switch (p_option) {
+ case HANDLE_OPTION_ANGLE: {
+ bool is_checked = pm->is_item_checked(HANDLE_OPTION_ANGLE);
+ mirror_handle_angle = !is_checked;
+ pm->set_item_checked(HANDLE_OPTION_ANGLE, mirror_handle_angle);
+ pm->set_item_disabled(HANDLE_OPTION_LENGTH, !mirror_handle_angle);
+ } break;
+ case HANDLE_OPTION_LENGTH: {
+ bool is_checked = pm->is_item_checked(HANDLE_OPTION_LENGTH);
+ mirror_handle_length = !is_checked;
+ pm->set_item_checked(HANDLE_OPTION_LENGTH, mirror_handle_length);
+ } break;
+ }
+}
+
void PathEditorPlugin::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
@@ -510,6 +554,7 @@ void PathEditorPlugin::_bind_methods() {
ClassDB::bind_method(D_METHOD("_mode_changed"), &PathEditorPlugin::_mode_changed);
ClassDB::bind_method(D_METHOD("_close_curve"), &PathEditorPlugin::_close_curve);
+ ClassDB::bind_method(D_METHOD("_handle_option_pressed"), &PathEditorPlugin::_handle_option_pressed);
}
PathEditorPlugin *PathEditorPlugin::singleton = NULL;
@@ -519,6 +564,8 @@ PathEditorPlugin::PathEditorPlugin(EditorNode *p_node) {
path = NULL;
editor = p_node;
singleton = this;
+ mirror_handle_angle = true;
+ mirror_handle_length = true;
path_material = Ref<SpatialMaterial>(memnew(SpatialMaterial));
path_material->set_albedo(Color(0.5, 0.5, 1.0, 0.8));
@@ -567,6 +614,20 @@ PathEditorPlugin::PathEditorPlugin(EditorNode *p_node) {
curve_close->set_tooltip(TTR("Close Curve"));
SpatialEditor::get_singleton()->add_control_to_menu_panel(curve_close);
+ PopupMenu *menu;
+
+ handle_menu = memnew(MenuButton);
+ handle_menu->set_text(TTR("Options"));
+ handle_menu->hide();
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(handle_menu);
+
+ menu = handle_menu->get_popup();
+ menu->add_check_item(TTR("Mirror Handle Angles"));
+ menu->set_item_checked(HANDLE_OPTION_ANGLE, mirror_handle_angle);
+ menu->add_check_item(TTR("Mirror Handle Lengths"));
+ menu->set_item_checked(HANDLE_OPTION_LENGTH, mirror_handle_length);
+ menu->connect("id_pressed", this, "_handle_option_pressed");
+
curve_edit->set_pressed(true);
/*
collision_polygon_editor = memnew( PathEditor(p_node) );
diff --git a/editor/plugins/path_editor_plugin.h b/editor/plugins/path_editor_plugin.h
index 6d5f07f729..52dfb78b61 100644
--- a/editor/plugins/path_editor_plugin.h
+++ b/editor/plugins/path_editor_plugin.h
@@ -1,4 +1,4 @@
-/*************************************************************************/
+/*************************************************************************/
/* path_editor_plugin.h */
/*************************************************************************/
/* This file is part of: */
@@ -40,6 +40,8 @@ class PathSpatialGizmo : public EditorSpatialGizmo {
Path *path;
mutable Vector3 original;
+ mutable float orig_in_length;
+ mutable float orig_out_length;
public:
virtual String get_handle_name(int p_idx) const;
@@ -60,6 +62,7 @@ class PathEditorPlugin : public EditorPlugin {
ToolButton *curve_edit;
ToolButton *curve_del;
ToolButton *curve_close;
+ MenuButton *handle_menu;
EditorNode *editor;
@@ -67,6 +70,15 @@ class PathEditorPlugin : public EditorPlugin {
void _mode_changed(int p_idx);
void _close_curve();
+ void _handle_option_pressed(int p_option);
+ bool handle_clicked;
+ bool mirror_handle_angle;
+ bool mirror_handle_length;
+
+ enum HandleOption {
+ HANDLE_OPTION_ANGLE,
+ HANDLE_OPTION_LENGTH
+ };
protected:
void _notification(int p_what);
@@ -88,6 +100,11 @@ public:
virtual bool handles(Object *p_object) const;
virtual void make_visible(bool p_visible);
+ bool mirror_angle_enabled() { return mirror_handle_angle; }
+ bool mirror_length_enabled() { return mirror_handle_length; }
+ bool is_handle_clicked() { return handle_clicked; }
+ void set_handle_clicked(bool clicked) { handle_clicked = clicked; }
+
PathEditorPlugin(EditorNode *p_node);
~PathEditorPlugin();
};
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index aa4673f41e..876da7f61a 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -1872,6 +1872,7 @@ void ScriptEditor::save_all_scripts() {
}
_update_script_names();
+ EditorFileSystem::get_singleton()->update_script_classes();
}
void ScriptEditor::apply_scripts() const {
diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp
index 9b31e1a421..4b7f27c0c1 100644
--- a/editor/plugins/shader_editor_plugin.cpp
+++ b/editor/plugins/shader_editor_plugin.cpp
@@ -130,9 +130,9 @@ void ShaderTextEditor::_load_theme_settings() {
}
}
- for (const Set<String>::Element *E = ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader->get_mode())).front(); E; E = E->next()) {
+ for (int i = 0; i < ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader->get_mode())).size(); i++) {
- keywords.push_back(E->get());
+ keywords.push_back(ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader->get_mode()))[i]);
}
}
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
new file mode 100644
index 0000000000..682ca744ff
--- /dev/null
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -0,0 +1,1217 @@
+#include "visual_shader_editor_plugin.h"
+
+#include "core/io/resource_loader.h"
+#include "core/project_settings.h"
+#include "editor/editor_properties.h"
+#include "os/input.h"
+#include "os/keyboard.h"
+#include "scene/animation/animation_player.h"
+#include "scene/gui/menu_button.h"
+#include "scene/gui/panel.h"
+#include "scene/main/viewport.h"
+
+Control *VisualShaderNodePlugin::create_editor(const Ref<VisualShaderNode> &p_node) {
+
+ if (get_script_instance()) {
+ return get_script_instance()->call("create_editor", p_node);
+ }
+ return NULL;
+}
+
+void VisualShaderNodePlugin::_bind_methods() {
+
+ BIND_VMETHOD(MethodInfo(Variant::OBJECT, "create_editor", PropertyInfo(Variant::OBJECT, "for_node", PROPERTY_HINT_RESOURCE_TYPE, "VisualShaderNode")));
+}
+
+///////////////////
+
+void VisualShaderEditor::edit(VisualShader *p_visual_shader) {
+
+ if (p_visual_shader) {
+ visual_shader = Ref<VisualShader>(p_visual_shader);
+ } else {
+ visual_shader.unref();
+ }
+
+ if (visual_shader.is_null()) {
+ hide();
+ } else {
+ _update_graph();
+ }
+}
+
+void VisualShaderEditor::add_plugin(const Ref<VisualShaderNodePlugin> &p_plugin) {
+ if (plugins.find(p_plugin) != -1)
+ return;
+ plugins.push_back(p_plugin);
+}
+
+void VisualShaderEditor::remove_plugin(const Ref<VisualShaderNodePlugin> &p_plugin) {
+ plugins.erase(p_plugin);
+}
+
+void VisualShaderEditor::add_custom_type(const String &p_name, const String &p_category, const Ref<Script> &p_script) {
+
+ for (int i = 0; i < add_options.size(); i++) {
+ ERR_FAIL_COND(add_options[i].script == p_script);
+ }
+
+ AddOption ao;
+ ao.name = p_name;
+ ao.script = p_script;
+ ao.category = p_category;
+ add_options.push_back(ao);
+
+ _update_options_menu();
+}
+
+void VisualShaderEditor::remove_custom_type(const Ref<Script> &p_script) {
+
+ for (int i = 0; i < add_options.size(); i++) {
+ if (add_options[i].script == p_script) {
+ add_options.remove(i);
+ return;
+ }
+ }
+
+ _update_options_menu();
+}
+
+void VisualShaderEditor::_update_options_menu() {
+
+ String prev_category;
+ add_node->get_popup()->clear();
+ for (int i = 0; i < add_options.size(); i++) {
+ if (prev_category != add_options[i].category) {
+ add_node->get_popup()->add_separator(add_options[i].category);
+ }
+ add_node->get_popup()->add_item(add_options[i].name, i);
+ prev_category = add_options[i].category;
+ }
+}
+
+Size2 VisualShaderEditor::get_minimum_size() const {
+
+ return Size2(10, 200);
+}
+
+void VisualShaderEditor::_draw_color_over_button(Object *obj, Color p_color) {
+
+ Button *button = Object::cast_to<Button>(obj);
+ if (!button)
+ return;
+
+ Ref<StyleBox> normal = get_stylebox("normal", "Button");
+ button->draw_rect(Rect2(normal->get_offset(), button->get_size() - normal->get_minimum_size()), p_color);
+}
+
+static Ref<StyleBoxEmpty> make_empty_stylebox(float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_bottom = -1) {
+ Ref<StyleBoxEmpty> style(memnew(StyleBoxEmpty));
+ style->set_default_margin(MARGIN_LEFT, p_margin_left * EDSCALE);
+ style->set_default_margin(MARGIN_RIGHT, p_margin_right * EDSCALE);
+ style->set_default_margin(MARGIN_BOTTOM, p_margin_bottom * EDSCALE);
+ style->set_default_margin(MARGIN_TOP, p_margin_top * EDSCALE);
+ return style;
+}
+
+void VisualShaderEditor::_update_graph() {
+
+ if (updating)
+ return;
+
+ graph->set_scroll_ofs(visual_shader->get_graph_offset() * EDSCALE);
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+ graph->clear_connections();
+ //erase all nodes
+ for (int i = 0; i < graph->get_child_count(); i++) {
+
+ if (Object::cast_to<GraphNode>(graph->get_child(i))) {
+ memdelete(graph->get_child(i));
+ i--;
+ }
+ }
+
+ static const Color type_color[3] = {
+ Color::html("#61daf4"),
+ Color::html("#d67dee"),
+ Color::html("#f6a86e")
+ };
+
+ List<VisualShader::Connection> connections;
+ visual_shader->get_node_connections(type, &connections);
+
+ Ref<StyleBoxEmpty> label_style = make_empty_stylebox(2, 1, 2, 1);
+
+ Vector<int> nodes = visual_shader->get_node_list(type);
+
+ for (int n_i = 0; n_i < nodes.size(); n_i++) {
+
+ Vector2 position = visual_shader->get_node_position(type, nodes[n_i]);
+ Ref<VisualShaderNode> vsnode = visual_shader->get_node(type, nodes[n_i]);
+
+ GraphNode *node = memnew(GraphNode);
+ graph->add_child(node);
+
+ /*if (!vsnode->is_connected("changed", this, "_node_changed")) {
+ vsnode->connect("changed", this, "_node_changed", varray(vsnode->get_instance_id()), CONNECT_DEFERRED);
+ }*/
+
+ node->set_offset(position);
+
+ node->set_title(vsnode->get_caption());
+ node->set_name(itos(nodes[n_i]));
+
+ if (nodes[n_i] >= 2) {
+ node->set_show_close_button(true);
+ node->connect("close_request", this, "_delete_request", varray(nodes[n_i]), CONNECT_DEFERRED);
+ }
+
+ node->connect("dragged", this, "_node_dragged", varray(nodes[n_i]));
+
+ Control *custom_editor = NULL;
+ int port_offset = 0;
+
+ Ref<VisualShaderNodeUniform> uniform = vsnode;
+ if (uniform.is_valid()) {
+ LineEdit *uniform_name = memnew(LineEdit);
+ uniform_name->set_text(uniform->get_uniform_name());
+ node->add_child(uniform_name);
+ uniform_name->connect("text_entered", this, "_line_edit_changed", varray(uniform_name, nodes[n_i]));
+ uniform_name->connect("focus_exited", this, "_line_edit_focus_out", varray(uniform_name, nodes[n_i]));
+
+ if (vsnode->get_input_port_count() == 0 && vsnode->get_output_port_count() == 1 && vsnode->get_output_port_name(0) == "") {
+ //shortcut
+ VisualShaderNode::PortType port_right = vsnode->get_output_port_type(0);
+ node->set_slot(0, false, VisualShaderNode::PORT_TYPE_SCALAR, Color(), true, port_right, type_color[port_right]);
+ continue;
+ }
+ port_offset++;
+ }
+
+ for (int i = 0; i < plugins.size(); i++) {
+ custom_editor = plugins[i]->create_editor(vsnode);
+ if (custom_editor) {
+ break;
+ }
+ }
+
+ if (custom_editor && vsnode->get_output_port_count() > 0 && vsnode->get_output_port_name(0) == "" && (vsnode->get_input_port_count() == 0 || vsnode->get_input_port_name(0) == "")) {
+ //will be embedded in first port
+ } else if (custom_editor) {
+ port_offset++;
+ node->add_child(custom_editor);
+ custom_editor = NULL;
+ }
+
+ for (int i = 0; i < MAX(vsnode->get_input_port_count(), vsnode->get_output_port_count()); i++) {
+
+ if (vsnode->is_port_separator(i)) {
+ node->add_child(memnew(HSeparator));
+ port_offset++;
+ }
+
+ bool valid_left = i < vsnode->get_input_port_count();
+ VisualShaderNode::PortType port_left = VisualShaderNode::PORT_TYPE_SCALAR;
+ bool port_left_used = false;
+ String name_left;
+ if (valid_left) {
+ name_left = vsnode->get_input_port_name(i);
+ port_left = vsnode->get_input_port_type(i);
+ for (List<VisualShader::Connection>::Element *E = connections.front(); E; E = E->next()) {
+ if (E->get().to_node == nodes[n_i] && E->get().to_port == i) {
+ port_left_used = true;
+ }
+ }
+ }
+
+ bool valid_right = i < vsnode->get_output_port_count();
+ VisualShaderNode::PortType port_right = VisualShaderNode::PORT_TYPE_SCALAR;
+ String name_right;
+ if (valid_right) {
+ name_right = vsnode->get_output_port_name(i);
+ port_right = vsnode->get_output_port_type(i);
+ }
+
+ HBoxContainer *hb = memnew(HBoxContainer);
+
+ Variant default_value;
+
+ if (valid_left && !port_left_used) {
+ default_value = vsnode->get_input_port_default_value(i);
+ }
+
+ if (default_value.get_type() != Variant::NIL) { // only a label
+ Button *button = memnew(Button);
+ hb->add_child(button);
+ button->connect("pressed", this, "_edit_port_default_input", varray(button, nodes[n_i], i));
+
+ switch (default_value.get_type()) {
+
+ case Variant::COLOR: {
+ button->set_custom_minimum_size(Size2(30, 0) * EDSCALE);
+ button->connect("draw", this, "_draw_color_over_button", varray(button, default_value));
+ } break;
+ case Variant::INT:
+ case Variant::REAL: {
+ button->set_text(String::num(default_value, 4));
+ } break;
+ case Variant::VECTOR3: {
+ Vector3 v = default_value;
+ button->set_text(String::num(v.x, 3) + "," + String::num(v.y, 3) + "," + String::num(v.z, 3));
+ } break;
+ default: {}
+ }
+ }
+
+ if (i == 0 && custom_editor) {
+
+ hb->add_child(custom_editor);
+ custom_editor->set_h_size_flags(SIZE_EXPAND_FILL);
+ } else {
+
+ if (valid_left) {
+
+ Label *label = memnew(Label);
+ label->set_text(name_left);
+ label->add_style_override("normal", label_style); //more compact
+ hb->add_child(label);
+ }
+
+ hb->add_spacer();
+
+ if (valid_right) {
+
+ Label *label = memnew(Label);
+ label->set_text(name_right);
+ label->set_align(Label::ALIGN_RIGHT);
+ label->add_style_override("normal", label_style); //more compact
+ hb->add_child(label);
+ }
+ }
+
+ if (valid_right && edit_type->get_selected() == VisualShader::TYPE_FRAGMENT) {
+ TextureButton *preview = memnew(TextureButton);
+ preview->set_toggle_mode(true);
+ preview->set_normal_texture(get_icon("GuiVisibilityHidden", "EditorIcons"));
+ preview->set_pressed_texture(get_icon("GuiVisibilityVisible", "EditorIcons"));
+ preview->set_v_size_flags(SIZE_SHRINK_CENTER);
+
+ if (vsnode->get_output_port_for_preview() == i) {
+ preview->set_pressed(true);
+ }
+
+ preview->connect("pressed", this, "_preview_select_port", varray(nodes[n_i], i), CONNECT_DEFERRED);
+ hb->add_child(preview);
+ }
+
+ node->add_child(hb);
+
+ node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], valid_right, port_right, type_color[port_right]);
+ }
+
+ if (vsnode->get_output_port_for_preview() >= 0) {
+ VisualShaderNodePortPreview *port_preview = memnew(VisualShaderNodePortPreview);
+ port_preview->setup(visual_shader, type, nodes[n_i], vsnode->get_output_port_for_preview());
+ port_preview->set_h_size_flags(SIZE_SHRINK_CENTER);
+ node->add_child(port_preview);
+ }
+
+ String error = vsnode->get_warning(visual_shader->get_mode(), type);
+ if (error != String()) {
+ Label *error_label = memnew(Label);
+ error_label->add_color_override("font_color", get_color("error_color", "Editor"));
+ error_label->set_text(error);
+ node->add_child(error_label);
+ }
+ }
+
+ for (List<VisualShader::Connection>::Element *E = connections.front(); E; E = E->next()) {
+
+ int from = E->get().from_node;
+ int from_idx = E->get().from_port;
+ int to = E->get().to_node;
+ int to_idx = E->get().to_port;
+
+ graph->connect_node(itos(from), from_idx, itos(to), to_idx);
+ }
+}
+
+void VisualShaderEditor::_preview_select_port(int p_node, int p_port) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+ Ref<VisualShaderNode> node = visual_shader->get_node(type, p_node);
+ if (node.is_null()) {
+ return;
+ }
+
+ if (node->get_output_port_for_preview() == p_port) {
+ p_port = -1; //toggle it
+ }
+ undo_redo->create_action("Set Uniform Name");
+ undo_redo->add_do_method(node.ptr(), "set_output_port_for_preview", p_port);
+ undo_redo->add_undo_method(node.ptr(), "set_output_port_for_preview", node->get_output_port_for_preview());
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+}
+
+void VisualShaderEditor::_line_edit_changed(const String &p_text, Object *line_edit, int p_node_id) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+
+ Ref<VisualShaderNodeUniform> node = visual_shader->get_node(type, p_node_id);
+ ERR_FAIL_COND(!node.is_valid());
+
+ String validated_name = visual_shader->validate_uniform_name(p_text, node);
+
+ updating = true;
+ undo_redo->create_action("Set Uniform Name");
+ undo_redo->add_do_method(node.ptr(), "set_uniform_name", validated_name);
+ undo_redo->add_undo_method(node.ptr(), "set_uniform_name", node->get_uniform_name());
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+ updating = false;
+
+ Object::cast_to<LineEdit>(line_edit)->set_text(validated_name);
+}
+
+void VisualShaderEditor::_line_edit_focus_out(Object *line_edit, int p_node_id) {
+
+ String text = Object::cast_to<LineEdit>(line_edit)->get_text();
+ _line_edit_changed(text, line_edit, p_node_id);
+}
+
+void VisualShaderEditor::_port_edited() {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+
+ Variant value = property_editor->get_variant();
+ Ref<VisualShaderNode> vsn = visual_shader->get_node(type, editing_node);
+ ERR_FAIL_COND(!vsn.is_valid());
+
+ undo_redo->create_action("Set Input Default Port");
+ undo_redo->add_do_method(vsn.ptr(), "set_input_port_default_value", editing_port, value);
+ undo_redo->add_undo_method(vsn.ptr(), "set_input_port_default_value", editing_port, vsn->get_input_port_default_value(editing_port));
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+
+ property_editor->hide();
+}
+
+void VisualShaderEditor::_edit_port_default_input(Object *p_button, int p_node, int p_port) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+
+ Ref<VisualShaderNode> vsn = visual_shader->get_node(type, p_node);
+
+ Button *button = Object::cast_to<Button>(p_button);
+ ERR_FAIL_COND(!button);
+ Variant value = vsn->get_input_port_default_value(p_port);
+ property_editor->set_global_position(button->get_global_position() + Vector2(0, button->get_size().height));
+ property_editor->edit(NULL, "", value.get_type(), value, 0, "");
+ property_editor->popup();
+ editing_node = p_node;
+ editing_port = p_port;
+}
+
+void VisualShaderEditor::_add_node(int p_idx) {
+
+ ERR_FAIL_INDEX(p_idx, add_options.size());
+
+ Ref<VisualShaderNode> vsnode;
+
+ if (add_options[p_idx].type != String()) {
+ VisualShaderNode *vsn = Object::cast_to<VisualShaderNode>(ClassDB::instance(add_options[p_idx].type));
+ ERR_FAIL_COND(!vsn);
+ vsnode = Ref<VisualShaderNode>(vsn);
+ } else {
+ ERR_FAIL_COND(add_options[p_idx].script.is_null());
+ String base_type = add_options[p_idx].script->get_instance_base_type();
+ VisualShaderNode *vsn = Object::cast_to<VisualShaderNode>(ClassDB::instance(base_type));
+ ERR_FAIL_COND(!vsn);
+ vsnode = Ref<VisualShaderNode>(vsn);
+ vsnode->set_script(add_options[p_idx].script.get_ref_ptr());
+ }
+
+ Point2 position = (graph->get_scroll_ofs() + graph->get_size() * 0.5) / EDSCALE;
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+
+ int id_to_use = visual_shader->get_valid_node_id(type);
+
+ undo_redo->create_action("Add Node to Visual Shader");
+ undo_redo->add_do_method(visual_shader.ptr(), "add_node", type, vsnode, position, id_to_use);
+ undo_redo->add_undo_method(visual_shader.ptr(), "remove_node", type, id_to_use);
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+}
+
+void VisualShaderEditor::_node_dragged(const Vector2 &p_from, const Vector2 &p_to, int p_node) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+
+ updating = true;
+ undo_redo->create_action("Node Moved");
+ undo_redo->add_do_method(visual_shader.ptr(), "set_node_position", type, p_node, p_to);
+ undo_redo->add_undo_method(visual_shader.ptr(), "set_node_position", type, p_node, p_from);
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+ updating = false;
+}
+
+void VisualShaderEditor::_connection_request(const String &p_from, int p_from_index, const String &p_to, int p_to_index) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+
+ int from = p_from.to_int();
+ int to = p_to.to_int();
+
+ if (!visual_shader->can_connect_nodes(type, from, p_from_index, to, p_to_index)) {
+ EditorNode::get_singleton()->show_warning(TTR("Unable to connect, port may be in use or connection may be invalid."));
+ return;
+ }
+
+ undo_redo->create_action("Nodes Connected");
+ undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", type, from, p_from_index, to, p_to_index);
+ undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", type, from, p_from_index, to, p_to_index);
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+}
+
+void VisualShaderEditor::_disconnection_request(const String &p_from, int p_from_index, const String &p_to, int p_to_index) {
+
+ graph->disconnect_node(p_from, p_from_index, p_to, p_to_index);
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+
+ int from = p_from.to_int();
+ int to = p_to.to_int();
+
+ //updating = true; seems graph edit can handle this, no need to protect
+ undo_redo->create_action("Nodes Disconnected");
+ undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from, p_from_index, to, p_to_index);
+ undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes", type, from, p_from_index, to, p_to_index);
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+ //updating = false;
+}
+
+void VisualShaderEditor::_connection_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_position) {
+}
+
+void VisualShaderEditor::_delete_request(int which) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+
+ undo_redo->create_action("Delete Node");
+ undo_redo->add_do_method(visual_shader.ptr(), "remove_node", type, which);
+ undo_redo->add_undo_method(visual_shader.ptr(), "add_node", type, visual_shader->get_node(type, which), visual_shader->get_node_position(type, which), which);
+
+ List<VisualShader::Connection> conns;
+ visual_shader->get_node_connections(type, &conns);
+
+ for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) {
+ if (E->get().from_node == which || E->get().to_node == which) {
+ undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes", type, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port);
+ }
+ }
+
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+}
+
+void VisualShaderEditor::_node_selected(Object *p_node) {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+
+ GraphNode *gn = Object::cast_to<GraphNode>(p_node);
+ ERR_FAIL_COND(!gn);
+
+ int id = String(gn->get_name()).to_int();
+
+ Ref<VisualShaderNode> vsnode = visual_shader->get_node(type, id);
+ ERR_FAIL_COND(!vsnode.is_valid());
+
+ //do not rely on this, makes editor more complex
+ //EditorNode::get_singleton()->push_item(vsnode.ptr(), "", true);
+}
+
+void VisualShaderEditor::_input(const Ref<InputEvent> p_event) {
+ if (graph->has_focus()) {
+ Ref<InputEventMouseButton> mb = p_event;
+
+ if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_RIGHT) {
+ add_node->get_popup()->set_position(get_viewport()->get_mouse_position());
+ add_node->get_popup()->show_modal();
+ }
+ }
+}
+
+void VisualShaderEditor::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+
+ error_panel->add_style_override("panel", get_stylebox("bg", "Tree"));
+ error_label->add_color_override("font_color", get_color("error_color", "Editor"));
+ }
+
+ if (p_what == NOTIFICATION_PROCESS) {
+ }
+}
+
+void VisualShaderEditor::_scroll_changed(const Vector2 &p_scroll) {
+ if (updating)
+ return;
+ updating = true;
+ visual_shader->set_graph_offset(p_scroll / EDSCALE);
+ updating = false;
+}
+
+void VisualShaderEditor::_node_changed(int p_id) {
+ if (updating)
+ return;
+
+ if (is_visible_in_tree()) {
+ _update_graph();
+ }
+}
+
+void VisualShaderEditor::_duplicate_nodes() {
+
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+
+ List<int> nodes;
+
+ for (int i = 0; i < graph->get_child_count(); i++) {
+
+ if (Object::cast_to<GraphNode>(graph->get_child(i))) {
+ int id = String(graph->get_child(i)->get_name()).to_int();
+ Ref<VisualShaderNode> node = visual_shader->get_node(type, id);
+ Ref<VisualShaderNodeOutput> output = node;
+ if (output.is_valid()) //cant duplicate output
+ continue;
+ if (node.is_valid()) {
+ nodes.push_back(id);
+ }
+ }
+ }
+
+ if (nodes.empty())
+ return;
+
+ undo_redo->create_action("Duplicate Nodes");
+
+ int base_id = visual_shader->get_valid_node_id(type);
+ int id_from = base_id;
+ Map<int, int> connection_remap;
+
+ for (List<int>::Element *E = nodes.front(); E; E = E->next()) {
+
+ connection_remap[E->get()] = id_from;
+ Ref<VisualShaderNode> node = visual_shader->get_node(type, E->get());
+
+ Ref<VisualShaderNode> dupli = node->duplicate();
+
+ undo_redo->add_do_method(visual_shader.ptr(), "add_node", type, dupli, visual_shader->get_node_position(type, E->get()) + Vector2(10, 10) * EDSCALE, id_from);
+ undo_redo->add_undo_method(visual_shader.ptr(), "remove_node", type, id_from);
+
+ id_from++;
+ }
+
+ List<VisualShader::Connection> conns;
+ visual_shader->get_node_connections(type, &conns);
+
+ for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) {
+ if (connection_remap.has(E->get().from_node) && connection_remap.has(E->get().to_node)) {
+ undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", type, connection_remap[E->get().from_node], E->get().from_port, connection_remap[E->get().to_node], E->get().to_port);
+ }
+ }
+
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+
+ //reselect
+ for (int i = 0; i < graph->get_child_count(); i++) {
+
+ if (Object::cast_to<GraphNode>(graph->get_child(i))) {
+ int id = String(graph->get_child(i)->get_name()).to_int();
+ if (nodes.find(id)) {
+ Object::cast_to<GraphNode>(graph->get_child(i))->set_selected(true);
+ } else {
+ Object::cast_to<GraphNode>(graph->get_child(i))->set_selected(false);
+ }
+ }
+ }
+}
+
+void VisualShaderEditor::_mode_selected(int p_id) {
+ _update_graph();
+}
+
+void VisualShaderEditor::_input_select_item(Ref<VisualShaderNodeInput> input, String name) {
+
+ String prev_name = input->get_input_name();
+
+ if (name == prev_name)
+ return;
+
+ bool type_changed = input->get_input_type_by_name(name) != input->get_input_type_by_name(prev_name);
+
+ UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ undo_redo->create_action("Visual Shader Input Type Changed");
+
+ undo_redo->add_do_method(input.ptr(), "set_input_name", name);
+ undo_redo->add_undo_method(input.ptr(), "set_input_name", prev_name);
+
+ if (type_changed) {
+ //restore connections if type changed
+ VisualShader::Type type = VisualShader::Type(edit_type->get_selected());
+ int id = visual_shader->find_node_id(type, input);
+ List<VisualShader::Connection> conns;
+ visual_shader->get_node_connections(type, &conns);
+ for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) {
+ if (E->get().from_node == id) {
+ undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes", type, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port);
+ }
+ }
+ }
+
+ undo_redo->add_do_method(VisualShaderEditor::get_singleton(), "_update_graph");
+ undo_redo->add_undo_method(VisualShaderEditor::get_singleton(), "_update_graph");
+
+ undo_redo->commit_action();
+}
+
+void VisualShaderEditor::_bind_methods() {
+
+ ClassDB::bind_method("_update_graph", &VisualShaderEditor::_update_graph);
+ ClassDB::bind_method("_add_node", &VisualShaderEditor::_add_node);
+ ClassDB::bind_method("_node_dragged", &VisualShaderEditor::_node_dragged);
+ ClassDB::bind_method("_connection_request", &VisualShaderEditor::_connection_request);
+ ClassDB::bind_method("_disconnection_request", &VisualShaderEditor::_disconnection_request);
+ ClassDB::bind_method("_node_selected", &VisualShaderEditor::_node_selected);
+ ClassDB::bind_method("_scroll_changed", &VisualShaderEditor::_scroll_changed);
+ ClassDB::bind_method("_delete_request", &VisualShaderEditor::_delete_request);
+ ClassDB::bind_method("_node_changed", &VisualShaderEditor::_node_changed);
+ ClassDB::bind_method("_edit_port_default_input", &VisualShaderEditor::_edit_port_default_input);
+ ClassDB::bind_method("_port_edited", &VisualShaderEditor::_port_edited);
+ ClassDB::bind_method("_connection_to_empty", &VisualShaderEditor::_connection_to_empty);
+ ClassDB::bind_method("_line_edit_focus_out", &VisualShaderEditor::_line_edit_focus_out);
+ ClassDB::bind_method("_line_edit_changed", &VisualShaderEditor::_line_edit_changed);
+ ClassDB::bind_method("_duplicate_nodes", &VisualShaderEditor::_duplicate_nodes);
+ ClassDB::bind_method("_mode_selected", &VisualShaderEditor::_mode_selected);
+ ClassDB::bind_method("_input_select_item", &VisualShaderEditor::_input_select_item);
+ ClassDB::bind_method("_preview_select_port", &VisualShaderEditor::_preview_select_port);
+ ClassDB::bind_method("_input", &VisualShaderEditor::_input);
+}
+
+VisualShaderEditor *VisualShaderEditor::singleton = NULL;
+
+VisualShaderEditor::VisualShaderEditor() {
+
+ singleton = this;
+ updating = false;
+
+ graph = memnew(GraphEdit);
+ add_child(graph);
+ graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_SCALAR);
+ graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_VECTOR);
+ graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_TRANSFORM);
+ //graph->add_valid_left_disconnect_type(0);
+ graph->set_v_size_flags(SIZE_EXPAND_FILL);
+ graph->connect("connection_request", this, "_connection_request", varray(), CONNECT_DEFERRED);
+ graph->connect("disconnection_request", this, "_disconnection_request", varray(), CONNECT_DEFERRED);
+ graph->connect("node_selected", this, "_node_selected");
+ graph->connect("scroll_offset_changed", this, "_scroll_changed");
+ graph->connect("duplicate_nodes_request", this, "_duplicate_nodes");
+ graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_SCALAR);
+ graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_VECTOR);
+ //graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR, VisualShaderNode::PORT_TYPE_SCALAR);
+ graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR, VisualShaderNode::PORT_TYPE_VECTOR);
+ graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_TRANSFORM, VisualShaderNode::PORT_TYPE_TRANSFORM);
+
+ VSeparator *vs = memnew(VSeparator);
+ graph->get_zoom_hbox()->add_child(vs);
+ graph->get_zoom_hbox()->move_child(vs, 0);
+
+ edit_type = memnew(OptionButton);
+ edit_type->add_item(TTR("Vertex"));
+ edit_type->add_item(TTR("Fragment"));
+ edit_type->add_item(TTR("Light"));
+ edit_type->select(1);
+ edit_type->connect("item_selected", this, "_mode_selected");
+ graph->get_zoom_hbox()->add_child(edit_type);
+ graph->get_zoom_hbox()->move_child(edit_type, 0);
+
+ add_node = memnew(MenuButton);
+ graph->get_zoom_hbox()->add_child(add_node);
+ add_node->set_text(TTR("Add Node.."));
+ graph->get_zoom_hbox()->move_child(add_node, 0);
+ add_node->get_popup()->connect("id_pressed", this, "_add_node");
+
+ add_options.push_back(AddOption("Scalar", "Constants", "VisualShaderNodeScalarConstant"));
+ add_options.push_back(AddOption("Vector", "Constants", "VisualShaderNodeVec3Constant"));
+ add_options.push_back(AddOption("Color", "Constants", "VisualShaderNodeColorConstant"));
+ add_options.push_back(AddOption("Transform", "Constants", "VisualShaderNodeTransformConstant"));
+ add_options.push_back(AddOption("Texture", "Constants", "VisualShaderNodeTexture"));
+ add_options.push_back(AddOption("CubeMap", "Constants", "VisualShaderNodeCubeMap"));
+ add_options.push_back(AddOption("ScalarOp", "Operators", "VisualShaderNodeScalarOp"));
+ add_options.push_back(AddOption("VectorOp", "Operators", "VisualShaderNodeVectorOp"));
+ add_options.push_back(AddOption("ColorOp", "Operators", "VisualShaderNodeColorOp"));
+ add_options.push_back(AddOption("TransformMult", "Operators", "VisualShaderNodeTransformMult"));
+ add_options.push_back(AddOption("TransformVectorMult", "Operators", "VisualShaderNodeTransformVecMult"));
+ add_options.push_back(AddOption("ScalarFunc", "Functions", "VisualShaderNodeScalarFunc"));
+ add_options.push_back(AddOption("VectorFunc", "Functions", "VisualShaderNodeVectorFunc"));
+ add_options.push_back(AddOption("DotProduct", "Functions", "VisualShaderNodeDotProduct"));
+ add_options.push_back(AddOption("VectorLen", "Functions", "VisualShaderNodeVectorLen"));
+ add_options.push_back(AddOption("ScalarInterp", "Interpolation", "VisualShaderNodeScalarInterp"));
+ add_options.push_back(AddOption("VectorInterp", "Interpolation", "VisualShaderNodeVectorInterp"));
+ add_options.push_back(AddOption("VectorCompose", "Compose", "VisualShaderNodeVectorCompose"));
+ add_options.push_back(AddOption("TransformCompose", "Compose", "VisualShaderNodeTransformCompose"));
+ add_options.push_back(AddOption("VectorDecompose", "Decompose", "VisualShaderNodeVectorDecompose"));
+ add_options.push_back(AddOption("TransformDecompose", "Decompose", "VisualShaderNodeTransformDecompose"));
+ add_options.push_back(AddOption("Scalar", "Uniforms", "VisualShaderNodeScalarUniform"));
+ add_options.push_back(AddOption("Vector", "Uniforms", "VisualShaderNodeVec3Uniform"));
+ add_options.push_back(AddOption("Color", "Uniforms", "VisualShaderNodeColorUniform"));
+ add_options.push_back(AddOption("Transform", "Uniforms", "VisualShaderNodeTransformUniform"));
+ add_options.push_back(AddOption("Texture", "Uniforms", "VisualShaderNodeTextureUniform"));
+ add_options.push_back(AddOption("CubeMap", "Uniforms", "VisualShaderNodeCubeMapUniform"));
+ add_options.push_back(AddOption("Input", "Inputs", "VisualShaderNodeInput"));
+
+ _update_options_menu();
+
+ error_panel = memnew(PanelContainer);
+ add_child(error_panel);
+ error_label = memnew(Label);
+ error_panel->add_child(error_label);
+ error_label->set_text("eh");
+ error_panel->hide();
+
+ undo_redo = EditorNode::get_singleton()->get_undo_redo();
+
+ Ref<VisualShaderNodePluginDefault> default_plugin;
+ default_plugin.instance();
+ add_plugin(default_plugin);
+
+ property_editor = memnew(CustomPropertyEditor);
+ add_child(property_editor);
+
+ property_editor->connect("variant_changed", this, "_port_edited");
+}
+
+void VisualShaderEditorPlugin::edit(Object *p_object) {
+
+ visual_shader_editor->edit(Object::cast_to<VisualShader>(p_object));
+}
+
+bool VisualShaderEditorPlugin::handles(Object *p_object) const {
+
+ return p_object->is_class("VisualShader");
+}
+
+void VisualShaderEditorPlugin::make_visible(bool p_visible) {
+
+ if (p_visible) {
+ //editor->hide_animation_player_editors();
+ //editor->animation_panel_make_visible(true);
+ button->show();
+ editor->make_bottom_panel_item_visible(visual_shader_editor);
+ visual_shader_editor->set_process_input(true);
+ //visual_shader_editor->set_process(true);
+ } else {
+
+ if (visual_shader_editor->is_visible_in_tree())
+ editor->hide_bottom_panel();
+ button->hide();
+ visual_shader_editor->set_process_input(false);
+ //visual_shader_editor->set_process(false);
+ }
+}
+
+VisualShaderEditorPlugin::VisualShaderEditorPlugin(EditorNode *p_node) {
+
+ editor = p_node;
+ visual_shader_editor = memnew(VisualShaderEditor);
+ visual_shader_editor->set_custom_minimum_size(Size2(0, 300));
+
+ button = editor->add_bottom_panel_item(TTR("VisualShader"), visual_shader_editor);
+ button->hide();
+}
+
+VisualShaderEditorPlugin::~VisualShaderEditorPlugin() {
+}
+
+////////////////
+
+class VisualShaderNodePluginInputEditor : public OptionButton {
+ GDCLASS(VisualShaderNodePluginInputEditor, OptionButton)
+
+ Ref<VisualShaderNodeInput> input;
+
+protected:
+ static void _bind_methods() {
+ ClassDB::bind_method("_item_selected", &VisualShaderNodePluginInputEditor::_item_selected);
+ }
+
+public:
+ void _notification(int p_what) {
+ if (p_what == NOTIFICATION_READY) {
+ connect("item_selected", this, "_item_selected");
+ }
+ }
+
+ void _item_selected(int p_item) {
+ VisualShaderEditor::get_singleton()->call_deferred("_input_select_item", input, get_item_text(p_item));
+ }
+
+ void setup(const Ref<VisualShaderNodeInput> &p_input) {
+ input = p_input;
+ Ref<Texture> type_icon[3] = {
+ EditorNode::get_singleton()->get_gui_base()->get_icon("float", "EditorIcons"),
+ EditorNode::get_singleton()->get_gui_base()->get_icon("Vector3", "EditorIcons"),
+ EditorNode::get_singleton()->get_gui_base()->get_icon("Transform", "EditorIcons"),
+ };
+
+ add_item("[None]");
+ int to_select = -1;
+ for (int i = 0; i < input->get_input_index_count(); i++) {
+ if (input->get_input_name() == input->get_input_index_name(i)) {
+ to_select = i + 1;
+ }
+ add_icon_item(type_icon[input->get_input_index_type(i)], input->get_input_index_name(i));
+ }
+
+ if (to_select >= 0) {
+ select(to_select);
+ }
+ }
+};
+
+class VisualShaderNodePluginDefaultEditor : public VBoxContainer {
+ GDCLASS(VisualShaderNodePluginDefaultEditor, VBoxContainer)
+public:
+ void _property_changed(const String &prop, const Variant &p_value) {
+
+ UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+
+ updating = true;
+ undo_redo->create_action("Edit Visual Property: " + prop, UndoRedo::MERGE_ENDS);
+ undo_redo->add_do_property(node.ptr(), prop, p_value);
+ undo_redo->add_undo_property(node.ptr(), prop, node->get(prop));
+ undo_redo->commit_action();
+ updating = false;
+ }
+
+ void _node_changed() {
+ if (updating)
+ return;
+ for (int i = 0; i < properties.size(); i++) {
+ properties[i]->update_property();
+ }
+ }
+
+ void _refresh_request() {
+ VisualShaderEditor::get_singleton()->call_deferred("_update_graph");
+ }
+
+ bool updating;
+ Ref<VisualShaderNode> node;
+ Vector<EditorProperty *> properties;
+
+ void setup(Vector<EditorProperty *> p_properties, const Vector<StringName> &p_names, Ref<VisualShaderNode> p_node) {
+ updating = false;
+ node = p_node;
+ properties = p_properties;
+
+ for (int i = 0; i < p_properties.size(); i++) {
+
+ add_child(p_properties[i]);
+
+ properties[i]->connect("property_changed", this, "_property_changed");
+ properties[i]->set_object_and_property(node.ptr(), p_names[i]);
+ properties[i]->update_property();
+ properties[i]->set_name_split_ratio(0);
+ }
+ node->connect("changed", this, "_node_changed");
+ node->connect("editor_refresh_request", this, "_refresh_request", varray(), CONNECT_DEFERRED);
+ }
+
+ static void _bind_methods() {
+ ClassDB::bind_method("_property_changed", &VisualShaderNodePluginDefaultEditor::_property_changed);
+ ClassDB::bind_method("_node_changed", &VisualShaderNodePluginDefaultEditor::_node_changed);
+ ClassDB::bind_method("_refresh_request", &VisualShaderNodePluginDefaultEditor::_refresh_request);
+ }
+};
+
+Control *VisualShaderNodePluginDefault::create_editor(const Ref<VisualShaderNode> &p_node) {
+
+ if (p_node->is_class("VisualShaderNodeInput")) {
+ //create input
+ VisualShaderNodePluginInputEditor *input_editor = memnew(VisualShaderNodePluginInputEditor);
+ input_editor->setup(p_node);
+ return input_editor;
+ }
+
+ Vector<StringName> properties = p_node->get_editable_properties();
+ if (properties.size() == 0) {
+ return NULL;
+ }
+
+ List<PropertyInfo> props;
+ p_node->get_property_list(&props);
+
+ Vector<PropertyInfo> pinfo;
+
+ for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
+
+ for (int i = 0; i < properties.size(); i++) {
+ if (E->get().name == String(properties[i])) {
+ pinfo.push_back(E->get());
+ }
+ }
+ }
+
+ if (pinfo.size() == 0)
+ return NULL;
+
+ properties.clear();
+
+ Ref<VisualShaderNode> node = p_node;
+ Vector<EditorProperty *> editors;
+
+ for (int i = 0; i < pinfo.size(); i++) {
+
+ EditorProperty *prop = EditorInspector::instantiate_property_editor(node.ptr(), pinfo[i].type, pinfo[i].name, pinfo[i].hint, pinfo[i].hint_string, pinfo[i].usage);
+ if (!prop)
+ return NULL;
+
+ if (Object::cast_to<EditorPropertyResource>(prop)) {
+ Object::cast_to<EditorPropertyResource>(prop)->set_use_sub_inspector(false);
+ prop->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
+ } else if (Object::cast_to<EditorPropertyTransform>(prop)) {
+ prop->set_custom_minimum_size(Size2(250 * EDSCALE, 0));
+ } else if (Object::cast_to<EditorPropertyFloat>(prop) || Object::cast_to<EditorPropertyVector3>(prop)) {
+ prop->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
+ } else if (Object::cast_to<EditorPropertyEnum>(prop)) {
+ prop->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
+ Object::cast_to<EditorPropertyEnum>(prop)->set_option_button_clip(false);
+ }
+
+ editors.push_back(prop);
+ properties.push_back(pinfo[i].name);
+ }
+ VisualShaderNodePluginDefaultEditor *editor = memnew(VisualShaderNodePluginDefaultEditor);
+ editor->setup(editors, properties, p_node);
+ return editor;
+}
+
+void EditorPropertyShaderMode::_option_selected(int p_which) {
+
+ //will not use this, instead will do all the logic setting manually
+ //emit_signal("property_changed", get_edited_property(), p_which);
+
+ Ref<VisualShader> visual_shader(Object::cast_to<VisualShader>(get_edited_object()));
+
+ if (visual_shader->get_mode() == p_which)
+ return;
+
+ UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ undo_redo->create_action("Visual Shader Mode Changed");
+ //do is easy
+ undo_redo->add_do_method(visual_shader.ptr(), "set_mode", p_which);
+ undo_redo->add_undo_method(visual_shader.ptr(), "set_mode", visual_shader->get_mode());
+ //now undo is hell
+
+ //1. restore connections to output
+ for (int i = 0; i < VisualShader::TYPE_MAX; i++) {
+
+ VisualShader::Type type = VisualShader::Type(i);
+ List<VisualShader::Connection> conns;
+ visual_shader->get_node_connections(type, &conns);
+ for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) {
+ if (E->get().to_node == VisualShader::NODE_ID_OUTPUT) {
+ undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes", type, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port);
+ }
+ }
+ }
+ //2. restore input indices
+ for (int i = 0; i < VisualShader::TYPE_MAX; i++) {
+
+ VisualShader::Type type = VisualShader::Type(i);
+ Vector<int> nodes = visual_shader->get_node_list(type);
+ for (int i = 0; i < nodes.size(); i++) {
+ Ref<VisualShaderNodeInput> input = visual_shader->get_node(type, nodes[i]);
+ if (!input.is_valid()) {
+ continue;
+ }
+
+ undo_redo->add_undo_method(input.ptr(), "set_input_name", input->get_input_name());
+ }
+ }
+
+ //3. restore enums and flags
+ List<PropertyInfo> props;
+ visual_shader->get_property_list(&props);
+
+ for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
+
+ if (E->get().name.begins_with("flags/") || E->get().name.begins_with("modes/")) {
+ undo_redo->add_undo_property(visual_shader.ptr(), E->get().name, visual_shader->get(E->get().name));
+ }
+ }
+
+ //update graph
+ undo_redo->add_do_method(VisualShaderEditor::get_singleton(), "_update_graph");
+ undo_redo->add_undo_method(VisualShaderEditor::get_singleton(), "_update_graph");
+
+ undo_redo->commit_action();
+}
+
+void EditorPropertyShaderMode::update_property() {
+
+ int which = get_edited_object()->get(get_edited_property());
+ options->select(which);
+}
+
+void EditorPropertyShaderMode::setup(const Vector<String> &p_options) {
+ for (int i = 0; i < p_options.size(); i++) {
+ options->add_item(p_options[i], i);
+ }
+}
+
+void EditorPropertyShaderMode::set_option_button_clip(bool p_enable) {
+ options->set_clip_text(p_enable);
+}
+
+void EditorPropertyShaderMode::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_option_selected"), &EditorPropertyShaderMode::_option_selected);
+}
+
+EditorPropertyShaderMode::EditorPropertyShaderMode() {
+ options = memnew(OptionButton);
+ options->set_clip_text(true);
+ add_child(options);
+ add_focusable(options);
+ options->connect("item_selected", this, "_option_selected");
+}
+
+bool EditorInspectorShaderModePlugin::can_handle(Object *p_object) {
+ return true; //can handle everything
+}
+
+void EditorInspectorShaderModePlugin::parse_begin(Object *p_object) {
+ //do none
+}
+
+bool EditorInspectorShaderModePlugin::parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage) {
+
+ if (p_path == "mode" && p_object->is_class("VisualShader") && p_type == Variant::INT) {
+
+ EditorPropertyShaderMode *editor = memnew(EditorPropertyShaderMode);
+ Vector<String> options = p_hint_text.split(",");
+ editor->setup(options);
+ add_property_editor(p_path, editor);
+
+ return true;
+ }
+
+ return false; //can be overriden, although it will most likely be last anyway
+}
+
+void EditorInspectorShaderModePlugin::parse_end() {
+ //do none
+}
+//////////////////////////////////
+
+void VisualShaderNodePortPreview::_shader_changed() {
+ if (shader.is_null()) {
+ return;
+ }
+
+ Vector<VisualShader::DefaultTextureParam> default_textures;
+ String shader_code = shader->generate_preview_shader(type, node, port, default_textures);
+
+ Ref<Shader> preview_shader;
+ preview_shader.instance();
+ preview_shader->set_code(shader_code);
+ for (int i = 0; i < default_textures.size(); i++) {
+ preview_shader->set_default_texture_param(default_textures[i].name, default_textures[i].param);
+ }
+
+ Ref<ShaderMaterial> material;
+ material.instance();
+ material->set_shader(preview_shader);
+
+ //find if a material is also being edited and copy parameters to this one
+
+ for (int i = EditorNode::get_singleton()->get_editor_history()->get_path_size() - 1; i >= 0; i--) {
+ Object *object = ObjectDB::get_instance(EditorNode::get_singleton()->get_editor_history()->get_path_object(i));
+ if (!object)
+ continue;
+ ShaderMaterial *src_mat = Object::cast_to<ShaderMaterial>(object);
+ if (src_mat && src_mat->get_shader().is_valid()) {
+
+ List<PropertyInfo> params;
+ src_mat->get_shader()->get_param_list(&params);
+ for (List<PropertyInfo>::Element *E = params.front(); E; E = E->next()) {
+ material->set(E->get().name, src_mat->get(E->get().name));
+ }
+ }
+ }
+
+ set_material(material);
+}
+
+void VisualShaderNodePortPreview::setup(const Ref<VisualShader> &p_shader, VisualShader::Type p_type, int p_node, int p_port) {
+
+ shader = p_shader;
+ shader->connect("changed", this, "_shader_changed");
+ type = p_type;
+ port = p_port;
+ node = p_node;
+ update();
+ _shader_changed();
+}
+
+Size2 VisualShaderNodePortPreview::get_minimum_size() const {
+ return Size2(100, 100) * EDSCALE;
+}
+
+void VisualShaderNodePortPreview::_notification(int p_what) {
+ if (p_what == NOTIFICATION_DRAW) {
+ Vector<Vector2> points;
+ Vector<Vector2> uvs;
+ Vector<Color> colors;
+ points.push_back(Vector2());
+ uvs.push_back(Vector2(0, 0));
+ colors.push_back(Color(1, 1, 1, 1));
+ points.push_back(Vector2(get_size().width, 0));
+ uvs.push_back(Vector2(1, 0));
+ colors.push_back(Color(1, 1, 1, 1));
+ points.push_back(get_size());
+ uvs.push_back(Vector2(1, 1));
+ colors.push_back(Color(1, 1, 1, 1));
+ points.push_back(Vector2(0, get_size().height));
+ uvs.push_back(Vector2(0, 1));
+ colors.push_back(Color(1, 1, 1, 1));
+
+ draw_primitive(points, colors, uvs);
+ }
+}
+
+void VisualShaderNodePortPreview::_bind_methods() {
+ ClassDB::bind_method("_shader_changed", &VisualShaderNodePortPreview::_shader_changed);
+}
+
+VisualShaderNodePortPreview::VisualShaderNodePortPreview() {
+}
diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h
new file mode 100644
index 0000000000..f86374ff6b
--- /dev/null
+++ b/editor/plugins/visual_shader_editor_plugin.h
@@ -0,0 +1,187 @@
+#ifndef VISUAL_SHADER_EDITOR_PLUGIN_H
+#define VISUAL_SHADER_EDITOR_PLUGIN_H
+
+#include "editor/editor_node.h"
+#include "editor/editor_plugin.h"
+#include "editor/property_editor.h"
+#include "scene/gui/button.h"
+#include "scene/gui/graph_edit.h"
+#include "scene/gui/popup.h"
+#include "scene/gui/tree.h"
+#include "scene/resources/visual_shader.h"
+
+class VisualShaderNodePlugin : public Reference {
+
+ GDCLASS(VisualShaderNodePlugin, Reference)
+protected:
+ static void _bind_methods();
+
+public:
+ virtual Control *create_editor(const Ref<VisualShaderNode> &p_node);
+};
+
+class VisualShaderEditor : public VBoxContainer {
+
+ GDCLASS(VisualShaderEditor, VBoxContainer);
+
+ CustomPropertyEditor *property_editor;
+ int editing_node;
+ int editing_port;
+
+ Ref<VisualShader> visual_shader;
+ GraphEdit *graph;
+ MenuButton *add_node;
+
+ OptionButton *edit_type;
+
+ PanelContainer *error_panel;
+ Label *error_label;
+
+ UndoRedo *undo_redo;
+
+ void _update_graph();
+
+ struct AddOption {
+ String name;
+ String category;
+ String type;
+ Ref<Script> script;
+ AddOption(const String &p_name = String(), const String &p_category = String(), const String &p_type = String()) {
+ name = p_name;
+ type = p_type;
+ category = p_category;
+ }
+ };
+
+ Vector<AddOption> add_options;
+
+ void _draw_color_over_button(Object *obj, Color p_color);
+
+ void _add_node(int p_idx);
+ void _update_options_menu();
+
+ static VisualShaderEditor *singleton;
+
+ void _node_dragged(const Vector2 &p_from, const Vector2 &p_to, int p_node);
+ bool updating;
+
+ void _connection_request(const String &p_from, int p_from_index, const String &p_to, int p_to_index);
+ void _disconnection_request(const String &p_from, int p_from_index, const String &p_to, int p_to_index);
+
+ void _scroll_changed(const Vector2 &p_scroll);
+ void _node_selected(Object *p_node);
+
+ void _delete_request(int);
+
+ void _removed_from_graph();
+
+ void _node_changed(int p_id);
+
+ void _edit_port_default_input(Object *p_button, int p_node, int p_port);
+ void _port_edited();
+
+ void _connection_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_position);
+
+ void _line_edit_changed(const String &p_text, Object *line_edit, int p_node_id);
+ void _line_edit_focus_out(Object *line_edit, int p_node_id);
+
+ void _duplicate_nodes();
+
+ Vector<Ref<VisualShaderNodePlugin> > plugins;
+
+ void _mode_selected(int p_id);
+
+ void _input_select_item(Ref<VisualShaderNodeInput> input, String name);
+
+ void _preview_select_port(int p_node, int p_port);
+ void _input(const Ref<InputEvent> p_event);
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ void add_plugin(const Ref<VisualShaderNodePlugin> &p_plugin);
+ void remove_plugin(const Ref<VisualShaderNodePlugin> &p_plugin);
+
+ static VisualShaderEditor *get_singleton() { return singleton; }
+
+ void add_custom_type(const String &p_name, const String &p_category, const Ref<Script> &p_script);
+ void remove_custom_type(const Ref<Script> &p_script);
+
+ virtual Size2 get_minimum_size() const;
+ void edit(VisualShader *p_visual_shader);
+ VisualShaderEditor();
+};
+
+class VisualShaderEditorPlugin : public EditorPlugin {
+
+ GDCLASS(VisualShaderEditorPlugin, EditorPlugin);
+
+ VisualShaderEditor *visual_shader_editor;
+ EditorNode *editor;
+ Button *button;
+
+public:
+ virtual String get_name() const { return "VisualShader"; }
+ bool has_main_screen() const { return false; }
+ virtual void edit(Object *p_object);
+ virtual bool handles(Object *p_object) const;
+ virtual void make_visible(bool p_visible);
+
+ VisualShaderEditorPlugin(EditorNode *p_node);
+ ~VisualShaderEditorPlugin();
+};
+
+class VisualShaderNodePluginDefault : public VisualShaderNodePlugin {
+
+ GDCLASS(VisualShaderNodePluginDefault, VisualShaderNodePlugin)
+
+public:
+ virtual Control *create_editor(const Ref<VisualShaderNode> &p_node);
+};
+
+class EditorPropertyShaderMode : public EditorProperty {
+ GDCLASS(EditorPropertyShaderMode, EditorProperty)
+ OptionButton *options;
+
+ void _option_selected(int p_which);
+
+protected:
+ static void _bind_methods();
+
+public:
+ void setup(const Vector<String> &p_options);
+ virtual void update_property();
+ void set_option_button_clip(bool p_enable);
+ EditorPropertyShaderMode();
+};
+
+class EditorInspectorShaderModePlugin : public EditorInspectorPlugin {
+ GDCLASS(EditorInspectorShaderModePlugin, EditorInspectorPlugin)
+
+public:
+ virtual bool can_handle(Object *p_object);
+ virtual void parse_begin(Object *p_object);
+ virtual bool parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage);
+ virtual void parse_end();
+};
+
+class VisualShaderNodePortPreview : public Control {
+ GDCLASS(VisualShaderNodePortPreview, Control)
+ Ref<VisualShader> shader;
+ VisualShader::Type type;
+ int node;
+ int port;
+ void _shader_changed(); //must regen
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ virtual Size2 get_minimum_size() const;
+ void setup(const Ref<VisualShader> &p_shader, VisualShader::Type p_type, int p_node, int p_port);
+ VisualShaderNodePortPreview();
+};
+
+#endif // VISUAL_SHADER_EDITOR_PLUGIN_H
diff --git a/editor/project_export.cpp b/editor/project_export.cpp
index 9f87fc82b5..170546f14c 100644
--- a/editor/project_export.cpp
+++ b/editor/project_export.cpp
@@ -76,6 +76,9 @@ void ProjectExportDialog::popup_export() {
}
_update_presets();
+ if (presets->get_current() >= 0) {
+ _edit_preset(presets->get_current()); // triggers rescan for templates if newly installed
+ }
// Restore valid window bounds or pop up at default size.
if (EditorSettings::get_singleton()->has_setting("interface/dialogs/export_bounds")) {
@@ -154,7 +157,6 @@ void ProjectExportDialog::_update_presets() {
if (current_idx != -1) {
presets->select(current_idx);
- //_edit_preset(current_idx);
}
updating = false;
@@ -167,6 +169,7 @@ void ProjectExportDialog::_edit_preset(int p_index) {
name->set_editable(false);
runnable->set_disabled(true);
parameters->edit(NULL);
+ presets->unselect_all();
delete_preset->set_disabled(true);
sections->hide();
patches->clear();
@@ -438,11 +441,9 @@ void ProjectExportDialog::_delete_preset() {
void ProjectExportDialog::_delete_preset_confirm() {
int idx = presets->get_current();
- parameters->edit(NULL); //to avoid crash
_edit_preset(-1);
EditorExport::get_singleton()->remove_export_preset(idx);
_update_presets();
- _edit_preset(presets->get_current());
}
Variant ProjectExportDialog::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp
index 8c7565a441..e6ae2d64e7 100644
--- a/editor/project_settings_editor.cpp
+++ b/editor/project_settings_editor.cpp
@@ -106,6 +106,12 @@ void ProjectSettingsEditor::_notification(int p_what) {
translation_res_file_open->add_filter("*." + E->get());
translation_res_option_file_open->add_filter("*." + E->get());
}
+
+ restart_close_button->set_icon(get_icon("Close", "EditorIcons"));
+ restart_container->add_style_override("panel", get_stylebox("bg", "Tree"));
+ restart_icon->set_texture(get_icon("StatusWarning", "EditorIcons"));
+ restart_label->add_color_override("font_color", get_color("error_color", "Editor"));
+
} break;
case NOTIFICATION_POPUP_HIDE: {
EditorSettings::get_singleton()->set("interface/dialogs/project_settings_bounds", get_rect());
@@ -394,6 +400,7 @@ void ProjectSettingsEditor::_show_last_added(const Ref<InputEvent> &p_event, con
while (child) {
Variant input = child->get_meta("__input");
if (p_event == input) {
+ r->set_collapsed(false);
child->select(0);
found = true;
break;
@@ -654,6 +661,14 @@ void ProjectSettingsEditor::_update_actions() {
if (setting)
return;
+ Map<String, bool> collapsed;
+
+ if (input_editor->get_root() && input_editor->get_root()->get_children()) {
+ for (TreeItem *item = input_editor->get_root()->get_children(); item; item = item->get_next()) {
+ collapsed[item->get_text(0)] = item->is_collapsed();
+ }
+ }
+
input_editor->clear();
TreeItem *root = input_editor->create_item();
input_editor->set_hide_root(true);
@@ -677,6 +692,8 @@ void ProjectSettingsEditor::_update_actions() {
TreeItem *item = input_editor->create_item(root);
item->set_text(0, name);
item->set_custom_bg_color(0, get_color("prop_subsection", "Editor"));
+ if (collapsed.has(name))
+ item->set_collapsed(collapsed[name]);
item->set_editable(1, true);
item->set_cell_mode(1, TreeItem::CELL_MODE_RANGE);
@@ -789,15 +806,13 @@ void ProjectSettingsEditor::popup_project_settings() {
plugin_settings->update_plugins();
}
-void ProjectSettingsEditor::_item_selected() {
+void ProjectSettingsEditor::_item_selected(const String &p_path) {
- TreeItem *ti = globals_editor->get_property_editor()->get_property_tree()->get_selected();
- if (!ti)
- return;
- if (!ti->get_parent())
+ String selected_path = p_path;
+ if (selected_path == String())
return;
category->set_text(globals_editor->get_current_section());
- property->set_text(ti->get_text(0));
+ property->set_text(selected_path);
popup_copy_to_feature->set_disabled(false);
}
@@ -854,7 +869,7 @@ void ProjectSettingsEditor::_item_add() {
void ProjectSettingsEditor::_item_del() {
- String path = globals_editor->get_property_editor()->get_selected_path();
+ String path = globals_editor->get_inspector()->get_selected_path();
if (path == String()) {
EditorNode::get_singleton()->show_warning(TTR("Select a setting item first!"));
return;
@@ -1032,7 +1047,7 @@ void ProjectSettingsEditor::_copy_to_platform_about_to_show() {
void ProjectSettingsEditor::_copy_to_platform(int p_which) {
- String path = globals_editor->get_property_editor()->get_selected_path();
+ String path = globals_editor->get_inspector()->get_selected_path();
if (path == String()) {
EditorNode::get_singleton()->show_warning(TTR("Select a setting item first!"));
return;
@@ -1561,7 +1576,7 @@ void ProjectSettingsEditor::_update_translations() {
void ProjectSettingsEditor::_toggle_search_bar(bool p_pressed) {
- globals_editor->get_property_editor()->set_use_filter(p_pressed);
+ globals_editor->get_inspector()->set_use_filter(p_pressed);
if (p_pressed) {
@@ -1582,7 +1597,7 @@ void ProjectSettingsEditor::_clear_search_box() {
return;
search_box->clear();
- globals_editor->get_property_editor()->update_tree();
+ globals_editor->get_inspector()->update_tree();
}
void ProjectSettingsEditor::set_plugins_page() {
@@ -1595,6 +1610,18 @@ TabContainer *ProjectSettingsEditor::get_tabs() {
return tab_container;
}
+void ProjectSettingsEditor::_editor_restart() {
+ EditorNode::get_singleton()->save_all_scenes_and_restart();
+}
+
+void ProjectSettingsEditor::_editor_restart_request() {
+ restart_container->show();
+}
+
+void ProjectSettingsEditor::_editor_restart_close() {
+ restart_container->hide();
+}
+
void ProjectSettingsEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_item_selected"), &ProjectSettingsEditor::_item_selected);
@@ -1640,6 +1667,10 @@ void ProjectSettingsEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_copy_to_platform_about_to_show"), &ProjectSettingsEditor::_copy_to_platform_about_to_show);
+ ClassDB::bind_method(D_METHOD("_editor_restart_request"), &ProjectSettingsEditor::_editor_restart_request);
+ ClassDB::bind_method(D_METHOD("_editor_restart"), &ProjectSettingsEditor::_editor_restart);
+ ClassDB::bind_method(D_METHOD("_editor_restart_close"), &ProjectSettingsEditor::_editor_restart_close);
+
ClassDB::bind_method(D_METHOD("get_tabs"), &ProjectSettingsEditor::get_tabs);
}
@@ -1726,16 +1757,17 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
search_bar->add_child(clear_button);
clear_button->connect("pressed", this, "_clear_search_box");
- globals_editor = memnew(SectionedPropertyEditor);
+ globals_editor = memnew(SectionedInspector);
props_base->add_child(globals_editor);
- globals_editor->get_property_editor()->set_undo_redo(EditorNode::get_singleton()->get_undo_redo());
- globals_editor->get_property_editor()->set_property_selectable(true);
+ globals_editor->get_inspector()->set_undo_redo(EditorNode::get_singleton()->get_undo_redo());
+ globals_editor->get_inspector()->set_property_selectable(true);
//globals_editor->hide_top_label();
globals_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
globals_editor->register_search_box(search_box);
- globals_editor->get_property_editor()->get_property_tree()->connect("cell_selected", this, "_item_selected");
- globals_editor->get_property_editor()->connect("property_toggled", this, "_item_checked", varray(), CONNECT_DEFERRED);
- globals_editor->get_property_editor()->connect("property_edited", this, "_settings_prop_edited");
+ globals_editor->get_inspector()->connect("property_selected", this, "_item_selected");
+ //globals_editor->get_inspector()->connect("property_toggled", this, "_item_checked", varray(), CONNECT_DEFERRED);
+ globals_editor->get_inspector()->connect("property_edited", this, "_settings_prop_edited");
+ globals_editor->get_inspector()->connect("restart_requested", this, "_editor_restart_request");
Button *del = memnew(Button);
hbc->add_child(del);
@@ -1755,6 +1787,26 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
get_ok()->set_text(TTR("Close"));
set_hide_on_ok(true);
+ restart_container = memnew(PanelContainer);
+ props_base->add_child(restart_container);
+ HBoxContainer *restart_hb = memnew(HBoxContainer);
+ restart_container->add_child(restart_hb);
+ restart_icon = memnew(TextureRect);
+ restart_icon->set_v_size_flags(SIZE_SHRINK_CENTER);
+ restart_hb->add_child(restart_icon);
+ restart_label = memnew(Label);
+ restart_label->set_text(TTR("Editor must be restarted for changes to take effect"));
+ restart_hb->add_child(restart_label);
+ restart_hb->add_spacer();
+ Button *restart_button = memnew(Button);
+ restart_button->connect("pressed", this, "_editor_restart");
+ restart_hb->add_child(restart_button);
+ restart_button->set_text(TTR("Save & Restart"));
+ restart_close_button = memnew(ToolButton);
+ restart_close_button->connect("pressed", this, "_editor_restart_close");
+ restart_hb->add_child(restart_close_button);
+ restart_container->hide();
+
message = memnew(AcceptDialog);
add_child(message);
diff --git a/editor/project_settings_editor.h b/editor/project_settings_editor.h
index 0ced88d7f6..3b74ae1909 100644
--- a/editor/project_settings_editor.h
+++ b/editor/project_settings_editor.h
@@ -35,7 +35,7 @@
#include "editor/editor_autoload_settings.h"
#include "editor/editor_data.h"
#include "editor/editor_plugin_settings.h"
-#include "editor/property_editor.h"
+#include "editor/editor_sectioned_inspector.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/tab_container.h"
@@ -64,7 +64,7 @@ class ProjectSettingsEditor : public AcceptDialog {
EditorData *data;
UndoRedo *undo_redo;
- SectionedPropertyEditor *globals_editor;
+ SectionedInspector *globals_editor;
HBoxContainer *search_bar;
Button *search_button;
@@ -112,7 +112,7 @@ class ProjectSettingsEditor : public AcceptDialog {
EditorPluginSettings *plugin_settings;
- void _item_selected();
+ void _item_selected(const String &);
void _item_adds(String);
void _item_add();
void _item_del();
@@ -166,6 +166,15 @@ class ProjectSettingsEditor : public AcceptDialog {
static ProjectSettingsEditor *singleton;
+ Label *restart_label;
+ TextureRect *restart_icon;
+ PanelContainer *restart_container;
+ ToolButton *restart_close_button;
+
+ void _editor_restart_request();
+ void _editor_restart();
+ void _editor_restart_close();
+
protected:
void _notification(int p_what);
static void _bind_methods();
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 77ee65879b..2ffaa0ca12 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -551,6 +551,32 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
reparent_dialog->set_current(nodeset);
} break;
+ case TOOL_MAKE_ROOT: {
+
+ List<Node *> nodes = editor_selection->get_selected_node_list();
+ ERR_FAIL_COND(nodes.size() != 1);
+
+ Node *node = nodes.front()->get();
+ Node *root = get_tree()->get_edited_scene_root();
+
+ if (node == root)
+ return;
+
+ editor_data->get_undo_redo().create_action("Make node as Root");
+ _node_replace_owner(root, node, node, MODE_DO);
+ editor_data->get_undo_redo().add_do_method(node->get_parent(), "remove_child", node);
+ editor_data->get_undo_redo().add_do_method(editor, "set_edited_scene", node);
+ editor_data->get_undo_redo().add_do_method(node, "set_filename", root->get_filename());
+
+ editor_data->get_undo_redo().add_undo_method(node, "set_filename", String());
+ editor_data->get_undo_redo().add_undo_method(editor, "set_edited_scene", root);
+ editor_data->get_undo_redo().add_undo_method(node->get_parent(), "add_child", node);
+ _node_replace_owner(root, node, root, MODE_UNDO);
+ editor_data->get_undo_redo().add_do_method(scene_tree, "update_tree");
+ editor_data->get_undo_redo().add_undo_method(scene_tree, "update_tree");
+ editor_data->get_undo_redo().add_undo_reference(root);
+ editor_data->get_undo_redo().commit_action();
+ } break;
case TOOL_MULTI_EDIT: {
Node *root = EditorNode::get_singleton()->get_edited_scene();
@@ -757,6 +783,26 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
}
}
} break;
+ case TOOL_CREATE_2D_SCENE:
+ case TOOL_CREATE_3D_SCENE:
+ case TOOL_CREATE_USER_INTERFACE: {
+
+ Node *new_node;
+ switch (p_tool) {
+ case TOOL_CREATE_2D_SCENE: new_node = memnew(Node2D); break;
+ case TOOL_CREATE_3D_SCENE: new_node = memnew(Spatial); break;
+ case TOOL_CREATE_USER_INTERFACE: new_node = memnew(Control); break;
+ }
+
+ editor_data->get_undo_redo().create_action("New Scene Root");
+ editor_data->get_undo_redo().add_do_method(editor, "set_edited_scene", new_node);
+ editor_data->get_undo_redo().add_do_method(scene_tree, "update_tree");
+ editor_data->get_undo_redo().add_do_reference(new_node);
+ editor_data->get_undo_redo().add_undo_method(editor, "set_edited_scene", (Object *)NULL);
+ editor_data->get_undo_redo().commit_action();
+
+ } break;
+
default: {
if (p_tool >= EDIT_SUBRESOURCE_BASE) {
@@ -803,6 +849,35 @@ void SceneTreeDock::_notification(int p_what) {
EditorNode::get_singleton()->get_editor_selection()->connect("selection_changed", this, "_selection_changed");
+ create_root_dialog->add_child(memnew(Label(TTR("Create Root Node:"))));
+
+ Button *button_2d = memnew(Button);
+ create_root_dialog->add_child(button_2d);
+
+ button_2d->set_text(TTR("2D Scene"));
+ button_2d->set_icon(get_icon("Node2D", "EditorIcons"));
+ button_2d->connect("pressed", this, "_tool_selected", make_binds(TOOL_CREATE_2D_SCENE, false));
+
+ Button *button_3d = memnew(Button);
+ create_root_dialog->add_child(button_3d);
+ button_3d->set_text(TTR("3D Scene"));
+ button_3d->set_icon(get_icon("Spatial", "EditorIcons"));
+ button_3d->connect("pressed", this, "_tool_selected", make_binds(TOOL_CREATE_3D_SCENE, false));
+
+ Button *button_ui = memnew(Button);
+ create_root_dialog->add_child(button_ui);
+ button_ui->set_text(TTR("User Interface"));
+ button_ui->set_icon(get_icon("Control", "EditorIcons"));
+ button_ui->connect("pressed", this, "_tool_selected", make_binds(TOOL_CREATE_USER_INTERFACE, false));
+
+ Button *button_custom = memnew(Button);
+ create_root_dialog->add_child(button_custom);
+ button_custom->set_text(TTR("Custom Node"));
+ button_custom->set_icon(get_icon("Add", "EditorIcons"));
+ button_custom->connect("pressed", this, "_tool_selected", make_binds(TOOL_NEW, false));
+
+ create_root_dialog->add_spacer();
+
} break;
case NOTIFICATION_ENTER_TREE: {
@@ -820,21 +895,49 @@ void SceneTreeDock::_notification(int p_what) {
filter->add_icon_override("right_icon", get_icon("Search", "EditorIcons"));
} break;
+ case NOTIFICATION_PROCESS: {
+
+ bool show_create_root = bool(EDITOR_GET("interface/editors/show_scene_tree_root_selection")) && get_tree()->get_edited_scene_root() == NULL;
+
+ if (show_create_root != create_root_dialog->is_visible_in_tree()) {
+ if (show_create_root) {
+ create_root_dialog->show();
+ scene_tree->hide();
+ } else {
+ create_root_dialog->hide();
+ scene_tree->show();
+ }
+ }
+
+ } break;
}
}
-void SceneTreeDock::_node_replace_owner(Node *p_base, Node *p_node, Node *p_root) {
+void SceneTreeDock::_node_replace_owner(Node *p_base, Node *p_node, Node *p_root, ReplaceOwnerMode p_mode) {
if (p_base != p_node) {
if (p_node->get_owner() == p_base) {
UndoRedo *undo_redo = &editor_data->get_undo_redo();
- undo_redo->add_do_method(p_node, "set_owner", p_root);
- undo_redo->add_undo_method(p_node, "set_owner", p_base);
+ switch (p_mode) {
+ case MODE_BIDI: {
+ undo_redo->add_do_method(p_node, "set_owner", p_root);
+ undo_redo->add_undo_method(p_node, "set_owner", p_base);
+
+ } break;
+ case MODE_DO: {
+ undo_redo->add_do_method(p_node, "set_owner", p_root);
+
+ } break;
+ case MODE_UNDO: {
+ undo_redo->add_undo_method(p_node, "set_owner", p_root);
+
+ } break;
+ }
}
}
for (int i = 0; i < p_node->get_child_count(); i++) {
- _node_replace_owner(p_base, p_node->get_child(i), p_root);
+ _node_replace_owner(p_base, p_node->get_child(i), p_root, p_mode);
}
}
@@ -1904,6 +2007,8 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
menu->add_icon_shortcut(get_icon("Reparent", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/reparent"), TOOL_REPARENT);
if (selection.size() == 1) {
+
+ menu->add_icon_shortcut(get_icon("NewRoot", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/make_root"), TOOL_MAKE_ROOT);
menu->add_separator();
menu->add_icon_shortcut(get_icon("Blend", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/merge_from_scene"), TOOL_MERGE_FROM_SCENE);
menu->add_icon_shortcut(get_icon("CreateNewSceneFrom", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/save_branch_as_scene"), TOOL_NEW_SCENE_FROM);
@@ -2090,6 +2195,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel
ED_SHORTCUT("scene_tree/move_down", TTR("Move Down"), KEY_MASK_CMD | KEY_DOWN);
ED_SHORTCUT("scene_tree/duplicate", TTR("Duplicate"), KEY_MASK_CMD | KEY_D);
ED_SHORTCUT("scene_tree/reparent", TTR("Reparent"));
+ ED_SHORTCUT("scene_tree/make_root", TTR("Make Scene Root"));
ED_SHORTCUT("scene_tree/merge_from_scene", TTR("Merge From Scene"));
ED_SHORTCUT("scene_tree/save_branch_as_scene", TTR("Save Branch as Scene"));
ED_SHORTCUT("scene_tree/copy_node_path", TTR("Copy Node Path"), KEY_MASK_CMD | KEY_C);
@@ -2153,6 +2259,10 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel
remote_tree = NULL;
button_hb->hide();
+ create_root_dialog = memnew(VBoxContainer);
+ vbc->add_child(create_root_dialog);
+ create_root_dialog->hide();
+
scene_tree = memnew(SceneTreeEditor(false, true, true));
vbc->add_child(scene_tree);
@@ -2227,4 +2337,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel
add_child(clear_inherit_confirm);
set_process_input(true);
+ set_process(true);
+
+ EDITOR_DEF("interface/editors/show_scene_tree_root_selection", true);
}
diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h
index 17deab25de..fd74611bde 100644
--- a/editor/scene_tree_dock.h
+++ b/editor/scene_tree_dock.h
@@ -68,6 +68,7 @@ class SceneTreeDock : public VBoxContainer {
TOOL_MOVE_DOWN,
TOOL_DUPLICATE,
TOOL_REPARENT,
+ TOOL_MAKE_ROOT,
TOOL_NEW_SCENE_FROM,
TOOL_MERGE_FROM_SCENE,
TOOL_MULTI_EDIT,
@@ -80,7 +81,12 @@ class SceneTreeDock : public VBoxContainer {
TOOL_SCENE_OPEN,
TOOL_SCENE_CLEAR_INHERITANCE,
TOOL_SCENE_CLEAR_INHERITANCE_CONFIRM,
- TOOL_SCENE_OPEN_INHERITED
+ TOOL_SCENE_OPEN_INHERITED,
+
+ TOOL_CREATE_2D_SCENE,
+ TOOL_CREATE_3D_SCENE,
+ TOOL_CREATE_USER_INTERFACE,
+
};
enum {
@@ -134,13 +140,22 @@ class SceneTreeDock : public VBoxContainer {
Node *edited_scene;
EditorNode *editor;
+ VBoxContainer *create_root_dialog;
+
void _add_children_to_popup(Object *p_obj, int p_depth);
void _node_reparent(NodePath p_path, bool p_keep_global_xform);
void _do_reparent(Node *p_new_parent, int p_position_in_parent, Vector<Node *> p_nodes, bool p_keep_global_xform);
void _set_owners(Node *p_owner, const Array &p_nodes);
- void _node_replace_owner(Node *p_base, Node *p_node, Node *p_root);
+
+ enum ReplaceOwnerMode {
+ MODE_BIDI,
+ MODE_DO,
+ MODE_UNDO
+ };
+
+ void _node_replace_owner(Node *p_base, Node *p_node, Node *p_root, ReplaceOwnerMode p_mode = MODE_BIDI);
void _load_request(const String &p_path);
void _script_open_request(const Ref<Script> &p_script);
diff --git a/editor/settings_config_dialog.cpp b/editor/settings_config_dialog.cpp
index ae88b3a035..fe379703e5 100644
--- a/editor/settings_config_dialog.cpp
+++ b/editor/settings_config_dialog.cpp
@@ -54,12 +54,12 @@ void EditorSettingsDialog::_settings_changed() {
void EditorSettingsDialog::_settings_property_edited(const String &p_name) {
- String full_name = property_editor->get_full_item_path(p_name);
+ String full_name = inspector->get_full_item_path(p_name);
// Small usability workaround to update the text color settings when the
// color theme is changed
if (full_name == "text_editor/theme/color_theme") {
- property_editor->get_property_editor()->update_tree();
+ inspector->get_inspector()->update_tree();
} else if (full_name == "interface/theme/accent_color" || full_name == "interface/theme/base_color" || full_name == "interface/theme/contrast") {
EditorSettings::get_singleton()->set_manually("interface/theme/preset", "Custom"); // set preset to Custom
} else if (full_name.begins_with("text_editor/highlighting")) {
@@ -88,8 +88,8 @@ void EditorSettingsDialog::popup_edit_settings() {
EditorSettings::get_singleton()->list_text_editor_themes(); // make sure we have an up to date list of themes
- property_editor->edit(EditorSettings::get_singleton());
- property_editor->get_property_editor()->update_tree();
+ inspector->edit(EditorSettings::get_singleton());
+ inspector->get_inspector()->update_tree();
search_box->select_all();
search_box->grab_focus();
@@ -120,7 +120,7 @@ void EditorSettingsDialog::_clear_search_box() {
return;
search_box->clear();
- property_editor->get_property_editor()->update_tree();
+ inspector->get_inspector()->update_tree();
}
void EditorSettingsDialog::_clear_shortcut_search_box() {
@@ -158,7 +158,7 @@ void EditorSettingsDialog::_notification(int p_what) {
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
_update_icons();
// Update theme colors.
- property_editor->update_category_list();
+ inspector->update_category_list();
_update_shortcuts();
} break;
}
@@ -202,6 +202,11 @@ void EditorSettingsDialog::_update_icons() {
shortcut_search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons"));
clear_button->set_icon(get_icon("Close", "EditorIcons"));
shortcut_clear_button->set_icon(get_icon("Close", "EditorIcons"));
+
+ restart_close_button->set_icon(get_icon("Close", "EditorIcons"));
+ restart_container->add_style_override("panel", get_stylebox("bg", "Tree"));
+ restart_icon->set_texture(get_icon("StatusWarning", "EditorIcons"));
+ restart_label->add_color_override("font_color", get_color("error_color", "Editor"));
}
void EditorSettingsDialog::_update_shortcuts() {
@@ -388,6 +393,18 @@ void EditorSettingsDialog::_focus_current_search_box() {
}
}
+void EditorSettingsDialog::_editor_restart() {
+ EditorNode::get_singleton()->save_all_scenes_and_restart();
+}
+
+void EditorSettingsDialog::_editor_restart_request() {
+ restart_container->show();
+}
+
+void EditorSettingsDialog::_editor_restart_close() {
+ restart_container->hide();
+}
+
void EditorSettingsDialog::_bind_methods() {
ClassDB::bind_method(D_METHOD("_unhandled_input"), &EditorSettingsDialog::_unhandled_input);
@@ -402,6 +419,10 @@ void EditorSettingsDialog::_bind_methods() {
ClassDB::bind_method(D_METHOD("_press_a_key_confirm"), &EditorSettingsDialog::_press_a_key_confirm);
ClassDB::bind_method(D_METHOD("_wait_for_key"), &EditorSettingsDialog::_wait_for_key);
ClassDB::bind_method(D_METHOD("_tabs_tab_changed"), &EditorSettingsDialog::_tabs_tab_changed);
+
+ ClassDB::bind_method(D_METHOD("_editor_restart_request"), &EditorSettingsDialog::_editor_restart_request);
+ ClassDB::bind_method(D_METHOD("_editor_restart"), &EditorSettingsDialog::_editor_restart);
+ ClassDB::bind_method(D_METHOD("_editor_restart_close"), &EditorSettingsDialog::_editor_restart_close);
}
EditorSettingsDialog::EditorSettingsDialog() {
@@ -434,14 +455,35 @@ EditorSettingsDialog::EditorSettingsDialog() {
hbc->add_child(clear_button);
clear_button->connect("pressed", this, "_clear_search_box");
- property_editor = memnew(SectionedPropertyEditor);
- //property_editor->hide_top_label();
- property_editor->get_property_editor()->set_use_filter(true);
- property_editor->register_search_box(search_box);
- property_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- property_editor->get_property_editor()->set_undo_redo(undo_redo);
- tab_general->add_child(property_editor);
- property_editor->get_property_editor()->connect("property_edited", this, "_settings_property_edited");
+ inspector = memnew(SectionedInspector);
+ //inspector->hide_top_label();
+ inspector->get_inspector()->set_use_filter(true);
+ inspector->register_search_box(search_box);
+ inspector->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ inspector->get_inspector()->set_undo_redo(undo_redo);
+ tab_general->add_child(inspector);
+ inspector->get_inspector()->connect("property_edited", this, "_settings_property_edited");
+ inspector->get_inspector()->connect("restart_requested", this, "_editor_restart_request");
+
+ restart_container = memnew(PanelContainer);
+ tab_general->add_child(restart_container);
+ HBoxContainer *restart_hb = memnew(HBoxContainer);
+ restart_container->add_child(restart_hb);
+ restart_icon = memnew(TextureRect);
+ restart_icon->set_v_size_flags(SIZE_SHRINK_CENTER);
+ restart_hb->add_child(restart_icon);
+ restart_label = memnew(Label);
+ restart_label->set_text(TTR("Editor must be restarted for changes to take effect"));
+ restart_hb->add_child(restart_label);
+ restart_hb->add_spacer();
+ Button *restart_button = memnew(Button);
+ restart_button->connect("pressed", this, "_editor_restart");
+ restart_hb->add_child(restart_button);
+ restart_button->set_text(TTR("Save & Restart"));
+ restart_close_button = memnew(ToolButton);
+ restart_close_button->connect("pressed", this, "_editor_restart_close");
+ restart_hb->add_child(restart_close_button);
+ restart_container->hide();
// Shortcuts Tab
diff --git a/editor/settings_config_dialog.h b/editor/settings_config_dialog.h
index 6676e870d0..6cf2eb6bdf 100644
--- a/editor/settings_config_dialog.h
+++ b/editor/settings_config_dialog.h
@@ -31,9 +31,14 @@
#ifndef SETTINGS_CONFIG_DIALOG_H
#define SETTINGS_CONFIG_DIALOG_H
-#include "property_editor.h"
+#include "editor/editor_sectioned_inspector.h"
+#include "editor_inspector.h"
+#include "scene/gui/dialogs.h"
+#include "scene/gui/panel_container.h"
#include "scene/gui/rich_text_label.h"
#include "scene/gui/tab_container.h"
+#include "scene/gui/texture_rect.h"
+#include "scene/gui/tool_button.h"
class EditorSettingsDialog : public AcceptDialog {
@@ -49,7 +54,7 @@ class EditorSettingsDialog : public AcceptDialog {
LineEdit *shortcut_search_box;
ToolButton *clear_button;
ToolButton *shortcut_clear_button;
- SectionedPropertyEditor *property_editor;
+ SectionedInspector *inspector;
Timer *timer;
@@ -89,6 +94,15 @@ class EditorSettingsDialog : public AcceptDialog {
static void _undo_redo_callback(void *p_self, const String &p_name);
+ Label *restart_label;
+ TextureRect *restart_icon;
+ PanelContainer *restart_container;
+ ToolButton *restart_close_button;
+
+ void _editor_restart_request();
+ void _editor_restart();
+ void _editor_restart_close();
+
protected:
static void _bind_methods();
diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp
index 3b0ac8864a..c45dea0df7 100644
--- a/editor/spatial_editor_gizmos.cpp
+++ b/editor/spatial_editor_gizmos.cpp
@@ -3272,10 +3272,10 @@ NavigationMeshSpatialGizmo::NavigationMeshSpatialGizmo(NavigationMeshInstance *p
navmesh = p_navmesh;
}
- //////
- ///
- ///
- ///
+//////
+///
+///
+///
#define BODY_A_RADIUS 0.25
#define BODY_B_RADIUS 0.27