diff options
Diffstat (limited to 'editor/plugins')
24 files changed, 2207 insertions, 377 deletions
diff --git a/editor/plugins/collision_polygon_3d_editor_plugin.cpp b/editor/plugins/collision_polygon_3d_editor_plugin.cpp index 6e71133c4f..a0df7e289e 100644 --- a/editor/plugins/collision_polygon_3d_editor_plugin.cpp +++ b/editor/plugins/collision_polygon_3d_editor_plugin.cpp @@ -32,8 +32,8 @@ #include "canvas_item_editor_plugin.h" #include "core/input/input.h" +#include "core/io/file_access.h" #include "core/math/geometry_2d.h" -#include "core/os/file_access.h" #include "core/os/keyboard.h" #include "editor/editor_settings.h" #include "node_3d_editor_plugin.h" diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index cf237f5a4a..235ccb18cb 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -832,7 +832,7 @@ struct FSample { }; static FSample _samples[] = { - { "hani", U"漢語" }, + { "hani", U"漢字" }, { "armn", U"Աբ" }, { "copt", U"Αα" }, { "cyrl", U"Аб" }, diff --git a/editor/plugins/font_editor_plugin.cpp b/editor/plugins/font_editor_plugin.cpp index fa58eb5480..8de7dc2447 100644 --- a/editor/plugins/font_editor_plugin.cpp +++ b/editor/plugins/font_editor_plugin.cpp @@ -56,7 +56,7 @@ struct FSample { }; static FSample _samples[] = { - { "hani", U"漢語" }, + { "hani", U"漢字" }, { "armn", U"Աբ" }, { "copt", U"Αα" }, { "cyrl", U"Аб" }, diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 6ab15f763f..46c35291ac 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -3551,8 +3551,8 @@ Dictionary Node3DEditorViewport::get_state() const { void Node3DEditorViewport::_bind_methods() { ClassDB::bind_method(D_METHOD("update_transform_gizmo_view"), &Node3DEditorViewport::update_transform_gizmo_view); // Used by call_deferred. - ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &Node3DEditorViewport::can_drop_data_fw); - ClassDB::bind_method(D_METHOD("drop_data_fw"), &Node3DEditorViewport::drop_data_fw); + ClassDB::bind_method(D_METHOD("_can_drop_data_fw"), &Node3DEditorViewport::can_drop_data_fw); + ClassDB::bind_method(D_METHOD("_drop_data_fw"), &Node3DEditorViewport::drop_data_fw); ADD_SIGNAL(MethodInfo("toggle_maximize_view", PropertyInfo(Variant::OBJECT, "viewport"))); ADD_SIGNAL(MethodInfo("clicked", PropertyInfo(Variant::OBJECT, "viewport"))); @@ -5056,11 +5056,11 @@ void Node3DEditor::_update_camera_override_button(bool p_game_running) { if (p_game_running) { button->set_disabled(false); - button->set_tooltip(TTR("Game Camera Override\nNo game instance running.")); + button->set_tooltip(TTR("Project Camera Override\nOverrides the running project's camera with the editor viewport camera.")); } else { button->set_disabled(true); button->set_pressed(false); - button->set_tooltip(TTR("Game Camera Override\nOverrides game camera with editor viewport camera.")); + button->set_tooltip(TTR("Project Camera Override\nNo project instance running. Run the project from the editor to use this feature.")); } } @@ -7475,15 +7475,15 @@ Ref<StandardMaterial3D> EditorNode3DGizmoPlugin::get_material(const String &p_na } String EditorNode3DGizmoPlugin::get_gizmo_name() const { - if (get_script_instance() && get_script_instance()->has_method("get_gizmo_name")) { - return get_script_instance()->call("get_gizmo_name"); + if (get_script_instance() && get_script_instance()->has_method("_get_gizmo_name")) { + return get_script_instance()->call("_get_gizmo_name"); } return TTR("Nameless gizmo"); } int EditorNode3DGizmoPlugin::get_priority() const { - if (get_script_instance() && get_script_instance()->has_method("get_priority")) { - return get_script_instance()->call("get_priority"); + if (get_script_instance() && get_script_instance()->has_method("_get_priority")) { + return get_script_instance()->call("_get_priority"); } return 0; } @@ -7510,8 +7510,8 @@ Ref<EditorNode3DGizmo> EditorNode3DGizmoPlugin::get_gizmo(Node3D *p_spatial) { void EditorNode3DGizmoPlugin::_bind_methods() { #define GIZMO_REF PropertyInfo(Variant::OBJECT, "gizmo", PROPERTY_HINT_RESOURCE_TYPE, "EditorNode3DGizmo") - BIND_VMETHOD(MethodInfo(Variant::BOOL, "has_gizmo", PropertyInfo(Variant::OBJECT, "spatial", PROPERTY_HINT_RESOURCE_TYPE, "Node3D"))); - BIND_VMETHOD(MethodInfo(GIZMO_REF, "create_gizmo", PropertyInfo(Variant::OBJECT, "spatial", PROPERTY_HINT_RESOURCE_TYPE, "Node3D"))); + BIND_VMETHOD(MethodInfo(Variant::BOOL, "_has_gizmo", PropertyInfo(Variant::OBJECT, "spatial", PROPERTY_HINT_RESOURCE_TYPE, "Node3D"))); + BIND_VMETHOD(MethodInfo(GIZMO_REF, "_create_gizmo", PropertyInfo(Variant::OBJECT, "spatial", PROPERTY_HINT_RESOURCE_TYPE, "Node3D"))); ClassDB::bind_method(D_METHOD("create_material", "name", "color", "billboard", "on_top", "use_vertex_color"), &EditorNode3DGizmoPlugin::create_material, DEFVAL(false), DEFVAL(false), DEFVAL(false)); ClassDB::bind_method(D_METHOD("create_icon_material", "name", "texture", "on_top", "color"), &EditorNode3DGizmoPlugin::create_icon_material, DEFVAL(false), DEFVAL(Color(1, 1, 1, 1))); @@ -7520,38 +7520,38 @@ void EditorNode3DGizmoPlugin::_bind_methods() { ClassDB::bind_method(D_METHOD("get_material", "name", "gizmo"), &EditorNode3DGizmoPlugin::get_material, DEFVAL(Ref<EditorNode3DGizmo>())); - BIND_VMETHOD(MethodInfo(Variant::STRING, "get_gizmo_name")); - BIND_VMETHOD(MethodInfo(Variant::INT, "get_priority")); - BIND_VMETHOD(MethodInfo(Variant::BOOL, "can_be_hidden")); - BIND_VMETHOD(MethodInfo(Variant::BOOL, "is_selectable_when_hidden")); + BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_gizmo_name")); + BIND_VMETHOD(MethodInfo(Variant::INT, "_get_priority")); + BIND_VMETHOD(MethodInfo(Variant::BOOL, "_can_be_hidden")); + BIND_VMETHOD(MethodInfo(Variant::BOOL, "_is_selectable_when_hidden")); - BIND_VMETHOD(MethodInfo("redraw", GIZMO_REF)); - BIND_VMETHOD(MethodInfo(Variant::STRING, "get_handle_name", GIZMO_REF, PropertyInfo(Variant::INT, "index"))); + BIND_VMETHOD(MethodInfo("_redraw", GIZMO_REF)); + BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_handle_name", GIZMO_REF, PropertyInfo(Variant::INT, "index"))); - MethodInfo hvget(Variant::NIL, "get_handle_value", GIZMO_REF, PropertyInfo(Variant::INT, "index")); + MethodInfo hvget(Variant::NIL, "_get_handle_value", GIZMO_REF, PropertyInfo(Variant::INT, "index")); hvget.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; BIND_VMETHOD(hvget); - BIND_VMETHOD(MethodInfo("set_handle", GIZMO_REF, PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera3D"), PropertyInfo(Variant::VECTOR2, "point"))); - MethodInfo cm = MethodInfo("commit_handle", GIZMO_REF, PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::NIL, "restore"), PropertyInfo(Variant::BOOL, "cancel")); + BIND_VMETHOD(MethodInfo("_set_handle", GIZMO_REF, PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera3D"), PropertyInfo(Variant::VECTOR2, "point"))); + MethodInfo cm = MethodInfo("_commit_handle", GIZMO_REF, PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::NIL, "restore"), PropertyInfo(Variant::BOOL, "cancel")); cm.default_arguments.push_back(false); BIND_VMETHOD(cm); - BIND_VMETHOD(MethodInfo(Variant::BOOL, "is_handle_highlighted", GIZMO_REF, PropertyInfo(Variant::INT, "index"))); + BIND_VMETHOD(MethodInfo(Variant::BOOL, "_is_handle_highlighted", GIZMO_REF, PropertyInfo(Variant::INT, "index"))); #undef GIZMO_REF } bool EditorNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - if (get_script_instance() && get_script_instance()->has_method("has_gizmo")) { - return get_script_instance()->call("has_gizmo", p_spatial); + if (get_script_instance() && get_script_instance()->has_method("_has_gizmo")) { + return get_script_instance()->call("_has_gizmo", p_spatial); } return false; } Ref<EditorNode3DGizmo> EditorNode3DGizmoPlugin::create_gizmo(Node3D *p_spatial) { - if (get_script_instance() && get_script_instance()->has_method("create_gizmo")) { - return get_script_instance()->call("create_gizmo", p_spatial); + if (get_script_instance() && get_script_instance()->has_method("_create_gizmo")) { + return get_script_instance()->call("_create_gizmo", p_spatial); } Ref<EditorNode3DGizmo> ref; @@ -7562,55 +7562,55 @@ Ref<EditorNode3DGizmo> EditorNode3DGizmoPlugin::create_gizmo(Node3D *p_spatial) } bool EditorNode3DGizmoPlugin::can_be_hidden() const { - if (get_script_instance() && get_script_instance()->has_method("can_be_hidden")) { - return get_script_instance()->call("can_be_hidden"); + if (get_script_instance() && get_script_instance()->has_method("_can_be_hidden")) { + return get_script_instance()->call("_can_be_hidden"); } return true; } bool EditorNode3DGizmoPlugin::is_selectable_when_hidden() const { - if (get_script_instance() && get_script_instance()->has_method("is_selectable_when_hidden")) { - return get_script_instance()->call("is_selectable_when_hidden"); + if (get_script_instance() && get_script_instance()->has_method("_is_selectable_when_hidden")) { + return get_script_instance()->call("_is_selectable_when_hidden"); } return false; } void EditorNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - if (get_script_instance() && get_script_instance()->has_method("redraw")) { + if (get_script_instance() && get_script_instance()->has_method("_redraw")) { Ref<EditorNode3DGizmo> ref(p_gizmo); - get_script_instance()->call("redraw", ref); + get_script_instance()->call("_redraw", ref); } } String EditorNode3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { - if (get_script_instance() && get_script_instance()->has_method("get_handle_name")) { - return get_script_instance()->call("get_handle_name", p_gizmo, p_idx); + if (get_script_instance() && get_script_instance()->has_method("_get_handle_name")) { + return get_script_instance()->call("_get_handle_name", p_gizmo, p_idx); } return ""; } Variant EditorNode3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { - if (get_script_instance() && get_script_instance()->has_method("get_handle_value")) { - return get_script_instance()->call("get_handle_value", p_gizmo, p_idx); + if (get_script_instance() && get_script_instance()->has_method("_get_handle_value")) { + return get_script_instance()->call("_get_handle_value", p_gizmo, p_idx); } return Variant(); } void EditorNode3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { - if (get_script_instance() && get_script_instance()->has_method("set_handle")) { - get_script_instance()->call("set_handle", p_gizmo, p_idx, p_camera, p_point); + if (get_script_instance() && get_script_instance()->has_method("_set_handle")) { + get_script_instance()->call("_set_handle", p_gizmo, p_idx, p_camera, p_point); } } void EditorNode3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { - if (get_script_instance() && get_script_instance()->has_method("commit_handle")) { - get_script_instance()->call("commit_handle", p_gizmo, p_idx, p_restore, p_cancel); + if (get_script_instance() && get_script_instance()->has_method("_commit_handle")) { + get_script_instance()->call("_commit_handle", p_gizmo, p_idx, p_restore, p_cancel); } } bool EditorNode3DGizmoPlugin::is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_idx) const { - if (get_script_instance() && get_script_instance()->has_method("is_handle_highlighted")) { - return get_script_instance()->call("is_handle_highlighted", p_gizmo, p_idx); + if (get_script_instance() && get_script_instance()->has_method("_is_handle_highlighted")) { + return get_script_instance()->call("_is_handle_highlighted", p_gizmo, p_idx); } return false; } diff --git a/editor/plugins/path_2d_editor_plugin.cpp b/editor/plugins/path_2d_editor_plugin.cpp index 208abeb87f..838e67b5e7 100644 --- a/editor/plugins/path_2d_editor_plugin.cpp +++ b/editor/plugins/path_2d_editor_plugin.cpp @@ -31,7 +31,7 @@ #include "path_2d_editor_plugin.h" #include "canvas_item_editor_plugin.h" -#include "core/os/file_access.h" +#include "core/io/file_access.h" #include "core/os/keyboard.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index d2ea7a10dc..8a14db0cfd 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -32,8 +32,8 @@ #include "canvas_item_editor_plugin.h" #include "core/input/input.h" +#include "core/io/file_access.h" #include "core/math/geometry_2d.h" -#include "core/os/file_access.h" #include "core/os/keyboard.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" diff --git a/editor/plugins/resource_preloader_editor_plugin.cpp b/editor/plugins/resource_preloader_editor_plugin.cpp index b4b8e82124..b8b2c6d343 100644 --- a/editor/plugins/resource_preloader_editor_plugin.cpp +++ b/editor/plugins/resource_preloader_editor_plugin.cpp @@ -339,9 +339,9 @@ void ResourcePreloaderEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_update_library"), &ResourcePreloaderEditor::_update_library); ClassDB::bind_method(D_METHOD("_remove_resource", "to_remove"), &ResourcePreloaderEditor::_remove_resource); - ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &ResourcePreloaderEditor::get_drag_data_fw); - ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &ResourcePreloaderEditor::can_drop_data_fw); - ClassDB::bind_method(D_METHOD("drop_data_fw"), &ResourcePreloaderEditor::drop_data_fw); + ClassDB::bind_method(D_METHOD("_get_drag_data_fw"), &ResourcePreloaderEditor::get_drag_data_fw); + ClassDB::bind_method(D_METHOD("_can_drop_data_fw"), &ResourcePreloaderEditor::can_drop_data_fw); + ClassDB::bind_method(D_METHOD("_drop_data_fw"), &ResourcePreloaderEditor::drop_data_fw); } ResourcePreloaderEditor::ResourcePreloaderEditor() { diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 4b7faec228..0410ab3a45 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -32,8 +32,8 @@ #include "core/config/project_settings.h" #include "core/input/input.h" +#include "core/io/file_access.h" #include "core/io/resource_loader.h" -#include "core/os/file_access.h" #include "core/os/keyboard.h" #include "core/os/os.h" #include "editor/debugger/editor_debugger_node.h" @@ -230,7 +230,7 @@ void ScriptEditorBase::_bind_methods() { ADD_SIGNAL(MethodInfo("search_in_files_requested", PropertyInfo(Variant::STRING, "text"))); ADD_SIGNAL(MethodInfo("replace_in_files_requested", PropertyInfo(Variant::STRING, "text"))); - BIND_VMETHOD(MethodInfo("add_syntax_highlighter", PropertyInfo(Variant::OBJECT, "highlighter"))); + BIND_VMETHOD(MethodInfo("_add_syntax_highlighter", PropertyInfo(Variant::OBJECT, "highlighter"))); } static bool _is_built_in_script(Script *p_script) { @@ -1640,10 +1640,13 @@ void ScriptEditor::ensure_select_current() { ScriptEditorBase *se = _get_current_editor(); if (se) { se->enable_editor(); + se->set_find_replace_bar(find_replace_bar); if (!grab_focus_block && is_visible_in_tree()) { se->ensure_focus(); } + } else { + find_replace_bar->hide(); } } @@ -3273,9 +3276,9 @@ void ScriptEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("register_syntax_highlighter", "syntax_highlighter"), &ScriptEditor::register_syntax_highlighter); ClassDB::bind_method(D_METHOD("unregister_syntax_highlighter", "syntax_highlighter"), &ScriptEditor::unregister_syntax_highlighter); - ClassDB::bind_method(D_METHOD("get_drag_data_fw", "point", "from"), &ScriptEditor::get_drag_data_fw); - ClassDB::bind_method(D_METHOD("can_drop_data_fw", "point", "data", "from"), &ScriptEditor::can_drop_data_fw); - ClassDB::bind_method(D_METHOD("drop_data_fw", "point", "data", "from"), &ScriptEditor::drop_data_fw); + ClassDB::bind_method(D_METHOD("_get_drag_data_fw", "point", "from"), &ScriptEditor::get_drag_data_fw); + ClassDB::bind_method(D_METHOD("_can_drop_data_fw", "point", "data", "from"), &ScriptEditor::can_drop_data_fw); + ClassDB::bind_method(D_METHOD("_drop_data_fw", "point", "data", "from"), &ScriptEditor::drop_data_fw); ClassDB::bind_method(D_METHOD("goto_line", "line_number"), &ScriptEditor::_goto_script_line2); ClassDB::bind_method(D_METHOD("get_current_script"), &ScriptEditor::_get_current_script); @@ -3379,11 +3382,19 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { help_overview->set_custom_minimum_size(Size2(0, 60) * EDSCALE); //need to give a bit of limit to avoid it from disappearing help_overview->set_v_size_flags(SIZE_EXPAND_FILL); + VBoxContainer *code_editor_container = memnew(VBoxContainer); + script_split->add_child(code_editor_container); + tab_container = memnew(TabContainer); tab_container->set_tabs_visible(false); tab_container->set_custom_minimum_size(Size2(200, 0) * EDSCALE); - script_split->add_child(tab_container); + code_editor_container->add_child(tab_container); tab_container->set_h_size_flags(SIZE_EXPAND_FILL); + tab_container->set_v_size_flags(SIZE_EXPAND_FILL); + + find_replace_bar = memnew(FindReplaceBar); + code_editor_container->add_child(find_replace_bar); + find_replace_bar->hide(); ED_SHORTCUT("script_editor/window_sort", TTR("Sort")); ED_SHORTCUT("script_editor/window_move_up", TTR("Move Up"), KEY_MASK_SHIFT | KEY_MASK_ALT | KEY_UP); diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index 9ae63b7c4f..1d379059d4 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -162,6 +162,7 @@ public: virtual void set_tooltip_request_func(String p_method, Object *p_obj) = 0; virtual Control *get_edit_menu() = 0; virtual void clear_edit_menu() = 0; + virtual void set_find_replace_bar(FindReplaceBar *p_bar) = 0; virtual Control *get_base_editor() const = 0; @@ -270,6 +271,7 @@ class ScriptEditor : public PanelContainer { ConfirmationDialog *erase_tab_confirm; ScriptCreateDialog *script_create_dialog; Button *scripts_visible; + FindReplaceBar *find_replace_bar; String current_theme; diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index a29e51e8fb..1f6da30547 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -1343,9 +1343,9 @@ void ScriptTextEditor::_notification(int p_what) { void ScriptTextEditor::_bind_methods() { ClassDB::bind_method("_update_connected_methods", &ScriptTextEditor::_update_connected_methods); - ClassDB::bind_method("get_drag_data_fw", &ScriptTextEditor::get_drag_data_fw); - ClassDB::bind_method("can_drop_data_fw", &ScriptTextEditor::can_drop_data_fw); - ClassDB::bind_method("drop_data_fw", &ScriptTextEditor::drop_data_fw); + ClassDB::bind_method("_get_drag_data_fw", &ScriptTextEditor::get_drag_data_fw); + ClassDB::bind_method("_can_drop_data_fw", &ScriptTextEditor::can_drop_data_fw); + ClassDB::bind_method("_drop_data_fw", &ScriptTextEditor::drop_data_fw); ClassDB::bind_method(D_METHOD("add_syntax_highlighter", "highlighter"), &ScriptTextEditor::add_syntax_highlighter); } @@ -1358,6 +1358,10 @@ void ScriptTextEditor::clear_edit_menu() { memdelete(edit_hb); } +void ScriptTextEditor::set_find_replace_bar(FindReplaceBar *p_bar) { + code_editor->set_find_replace_bar(p_bar); +} + void ScriptTextEditor::reload(bool p_soft) { CodeEdit *te = code_editor->get_text_editor(); Ref<Script> scr = script; diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h index f784bbe1f8..7bb961bf19 100644 --- a/editor/plugins/script_text_editor.h +++ b/editor/plugins/script_text_editor.h @@ -233,6 +233,8 @@ public: Control *get_edit_menu() override; virtual void clear_edit_menu() override; + virtual void set_find_replace_bar(FindReplaceBar *p_bar) override; + static void register_editor(); virtual Control *get_base_editor() const override; diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index 3beef78bfc..e4a5a3796e 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -751,6 +751,11 @@ ShaderEditor::ShaderEditor(EditorNode *p_node) { editor_box->set_v_size_flags(SIZE_EXPAND_FILL); editor_box->add_child(shader_editor); + FindReplaceBar *bar = memnew(FindReplaceBar); + main_container->add_child(bar); + bar->hide(); + shader_editor->set_find_replace_bar(bar); + warnings_panel = memnew(RichTextLabel); warnings_panel->set_custom_minimum_size(Size2(0, 100 * EDSCALE)); warnings_panel->set_h_size_flags(SIZE_EXPAND_FILL); diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp index 6dc98503ca..a97584ebce 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -666,9 +666,9 @@ void Skeleton3DEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_update_properties"), &Skeleton3DEditor::_update_properties); ClassDB::bind_method(D_METHOD("_on_click_option"), &Skeleton3DEditor::_on_click_option); - ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &Skeleton3DEditor::get_drag_data_fw); - ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &Skeleton3DEditor::can_drop_data_fw); - ClassDB::bind_method(D_METHOD("drop_data_fw"), &Skeleton3DEditor::drop_data_fw); + ClassDB::bind_method(D_METHOD("_get_drag_data_fw"), &Skeleton3DEditor::get_drag_data_fw); + ClassDB::bind_method(D_METHOD("_can_drop_data_fw"), &Skeleton3DEditor::can_drop_data_fw); + ClassDB::bind_method(D_METHOD("_drop_data_fw"), &Skeleton3DEditor::drop_data_fw); ClassDB::bind_method(D_METHOD("move_skeleton_bone"), &Skeleton3DEditor::move_skeleton_bone); } diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index 59bc8f9e55..a5a3d624ec 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -964,9 +964,9 @@ void SpriteFramesEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da void SpriteFramesEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_update_library", "skipsel"), &SpriteFramesEditor::_update_library, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &SpriteFramesEditor::get_drag_data_fw); - ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &SpriteFramesEditor::can_drop_data_fw); - ClassDB::bind_method(D_METHOD("drop_data_fw"), &SpriteFramesEditor::drop_data_fw); + ClassDB::bind_method(D_METHOD("_get_drag_data_fw"), &SpriteFramesEditor::get_drag_data_fw); + ClassDB::bind_method(D_METHOD("_can_drop_data_fw"), &SpriteFramesEditor::can_drop_data_fw); + ClassDB::bind_method(D_METHOD("_drop_data_fw"), &SpriteFramesEditor::drop_data_fw); } SpriteFramesEditor::SpriteFramesEditor() { diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp index e1c6e61a0b..dc7f85a790 100644 --- a/editor/plugins/text_editor.cpp +++ b/editor/plugins/text_editor.cpp @@ -281,6 +281,10 @@ void TextEditor::clear_edit_menu() { memdelete(edit_hb); } +void TextEditor::set_find_replace_bar(FindReplaceBar *p_bar) { + code_editor->set_find_replace_bar(p_bar); +} + void TextEditor::_edit_option(int p_op) { CodeEdit *tx = code_editor->get_text_editor(); diff --git a/editor/plugins/text_editor.h b/editor/plugins/text_editor.h index abb75cc9d8..4e667dc676 100644 --- a/editor/plugins/text_editor.h +++ b/editor/plugins/text_editor.h @@ -139,6 +139,7 @@ public: virtual Control *get_edit_menu() override; virtual void clear_edit_menu() override; + virtual void set_find_replace_bar(FindReplaceBar *p_bar) override; virtual void validate() override; diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index 9526160674..686ff0f9ef 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -863,13 +863,13 @@ Sprite2D *TextureRegionEditor::get_sprite() { void TextureRegionEditor::edit(Object *p_obj) { if (node_sprite) { - node_sprite->disconnect("changed", callable_mp(this, &TextureRegionEditor::_texture_changed)); + node_sprite->disconnect("texture_changed", callable_mp(this, &TextureRegionEditor::_texture_changed)); } if (node_sprite_3d) { - node_sprite_3d->disconnect("changed", callable_mp(this, &TextureRegionEditor::_texture_changed)); + node_sprite_3d->disconnect("texture_changed", callable_mp(this, &TextureRegionEditor::_texture_changed)); } if (node_ninepatch) { - node_ninepatch->disconnect("changed", callable_mp(this, &TextureRegionEditor::_texture_changed)); + node_ninepatch->disconnect("texture_changed", callable_mp(this, &TextureRegionEditor::_texture_changed)); } if (obj_styleBox.is_valid()) { obj_styleBox->disconnect("changed", callable_mp(this, &TextureRegionEditor::_texture_changed)); @@ -881,13 +881,22 @@ void TextureRegionEditor::edit(Object *p_obj) { node_sprite = Object::cast_to<Sprite2D>(p_obj); node_sprite_3d = Object::cast_to<Sprite3D>(p_obj); node_ninepatch = Object::cast_to<NinePatchRect>(p_obj); + + bool is_resource = false; if (Object::cast_to<StyleBoxTexture>(p_obj)) { obj_styleBox = Ref<StyleBoxTexture>(Object::cast_to<StyleBoxTexture>(p_obj)); + is_resource = true; } if (Object::cast_to<AtlasTexture>(p_obj)) { atlas_tex = Ref<AtlasTexture>(Object::cast_to<AtlasTexture>(p_obj)); + is_resource = true; + } + + if (is_resource) { + p_obj->connect("changed", callable_mp(this, &TextureRegionEditor::_texture_changed)); + } else { + p_obj->connect("texture_changed", callable_mp(this, &TextureRegionEditor::_texture_changed)); } - p_obj->connect("changed", callable_mp(this, &TextureRegionEditor::_texture_changed)); _edit_region(); } else { node_sprite = nullptr; diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index f1c73f99dc..6607bf6cad 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -30,12 +30,10 @@ #include "theme_editor_plugin.h" -#include "core/os/file_access.h" #include "core/os/keyboard.h" -#include "core/version.h" +#include "editor/editor_resource_picker.h" #include "editor/editor_scale.h" #include "editor/progress_dialog.h" -#include "scene/gui/progress_bar.h" void ThemeItemImportTree::_update_items_tree() { import_items_tree->clear(); @@ -756,7 +754,9 @@ void ThemeItemImportTree::_import_selected() { return; } - ProgressDialog::get_singleton()->add_task("import_theme_items", TTR("Importing Theme Items"), selected_items.size()); + // Prevent changes from immediatelly being reported while the operation is still ongoing. + edited_theme->_freeze_change_propagation(); + ProgressDialog::get_singleton()->add_task("import_theme_items", TTR("Importing Theme Items"), selected_items.size() + 2); int idx = 0; for (Map<ThemeItem, ItemCheckedState>::Element *E = selected_items.front(); E; E = E->next()) { @@ -814,6 +814,12 @@ void ThemeItemImportTree::_import_selected() { idx++; } + // Allow changes to be reported now that the operation is finished. + ProgressDialog::get_singleton()->task_step("import_theme_items", TTR("Updating the editor"), idx++); + edited_theme->_unfreeze_and_propagate_changes(); + // Make sure the task is not ended before the editor freezes to update the Inspector. + ProgressDialog::get_singleton()->task_step("import_theme_items", TTR("Finalizing"), idx++); + ProgressDialog::get_singleton()->end_task("import_theme_items"); emit_signal("items_imported"); } @@ -1201,7 +1207,7 @@ void ThemeItemEditorDialog::_close_dialog() { } void ThemeItemEditorDialog::_dialog_about_to_show() { - ERR_FAIL_COND(edited_theme.is_null()); + ERR_FAIL_COND_MSG(edited_theme.is_null(), "Invalid state of the Theme Editor; the Theme resource is missing."); _update_edit_types(); @@ -1458,6 +1464,9 @@ void ThemeItemEditorDialog::_add_theme_type() { edited_theme->add_color_type(edit_add_type_value->get_text()); edited_theme->add_constant_type(edit_add_type_value->get_text()); _update_edit_types(); + + // Force emit a change so that other parts of the editor can update. + edited_theme->emit_changed(); } void ThemeItemEditorDialog::_add_theme_item(Theme::DataType p_data_type, String p_item_name, String p_item_type) { @@ -1488,15 +1497,24 @@ void ThemeItemEditorDialog::_add_theme_item(Theme::DataType p_data_type, String void ThemeItemEditorDialog::_remove_data_type_items(Theme::DataType p_data_type, String p_item_type) { List<StringName> names; + // Prevent changes from immediatelly being reported while the operation is still ongoing. + edited_theme->_freeze_change_propagation(); + edited_theme->get_theme_item_list(p_data_type, p_item_type, &names); for (List<StringName>::Element *E = names.front(); E; E = E->next()) { edited_theme->clear_theme_item(p_data_type, E->get(), p_item_type); } + + // Allow changes to be reported now that the operation is finished. + edited_theme->_unfreeze_and_propagate_changes(); } void ThemeItemEditorDialog::_remove_class_items() { List<StringName> names; + // Prevent changes from immediatelly being reported while the operation is still ongoing. + edited_theme->_freeze_change_propagation(); + for (int dt = 0; dt < Theme::DATA_TYPE_MAX; dt++) { Theme::DataType data_type = (Theme::DataType)dt; @@ -1509,12 +1527,18 @@ void ThemeItemEditorDialog::_remove_class_items() { } } + // Allow changes to be reported now that the operation is finished. + edited_theme->_unfreeze_and_propagate_changes(); + _update_edit_item_tree(edited_item_type); } void ThemeItemEditorDialog::_remove_custom_items() { List<StringName> names; + // Prevent changes from immediatelly being reported while the operation is still ongoing. + edited_theme->_freeze_change_propagation(); + for (int dt = 0; dt < Theme::DATA_TYPE_MAX; dt++) { Theme::DataType data_type = (Theme::DataType)dt; @@ -1527,12 +1551,18 @@ void ThemeItemEditorDialog::_remove_custom_items() { } } + // Allow changes to be reported now that the operation is finished. + edited_theme->_unfreeze_and_propagate_changes(); + _update_edit_item_tree(edited_item_type); } void ThemeItemEditorDialog::_remove_all_items() { List<StringName> names; + // Prevent changes from immediatelly being reported while the operation is still ongoing. + edited_theme->_freeze_change_propagation(); + for (int dt = 0; dt < Theme::DATA_TYPE_MAX; dt++) { Theme::DataType data_type = (Theme::DataType)dt; @@ -1543,6 +1573,9 @@ void ThemeItemEditorDialog::_remove_all_items() { } } + // Allow changes to be reported now that the operation is finished. + edited_theme->_unfreeze_and_propagate_changes(); + _update_edit_item_tree(edited_item_type); } @@ -1907,268 +1940,1310 @@ ThemeItemEditorDialog::ThemeItemEditorDialog() { confirm_closing_dialog->connect("confirmed", callable_mp(this, &ThemeItemEditorDialog::_close_dialog)); } +VBoxContainer *ThemeTypeEditor::_create_item_list(Theme::DataType p_data_type) { + VBoxContainer *items_tab = memnew(VBoxContainer); + items_tab->set_custom_minimum_size(Size2(0, 160) * EDSCALE); + data_type_tabs->add_child(items_tab); + data_type_tabs->set_tab_title(data_type_tabs->get_tab_count() - 1, ""); + + ScrollContainer *items_sc = memnew(ScrollContainer); + items_sc->set_v_size_flags(SIZE_EXPAND_FILL); + items_sc->set_enable_h_scroll(false); + items_tab->add_child(items_sc); + VBoxContainer *items_list = memnew(VBoxContainer); + items_list->set_h_size_flags(SIZE_EXPAND_FILL); + items_sc->add_child(items_list); + + HBoxContainer *item_add_hb = memnew(HBoxContainer); + items_tab->add_child(item_add_hb); + LineEdit *item_add_edit = memnew(LineEdit); + item_add_edit->set_h_size_flags(SIZE_EXPAND_FILL); + item_add_hb->add_child(item_add_edit); + item_add_edit->connect("text_entered", callable_mp(this, &ThemeTypeEditor::_item_add_lineedit_cbk), varray(p_data_type, item_add_edit)); + Button *item_add_button = memnew(Button); + item_add_button->set_text(TTR("Add")); + item_add_hb->add_child(item_add_button); + item_add_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_item_add_cbk), varray(p_data_type, item_add_edit)); + + return items_list; +} + +void ThemeTypeEditor::_update_type_list() { + ERR_FAIL_COND(edited_theme.is_null()); + + if (updating) { + return; + } + updating = true; + + Control *focused = get_focus_owner(); + if (focused) { + if (focusables.has(focused)) { + // If focus is currently on one of the internal property editors, don't update. + updating = false; + return; + } + + Node *focus_parent = focused->get_parent(); + while (focus_parent) { + Control *c = Object::cast_to<Control>(focus_parent); + if (c && focusables.has(c)) { + // If focus is currently on one of the internal property editors, don't update. + updating = false; + return; + } + + focus_parent = focus_parent->get_parent(); + } + } + + List<StringName> theme_types; + edited_theme->get_type_list(&theme_types); + theme_types.sort_custom<StringName::AlphCompare>(); + + theme_type_list->clear(); + + if (theme_types.size() > 0) { + theme_type_list->set_disabled(false); + + bool item_reselected = false; + int e_idx = 0; + for (List<StringName>::Element *E = theme_types.front(); E; E = E->next()) { + Ref<Texture2D> item_icon; + if (E->get() == "") { + item_icon = get_theme_icon("NodeDisabled", "EditorIcons"); + } else { + item_icon = EditorNode::get_singleton()->get_class_icon(E->get(), "NodeDisabled"); + } + theme_type_list->add_icon_item(item_icon, E->get()); + + if (E->get() == edited_type) { + theme_type_list->select(e_idx); + item_reselected = true; + } + e_idx++; + } + + if (!item_reselected) { + theme_type_list->select(0); + _list_type_selected(0); + } else { + _update_type_items(); + } + } else { + theme_type_list->set_disabled(true); + theme_type_list->add_item(TTR("None")); + + edited_type = ""; + _update_type_items(); + } + + updating = false; +} + +void ThemeTypeEditor::_update_type_list_debounced() { + update_debounce_timer->start(); +} + +void ThemeTypeEditor::_update_add_type_options(const String &p_filter) { + add_type_options->clear(); + + List<StringName> names; + Theme::get_default()->get_type_list(&names); + names.sort_custom<StringName::AlphCompare>(); + + for (List<StringName>::Element *E = names.front(); E; E = E->next()) { + if (!p_filter.is_subsequence_ofi(String(E->get()))) { + continue; + } + + Ref<Texture2D> item_icon; + if (E->get() == "") { + item_icon = get_theme_icon("NodeDisabled", "EditorIcons"); + } else { + item_icon = EditorNode::get_singleton()->get_class_icon(E->get(), "NodeDisabled"); + } + + add_type_options->add_item(E->get(), item_icon); + } +} + +OrderedHashMap<StringName, bool> ThemeTypeEditor::_get_type_items(String p_type_name, void (Theme::*get_list_func)(StringName, List<StringName> *) const, bool include_default) { + OrderedHashMap<StringName, bool> items; + List<StringName> names; + + if (include_default) { + names.clear(); + (Theme::get_default().operator->()->*get_list_func)(p_type_name, &names); + names.sort_custom<StringName::AlphCompare>(); + for (List<StringName>::Element *E = names.front(); E; E = E->next()) { + items[E->get()] = false; + } + } + + { + names.clear(); + (edited_theme.operator->()->*get_list_func)(p_type_name, &names); + names.sort_custom<StringName::AlphCompare>(); + for (List<StringName>::Element *E = names.front(); E; E = E->next()) { + items[E->get()] = true; + } + } + + List<StringName> keys; + for (OrderedHashMap<StringName, bool>::Element E = items.front(); E; E = E.next()) { + keys.push_back(E.key()); + } + keys.sort_custom<StringName::AlphCompare>(); + + OrderedHashMap<StringName, bool> ordered_items; + for (List<StringName>::Element *E = keys.front(); E; E = E->next()) { + ordered_items[E->get()] = items[E->get()]; + } + + return ordered_items; +} + +HBoxContainer *ThemeTypeEditor::_create_property_control(Theme::DataType p_data_type, String p_item_name, bool p_editable) { + HBoxContainer *item_control = memnew(HBoxContainer); + + HBoxContainer *item_name_container = memnew(HBoxContainer); + item_name_container->set_h_size_flags(SIZE_EXPAND_FILL); + item_name_container->set_stretch_ratio(2.0); + item_control->add_child(item_name_container); + + Label *item_name = memnew(Label); + item_name->set_h_size_flags(SIZE_EXPAND_FILL); + item_name->set_clip_text(true); + item_name->set_text(p_item_name); + item_name->set_tooltip(p_item_name); + item_name_container->add_child(item_name); + + if (p_editable) { + LineEdit *item_name_edit = memnew(LineEdit); + item_name_edit->set_h_size_flags(SIZE_EXPAND_FILL); + item_name_edit->set_text(p_item_name); + item_name_container->add_child(item_name_edit); + item_name_edit->connect("text_entered", callable_mp(this, &ThemeTypeEditor::_item_rename_entered), varray(p_data_type, p_item_name, item_name_container)); + item_name_edit->hide(); + + Button *item_rename_button = memnew(Button); + item_rename_button->set_icon(get_theme_icon("Edit", "EditorIcons")); + item_rename_button->set_tooltip(TTR("Rename Item")); + item_rename_button->set_flat(true); + item_name_container->add_child(item_rename_button); + item_rename_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_item_rename_cbk), varray(p_data_type, p_item_name, item_name_container)); + + Button *item_remove_button = memnew(Button); + item_remove_button->set_icon(get_theme_icon("Remove", "EditorIcons")); + item_remove_button->set_tooltip(TTR("Remove Item")); + item_remove_button->set_flat(true); + item_name_container->add_child(item_remove_button); + item_remove_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_item_remove_cbk), varray(p_data_type, p_item_name)); + + Button *item_rename_confirm_button = memnew(Button); + item_rename_confirm_button->set_icon(get_theme_icon("ImportCheck", "EditorIcons")); + item_rename_confirm_button->set_tooltip(TTR("Confirm Item Rename")); + item_rename_confirm_button->set_flat(true); + item_name_container->add_child(item_rename_confirm_button); + item_rename_confirm_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_item_rename_confirmed), varray(p_data_type, p_item_name, item_name_container)); + item_rename_confirm_button->hide(); + + Button *item_rename_cancel_button = memnew(Button); + item_rename_cancel_button->set_icon(get_theme_icon("ImportFail", "EditorIcons")); + item_rename_cancel_button->set_tooltip(TTR("Cancel Item Rename")); + item_rename_cancel_button->set_flat(true); + item_name_container->add_child(item_rename_cancel_button); + item_rename_cancel_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_item_rename_canceled), varray(p_data_type, p_item_name, item_name_container)); + item_rename_cancel_button->hide(); + } else { + item_name->add_theme_color_override("font_color", get_theme_color("disabled_font_color", "Editor")); + + Button *item_override_button = memnew(Button); + item_override_button->set_icon(get_theme_icon("Add", "EditorIcons")); + item_override_button->set_tooltip(TTR("Override Item")); + item_override_button->set_flat(true); + item_name_container->add_child(item_override_button); + item_override_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_item_override_cbk), varray(p_data_type, p_item_name)); + } + + return item_control; +} + +void ThemeTypeEditor::_add_focusable(Control *p_control) { + focusables.append(p_control); +} + +void ThemeTypeEditor::_update_type_items() { + bool show_default = show_default_items_button->is_pressed(); + List<StringName> names; + + focusables.clear(); + + // Colors. + { + for (int i = color_items_list->get_child_count() - 1; i >= 0; i--) { + Node *node = color_items_list->get_child(i); + node->queue_delete(); + color_items_list->remove_child(node); + } + + OrderedHashMap<StringName, bool> color_items = _get_type_items(edited_type, &Theme::get_color_list, show_default); + for (OrderedHashMap<StringName, bool>::Element E = color_items.front(); E; E = E.next()) { + HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_COLOR, E.key(), E.get()); + ColorPickerButton *item_editor = memnew(ColorPickerButton); + item_editor->set_h_size_flags(SIZE_EXPAND_FILL); + item_control->add_child(item_editor); + + if (E.get()) { + item_editor->set_pick_color(edited_theme->get_color(E.key(), edited_type)); + item_editor->connect("color_changed", callable_mp(this, &ThemeTypeEditor::_color_item_changed), varray(E.key())); + } else { + item_editor->set_pick_color(Theme::get_default()->get_color(E.key(), edited_type)); + item_editor->set_disabled(true); + } + + _add_focusable(item_editor); + color_items_list->add_child(item_control); + } + } + + // Constants. + { + for (int i = constant_items_list->get_child_count() - 1; i >= 0; i--) { + Node *node = constant_items_list->get_child(i); + node->queue_delete(); + constant_items_list->remove_child(node); + } + + OrderedHashMap<StringName, bool> constant_items = _get_type_items(edited_type, &Theme::get_constant_list, show_default); + for (OrderedHashMap<StringName, bool>::Element E = constant_items.front(); E; E = E.next()) { + HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_CONSTANT, E.key(), E.get()); + SpinBox *item_editor = memnew(SpinBox); + item_editor->set_h_size_flags(SIZE_EXPAND_FILL); + item_editor->set_min(-100000); + item_editor->set_max(100000); + item_editor->set_step(1); + item_editor->set_allow_lesser(true); + item_editor->set_allow_greater(true); + item_control->add_child(item_editor); + + if (E.get()) { + item_editor->set_value(edited_theme->get_constant(E.key(), edited_type)); + item_editor->connect("value_changed", callable_mp(this, &ThemeTypeEditor::_constant_item_changed), varray(E.key())); + } else { + item_editor->set_value(Theme::get_default()->get_constant(E.key(), edited_type)); + item_editor->set_editable(false); + } + + _add_focusable(item_editor); + constant_items_list->add_child(item_control); + } + } + + // Fonts. + { + for (int i = font_items_list->get_child_count() - 1; i >= 0; i--) { + Node *node = font_items_list->get_child(i); + node->queue_delete(); + font_items_list->remove_child(node); + } + + OrderedHashMap<StringName, bool> font_items = _get_type_items(edited_type, &Theme::get_font_list, show_default); + for (OrderedHashMap<StringName, bool>::Element E = font_items.front(); E; E = E.next()) { + HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_FONT, E.key(), E.get()); + EditorResourcePicker *item_editor = memnew(EditorResourcePicker); + item_editor->set_h_size_flags(SIZE_EXPAND_FILL); + item_editor->set_base_type("Font"); + item_control->add_child(item_editor); + + if (E.get()) { + if (edited_theme->has_font(E.key(), edited_type)) { + item_editor->set_edited_resource(edited_theme->get_font(E.key(), edited_type)); + } else { + item_editor->set_edited_resource(RES()); + } + item_editor->connect("resource_selected", callable_mp(this, &ThemeTypeEditor::_edit_resource_item), varray(item_control)); + item_editor->connect("resource_changed", callable_mp(this, &ThemeTypeEditor::_font_item_changed), varray(E.key())); + } else { + if (Theme::get_default()->has_font(E.key(), edited_type)) { + item_editor->set_edited_resource(Theme::get_default()->get_font(E.key(), edited_type)); + } else { + item_editor->set_edited_resource(RES()); + } + item_editor->set_editable(false); + } + + _add_focusable(item_editor); + font_items_list->add_child(item_control); + } + } + + // Fonts sizes. + { + for (int i = font_size_items_list->get_child_count() - 1; i >= 0; i--) { + Node *node = font_size_items_list->get_child(i); + node->queue_delete(); + font_size_items_list->remove_child(node); + } + + OrderedHashMap<StringName, bool> font_size_items = _get_type_items(edited_type, &Theme::get_font_size_list, show_default); + for (OrderedHashMap<StringName, bool>::Element E = font_size_items.front(); E; E = E.next()) { + HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_FONT_SIZE, E.key(), E.get()); + SpinBox *item_editor = memnew(SpinBox); + item_editor->set_h_size_flags(SIZE_EXPAND_FILL); + item_editor->set_min(-100000); + item_editor->set_max(100000); + item_editor->set_step(1); + item_editor->set_allow_lesser(true); + item_editor->set_allow_greater(true); + item_control->add_child(item_editor); + + if (E.get()) { + item_editor->set_value(edited_theme->get_font_size(E.key(), edited_type)); + item_editor->connect("value_changed", callable_mp(this, &ThemeTypeEditor::_font_size_item_changed), varray(E.key())); + } else { + item_editor->set_value(Theme::get_default()->get_font_size(E.key(), edited_type)); + item_editor->set_editable(false); + } + + _add_focusable(item_editor); + font_size_items_list->add_child(item_control); + } + } + + // Icons. + { + for (int i = icon_items_list->get_child_count() - 1; i >= 0; i--) { + Node *node = icon_items_list->get_child(i); + node->queue_delete(); + icon_items_list->remove_child(node); + } + + OrderedHashMap<StringName, bool> icon_items = _get_type_items(edited_type, &Theme::get_icon_list, show_default); + for (OrderedHashMap<StringName, bool>::Element E = icon_items.front(); E; E = E.next()) { + HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_ICON, E.key(), E.get()); + EditorResourcePicker *item_editor = memnew(EditorResourcePicker); + item_editor->set_h_size_flags(SIZE_EXPAND_FILL); + item_editor->set_base_type("Texture2D"); + item_control->add_child(item_editor); + + if (E.get()) { + if (edited_theme->has_icon(E.key(), edited_type)) { + item_editor->set_edited_resource(edited_theme->get_icon(E.key(), edited_type)); + } else { + item_editor->set_edited_resource(RES()); + } + item_editor->connect("resource_selected", callable_mp(this, &ThemeTypeEditor::_edit_resource_item), varray(item_control)); + item_editor->connect("resource_changed", callable_mp(this, &ThemeTypeEditor::_icon_item_changed), varray(E.key())); + } else { + if (Theme::get_default()->has_icon(E.key(), edited_type)) { + item_editor->set_edited_resource(Theme::get_default()->get_icon(E.key(), edited_type)); + } else { + item_editor->set_edited_resource(RES()); + } + item_editor->set_editable(false); + } + + _add_focusable(item_editor); + icon_items_list->add_child(item_control); + } + } + + // Styleboxes. + { + for (int i = stylebox_items_list->get_child_count() - 1; i >= 0; i--) { + Node *node = stylebox_items_list->get_child(i); + node->queue_delete(); + stylebox_items_list->remove_child(node); + } + + if (leading_stylebox.pinned) { + HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_STYLEBOX, leading_stylebox.item_name, true); + EditorResourcePicker *item_editor = memnew(EditorResourcePicker); + item_editor->set_h_size_flags(SIZE_EXPAND_FILL); + item_editor->set_stretch_ratio(1.5); + item_editor->set_base_type("StyleBox"); + + Button *pin_leader_button = memnew(Button); + pin_leader_button->set_flat(true); + pin_leader_button->set_toggle_mode(true); + pin_leader_button->set_pressed(true); + pin_leader_button->set_icon(get_theme_icon("Pin", "EditorIcons")); + pin_leader_button->set_tooltip(TTR("Unpin this StyleBox as a main style.")); + item_control->add_child(pin_leader_button); + pin_leader_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_unpin_leading_stylebox)); + + item_control->add_child(item_editor); + + if (leading_stylebox.stylebox.is_valid()) { + item_editor->set_edited_resource(leading_stylebox.stylebox); + } else { + item_editor->set_edited_resource(RES()); + } + item_editor->connect("resource_selected", callable_mp(this, &ThemeTypeEditor::_edit_resource_item), varray(item_control)); + item_editor->connect("resource_changed", callable_mp(this, &ThemeTypeEditor::_stylebox_item_changed), varray(leading_stylebox.item_name)); + + stylebox_items_list->add_child(item_control); + stylebox_items_list->add_child(memnew(HSeparator)); + } + + OrderedHashMap<StringName, bool> stylebox_items = _get_type_items(edited_type, &Theme::get_stylebox_list, show_default); + for (OrderedHashMap<StringName, bool>::Element E = stylebox_items.front(); E; E = E.next()) { + if (leading_stylebox.pinned && leading_stylebox.item_name == E.key()) { + continue; + } + + HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_STYLEBOX, E.key(), E.get()); + EditorResourcePicker *item_editor = memnew(EditorResourcePicker); + item_editor->set_h_size_flags(SIZE_EXPAND_FILL); + item_editor->set_stretch_ratio(1.5); + item_editor->set_base_type("StyleBox"); + + if (E.get()) { + Ref<StyleBox> stylebox_value; + if (edited_theme->has_stylebox(E.key(), edited_type)) { + stylebox_value = edited_theme->get_stylebox(E.key(), edited_type); + item_editor->set_edited_resource(stylebox_value); + } else { + item_editor->set_edited_resource(RES()); + } + item_editor->connect("resource_selected", callable_mp(this, &ThemeTypeEditor::_edit_resource_item), varray(item_control)); + item_editor->connect("resource_changed", callable_mp(this, &ThemeTypeEditor::_stylebox_item_changed), varray(E.key())); + + Button *pin_leader_button = memnew(Button); + pin_leader_button->set_flat(true); + pin_leader_button->set_toggle_mode(true); + pin_leader_button->set_icon(get_theme_icon("Pin", "EditorIcons")); + pin_leader_button->set_tooltip(TTR("Pin this StyleBox as a main style. Editing its properties will update the same properties in all other StyleBoxes of this type.")); + item_control->add_child(pin_leader_button); + pin_leader_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_pin_leading_stylebox), varray(stylebox_value, E.key())); + } else { + if (Theme::get_default()->has_stylebox(E.key(), edited_type)) { + item_editor->set_edited_resource(Theme::get_default()->get_stylebox(E.key(), edited_type)); + } else { + item_editor->set_edited_resource(RES()); + } + item_editor->set_editable(false); + } + + item_control->add_child(item_editor); + _add_focusable(item_editor); + stylebox_items_list->add_child(item_control); + } + } +} + +void ThemeTypeEditor::_list_type_selected(int p_index) { + edited_type = theme_type_list->get_item_text(p_index); + _update_type_items(); +} + +void ThemeTypeEditor::_add_type_button_cbk() { + add_type_dialog->popup_centered(Size2(560, 420) * EDSCALE); + add_type_filter->grab_focus(); +} + +void ThemeTypeEditor::_add_type_filter_cbk(const String &p_value) { + _update_add_type_options(p_value); +} + +void ThemeTypeEditor::_add_type_options_cbk(int p_index) { + add_type_filter->set_text(add_type_options->get_item_text(p_index)); +} + +void ThemeTypeEditor::_add_type_dialog_confirmed() { + select_type(add_type_filter->get_text().strip_edges()); +} + +void ThemeTypeEditor::_add_type_dialog_entered(const String &p_value) { + select_type(p_value.strip_edges()); + add_type_dialog->hide(); +} + +void ThemeTypeEditor::_add_type_dialog_activated(int p_index) { + select_type(add_type_options->get_item_text(p_index)); + add_type_dialog->hide(); +} + +void ThemeTypeEditor::_add_default_type_items() { + List<StringName> names; + + updating = true; + // Prevent changes from immediatelly being reported while the operation is still ongoing. + edited_theme->_freeze_change_propagation(); + + { + names.clear(); + Theme::get_default()->get_icon_list(edited_type, &names); + for (List<StringName>::Element *E = names.front(); E; E = E->next()) { + if (!edited_theme->has_icon(E->get(), edited_type)) { + edited_theme->set_icon(E->get(), edited_type, Ref<Texture2D>()); + } + } + } + { + names.clear(); + Theme::get_default()->get_stylebox_list(edited_type, &names); + for (List<StringName>::Element *E = names.front(); E; E = E->next()) { + if (!edited_theme->has_stylebox(E->get(), edited_type)) { + edited_theme->set_stylebox(E->get(), edited_type, Ref<StyleBox>()); + } + } + } + { + names.clear(); + Theme::get_default()->get_font_list(edited_type, &names); + for (List<StringName>::Element *E = names.front(); E; E = E->next()) { + if (!edited_theme->has_font(E->get(), edited_type)) { + edited_theme->set_font(E->get(), edited_type, Ref<Font>()); + } + } + } + { + names.clear(); + Theme::get_default()->get_font_size_list(edited_type, &names); + for (List<StringName>::Element *E = names.front(); E; E = E->next()) { + if (!edited_theme->has_font_size(E->get(), edited_type)) { + edited_theme->set_font_size(E->get(), edited_type, Theme::get_default()->get_font_size(E->get(), edited_type)); + } + } + } + { + names.clear(); + Theme::get_default()->get_color_list(edited_type, &names); + for (List<StringName>::Element *E = names.front(); E; E = E->next()) { + if (!edited_theme->has_color(E->get(), edited_type)) { + edited_theme->set_color(E->get(), edited_type, Theme::get_default()->get_color(E->get(), edited_type)); + } + } + } + { + names.clear(); + Theme::get_default()->get_constant_list(edited_type, &names); + for (List<StringName>::Element *E = names.front(); E; E = E->next()) { + if (!edited_theme->has_constant(E->get(), edited_type)) { + edited_theme->set_constant(E->get(), edited_type, Theme::get_default()->get_constant(E->get(), edited_type)); + } + } + } + + // Allow changes to be reported now that the operation is finished. + edited_theme->_unfreeze_and_propagate_changes(); + updating = false; + + _update_type_items(); +} + +void ThemeTypeEditor::_item_add_cbk(int p_data_type, Control *p_control) { + LineEdit *le = Object::cast_to<LineEdit>(p_control); + if (le->get_text().strip_edges().is_empty()) { + return; + } + + String item_name = le->get_text().strip_edges(); + switch (p_data_type) { + case Theme::DATA_TYPE_COLOR: { + edited_theme->set_color(item_name, edited_type, Color()); + } break; + case Theme::DATA_TYPE_CONSTANT: { + edited_theme->set_constant(item_name, edited_type, 0); + } break; + case Theme::DATA_TYPE_FONT: { + edited_theme->set_font(item_name, edited_type, Ref<Font>()); + } break; + case Theme::DATA_TYPE_FONT_SIZE: { + edited_theme->set_font_size(item_name, edited_type, -1); + } break; + case Theme::DATA_TYPE_ICON: { + edited_theme->set_icon(item_name, edited_type, Ref<Texture2D>()); + } break; + case Theme::DATA_TYPE_STYLEBOX: { + edited_theme->set_stylebox(item_name, edited_type, Ref<StyleBox>()); + } break; + } + + le->set_text(""); +} + +void ThemeTypeEditor::_item_add_lineedit_cbk(String p_value, int p_data_type, Control *p_control) { + _item_add_cbk(p_data_type, p_control); +} + +void ThemeTypeEditor::_item_override_cbk(int p_data_type, String p_item_name) { + switch (p_data_type) { + case Theme::DATA_TYPE_COLOR: { + edited_theme->set_color(p_item_name, edited_type, Theme::get_default()->get_color(p_item_name, edited_type)); + } break; + case Theme::DATA_TYPE_CONSTANT: { + edited_theme->set_constant(p_item_name, edited_type, Theme::get_default()->get_constant(p_item_name, edited_type)); + } break; + case Theme::DATA_TYPE_FONT: { + edited_theme->set_font(p_item_name, edited_type, Ref<Font>()); + } break; + case Theme::DATA_TYPE_FONT_SIZE: { + edited_theme->set_font_size(p_item_name, edited_type, Theme::get_default()->get_font_size(p_item_name, edited_type)); + } break; + case Theme::DATA_TYPE_ICON: { + edited_theme->set_icon(p_item_name, edited_type, Ref<Texture2D>()); + } break; + case Theme::DATA_TYPE_STYLEBOX: { + edited_theme->set_stylebox(p_item_name, edited_type, Ref<StyleBox>()); + } break; + } +} + +void ThemeTypeEditor::_item_remove_cbk(int p_data_type, String p_item_name) { + switch (p_data_type) { + case Theme::DATA_TYPE_COLOR: { + edited_theme->clear_color(p_item_name, edited_type); + } break; + case Theme::DATA_TYPE_CONSTANT: { + edited_theme->clear_constant(p_item_name, edited_type); + } break; + case Theme::DATA_TYPE_FONT: { + edited_theme->clear_font(p_item_name, edited_type); + } break; + case Theme::DATA_TYPE_FONT_SIZE: { + edited_theme->clear_font_size(p_item_name, edited_type); + } break; + case Theme::DATA_TYPE_ICON: { + edited_theme->clear_icon(p_item_name, edited_type); + } break; + case Theme::DATA_TYPE_STYLEBOX: { + edited_theme->clear_stylebox(p_item_name, edited_type); + } break; + } +} + +void ThemeTypeEditor::_item_rename_cbk(int p_data_type, String p_item_name, Control *p_control) { + // Label + Object::cast_to<Label>(p_control->get_child(0))->hide(); + // Label buttons + Object::cast_to<Button>(p_control->get_child(2))->hide(); + Object::cast_to<Button>(p_control->get_child(3))->hide(); + + // LineEdit + Object::cast_to<LineEdit>(p_control->get_child(1))->set_text(p_item_name); + Object::cast_to<LineEdit>(p_control->get_child(1))->show(); + // LineEdit buttons + Object::cast_to<Button>(p_control->get_child(4))->show(); + Object::cast_to<Button>(p_control->get_child(5))->show(); +} + +void ThemeTypeEditor::_item_rename_confirmed(int p_data_type, String p_item_name, Control *p_control) { + LineEdit *le = Object::cast_to<LineEdit>(p_control->get_child(1)); + if (le->get_text().strip_edges().is_empty()) { + return; + } + + String new_name = le->get_text().strip_edges(); + if (new_name == p_item_name) { + _item_rename_canceled(p_data_type, p_item_name, p_control); + return; + } + + switch (p_data_type) { + case Theme::DATA_TYPE_COLOR: { + edited_theme->rename_color(p_item_name, new_name, edited_type); + } break; + case Theme::DATA_TYPE_CONSTANT: { + edited_theme->rename_constant(p_item_name, new_name, edited_type); + } break; + case Theme::DATA_TYPE_FONT: { + edited_theme->rename_font(p_item_name, new_name, edited_type); + } break; + case Theme::DATA_TYPE_FONT_SIZE: { + edited_theme->rename_font_size(p_item_name, new_name, edited_type); + } break; + case Theme::DATA_TYPE_ICON: { + edited_theme->rename_icon(p_item_name, new_name, edited_type); + } break; + case Theme::DATA_TYPE_STYLEBOX: { + edited_theme->rename_stylebox(p_item_name, new_name, edited_type); + } break; + } +} + +void ThemeTypeEditor::_item_rename_entered(String p_value, int p_data_type, String p_item_name, Control *p_control) { + _item_rename_confirmed(p_data_type, p_item_name, p_control); +} + +void ThemeTypeEditor::_item_rename_canceled(int p_data_type, String p_item_name, Control *p_control) { + // LineEdit + Object::cast_to<LineEdit>(p_control->get_child(1))->hide(); + // LineEdit buttons + Object::cast_to<Button>(p_control->get_child(4))->hide(); + Object::cast_to<Button>(p_control->get_child(5))->hide(); + + // Label + Object::cast_to<Label>(p_control->get_child(0))->show(); + // Label buttons + Object::cast_to<Button>(p_control->get_child(2))->show(); + Object::cast_to<Button>(p_control->get_child(3))->show(); +} + +void ThemeTypeEditor::_color_item_changed(Color p_value, String p_item_name) { + edited_theme->set_color(p_item_name, edited_type, p_value); +} + +void ThemeTypeEditor::_constant_item_changed(float p_value, String p_item_name) { + edited_theme->set_constant(p_item_name, edited_type, int(p_value)); +} + +void ThemeTypeEditor::_font_size_item_changed(float p_value, String p_item_name) { + edited_theme->set_font_size(p_item_name, edited_type, int(p_value)); +} + +void ThemeTypeEditor::_edit_resource_item(RES p_resource, Control *p_editor) { + EditorNode::get_singleton()->edit_resource(p_resource); +} + +void ThemeTypeEditor::_font_item_changed(Ref<Font> p_value, String p_item_name) { + edited_theme->set_font(p_item_name, edited_type, p_value); +} + +void ThemeTypeEditor::_icon_item_changed(Ref<Texture2D> p_value, String p_item_name) { + edited_theme->set_icon(p_item_name, edited_type, p_value); +} + +void ThemeTypeEditor::_stylebox_item_changed(Ref<StyleBox> p_value, String p_item_name) { + edited_theme->set_stylebox(p_item_name, edited_type, p_value); + + if (leading_stylebox.pinned && leading_stylebox.item_name == p_item_name) { + if (leading_stylebox.stylebox.is_valid()) { + leading_stylebox.stylebox->disconnect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading)); + } + + leading_stylebox.stylebox = p_value; + leading_stylebox.ref_stylebox = (p_value.is_valid() ? p_value->duplicate() : RES()); + if (p_value.is_valid()) { + leading_stylebox.stylebox->connect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading)); + } + } +} + +void ThemeTypeEditor::_pin_leading_stylebox(Ref<StyleBox> p_stylebox, String p_item_name) { + if (leading_stylebox.stylebox.is_valid()) { + leading_stylebox.stylebox->disconnect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading)); + } + + LeadingStylebox leader; + leader.pinned = true; + leader.item_name = p_item_name; + leader.stylebox = p_stylebox; + leader.ref_stylebox = (p_stylebox.is_valid() ? p_stylebox->duplicate() : RES()); + + leading_stylebox = leader; + if (leading_stylebox.stylebox.is_valid()) { + leading_stylebox.stylebox->connect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading)); + } + + _update_type_items(); +} + +void ThemeTypeEditor::_unpin_leading_stylebox() { + if (leading_stylebox.stylebox.is_valid()) { + leading_stylebox.stylebox->disconnect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading)); + } + + LeadingStylebox leader; + leader.pinned = false; + leading_stylebox = leader; + + _update_type_items(); +} + +void ThemeTypeEditor::_update_stylebox_from_leading() { + if (!leading_stylebox.pinned || leading_stylebox.stylebox.is_null()) { + return; + } + + // Prevent changes from immediatelly being reported while the operation is still ongoing. + edited_theme->_freeze_change_propagation(); + + List<StringName> names; + edited_theme->get_stylebox_list(edited_type, &names); + List<Ref<StyleBox>> styleboxes; + for (List<StringName>::Element *E = names.front(); E; E = E->next()) { + if (E->get() == leading_stylebox.item_name) { + continue; + } + + Ref<StyleBox> sb = edited_theme->get_stylebox(E->get(), edited_type); + if (sb->get_class() == leading_stylebox.stylebox->get_class()) { + styleboxes.push_back(sb); + } + } + + List<PropertyInfo> props; + leading_stylebox.stylebox->get_property_list(&props); + for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { + if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) { + continue; + } + + Variant value = leading_stylebox.stylebox->get(E->get().name); + Variant ref_value = leading_stylebox.ref_stylebox->get(E->get().name); + if (value == ref_value) { + continue; + } + + for (List<Ref<StyleBox>>::Element *F = styleboxes.front(); F; F = F->next()) { + Ref<StyleBox> sb = F->get(); + sb->set(E->get().name, value); + } + } + + leading_stylebox.ref_stylebox = leading_stylebox.stylebox->duplicate(); + + // Allow changes to be reported now that the operation is finished. + edited_theme->_unfreeze_and_propagate_changes(); +} + +void ThemeTypeEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: { + add_type_button->set_icon(get_theme_icon("Add", "EditorIcons")); + + data_type_tabs->set_tab_icon(0, get_theme_icon("Color", "EditorIcons")); + data_type_tabs->set_tab_icon(1, get_theme_icon("MemberConstant", "EditorIcons")); + data_type_tabs->set_tab_icon(2, get_theme_icon("Font", "EditorIcons")); + data_type_tabs->set_tab_icon(3, get_theme_icon("FontSize", "EditorIcons")); + data_type_tabs->set_tab_icon(4, get_theme_icon("ImageTexture", "EditorIcons")); + data_type_tabs->set_tab_icon(5, get_theme_icon("StyleBoxFlat", "EditorIcons")); + + data_type_tabs->add_theme_style_override("tab_selected", get_theme_stylebox("tab_selected_odd", "TabContainer")); + data_type_tabs->add_theme_style_override("panel", get_theme_stylebox("panel_odd", "TabContainer")); + + _update_add_type_options(); + } break; + } +} + +void ThemeTypeEditor::set_edited_theme(const Ref<Theme> &p_theme) { + if (edited_theme.is_valid()) { + edited_theme->disconnect("changed", callable_mp(this, &ThemeTypeEditor::_update_type_list_debounced)); + } + + edited_theme = p_theme; + edited_theme->connect("changed", callable_mp(this, &ThemeTypeEditor::_update_type_list_debounced)); + _update_type_list(); +} + +void ThemeTypeEditor::select_type(String p_type_name) { + edited_type = p_type_name; + bool type_exists = false; + + for (int i = 0; i < theme_type_list->get_item_count(); i++) { + String type_name = theme_type_list->get_item_text(i); + if (type_name == edited_type) { + theme_type_list->select(i); + type_exists = true; + break; + } + } + + if (type_exists) { + _update_type_items(); + } else { + edited_theme->add_icon_type(edited_type); + edited_theme->add_stylebox_type(edited_type); + edited_theme->add_font_type(edited_type); + edited_theme->add_font_size_type(edited_type); + edited_theme->add_color_type(edited_type); + edited_theme->add_constant_type(edited_type); + + _update_type_list(); + } +} + +ThemeTypeEditor::ThemeTypeEditor() { + VBoxContainer *main_vb = memnew(VBoxContainer); + add_child(main_vb); + + HBoxContainer *type_list_hb = memnew(HBoxContainer); + main_vb->add_child(type_list_hb); + + Label *type_list_label = memnew(Label); + type_list_label->set_text(TTR("Type:")); + type_list_hb->add_child(type_list_label); + + theme_type_list = memnew(OptionButton); + theme_type_list->set_h_size_flags(SIZE_EXPAND_FILL); + type_list_hb->add_child(theme_type_list); + theme_type_list->connect("item_selected", callable_mp(this, &ThemeTypeEditor::_list_type_selected)); + + add_type_button = memnew(Button); + add_type_button->set_tooltip(TTR("Add Type")); + type_list_hb->add_child(add_type_button); + add_type_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_add_type_button_cbk)); + + add_type_dialog = memnew(ConfirmationDialog); + add_type_dialog->set_title(TTR("Add Item Type")); + type_list_hb->add_child(add_type_dialog); + add_type_dialog->connect("confirmed", callable_mp(this, &ThemeTypeEditor::_add_type_dialog_confirmed)); + + VBoxContainer *add_type_vb = memnew(VBoxContainer); + add_type_dialog->add_child(add_type_vb); + + Label *add_type_filter_label = memnew(Label); + add_type_filter_label->set_text(TTR("Name:")); + add_type_vb->add_child(add_type_filter_label); + add_type_filter = memnew(LineEdit); + add_type_vb->add_child(add_type_filter); + add_type_filter->connect("text_changed", callable_mp(this, &ThemeTypeEditor::_add_type_filter_cbk)); + add_type_filter->connect("text_entered", callable_mp(this, &ThemeTypeEditor::_add_type_dialog_entered)); + Label *add_type_options_label = memnew(Label); + add_type_options_label->set_text(TTR("Node Types:")); + add_type_vb->add_child(add_type_options_label); + add_type_options = memnew(ItemList); + add_type_options->set_v_size_flags(SIZE_EXPAND_FILL); + add_type_vb->add_child(add_type_options); + add_type_options->connect("item_selected", callable_mp(this, &ThemeTypeEditor::_add_type_options_cbk)); + add_type_options->connect("item_activated", callable_mp(this, &ThemeTypeEditor::_add_type_dialog_activated)); + + HBoxContainer *type_controls = memnew(HBoxContainer); + main_vb->add_child(type_controls); + + show_default_items_button = memnew(CheckButton); + show_default_items_button->set_h_size_flags(SIZE_EXPAND_FILL); + show_default_items_button->set_text(TTR("Show Default")); + show_default_items_button->set_tooltip(TTR("Show default type items alongside items that have been overridden.")); + show_default_items_button->set_pressed(true); + type_controls->add_child(show_default_items_button); + show_default_items_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_update_type_items)); + + Button *add_default_items_button = memnew(Button); + add_default_items_button->set_h_size_flags(SIZE_EXPAND_FILL); + add_default_items_button->set_text(TTR("Override All")); + add_default_items_button->set_tooltip(TTR("Override all default type items.")); + type_controls->add_child(add_default_items_button); + add_default_items_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_add_default_type_items)); + + data_type_tabs = memnew(TabContainer); + main_vb->add_child(data_type_tabs); + data_type_tabs->set_v_size_flags(SIZE_EXPAND_FILL); + data_type_tabs->set_use_hidden_tabs_for_min_size(true); + + color_items_list = _create_item_list(Theme::DATA_TYPE_COLOR); + constant_items_list = _create_item_list(Theme::DATA_TYPE_CONSTANT); + font_items_list = _create_item_list(Theme::DATA_TYPE_FONT); + font_size_items_list = _create_item_list(Theme::DATA_TYPE_FONT_SIZE); + icon_items_list = _create_item_list(Theme::DATA_TYPE_ICON); + stylebox_items_list = _create_item_list(Theme::DATA_TYPE_STYLEBOX); + + update_debounce_timer = memnew(Timer); + update_debounce_timer->set_one_shot(true); + update_debounce_timer->set_wait_time(0.5); + update_debounce_timer->connect("timeout", callable_mp(this, &ThemeTypeEditor::_update_type_list)); + add_child(update_debounce_timer); +} + void ThemeEditor::edit(const Ref<Theme> &p_theme) { + if (theme == p_theme) { + return; + } + theme = p_theme; + theme_type_editor->set_edited_theme(p_theme); theme_edit_dialog->set_edited_theme(p_theme); - main_panel->set_theme(p_theme); - main_container->set_theme(p_theme); -} -void ThemeEditor::_propagate_redraw(Control *p_at) { - p_at->notification(NOTIFICATION_THEME_CHANGED); - p_at->minimum_size_changed(); - p_at->update(); - for (int i = 0; i < p_at->get_child_count(); i++) { - Control *a = Object::cast_to<Control>(p_at->get_child(i)); - if (a) { - _propagate_redraw(a); + for (int i = 0; i < preview_tabs_content->get_child_count(); i++) { + ThemeEditorPreview *preview_tab = Object::cast_to<ThemeEditorPreview>(preview_tabs_content->get_child(i)); + if (!preview_tab) { + continue; } + + preview_tab->set_preview_theme(p_theme); } + + theme_name->set_text(TTR("Theme") + ": " + theme->get_path().get_file()); +} + +Ref<Theme> ThemeEditor::get_edited_theme() { + return theme; } -void ThemeEditor::_refresh_interval() { - _propagate_redraw(main_panel); - _propagate_redraw(main_container); +void ThemeEditor::_theme_save_button_cbk(bool p_save_as) { + ERR_FAIL_COND_MSG(theme.is_null(), "Invalid state of the Theme Editor; the Theme resource is missing."); + + if (p_save_as) { + EditorNode::get_singleton()->save_resource_as(theme); + } else { + EditorNode::get_singleton()->save_resource(theme); + } } void ThemeEditor::_theme_edit_button_cbk() { theme_edit_dialog->popup_centered(Size2(850, 760) * EDSCALE); } +void ThemeEditor::_add_preview_button_cbk() { + preview_scene_dialog->popup_file_dialog(); +} + +void ThemeEditor::_preview_scene_dialog_cbk(const String &p_path) { + SceneThemeEditorPreview *preview_tab = memnew(SceneThemeEditorPreview); + if (!preview_tab->set_preview_scene(p_path)) { + return; + } + + _add_preview_tab(preview_tab, p_path.get_file(), get_theme_icon("PackedScene", "EditorIcons")); + preview_tab->connect("scene_invalidated", callable_mp(this, &ThemeEditor::_remove_preview_tab_invalid), varray(preview_tab)); + preview_tab->connect("scene_reloaded", callable_mp(this, &ThemeEditor::_update_preview_tab), varray(preview_tab)); +} + +void ThemeEditor::_add_preview_tab(ThemeEditorPreview *p_preview_tab, const String &p_preview_name, const Ref<Texture2D> &p_icon) { + p_preview_tab->set_preview_theme(theme); + + preview_tabs->add_tab(p_preview_name, p_icon); + preview_tabs_content->add_child(p_preview_tab); + preview_tabs->set_tab_right_button(preview_tabs->get_tab_count() - 1, EditorNode::get_singleton()->get_gui_base()->get_theme_icon("close", "Tabs")); + p_preview_tab->connect("control_picked", callable_mp(this, &ThemeEditor::_preview_control_picked)); + + preview_tabs->set_current_tab(preview_tabs->get_tab_count() - 1); +} + +void ThemeEditor::_change_preview_tab(int p_tab) { + ERR_FAIL_INDEX_MSG(p_tab, preview_tabs_content->get_child_count(), "Attempting to open a preview tab that doesn't exist."); + + for (int i = 0; i < preview_tabs_content->get_child_count(); i++) { + Control *c = Object::cast_to<Control>(preview_tabs_content->get_child(i)); + if (!c) { + continue; + } + + c->set_visible(i == p_tab); + } +} + +void ThemeEditor::_remove_preview_tab(int p_tab) { + ERR_FAIL_INDEX_MSG(p_tab, preview_tabs_content->get_child_count(), "Attempting to remove a preview tab that doesn't exist."); + + ThemeEditorPreview *preview_tab = Object::cast_to<ThemeEditorPreview>(preview_tabs_content->get_child(p_tab)); + ERR_FAIL_COND_MSG(Object::cast_to<DefaultThemeEditorPreview>(preview_tab), "Attemptying to remove the default preview tab."); + + if (preview_tab) { + preview_tab->disconnect("control_picked", callable_mp(this, &ThemeEditor::_preview_control_picked)); + if (preview_tab->is_connected("scene_invalidated", callable_mp(this, &ThemeEditor::_remove_preview_tab_invalid))) { + preview_tab->disconnect("scene_invalidated", callable_mp(this, &ThemeEditor::_remove_preview_tab_invalid)); + } + if (preview_tab->is_connected("scene_reloaded", callable_mp(this, &ThemeEditor::_update_preview_tab))) { + preview_tab->disconnect("scene_reloaded", callable_mp(this, &ThemeEditor::_update_preview_tab)); + } + + preview_tabs_content->remove_child(preview_tab); + preview_tabs->remove_tab(p_tab); + _change_preview_tab(preview_tabs->get_current_tab()); + } +} + +void ThemeEditor::_remove_preview_tab_invalid(Node *p_tab_control) { + int tab_index = p_tab_control->get_index(); + _remove_preview_tab(tab_index); +} + +void ThemeEditor::_update_preview_tab(Node *p_tab_control) { + if (!Object::cast_to<SceneThemeEditorPreview>(p_tab_control)) { + return; + } + + int tab_index = p_tab_control->get_index(); + SceneThemeEditorPreview *scene_preview = Object::cast_to<SceneThemeEditorPreview>(p_tab_control); + preview_tabs->set_tab_title(tab_index, scene_preview->get_preview_scene_path().get_file()); +} + +void ThemeEditor::_preview_control_picked(String p_class_name) { + theme_type_editor->select_type(p_class_name); +} + void ThemeEditor::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_PROCESS: { - time_left -= get_process_delta_time(); - if (time_left < 0) { - time_left = 1.5; - _refresh_interval(); - } + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: { + preview_tabs->add_theme_style_override("tab_selected", get_theme_stylebox("ThemeEditorPreviewFG", "EditorStyles")); + preview_tabs->add_theme_style_override("tab_unselected", get_theme_stylebox("ThemeEditorPreviewBG", "EditorStyles")); + preview_tabs_content->add_theme_style_override("panel", get_theme_stylebox("panel_odd", "TabContainer")); + + add_preview_button->set_icon(get_theme_icon("Add", "EditorIcons")); } break; } } -void ThemeEditor::_bind_methods() { -} - ThemeEditor::ThemeEditor() { HBoxContainer *top_menu = memnew(HBoxContainer); add_child(top_menu); - top_menu->add_child(memnew(Label(TTR("Preview:")))); + theme_name = memnew(Label); + theme_name->set_text(TTR("Theme") + ": "); + top_menu->add_child(theme_name); + top_menu->add_spacer(false); - theme_edit_button = memnew(Button); - theme_edit_button->set_text(TTR("Manage Items")); + Button *theme_save_button = memnew(Button); + theme_save_button->set_text(TTR("Save")); + theme_save_button->set_flat(true); + theme_save_button->connect("pressed", callable_mp(this, &ThemeEditor::_theme_save_button_cbk), varray(false)); + top_menu->add_child(theme_save_button); + + Button *theme_save_as_button = memnew(Button); + theme_save_as_button->set_text(TTR("Save As...")); + theme_save_as_button->set_flat(true); + theme_save_as_button->connect("pressed", callable_mp(this, &ThemeEditor::_theme_save_button_cbk), varray(true)); + top_menu->add_child(theme_save_as_button); + + top_menu->add_child(memnew(VSeparator)); + + Button *theme_edit_button = memnew(Button); + theme_edit_button->set_text(TTR("Manage Items...")); theme_edit_button->set_tooltip(TTR("Add, remove, organize and import Theme items.")); theme_edit_button->set_flat(true); theme_edit_button->connect("pressed", callable_mp(this, &ThemeEditor::_theme_edit_button_cbk)); top_menu->add_child(theme_edit_button); - ScrollContainer *scroll = memnew(ScrollContainer); - add_child(scroll); - scroll->set_enable_v_scroll(true); - scroll->set_enable_h_scroll(true); - scroll->set_v_size_flags(SIZE_EXPAND_FILL); - - MarginContainer *root_container = memnew(MarginContainer); - scroll->add_child(root_container); - root_container->set_theme(Theme::get_default()); - root_container->set_clip_contents(true); - root_container->set_custom_minimum_size(Size2(700, 0) * EDSCALE); - root_container->set_v_size_flags(SIZE_EXPAND_FILL); - root_container->set_h_size_flags(SIZE_EXPAND_FILL); - - //// Preview Controls //// - - main_panel = memnew(Panel); - root_container->add_child(main_panel); - - main_container = memnew(MarginContainer); - root_container->add_child(main_container); - main_container->add_theme_constant_override("margin_right", 4 * EDSCALE); - main_container->add_theme_constant_override("margin_top", 4 * EDSCALE); - main_container->add_theme_constant_override("margin_left", 4 * EDSCALE); - main_container->add_theme_constant_override("margin_bottom", 4 * EDSCALE); - - HBoxContainer *main_hb = memnew(HBoxContainer); - main_container->add_child(main_hb); - - VBoxContainer *first_vb = memnew(VBoxContainer); - main_hb->add_child(first_vb); - first_vb->set_h_size_flags(SIZE_EXPAND_FILL); - first_vb->add_theme_constant_override("separation", 10 * EDSCALE); - - first_vb->add_child(memnew(Label("Label"))); - - first_vb->add_child(memnew(Button("Button"))); - Button *bt = memnew(Button); - bt->set_text(TTR("Toggle Button")); - bt->set_toggle_mode(true); - bt->set_pressed(true); - first_vb->add_child(bt); - bt = memnew(Button); - bt->set_text(TTR("Disabled Button")); - bt->set_disabled(true); - first_vb->add_child(bt); - Button *tb = memnew(Button); - tb->set_flat(true); - tb->set_text("Button"); - first_vb->add_child(tb); - - CheckButton *cb = memnew(CheckButton); - cb->set_text("CheckButton"); - first_vb->add_child(cb); - CheckBox *cbx = memnew(CheckBox); - cbx->set_text("CheckBox"); - first_vb->add_child(cbx); - - MenuButton *test_menu_button = memnew(MenuButton); - test_menu_button->set_text("MenuButton"); - test_menu_button->get_popup()->add_item(TTR("Item")); - test_menu_button->get_popup()->add_item(TTR("Disabled Item")); - test_menu_button->get_popup()->set_item_disabled(1, true); - test_menu_button->get_popup()->add_separator(); - test_menu_button->get_popup()->add_check_item(TTR("Check Item")); - test_menu_button->get_popup()->add_check_item(TTR("Checked Item")); - test_menu_button->get_popup()->set_item_checked(4, true); - test_menu_button->get_popup()->add_separator(); - test_menu_button->get_popup()->add_radio_check_item(TTR("Radio Item")); - test_menu_button->get_popup()->add_radio_check_item(TTR("Checked Radio Item")); - test_menu_button->get_popup()->set_item_checked(7, true); - test_menu_button->get_popup()->add_separator(TTR("Named Sep.")); - - PopupMenu *test_submenu = memnew(PopupMenu); - test_menu_button->get_popup()->add_child(test_submenu); - test_submenu->set_name("submenu"); - test_menu_button->get_popup()->add_submenu_item(TTR("Submenu"), "submenu"); - test_submenu->add_item(TTR("Subitem 1")); - test_submenu->add_item(TTR("Subitem 2")); - first_vb->add_child(test_menu_button); - - OptionButton *test_option_button = memnew(OptionButton); - test_option_button->add_item("OptionButton"); - test_option_button->add_separator(); - test_option_button->add_item(TTR("Has")); - test_option_button->add_item(TTR("Many")); - test_option_button->add_item(TTR("Options")); - first_vb->add_child(test_option_button); - first_vb->add_child(memnew(ColorPickerButton)); - - VBoxContainer *second_vb = memnew(VBoxContainer); - second_vb->set_h_size_flags(SIZE_EXPAND_FILL); - main_hb->add_child(second_vb); - second_vb->add_theme_constant_override("separation", 10 * EDSCALE); - LineEdit *le = memnew(LineEdit); - le->set_text("LineEdit"); - second_vb->add_child(le); - le = memnew(LineEdit); - le->set_text(TTR("Disabled LineEdit")); - le->set_editable(false); - second_vb->add_child(le); - TextEdit *te = memnew(TextEdit); - te->set_text("TextEdit"); - te->set_custom_minimum_size(Size2(0, 100) * EDSCALE); - second_vb->add_child(te); - second_vb->add_child(memnew(SpinBox)); - - HBoxContainer *vhb = memnew(HBoxContainer); - second_vb->add_child(vhb); - vhb->set_custom_minimum_size(Size2(0, 100) * EDSCALE); - vhb->add_child(memnew(VSlider)); - VScrollBar *vsb = memnew(VScrollBar); - vsb->set_page(25); - vhb->add_child(vsb); - vhb->add_child(memnew(VSeparator)); - VBoxContainer *hvb = memnew(VBoxContainer); - vhb->add_child(hvb); - hvb->set_alignment(ALIGN_CENTER); - hvb->set_h_size_flags(SIZE_EXPAND_FILL); - hvb->add_child(memnew(HSlider)); - HScrollBar *hsb = memnew(HScrollBar); - hsb->set_page(25); - hvb->add_child(hsb); - HSlider *hs = memnew(HSlider); - hs->set_editable(false); - hvb->add_child(hs); - hvb->add_child(memnew(HSeparator)); - ProgressBar *pb = memnew(ProgressBar); - pb->set_value(50); - hvb->add_child(pb); - - VBoxContainer *third_vb = memnew(VBoxContainer); - third_vb->set_h_size_flags(SIZE_EXPAND_FILL); - third_vb->add_theme_constant_override("separation", 10 * EDSCALE); - main_hb->add_child(third_vb); - - TabContainer *tc = memnew(TabContainer); - third_vb->add_child(tc); - tc->set_custom_minimum_size(Size2(0, 135) * EDSCALE); - Control *tcc = memnew(Control); - tcc->set_name(TTR("Tab 1")); - tc->add_child(tcc); - tcc = memnew(Control); - tcc->set_name(TTR("Tab 2")); - tc->add_child(tcc); - tcc = memnew(Control); - tcc->set_name(TTR("Tab 3")); - tc->add_child(tcc); - tc->set_tab_disabled(2, true); - - Tree *test_tree = memnew(Tree); - third_vb->add_child(test_tree); - test_tree->set_custom_minimum_size(Size2(0, 175) * EDSCALE); - test_tree->add_theme_constant_override("draw_relationship_lines", 1); - - TreeItem *item = test_tree->create_item(); - item->set_text(0, "Tree"); - item = test_tree->create_item(test_tree->get_root()); - item->set_text(0, "Item"); - item = test_tree->create_item(test_tree->get_root()); - item->set_editable(0, true); - item->set_text(0, TTR("Editable Item")); - TreeItem *sub_tree = test_tree->create_item(test_tree->get_root()); - sub_tree->set_text(0, TTR("Subtree")); - item = test_tree->create_item(sub_tree); - item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); - item->set_editable(0, true); - item->set_text(0, "Check Item"); - item = test_tree->create_item(sub_tree); - item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE); - item->set_editable(0, true); - item->set_range_config(0, 0, 20, 0.1); - item->set_range(0, 2); - item = test_tree->create_item(sub_tree); - item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE); - item->set_editable(0, true); - item->set_text(0, TTR("Has,Many,Options")); - item->set_range(0, 2); - - main_hb->add_theme_constant_override("separation", 20 * EDSCALE); - theme_edit_dialog = memnew(ThemeItemEditorDialog); theme_edit_dialog->hide(); - add_child(theme_edit_dialog); + top_menu->add_child(theme_edit_dialog); + + HSplitContainer *main_hs = memnew(HSplitContainer); + main_hs->set_v_size_flags(SIZE_EXPAND_FILL); + add_child(main_hs); + + VBoxContainer *preview_tabs_vb = memnew(VBoxContainer); + preview_tabs_vb->set_h_size_flags(SIZE_EXPAND_FILL); + preview_tabs_vb->set_custom_minimum_size(Size2(520, 0) * EDSCALE); + preview_tabs_vb->add_theme_constant_override("separation", 2 * EDSCALE); + main_hs->add_child(preview_tabs_vb); + HBoxContainer *preview_tabbar_hb = memnew(HBoxContainer); + preview_tabs_vb->add_child(preview_tabbar_hb); + preview_tabs_content = memnew(PanelContainer); + preview_tabs_content->set_v_size_flags(SIZE_EXPAND_FILL); + preview_tabs_content->set_draw_behind_parent(true); + preview_tabs_vb->add_child(preview_tabs_content); + + preview_tabs = memnew(Tabs); + preview_tabs->set_tab_align(Tabs::ALIGN_LEFT); + preview_tabs->set_h_size_flags(SIZE_EXPAND_FILL); + preview_tabbar_hb->add_child(preview_tabs); + preview_tabs->connect("tab_changed", callable_mp(this, &ThemeEditor::_change_preview_tab)); + preview_tabs->connect("right_button_pressed", callable_mp(this, &ThemeEditor::_remove_preview_tab)); + + HBoxContainer *add_preview_button_hb = memnew(HBoxContainer); + preview_tabbar_hb->add_child(add_preview_button_hb); + add_preview_button = memnew(Button); + add_preview_button->set_text(TTR("Add Preview")); + add_preview_button_hb->add_child(add_preview_button); + add_preview_button->connect("pressed", callable_mp(this, &ThemeEditor::_add_preview_button_cbk)); + + DefaultThemeEditorPreview *default_preview_tab = memnew(DefaultThemeEditorPreview); + preview_tabs_content->add_child(default_preview_tab); + default_preview_tab->connect("control_picked", callable_mp(this, &ThemeEditor::_preview_control_picked)); + preview_tabs->add_tab(TTR("Default Preview")); + + preview_scene_dialog = memnew(EditorFileDialog); + preview_scene_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE); + preview_scene_dialog->set_title(TTR("Select UI Scene:")); + List<String> ext; + ResourceLoader::get_recognized_extensions_for_type("PackedScene", &ext); + for (List<String>::Element *E = ext.front(); E; E = E->next()) { + preview_scene_dialog->add_filter("*." + E->get() + "; Scene"); + } + main_hs->add_child(preview_scene_dialog); + preview_scene_dialog->connect("file_selected", callable_mp(this, &ThemeEditor::_preview_scene_dialog_cbk)); + + theme_type_editor = memnew(ThemeTypeEditor); + main_hs->add_child(theme_type_editor); + theme_type_editor->set_custom_minimum_size(Size2(360, 0) * EDSCALE); } void ThemeEditorPlugin::edit(Object *p_node) { if (Object::cast_to<Theme>(p_node)) { theme_editor->edit(Object::cast_to<Theme>(p_node)); + } else if (Object::cast_to<Font>(p_node) || Object::cast_to<StyleBox>(p_node) || Object::cast_to<Texture2D>(p_node)) { + // Do nothing, keep editing the existing theme. } else { theme_editor->edit(Ref<Theme>()); } } bool ThemeEditorPlugin::handles(Object *p_node) const { - return p_node->is_class("Theme"); + if (Object::cast_to<Theme>(p_node)) { + return true; + } + + Ref<Theme> edited_theme = theme_editor->get_edited_theme(); + if (edited_theme.is_null()) { + return false; + } + + // If we are editing a theme already and this particular resource happens to belong to it, + // then we just keep editing it, despite not being able to directly handle it. + // This only goes one layer deep, but if required this can be extended to support, say, FontData inside of Font. + bool belongs_to_theme = false; + + if (Object::cast_to<Font>(p_node)) { + Ref<Font> font_item = Object::cast_to<Font>(p_node); + List<StringName> types; + List<StringName> names; + + edited_theme->get_font_type_list(&types); + for (List<StringName>::Element *E = types.front(); E; E = E->next()) { + names.clear(); + edited_theme->get_font_list(E->get(), &names); + + for (List<StringName>::Element *F = names.front(); F; F = F->next()) { + if (font_item == edited_theme->get_font(F->get(), E->get())) { + belongs_to_theme = true; + break; + } + } + } + } else if (Object::cast_to<StyleBox>(p_node)) { + Ref<StyleBox> stylebox_item = Object::cast_to<StyleBox>(p_node); + List<StringName> types; + List<StringName> names; + + edited_theme->get_stylebox_type_list(&types); + for (List<StringName>::Element *E = types.front(); E; E = E->next()) { + names.clear(); + edited_theme->get_stylebox_list(E->get(), &names); + + for (List<StringName>::Element *F = names.front(); F; F = F->next()) { + if (stylebox_item == edited_theme->get_stylebox(F->get(), E->get())) { + belongs_to_theme = true; + break; + } + } + } + } else if (Object::cast_to<Texture2D>(p_node)) { + Ref<Texture2D> icon_item = Object::cast_to<Texture2D>(p_node); + List<StringName> types; + List<StringName> names; + + edited_theme->get_icon_type_list(&types); + for (List<StringName>::Element *E = types.front(); E; E = E->next()) { + names.clear(); + edited_theme->get_icon_list(E->get(), &names); + + for (List<StringName>::Element *F = names.front(); F; F = F->next()) { + if (icon_item == edited_theme->get_icon(F->get(), E->get())) { + belongs_to_theme = true; + break; + } + } + } + } + + return belongs_to_theme; } void ThemeEditorPlugin::make_visible(bool p_visible) { if (p_visible) { - theme_editor->set_process(true); button->show(); editor->make_bottom_panel_item_visible(theme_editor); } else { - theme_editor->set_process(false); if (theme_editor->is_visible_in_tree()) { editor->hide_bottom_panel(); } diff --git a/editor/plugins/theme_editor_plugin.h b/editor/plugins/theme_editor_plugin.h index c42ebf1a19..77baf46395 100644 --- a/editor/plugins/theme_editor_plugin.h +++ b/editor/plugins/theme_editor_plugin.h @@ -31,13 +31,13 @@ #ifndef THEME_EDITOR_PLUGIN_H #define THEME_EDITOR_PLUGIN_H -#include "scene/gui/check_box.h" -#include "scene/gui/file_dialog.h" #include "scene/gui/margin_container.h" #include "scene/gui/option_button.h" #include "scene/gui/scroll_container.h" +#include "scene/gui/tabs.h" #include "scene/gui/texture_rect.h" #include "scene/resources/theme.h" +#include "theme_editor_preview.h" #include "editor/editor_node.h" @@ -263,30 +263,123 @@ public: ThemeItemEditorDialog(); }; +class ThemeTypeEditor : public MarginContainer { + GDCLASS(ThemeTypeEditor, MarginContainer); + + Ref<Theme> edited_theme; + String edited_type; + bool updating = false; + + struct LeadingStylebox { + bool pinned = false; + StringName item_name; + Ref<StyleBox> stylebox; + Ref<StyleBox> ref_stylebox; + }; + + LeadingStylebox leading_stylebox; + + OptionButton *theme_type_list; + Button *add_type_button; + ConfirmationDialog *add_type_dialog; + LineEdit *add_type_filter; + ItemList *add_type_options; + + CheckButton *show_default_items_button; + + TabContainer *data_type_tabs; + VBoxContainer *color_items_list; + VBoxContainer *constant_items_list; + VBoxContainer *font_items_list; + VBoxContainer *font_size_items_list; + VBoxContainer *icon_items_list; + VBoxContainer *stylebox_items_list; + + Vector<Control *> focusables; + Timer *update_debounce_timer; + + VBoxContainer *_create_item_list(Theme::DataType p_data_type); + void _update_type_list(); + void _update_type_list_debounced(); + void _update_add_type_options(const String &p_filter = ""); + OrderedHashMap<StringName, bool> _get_type_items(String p_type_name, void (Theme::*get_list_func)(StringName, List<StringName> *) const, bool include_default); + HBoxContainer *_create_property_control(Theme::DataType p_data_type, String p_item_name, bool p_editable); + void _add_focusable(Control *p_control); + void _update_type_items(); + + void _list_type_selected(int p_index); + void _select_type(String p_type_name); + void _add_type_button_cbk(); + void _add_type_filter_cbk(const String &p_value); + void _add_type_options_cbk(int p_index); + void _add_type_dialog_confirmed(); + void _add_type_dialog_entered(const String &p_value); + void _add_type_dialog_activated(int p_index); + void _add_default_type_items(); + + void _item_add_cbk(int p_data_type, Control *p_control); + void _item_add_lineedit_cbk(String p_value, int p_data_type, Control *p_control); + void _item_override_cbk(int p_data_type, String p_item_name); + void _item_remove_cbk(int p_data_type, String p_item_name); + void _item_rename_cbk(int p_data_type, String p_item_name, Control *p_control); + void _item_rename_confirmed(int p_data_type, String p_item_name, Control *p_control); + void _item_rename_entered(String p_value, int p_data_type, String p_item_name, Control *p_control); + void _item_rename_canceled(int p_data_type, String p_item_name, Control *p_control); + + void _color_item_changed(Color p_value, String p_item_name); + void _constant_item_changed(float p_value, String p_item_name); + void _font_size_item_changed(float p_value, String p_item_name); + void _edit_resource_item(RES p_resource, Control *p_editor); + void _font_item_changed(Ref<Font> p_value, String p_item_name); + void _icon_item_changed(Ref<Texture2D> p_value, String p_item_name); + void _stylebox_item_changed(Ref<StyleBox> p_value, String p_item_name); + void _pin_leading_stylebox(Ref<StyleBox> p_stylebox, String p_item_name); + void _unpin_leading_stylebox(); + void _update_stylebox_from_leading(); + +protected: + void _notification(int p_what); + +public: + void set_edited_theme(const Ref<Theme> &p_theme); + void select_type(String p_type_name); + + ThemeTypeEditor(); +}; + class ThemeEditor : public VBoxContainer { GDCLASS(ThemeEditor, VBoxContainer); Ref<Theme> theme; - double time_left = 0; + Tabs *preview_tabs; + PanelContainer *preview_tabs_content; + Button *add_preview_button; + EditorFileDialog *preview_scene_dialog; - Button *theme_edit_button; - ThemeItemEditorDialog *theme_edit_dialog; + ThemeTypeEditor *theme_type_editor; - Panel *main_panel; - MarginContainer *main_container; - Tree *test_tree; + Label *theme_name; + ThemeItemEditorDialog *theme_edit_dialog; + void _theme_save_button_cbk(bool p_save_as); void _theme_edit_button_cbk(); - void _propagate_redraw(Control *p_at); - void _refresh_interval(); + + void _add_preview_button_cbk(); + void _preview_scene_dialog_cbk(const String &p_path); + void _add_preview_tab(ThemeEditorPreview *p_preview_tab, const String &p_preview_name, const Ref<Texture2D> &p_icon); + void _change_preview_tab(int p_tab); + void _remove_preview_tab(int p_tab); + void _remove_preview_tab_invalid(Node *p_tab_control); + void _update_preview_tab(Node *p_tab_control); + void _preview_control_picked(String p_class_name); protected: void _notification(int p_what); - static void _bind_methods(); public: void edit(const Ref<Theme> &p_theme); + Ref<Theme> get_edited_theme(); ThemeEditor(); }; diff --git a/editor/plugins/theme_editor_preview.cpp b/editor/plugins/theme_editor_preview.cpp new file mode 100644 index 0000000000..766f8508c1 --- /dev/null +++ b/editor/plugins/theme_editor_preview.cpp @@ -0,0 +1,464 @@ +/*************************************************************************/ +/* theme_editor_preview.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 "theme_editor_preview.h" + +#include "core/input/input.h" +#include "core/math/math_funcs.h" +#include "scene/resources/packed_scene.h" + +#include "editor/editor_scale.h" + +void ThemeEditorPreview::set_preview_theme(const Ref<Theme> &p_theme) { + preview_content->set_theme(p_theme); +} + +void ThemeEditorPreview::add_preview_overlay(Control *p_overlay) { + preview_overlay->add_child(p_overlay); + p_overlay->hide(); +} + +void ThemeEditorPreview::_propagate_redraw(Control *p_at) { + p_at->notification(NOTIFICATION_THEME_CHANGED); + p_at->minimum_size_changed(); + p_at->update(); + for (int i = 0; i < p_at->get_child_count(); i++) { + Control *a = Object::cast_to<Control>(p_at->get_child(i)); + if (a) { + _propagate_redraw(a); + } + } +} + +void ThemeEditorPreview::_refresh_interval() { + // In case the project settings have changed. + preview_bg->set_color(GLOBAL_GET("rendering/environment/defaults/default_clear_color")); + + _propagate_redraw(preview_bg); + _propagate_redraw(preview_content); +} + +void ThemeEditorPreview::_preview_visibility_changed() { + set_process(is_visible()); +} + +void ThemeEditorPreview::_picker_button_cbk() { + picker_overlay->set_visible(picker_button->is_pressed()); +} + +Control *ThemeEditorPreview::_find_hovered_control(Control *p_parent, Vector2 p_mouse_position) { + Control *found = nullptr; + + for (int i = 0; i < p_parent->get_child_count(); i++) { + Control *cc = Object::cast_to<Control>(p_parent->get_child(i)); + if (!cc || !cc->is_visible()) { + continue; + } + + Rect2 crect = cc->get_rect(); + if (crect.has_point(p_mouse_position)) { + // Check if there is a child control under mouse. + if (cc->get_child_count() > 0) { + found = _find_hovered_control(cc, p_mouse_position - cc->get_position()); + } + + // If there are no applicable children, use the control itself. + if (!found) { + found = cc; + } + break; + } + } + + return found; +} + +void ThemeEditorPreview::_draw_picker_overlay() { + if (!picker_button->is_pressed()) { + return; + } + + picker_overlay->draw_rect(Rect2(Vector2(0.0, 0.0), picker_overlay->get_size()), get_theme_color("preview_picker_overlay_color", "ThemeEditor")); + if (hovered_control) { + Rect2 highlight_rect = hovered_control->get_global_rect(); + highlight_rect.position = picker_overlay->get_global_transform().affine_inverse().xform(highlight_rect.position); + + picker_overlay->draw_style_box(get_theme_stylebox("preview_picker_overlay", "ThemeEditor"), highlight_rect); + } +} + +void ThemeEditorPreview::_gui_input_picker_overlay(const Ref<InputEvent> &p_event) { + if (!picker_button->is_pressed()) { + return; + } + + Ref<InputEventMouseButton> mb = p_event; + + if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) { + if (hovered_control) { + StringName theme_type = hovered_control->get_theme_custom_type(); + if (theme_type == StringName()) { + theme_type = hovered_control->get_class_name(); + } + + emit_signal("control_picked", theme_type); + picker_button->set_pressed(false); + picker_overlay->set_visible(false); + } + } + + Ref<InputEventMouseMotion> mm = p_event; + + if (mm.is_valid()) { + Vector2 mp = preview_content->get_local_mouse_position(); + hovered_control = _find_hovered_control(preview_content, mp); + picker_overlay->update(); + } +} + +void ThemeEditorPreview::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + if (is_visible_in_tree()) { + set_process(true); + } + + connect("visibility_changed", callable_mp(this, &ThemeEditorPreview::_preview_visibility_changed)); + [[fallthrough]]; + } + case NOTIFICATION_THEME_CHANGED: { + picker_button->set_icon(get_theme_icon("ColorPick", "EditorIcons")); + } break; + case NOTIFICATION_PROCESS: { + time_left -= get_process_delta_time(); + if (time_left < 0) { + time_left = 1.5; + _refresh_interval(); + } + } break; + } +} + +void ThemeEditorPreview::_bind_methods() { + ADD_SIGNAL(MethodInfo("control_picked", PropertyInfo(Variant::STRING, "class_name"))); +} + +ThemeEditorPreview::ThemeEditorPreview() { + preview_toolbar = memnew(HBoxContainer); + add_child(preview_toolbar); + + picker_button = memnew(Button); + preview_toolbar->add_child(picker_button); + picker_button->set_flat(true); + picker_button->set_toggle_mode(true); + picker_button->set_tooltip(TTR("Toggle the control picker, allowing to visually select control types for edit.")); + picker_button->connect("pressed", callable_mp(this, &ThemeEditorPreview::_picker_button_cbk)); + + MarginContainer *preview_body = memnew(MarginContainer); + preview_body->set_custom_minimum_size(Size2(480, 0) * EDSCALE); + preview_body->set_v_size_flags(SIZE_EXPAND_FILL); + add_child(preview_body); + + ScrollContainer *preview_container = memnew(ScrollContainer); + preview_container->set_enable_v_scroll(true); + preview_container->set_enable_h_scroll(true); + preview_body->add_child(preview_container); + + MarginContainer *preview_root = memnew(MarginContainer); + preview_container->add_child(preview_root); + preview_root->set_theme(Theme::get_default()); + preview_root->set_clip_contents(true); + preview_root->set_custom_minimum_size(Size2(450, 0) * EDSCALE); + preview_root->set_v_size_flags(SIZE_EXPAND_FILL); + preview_root->set_h_size_flags(SIZE_EXPAND_FILL); + + preview_bg = memnew(ColorRect); + preview_bg->set_anchors_and_offsets_preset(PRESET_WIDE); + preview_bg->set_color(GLOBAL_GET("rendering/environment/defaults/default_clear_color")); + preview_root->add_child(preview_bg); + + preview_content = memnew(MarginContainer); + preview_root->add_child(preview_content); + preview_content->add_theme_constant_override("margin_right", 4 * EDSCALE); + preview_content->add_theme_constant_override("margin_top", 4 * EDSCALE); + preview_content->add_theme_constant_override("margin_left", 4 * EDSCALE); + preview_content->add_theme_constant_override("margin_bottom", 4 * EDSCALE); + + preview_overlay = memnew(MarginContainer); + preview_overlay->set_mouse_filter(MOUSE_FILTER_IGNORE); + preview_body->add_child(preview_overlay); + + picker_overlay = memnew(Control); + add_preview_overlay(picker_overlay); + picker_overlay->connect("draw", callable_mp(this, &ThemeEditorPreview::_draw_picker_overlay)); + picker_overlay->connect("gui_input", callable_mp(this, &ThemeEditorPreview::_gui_input_picker_overlay)); +} + +DefaultThemeEditorPreview::DefaultThemeEditorPreview() { + Panel *main_panel = memnew(Panel); + preview_content->add_child(main_panel); + + MarginContainer *main_mc = memnew(MarginContainer); + main_mc->add_theme_constant_override("margin_right", 4 * EDSCALE); + main_mc->add_theme_constant_override("margin_top", 4 * EDSCALE); + main_mc->add_theme_constant_override("margin_left", 4 * EDSCALE); + main_mc->add_theme_constant_override("margin_bottom", 4 * EDSCALE); + preview_content->add_child(main_mc); + + HBoxContainer *main_hb = memnew(HBoxContainer); + main_mc->add_child(main_hb); + main_hb->add_theme_constant_override("separation", 20 * EDSCALE); + + VBoxContainer *first_vb = memnew(VBoxContainer); + main_hb->add_child(first_vb); + first_vb->set_h_size_flags(SIZE_EXPAND_FILL); + first_vb->add_theme_constant_override("separation", 10 * EDSCALE); + + first_vb->add_child(memnew(Label("Label"))); + + first_vb->add_child(memnew(Button("Button"))); + Button *bt = memnew(Button); + bt->set_text(TTR("Toggle Button")); + bt->set_toggle_mode(true); + bt->set_pressed(true); + first_vb->add_child(bt); + bt = memnew(Button); + bt->set_text(TTR("Disabled Button")); + bt->set_disabled(true); + first_vb->add_child(bt); + Button *tb = memnew(Button); + tb->set_flat(true); + tb->set_text("Button"); + first_vb->add_child(tb); + + CheckButton *cb = memnew(CheckButton); + cb->set_text("CheckButton"); + first_vb->add_child(cb); + CheckBox *cbx = memnew(CheckBox); + cbx->set_text("CheckBox"); + first_vb->add_child(cbx); + + MenuButton *test_menu_button = memnew(MenuButton); + test_menu_button->set_text("MenuButton"); + test_menu_button->get_popup()->add_item(TTR("Item")); + test_menu_button->get_popup()->add_item(TTR("Disabled Item")); + test_menu_button->get_popup()->set_item_disabled(1, true); + test_menu_button->get_popup()->add_separator(); + test_menu_button->get_popup()->add_check_item(TTR("Check Item")); + test_menu_button->get_popup()->add_check_item(TTR("Checked Item")); + test_menu_button->get_popup()->set_item_checked(4, true); + test_menu_button->get_popup()->add_separator(); + test_menu_button->get_popup()->add_radio_check_item(TTR("Radio Item")); + test_menu_button->get_popup()->add_radio_check_item(TTR("Checked Radio Item")); + test_menu_button->get_popup()->set_item_checked(7, true); + test_menu_button->get_popup()->add_separator(TTR("Named Sep.")); + + PopupMenu *test_submenu = memnew(PopupMenu); + test_menu_button->get_popup()->add_child(test_submenu); + test_submenu->set_name("submenu"); + test_menu_button->get_popup()->add_submenu_item(TTR("Submenu"), "submenu"); + test_submenu->add_item(TTR("Subitem 1")); + test_submenu->add_item(TTR("Subitem 2")); + first_vb->add_child(test_menu_button); + + OptionButton *test_option_button = memnew(OptionButton); + test_option_button->add_item("OptionButton"); + test_option_button->add_separator(); + test_option_button->add_item(TTR("Has")); + test_option_button->add_item(TTR("Many")); + test_option_button->add_item(TTR("Options")); + first_vb->add_child(test_option_button); + first_vb->add_child(memnew(ColorPickerButton)); + + VBoxContainer *second_vb = memnew(VBoxContainer); + second_vb->set_h_size_flags(SIZE_EXPAND_FILL); + main_hb->add_child(second_vb); + second_vb->add_theme_constant_override("separation", 10 * EDSCALE); + LineEdit *le = memnew(LineEdit); + le->set_text("LineEdit"); + second_vb->add_child(le); + le = memnew(LineEdit); + le->set_text(TTR("Disabled LineEdit")); + le->set_editable(false); + second_vb->add_child(le); + TextEdit *te = memnew(TextEdit); + te->set_text("TextEdit"); + te->set_custom_minimum_size(Size2(0, 100) * EDSCALE); + second_vb->add_child(te); + second_vb->add_child(memnew(SpinBox)); + + HBoxContainer *vhb = memnew(HBoxContainer); + second_vb->add_child(vhb); + vhb->set_custom_minimum_size(Size2(0, 100) * EDSCALE); + vhb->add_child(memnew(VSlider)); + VScrollBar *vsb = memnew(VScrollBar); + vsb->set_page(25); + vhb->add_child(vsb); + vhb->add_child(memnew(VSeparator)); + VBoxContainer *hvb = memnew(VBoxContainer); + vhb->add_child(hvb); + hvb->set_alignment(BoxContainer::ALIGN_CENTER); + hvb->set_h_size_flags(SIZE_EXPAND_FILL); + hvb->add_child(memnew(HSlider)); + HScrollBar *hsb = memnew(HScrollBar); + hsb->set_page(25); + hvb->add_child(hsb); + HSlider *hs = memnew(HSlider); + hs->set_editable(false); + hvb->add_child(hs); + hvb->add_child(memnew(HSeparator)); + ProgressBar *pb = memnew(ProgressBar); + pb->set_value(50); + hvb->add_child(pb); + + VBoxContainer *third_vb = memnew(VBoxContainer); + third_vb->set_h_size_flags(SIZE_EXPAND_FILL); + third_vb->add_theme_constant_override("separation", 10 * EDSCALE); + main_hb->add_child(third_vb); + + TabContainer *tc = memnew(TabContainer); + third_vb->add_child(tc); + tc->set_custom_minimum_size(Size2(0, 135) * EDSCALE); + Control *tcc = memnew(Control); + tcc->set_name(TTR("Tab 1")); + tc->add_child(tcc); + tcc = memnew(Control); + tcc->set_name(TTR("Tab 2")); + tc->add_child(tcc); + tcc = memnew(Control); + tcc->set_name(TTR("Tab 3")); + tc->add_child(tcc); + tc->set_tab_disabled(2, true); + + Tree *test_tree = memnew(Tree); + third_vb->add_child(test_tree); + test_tree->set_custom_minimum_size(Size2(0, 175) * EDSCALE); + + TreeItem *item = test_tree->create_item(); + item->set_text(0, "Tree"); + item = test_tree->create_item(test_tree->get_root()); + item->set_text(0, "Item"); + item = test_tree->create_item(test_tree->get_root()); + item->set_editable(0, true); + item->set_text(0, TTR("Editable Item")); + TreeItem *sub_tree = test_tree->create_item(test_tree->get_root()); + sub_tree->set_text(0, TTR("Subtree")); + item = test_tree->create_item(sub_tree); + item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); + item->set_editable(0, true); + item->set_text(0, "Check Item"); + item = test_tree->create_item(sub_tree); + item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE); + item->set_editable(0, true); + item->set_range_config(0, 0, 20, 0.1); + item->set_range(0, 2); + item = test_tree->create_item(sub_tree); + item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE); + item->set_editable(0, true); + item->set_text(0, TTR("Has,Many,Options")); + item->set_range(0, 2); +} + +void SceneThemeEditorPreview::_reload_scene() { + if (loaded_scene.is_null()) { + return; + } + + if (loaded_scene->get_path().is_empty() || !ResourceLoader::exists(loaded_scene->get_path())) { + EditorNode::get_singleton()->show_warning(TTR("Invalid path, the PackedScene resource was probably moved or removed.")); + emit_signal("scene_invalidated"); + return; + } + + for (int i = preview_content->get_child_count() - 1; i >= 0; i--) { + Node *node = preview_content->get_child(i); + node->queue_delete(); + preview_content->remove_child(node); + } + + Node *instance = loaded_scene->instance(); + if (!instance || !Object::cast_to<Control>(instance)) { + EditorNode::get_singleton()->show_warning(TTR("Invalid PackedScene resource, must have a Control node at its root.")); + emit_signal("scene_invalidated"); + return; + } + + preview_content->add_child(instance); + emit_signal("scene_reloaded"); +} + +void SceneThemeEditorPreview::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: { + reload_scene_button->set_icon(get_theme_icon("Reload", "EditorIcons")); + } break; + } +} + +void SceneThemeEditorPreview::_bind_methods() { + ADD_SIGNAL(MethodInfo("scene_invalidated")); + ADD_SIGNAL(MethodInfo("scene_reloaded")); +} + +bool SceneThemeEditorPreview::set_preview_scene(const String &p_path) { + loaded_scene = ResourceLoader::load(p_path); + if (loaded_scene.is_null()) { + EditorNode::get_singleton()->show_warning(TTR("Invalid file, not a PackedScene resource.")); + return false; + } + + Node *instance = loaded_scene->instance(); + if (!instance || !Object::cast_to<Control>(instance)) { + EditorNode::get_singleton()->show_warning(TTR("Invalid PackedScene resource, must have a Control node at its root.")); + return false; + } + + preview_content->add_child(instance); + return true; +} + +String SceneThemeEditorPreview::get_preview_scene_path() const { + if (loaded_scene.is_null()) { + return ""; + } + + return loaded_scene->get_path(); +} + +SceneThemeEditorPreview::SceneThemeEditorPreview() { + preview_toolbar->add_child(memnew(VSeparator)); + + reload_scene_button = memnew(Button); + reload_scene_button->set_flat(true); + reload_scene_button->set_tooltip(TTR("Reload the scene to reflect its most actual state.")); + preview_toolbar->add_child(reload_scene_button); + reload_scene_button->connect("pressed", callable_mp(this, &SceneThemeEditorPreview::_reload_scene)); +} diff --git a/editor/plugins/theme_editor_preview.h b/editor/plugins/theme_editor_preview.h new file mode 100644 index 0000000000..efb7e424d4 --- /dev/null +++ b/editor/plugins/theme_editor_preview.h @@ -0,0 +1,118 @@ +/*************************************************************************/ +/* theme_editor_preview.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 THEME_EDITOR_PREVIEW_H +#define THEME_EDITOR_PREVIEW_H + +#include "scene/gui/box_container.h" +#include "scene/gui/check_box.h" +#include "scene/gui/check_button.h" +#include "scene/gui/color_picker.h" +#include "scene/gui/color_rect.h" +#include "scene/gui/label.h" +#include "scene/gui/margin_container.h" +#include "scene/gui/menu_button.h" +#include "scene/gui/option_button.h" +#include "scene/gui/panel.h" +#include "scene/gui/progress_bar.h" +#include "scene/gui/scroll_container.h" +#include "scene/gui/separator.h" +#include "scene/gui/spin_box.h" +#include "scene/gui/tab_container.h" +#include "scene/gui/text_edit.h" +#include "scene/gui/tree.h" +#include "scene/resources/theme.h" + +#include "editor/editor_node.h" + +class ThemeEditorPreview : public VBoxContainer { + GDCLASS(ThemeEditorPreview, VBoxContainer); + + ColorRect *preview_bg; + MarginContainer *preview_overlay; + Control *picker_overlay; + Control *hovered_control = nullptr; + + double time_left = 0; + + void _propagate_redraw(Control *p_at); + void _refresh_interval(); + void _preview_visibility_changed(); + + void _picker_button_cbk(); + Control *_find_hovered_control(Control *p_parent, Vector2 p_mouse_position); + + void _draw_picker_overlay(); + void _gui_input_picker_overlay(const Ref<InputEvent> &p_event); + +protected: + HBoxContainer *preview_toolbar; + MarginContainer *preview_content; + Button *picker_button; + + void add_preview_overlay(Control *p_overlay); + + void _notification(int p_what); + static void _bind_methods(); + +public: + void set_preview_theme(const Ref<Theme> &p_theme); + + ThemeEditorPreview(); +}; + +class DefaultThemeEditorPreview : public ThemeEditorPreview { + GDCLASS(DefaultThemeEditorPreview, ThemeEditorPreview); + +public: + DefaultThemeEditorPreview(); +}; + +class SceneThemeEditorPreview : public ThemeEditorPreview { + GDCLASS(SceneThemeEditorPreview, ThemeEditorPreview); + + Ref<PackedScene> loaded_scene; + + Button *reload_scene_button; + + void _reload_scene(); + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + bool set_preview_scene(const String &p_path); + String get_preview_scene_path() const; + + SceneThemeEditorPreview(); +}; + +#endif // THEME_EDITOR_PREVIEW_H diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp index 6078c986cb..11842981f3 100644 --- a/editor/plugins/tiles/tile_set_editor.cpp +++ b/editor/plugins/tiles/tile_set_editor.cpp @@ -436,8 +436,8 @@ void TileSetEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p } void TileSetEditor::_bind_methods() { - ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &TileSetEditor::can_drop_data_fw); - ClassDB::bind_method(D_METHOD("drop_data_fw"), &TileSetEditor::drop_data_fw); + ClassDB::bind_method(D_METHOD("_can_drop_data_fw"), &TileSetEditor::can_drop_data_fw); + ClassDB::bind_method(D_METHOD("_drop_data_fw"), &TileSetEditor::drop_data_fw); } TileDataEditor *TileSetEditor::get_tile_data_editor(String p_property) { diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 7ef5e9e8ce..9217f2ac4c 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -44,6 +44,7 @@ #include "scene/gui/panel.h" #include "scene/main/window.h" #include "scene/resources/visual_shader_nodes.h" +#include "scene/resources/visual_shader_particle_nodes.h" #include "scene/resources/visual_shader_sdf_nodes.h" #include "servers/display_server.h" #include "servers/rendering/shader_types.h" @@ -71,13 +72,13 @@ const int MAX_FLOAT_CONST_DEFS = sizeof(float_constant_defs) / sizeof(FloatConst Control *VisualShaderNodePlugin::create_editor(const Ref<Resource> &p_parent_resource, const Ref<VisualShaderNode> &p_node) { if (get_script_instance()) { - return get_script_instance()->call("create_editor", p_parent_resource, p_node); + return get_script_instance()->call("_create_editor", p_parent_resource, p_node); } return nullptr; } void VisualShaderNodePlugin::_bind_methods() { - BIND_VMETHOD(MethodInfo(Variant::OBJECT, "create_editor", PropertyInfo(Variant::OBJECT, "parent_resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource"), PropertyInfo(Variant::OBJECT, "for_node", PROPERTY_HINT_RESOURCE_TYPE, "VisualShaderNode"))); + BIND_VMETHOD(MethodInfo(Variant::OBJECT, "_create_editor", PropertyInfo(Variant::OBJECT, "parent_resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource"), PropertyInfo(Variant::OBJECT, "for_node", PROPERTY_HINT_RESOURCE_TYPE, "VisualShaderNode"))); } /////////////////// @@ -417,6 +418,11 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) { } } + Ref<VisualShaderNodeParticleEmit> emit = vsnode; + if (emit.is_valid()) { + node->set_custom_minimum_size(Size2(200 * EDSCALE, 0)); + } + Ref<VisualShaderNodeUniform> uniform = vsnode; if (uniform.is_valid()) { VisualShaderEditor::get_singleton()->graph->add_child(node); @@ -1013,13 +1019,13 @@ bool VisualShaderEditor::_is_available(int p_mode) { if (p_mode != -1) { switch (current_mode) { - case 0: // Vertex or Emit + case 0: // Vertex / Emit current_mode = 1; break; - case 1: // Fragment or Process + case 1: // Fragment / Process current_mode = 2; break; - case 2: // Light or End + case 2: // Light / Collide current_mode = 4; break; default: @@ -1235,22 +1241,29 @@ void VisualShaderEditor::_update_options_menu() { void VisualShaderEditor::_set_mode(int p_which) { if (p_which == VisualShader::MODE_SKY) { - edit_type_standart->set_visible(false); + edit_type_standard->set_visible(false); edit_type_particles->set_visible(false); edit_type_sky->set_visible(true); edit_type = edit_type_sky; + custom_mode_box->set_visible(false); mode = MODE_FLAGS_SKY; } else if (p_which == VisualShader::MODE_PARTICLES) { - edit_type_standart->set_visible(false); + edit_type_standard->set_visible(false); edit_type_particles->set_visible(true); edit_type_sky->set_visible(false); edit_type = edit_type_particles; + if ((edit_type->get_selected() + 3) > VisualShader::TYPE_PROCESS) { + custom_mode_box->set_visible(false); + } else { + custom_mode_box->set_visible(true); + } mode = MODE_FLAGS_PARTICLES; } else { edit_type_particles->set_visible(false); - edit_type_standart->set_visible(true); + edit_type_standard->set_visible(true); edit_type_sky->set_visible(false); - edit_type = edit_type_standart; + edit_type = edit_type_standard; + custom_mode_box->set_visible(false); mode = MODE_FLAGS_SPATIAL_CANVASITEM; } visual_shader->set_shader_type(get_current_shader_type()); @@ -1403,9 +1416,9 @@ void VisualShaderEditor::_update_graph() { VisualShader::Type VisualShaderEditor::get_current_shader_type() const { VisualShader::Type type; if (mode & MODE_FLAGS_PARTICLES) { - type = VisualShader::Type(edit_type->get_selected() + 3); + type = VisualShader::Type(edit_type->get_selected() + 3 + (custom_mode_enabled ? 3 : 0)); } else if (mode & MODE_FLAGS_SKY) { - type = VisualShader::Type(edit_type->get_selected() + 6); + type = VisualShader::Type(edit_type->get_selected() + 8); } else { type = VisualShader::Type(edit_type->get_selected()); } @@ -3224,8 +3237,18 @@ void VisualShaderEditor::_mode_selected(int p_id) { int offset = 0; if (mode & MODE_FLAGS_PARTICLES) { offset = 3; + if (p_id + offset > VisualShader::TYPE_PROCESS) { + custom_mode_box->set_visible(false); + custom_mode_enabled = false; + } else { + custom_mode_box->set_visible(true); + if (custom_mode_box->is_pressed()) { + custom_mode_enabled = true; + offset += 3; + } + } } else if (mode & MODE_FLAGS_SKY) { - offset = 6; + offset = 8; } visual_shader->set_shader_type(VisualShader::Type(p_id + offset)); @@ -3233,6 +3256,21 @@ void VisualShaderEditor::_mode_selected(int p_id) { _update_graph(); } +void VisualShaderEditor::_custom_mode_toggled(bool p_enabled) { + if (!(mode & MODE_FLAGS_PARTICLES)) { + return; + } + custom_mode_enabled = p_enabled; + int id = edit_type->get_selected() + 3; + if (p_enabled) { + visual_shader->set_shader_type(VisualShader::Type(id + 3)); + } else { + visual_shader->set_shader_type(VisualShader::Type(id)); + } + _update_options_menu(); + _update_graph(); +} + void VisualShaderEditor::_input_select_item(Ref<VisualShaderNodeInput> p_input, String p_name) { String prev_name = p_input->get_input_name(); @@ -3656,9 +3694,9 @@ void VisualShaderEditor::_bind_methods() { ClassDB::bind_method("_update_uniform", &VisualShaderEditor::_update_uniform); ClassDB::bind_method("_expand_output_port", &VisualShaderEditor::_expand_output_port); - ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &VisualShaderEditor::get_drag_data_fw); - ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &VisualShaderEditor::can_drop_data_fw); - ClassDB::bind_method(D_METHOD("drop_data_fw"), &VisualShaderEditor::drop_data_fw); + ClassDB::bind_method(D_METHOD("_get_drag_data_fw"), &VisualShaderEditor::get_drag_data_fw); + ClassDB::bind_method(D_METHOD("_can_drop_data_fw"), &VisualShaderEditor::can_drop_data_fw); + ClassDB::bind_method(D_METHOD("_drop_data_fw"), &VisualShaderEditor::drop_data_fw); ClassDB::bind_method("_is_available", &VisualShaderEditor::_is_available); } @@ -3731,17 +3769,23 @@ VisualShaderEditor::VisualShaderEditor() { graph->get_zoom_hbox()->add_child(vs); graph->get_zoom_hbox()->move_child(vs, 0); - edit_type_standart = memnew(OptionButton); - edit_type_standart->add_item(TTR("Vertex")); - edit_type_standart->add_item(TTR("Fragment")); - edit_type_standart->add_item(TTR("Light")); - edit_type_standart->select(1); - edit_type_standart->connect("item_selected", callable_mp(this, &VisualShaderEditor::_mode_selected)); + custom_mode_box = memnew(CheckBox); + custom_mode_box->set_text(TTR("Custom")); + custom_mode_box->set_pressed(false); + custom_mode_box->set_visible(false); + custom_mode_box->connect("toggled", callable_mp(this, &VisualShaderEditor::_custom_mode_toggled)); + + edit_type_standard = memnew(OptionButton); + edit_type_standard->add_item(TTR("Vertex")); + edit_type_standard->add_item(TTR("Fragment")); + edit_type_standard->add_item(TTR("Light")); + edit_type_standard->select(1); + edit_type_standard->connect("item_selected", callable_mp(this, &VisualShaderEditor::_mode_selected)); edit_type_particles = memnew(OptionButton); - edit_type_particles->add_item(TTR("Emit")); + edit_type_particles->add_item(TTR("Start")); edit_type_particles->add_item(TTR("Process")); - edit_type_particles->add_item(TTR("End")); + edit_type_particles->add_item(TTR("Collide")); edit_type_particles->select(0); edit_type_particles->connect("item_selected", callable_mp(this, &VisualShaderEditor::_mode_selected)); @@ -3750,14 +3794,16 @@ VisualShaderEditor::VisualShaderEditor() { edit_type_sky->select(0); edit_type_sky->connect("item_selected", callable_mp(this, &VisualShaderEditor::_mode_selected)); - edit_type = edit_type_standart; + edit_type = edit_type_standard; + graph->get_zoom_hbox()->add_child(custom_mode_box); + graph->get_zoom_hbox()->move_child(custom_mode_box, 0); + graph->get_zoom_hbox()->add_child(edit_type_standard); + graph->get_zoom_hbox()->move_child(edit_type_standard, 0); graph->get_zoom_hbox()->add_child(edit_type_particles); graph->get_zoom_hbox()->move_child(edit_type_particles, 0); graph->get_zoom_hbox()->add_child(edit_type_sky); graph->get_zoom_hbox()->move_child(edit_type_sky, 0); - graph->get_zoom_hbox()->add_child(edit_type_standart); - graph->get_zoom_hbox()->move_child(edit_type_standart, 0); add_node = memnew(Button); add_node->set_flat(true); @@ -3977,9 +4023,10 @@ VisualShaderEditor::VisualShaderEditor() { // INPUT + const String input_param_shader_modes = TTR("'%s' input parameter for all shader modes."); + // SPATIAL-FOR-ALL - const String input_param_shader_modes = TTR("'%s' input parameter for all shader modes."); add_options.push_back(AddOption("Camera", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "camera"), "camera", VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("InvCamera", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "inv_camera"), "inv_camera", VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("InvProjection", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "inv_projection"), "inv_projection", VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_SPATIAL)); @@ -3998,6 +4045,23 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("Time", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "time"), "time", VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_CANVAS_ITEM)); add_options.push_back(AddOption("UV", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "uv"), "uv", VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_CANVAS_ITEM)); + // PARTICLES-FOR-ALL + + add_options.push_back(AddOption("Active", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "active"), "active", VisualShaderNode::PORT_TYPE_BOOLEAN, -1, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("Alpha", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "alpha"), "alpha", VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("AttractorForce", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "attractor_force"), "attractor_force", VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("Color", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "color"), "color", VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("Custom", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "custom"), "custom", VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("CustomAlpha", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "custom_alpha"), "custom_alpha", VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("Delta", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "delta"), "delta", VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("EmissionTransform", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "emission_transform"), "emission_transform", VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("Index", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "index"), "index", VisualShaderNode::PORT_TYPE_SCALAR_INT, -1, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("LifeTime", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "lifetime"), "lifetime", VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("Restart", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "restart"), "restart", VisualShaderNode::PORT_TYPE_BOOLEAN, -1, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("Time", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "time"), "time", VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("Transform", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "transform"), "transform", VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("Velocity", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "velocity"), "velocity", VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_PARTICLES)); + ///////////////// add_options.push_back(AddOption("Input", "Input", "Common", "VisualShaderNodeInput", TTR("Input parameter."))); @@ -4010,11 +4074,12 @@ VisualShaderEditor::VisualShaderEditor() { const String input_param_for_sky_shader_mode = TTR("'%s' input parameter for sky shader mode."); const String input_param_for_light_shader_mode = TTR("'%s' input parameter for light shader mode."); const String input_param_for_vertex_shader_mode = TTR("'%s' input parameter for vertex shader mode."); - const String input_param_for_emit_shader_mode = TTR("'%s' input parameter for emit shader mode."); + const String input_param_for_start_shader_mode = TTR("'%s' input parameter for start shader mode."); const String input_param_for_process_shader_mode = TTR("'%s' input parameter for process shader mode."); - const String input_param_for_end_shader_mode = TTR("'%s' input parameter for end shader mode."); - const String input_param_for_emit_and_process_shader_mode = TTR("'%s' input parameter for emit and process shader mode."); - const String input_param_for_vertex_and_fragment_shader_mode = TTR("'%s' input parameter for vertex and fragment shader mode."); + const String input_param_for_collide_shader_mode = TTR("'%s' input parameter for collide shader mode."); + const String input_param_for_start_and_process_shader_mode = TTR("'%s' input parameter for start and process shader modes."); + const String input_param_for_process_and_collide_shader_mode = TTR("'%s' input parameter for process and collide shader modes."); + const String input_param_for_vertex_and_fragment_shader_mode = TTR("'%s' input parameter for vertex and fragment shader modes."); add_options.push_back(AddOption("Alpha", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "alpha"), "alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("Binormal", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "binormal"), "binormal", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); @@ -4091,50 +4156,6 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("Vertex", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "vertex"), "vertex", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM)); add_options.push_back(AddOption("World", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "world"), "world", VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM)); - // PARTICLES INPUTS - - add_options.push_back(AddOption("Active", "Input", "Emit", "VisualShaderNodeInput", vformat(input_param_shader_modes, "active"), "active", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Alpha", "Input", "Emit", "VisualShaderNodeInput", vformat(input_param_shader_modes, "alpha"), "alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Color", "Input", "Emit", "VisualShaderNodeInput", vformat(input_param_shader_modes, "color"), "color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Custom", "Input", "Emit", "VisualShaderNodeInput", vformat(input_param_shader_modes, "custom"), "custom", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("CustomAlpha", "Input", "Emit", "VisualShaderNodeInput", vformat(input_param_shader_modes, "custom_alpha"), "custom_alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Delta", "Input", "Emit", "VisualShaderNodeInput", vformat(input_param_shader_modes, "delta"), "delta", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("EmissionTransform", "Input", "Emit", "VisualShaderNodeInput", vformat(input_param_shader_modes, "emission_transform"), "emission_transform", VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Index", "Input", "Emit", "VisualShaderNodeInput", vformat(input_param_shader_modes, "index"), "index", VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("LifeTime", "Input", "Emit", "VisualShaderNodeInput", vformat(input_param_shader_modes, "lifetime"), "lifetime", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Restart", "Input", "Emit", "VisualShaderNodeInput", vformat(input_param_shader_modes, "restart"), "restart", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Time", "Input", "Emit", "VisualShaderNodeInput", vformat(input_param_shader_modes, "time"), "time", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Transform", "Input", "Emit", "VisualShaderNodeInput", vformat(input_param_shader_modes, "transform"), "transform", VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Velocity", "Input", "Emit", "VisualShaderNodeInput", vformat(input_param_shader_modes, "velocity"), "velocity", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES)); - - add_options.push_back(AddOption("Active", "Input", "Process", "VisualShaderNodeInput", vformat(input_param_shader_modes, "active"), "active", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Alpha", "Input", "Process", "VisualShaderNodeInput", vformat(input_param_shader_modes, "alpha"), "alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Color", "Input", "Process", "VisualShaderNodeInput", vformat(input_param_shader_modes, "color"), "color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Custom", "Input", "Process", "VisualShaderNodeInput", vformat(input_param_shader_modes, "custom"), "custom", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("CustomAlpha", "Input", "Process", "VisualShaderNodeInput", vformat(input_param_shader_modes, "custom_alpha"), "custom_alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Delta", "Input", "Process", "VisualShaderNodeInput", vformat(input_param_shader_modes, "delta"), "delta", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("EmissionTransform", "Input", "Process", "VisualShaderNodeInput", vformat(input_param_shader_modes, "emission_transform"), "emission_transform", VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Index", "Input", "Process", "VisualShaderNodeInput", vformat(input_param_shader_modes, "index"), "index", VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("LifeTime", "Input", "Process", "VisualShaderNodeInput", vformat(input_param_shader_modes, "lifetime"), "lifetime", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Restart", "Input", "Process", "VisualShaderNodeInput", vformat(input_param_shader_modes, "restart"), "restart", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Time", "Input", "Process", "VisualShaderNodeInput", vformat(input_param_shader_modes, "time"), "time", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Transform", "Input", "Process", "VisualShaderNodeInput", vformat(input_param_shader_modes, "transform"), "transform", VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Velocity", "Input", "Process", "VisualShaderNodeInput", vformat(input_param_shader_modes, "velocity"), "velocity", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES)); - - add_options.push_back(AddOption("Active", "Input", "End", "VisualShaderNodeInput", vformat(input_param_shader_modes, "active"), "active", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_END, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Alpha", "Input", "End", "VisualShaderNodeInput", vformat(input_param_shader_modes, "alpha"), "alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_END, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Color", "Input", "End", "VisualShaderNodeInput", vformat(input_param_shader_modes, "color"), "color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_END, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Custom", "Input", "End", "VisualShaderNodeInput", vformat(input_param_shader_modes, "custom"), "custom", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_END, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("CustomAlpha", "Input", "End", "VisualShaderNodeInput", vformat(input_param_shader_modes, "custom_alpha"), "custom_alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_END, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Delta", "Input", "End", "VisualShaderNodeInput", vformat(input_param_shader_modes, "delta"), "delta", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_END, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("EmissionTransform", "Input", "End", "VisualShaderNodeInput", vformat(input_param_shader_modes, "emission_transform"), "emission_transform", VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_END, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Index", "Input", "End", "VisualShaderNodeInput", vformat(input_param_shader_modes, "index"), "index", VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_END, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("LifeTime", "Input", "End", "VisualShaderNodeInput", vformat(input_param_shader_modes, "lifetime"), "lifetime", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_END, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Restart", "Input", "End", "VisualShaderNodeInput", vformat(input_param_shader_modes, "restart"), "restart", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_END, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Time", "Input", "End", "VisualShaderNodeInput", vformat(input_param_shader_modes, "time"), "time", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_END, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Transform", "Input", "End", "VisualShaderNodeInput", vformat(input_param_shader_modes, "transform"), "transform", VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_END, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Velocity", "Input", "End", "VisualShaderNodeInput", vformat(input_param_shader_modes, "velocity"), "velocity", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_END, Shader::MODE_PARTICLES)); - // SKY INPUTS add_options.push_back(AddOption("AtCubeMapPass", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "at_cubemap_pass"), "at_cubemap_pass", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY)); @@ -4167,6 +4188,22 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("SkyCoords", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "sky_coords"), "sky_coords", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY)); add_options.push_back(AddOption("Time", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "time"), "time", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + // PARTICLES + + add_options.push_back(AddOption("CollisionDepth", "Input", "Collide", "VisualShaderNodeInput", vformat(input_param_for_collide_shader_mode, "collision_depth"), "collision_depth", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("CollisionNormal", "Input", "Collide", "VisualShaderNodeInput", vformat(input_param_for_collide_shader_mode, "collision_normal"), "collision_normal", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES)); + + add_options.push_back(AddOption("EmitParticle", "Particles", "", "VisualShaderNodeParticleEmit", "", -1, -1, -1, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("ParticleAccelerator", "Particles", "", "VisualShaderNodeParticleAccelerator", "", -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("ParticleRandomness", "Particles", "", "VisualShaderNodeParticleRandomness", "", -1, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_EMIT | TYPE_FLAGS_PROCESS | TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("MultiplyByAxisAngle", "Particles", "Transform", "VisualShaderNodeParticleMultiplyByAxisAngle", "A node for help to multiply a position input vector by rotation using specific axis. Intended to work with emitters.", -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT | TYPE_FLAGS_PROCESS | TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES)); + + add_options.push_back(AddOption("BoxEmitter", "Particles", "Emitters", "VisualShaderNodeParticleBoxEmitter", "", -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("RingEmitter", "Particles", "Emitters", "VisualShaderNodeParticleRingEmitter", "", -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("SphereEmitter", "Particles", "Emitters", "VisualShaderNodeParticleSphereEmitter", "", -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES)); + + add_options.push_back(AddOption("ConeVelocity", "Particles", "Velocity", "VisualShaderNodeParticleConeVelocity", "", -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES)); + // SCALAR add_options.push_back(AddOption("FloatFunc", "Scalar", "Common", "VisualShaderNodeFloatFunc", TTR("Float function."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h index f12d05f7ed..fa98c6b780 100644 --- a/editor/plugins/visual_shader_editor_plugin.h +++ b/editor/plugins/visual_shader_editor_plugin.h @@ -41,8 +41,8 @@ #include "scene/gui/tree.h" #include "scene/resources/visual_shader.h" -class VisualShaderNodePlugin : public Reference { - GDCLASS(VisualShaderNodePlugin, Reference); +class VisualShaderNodePlugin : public RefCounted { + GDCLASS(VisualShaderNodePlugin, RefCounted); protected: static void _bind_methods(); @@ -51,8 +51,8 @@ public: virtual Control *create_editor(const Ref<Resource> &p_parent_resource, const Ref<VisualShaderNode> &p_node); }; -class VisualShaderGraphPlugin : public Reference { - GDCLASS(VisualShaderGraphPlugin, Reference); +class VisualShaderGraphPlugin : public RefCounted { + GDCLASS(VisualShaderGraphPlugin, RefCounted); private: struct InputPort { @@ -142,9 +142,11 @@ class VisualShaderEditor : public VBoxContainer { Button *preview_shader; OptionButton *edit_type = nullptr; - OptionButton *edit_type_standart; + OptionButton *edit_type_standard; OptionButton *edit_type_particles; OptionButton *edit_type_sky; + CheckBox *custom_mode_box; + bool custom_mode_enabled = false; bool pending_update_preview; bool shader_error; @@ -189,7 +191,9 @@ class VisualShaderEditor : public VBoxContainer { enum ParticlesTypeFlags { TYPE_FLAGS_EMIT = 1, TYPE_FLAGS_PROCESS = 2, - TYPE_FLAGS_END = 4 + TYPE_FLAGS_COLLIDE = 4, + TYPE_FLAGS_EMIT_CUSTOM = 8, + TYPE_FLAGS_PROCESS_CUSTOM = 16, }; enum SkyTypeFlags { @@ -385,6 +389,7 @@ class VisualShaderEditor : public VBoxContainer { Ref<VisualShaderGraphPlugin> graph_plugin; void _mode_selected(int p_id); + void _custom_mode_toggled(bool p_enabled); void _input_select_item(Ref<VisualShaderNodeInput> input, String name); void _uniform_select_item(Ref<VisualShaderNodeUniformRef> p_uniform, String p_name); |