diff options
Diffstat (limited to 'editor/plugins')
29 files changed, 1799 insertions, 594 deletions
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index e459d2f756..bd88d96a66 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -994,7 +994,7 @@ void AnimationPlayerEditor::_animation_duplicate() { } } -void AnimationPlayerEditor::_seek_value_changed(float p_value, bool p_set) { +void AnimationPlayerEditor::_seek_value_changed(float p_value, bool p_set, bool p_timeline_only) { if (updating || !player || player->is_playing()) { return; }; @@ -1015,18 +1015,18 @@ void AnimationPlayerEditor::_seek_value_changed(float p_value, bool p_set) { pos = Math::snapped(pos, _get_editor_step()); } - if (player->is_valid() && !p_set) { - float cpos = player->get_current_animation_position(); + if (!p_timeline_only) { + if (player->is_valid() && !p_set) { + float cpos = player->get_current_animation_position(); - player->seek_delta(pos, pos - cpos); - } else { - player->stop(true); - player->seek(pos, true); + player->seek_delta(pos, pos - cpos); + } else { + player->stop(true); + player->seek(pos, true); + } } track_editor->set_anim_pos(pos); - - updating = true; }; void AnimationPlayerEditor::_animation_player_changed(Object *p_pl) { @@ -1048,7 +1048,7 @@ void AnimationPlayerEditor::_animation_key_editor_anim_len_changed(float p_len) frame->set_max(p_len); } -void AnimationPlayerEditor::_animation_key_editor_seek(float p_pos, bool p_drag) { +void AnimationPlayerEditor::_animation_key_editor_seek(float p_pos, bool p_drag, bool p_timeline_only) { timeline_position = p_pos; if (!is_visible_in_tree()) { @@ -1070,7 +1070,7 @@ void AnimationPlayerEditor::_animation_key_editor_seek(float p_pos, bool p_drag) updating = true; frame->set_value(Math::snapped(p_pos, _get_editor_step())); updating = false; - _seek_value_changed(p_pos, !p_drag); + _seek_value_changed(p_pos, !p_drag, p_timeline_only); } void AnimationPlayerEditor::_animation_tool_menu(int p_option) { @@ -1693,7 +1693,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay animation->connect("item_selected", callable_mp(this, &AnimationPlayerEditor::_animation_selected)); file->connect("file_selected", callable_mp(this, &AnimationPlayerEditor::_dialog_action)); - frame->connect("value_changed", callable_mp(this, &AnimationPlayerEditor::_seek_value_changed), make_binds(true)); + frame->connect("value_changed", callable_mp(this, &AnimationPlayerEditor::_seek_value_changed), make_binds(true, false)); scale->connect("text_entered", callable_mp(this, &AnimationPlayerEditor::_scale_changed)); renaming = false; diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h index 2f6bf55e4c..5c2348f86b 100644 --- a/editor/plugins/animation_player_editor_plugin.h +++ b/editor/plugins/animation_player_editor_plugin.h @@ -187,7 +187,7 @@ class AnimationPlayerEditor : public VBoxContainer { void _scale_changed(const String &p_scale); void _dialog_action(String p_file); void _seek_frame_changed(const String &p_frame); - void _seek_value_changed(float p_value, bool p_set = false); + void _seek_value_changed(float p_value, bool p_set = false, bool p_timeline_only = false); void _blend_editor_next_changed(const int p_idx); void _list_changed(); @@ -197,7 +197,7 @@ class AnimationPlayerEditor : public VBoxContainer { void _animation_player_changed(Object *p_pl); - void _animation_key_editor_seek(float p_pos, bool p_drag); + void _animation_key_editor_seek(float p_pos, bool p_drag, bool p_timeline_only = false); void _animation_key_editor_anim_len_changed(float p_len); void _unhandled_key_input(const Ref<InputEvent> &p_ev); diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index a0d9afee74..93bb170128 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -464,7 +464,7 @@ void EditorAssetLibraryItemDownload::_make_request() { retry->hide(); download->cancel_request(); - download->set_download_file(EditorSettings::get_singleton()->get_cache_dir().plus_file("tmp_asset_" + itos(asset_id)) + ".zip"); + download->set_download_file(EditorPaths::get_singleton()->get_cache_dir().plus_file("tmp_asset_" + itos(asset_id)) + ".zip"); Error err = download->request(host); if (err != OK) { @@ -702,7 +702,7 @@ void EditorAssetLibrary::_image_update(bool use_cache, bool final, const PackedB PackedByteArray image_data = p_data; if (use_cache) { - String cache_filename_base = EditorSettings::get_singleton()->get_cache_dir().plus_file("assetimage_" + image_queue[p_queue_id].image_url.md5_text()); + String cache_filename_base = EditorPaths::get_singleton()->get_cache_dir().plus_file("assetimage_" + image_queue[p_queue_id].image_url.md5_text()); FileAccess *file = FileAccess::open(cache_filename_base + ".data", FileAccess::READ); @@ -781,7 +781,7 @@ void EditorAssetLibrary::_image_request_completed(int p_status, int p_code, cons if (p_code != HTTPClient::RESPONSE_NOT_MODIFIED) { for (int i = 0; i < headers.size(); i++) { if (headers[i].findn("ETag:") == 0) { // Save etag - String cache_filename_base = EditorSettings::get_singleton()->get_cache_dir().plus_file("assetimage_" + image_queue[p_queue_id].image_url.md5_text()); + String cache_filename_base = EditorPaths::get_singleton()->get_cache_dir().plus_file("assetimage_" + image_queue[p_queue_id].image_url.md5_text()); String new_etag = headers[i].substr(headers[i].find(":") + 1, headers[i].length()).strip_edges(); FileAccess *file; @@ -829,7 +829,7 @@ void EditorAssetLibrary::_update_image_queue() { List<int> to_delete; for (Map<int, ImageQueue>::Element *E = image_queue.front(); E; E = E->next()) { if (!E->get().active && current_images < max_images) { - String cache_filename_base = EditorSettings::get_singleton()->get_cache_dir().plus_file("assetimage_" + E->get().image_url.md5_text()); + String cache_filename_base = EditorPaths::get_singleton()->get_cache_dir().plus_file("assetimage_" + E->get().image_url.md5_text()); Vector<String> headers; if (FileAccess::exists(cache_filename_base + ".etag") && FileAccess::exists(cache_filename_base + ".data")) { diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 21bff41498..2be586733b 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -4380,7 +4380,7 @@ void CanvasItemEditor::_update_scrollbars() { // Calculate scrollable area. Rect2 canvas_item_rect = Rect2(Point2(), screen_rect); - if (editor->get_edited_scene()) { + if (editor->is_inside_tree() && editor->get_edited_scene()) { Rect2 content_rect = _get_encompassing_rect(editor->get_edited_scene()); canvas_item_rect.expand_to(content_rect.position); canvas_item_rect.expand_to(content_rect.position + content_rect.size); @@ -5361,8 +5361,10 @@ void CanvasItemEditor::_bind_methods() { ClassDB::bind_method("_queue_update_bone_list", &CanvasItemEditor::_update_bone_list); ClassDB::bind_method("_update_bone_list", &CanvasItemEditor::_update_bone_list); ClassDB::bind_method("_reset_create_position", &CanvasItemEditor::_reset_create_position); + ClassDB::bind_method(D_METHOD("get_state"), &CanvasItemEditor::get_state); ClassDB::bind_method(D_METHOD("set_state"), &CanvasItemEditor::set_state); ClassDB::bind_method(D_METHOD("update_viewport"), &CanvasItemEditor::update_viewport); + ClassDB::bind_method(D_METHOD("_zoom_on_position"), &CanvasItemEditor::_zoom_on_position); ADD_SIGNAL(MethodInfo("item_lock_status_changed")); ADD_SIGNAL(MethodInfo("item_group_status_changed")); diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index 18cc5d43fb..2d79e4f3e3 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -265,7 +265,7 @@ Ref<Texture2D> EditorPackedScenePreviewPlugin::generate(const RES &p_from, const } Ref<Texture2D> EditorPackedScenePreviewPlugin::generate_from_path(const String &p_path, const Size2 &p_size) const { - String temp_path = EditorSettings::get_singleton()->get_cache_dir(); + String temp_path = EditorPaths::get_singleton()->get_cache_dir(); String cache_base = ProjectSettings::get_singleton()->globalize_path(p_path).md5_text(); cache_base = temp_path.plus_file("resthumb-" + cache_base); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 641d823d8f..ba39ce3aed 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -85,10 +85,10 @@ void ViewportRotationControl::_notification(int p_what) { axis_menu_options.clear(); axis_menu_options.push_back(Node3DEditorViewport::VIEW_RIGHT); axis_menu_options.push_back(Node3DEditorViewport::VIEW_TOP); - axis_menu_options.push_back(Node3DEditorViewport::VIEW_FRONT); + axis_menu_options.push_back(Node3DEditorViewport::VIEW_REAR); axis_menu_options.push_back(Node3DEditorViewport::VIEW_LEFT); axis_menu_options.push_back(Node3DEditorViewport::VIEW_BOTTOM); - axis_menu_options.push_back(Node3DEditorViewport::VIEW_REAR); + axis_menu_options.push_back(Node3DEditorViewport::VIEW_FRONT); axis_colors.clear(); axis_colors.push_back(get_theme_color("axis_x_color", "Editor")); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 1147267ce0..623e4ea66e 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -1504,8 +1504,10 @@ void ScriptEditor::_notification(int p_what) { recent_scripts->set_as_minsize(); - _update_script_colors(); - _update_script_names(); + if (is_inside_tree()) { + _update_script_colors(); + _update_script_names(); + } } break; case NOTIFICATION_READY: { diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 25755cc6cc..a29e51e8fb 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -170,66 +170,25 @@ void ScriptTextEditor::_load_theme_settings() { CodeEdit *text_edit = code_editor->get_text_editor(); text_edit->clear_keywords(); + Color updated_marked_line_color = EDITOR_GET("text_editor/highlighting/mark_color"); Color updated_safe_line_number_color = EDITOR_GET("text_editor/highlighting/safe_line_number_color"); - if (updated_safe_line_number_color != safe_line_number_color) { + + bool safe_line_number_color_updated = updated_safe_line_number_color != safe_line_number_color; + bool marked_line_color_updated = updated_marked_line_color != marked_line_color; + if (safe_line_number_color_updated || marked_line_color_updated) { safe_line_number_color = updated_safe_line_number_color; for (int i = 0; i < text_edit->get_line_count(); i++) { - if (text_edit->get_line_gutter_item_color(i, line_number_gutter) != default_line_number_color) { + if (marked_line_color_updated && text_edit->get_line_background_color(i) == marked_line_color) { + text_edit->set_line_background_color(i, updated_marked_line_color); + } + + if (safe_line_number_color_updated && text_edit->get_line_gutter_item_color(i, line_number_gutter) != default_line_number_color) { text_edit->set_line_gutter_item_color(i, line_number_gutter, safe_line_number_color); } } + marked_line_color = updated_marked_line_color; } - Color background_color = EDITOR_GET("text_editor/highlighting/background_color"); - Color completion_background_color = EDITOR_GET("text_editor/highlighting/completion_background_color"); - Color completion_selected_color = EDITOR_GET("text_editor/highlighting/completion_selected_color"); - Color completion_existing_color = EDITOR_GET("text_editor/highlighting/completion_existing_color"); - Color completion_scroll_color = EDITOR_GET("text_editor/highlighting/completion_scroll_color"); - Color completion_font_color = EDITOR_GET("text_editor/highlighting/completion_font_color"); - Color text_color = EDITOR_GET("text_editor/highlighting/text_color"); - Color line_number_color = EDITOR_GET("text_editor/highlighting/line_number_color"); - Color caret_color = EDITOR_GET("text_editor/highlighting/caret_color"); - Color caret_background_color = EDITOR_GET("text_editor/highlighting/caret_background_color"); - Color text_selected_color = EDITOR_GET("text_editor/highlighting/text_selected_color"); - Color selection_color = EDITOR_GET("text_editor/highlighting/selection_color"); - Color brace_mismatch_color = EDITOR_GET("text_editor/highlighting/brace_mismatch_color"); - Color current_line_color = EDITOR_GET("text_editor/highlighting/current_line_color"); - Color line_length_guideline_color = EDITOR_GET("text_editor/highlighting/line_length_guideline_color"); - Color word_highlighted_color = EDITOR_GET("text_editor/highlighting/word_highlighted_color"); - Color mark_color = EDITOR_GET("text_editor/highlighting/mark_color"); - Color bookmark_color = EDITOR_GET("text_editor/highlighting/bookmark_color"); - Color breakpoint_color = EDITOR_GET("text_editor/highlighting/breakpoint_color"); - Color executing_line_color = EDITOR_GET("text_editor/highlighting/executing_line_color"); - Color code_folding_color = EDITOR_GET("text_editor/highlighting/code_folding_color"); - Color search_result_color = EDITOR_GET("text_editor/highlighting/search_result_color"); - Color search_result_border_color = EDITOR_GET("text_editor/highlighting/search_result_border_color"); - - text_edit->add_theme_color_override("background_color", background_color); - text_edit->add_theme_color_override("completion_background_color", completion_background_color); - text_edit->add_theme_color_override("completion_selected_color", completion_selected_color); - text_edit->add_theme_color_override("completion_existing_color", completion_existing_color); - text_edit->add_theme_color_override("completion_scroll_color", completion_scroll_color); - text_edit->add_theme_color_override("completion_font_color", completion_font_color); - text_edit->add_theme_color_override("font_color", text_color); - text_edit->add_theme_color_override("line_number_color", line_number_color); - text_edit->add_theme_color_override("caret_color", caret_color); - text_edit->add_theme_color_override("caret_background_color", caret_background_color); - text_edit->add_theme_color_override("font_selected_color", text_selected_color); - text_edit->add_theme_color_override("selection_color", selection_color); - text_edit->add_theme_color_override("brace_mismatch_color", brace_mismatch_color); - text_edit->add_theme_color_override("current_line_color", current_line_color); - text_edit->add_theme_color_override("line_length_guideline_color", line_length_guideline_color); - text_edit->add_theme_color_override("word_highlighted_color", word_highlighted_color); - text_edit->add_theme_color_override("bookmark_color", bookmark_color); - text_edit->add_theme_color_override("breakpoint_color", breakpoint_color); - text_edit->add_theme_color_override("executing_line_color", executing_line_color); - text_edit->add_theme_color_override("mark_color", mark_color); - text_edit->add_theme_color_override("code_folding_color", code_folding_color); - text_edit->add_theme_color_override("search_result_color", search_result_color); - text_edit->add_theme_color_override("search_result_border_color", search_result_border_color); - - text_edit->add_theme_constant_override("line_spacing", EDITOR_DEF("text_editor/theme/line_spacing", 6)); - theme_loaded = true; if (!script.is_null()) { _set_theme_for_script(); @@ -244,6 +203,26 @@ void ScriptTextEditor::_set_theme_for_script() { CodeEdit *text_edit = code_editor->get_text_editor(); text_edit->get_syntax_highlighter()->update_cache(); + List<String> strings; + script->get_language()->get_string_delimiters(&strings); + text_edit->clear_string_delimiters(); + for (List<String>::Element *E = strings.front(); E; E = E->next()) { + String string = E->get(); + String beg = string.get_slice(" ", 0); + String end = string.get_slice_count(" ") > 1 ? string.get_slice(" ", 1) : String(); + text_edit->add_string_delimiter(beg, end, end == ""); + } + + List<String> comments; + script->get_language()->get_comment_delimiters(&comments); + text_edit->clear_comment_delimiters(); + for (List<String>::Element *E = comments.front(); E; E = E->next()) { + String comment = E->get(); + String beg = comment.get_slice(" ", 0); + String end = comment.get_slice_count(" ") > 1 ? comment.get_slice(" ", 1) : String(); + text_edit->add_comment_delimiter(beg, end, end == ""); + } + /* add keywords for auto completion */ // singleton autoloads (as types, just as engine singletons are) Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); @@ -546,7 +525,7 @@ void ScriptTextEditor::_validate_script() { bool highlight_safe = EDITOR_DEF("text_editor/highlighting/highlight_type_safe_lines", true); bool last_is_safe = false; for (int i = 0; i < te->get_line_count(); i++) { - te->set_line_as_marked(i, line == i); + te->set_line_background_color(i, (line == i) ? marked_line_color : Color(0, 0, 0, 0)); if (highlight_safe) { if (safe_lines.has(i + 1)) { te->set_line_gutter_item_color(i, line_number_gutter, safe_line_number_color); @@ -1097,7 +1076,7 @@ void ScriptTextEditor::_edit_option(int p_op) { _edit_option_toggle_inline_comment(); } break; case EDIT_COMPLETE: { - tx->query_code_comple(); + tx->request_code_completion(true); } break; case EDIT_AUTO_INDENT: { String text = tx->get_text(); @@ -1470,11 +1449,17 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data Array files = d["files"]; String text_to_drop; + bool preload = Input::get_singleton()->is_key_pressed(KEY_CTRL); for (int i = 0; i < files.size(); i++) { if (i > 0) { - text_to_drop += ","; + text_to_drop += ", "; + } + + if (preload) { + text_to_drop += "preload(\"" + String(files[i]).c_escape() + "\")"; + } else { + text_to_drop += "\"" + String(files[i]).c_escape() + "\""; } - text_to_drop += "\"" + String(files[i]).c_escape() + "\""; } te->cursor_set_line(row); @@ -1839,9 +1824,7 @@ ScriptTextEditor::ScriptTextEditor() { update_settings(); - code_editor->get_text_editor()->set_callhint_settings( - EditorSettings::get_singleton()->get("text_editor/completion/put_callhint_tooltip_below_current_line"), - EditorSettings::get_singleton()->get("text_editor/completion/callhint_tooltip_offset")); + code_editor->get_text_editor()->set_code_hint_draw_below(EditorSettings::get_singleton()->get("text_editor/completion/put_callhint_tooltip_below_current_line")); code_editor->get_text_editor()->set_select_identifiers_on_hover(true); code_editor->get_text_editor()->set_context_menu_enabled(false); @@ -1937,7 +1920,7 @@ void ScriptTextEditor::register_editor() { #ifdef OSX_ENABLED ED_SHORTCUT("script_text_editor/clone_down", TTR("Clone Down"), KEY_MASK_SHIFT | KEY_MASK_CMD | KEY_C); #else - ED_SHORTCUT("script_text_editor/clone_down", TTR("Clone Down"), KEY_MASK_CMD | KEY_D); + ED_SHORTCUT("script_text_editor/clone_down", TTR("Clone Down"), KEY_MASK_SHIFT | KEY_MASK_CMD | KEY_D); #endif ED_SHORTCUT("script_text_editor/evaluate_selection", TTR("Evaluate Selection"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_E); ED_SHORTCUT("script_text_editor/trim_trailing_whitespace", TTR("Trim Trailing Whitespace"), KEY_MASK_CMD | KEY_MASK_ALT | KEY_T); diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h index f79abc60ab..f784bbe1f8 100644 --- a/editor/plugins/script_text_editor.h +++ b/editor/plugins/script_text_editor.h @@ -89,6 +89,8 @@ class ScriptTextEditor : public ScriptEditorBase { Color default_line_number_color = Color(1, 1, 1); Color safe_line_number_color = Color(1, 1, 1); + Color marked_line_color = Color(1, 1, 1); + PopupPanel *color_panel = nullptr; ColorPicker *color_picker = nullptr; Vector2 color_position; diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index 3cdba9cf16..3beef78bfc 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -37,12 +37,18 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" +#include "editor/project_settings_editor.h" #include "editor/property_editor.h" #include "servers/display_server.h" #include "servers/rendering/shader_types.h" /*** SHADER SCRIPT EDITOR ****/ +static bool saved_warnings_enabled = false; +static bool saved_treat_warning_as_errors = false; +static Map<ShaderWarning::Code, bool> saved_warnings; +static uint32_t saved_warning_flags = 0U; + Ref<Shader> ShaderTextEditor::get_edited_shader() const { return shader; } @@ -57,6 +63,8 @@ void ShaderTextEditor::set_edited_shader(const Ref<Shader> &p_shader) { get_text_editor()->set_text(p_shader->get_code()); get_text_editor()->clear_undo_history(); + get_text_editor()->call_deferred("set_h_scroll", 0); + get_text_editor()->call_deferred("set_v_scroll", 0); _validate_script(); _line_col_changed(); @@ -82,54 +90,21 @@ void ShaderTextEditor::reload_text() { update_line_and_column(); } +void ShaderTextEditor::set_warnings_panel(RichTextLabel *p_warnings_panel) { + warnings_panel = p_warnings_panel; +} + void ShaderTextEditor::_load_theme_settings() { - Color background_color = EDITOR_GET("text_editor/highlighting/background_color"); - Color completion_background_color = EDITOR_GET("text_editor/highlighting/completion_background_color"); - Color completion_selected_color = EDITOR_GET("text_editor/highlighting/completion_selected_color"); - Color completion_existing_color = EDITOR_GET("text_editor/highlighting/completion_existing_color"); - Color completion_scroll_color = EDITOR_GET("text_editor/highlighting/completion_scroll_color"); - Color completion_font_color = EDITOR_GET("text_editor/highlighting/completion_font_color"); - Color text_color = EDITOR_GET("text_editor/highlighting/text_color"); - Color line_number_color = EDITOR_GET("text_editor/highlighting/line_number_color"); - Color caret_color = EDITOR_GET("text_editor/highlighting/caret_color"); - Color caret_background_color = EDITOR_GET("text_editor/highlighting/caret_background_color"); - Color text_selected_color = EDITOR_GET("text_editor/highlighting/text_selected_color"); - Color selection_color = EDITOR_GET("text_editor/highlighting/selection_color"); - Color brace_mismatch_color = EDITOR_GET("text_editor/highlighting/brace_mismatch_color"); - Color current_line_color = EDITOR_GET("text_editor/highlighting/current_line_color"); - Color line_length_guideline_color = EDITOR_GET("text_editor/highlighting/line_length_guideline_color"); - Color word_highlighted_color = EDITOR_GET("text_editor/highlighting/word_highlighted_color"); - Color mark_color = EDITOR_GET("text_editor/highlighting/mark_color"); - Color bookmark_color = EDITOR_GET("text_editor/highlighting/bookmark_color"); - Color breakpoint_color = EDITOR_GET("text_editor/highlighting/breakpoint_color"); - Color executing_line_color = EDITOR_GET("text_editor/highlighting/executing_line_color"); - Color code_folding_color = EDITOR_GET("text_editor/highlighting/code_folding_color"); - Color search_result_color = EDITOR_GET("text_editor/highlighting/search_result_color"); - Color search_result_border_color = EDITOR_GET("text_editor/highlighting/search_result_border_color"); - - get_text_editor()->add_theme_color_override("background_color", background_color); - get_text_editor()->add_theme_color_override("completion_background_color", completion_background_color); - get_text_editor()->add_theme_color_override("completion_selected_color", completion_selected_color); - get_text_editor()->add_theme_color_override("completion_existing_color", completion_existing_color); - get_text_editor()->add_theme_color_override("completion_scroll_color", completion_scroll_color); - get_text_editor()->add_theme_color_override("completion_font_color", completion_font_color); - get_text_editor()->add_theme_color_override("font_color", text_color); - get_text_editor()->add_theme_color_override("line_number_color", line_number_color); - get_text_editor()->add_theme_color_override("caret_color", caret_color); - get_text_editor()->add_theme_color_override("caret_background_color", caret_background_color); - get_text_editor()->add_theme_color_override("font_selected_color", text_selected_color); - get_text_editor()->add_theme_color_override("selection_color", selection_color); - get_text_editor()->add_theme_color_override("brace_mismatch_color", brace_mismatch_color); - get_text_editor()->add_theme_color_override("current_line_color", current_line_color); - get_text_editor()->add_theme_color_override("line_length_guideline_color", line_length_guideline_color); - get_text_editor()->add_theme_color_override("word_highlighted_color", word_highlighted_color); - get_text_editor()->add_theme_color_override("mark_color", mark_color); - get_text_editor()->add_theme_color_override("bookmark_color", bookmark_color); - get_text_editor()->add_theme_color_override("breakpoint_color", breakpoint_color); - get_text_editor()->add_theme_color_override("executing_line_color", executing_line_color); - get_text_editor()->add_theme_color_override("code_folding_color", code_folding_color); - get_text_editor()->add_theme_color_override("search_result_color", search_result_color); - get_text_editor()->add_theme_color_override("search_result_border_color", search_result_border_color); + CodeEdit *text_editor = get_text_editor(); + Color updated_marked_line_color = EDITOR_GET("text_editor/highlighting/mark_color"); + if (updated_marked_line_color != marked_line_color) { + for (int i = 0; i < text_editor->get_line_count(); i++) { + if (text_editor->get_line_background_color(i) == marked_line_color) { + text_editor->set_line_background_color(i, updated_marked_line_color); + } + } + marked_line_color = updated_marked_line_color; + } syntax_highlighter->set_number_color(EDITOR_GET("text_editor/highlighting/number_color")); syntax_highlighter->set_symbol_color(EDITOR_GET("text_editor/highlighting/symbol_color")); @@ -178,6 +153,16 @@ void ShaderTextEditor::_load_theme_settings() { syntax_highlighter->clear_color_regions(); syntax_highlighter->add_color_region("/*", "*/", comment_color, false); syntax_highlighter->add_color_region("//", "", comment_color, true); + + text_editor->clear_comment_delimiters(); + text_editor->add_comment_delimiter("/*", "*/", false); + text_editor->add_comment_delimiter("//", "", true); + + if (warnings_panel) { + // Warnings panel + warnings_panel->add_theme_font_override("normal_font", EditorNode::get_singleton()->get_gui_base()->get_theme_font("main", "EditorFonts")); + warnings_panel->add_theme_font_size_override("normal_font_size", EditorNode::get_singleton()->get_gui_base()->get_theme_font_size("main_size", "EditorFonts")); + } } void ShaderTextEditor::_check_shader_mode() { @@ -224,6 +209,9 @@ void ShaderTextEditor::_validate_script() { ShaderLanguage sl; + sl.enable_warning_checking(saved_warnings_enabled); + sl.set_warning_flags(saved_warning_flags); + Error err = sl.compile(code, ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode())), ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode())), ShaderLanguage::VaryingFunctionNames(), ShaderTypes::get_singleton()->get_types(), _get_global_variable_type); if (err != OK) { @@ -231,20 +219,70 @@ void ShaderTextEditor::_validate_script() { set_error(error_text); set_error_pos(sl.get_error_line() - 1, 0); for (int i = 0; i < get_text_editor()->get_line_count(); i++) { - get_text_editor()->set_line_as_marked(i, false); + get_text_editor()->set_line_background_color(i, Color(0, 0, 0, 0)); } - get_text_editor()->set_line_as_marked(sl.get_error_line() - 1, true); - + get_text_editor()->set_line_background_color(sl.get_error_line() - 1, marked_line_color); } else { for (int i = 0; i < get_text_editor()->get_line_count(); i++) { - get_text_editor()->set_line_as_marked(i, false); + get_text_editor()->set_line_background_color(i, Color(0, 0, 0, 0)); } set_error(""); } + if (warnings.size() > 0 || err != OK) { + warnings_panel->clear(); + } + warnings.clear(); + for (List<ShaderWarning>::Element *E = sl.get_warnings_ptr(); E; E = E->next()) { + warnings.push_back(E->get()); + } + if (warnings.size() > 0 && err == OK) { + warnings.sort_custom<WarningsComparator>(); + _update_warning_panel(); + } else { + set_warning_nb(0); + } emit_signal("script_changed"); } +void ShaderTextEditor::_update_warning_panel() { + int warning_count = 0; + + warnings_panel->push_table(2); + for (int i = 0; i < warnings.size(); i++) { + ShaderWarning &w = warnings[i]; + + if (warning_count == 0) { + if (saved_treat_warning_as_errors) { + String error_text = "error(" + itos(w.get_line()) + "): " + w.get_message() + " " + TTR("Warnings should be fixed to prevent errors."); + set_error_pos(w.get_line() - 1, 0); + set_error(error_text); + get_text_editor()->set_line_background_color(w.get_line() - 1, marked_line_color); + } + } + + warning_count++; + + // First cell. + warnings_panel->push_cell(); + warnings_panel->push_meta(w.get_line() - 1); + warnings_panel->push_color(warnings_panel->get_theme_color("warning_color", "Editor")); + warnings_panel->add_text(TTR("Line") + " " + itos(w.get_line())); + warnings_panel->add_text(" (" + w.get_name() + "):"); + warnings_panel->pop(); // Color. + warnings_panel->pop(); // Meta goto. + warnings_panel->pop(); // Cell. + + // Second cell. + warnings_panel->push_cell(); + warnings_panel->add_text(w.get_message()); + warnings_panel->pop(); // Cell. + } + warnings_panel->pop(); // Table. + + set_warning_nb(warning_count); +} + void ShaderTextEditor::_bind_methods() { } @@ -314,7 +352,7 @@ void ShaderEditor::_menu_option(int p_option) { } break; case EDIT_COMPLETE: { - shader_editor->get_text_editor()->query_code_comple(); + shader_editor->get_text_editor()->request_code_completion(); } break; case SEARCH_FIND: { shader_editor->get_find_replace_bar()->popup_search(); @@ -358,10 +396,6 @@ void ShaderEditor::_notification(int p_what) { } } -void ShaderEditor::_params_changed() { - shader_editor->_validate_script(); -} - void ShaderEditor::_editor_settings_changed() { shader_editor->update_editor_settings(); @@ -370,8 +404,19 @@ void ShaderEditor::_editor_settings_changed() { shader_editor->get_text_editor()->set_draw_executing_lines_gutter(false); } +void ShaderEditor::_show_warnings_panel(bool p_show) { + warnings_panel->set_visible(p_show); +} + +void ShaderEditor::_warning_clicked(Variant p_line) { + if (p_line.get_type() == Variant::INT) { + shader_editor->get_text_editor()->cursor_set_line(p_line.operator int64_t()); + } +} + void ShaderEditor::_bind_methods() { - ClassDB::bind_method("_params_changed", &ShaderEditor::_params_changed); + ClassDB::bind_method("_show_warnings_panel", &ShaderEditor::_show_warnings_panel); + ClassDB::bind_method("_warning_clicked", &ShaderEditor::_warning_clicked); } void ShaderEditor::ensure_select_current() { @@ -389,6 +434,47 @@ void ShaderEditor::goto_line_selection(int p_line, int p_begin, int p_end) { shader_editor->goto_line_selection(p_line, p_begin, p_end); } +void ShaderEditor::_project_settings_changed() { + _update_warnings(true); +} + +void ShaderEditor::_update_warnings(bool p_validate) { + bool changed = false; + + bool warnings_enabled = GLOBAL_GET("debug/shader_language/warnings/enable").booleanize(); + if (warnings_enabled != saved_warnings_enabled) { + saved_warnings_enabled = warnings_enabled; + changed = true; + } + + bool treat_warning_as_errors = GLOBAL_GET("debug/shader_language/warnings/treat_warnings_as_errors").booleanize(); + if (treat_warning_as_errors != saved_treat_warning_as_errors) { + saved_treat_warning_as_errors = treat_warning_as_errors; + changed = true; + } + + bool update_flags = false; + + for (int i = 0; i < ShaderWarning::WARNING_MAX; i++) { + ShaderWarning::Code code = (ShaderWarning::Code)i; + bool value = GLOBAL_GET("debug/shader_language/warnings/" + ShaderWarning::get_name_from_code(code).to_lower()); + + if (saved_warnings[code] != value) { + saved_warnings[code] = value; + update_flags = true; + changed = true; + } + } + + if (update_flags) { + saved_warning_flags = (uint32_t)ShaderWarning::get_flags_from_codemap(saved_warnings); + } + + if (p_validate && changed && shader_editor && shader_editor->get_edited_shader().is_valid()) { + shader_editor->validate_script(); + } +} + void ShaderEditor::_check_for_external_edit() { if (shader.is_null() || !shader.is_valid()) { return; @@ -560,17 +646,24 @@ void ShaderEditor::_make_context_menu(bool p_selection, Vector2 p_position) { } ShaderEditor::ShaderEditor(EditorNode *p_node) { + GLOBAL_DEF("debug/shader_language/warnings/enable", true); + GLOBAL_DEF("debug/shader_language/warnings/treat_warnings_as_errors", false); + for (int i = 0; i < (int)ShaderWarning::WARNING_MAX; i++) { + GLOBAL_DEF("debug/shader_language/warnings/" + ShaderWarning::get_name_from_code((ShaderWarning::Code)i).to_lower(), true); + } + _update_warnings(false); + shader_editor = memnew(ShaderTextEditor); shader_editor->set_v_size_flags(SIZE_EXPAND_FILL); shader_editor->add_theme_constant_override("separation", 0); shader_editor->set_anchors_and_offsets_preset(Control::PRESET_WIDE); + shader_editor->connect("show_warnings_panel", callable_mp(this, &ShaderEditor::_show_warnings_panel)); shader_editor->connect("script_changed", callable_mp(this, &ShaderEditor::apply_shaders)); EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &ShaderEditor::_editor_settings_changed)); + ProjectSettingsEditor::get_singleton()->connect("confirmed", callable_mp(this, &ShaderEditor::_project_settings_changed)); - shader_editor->get_text_editor()->set_callhint_settings( - EditorSettings::get_singleton()->get("text_editor/completion/put_callhint_tooltip_below_current_line"), - EditorSettings::get_singleton()->get("text_editor/completion/callhint_tooltip_offset")); + shader_editor->get_text_editor()->set_code_hint_draw_below(EditorSettings::get_singleton()->get("text_editor/completion/put_callhint_tooltip_below_current_line")); shader_editor->get_text_editor()->set_select_identifiers_on_hover(true); shader_editor->get_text_editor()->set_context_menu_enabled(false); @@ -651,7 +744,23 @@ ShaderEditor::ShaderEditor(EditorNode *p_node) { hbc->add_child(goto_menu); hbc->add_child(help_menu); hbc->add_theme_style_override("panel", p_node->get_gui_base()->get_theme_stylebox("ScriptEditorPanel", "EditorStyles")); - main_container->add_child(shader_editor); + + VSplitContainer *editor_box = memnew(VSplitContainer); + main_container->add_child(editor_box); + editor_box->set_anchors_and_offsets_preset(Control::PRESET_WIDE); + editor_box->set_v_size_flags(SIZE_EXPAND_FILL); + editor_box->add_child(shader_editor); + + warnings_panel = memnew(RichTextLabel); + warnings_panel->set_custom_minimum_size(Size2(0, 100 * EDSCALE)); + warnings_panel->set_h_size_flags(SIZE_EXPAND_FILL); + warnings_panel->set_meta_underline(true); + warnings_panel->set_selection_enabled(true); + warnings_panel->set_focus_mode(FOCUS_CLICK); + warnings_panel->hide(); + warnings_panel->connect("meta_clicked", callable_mp(this, &ShaderEditor::_warning_clicked)); + editor_box->add_child(warnings_panel); + shader_editor->set_warnings_panel(warnings_panel); goto_line_dialog = memnew(GotoLineDialog); add_child(goto_line_dialog); diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h index 731c0a5b7e..d7da73f2ae 100644 --- a/editor/plugins/shader_editor_plugin.h +++ b/editor/plugins/shader_editor_plugin.h @@ -35,6 +35,7 @@ #include "editor/editor_plugin.h" #include "scene/gui/menu_button.h" #include "scene/gui/panel_container.h" +#include "scene/gui/rich_text_label.h" #include "scene/gui/tab_container.h" #include "scene/gui/text_edit.h" #include "scene/main/timer.h" @@ -44,10 +45,19 @@ class ShaderTextEditor : public CodeTextEditor { GDCLASS(ShaderTextEditor, CodeTextEditor); + Color marked_line_color = Color(1, 1, 1); + + struct WarningsComparator { + _ALWAYS_INLINE_ bool operator()(const ShaderWarning &p_a, const ShaderWarning &p_b) const { return (p_a.get_line() < p_b.get_line()); } + }; + Ref<CodeHighlighter> syntax_highlighter; + RichTextLabel *warnings_panel = nullptr; Ref<Shader> shader; + List<ShaderWarning> warnings; void _check_shader_mode(); + void _update_warning_panel(); protected: static void _bind_methods(); @@ -59,6 +69,7 @@ public: virtual void _validate_script() override; void reload_text(); + void set_warnings_panel(RichTextLabel *p_warnings_panel); Ref<Shader> get_edited_shader() const; void set_edited_shader(const Ref<Shader> &p_shader); @@ -100,6 +111,7 @@ class ShaderEditor : public PanelContainer { PopupMenu *bookmarks_menu; MenuButton *help_menu; PopupMenu *context_menu; + RichTextLabel *warnings_panel = nullptr; uint64_t idle; GotoLineDialog *goto_line_dialog; @@ -109,13 +121,16 @@ class ShaderEditor : public PanelContainer { ShaderTextEditor *shader_editor; void _menu_option(int p_option); - void _params_changed(); mutable Ref<Shader> shader; void _editor_settings_changed(); + void _project_settings_changed(); void _check_for_external_edit(); void _reload_shader_from_disk(); + void _show_warnings_panel(bool p_show); + void _warning_clicked(Variant p_line); + void _update_warnings(bool p_validate); protected: void _notification(int p_what); diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index 551d1c027a..59bc8f9e55 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -220,7 +220,7 @@ void SpriteFramesEditor::_sheet_zoom_out() { void SpriteFramesEditor::_sheet_zoom_reset() { // Default the zoom to match the editor scale, but don't dezoom on editor scales below 100% to prevent pixel art from looking bad. - sheet_zoom = MAX(1.0, EDSCALE); + sheet_zoom = MAX(1.0f, EDSCALE); Size2 texture_size = split_sheet_preview->get_texture()->get_size(); split_sheet_preview->set_custom_minimum_size(texture_size * sheet_zoom); } @@ -252,10 +252,10 @@ void SpriteFramesEditor::_prepare_sprite_sheet(const String &p_file) { EditorNode::get_singleton()->show_warning(TTR("Unable to load images")); ERR_FAIL_COND(!texture.is_valid()); } - bool new_texture = texture != split_sheet_preview->get_texture(); frames_selected.clear(); last_frame_selected = -1; + bool new_texture = texture != split_sheet_preview->get_texture(); split_sheet_preview->set_texture(texture); if (new_texture) { //different texture, reset to 4x4 @@ -280,17 +280,17 @@ void SpriteFramesEditor::_notification(int p_what) { move_down->set_icon(get_theme_icon("MoveRight", "EditorIcons")); _delete->set_icon(get_theme_icon("Remove", "EditorIcons")); zoom_out->set_icon(get_theme_icon("ZoomLess", "EditorIcons")); - zoom_1->set_icon(get_theme_icon("ZoomReset", "EditorIcons")); + zoom_reset->set_icon(get_theme_icon("ZoomReset", "EditorIcons")); zoom_in->set_icon(get_theme_icon("ZoomMore", "EditorIcons")); new_anim->set_icon(get_theme_icon("New", "EditorIcons")); remove_anim->set_icon(get_theme_icon("Remove", "EditorIcons")); split_sheet_zoom_out->set_icon(get_theme_icon("ZoomLess", "EditorIcons")); - split_sheet_zoom_1->set_icon(get_theme_icon("ZoomReset", "EditorIcons")); + split_sheet_zoom_reset->set_icon(get_theme_icon("ZoomReset", "EditorIcons")); split_sheet_zoom_in->set_icon(get_theme_icon("ZoomMore", "EditorIcons")); [[fallthrough]]; } case NOTIFICATION_THEME_CHANGED: { - splite_sheet_scroll->add_theme_style_override("bg", get_theme_stylebox("bg", "Tree")); + split_sheet_scroll->add_theme_style_override("bg", get_theme_stylebox("bg", "Tree")); } break; case NOTIFICATION_READY: { add_theme_constant_override("autohide", 1); // Fixes the dragger always showing up. @@ -733,7 +733,7 @@ void SpriteFramesEditor::_zoom_out() { } void SpriteFramesEditor::_zoom_reset() { - thumbnail_zoom = MAX(1.0, EDSCALE); + thumbnail_zoom = MAX(1.0f, EDSCALE); tree->set_fixed_column_width(thumbnail_default_size * 3 / 2); tree->set_fixed_icon_size(Size2(thumbnail_default_size, thumbnail_default_size)); } @@ -1086,11 +1086,13 @@ SpriteFramesEditor::SpriteFramesEditor() { zoom_out->set_flat(true); zoom_out->set_tooltip(TTR("Zoom Out")); hbc->add_child(zoom_out); - zoom_1 = memnew(Button); - zoom_1->connect("pressed", callable_mp(this, &SpriteFramesEditor::_zoom_reset)); - zoom_1->set_flat(true); - zoom_1->set_tooltip(TTR("Zoom Reset")); - hbc->add_child(zoom_1); + + zoom_reset = memnew(Button); + zoom_reset->connect("pressed", callable_mp(this, &SpriteFramesEditor::_zoom_reset)); + zoom_reset->set_flat(true); + zoom_reset->set_tooltip(TTR("Zoom Reset")); + hbc->add_child(zoom_reset); + zoom_in = memnew(Button); zoom_in->connect("pressed", callable_mp(this, &SpriteFramesEditor::_zoom_in)); zoom_in->set_flat(true); @@ -1183,16 +1185,16 @@ SpriteFramesEditor::SpriteFramesEditor() { split_sheet_preview->connect("draw", callable_mp(this, &SpriteFramesEditor::_sheet_preview_draw)); split_sheet_preview->connect("gui_input", callable_mp(this, &SpriteFramesEditor::_sheet_preview_input)); - splite_sheet_scroll = memnew(ScrollContainer); - splite_sheet_scroll->set_enable_h_scroll(true); - splite_sheet_scroll->set_enable_v_scroll(true); - splite_sheet_scroll->connect("gui_input", callable_mp(this, &SpriteFramesEditor::_sheet_scroll_input)); - split_sheet_panel->add_child(splite_sheet_scroll); + split_sheet_scroll = memnew(ScrollContainer); + split_sheet_scroll->set_enable_h_scroll(true); + split_sheet_scroll->set_enable_v_scroll(true); + split_sheet_scroll->connect("gui_input", callable_mp(this, &SpriteFramesEditor::_sheet_scroll_input)); + split_sheet_panel->add_child(split_sheet_scroll); CenterContainer *cc = memnew(CenterContainer); cc->add_child(split_sheet_preview); cc->set_h_size_flags(SIZE_EXPAND_FILL); cc->set_v_size_flags(SIZE_EXPAND_FILL); - splite_sheet_scroll->add_child(cc); + split_sheet_scroll->add_child(cc); MarginContainer *split_sheet_zoom_margin = memnew(MarginContainer); split_sheet_panel->add_child(split_sheet_zoom_margin); @@ -1209,12 +1211,14 @@ SpriteFramesEditor::SpriteFramesEditor() { split_sheet_zoom_out->set_tooltip(TTR("Zoom Out")); split_sheet_zoom_out->connect("pressed", callable_mp(this, &SpriteFramesEditor::_sheet_zoom_out)); split_sheet_zoom_hb->add_child(split_sheet_zoom_out); - split_sheet_zoom_1 = memnew(Button); - split_sheet_zoom_1->set_flat(true); - split_sheet_zoom_1->set_focus_mode(FOCUS_NONE); - split_sheet_zoom_1->set_tooltip(TTR("Zoom Reset")); - split_sheet_zoom_1->connect("pressed", callable_mp(this, &SpriteFramesEditor::_sheet_zoom_reset)); - split_sheet_zoom_hb->add_child(split_sheet_zoom_1); + + split_sheet_zoom_reset = memnew(Button); + split_sheet_zoom_reset->set_flat(true); + split_sheet_zoom_reset->set_focus_mode(FOCUS_NONE); + split_sheet_zoom_reset->set_tooltip(TTR("Zoom Reset")); + split_sheet_zoom_reset->connect("pressed", callable_mp(this, &SpriteFramesEditor::_sheet_zoom_reset)); + split_sheet_zoom_hb->add_child(split_sheet_zoom_reset); + split_sheet_zoom_in = memnew(Button); split_sheet_zoom_in->set_flat(true); split_sheet_zoom_in->set_focus_mode(FOCUS_NONE); @@ -1230,14 +1234,14 @@ SpriteFramesEditor::SpriteFramesEditor() { // Config scale. scale_ratio = 1.2f; - thumbnail_default_size = 96 * MAX(1.0, EDSCALE); - thumbnail_zoom = MAX(1.0, EDSCALE); - max_thumbnail_zoom = 8.0f * MAX(1.0, EDSCALE); - min_thumbnail_zoom = 0.1f * MAX(1.0, EDSCALE); + thumbnail_default_size = 96 * MAX(1, EDSCALE); + thumbnail_zoom = MAX(1.0f, EDSCALE); + max_thumbnail_zoom = 8.0f * MAX(1.0f, EDSCALE); + min_thumbnail_zoom = 0.1f * MAX(1.0f, EDSCALE); // Default the zoom to match the editor scale, but don't dezoom on editor scales below 100% to prevent pixel art from looking bad. - sheet_zoom = MAX(1.0, EDSCALE); - max_sheet_zoom = 16.0f * MAX(1.0, EDSCALE); - min_sheet_zoom = 0.01f * MAX(1.0, EDSCALE); + sheet_zoom = MAX(1.0f, EDSCALE); + max_sheet_zoom = 16.0f * MAX(1.0f, EDSCALE); + min_sheet_zoom = 0.01f * MAX(1.0f, EDSCALE); _zoom_reset(); } diff --git a/editor/plugins/sprite_frames_editor_plugin.h b/editor/plugins/sprite_frames_editor_plugin.h index bbc26ca726..77cdbb4af6 100644 --- a/editor/plugins/sprite_frames_editor_plugin.h +++ b/editor/plugins/sprite_frames_editor_plugin.h @@ -54,13 +54,12 @@ class SpriteFramesEditor : public HSplitContainer { Button *move_up; Button *move_down; Button *zoom_out; - Button *zoom_1; + Button *zoom_reset; Button *zoom_in; ItemList *tree; bool loading_scene; int sel; - HSplitContainer *split; Button *new_anim; Button *remove_anim; @@ -79,12 +78,12 @@ class SpriteFramesEditor : public HSplitContainer { ConfirmationDialog *delete_dialog; ConfirmationDialog *split_sheet_dialog; - ScrollContainer *splite_sheet_scroll; + ScrollContainer *split_sheet_scroll; TextureRect *split_sheet_preview; SpinBox *split_sheet_h; SpinBox *split_sheet_v; Button *split_sheet_zoom_out; - Button *split_sheet_zoom_1; + Button *split_sheet_zoom_reset; Button *split_sheet_zoom_in; EditorFileDialog *file_split_sheet; Set<int> frames_selected; diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp index e6fb2710ae..1edcbd2cc9 100644 --- a/editor/plugins/text_editor.cpp +++ b/editor/plugins/text_editor.cpp @@ -59,58 +59,7 @@ void TextEditor::_change_syntax_highlighter(int p_idx) { } void TextEditor::_load_theme_settings() { - CodeEdit *text_edit = code_editor->get_text_editor(); - text_edit->get_syntax_highlighter()->update_cache(); - - Color background_color = EDITOR_GET("text_editor/highlighting/background_color"); - Color completion_background_color = EDITOR_GET("text_editor/highlighting/completion_background_color"); - Color completion_selected_color = EDITOR_GET("text_editor/highlighting/completion_selected_color"); - Color completion_existing_color = EDITOR_GET("text_editor/highlighting/completion_existing_color"); - Color completion_scroll_color = EDITOR_GET("text_editor/highlighting/completion_scroll_color"); - Color completion_font_color = EDITOR_GET("text_editor/highlighting/completion_font_color"); - Color text_color = EDITOR_GET("text_editor/highlighting/text_color"); - Color line_number_color = EDITOR_GET("text_editor/highlighting/line_number_color"); - Color caret_color = EDITOR_GET("text_editor/highlighting/caret_color"); - Color caret_background_color = EDITOR_GET("text_editor/highlighting/caret_background_color"); - Color text_selected_color = EDITOR_GET("text_editor/highlighting/text_selected_color"); - Color selection_color = EDITOR_GET("text_editor/highlighting/selection_color"); - Color brace_mismatch_color = EDITOR_GET("text_editor/highlighting/brace_mismatch_color"); - Color current_line_color = EDITOR_GET("text_editor/highlighting/current_line_color"); - Color line_length_guideline_color = EDITOR_GET("text_editor/highlighting/line_length_guideline_color"); - Color word_highlighted_color = EDITOR_GET("text_editor/highlighting/word_highlighted_color"); - Color mark_color = EDITOR_GET("text_editor/highlighting/mark_color"); - Color bookmark_color = EDITOR_GET("text_editor/highlighting/bookmark_color"); - Color breakpoint_color = EDITOR_GET("text_editor/highlighting/breakpoint_color"); - Color executing_line_color = EDITOR_GET("text_editor/highlighting/executing_line_color"); - Color code_folding_color = EDITOR_GET("text_editor/highlighting/code_folding_color"); - Color search_result_color = EDITOR_GET("text_editor/highlighting/search_result_color"); - Color search_result_border_color = EDITOR_GET("text_editor/highlighting/search_result_border_color"); - - text_edit->add_theme_color_override("background_color", background_color); - text_edit->add_theme_color_override("completion_background_color", completion_background_color); - text_edit->add_theme_color_override("completion_selected_color", completion_selected_color); - text_edit->add_theme_color_override("completion_existing_color", completion_existing_color); - text_edit->add_theme_color_override("completion_scroll_color", completion_scroll_color); - text_edit->add_theme_color_override("completion_font_color", completion_font_color); - text_edit->add_theme_color_override("font_color", text_color); - text_edit->add_theme_color_override("line_number_color", line_number_color); - text_edit->add_theme_color_override("caret_color", caret_color); - text_edit->add_theme_color_override("caret_background_color", caret_background_color); - text_edit->add_theme_color_override("font_selected_color", text_selected_color); - text_edit->add_theme_color_override("selection_color", selection_color); - text_edit->add_theme_color_override("brace_mismatch_color", brace_mismatch_color); - text_edit->add_theme_color_override("current_line_color", current_line_color); - text_edit->add_theme_color_override("line_length_guideline_color", line_length_guideline_color); - text_edit->add_theme_color_override("word_highlighted_color", word_highlighted_color); - text_edit->add_theme_color_override("breakpoint_color", breakpoint_color); - text_edit->add_theme_color_override("executing_line_color", executing_line_color); - text_edit->add_theme_color_override("mark_color", mark_color); - text_edit->add_theme_color_override("bookmark_color", bookmark_color); - text_edit->add_theme_color_override("code_folding_color", code_folding_color); - text_edit->add_theme_color_override("search_result_color", search_result_color); - text_edit->add_theme_color_override("search_result_border_color", search_result_border_color); - - text_edit->add_theme_constant_override("line_spacing", EDITOR_DEF("text_editor/theme/line_spacing", 6)); + code_editor->get_text_editor()->get_syntax_highlighter()->update_cache(); } String TextEditor::get_name() { diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index 62ecdb8c32..9526160674 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -937,7 +937,6 @@ void TextureRegionEditor::_edit_region() { if (cache_map.has(texture->get_rid())) { autoslice_cache = cache_map[texture->get_rid()]; autoslice_is_dirty = false; - return; } else { if (is_visible() && snap_mode == SNAP_AUTOSLICE) { _update_autoslice(); diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp index 8c5bb56287..78f181e321 100644 --- a/editor/plugins/tiles/tile_atlas_view.cpp +++ b/editor/plugins/tiles/tile_atlas_view.cpp @@ -39,6 +39,7 @@ #include "scene/gui/texture_rect.h" #include "editor/editor_scale.h" +#include "editor/editor_settings.h" void TileAtlasView::_gui_input(const Ref<InputEvent> &p_event) { bool ctrl = Input::get_singleton()->is_key_pressed(KEY_CTRL); @@ -195,9 +196,9 @@ void TileAtlasView::_base_tiles_root_control_gui_input(const Ref<InputEvent> &p_ if (mm.is_valid()) { Transform2D xform = base_tiles_drawing_root->get_transform().affine_inverse(); Vector2i coords = get_atlas_tile_coords_at_pos(xform.xform(mm->get_position())); - if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { coords = tile_set_atlas_source->get_tile_at_coords(coords); - if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { base_tiles_root_control->set_tooltip(vformat(TTR("Source: %d\nAtlas coordinates: %s\nAlternative: 0"), source_id, coords)); } } @@ -215,7 +216,7 @@ void TileAtlasView::_draw_base_tiles() { for (int x = 0; x < grid_size.x; x++) { for (int y = 0; y < grid_size.y; y++) { Vector2i coords = Vector2i(x, y); - if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) { Rect2i rect = Rect2i(texture_region_size * coords + margins, texture_region_size); base_tiles_draw->draw_texture_rect_region(texture, rect, rect); } @@ -229,17 +230,23 @@ void TileAtlasView::_draw_base_tiles() { rect.set_end(Vector2i(texture->get_size().x, margins.y)); base_tiles_draw->draw_texture_rect_region(texture, rect, rect); // Bottom - rect.position = Vector2i(0, margins.y + (grid_size.y * texture_region_size.y)); - rect.set_end(texture->get_size()); - base_tiles_draw->draw_texture_rect_region(texture, rect, rect); + int bottom_border = margins.y + (grid_size.y * texture_region_size.y); + if (bottom_border < texture->get_size().y) { + rect.position = Vector2i(0, bottom_border); + rect.set_end(texture->get_size()); + base_tiles_draw->draw_texture_rect_region(texture, rect, rect); + } // Left rect.position = Vector2i(0, margins.y); rect.set_end(Vector2i(margins.x, margins.y + (grid_size.y * texture_region_size.y))); base_tiles_draw->draw_texture_rect_region(texture, rect, rect); // Right. - rect.position = Vector2i(margins.x + (grid_size.x * texture_region_size.x), margins.y); - rect.set_end(Vector2i(texture->get_size().x, margins.y + (grid_size.y * texture_region_size.y))); - base_tiles_draw->draw_texture_rect_region(texture, rect, rect); + int right_border = margins.x + (grid_size.x * texture_region_size.x); + if (right_border < texture->get_size().x) { + rect.position = Vector2i(right_border, margins.y); + rect.set_end(Vector2i(texture->get_size().x, margins.y + (grid_size.y * texture_region_size.y))); + base_tiles_draw->draw_texture_rect_region(texture, rect, rect); + } // Draw actual tiles, using their properties (modulation, etc...) for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) { @@ -249,7 +256,7 @@ void TileAtlasView::_draw_base_tiles() { Vector2i offset_pos = (margins + (atlas_coords * texture_region_size) + tile_set_atlas_source->get_tile_texture_region(atlas_coords).size / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, 0)); // Draw the tile. - TileSetAtlasPluginRendering::draw_tile(base_tiles_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, 0); + TileSetPluginAtlasRendering::draw_tile(base_tiles_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, 0); } } } @@ -268,7 +275,7 @@ void TileAtlasView::_draw_base_tiles_texture_grid() { for (int y = 0; y < grid_size.y; y++) { Vector2i origin = margins + (Vector2i(x, y) * (texture_region_size + separation)); Vector2i base_tile_coords = tile_set_atlas_source->get_tile_at_coords(Vector2i(x, y)); - if (base_tile_coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (base_tile_coords != TileSetSource::INVALID_ATLAS_COORDS) { if (base_tile_coords == Vector2i(x, y)) { // Draw existing tile. Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(base_tile_coords); @@ -299,7 +306,7 @@ void TileAtlasView::_draw_base_tiles_dark() { Vector2i origin = margins + (Vector2i(x, y) * (texture_region_size + separation)); Vector2i base_tile_coords = tile_set_atlas_source->get_tile_at_coords(Vector2i(x, y)); - if (base_tile_coords == TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (base_tile_coords == TileSetSource::INVALID_ATLAS_COORDS) { // Draw the grid. base_tiles_dark->draw_rect(Rect2i(origin, texture_region_size), Color(0.0, 0.0, 0.0, 0.5), true); } @@ -310,6 +317,7 @@ void TileAtlasView::_draw_base_tiles_dark() { void TileAtlasView::_draw_base_tiles_shape_grid() { // Draw the shapes. + Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color"); Vector2i tile_shape_size = tile_set->get_tile_size(); for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) { Vector2i tile_id = tile_set_atlas_source->get_tile_id(i); @@ -318,7 +326,7 @@ void TileAtlasView::_draw_base_tiles_shape_grid() { Vector2 origin = texture_region.position + (texture_region.size - tile_shape_size) / 2 + in_tile_base_offset; // Draw only if the tile shape fits in the texture region - tile_set->draw_tile_shape(base_tiles_shape_grid, Rect2(origin, tile_shape_size), Color(1.0, 0.5, 0.2, 0.8)); + tile_set->draw_tile_shape(base_tiles_shape_grid, Rect2(origin, tile_shape_size), grid_color); } } @@ -331,7 +339,7 @@ void TileAtlasView::_alternative_tiles_root_control_gui_input(const Ref<InputEve Vector3i coords3 = get_alternative_tile_at_pos(xform.xform(mm->get_position())); Vector2i coords = Vector2i(coords3.x, coords3.y); int alternative_id = coords3.z; - if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS && alternative_id != TileSetAtlasSource::INVALID_TILE_ALTERNATIVE) { + if (coords != TileSetSource::INVALID_ATLAS_COORDS && alternative_id != TileSetSource::INVALID_TILE_ALTERNATIVE) { alternative_tiles_root_control->set_tooltip(vformat(TTR("Source: %d\nAtlas coordinates: %s\nAlternative: %d"), source_id, coords, alternative_id)); } } @@ -364,7 +372,7 @@ void TileAtlasView::_draw_alternatives() { } // Draw the tile. - TileSetAtlasPluginRendering::draw_tile(alternatives_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, alternative_id); + TileSetPluginAtlasRendering::draw_tile(alternatives_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, alternative_id); // Increment the x position. current_pos.x += transposed ? texture_region.size.y : texture_region.size.x; @@ -464,7 +472,7 @@ Vector2i TileAtlasView::get_atlas_tile_coords_at_pos(const Vector2 p_pos) const return ret; } - return TileSetAtlasSource::INVALID_ATLAS_COORDS; + return TileSetSource::INVALID_ATLAS_COORDS; } void TileAtlasView::_update_alternative_tiles_rect_cache() { @@ -506,7 +514,7 @@ Vector3i TileAtlasView::get_alternative_tile_at_pos(const Vector2 p_pos) const { } } - return Vector3i(TileSetAtlasSource::INVALID_ATLAS_COORDS.x, TileSetAtlasSource::INVALID_ATLAS_COORDS.y, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE); + return Vector3i(TileSetSource::INVALID_ATLAS_COORDS.x, TileSetSource::INVALID_ATLAS_COORDS.y, TileSetSource::INVALID_TILE_ALTERNATIVE); } Rect2i TileAtlasView::get_alternative_tile_rect(const Vector2i p_coords, int p_alternative_tile) { diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp index 0b674bec39..61457e3e59 100644 --- a/editor/plugins/tiles/tile_data_editors.cpp +++ b/editor/plugins/tiles/tile_data_editors.cpp @@ -113,7 +113,22 @@ void TileDataPositionEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform ERR_FAIL_COND(value.get_type() != Variant::VECTOR2I && value.get_type() != Variant::VECTOR2); Ref<Texture2D> position_icon = TileSetEditor::get_singleton()->get_theme_icon("EditorPosition", "EditorIcons"); - p_canvas_item->draw_texture(position_icon, p_transform.get_origin() + Vector2(value) - position_icon->get_size() / 2); + p_canvas_item->draw_texture(position_icon, p_transform.xform(Vector2(value)) - position_icon->get_size() / 2); +} + +void TileDataYSortEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) { + TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile); + ERR_FAIL_COND(!tile_data); + + bool valid; + Variant value = tile_data->get(p_property, &valid); + if (!valid) { + return; + } + ERR_FAIL_COND(value.get_type() != Variant::INT); + + Ref<Texture2D> position_icon = TileSetEditor::get_singleton()->get_theme_icon("EditorPosition", "EditorIcons"); + p_canvas_item->draw_texture(position_icon, p_transform.xform(Vector2(0, value)) - position_icon->get_size() / 2); } void TileDataOcclusionShapeEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) { @@ -166,7 +181,7 @@ void TileDataTerrainsEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform Vector<String> components = String(p_property).split("/", true); if (components[0] == "terrain_mode" || components[0] == "terrain" || components[0] == "terrains_peering_bit") { - TileSetAtlasPluginTerrain::draw_terrains(p_canvas_item, p_transform, p_tile_set, tile_data); + TileSetPluginAtlasTerrain::draw_terrains(p_canvas_item, p_transform, p_tile_set, tile_data); } } diff --git a/editor/plugins/tiles/tile_data_editors.h b/editor/plugins/tiles/tile_data_editors.h index f4f9c25157..b82189e1ee 100644 --- a/editor/plugins/tiles/tile_data_editors.h +++ b/editor/plugins/tiles/tile_data_editors.h @@ -79,6 +79,13 @@ public: virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override; }; +class TileDataYSortEditor : public TileDataEditor { + GDCLASS(TileDataYSortEditor, TileDataEditor); + +public: + virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override; +}; + class TileDataOcclusionShapeEditor : public TileDataEditor { GDCLASS(TileDataOcclusionShapeEditor, TileDataEditor); diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp index 73640a78c2..ef13d8ea12 100644 --- a/editor/plugins/tiles/tile_map_editor.cpp +++ b/editor/plugins/tiles/tile_map_editor.cpp @@ -32,6 +32,7 @@ #include "tiles_editor_plugin.h" +#include "editor/editor_resource_preview.h" #include "editor/editor_scale.h" #include "editor/plugins/canvas_item_editor_plugin.h" @@ -55,18 +56,25 @@ void TileMapEditorTilesPlugin::_notification(int p_what) { picker_button->set_icon(get_theme_icon("ColorPick", "EditorIcons")); erase_button->set_icon(get_theme_icon("Eraser", "EditorIcons")); - missing_texture_texture = get_theme_icon("TileSet", "EditorIcons"); + toggle_grid_button->set_icon(get_theme_icon("Grid", "EditorIcons")); + + missing_atlas_texture_icon = get_theme_icon("TileSet", "EditorIcons"); + + toggle_grid_button->set_pressed(EditorSettings::get_singleton()->get("editors/tiles_editor/display_grid")); break; case NOTIFICATION_VISIBILITY_CHANGED: _stop_dragging(); + break; + case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: + toggle_grid_button->set_pressed(EditorSettings::get_singleton()->get("editors/tiles_editor/display_grid")); + break; } } void TileMapEditorTilesPlugin::tile_set_changed() { _update_fix_selected_and_hovered(); - _update_bottom_panel(); _update_tile_set_sources_list(); - _update_atlas_view(); + _update_bottom_panel(); } void TileMapEditorTilesPlugin::_on_random_tile_checkbox_toggled(bool p_pressed) { @@ -77,6 +85,10 @@ void TileMapEditorTilesPlugin::_on_scattering_spinbox_changed(double p_value) { scattering = p_value; } +void TileMapEditorTilesPlugin::_on_grid_toggled(bool p_pressed) { + EditorSettings::get_singleton()->set("editors/tiles_editor/display_grid", p_pressed); +} + void TileMapEditorTilesPlugin::_update_toolbar() { // Stop draggig if needed. _stop_dragging(); @@ -146,19 +158,38 @@ void TileMapEditorTilesPlugin::_update_tile_set_sources_list() { for (int i = 0; i < tile_set->get_source_count(); i++) { int source_id = tile_set->get_source_id(i); - // TODO: handle with virtual functions TileSetSource *source = *tile_set->get_source(source_id); + + Ref<Texture2D> texture; + String item_text; + + // Atlas source. TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); if (atlas_source) { - Ref<Texture2D> texture = atlas_source->get_texture(); + texture = atlas_source->get_texture(); if (texture.is_valid()) { - sources_list->add_item(vformat("%s - (id:%d)", texture->get_path().get_file(), source_id), texture); + item_text = vformat("%s (id:%d)", texture->get_path().get_file(), source_id); } else { - sources_list->add_item(vformat("No Texture Atlas Source - (id:%d)", source_id), missing_texture_texture); + item_text = vformat("No Texture Atlas Source (id:%d)", source_id); } - } else { - sources_list->add_item(vformat("Unknown Type Source - (id:%d)", source_id), missing_texture_texture); } + + // Scene collection source. + TileSetScenesCollectionSource *scene_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source); + if (scene_collection_source) { + texture = get_theme_icon("PackedScene", "EditorIcons"); + item_text = vformat(TTR("Scene Collection Source (id:%d)"), source_id); + } + + // Use default if not valid. + if (item_text.is_empty()) { + item_text = vformat(TTR("Unknown Type Source (id:%d)"), source_id); + } + if (!texture.is_valid()) { + texture = missing_atlas_texture_icon; + } + + sources_list->add_item(item_text, texture); sources_list->set_item_metadata(i, source_id); } @@ -176,7 +207,7 @@ void TileMapEditorTilesPlugin::_update_tile_set_sources_list() { TilesEditor::get_singleton()->set_atlas_sources_lists_current(sources_list->get_current()); } -void TileMapEditorTilesPlugin::_update_atlas_view() { +void TileMapEditorTilesPlugin::_update_bottom_panel() { // Update the atlas display. TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); if (!tile_map) { @@ -185,39 +216,161 @@ void TileMapEditorTilesPlugin::_update_atlas_view() { Ref<TileSet> tile_set = tile_map->get_tileset(); if (!tile_set.is_valid()) { - tile_atlas_view->hide(); return; } int source_index = sources_list->get_current(); if (source_index >= 0 && source_index < sources_list->get_item_count()) { + atlas_sources_split_container->show(); + missing_source_label->hide(); + int source_id = sources_list->get_item_metadata(source_index); TileSetSource *source = *tile_set->get_source(source_id); TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source); + if (atlas_source) { - tile_atlas_view->set_atlas_source(*tile_map->get_tileset(), atlas_source, source_id); tile_atlas_view->show(); + scene_tiles_list->hide(); + invalid_source_label->hide(); + _update_atlas_view(); + } else if (scenes_collection_source) { + tile_atlas_view->hide(); + scene_tiles_list->show(); + invalid_source_label->hide(); + _update_scenes_collection_view(); + } else { + tile_atlas_view->hide(); + scene_tiles_list->hide(); + invalid_source_label->show(); } } else { + atlas_sources_split_container->hide(); + missing_source_label->show(); + tile_atlas_view->hide(); + scene_tiles_list->hide(); + invalid_source_label->hide(); } +} - // Synchronize atlas view. - TilesEditor::get_singleton()->synchronize_atlas_view(tile_atlas_view); +void TileMapEditorTilesPlugin::_update_atlas_view() { + TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); + if (!tile_map) { + return; + } + Ref<TileSet> tile_set = tile_map->get_tileset(); + if (!tile_set.is_valid()) { + return; + } + + int source_id = sources_list->get_item_metadata(sources_list->get_current()); + TileSetSource *source = *tile_set->get_source(source_id); + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + ERR_FAIL_COND(!atlas_source); + + tile_atlas_view->set_atlas_source(*tile_map->get_tileset(), atlas_source, source_id); + TilesEditor::get_singleton()->synchronize_atlas_view(tile_atlas_view); tile_atlas_control->update(); } -void TileMapEditorTilesPlugin::_update_bottom_panel() { +void TileMapEditorTilesPlugin::_update_scenes_collection_view() { TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); if (!tile_map) { return; } + Ref<TileSet> tile_set = tile_map->get_tileset(); + if (!tile_set.is_valid()) { + return; + } + + int source_id = sources_list->get_item_metadata(sources_list->get_current()); + TileSetSource *source = *tile_set->get_source(source_id); + TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source); + ERR_FAIL_COND(!scenes_collection_source); + + // Clear the list. + scene_tiles_list->clear(); - // Update the tabs. - missing_source_label->set_visible(tile_set.is_valid() && tile_set->get_source_count() == 0); - atlas_sources_split_container->set_visible(tile_set.is_valid() && tile_set->get_source_count() > 0); + // Rebuild the list. + for (int i = 0; i < scenes_collection_source->get_scene_tiles_count(); i++) { + int scene_id = scenes_collection_source->get_scene_tile_id(i); + + Ref<PackedScene> scene = scenes_collection_source->get_scene_tile_scene(scene_id); + + int item_index = 0; + if (scene.is_valid()) { + item_index = scene_tiles_list->add_item(vformat("%s (path:%s id:%d)", scene->get_path().get_file().get_basename(), scene->get_path(), scene_id)); + Variant udata = i; + EditorResourcePreview::get_singleton()->queue_edited_resource_preview(scene, this, "_scene_thumbnail_done", udata); + } else { + item_index = scene_tiles_list->add_item(TTR("Tile with Invalid Scene"), get_theme_icon("PackedScene", "EditorIcons")); + } + scene_tiles_list->set_item_metadata(item_index, scene_id); + + // Check if in selection. + if (tile_set_selection.has(TileMapCell(source_id, Vector2i(), scene_id))) { + scene_tiles_list->select(item_index, false); + } + } + + // Icon size update. + int int_size = int(EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size")) * EDSCALE; + scene_tiles_list->set_fixed_icon_size(Vector2(int_size, int_size)); +} + +void TileMapEditorTilesPlugin::_scene_thumbnail_done(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, Variant p_ud) { + int index = p_ud; + + if (index >= 0 && index < scene_tiles_list->get_item_count()) { + scene_tiles_list->set_item_icon(index, p_preview); + } +} + +void TileMapEditorTilesPlugin::_scenes_list_multi_selected(int p_index, bool p_selected) { + TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); + if (!tile_map) { + return; + } + + Ref<TileSet> tile_set = tile_map->get_tileset(); + if (!tile_set.is_valid()) { + return; + } + + // Add or remove the Tile form the selection. + int scene_id = scene_tiles_list->get_item_metadata(p_index); + int source_id = sources_list->get_item_metadata(sources_list->get_current()); + TileSetSource *source = *tile_set->get_source(source_id); + TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source); + ERR_FAIL_COND(!scenes_collection_source); + + TileMapCell selected = TileMapCell(source_id, Vector2i(), scene_id); + + // Clear the selection if shift is not pressed. + if (!Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { + tile_set_selection.clear(); + } + + if (p_selected) { + tile_set_selection.insert(selected); + } else { + if (tile_set_selection.has(selected)) { + tile_set_selection.erase(selected); + } + } + + _update_selection_pattern_from_tileset_selection(); +} + +void TileMapEditorTilesPlugin::_scenes_list_nothing_selected() { + scene_tiles_list->deselect_all(); + tile_set_selection.clear(); + tile_map_selection.clear(); + selection_pattern->clear(); + _update_selection_pattern_from_tileset_selection(); } bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p_event) { @@ -257,7 +410,7 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p if (!tile_map_selection.is_empty()) { undo_redo->create_action(TTR("Delete tiles")); for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) { - undo_redo->add_do_method(tile_map, "set_cell", E->get(), -1, TileSetAtlasSource::INVALID_ATLAS_COORDS, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE); + undo_redo->add_do_method(tile_map, "set_cell", E->get(), -1, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); undo_redo->add_undo_method(tile_map, "set_cell", E->get(), tile_map->get_cell_source_id(E->get()), tile_map->get_cell_atlas_coords(E->get()), tile_map->get_cell_alternative_tile(E->get())); } undo_redo->add_undo_method(this, "_set_tile_map_selection", _get_tile_map_selection()); @@ -288,7 +441,7 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p if (!tile_map_selection.is_empty()) { undo_redo->create_action(TTR("Delete tiles")); for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) { - undo_redo->add_do_method(tile_map, "set_cell", E->get(), -1, TileSetAtlasSource::INVALID_ATLAS_COORDS, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE); + undo_redo->add_do_method(tile_map, "set_cell", E->get(), -1, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); undo_redo->add_undo_method(tile_map, "set_cell", E->get(), tile_map->get_cell_source_id(E->get()), tile_map->get_cell_atlas_coords(E->get()), tile_map->get_cell_alternative_tile(E->get())); } undo_redo->add_undo_method(this, "_set_tile_map_selection", _get_tile_map_selection()); @@ -364,7 +517,7 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) { Vector2i coords = E->get(); drag_modified.insert(coords, TileMapCell(tile_map->get_cell_source_id(coords), tile_map->get_cell_atlas_coords(coords), tile_map->get_cell_alternative_tile(coords))); - tile_map->set_cell(coords, -1, TileSetAtlasSource::INVALID_ATLAS_COORDS, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE); + tile_map->set_cell(coords, -1, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); } } else { // Select tiles @@ -463,11 +616,13 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over if (drag_type == DRAG_TYPE_MOVE || (drag_type == DRAG_TYPE_SELECT && !Input::get_singleton()->is_key_pressed(KEY_CTRL) && !Input::get_singleton()->is_key_pressed(KEY_SHIFT))) { // Do nothing } else { - tile_map->draw_cells_outline(p_overlay, tile_map_selection, Color(0.0, 0.0, 1.0), xform); + Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color"); + Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0); + tile_map->draw_cells_outline(p_overlay, tile_map_selection, selection_color, xform); } } - // handle the preview of the tiles to be placed. + // Handle the preview of the tiles to be placed. if (is_visible_in_tree() && has_mouse) { // Only if the tilemap editor is opened and the viewport is hovered. Map<Vector2i, TileMapCell> preview; Rect2i drawn_grid_rect; @@ -562,32 +717,37 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over const int fading = 5; // Draw the lines of the grid behind the preview. - if (drawn_grid_rect.size.x > 0 && drawn_grid_rect.size.y > 0) { - drawn_grid_rect = drawn_grid_rect.grow(fading); - for (int x = drawn_grid_rect.position.x; x < (drawn_grid_rect.position.x + drawn_grid_rect.size.x); x++) { - for (int y = drawn_grid_rect.position.y; y < (drawn_grid_rect.position.y + drawn_grid_rect.size.y); y++) { - Vector2i pos_in_rect = Vector2i(x, y) - drawn_grid_rect.position; - - // Fade out the border of the grid. - float left_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.x), 0.0f, 1.0f); - float right_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.x, (float)(drawn_grid_rect.size.x - fading), (float)pos_in_rect.x), 0.0f, 1.0f); - float top_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.y), 0.0f, 1.0f); - float bottom_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.y, (float)(drawn_grid_rect.size.y - fading), (float)pos_in_rect.y), 0.0f, 1.0f); - float opacity = CLAMP(MIN(left_opacity, MIN(right_opacity, MIN(top_opacity, bottom_opacity))) + 0.1, 0.0f, 1.0f); - - Rect2 cell_region = xform.xform(Rect2(tile_map->map_to_world(Vector2(x, y)) - tile_shape_size / 2, tile_shape_size)); - tile_set->draw_tile_shape(p_overlay, cell_region, Color(1.0, 0.5, 0.2, 0.5 * opacity), false); + bool display_grid = EditorSettings::get_singleton()->get("editors/tiles_editor/display_grid"); + if (display_grid) { + Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color"); + if (drawn_grid_rect.size.x > 0 && drawn_grid_rect.size.y > 0) { + drawn_grid_rect = drawn_grid_rect.grow(fading); + for (int x = drawn_grid_rect.position.x; x < (drawn_grid_rect.position.x + drawn_grid_rect.size.x); x++) { + for (int y = drawn_grid_rect.position.y; y < (drawn_grid_rect.position.y + drawn_grid_rect.size.y); y++) { + Vector2i pos_in_rect = Vector2i(x, y) - drawn_grid_rect.position; + + // Fade out the border of the grid. + float left_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.x), 0.0f, 1.0f); + float right_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.x, (float)(drawn_grid_rect.size.x - fading), (float)pos_in_rect.x), 0.0f, 1.0f); + float top_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.y), 0.0f, 1.0f); + float bottom_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.y, (float)(drawn_grid_rect.size.y - fading), (float)pos_in_rect.y), 0.0f, 1.0f); + float opacity = CLAMP(MIN(left_opacity, MIN(right_opacity, MIN(top_opacity, bottom_opacity))) + 0.1, 0.0f, 1.0f); + + Rect2 cell_region = xform.xform(Rect2(tile_map->map_to_world(Vector2(x, y)) - tile_shape_size / 2, tile_shape_size)); + Color color = grid_color; + color.a = color.a * opacity; + tile_set->draw_tile_shape(p_overlay, cell_region, color, false); + } } } } // Draw the preview. for (Map<Vector2i, TileMapCell>::Element *E = preview.front(); E; E = E->next()) { + Vector2i size = tile_set->get_tile_size(); + Vector2 position = tile_map->map_to_world(E->key()) - size / 2; + Rect2 cell_region = xform.xform(Rect2(position, size)); if (!erase_button->is_pressed() && random_tile_checkbox->is_pressed()) { - Vector2i size = tile_set->get_tile_size(); - Vector2 position = tile_map->map_to_world(E->key()) - size / 2; - Rect2 cell_region = xform.xform(Rect2(position, size)); - tile_set->draw_tile_shape(p_overlay, cell_region, Color(1.0, 1.0, 1.0, 0.5), true); } else { if (tile_set->has_source(E->get().source_id)) { @@ -629,12 +789,10 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over // Draw the tile. p_overlay->draw_texture_rect_region(atlas_source->get_texture(), dest_rect, source_rect, modulate * Color(1.0, 1.0, 1.0, 0.5), transpose, tile_set->is_uv_clipping()); + } else { + tile_set->draw_tile_shape(p_overlay, cell_region, Color(1.0, 1.0, 1.0, 0.5), true); } } else { - Vector2i size = tile_set->get_tile_size(); - Vector2 position = tile_map->map_to_world(E->key()) - size / 2; - Rect2 cell_region = xform.xform(Rect2(position, size)); - tile_set->draw_tile_shape(p_overlay, cell_region, Color(0.0, 0.0, 0.0, 0.5), true); } } @@ -669,7 +827,9 @@ TileMapCell TileMapEditorTilesPlugin::_pick_random_tile(const TileMapPattern *p_ TileSetSource *source = *tile_set->get_source(source_id); TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); if (atlas_source) { - sum += Object::cast_to<TileData>(atlas_source->get_tile_data(atlas_coords, alternative_tile))->get_probability(); + TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(atlas_coords, alternative_tile)); + ERR_FAIL_COND_V(!tile_data, TileMapCell()); + sum += tile_data->get_probability(); } else { sum += 1.0; } @@ -698,7 +858,7 @@ TileMapCell TileMapEditorTilesPlugin::_pick_random_tile(const TileMapPattern *p_ return TileMapCell(); } -Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_line(Vector2 p_start_drag_mouse_pos, Vector2 p_from_mouse_pos, Vector2i p_to_mouse_pos) { +Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_line(Vector2 p_start_drag_mouse_pos, Vector2 p_from_mouse_pos, Vector2 p_to_mouse_pos) { TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); if (!tile_map) { return Map<Vector2i, TileMapCell>(); @@ -711,7 +871,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_line(Vector2 p_start_ // Get or create the pattern. TileMapPattern erase_pattern; - erase_pattern.set_cell(Vector2i(0, 0), -1, TileSetAtlasSource::INVALID_ATLAS_COORDS, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE); + erase_pattern.set_cell(Vector2i(0, 0), -1, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); TileMapPattern *pattern = erase_button->is_pressed() ? &erase_pattern : selection_pattern; Map<Vector2i, TileMapCell> output; @@ -763,7 +923,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_rect(Vector2i p_start // Get or create the pattern. TileMapPattern erase_pattern; - erase_pattern.set_cell(Vector2i(0, 0), -1, TileSetAtlasSource::INVALID_ATLAS_COORDS, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE); + erase_pattern.set_cell(Vector2i(0, 0), -1, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); TileMapPattern *pattern = erase_button->is_pressed() ? &erase_pattern : selection_pattern; // Compute the offset to align things to the bottom or right. @@ -814,7 +974,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i // Get or create the pattern. TileMapPattern erase_pattern; - erase_pattern.set_cell(Vector2i(0, 0), -1, TileSetAtlasSource::INVALID_ATLAS_COORDS, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE); + erase_pattern.set_cell(Vector2i(0, 0), -1, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); TileMapPattern *pattern = erase_button->is_pressed() ? &erase_pattern : selection_pattern; Map<Vector2i, TileMapCell> output; @@ -1090,8 +1250,8 @@ void TileMapEditorTilesPlugin::_update_fix_selected_and_hovered() { TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); if (!tile_map) { hovered_tile.source_id = -1; - hovered_tile.set_atlas_coords(TileSetAtlasSource::INVALID_ATLAS_COORDS); - hovered_tile.alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE; + hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS); + hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE; tile_set_selection.clear(); tile_map_selection.clear(); selection_pattern->clear(); @@ -1101,8 +1261,8 @@ void TileMapEditorTilesPlugin::_update_fix_selected_and_hovered() { Ref<TileSet> tile_set = tile_map->get_tileset(); if (!tile_set.is_valid()) { hovered_tile.source_id = -1; - hovered_tile.set_atlas_coords(TileSetAtlasSource::INVALID_ATLAS_COORDS); - hovered_tile.alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE; + hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS); + hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE; tile_set_selection.clear(); tile_map_selection.clear(); selection_pattern->clear(); @@ -1112,8 +1272,8 @@ void TileMapEditorTilesPlugin::_update_fix_selected_and_hovered() { int source_index = sources_list->get_current(); if (source_index < 0 || source_index >= sources_list->get_item_count()) { hovered_tile.source_id = -1; - hovered_tile.set_atlas_coords(TileSetAtlasSource::INVALID_ATLAS_COORDS); - hovered_tile.alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE; + hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS); + hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE; tile_set_selection.clear(); tile_map_selection.clear(); selection_pattern->clear(); @@ -1128,8 +1288,8 @@ void TileMapEditorTilesPlugin::_update_fix_selected_and_hovered() { !tile_set->get_source(hovered_tile.source_id)->has_tile(hovered_tile.get_atlas_coords()) || !tile_set->get_source(hovered_tile.source_id)->has_alternative_tile(hovered_tile.get_atlas_coords(), hovered_tile.alternative_tile)) { hovered_tile.source_id = -1; - hovered_tile.set_atlas_coords(TileSetAtlasSource::INVALID_ATLAS_COORDS); - hovered_tile.alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE; + hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS); + hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE; } // Selection if needed. @@ -1187,6 +1347,7 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_selection( per_source[E->get().source_id].push_back(&(E->get())); } + int vertical_offset = 0; for (Map<int, List<const TileMapCell *>>::Element *E_source = per_source.front(); E_source; E_source = E_source->next()) { // Per source. List<const TileMapCell *> unorganized; @@ -1199,24 +1360,21 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_selection( // Organize using coordinates. for (List<const TileMapCell *>::Element *E_cell = E_source->get().front(); E_cell; E_cell = E_cell->next()) { const TileMapCell *current = E_cell->get(); - if (organized_pattern.has(current->get_atlas_coords())) { - if (current->alternative_tile < organized_pattern[current->get_atlas_coords()]->alternative_tile) { - unorganized.push_back(organized_pattern[current->get_atlas_coords()]); - organized_pattern[current->get_atlas_coords()] = current; - } else { - unorganized.push_back(current); - } - } else { + if (current->alternative_tile == 0) { organized_pattern[current->get_atlas_coords()] = current; + } else { + unorganized.push_back(current); } } // Compute the encompassing rect for the organized pattern. Map<Vector2i, const TileMapCell *>::Element *E_cell = organized_pattern.front(); - encompassing_rect_coords = Rect2i(E_cell->key(), Vector2i(1, 1)); - for (; E_cell; E_cell = E_cell->next()) { - encompassing_rect_coords.expand_to(E_cell->key() + Vector2i(1, 1)); - encompassing_rect_coords.expand_to(E_cell->key()); + if (E_cell) { + encompassing_rect_coords = Rect2i(E_cell->key(), Vector2i(1, 1)); + for (; E_cell; E_cell = E_cell->next()) { + encompassing_rect_coords.expand_to(E_cell->key() + Vector2i(1, 1)); + encompassing_rect_coords.expand_to(E_cell->key()); + } } } else { // Add everything unorganized. @@ -1227,12 +1385,15 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_selection( // Now add everything to the output pattern. for (Map<Vector2i, const TileMapCell *>::Element *E_cell = organized_pattern.front(); E_cell; E_cell = E_cell->next()) { - selection_pattern->set_cell(E_cell->key() - encompassing_rect_coords.position, E_cell->get()->source_id, E_cell->get()->get_atlas_coords(), E_cell->get()->alternative_tile); + selection_pattern->set_cell(E_cell->key() - encompassing_rect_coords.position + Vector2i(0, vertical_offset), E_cell->get()->source_id, E_cell->get()->get_atlas_coords(), E_cell->get()->alternative_tile); } Vector2i organized_size = selection_pattern->get_size(); + int unorganized_index = 0; for (List<const TileMapCell *>::Element *E_cell = unorganized.front(); E_cell; E_cell = E_cell->next()) { - selection_pattern->set_cell(Vector2(organized_size.x, 0), E_cell->get()->source_id, E_cell->get()->get_atlas_coords(), E_cell->get()->alternative_tile); + selection_pattern->set_cell(Vector2(organized_size.x + unorganized_index, vertical_offset), E_cell->get()->source_id, E_cell->get()->get_atlas_coords(), E_cell->get()->alternative_tile); + unorganized_index++; } + vertical_offset += MAX(organized_size.y, 1); } CanvasItemEditor::get_singleton()->update_viewport(); } @@ -1246,7 +1407,7 @@ void TileMapEditorTilesPlugin::_update_tileset_selection_from_selection_pattern( tile_set_selection.insert(TileMapCell(selection_pattern->get_cell_source_id(coords), selection_pattern->get_cell_atlas_coords(coords), selection_pattern->get_cell_alternative_tile(coords))); } } - _update_atlas_view(); + _update_bottom_panel(); } void TileMapEditorTilesPlugin::_tile_atlas_control_draw() { @@ -1276,14 +1437,16 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_draw() { } // Draw the selection. + Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color"); + Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0); for (Set<TileMapCell>::Element *E = tile_set_selection.front(); E; E = E->next()) { if (E->get().source_id == source_id && E->get().alternative_tile == 0) { - tile_atlas_control->draw_rect(atlas->get_tile_texture_region(E->get().get_atlas_coords()), Color(0.0, 0.0, 1.0), false); + tile_atlas_control->draw_rect(atlas->get_tile_texture_region(E->get().get_atlas_coords()), selection_color, false); } } // Draw the hovered tile. - if (hovered_tile.get_atlas_coords() != TileSetAtlasSource::INVALID_ATLAS_COORDS && hovered_tile.alternative_tile == 0 && !tile_set_dragging_selection) { + if (hovered_tile.get_atlas_coords() != TileSetSource::INVALID_ATLAS_COORDS && hovered_tile.alternative_tile == 0 && !tile_set_dragging_selection) { tile_atlas_control->draw_rect(atlas->get_tile_texture_region(hovered_tile.get_atlas_coords()), Color(1.0, 1.0, 1.0), false); } @@ -1299,22 +1462,22 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_draw() { for (int x = region.position.x; x < region.get_end().x; x++) { for (int y = region.position.y; y < region.get_end().y; y++) { Vector2i tile = atlas->get_tile_at_coords(Vector2i(x, y)); - if (tile != TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (tile != TileSetSource::INVALID_ATLAS_COORDS) { to_draw.insert(tile); } } } - + Color selection_rect_color = selection_color.lightened(0.2); for (Set<Vector2i>::Element *E = to_draw.front(); E; E = E->next()) { - tile_atlas_control->draw_rect(atlas->get_tile_texture_region(E->get()), Color(0.8, 0.8, 1.0), false); + tile_atlas_control->draw_rect(atlas->get_tile_texture_region(E->get()), selection_rect_color, false); } } } void TileMapEditorTilesPlugin::_tile_atlas_control_mouse_exited() { hovered_tile.source_id = -1; - hovered_tile.set_atlas_coords(TileSetAtlasSource::INVALID_ATLAS_COORDS); - hovered_tile.alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE; + hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS); + hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE; tile_set_dragging_selection = false; tile_atlas_control->update(); } @@ -1347,12 +1510,12 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_gui_input(const Ref<InputEven // Update the hovered tile hovered_tile.source_id = source_id; - hovered_tile.set_atlas_coords(TileSetAtlasSource::INVALID_ATLAS_COORDS); - hovered_tile.alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE; + hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS); + hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE; Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position()); - if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { coords = atlas->get_tile_at_coords(coords); - if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { hovered_tile.set_atlas_coords(coords); hovered_tile.alternative_tile = 0; } @@ -1373,7 +1536,7 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_gui_input(const Ref<InputEven tile_set_selection.clear(); } - if (hovered_tile.get_atlas_coords() != TileSetAtlasSource::INVALID_ATLAS_COORDS && hovered_tile.alternative_tile == 0) { + if (hovered_tile.get_atlas_coords() != TileSetSource::INVALID_ATLAS_COORDS && hovered_tile.alternative_tile == 0) { if (mb->is_shift_pressed() && tile_set_selection.has(TileMapCell(source_id, hovered_tile.get_atlas_coords(), 0))) { tile_set_selection.erase(TileMapCell(source_id, hovered_tile.get_atlas_coords(), 0)); } else { @@ -1389,18 +1552,18 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_gui_input(const Ref<InputEven // Compute the covered area. Vector2i start_tile = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_set_drag_start_mouse_pos); Vector2i end_tile = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position()); - if (start_tile != TileSetAtlasSource::INVALID_ATLAS_COORDS && end_tile != TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (start_tile != TileSetSource::INVALID_ATLAS_COORDS && end_tile != TileSetSource::INVALID_ATLAS_COORDS) { Rect2i region = Rect2i(start_tile, end_tile - start_tile).abs(); region.size += Vector2i(1, 1); // To update the selection, we copy the selected/not selected status of the tiles we drag from. Vector2i start_coords = atlas->get_tile_at_coords(start_tile); - if (mb->is_shift_pressed() && start_coords != TileSetAtlasSource::INVALID_ATLAS_COORDS && !tile_set_selection.has(TileMapCell(source_id, start_coords, 0))) { + if (mb->is_shift_pressed() && start_coords != TileSetSource::INVALID_ATLAS_COORDS && !tile_set_selection.has(TileMapCell(source_id, start_coords, 0))) { // Remove from the selection. for (int x = region.position.x; x < region.get_end().x; x++) { for (int y = region.position.y; y < region.get_end().y; y++) { Vector2i tile_coords = atlas->get_tile_at_coords(Vector2i(x, y)); - if (tile_coords != TileSetAtlasSource::INVALID_ATLAS_COORDS && tile_set_selection.has(TileMapCell(source_id, tile_coords, 0))) { + if (tile_coords != TileSetSource::INVALID_ATLAS_COORDS && tile_set_selection.has(TileMapCell(source_id, tile_coords, 0))) { tile_set_selection.erase(TileMapCell(source_id, tile_coords, 0)); } } @@ -1410,7 +1573,7 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_gui_input(const Ref<InputEven for (int x = region.position.x; x < region.get_end().x; x++) { for (int y = region.position.y; y < region.get_end().y; y++) { Vector2i tile_coords = atlas->get_tile_at_coords(Vector2i(x, y)); - if (tile_coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (tile_coords != TileSetSource::INVALID_ATLAS_COORDS) { tile_set_selection.insert(TileMapCell(source_id, tile_coords, 0)); } } @@ -1453,7 +1616,7 @@ void TileMapEditorTilesPlugin::_tile_alternatives_control_draw() { // Draw the selection. for (Set<TileMapCell>::Element *E = tile_set_selection.front(); E; E = E->next()) { - if (E->get().source_id == source_id && E->get().get_atlas_coords() != TileSetAtlasSource::INVALID_ATLAS_COORDS && E->get().alternative_tile > 0) { + if (E->get().source_id == source_id && E->get().get_atlas_coords() != TileSetSource::INVALID_ATLAS_COORDS && E->get().alternative_tile > 0) { Rect2i rect = tile_atlas_view->get_alternative_tile_rect(E->get().get_atlas_coords(), E->get().alternative_tile); if (rect != Rect2i()) { alternative_tiles_control->draw_rect(rect, Color(0.2, 0.2, 1.0), false); @@ -1462,7 +1625,7 @@ void TileMapEditorTilesPlugin::_tile_alternatives_control_draw() { } // Draw hovered tile. - if (hovered_tile.get_atlas_coords() != TileSetAtlasSource::INVALID_ATLAS_COORDS && hovered_tile.alternative_tile > 0) { + if (hovered_tile.get_atlas_coords() != TileSetSource::INVALID_ATLAS_COORDS && hovered_tile.alternative_tile > 0) { Rect2i rect = tile_atlas_view->get_alternative_tile_rect(hovered_tile.get_atlas_coords(), hovered_tile.alternative_tile); if (rect != Rect2i()) { alternative_tiles_control->draw_rect(rect, Color(1.0, 1.0, 1.0), false); @@ -1472,8 +1635,8 @@ void TileMapEditorTilesPlugin::_tile_alternatives_control_draw() { void TileMapEditorTilesPlugin::_tile_alternatives_control_mouse_exited() { hovered_tile.source_id = -1; - hovered_tile.set_atlas_coords(TileSetAtlasSource::INVALID_ATLAS_COORDS); - hovered_tile.alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE; + hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS); + hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE; tile_set_dragging_selection = false; alternative_tiles_control->update(); } @@ -1506,12 +1669,12 @@ void TileMapEditorTilesPlugin::_tile_alternatives_control_gui_input(const Ref<In // Update the hovered tile hovered_tile.source_id = source_id; - hovered_tile.set_atlas_coords(TileSetAtlasSource::INVALID_ATLAS_COORDS); - hovered_tile.alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE; + hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS); + hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE; Vector3i alternative_coords = tile_atlas_view->get_alternative_tile_at_pos(alternative_tiles_control->get_local_mouse_position()); Vector2i coords = Vector2i(alternative_coords.x, alternative_coords.y); int alternative = alternative_coords.z; - if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS && alternative != TileSetAtlasSource::INVALID_TILE_ALTERNATIVE) { + if (coords != TileSetSource::INVALID_ATLAS_COORDS && alternative != TileSetSource::INVALID_TILE_ALTERNATIVE) { hovered_tile.set_atlas_coords(coords); hovered_tile.alternative_tile = alternative; } @@ -1530,7 +1693,7 @@ void TileMapEditorTilesPlugin::_tile_alternatives_control_gui_input(const Ref<In tile_set_selection.clear(); } - if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS && alternative != TileSetAtlasSource::INVALID_TILE_ALTERNATIVE) { + if (coords != TileSetSource::INVALID_ATLAS_COORDS && alternative != TileSetAtlasSource::INVALID_TILE_ALTERNATIVE) { if (mb->is_shift_pressed() && tile_set_selection.has(TileMapCell(source_id, hovered_tile.get_atlas_coords(), hovered_tile.alternative_tile))) { tile_set_selection.erase(TileMapCell(source_id, hovered_tile.get_atlas_coords(), hovered_tile.alternative_tile)); } else { @@ -1565,13 +1728,14 @@ TypedArray<Vector2i> TileMapEditorTilesPlugin::_get_tile_map_selection() const { void TileMapEditorTilesPlugin::edit(ObjectID p_tile_map_id) { tile_map_id = p_tile_map_id; - // Clean the selection. + // Clear the selection. tile_set_selection.clear(); tile_map_selection.clear(); selection_pattern->clear(); } void TileMapEditorTilesPlugin::_bind_methods() { + ClassDB::bind_method(D_METHOD("_scene_thumbnail_done"), &TileMapEditorTilesPlugin::_scene_thumbnail_done); ClassDB::bind_method(D_METHOD("_set_tile_map_selection", "selection"), &TileMapEditorTilesPlugin::_set_tile_map_selection); ClassDB::bind_method(D_METHOD("_get_tile_map_selection"), &TileMapEditorTilesPlugin::_get_tile_map_selection); } @@ -1588,6 +1752,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() { // --- Toolbar --- toolbar = memnew(HBoxContainer); + toolbar->set_h_size_flags(SIZE_EXPAND_FILL); HBoxContainer *tilemap_tiles_tools_buttons = memnew(HBoxContainer); @@ -1632,7 +1797,6 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() { bucket_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/bucket_tool", "Bucket", KEY_B)); bucket_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_update_toolbar)); tilemap_tiles_tools_buttons->add_child(bucket_tool_button); - toolbar->add_child(tilemap_tiles_tools_buttons); // -- TileMap tool settings -- @@ -1692,6 +1856,18 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() { _on_random_tile_checkbox_toggled(false); + // Wide empty separation control. + Control *h_empty_space = memnew(Control); + h_empty_space->set_h_size_flags(SIZE_EXPAND_FILL); + toolbar->add_child(h_empty_space); + + // Grid toggle. + toggle_grid_button = memnew(Button); + toggle_grid_button->set_flat(true); + toggle_grid_button->set_toggle_mode(true); + toggle_grid_button->connect("toggled", callable_mp(this, &TileMapEditorTilesPlugin::_on_grid_toggled)); + toolbar->add_child(toggle_grid_button); + // Default tool. paint_tool_button->set_pressed(true); _update_toolbar(); @@ -1718,20 +1894,20 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() { sources_list->set_h_size_flags(SIZE_EXPAND_FILL); sources_list->set_stretch_ratio(0.25); sources_list->set_custom_minimum_size(Size2i(70, 0) * EDSCALE); + sources_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST); sources_list->connect("item_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_fix_selected_and_hovered).unbind(1)); - sources_list->connect("item_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_atlas_view).unbind(1)); + sources_list->connect("item_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_bottom_panel).unbind(1)); sources_list->connect("item_selected", callable_mp(TilesEditor::get_singleton(), &TilesEditor::set_atlas_sources_lists_current)); sources_list->connect("visibility_changed", callable_mp(TilesEditor::get_singleton(), &TilesEditor::synchronize_atlas_sources_list), varray(sources_list)); - //sources_list->set_drag_forwarding(this); atlas_sources_split_container->add_child(sources_list); + // Tile atlas source. tile_atlas_view = memnew(TileAtlasView); tile_atlas_view->set_h_size_flags(SIZE_EXPAND_FILL); tile_atlas_view->set_v_size_flags(SIZE_EXPAND_FILL); tile_atlas_view->set_texture_grid_visible(false); tile_atlas_view->set_tile_shape_grid_visible(false); tile_atlas_view->connect("transform_changed", callable_mp(TilesEditor::get_singleton(), &TilesEditor::set_atlas_view_transform)); - //tile_atlas_view->connect("visibility_changed", callable_mp(TilesEditor::get_singleton(), &TilesEditor::synchronize_atlas_view), varray(tile_atlas_view)); atlas_sources_split_container->add_child(tile_atlas_view); tile_atlas_control = memnew(Control); @@ -1746,6 +1922,27 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() { alternative_tiles_control->connect("gui_input", callable_mp(this, &TileMapEditorTilesPlugin::_tile_alternatives_control_gui_input)); tile_atlas_view->add_control_over_alternative_tiles(alternative_tiles_control); + // Scenes collection source. + scene_tiles_list = memnew(ItemList); + scene_tiles_list->set_h_size_flags(SIZE_EXPAND_FILL); + scene_tiles_list->set_v_size_flags(SIZE_EXPAND_FILL); + scene_tiles_list->set_drag_forwarding(this); + scene_tiles_list->set_select_mode(ItemList::SELECT_MULTI); + scene_tiles_list->connect("multi_selected", callable_mp(this, &TileMapEditorTilesPlugin::_scenes_list_multi_selected)); + scene_tiles_list->connect("nothing_selected", callable_mp(this, &TileMapEditorTilesPlugin::_scenes_list_nothing_selected)); + scene_tiles_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST); + atlas_sources_split_container->add_child(scene_tiles_list); + + // Invalid source label. + invalid_source_label = memnew(Label); + invalid_source_label->set_text(TTR("Invalid source selected.")); + invalid_source_label->set_h_size_flags(SIZE_EXPAND_FILL); + invalid_source_label->set_v_size_flags(SIZE_EXPAND_FILL); + invalid_source_label->set_align(Label::ALIGN_CENTER); + invalid_source_label->set_valign(Label::VALIGN_CENTER); + invalid_source_label->hide(); + atlas_sources_split_container->add_child(invalid_source_label); + _update_bottom_panel(); } @@ -2771,8 +2968,8 @@ void TileMapEditorTerrainsPlugin::_update_terrains_cache() { TileMapCell empty_cell; empty_cell.source_id = -1; - empty_cell.set_atlas_coords(TileSetAtlasSource::INVALID_ATLAS_COORDS); - empty_cell.alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE; + empty_cell.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS); + empty_cell.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE; per_terrain_terrains_tile_patterns_tiles[i][empty_pattern].insert(empty_cell); } } @@ -3263,19 +3460,25 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { } // Draw the grid. - for (int x = displayed_rect.position.x; x < (displayed_rect.position.x + displayed_rect.size.x); x++) { - for (int y = displayed_rect.position.y; y < (displayed_rect.position.y + displayed_rect.size.y); y++) { - Vector2i pos_in_rect = Vector2i(x, y) - displayed_rect.position; + bool display_grid = EditorSettings::get_singleton()->get("editors/tiles_editor/display_grid"); + if (display_grid) { + Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color"); + for (int x = displayed_rect.position.x; x < (displayed_rect.position.x + displayed_rect.size.x); x++) { + for (int y = displayed_rect.position.y; y < (displayed_rect.position.y + displayed_rect.size.y); y++) { + Vector2i pos_in_rect = Vector2i(x, y) - displayed_rect.position; - // Fade out the border of the grid. - float left_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.x), 0.0f, 1.0f); - float right_opacity = CLAMP(Math::inverse_lerp((float)displayed_rect.size.x, (float)(displayed_rect.size.x - fading), (float)pos_in_rect.x), 0.0f, 1.0f); - float top_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.y), 0.0f, 1.0f); - float bottom_opacity = CLAMP(Math::inverse_lerp((float)displayed_rect.size.y, (float)(displayed_rect.size.y - fading), (float)pos_in_rect.y), 0.0f, 1.0f); - float opacity = CLAMP(MIN(left_opacity, MIN(right_opacity, MIN(top_opacity, bottom_opacity))) + 0.1, 0.0f, 1.0f); + // Fade out the border of the grid. + float left_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.x), 0.0f, 1.0f); + float right_opacity = CLAMP(Math::inverse_lerp((float)displayed_rect.size.x, (float)(displayed_rect.size.x - fading), (float)pos_in_rect.x), 0.0f, 1.0f); + float top_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.y), 0.0f, 1.0f); + float bottom_opacity = CLAMP(Math::inverse_lerp((float)displayed_rect.size.y, (float)(displayed_rect.size.y - fading), (float)pos_in_rect.y), 0.0f, 1.0f); + float opacity = CLAMP(MIN(left_opacity, MIN(right_opacity, MIN(top_opacity, bottom_opacity))) + 0.1, 0.0f, 1.0f); - Rect2 cell_region = xform.xform(Rect2(tile_map->map_to_world(Vector2(x, y)) - tile_shape_size / 2, tile_shape_size)); - tile_set->draw_tile_shape(p_overlay, cell_region, Color(1.0, 0.5, 0.2, 0.5 * opacity), false); + Rect2 cell_region = xform.xform(Rect2(tile_map->map_to_world(Vector2(x, y)) - tile_shape_size / 2, tile_shape_size)); + Color color = grid_color; + color.a = color.a * opacity; + tile_set->draw_tile_shape(p_overlay, cell_region, color, false); + } } } @@ -3337,9 +3540,8 @@ TileMapEditor::TileMapEditor() { // --- TileMap toolbar --- tilemap_toolbar = memnew(HBoxContainer); - //tilemap_toolbar->add_child(memnew(VSeparator)); + tilemap_toolbar->set_h_size_flags(SIZE_EXPAND_FILL); tilemap_toolbar->add_child(tabs); - //tilemap_toolbar->add_child(memnew(VSeparator)); for (int i = 0; i < tile_map_editor_plugins.size(); i++) { tile_map_editor_plugins[i]->get_toolbar()->hide(); tilemap_toolbar->add_child(tile_map_editor_plugins[i]->get_toolbar()); diff --git a/editor/plugins/tiles/tile_map_editor.h b/editor/plugins/tiles/tile_map_editor.h index f780686b82..a6f4ec3021 100644 --- a/editor/plugins/tiles/tile_map_editor.h +++ b/editor/plugins/tiles/tile_map_editor.h @@ -58,7 +58,7 @@ private: ObjectID tile_map_id; virtual void edit(ObjectID p_tile_map_id) override; - // Toolbar. + ///// Toolbar ///// HBoxContainer *toolbar; Ref<ButtonGroup> tool_buttons_group; @@ -82,9 +82,12 @@ private: void _on_random_tile_checkbox_toggled(bool p_pressed); void _on_scattering_spinbox_changed(double p_value); + Button *toggle_grid_button; + void _on_grid_toggled(bool p_pressed); + void _update_toolbar(); - // Tilemap editing. + ///// Tilemap editing. ///// bool has_mouse = false; void _mouse_exited_viewport(); @@ -105,12 +108,12 @@ private: Map<Vector2i, TileMapCell> drag_modified; TileMapCell _pick_random_tile(const TileMapPattern *p_pattern); - Map<Vector2i, TileMapCell> _draw_line(Vector2 p_start_drag_mouse_pos, Vector2 p_from_mouse_pos, Vector2i p_to_mouse_pos); - Map<Vector2i, TileMapCell> _draw_rect(Vector2i p_start_mouse_pos, Vector2i p_end_mouse_pos); + Map<Vector2i, TileMapCell> _draw_line(Vector2 p_start_drag_mouse_pos, Vector2 p_from_mouse_pos, Vector2 p_to_mouse_pos); + Map<Vector2i, TileMapCell> _draw_rect(Vector2i p_start_cell, Vector2i p_end_cell); Map<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous); void _stop_dragging(); - // Selection system. + ///// Selection system. ///// Set<Vector2i> tile_map_selection; TileMapPattern *tile_map_clipboard = memnew(TileMapPattern); TileMapPattern *selection_pattern = memnew(TileMapPattern); @@ -124,22 +127,24 @@ private: void _update_tileset_selection_from_selection_pattern(); void _update_fix_selected_and_hovered(); - // Bottom panel. - bool tile_set_dragging_selection = false; - Vector2i tile_set_drag_start_mouse_pos; - + ///// Bottom panel. ////. Label *missing_source_label; - HSplitContainer *atlas_sources_split_container; + Label *invalid_source_label; ItemList *sources_list; - TileAtlasView *tile_atlas_view; - Ref<Texture2D> missing_texture_texture; + + Ref<Texture2D> missing_atlas_texture_icon; void _update_tile_set_sources_list(); - void _update_atlas_view(); void _update_bottom_panel(); + // Atlas sources. TileMapCell hovered_tile; + TileAtlasView *tile_atlas_view; + HSplitContainer *atlas_sources_split_container; + + bool tile_set_dragging_selection = false; + Vector2i tile_set_drag_start_mouse_pos; Control *tile_atlas_control; void _tile_atlas_control_mouse_exited(); @@ -151,6 +156,16 @@ private: void _tile_alternatives_control_mouse_exited(); void _tile_alternatives_control_gui_input(const Ref<InputEvent> &p_event); + void _update_atlas_view(); + + // Scenes collection sources. + ItemList *scene_tiles_list; + + void _update_scenes_collection_view(); + void _scene_thumbnail_done(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, Variant p_ud); + void _scenes_list_multi_selected(int p_index, bool p_selected); + void _scenes_list_nothing_selected(); + // Update callback virtual void tile_set_changed() override; diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp index 324e429592..8e7d613027 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp @@ -53,10 +53,10 @@ void TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::set_id(int p_id) { if (source_id == p_id) { return; } - ERR_FAIL_COND_MSG(tile_set->has_source(p_id), vformat("Cannot change TileSet atlas source ID. Another atlas source exists with id %d.", p_id)); + ERR_FAIL_COND_MSG(tile_set->has_source(p_id), vformat("Cannot change TileSet Atlas Source ID. Another source exists with id %d.", p_id)); int previous_source = source_id; - source_id = p_id; // source_id must be updated before, because it's used by the atlas source list update. + source_id = p_id; // source_id must be updated before, because it's used by the source list update. tile_set->set_source_id(previous_source, p_id); emit_signal("changed", "id"); } @@ -126,7 +126,7 @@ void TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::edit(Ref<TileSet> } // -- Proxy object used by the tile inspector -- -bool TileSetAtlasSourceEditor::TileProxyObject::_set(const StringName &p_name, const Variant &p_value) { +bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_set(const StringName &p_name, const Variant &p_value) { if (!tile_set_atlas_source) { return false; } @@ -152,9 +152,9 @@ bool TileSetAtlasSourceEditor::TileProxyObject::_set(const StringName &p_name, c return true; } else if (alternative == 0 && p_name == "size_in_atlas") { Vector2i as_vector2i = Vector2i(p_value); - ERR_FAIL_COND_V(!tile_set_atlas_source->can_move_tile_in_atlas(coords, TileSetAtlasSource::INVALID_ATLAS_COORDS, as_vector2i), false); + ERR_FAIL_COND_V(!tile_set_atlas_source->can_move_tile_in_atlas(coords, TileSetSource::INVALID_ATLAS_COORDS, as_vector2i), false); - tile_set_atlas_source->move_tile_in_atlas(coords, TileSetAtlasSource::INVALID_ATLAS_COORDS, as_vector2i); + tile_set_atlas_source->move_tile_in_atlas(coords, TileSetSource::INVALID_ATLAS_COORDS, as_vector2i); emit_signal("changed", "size_in_atlas"); return true; } else if (alternative > 0 && p_name == "alternative_id") { @@ -197,7 +197,7 @@ bool TileSetAtlasSourceEditor::TileProxyObject::_set(const StringName &p_name, c return any_valid; } -bool TileSetAtlasSourceEditor::TileProxyObject::_get(const StringName &p_name, Variant &r_ret) const { +bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_get(const StringName &p_name, Variant &r_ret) const { if (!tile_set_atlas_source) { return false; } @@ -237,7 +237,7 @@ bool TileSetAtlasSourceEditor::TileProxyObject::_get(const StringName &p_name, V return false; } -void TileSetAtlasSourceEditor::TileProxyObject::_get_property_list(List<PropertyInfo> *p_list) const { +void TileSetAtlasSourceEditor::AtlasTileProxyObject::_get_property_list(List<PropertyInfo> *p_list) const { if (!tile_set_atlas_source) { return; } @@ -310,12 +310,11 @@ void TileSetAtlasSourceEditor::TileProxyObject::_get_property_list(List<Property } } -void TileSetAtlasSourceEditor::TileProxyObject::edit(TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id, Set<TileSelection> p_tiles) { +void TileSetAtlasSourceEditor::AtlasTileProxyObject::edit(TileSetAtlasSource *p_tile_set_atlas_source, Set<TileSelection> p_tiles) { ERR_FAIL_COND(!p_tile_set_atlas_source); - ERR_FAIL_COND(p_source_id < 0); ERR_FAIL_COND(p_tiles.is_empty()); for (Set<TileSelection>::Element *E = p_tiles.front(); E; E = E->next()) { - ERR_FAIL_COND(E->get().tile == TileSetAtlasSource::INVALID_ATLAS_COORDS); + ERR_FAIL_COND(E->get().tile == TileSetSource::INVALID_ATLAS_COORDS); ERR_FAIL_COND(E->get().alternative < 0); } @@ -333,7 +332,6 @@ void TileSetAtlasSourceEditor::TileProxyObject::edit(TileSetAtlasSource *p_tile_ } tile_set_atlas_source = p_tile_set_atlas_source; - source_id = p_source_id; tiles = Set<TileSelection>(p_tiles); // Connect to changes. @@ -352,7 +350,7 @@ void TileSetAtlasSourceEditor::TileProxyObject::edit(TileSetAtlasSource *p_tile_ notify_property_list_changed(); } -void TileSetAtlasSourceEditor::TileProxyObject::_bind_methods() { +void TileSetAtlasSourceEditor::AtlasTileProxyObject::_bind_methods() { ADD_SIGNAL(MethodInfo("changed", PropertyInfo(Variant::STRING, "what"))); } @@ -391,12 +389,12 @@ void TileSetAtlasSourceEditor::_update_fix_selected_and_hovered_tiles() { // Fix hovered. if (!tile_set_atlas_source->has_tile(hovered_base_tile_coords)) { - hovered_base_tile_coords = TileSetAtlasSource::INVALID_ATLAS_COORDS; + hovered_base_tile_coords = TileSetSource::INVALID_ATLAS_COORDS; } Vector2i coords = Vector2i(hovered_alternative_tile_coords.x, hovered_alternative_tile_coords.y); int alternative = hovered_alternative_tile_coords.z; if (!tile_set_atlas_source->has_tile(coords) || !tile_set_atlas_source->has_alternative_tile(coords, alternative)) { - hovered_alternative_tile_coords = Vector3i(TileSetAtlasSource::INVALID_ATLAS_COORDS.x, TileSetAtlasSource::INVALID_ATLAS_COORDS.y, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE); + hovered_alternative_tile_coords = Vector3i(TileSetSource::INVALID_ATLAS_COORDS.x, TileSetSource::INVALID_ATLAS_COORDS.y, TileSetSource::INVALID_TILE_ALTERNATIVE); } } @@ -405,7 +403,7 @@ void TileSetAtlasSourceEditor::_update_tile_inspector() { // Update the proxy object. if (has_atlas_tile_selected) { - tile_proxy_object->edit(tile_set_atlas_source, tile_set_atlas_source_id, selection); + tile_proxy_object->edit(tile_set_atlas_source, selection); } // Update visibility. @@ -486,7 +484,7 @@ void TileSetAtlasSourceEditor::_update_toolbar() { } void TileSetAtlasSourceEditor::_tile_atlas_control_mouse_exited() { - hovered_base_tile_coords = TileSetAtlasSource::INVALID_ATLAS_COORDS; + hovered_base_tile_coords = TileSetSource::INVALID_ATLAS_COORDS; tile_atlas_control->update(); tile_atlas_control_unscaled->update(); tile_atlas_view->update(); @@ -514,7 +512,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven if (selection.size() == 1) { // Change the cursor depending on the hovered thing. TileSelection selected = selection.front()->get(); - if (selected.tile != TileSetAtlasSource::INVALID_ATLAS_COORDS && selected.alternative == 0) { + if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS && selected.alternative == 0) { Vector2 mouse_local_pos = tile_atlas_control->get_local_mouse_position(); Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(selected.tile); Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile); @@ -560,7 +558,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven Vector<Point2i> line = Geometry2D::bresenham_line(last_base_tiles_coords, new_base_tiles_coords); for (int i = 0; i < line.size(); i++) { - if (tile_set_atlas_source->get_tile_at_coords(line[i]) == TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (tile_set_atlas_source->get_tile_at_coords(line[i]) == TileSetSource::INVALID_ATLAS_COORDS) { tile_set_atlas_source->create_tile(line[i]); drag_modified_tiles.insert(line[i]); } @@ -576,7 +574,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven Vector<Point2i> line = Geometry2D::bresenham_line(last_base_tiles_coords, new_base_tiles_coords); for (int i = 0; i < line.size(); i++) { Vector2i base_tile_coords = tile_set_atlas_source->get_tile_at_coords(line[i]); - if (base_tile_coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (base_tile_coords != TileSetSource::INVALID_ATLAS_COORDS) { drag_modified_tiles.insert(base_tile_coords); } } @@ -662,17 +660,17 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven // Remove a first tile. Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos); - if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { coords = tile_set_atlas_source->get_tile_at_coords(coords); } - if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { drag_modified_tiles.insert(coords); } } else { if (mb->is_shift_pressed()) { // Create a big tile. Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(mouse_local_pos); - if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS && tile_set_atlas_source->get_tile_at_coords(coords) == TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (coords != TileSetSource::INVALID_ATLAS_COORDS && tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) { // Setup the dragging info, only if we start on an empty tile. drag_type = DRAG_TYPE_CREATE_BIG_TILE; drag_start_mouse_pos = mouse_local_pos; @@ -692,7 +690,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven // Create a first tile if needed. Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos); - if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS && tile_set_atlas_source->get_tile_at_coords(coords) == TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (coords != TileSetSource::INVALID_ATLAS_COORDS && tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) { tile_set_atlas_source->create_tile(coords); drag_modified_tiles.insert(coords); } @@ -710,7 +708,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven if (mb->is_shift_pressed()) { // Create a big tile. Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(mouse_local_pos); - if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS && tile_set_atlas_source->get_tile_at_coords(coords) == TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (coords != TileSetSource::INVALID_ATLAS_COORDS && tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) { // Setup the dragging info, only if we start on an empty tile. drag_type = DRAG_TYPE_CREATE_BIG_TILE; drag_start_mouse_pos = mouse_local_pos; @@ -732,7 +730,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven drag_type = DRAG_TYPE_NONE; if (selection.size() == 1) { TileSelection selected = selection.front()->get(); - if (selected.tile != TileSetAtlasSource::INVALID_ATLAS_COORDS && selected.alternative == 0) { + if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS && selected.alternative == 0) { Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(selected.tile); Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile); Size2 zoomed_size = resize_handle->get_size() / tile_atlas_view->get_zoom(); @@ -771,17 +769,17 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven // Selecting then dragging a tile. if (drag_type == DRAG_TYPE_NONE) { - TileSelection selected = { TileSetAtlasSource::INVALID_ATLAS_COORDS, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE }; + TileSelection selected = { TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE }; Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(mouse_local_pos); - if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { coords = tile_set_atlas_source->get_tile_at_coords(coords); - if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { selected = { coords, 0 }; } } bool shift = mb->is_shift_pressed(); - if (!shift && selection.size() == 1 && selected.tile != TileSetAtlasSource::INVALID_ATLAS_COORDS && selection.has(selected)) { + if (!shift && selection.size() == 1 && selected.tile != TileSetSource::INVALID_ATLAS_COORDS && selection.has(selected)) { // Start move dragging. drag_type = DRAG_TYPE_MOVE_TILE; drag_start_mouse_pos = mouse_local_pos; @@ -812,13 +810,13 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven // Right click pressed. TileSelection selected = { tile_atlas_view->get_atlas_tile_coords_at_pos(mouse_local_pos), 0 }; - if (selected.tile != TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS) { selected.tile = tile_set_atlas_source->get_tile_at_coords(selected.tile); } // Set the selection if needed. if (selection.size() <= 1) { - if (selected.tile != TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS) { undo_redo->create_action(TTR("Select tiles")); undo_redo->add_undo_method(this, "_set_selection_from_array", _get_selection_as_array()); selection.clear(); @@ -831,15 +829,15 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven } // Pops up the correct menu, depending on whether we have a tile or not. - if (selected.tile != TileSetAtlasSource::INVALID_ATLAS_COORDS && selection.has(selected)) { + if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS && selection.has(selected)) { // We have a tile. menu_option_coords = selected.tile; menu_option_alternative = 0; base_tile_popup_menu->popup(Rect2i(get_global_mouse_position(), Size2i())); - } else if (hovered_base_tile_coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) { + } else if (hovered_base_tile_coords != TileSetSource::INVALID_ATLAS_COORDS) { // We don't have a tile, but can create one. menu_option_coords = hovered_base_tile_coords; - menu_option_alternative = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE; + menu_option_alternative = TileSetSource::INVALID_TILE_ALTERNATIVE; empty_base_tile_popup_menu->popup(Rect2i(get_global_mouse_position(), Size2i())); } } else { @@ -902,7 +900,7 @@ void TileSetAtlasSourceEditor::_end_dragging() { for (int x = area.get_position().x; x < area.get_end().x; x++) { for (int y = area.get_position().y; y < area.get_end().y; y++) { Vector2i coords = Vector2i(x, y); - if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) { undo_redo->add_do_method(tile_set_atlas_source, "create_tile", coords); undo_redo->add_undo_method(tile_set_atlas_source, "remove_tile", coords); } @@ -923,7 +921,7 @@ void TileSetAtlasSourceEditor::_end_dragging() { for (int x = area.get_position().x; x < area.get_end().x; x++) { for (int y = area.get_position().y; y < area.get_end().y; y++) { Vector2i coords = tile_set_atlas_source->get_tile_at_coords(Vector2i(x, y)); - if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { to_delete.insert(coords); } } @@ -964,8 +962,8 @@ void TileSetAtlasSourceEditor::_end_dragging() { case DRAG_TYPE_RECT_SELECT: { Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos); Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position()); - ERR_FAIL_COND(start_base_tiles_coords == TileSetAtlasSource::INVALID_ATLAS_COORDS); - ERR_FAIL_COND(new_base_tiles_coords == TileSetAtlasSource::INVALID_ATLAS_COORDS); + ERR_FAIL_COND(start_base_tiles_coords == TileSetSource::INVALID_ATLAS_COORDS); + ERR_FAIL_COND(new_base_tiles_coords == TileSetSource::INVALID_ATLAS_COORDS); Rect2i region = Rect2i(start_base_tiles_coords, new_base_tiles_coords - start_base_tiles_coords).abs(); region.size += Vector2i(1, 1); @@ -977,7 +975,7 @@ void TileSetAtlasSourceEditor::_end_dragging() { bool add_to_selection = true; if (Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { Vector2i coords = tile_set_atlas_source->get_tile_at_coords(start_base_tiles_coords); - if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { if (selection.has({ coords, 0 })) { add_to_selection = false; } @@ -991,7 +989,7 @@ void TileSetAtlasSourceEditor::_end_dragging() { for (int y = region.position.y; y < region.get_end().y; y++) { Vector2i coords = Vector2i(x, y); coords = tile_set_atlas_source->get_tile_at_coords(coords); - if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { if (add_to_selection && !selection.has({ coords, 0 })) { selection.insert({ coords, 0 }); } else if (!add_to_selection && selection.has({ coords, 0 })) { @@ -1180,6 +1178,10 @@ Array TileSetAtlasSourceEditor::_get_selection_as_array() { } void TileSetAtlasSourceEditor::_tile_atlas_control_draw() { + // Colors. + Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color"); + Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0); + // Draw the selected tile. if (tools_button_group->get_pressed_button() == tool_select_button) { for (Set<TileSelection>::Element *E = selection.front(); E; E = E->next()) { @@ -1187,7 +1189,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() { if (selected.alternative == 0) { // Draw the rect. Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile); - tile_atlas_control->draw_rect(region, Color(0.2, 0.2, 1.0), false); + tile_atlas_control->draw_rect(region, selection_color, false); } } @@ -1236,14 +1238,14 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() { Color color = Color(0.0, 0.0, 0.0); if (drag_type == DRAG_TYPE_RECT_SELECT) { - color = Color(0.5, 0.5, 1.0); + color = selection_color.lightened(0.2); } Set<Vector2i> to_paint; for (int x = area.get_position().x; x < area.get_end().x; x++) { for (int y = area.get_position().y; y < area.get_end().y; y++) { Vector2i coords = tile_set_atlas_source->get_tile_at_coords(Vector2i(x, y)); - if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { to_paint.insert(coords); } } @@ -1266,7 +1268,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() { for (int x = area.get_position().x; x < area.get_end().x; x++) { for (int y = area.get_position().y; y < area.get_end().y; y++) { Vector2i coords = Vector2i(x, y); - if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) { Vector2i origin = margins + (coords * (tile_size + separation)); tile_atlas_control->draw_rect(Rect2i(origin, tile_size), Color(1.0, 1.0, 1.0), false); } @@ -1290,7 +1292,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() { Vector2i grid_size = tile_set_atlas_source->get_atlas_grid_size(); if (hovered_base_tile_coords.x >= 0 && hovered_base_tile_coords.y >= 0 && hovered_base_tile_coords.x < grid_size.x && hovered_base_tile_coords.y < grid_size.y) { Vector2i hovered_tile = tile_set_atlas_source->get_tile_at_coords(hovered_base_tile_coords); - if (hovered_tile != TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (hovered_tile != TileSetSource::INVALID_ATLAS_COORDS) { // Draw existing hovered tile. tile_atlas_control->draw_rect(tile_set_atlas_source->get_tile_texture_region(hovered_tile), Color(1.0, 1.0, 1.0), false); } else { @@ -1349,7 +1351,7 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_gui_input(const Ref<In selection.clear(); TileSelection selected = { Vector2i(tile.x, tile.y), int(tile.z) }; - if (selected.tile != TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS) { selection.insert(selected); } @@ -1364,7 +1366,7 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_gui_input(const Ref<In selection.clear(); TileSelection selected = { Vector2i(tile.x, tile.y), int(tile.z) }; - if (selected.tile != TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS) { selection.insert(selected); } @@ -1387,7 +1389,7 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_gui_input(const Ref<In } void TileSetAtlasSourceEditor::_tile_alternatives_control_mouse_exited() { - hovered_alternative_tile_coords = Vector3i(TileSetAtlasSource::INVALID_ATLAS_COORDS.x, TileSetAtlasSource::INVALID_ATLAS_COORDS.y, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE); + hovered_alternative_tile_coords = Vector3i(TileSetSource::INVALID_ATLAS_COORDS.x, TileSetSource::INVALID_ATLAS_COORDS.y, TileSetSource::INVALID_TILE_ALTERNATIVE); tile_atlas_control->update(); tile_atlas_control_unscaled->update(); alternative_tiles_control->update(); @@ -1395,11 +1397,14 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_mouse_exited() { } void TileSetAtlasSourceEditor::_tile_alternatives_control_draw() { + Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color"); + Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0); + // Update the hovered alternative tile. if (tools_button_group->get_pressed_button() == tool_select_button) { // Draw hovered tile. Vector2i coords = Vector2(hovered_alternative_tile_coords.x, hovered_alternative_tile_coords.y); - if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { Rect2i rect = tile_atlas_view->get_alternative_tile_rect(coords, hovered_alternative_tile_coords.z); if (rect != Rect2i()) { alternative_tiles_control->draw_rect(rect, Color(1.0, 1.0, 1.0), false); @@ -1412,7 +1417,7 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_draw() { if (selected.alternative >= 1) { Rect2i rect = tile_atlas_view->get_alternative_tile_rect(selected.tile, selected.alternative); if (rect != Rect2i()) { - alternative_tiles_control->draw_rect(rect, Color(0.2, 0.2, 1.0), false); + alternative_tiles_control->draw_rect(rect, selection_color, false); } } } @@ -1441,7 +1446,7 @@ void TileSetAtlasSourceEditor::_undo_redo_inspector_callback(Object *p_undo_redo #define ADD_UNDO(obj, property) undo_redo->add_undo_property(obj, property, tile_data->get(property)); - TileProxyObject *tile_data = Object::cast_to<TileProxyObject>(p_edited); + AtlasTileProxyObject *tile_data = Object::cast_to<AtlasTileProxyObject>(p_edited); if (tile_data) { Vector<String> components = String(p_property).split("/", true, 2); if (components.size() == 2 && components[1] == "shapes_count") { @@ -1518,7 +1523,7 @@ void TileSetAtlasSourceEditor::_auto_create_tiles() { for (int x = 0; x < grid_size.x; x++) { // Check if we have a tile at the coord Vector2i coords = Vector2i(x, y); - if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetAtlasSource::INVALID_ATLAS_COORDS) { + if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) { // Check if the texture is empty at the given coords. Rect2i region = Rect2i(margins + (coords * (texture_region_size + separation)), texture_region_size); bool is_opaque = false; @@ -1673,7 +1678,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { tile_inspector_label->hide(); middle_vbox_container->add_child(tile_inspector_label); - tile_proxy_object = memnew(TileProxyObject(this)); + tile_proxy_object = memnew(AtlasTileProxyObject(this)); tile_proxy_object->connect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_update_atlas_view).unbind(1)); tile_inspector = memnew(EditorInspector); diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.h b/editor/plugins/tiles/tile_set_atlas_source_editor.h index ff68aa8288..70f2cdbe01 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.h +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.h @@ -45,8 +45,8 @@ class TileSetAtlasSourceEditor : public HBoxContainer { private: // A class to store which tiles are selected. struct TileSelection { - Vector2i tile = TileSetAtlasSource::INVALID_ATLAS_COORDS; - int alternative = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE; + Vector2i tile = TileSetSource::INVALID_ATLAS_COORDS; + int alternative = TileSetSource::INVALID_TILE_ALTERNATIVE; bool operator<(const TileSelection &p_other) const { if (tile == p_other.tile) { @@ -80,14 +80,13 @@ private: }; // -- Proxy object for a tile, needed by the inspector -- - class TileProxyObject : public Object { - GDCLASS(TileProxyObject, Object); + class AtlasTileProxyObject : public Object { + GDCLASS(AtlasTileProxyObject, Object); private: TileSetAtlasSourceEditor *tiles_set_atlas_source_editor; TileSetAtlasSource *tile_set_atlas_source = nullptr; - int source_id; Set<TileSelection> tiles = Set<TileSelection>(); protected: @@ -99,10 +98,10 @@ private: public: // Update the proxyed object. - void edit(TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id = -1, Set<TileSelection> p_tiles = Set<TileSelection>()); + void edit(TileSetAtlasSource *p_tile_set_atlas_source, Set<TileSelection> p_tiles = Set<TileSelection>()); - TileProxyObject(TileSetAtlasSourceEditor *p_tiles_editor_source_tab) { - tiles_set_atlas_source_editor = p_tiles_editor_source_tab; + AtlasTileProxyObject(TileSetAtlasSourceEditor *p_tiles_set_atlas_source_editor) { + tiles_set_atlas_source_editor = p_tiles_set_atlas_source_editor; } }; @@ -115,7 +114,7 @@ private: bool tile_set_atlas_source_changed_needs_update = false; // -- Inspector -- - TileProxyObject *tile_proxy_object; + AtlasTileProxyObject *tile_proxy_object; Label *tile_inspector_label; EditorInspector *tile_inspector; String selected_property; @@ -175,7 +174,7 @@ private: ADVANCED_AUTO_REMOVE_TILES, }; Vector2i menu_option_coords; - int menu_option_alternative = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE; + int menu_option_alternative = TileSetSource::INVALID_TILE_ALTERNATIVE; void _menu_option(int p_option); // Tool buttons. @@ -198,7 +197,7 @@ private: Array _get_selection_as_array(); // A control on the tile atlas to draw and handle input events. - Vector2i hovered_base_tile_coords = TileSetAtlasSource::INVALID_ATLAS_COORDS; + Vector2i hovered_base_tile_coords = TileSetSource::INVALID_ATLAS_COORDS; PopupMenu *base_tile_popup_menu; PopupMenu *empty_base_tile_popup_menu; @@ -213,7 +212,7 @@ private: void _tile_atlas_view_transform_changed(); // A control over the alternative tiles. - Vector3i hovered_alternative_tile_coords = Vector3i(TileSetAtlasSource::INVALID_ATLAS_COORDS.x, TileSetAtlasSource::INVALID_ATLAS_COORDS.y, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE); + Vector3i hovered_alternative_tile_coords = Vector3i(TileSetSource::INVALID_ATLAS_COORDS.x, TileSetSource::INVALID_ATLAS_COORDS.y, TileSetSource::INVALID_TILE_ALTERNATIVE); PopupMenu *alternative_tile_popup_menu; Control *alternative_tiles_control; diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp index 5573fae670..6078c986cb 100644 --- a/editor/plugins/tiles/tile_set_editor.cpp +++ b/editor/plugins/tiles/tile_set_editor.cpp @@ -140,20 +140,39 @@ void TileSetEditor::_update_atlas_sources_list(int force_selected_id) { for (int i = 0; i < tile_set->get_source_count(); i++) { int source_id = tile_set->get_source_id(i); - // TODO: handle with virtual functions TileSetSource *source = *tile_set->get_source(source_id); + + Ref<Texture2D> texture; + String item_text; + + // Atlas source. TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); if (atlas_source) { - Ref<Texture2D> texture = atlas_source->get_texture(); + texture = atlas_source->get_texture(); if (texture.is_valid()) { - sources_list->add_item(vformat("%s - (id:%d)", texture->get_path().get_file(), source_id), texture); + item_text = vformat("%s (id:%d)", texture->get_path().get_file(), source_id); } else { - sources_list->add_item(vformat("No texture atlas source - (id:%d)", source_id), missing_texture_texture); + item_text = vformat(TTR("No Texture Atlas Source (id:%d)"), source_id); } - } else { - sources_list->add_item(vformat("Unknown type source - (id:%d)", source_id), missing_texture_texture); } - sources_list->set_item_metadata(sources_list->get_item_count() - 1, source_id); + + // Scene collection source. + TileSetScenesCollectionSource *scene_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source); + if (scene_collection_source) { + texture = get_theme_icon("PackedScene", "EditorIcons"); + item_text = vformat(TTR("Scene Collection Source (id:%d)"), source_id); + } + + // Use default if not valid. + if (item_text.is_empty()) { + item_text = vformat(TTR("Unknown Type Source (id:%d)"), source_id); + } + if (!texture.is_valid()) { + texture = missing_texture_texture; + } + + sources_list->add_item(item_text, texture); + sources_list->set_item_metadata(i, source_id); } // Set again the current selected item if needed. @@ -193,35 +212,63 @@ void TileSetEditor::_source_selected(int p_source_index) { if (p_source_index >= 0) { int source_id = sources_list->get_item_metadata(p_source_index); TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(*tile_set->get_source(source_id)); + TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(*tile_set->get_source(source_id)); if (atlas_source) { - tile_set_atlas_source_editor->edit(*tile_set, atlas_source, source_id); no_source_selected_label->hide(); + tile_set_atlas_source_editor->edit(*tile_set, atlas_source, source_id); tile_set_atlas_source_editor->show(); + tile_set_scenes_collection_source_editor->hide(); + } else if (scenes_collection_source) { + no_source_selected_label->hide(); + tile_set_atlas_source_editor->hide(); + tile_set_scenes_collection_source_editor->edit(*tile_set, scenes_collection_source, source_id); + tile_set_scenes_collection_source_editor->show(); } else { no_source_selected_label->show(); tile_set_atlas_source_editor->hide(); + tile_set_scenes_collection_source_editor->hide(); } } else { no_source_selected_label->show(); tile_set_atlas_source_editor->hide(); + tile_set_scenes_collection_source_editor->hide(); } } -void TileSetEditor::_source_add_pressed() { +void TileSetEditor::_source_add_id_pressed(int p_id_pressed) { ERR_FAIL_COND(!tile_set.is_valid()); - int source_id = tile_set->get_next_source_id(); + switch (p_id_pressed) { + case 0: { + int source_id = tile_set->get_next_source_id(); - Ref<TileSetAtlasSource> atlas_source = memnew(TileSetAtlasSource); + Ref<TileSetAtlasSource> atlas_source = memnew(TileSetAtlasSource); - // Add a new source. - undo_redo->create_action(TTR("Add atlas source")); - undo_redo->add_do_method(*tile_set, "add_source", atlas_source, source_id); - undo_redo->add_do_method(*atlas_source, "set_texture_region_size", tile_set->get_tile_size()); - undo_redo->add_undo_method(*tile_set, "remove_source", source_id); - undo_redo->commit_action(); + // Add a new source. + undo_redo->create_action(TTR("Add atlas source")); + undo_redo->add_do_method(*tile_set, "add_source", atlas_source, source_id); + undo_redo->add_do_method(*atlas_source, "set_texture_region_size", tile_set->get_tile_size()); + undo_redo->add_undo_method(*tile_set, "remove_source", source_id); + undo_redo->commit_action(); + + _update_atlas_sources_list(source_id); + } break; + case 1: { + int source_id = tile_set->get_next_source_id(); + + Ref<TileSetScenesCollectionSource> scene_collection_source = memnew(TileSetScenesCollectionSource); + + // Add a new source. + undo_redo->create_action(TTR("Add atlas source")); + undo_redo->add_do_method(*tile_set, "add_source", scene_collection_source, source_id); + undo_redo->add_undo_method(*tile_set, "remove_source", source_id); + undo_redo->commit_action(); - _update_atlas_sources_list(source_id); + _update_atlas_sources_list(source_id); + } break; + default: + ERR_FAIL(); + } } void TileSetEditor::_source_delete_pressed() { @@ -251,6 +298,9 @@ void TileSetEditor::_notification(int p_what) { break; case NOTIFICATION_INTERNAL_PROCESS: if (tile_set_changed_needs_update) { + if (tile_set.is_valid()) { + tile_set->set_edited(true); + } _update_atlas_sources_list(); tile_set_changed_needs_update = false; } @@ -398,7 +448,7 @@ TileDataEditor *TileSetEditor::get_tile_data_editor(String p_property) { } else if (p_property == "probability") { return tile_data_float_editor; } else if (p_property == "y_sort_origin") { - return tile_data_position_editor; + return tile_data_y_sort_editor; } else if (p_property == "texture_offset") { return tile_data_texture_offset_editor; } else if (components.size() >= 1 && components[0].begins_with("occlusion_layer_")) { @@ -434,6 +484,7 @@ void TileSetEditor::edit(Ref<TileSet> p_tile_set) { } tile_set_atlas_source_editor->hide(); + tile_set_scenes_collection_source_editor->hide(); no_source_selected_label->show(); } @@ -464,6 +515,7 @@ TileSetEditor::TileSetEditor() { sources_list->connect("item_selected", callable_mp(this, &TileSetEditor::_source_selected)); sources_list->connect("item_selected", callable_mp(TilesEditor::get_singleton(), &TilesEditor::set_atlas_sources_lists_current)); sources_list->connect("visibility_changed", callable_mp(TilesEditor::get_singleton(), &TilesEditor::synchronize_atlas_sources_list), varray(sources_list)); + sources_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST); sources_list->set_drag_forwarding(this); split_container_left_side->add_child(sources_list); @@ -477,11 +529,19 @@ TileSetEditor::TileSetEditor() { sources_delete_button->connect("pressed", callable_mp(this, &TileSetEditor::_source_delete_pressed)); sources_bottom_actions->add_child(sources_delete_button); - sources_add_button = memnew(Button); + sources_add_button = memnew(MenuButton); sources_add_button->set_flat(true); - sources_add_button->connect("pressed", callable_mp(this, &TileSetEditor::_source_add_pressed)); + sources_add_button->get_popup()->add_item(TTR("Atlas")); + sources_add_button->get_popup()->add_item(TTR("Scenes Collection")); + sources_add_button->get_popup()->connect("id_pressed", callable_mp(this, &TileSetEditor::_source_add_id_pressed)); sources_bottom_actions->add_child(sources_add_button); + // Right side container. + VBoxContainer *split_container_right_side = memnew(VBoxContainer); + split_container_right_side->set_h_size_flags(SIZE_EXPAND_FILL); + split_container_right_side->set_v_size_flags(SIZE_EXPAND_FILL); + split_container->add_child(split_container_right_side); + // No source selected. no_source_selected_label = memnew(Label); no_source_selected_label->set_text(TTR("No TileSet source selected. Select or create a TileSet source.")); @@ -489,16 +549,24 @@ TileSetEditor::TileSetEditor() { no_source_selected_label->set_v_size_flags(SIZE_EXPAND_FILL); no_source_selected_label->set_align(Label::ALIGN_CENTER); no_source_selected_label->set_valign(Label::VALIGN_CENTER); - split_container->add_child(no_source_selected_label); + split_container_right_side->add_child(no_source_selected_label); // Atlases editor. tile_set_atlas_source_editor = memnew(TileSetAtlasSourceEditor); tile_set_atlas_source_editor->set_h_size_flags(SIZE_EXPAND_FILL); tile_set_atlas_source_editor->set_v_size_flags(SIZE_EXPAND_FILL); tile_set_atlas_source_editor->connect("source_id_changed", callable_mp(this, &TileSetEditor::_update_atlas_sources_list)); - split_container->add_child(tile_set_atlas_source_editor); + split_container_right_side->add_child(tile_set_atlas_source_editor); tile_set_atlas_source_editor->hide(); + // Scenes collection editor. + tile_set_scenes_collection_source_editor = memnew(TileSetScenesCollectionSourceEditor); + tile_set_scenes_collection_source_editor->set_h_size_flags(SIZE_EXPAND_FILL); + tile_set_scenes_collection_source_editor->set_v_size_flags(SIZE_EXPAND_FILL); + tile_set_scenes_collection_source_editor->connect("source_id_changed", callable_mp(this, &TileSetEditor::_update_atlas_sources_list)); + split_container_right_side->add_child(tile_set_scenes_collection_source_editor); + tile_set_scenes_collection_source_editor->hide(); + // Registers UndoRedo inspector callback. EditorNode::get_singleton()->get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &TileSetEditor::_undo_redo_inspector_callback)); } @@ -510,7 +578,7 @@ TileSetEditor::~TileSetEditor() { // Delete tile data editors. memdelete(tile_data_texture_offset_editor); - memdelete(tile_data_position_editor); + memdelete(tile_data_y_sort_editor); memdelete(tile_data_integer_editor); memdelete(tile_data_float_editor); memdelete(tile_data_occlusion_shape_editor); diff --git a/editor/plugins/tiles/tile_set_editor.h b/editor/plugins/tiles/tile_set_editor.h index c4aebb40a2..f584c043cc 100644 --- a/editor/plugins/tiles/tile_set_editor.h +++ b/editor/plugins/tiles/tile_set_editor.h @@ -35,6 +35,7 @@ #include "scene/resources/tile_set.h" #include "tile_data_editors.h" #include "tile_set_atlas_source_editor.h" +#include "tile_set_scenes_collection_source_editor.h" class TileSetEditor : public VBoxContainer { GDCLASS(TileSetEditor, VBoxContainer); @@ -47,6 +48,7 @@ private: Label *no_source_selected_label; TileSetAtlasSourceEditor *tile_set_atlas_source_editor; + TileSetScenesCollectionSourceEditor *tile_set_scenes_collection_source_editor; UndoRedo *undo_redo = EditorNode::get_undo_redo(); @@ -54,7 +56,7 @@ private: // List of tile data editors. TileDataTextureOffsetEditor *tile_data_texture_offset_editor = memnew(TileDataTextureOffsetEditor); - TileDataPositionEditor *tile_data_position_editor = memnew(TileDataPositionEditor); + TileDataYSortEditor *tile_data_y_sort_editor = memnew(TileDataYSortEditor); TileDataIntegerEditor *tile_data_integer_editor = memnew(TileDataIntegerEditor); TileDataFloatEditor *tile_data_float_editor = memnew(TileDataFloatEditor); TileDataOcclusionShapeEditor *tile_data_occlusion_shape_editor = memnew(TileDataOcclusionShapeEditor); @@ -64,11 +66,11 @@ private: // -- Sources management -- Button *sources_delete_button; - Button *sources_add_button; + MenuButton *sources_add_button; ItemList *sources_list; Ref<Texture2D> missing_texture_texture; void _source_selected(int p_source_index); - void _source_add_pressed(); + void _source_add_id_pressed(int p_id_pressed); void _source_delete_pressed(); void _tile_set_changed(); diff --git a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp new file mode 100644 index 0000000000..568d4ca8d7 --- /dev/null +++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp @@ -0,0 +1,511 @@ +/*************************************************************************/ +/* tile_set_scenes_collection_source_editor.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 "tile_set_scenes_collection_source_editor.h" + +#include "editor/editor_resource_preview.h" +#include "editor/editor_scale.h" +#include "editor/editor_settings.h" + +#include "scene/gui/item_list.h" + +#include "core/core_string_names.h" + +void TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::set_id(int p_id) { + ERR_FAIL_COND(p_id < 0); + if (source_id == p_id) { + return; + } + ERR_FAIL_COND_MSG(tile_set->has_source(p_id), vformat("Cannot change TileSet Scenes Collection source ID. Another TileSet source exists with id %d.", p_id)); + + int previous_source = source_id; + source_id = p_id; // source_id must be updated before, because it's used by the source list update. + tile_set->set_source_id(previous_source, p_id); + emit_signal("changed", "id"); +} + +int TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::get_id() { + return source_id; +} + +bool TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::_set(const StringName &p_name, const Variant &p_value) { + bool valid = false; + tile_set_scenes_collection_source->set(p_name, p_value, &valid); + if (valid) { + emit_signal("changed", String(p_name).utf8().get_data()); + } + return valid; +} + +bool TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::_get(const StringName &p_name, Variant &r_ret) const { + if (!tile_set_scenes_collection_source) { + return false; + } + bool valid = false; + r_ret = tile_set_scenes_collection_source->get(p_name, &valid); + return valid; +} + +void TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::_bind_methods() { + // -- Shape and layout -- + ClassDB::bind_method(D_METHOD("set_id", "id"), &TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::set_id); + ClassDB::bind_method(D_METHOD("get_id"), &TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::get_id); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "id"), "set_id", "get_id"); + + ADD_SIGNAL(MethodInfo("changed", PropertyInfo(Variant::STRING, "what"))); +} + +void TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::edit(Ref<TileSet> p_tile_set, TileSetScenesCollectionSource *p_tile_set_scenes_collection_source, int p_source_id) { + ERR_FAIL_COND(!p_tile_set.is_valid()); + ERR_FAIL_COND(!p_tile_set_scenes_collection_source); + ERR_FAIL_COND(p_source_id < 0); + ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_scenes_collection_source); + + // Disconnect to changes. + if (tile_set_scenes_collection_source) { + tile_set_scenes_collection_source->disconnect(CoreStringNames::get_singleton()->property_list_changed, callable_mp((Object *)this, &Object::notify_property_list_changed)); + } + + tile_set = p_tile_set; + tile_set_scenes_collection_source = p_tile_set_scenes_collection_source; + source_id = p_source_id; + + // Connect to changes. + if (tile_set_scenes_collection_source) { + if (!tile_set_scenes_collection_source->is_connected(CoreStringNames::get_singleton()->property_list_changed, callable_mp((Object *)this, &Object::notify_property_list_changed))) { + tile_set_scenes_collection_source->connect(CoreStringNames::get_singleton()->property_list_changed, callable_mp((Object *)this, &Object::notify_property_list_changed)); + } + } + + notify_property_list_changed(); +} + +// -- Proxy object used by the tile inspector -- +bool TileSetScenesCollectionSourceEditor::SceneTileProxyObject::_set(const StringName &p_name, const Variant &p_value) { + if (!tile_set_scenes_collection_source) { + return false; + } + + if (p_name == "id") { + int as_int = int(p_value); + ERR_FAIL_COND_V(as_int < 0, false); + ERR_FAIL_COND_V(tile_set_scenes_collection_source->has_scene_tile_id(as_int), false); + tile_set_scenes_collection_source->set_scene_tile_id(scene_id, as_int); + scene_id = as_int; + emit_signal("changed", "id"); + for (int i = 0; i < tile_set_scenes_collection_source_editor->scene_tiles_list->get_item_count(); i++) { + if (int(tile_set_scenes_collection_source_editor->scene_tiles_list->get_item_metadata(i)) == scene_id) { + tile_set_scenes_collection_source_editor->scene_tiles_list->select(i); + break; + } + } + return true; + } else if (p_name == "scene") { + tile_set_scenes_collection_source->set_scene_tile_scene(scene_id, p_value); + emit_signal("changed", "scene"); + return true; + } else if (p_name == "display_placeholder") { + tile_set_scenes_collection_source->set_scene_tile_display_placeholder(scene_id, p_value); + emit_signal("changed", "display_placeholder"); + return true; + } + + return false; +} + +bool TileSetScenesCollectionSourceEditor::SceneTileProxyObject::_get(const StringName &p_name, Variant &r_ret) const { + if (!tile_set_scenes_collection_source) { + return false; + } + + if (p_name == "id") { + r_ret = scene_id; + return true; + } else if (p_name == "scene") { + r_ret = tile_set_scenes_collection_source->get_scene_tile_scene(scene_id); + return true; + } else if (p_name == "display_placeholder") { + r_ret = tile_set_scenes_collection_source->get_scene_tile_display_placeholder(scene_id); + return true; + } + + return false; +} + +void TileSetScenesCollectionSourceEditor::SceneTileProxyObject::_get_property_list(List<PropertyInfo> *p_list) const { + if (!tile_set_scenes_collection_source) { + return; + } + + p_list->push_back(PropertyInfo(Variant::INT, "id", PROPERTY_HINT_NONE, "")); + p_list->push_back(PropertyInfo(Variant::OBJECT, "scene", PROPERTY_HINT_RESOURCE_TYPE, "PackedScene")); + p_list->push_back(PropertyInfo(Variant::BOOL, "display_placeholder", PROPERTY_HINT_NONE, "")); +} + +void TileSetScenesCollectionSourceEditor::SceneTileProxyObject::edit(TileSetScenesCollectionSource *p_tile_set_scenes_collection_source, int p_scene_id) { + ERR_FAIL_COND(!p_tile_set_scenes_collection_source); + ERR_FAIL_COND(!p_tile_set_scenes_collection_source->has_scene_tile_id(p_scene_id)); + + tile_set_scenes_collection_source = p_tile_set_scenes_collection_source; + scene_id = p_scene_id; + + notify_property_list_changed(); +} + +void TileSetScenesCollectionSourceEditor::SceneTileProxyObject::_bind_methods() { + ADD_SIGNAL(MethodInfo("changed", PropertyInfo(Variant::STRING, "what"))); +} + +void TileSetScenesCollectionSourceEditor::_scenes_collection_source_proxy_object_changed(String p_what) { + if (p_what == "id") { + emit_signal("source_id_changed", scenes_collection_source_proxy_object->get_id()); + } +} + +void TileSetScenesCollectionSourceEditor::_tile_set_scenes_collection_source_changed() { + tile_set_scenes_collection_source_changed_needs_update = true; +} + +void TileSetScenesCollectionSourceEditor::_scene_thumbnail_done(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, Variant p_ud) { + int index = p_ud; + + if (index >= 0 && index < scene_tiles_list->get_item_count()) { + scene_tiles_list->set_item_icon(index, p_preview); + } +} + +void TileSetScenesCollectionSourceEditor::_scenes_list_item_activated(int p_index) { + Ref<PackedScene> packed_scene = tile_set_scenes_collection_source->get_scene_tile_scene(scene_tiles_list->get_item_metadata(p_index)); + if (packed_scene.is_valid()) { + EditorNode::get_singleton()->open_request(packed_scene->get_path()); + } +} + +void TileSetScenesCollectionSourceEditor::_source_add_pressed() { + int scene_id = tile_set_scenes_collection_source->get_next_scene_tile_id(); + undo_redo->create_action(TTR("Add a Scene Tile")); + undo_redo->add_do_method(tile_set_scenes_collection_source, "create_scene_tile", Ref<PackedScene>(), scene_id); + undo_redo->add_undo_method(tile_set_scenes_collection_source, "remove_scene_tile", scene_id); + undo_redo->commit_action(); + _update_scenes_list(); + _update_action_buttons(); + _update_tile_inspector(); +} + +void TileSetScenesCollectionSourceEditor::_source_delete_pressed() { + Vector<int> selected_indices = scene_tiles_list->get_selected_items(); + ERR_FAIL_COND(selected_indices.size() <= 0); + int scene_id = scene_tiles_list->get_item_metadata(selected_indices[0]); + + undo_redo->create_action(TTR("Remove a Scene Tile")); + undo_redo->add_do_method(tile_set_scenes_collection_source, "remove_scene_tile", scene_id); + undo_redo->add_undo_method(tile_set_scenes_collection_source, "create_scene_tile", tile_set_scenes_collection_source->get_scene_tile_scene(scene_id), scene_id); + undo_redo->commit_action(); + _update_scenes_list(); + _update_action_buttons(); + _update_tile_inspector(); +} + +void TileSetScenesCollectionSourceEditor::_update_source_inspector() { + // Update the proxy object. + scenes_collection_source_proxy_object->edit(tile_set, tile_set_scenes_collection_source, tile_set_source_id); +} + +void TileSetScenesCollectionSourceEditor::_update_tile_inspector() { + Vector<int> selected_indices = scene_tiles_list->get_selected_items(); + bool has_atlas_tile_selected = (selected_indices.size() > 0); + + // Update the proxy object. + if (has_atlas_tile_selected) { + int scene_id = scene_tiles_list->get_item_metadata(selected_indices[0]); + tile_proxy_object->edit(tile_set_scenes_collection_source, scene_id); + } + + // Update visibility. + tile_inspector_label->set_visible(has_atlas_tile_selected); + tile_inspector->set_visible(has_atlas_tile_selected); +} + +void TileSetScenesCollectionSourceEditor::_update_action_buttons() { + Vector<int> selected_indices = scene_tiles_list->get_selected_items(); + scene_tile_delete_button->set_disabled(selected_indices.size() <= 0); +} + +void TileSetScenesCollectionSourceEditor::_update_scenes_list() { + if (!tile_set_scenes_collection_source) { + return; + } + + // Get the previously selected id. + Vector<int> selected_indices = scene_tiles_list->get_selected_items(); + int old_selected_scene_id = (selected_indices.size() > 0) ? int(scene_tiles_list->get_item_metadata(selected_indices[0])) : -1; + + // Clear the list. + scene_tiles_list->clear(); + + // Rebuild the list. + int to_reselect = -1; + for (int i = 0; i < tile_set_scenes_collection_source->get_scene_tiles_count(); i++) { + int scene_id = tile_set_scenes_collection_source->get_scene_tile_id(i); + + Ref<PackedScene> scene = tile_set_scenes_collection_source->get_scene_tile_scene(scene_id); + + int item_index = 0; + if (scene.is_valid()) { + item_index = scene_tiles_list->add_item(vformat("%s (path:%s id:%d)", scene->get_path().get_file().get_basename(), scene->get_path(), scene_id)); + Variant udata = i; + EditorResourcePreview::get_singleton()->queue_edited_resource_preview(scene, this, "_scene_thumbnail_done", udata); + } else { + item_index = scene_tiles_list->add_item(TTR("Tile with Invalid Scene"), get_theme_icon("PackedScene", "EditorIcons")); + } + scene_tiles_list->set_item_metadata(item_index, scene_id); + + if (old_selected_scene_id >= 0 && scene_id == old_selected_scene_id) { + to_reselect = i; + } + } + + // Reselect if needed. + if (to_reselect >= 0) { + scene_tiles_list->select(to_reselect); + } + + // Icon size update. + int int_size = int(EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size")) * EDSCALE; + scene_tiles_list->set_fixed_icon_size(Vector2(int_size, int_size)); +} + +void TileSetScenesCollectionSourceEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: + scene_tile_add_button->set_icon(get_theme_icon("Add", "EditorIcons")); + scene_tile_delete_button->set_icon(get_theme_icon("Remove", "EditorIcons")); + _update_scenes_list(); + break; + case NOTIFICATION_INTERNAL_PROCESS: + if (tile_set_scenes_collection_source_changed_needs_update) { + // Update everything. + _update_source_inspector(); + _update_scenes_list(); + _update_action_buttons(); + _update_tile_inspector(); + tile_set_scenes_collection_source_changed_needs_update = false; + } + break; + case NOTIFICATION_VISIBILITY_CHANGED: + // Update things just in case. + _update_scenes_list(); + _update_action_buttons(); + break; + default: + break; + } +} + +void TileSetScenesCollectionSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetScenesCollectionSource *p_tile_set_scenes_collection_source, int p_source_id) { + ERR_FAIL_COND(!p_tile_set.is_valid()); + ERR_FAIL_COND(!p_tile_set_scenes_collection_source); + ERR_FAIL_COND(p_source_id < 0); + ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_scenes_collection_source); + + if (p_tile_set == tile_set && p_tile_set_scenes_collection_source == tile_set_scenes_collection_source && p_source_id == tile_set_source_id) { + return; + } + + // Remove listener for old objects. + if (tile_set_scenes_collection_source) { + tile_set_scenes_collection_source->disconnect("changed", callable_mp(this, &TileSetScenesCollectionSourceEditor::_tile_set_scenes_collection_source_changed)); + } + + // Change the edited object. + tile_set = p_tile_set; + tile_set_scenes_collection_source = p_tile_set_scenes_collection_source; + tile_set_source_id = p_source_id; + + // Add the listener again. + if (tile_set_scenes_collection_source) { + tile_set_scenes_collection_source->connect("changed", callable_mp(this, &TileSetScenesCollectionSourceEditor::_tile_set_scenes_collection_source_changed)); + } + + // Update everything. + _update_source_inspector(); + _update_scenes_list(); + _update_action_buttons(); + _update_tile_inspector(); +} + +void TileSetScenesCollectionSourceEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) { + if (!can_drop_data_fw(p_point, p_data, p_from)) { + return; + } + + if (p_from == scene_tiles_list) { + // Handle dropping a texture in the list of atlas resources. + int scene_id = -1; + Dictionary d = p_data; + Vector<String> files = d["files"]; + for (int i = 0; i < files.size(); i++) { + Ref<PackedScene> resource = ResourceLoader::load(files[i]); + if (resource.is_valid()) { + scene_id = tile_set_scenes_collection_source->get_next_scene_tile_id(); + undo_redo->create_action(TTR("Add a Scene Tile")); + undo_redo->add_do_method(tile_set_scenes_collection_source, "create_scene_tile", resource, scene_id); + undo_redo->add_undo_method(tile_set_scenes_collection_source, "remove_scene_tile", scene_id); + undo_redo->commit_action(); + } + } + + _update_scenes_list(); + _update_action_buttons(); + _update_tile_inspector(); + } +} + +bool TileSetScenesCollectionSourceEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { + if (p_from == scene_tiles_list) { + Dictionary d = p_data; + + if (!d.has("type")) { + return false; + } + + // Check if we have a Texture2D. + if (String(d["type"]) == "files") { + Vector<String> files = d["files"]; + + if (files.size() == 0) { + return false; + } + + for (int i = 0; i < files.size(); i++) { + String file = files[i]; + String ftype = EditorFileSystem::get_singleton()->get_file_type(file); + + if (!ClassDB::is_parent_class(ftype, "PackedScene")) { + return false; + } + } + + return true; + } + } + return false; +} + +void TileSetScenesCollectionSourceEditor::_bind_methods() { + ADD_SIGNAL(MethodInfo("source_id_changed", PropertyInfo(Variant::INT, "source_id"))); + + ClassDB::bind_method(D_METHOD("_scene_thumbnail_done"), &TileSetScenesCollectionSourceEditor::_scene_thumbnail_done); + ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &TileSetScenesCollectionSourceEditor::can_drop_data_fw); + ClassDB::bind_method(D_METHOD("drop_data_fw"), &TileSetScenesCollectionSourceEditor::drop_data_fw); +} + +TileSetScenesCollectionSourceEditor::TileSetScenesCollectionSourceEditor() { + // -- Right side -- + HSplitContainer *split_container_right_side = memnew(HSplitContainer); + split_container_right_side->set_h_size_flags(SIZE_EXPAND_FILL); + add_child(split_container_right_side); + + // Middle panel. + ScrollContainer *middle_panel = memnew(ScrollContainer); + middle_panel->set_enable_h_scroll(false); + middle_panel->set_custom_minimum_size(Size2i(200, 0) * EDSCALE); + split_container_right_side->add_child(middle_panel); + + VBoxContainer *middle_vbox_container = memnew(VBoxContainer); + middle_vbox_container->set_h_size_flags(SIZE_EXPAND_FILL); + middle_panel->add_child(middle_vbox_container); + + // Scenes collection source inspector. + scenes_collection_source_inspector_label = memnew(Label); + scenes_collection_source_inspector_label->set_text(TTR("Scenes collection properties:")); + middle_vbox_container->add_child(scenes_collection_source_inspector_label); + + scenes_collection_source_proxy_object = memnew(TileSetScenesCollectionProxyObject()); + scenes_collection_source_proxy_object->connect("changed", callable_mp(this, &TileSetScenesCollectionSourceEditor::_scenes_collection_source_proxy_object_changed)); + + scenes_collection_source_inspector = memnew(EditorInspector); + scenes_collection_source_inspector->set_undo_redo(undo_redo); + scenes_collection_source_inspector->set_enable_v_scroll(false); + scenes_collection_source_inspector->edit(scenes_collection_source_proxy_object); + middle_vbox_container->add_child(scenes_collection_source_inspector); + + // Tile inspector. + tile_inspector_label = memnew(Label); + tile_inspector_label->set_text(TTR("Tile properties:")); + tile_inspector_label->hide(); + middle_vbox_container->add_child(tile_inspector_label); + + tile_proxy_object = memnew(SceneTileProxyObject(this)); + tile_proxy_object->connect("changed", callable_mp(this, &TileSetScenesCollectionSourceEditor::_update_scenes_list).unbind(1)); + tile_proxy_object->connect("changed", callable_mp(this, &TileSetScenesCollectionSourceEditor::_update_action_buttons).unbind(1)); + + tile_inspector = memnew(EditorInspector); + tile_inspector->set_undo_redo(undo_redo); + tile_inspector->set_enable_v_scroll(false); + tile_inspector->edit(tile_proxy_object); + tile_inspector->set_use_folding(true); + middle_vbox_container->add_child(tile_inspector); + + // Scenes list. + VBoxContainer *right_vbox_container = memnew(VBoxContainer); + split_container_right_side->add_child(right_vbox_container); + + scene_tiles_list = memnew(ItemList); + scene_tiles_list->set_h_size_flags(SIZE_EXPAND_FILL); + scene_tiles_list->set_v_size_flags(SIZE_EXPAND_FILL); + scene_tiles_list->set_drag_forwarding(this); + scene_tiles_list->connect("item_selected", callable_mp(this, &TileSetScenesCollectionSourceEditor::_update_tile_inspector).unbind(1)); + scene_tiles_list->connect("item_selected", callable_mp(this, &TileSetScenesCollectionSourceEditor::_update_action_buttons).unbind(1)); + scene_tiles_list->connect("item_activated", callable_mp(this, &TileSetScenesCollectionSourceEditor::_scenes_list_item_activated)); + scene_tiles_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST); + right_vbox_container->add_child(scene_tiles_list); + + HBoxContainer *scenes_bottom_actions = memnew(HBoxContainer); + right_vbox_container->add_child(scenes_bottom_actions); + + scene_tile_add_button = memnew(Button); + scene_tile_add_button->set_flat(true); + scene_tile_add_button->connect("pressed", callable_mp(this, &TileSetScenesCollectionSourceEditor::_source_add_pressed)); + scenes_bottom_actions->add_child(scene_tile_add_button); + + scene_tile_delete_button = memnew(Button); + scene_tile_delete_button->set_flat(true); + scene_tile_delete_button->set_disabled(true); + scene_tile_delete_button->connect("pressed", callable_mp(this, &TileSetScenesCollectionSourceEditor::_source_delete_pressed)); + scenes_bottom_actions->add_child(scene_tile_delete_button); +} + +TileSetScenesCollectionSourceEditor::~TileSetScenesCollectionSourceEditor() { + memdelete(scenes_collection_source_proxy_object); + memdelete(tile_proxy_object); +} diff --git a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h new file mode 100644 index 0000000000..195aa79bc4 --- /dev/null +++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h @@ -0,0 +1,139 @@ +/*************************************************************************/ +/* tile_set_scenes_collection_source_editor.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 TILE_SET_SCENES_COLLECTION_SOURCE_EDITOR_H +#define TILE_SET_SCENES_COLLECTION_SOURCE_EDITOR_H + +#include "editor/editor_node.h" +#include "scene/gui/box_container.h" +#include "scene/resources/tile_set.h" + +class TileSetScenesCollectionSourceEditor : public HBoxContainer { + GDCLASS(TileSetScenesCollectionSourceEditor, HBoxContainer); + +private: + // -- Proxy object for an atlas source, needed by the inspector -- + class TileSetScenesCollectionProxyObject : public Object { + GDCLASS(TileSetScenesCollectionProxyObject, Object); + + private: + Ref<TileSet> tile_set; + TileSetScenesCollectionSource *tile_set_scenes_collection_source = nullptr; + int source_id = -1; + + protected: + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + static void _bind_methods(); + + public: + void set_id(int p_id); + int get_id(); + + void edit(Ref<TileSet> p_tile_set, TileSetScenesCollectionSource *p_tile_set_scenes_collection_source, int p_source_id); + }; + + // -- Proxy object for a tile, needed by the inspector -- + class SceneTileProxyObject : public Object { + GDCLASS(SceneTileProxyObject, Object); + + private: + TileSetScenesCollectionSourceEditor *tile_set_scenes_collection_source_editor; + + TileSetScenesCollectionSource *tile_set_scenes_collection_source = nullptr; + int source_id; + int scene_id; + + protected: + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; + + static void _bind_methods(); + + public: + // Update the proxyed object. + void edit(TileSetScenesCollectionSource *p_tile_set_atlas_source, int p_scene_id); + + SceneTileProxyObject(TileSetScenesCollectionSourceEditor *p_tiles_set_scenes_collection_source_editor) { + tile_set_scenes_collection_source_editor = p_tiles_set_scenes_collection_source_editor; + } + }; + +private: + Ref<TileSet> tile_set; + TileSetScenesCollectionSource *tile_set_scenes_collection_source = nullptr; + int tile_set_source_id = -1; + + UndoRedo *undo_redo = EditorNode::get_undo_redo(); + + bool tile_set_scenes_collection_source_changed_needs_update = false; + + // Source inspector. + TileSetScenesCollectionProxyObject *scenes_collection_source_proxy_object; + Label *scenes_collection_source_inspector_label; + EditorInspector *scenes_collection_source_inspector; + + // Tile inspector. + SceneTileProxyObject *tile_proxy_object; + Label *tile_inspector_label; + EditorInspector *tile_inspector; + + ItemList *scene_tiles_list; + Button *scene_tile_add_button; + Button *scene_tile_delete_button; + + void _tile_set_scenes_collection_source_changed(); + void _scenes_collection_source_proxy_object_changed(String p_what); + void _scene_thumbnail_done(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, Variant p_ud); + void _scenes_list_item_activated(int p_index); + + void _source_add_pressed(); + void _source_delete_pressed(); + + // Update methods. + void _update_source_inspector(); + void _update_tile_inspector(); + void _update_scenes_list(); + void _update_action_buttons(); + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + void edit(Ref<TileSet> p_tile_set, TileSetScenesCollectionSource *p_tile_set_scenes_collection_source, int p_source_id); + void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); + bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; + TileSetScenesCollectionSourceEditor(); + ~TileSetScenesCollectionSourceEditor(); +}; + +#endif diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp index 971ff15073..fb111efc17 100644 --- a/editor/plugins/tiles/tiles_editor_plugin.cpp +++ b/editor/plugins/tiles/tiles_editor_plugin.cpp @@ -202,6 +202,7 @@ TilesEditor::TilesEditor(EditorNode *p_editor) { // Toolbar. HBoxContainer *toolbar = memnew(HBoxContainer); + toolbar->set_h_size_flags(SIZE_EXPAND_FILL); add_child(toolbar); // Switch button. diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 18a1e7f693..e6a5ed0e63 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -322,6 +322,12 @@ void VisualShaderGraphPlugin::register_uniform_name(int p_node_id, LineEdit *p_u links[p_node_id].uniform_name = p_uniform_name; } +void VisualShaderGraphPlugin::update_theme() { + vector_expanded_color[0] = VisualShaderEditor::get_singleton()->get_theme_color("axis_x_color", "Editor"); // red + vector_expanded_color[1] = VisualShaderEditor::get_singleton()->get_theme_color("axis_y_color", "Editor"); // green + vector_expanded_color[2] = VisualShaderEditor::get_singleton()->get_theme_color("axis_z_color", "Editor"); // blue +} + void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) { if (p_type != visual_shader->get_shader_type()) { return; @@ -340,6 +346,12 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) { Color(1.0, 1.0, 0.0), // sampler }; + static const String vector_expanded_name[3] = { + "red", + "green", + "blue" + }; + Ref<VisualShaderNode> vsnode = visual_shader->get_node(p_type, p_id); Ref<VisualShaderNodeResizableBase> resizable_node = Object::cast_to<VisualShaderNodeResizableBase>(vsnode.ptr()); @@ -349,6 +361,8 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) { Ref<VisualShaderNodeGroupBase> group_node = Object::cast_to<VisualShaderNodeGroupBase>(vsnode.ptr()); bool is_group = !group_node.is_null(); + bool is_comment = false; + Ref<VisualShaderNodeExpression> expression_node = Object::cast_to<VisualShaderNodeExpression>(group_node.ptr()); bool is_expression = !expression_node.is_null(); String expression = ""; @@ -392,13 +406,13 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) { if (is_resizable) { Ref<VisualShaderNodeComment> comment_node = Object::cast_to<VisualShaderNodeComment>(vsnode.ptr()); if (comment_node.is_valid()) { + is_comment = true; node->set_comment(true); Label *comment_label = memnew(Label); node->add_child(comment_label); comment_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); comment_label->set_v_size_flags(Control::SIZE_EXPAND_FILL); - comment_label->set_mouse_filter(Control::MouseFilter::MOUSE_FILTER_STOP); comment_label->set_text(comment_node->get_description()); } } @@ -551,13 +565,32 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) { } } - for (int i = 0; i < MAX(vsnode->get_input_port_count(), vsnode->get_output_port_count()); i++) { + int output_port_count = 0; + for (int i = 0; i < vsnode->get_output_port_count(); i++) { + if (vsnode->_is_output_port_expanded(i)) { + if (vsnode->get_output_port_type(i) == VisualShaderNode::PORT_TYPE_VECTOR) { + output_port_count += 3; + } + } + output_port_count++; + } + int max_ports = MAX(vsnode->get_input_port_count(), output_port_count); + VisualShaderNode::PortType expanded_type = VisualShaderNode::PORT_TYPE_SCALAR; + int expanded_port_counter = 0; + + for (int i = 0, j = 0; i < max_ports; i++, j++) { + if (expanded_type == VisualShaderNode::PORT_TYPE_VECTOR && expanded_port_counter >= 3) { + expanded_type = VisualShaderNode::PORT_TYPE_SCALAR; + expanded_port_counter = 0; + i -= 3; + } + if (vsnode->is_port_separator(i)) { node->add_child(memnew(HSeparator)); port_offset++; } - bool valid_left = i < vsnode->get_input_port_count(); + bool valid_left = j < vsnode->get_input_port_count(); VisualShaderNode::PortType port_left = VisualShaderNode::PORT_TYPE_SCALAR; bool port_left_used = false; String name_left; @@ -565,18 +598,24 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) { name_left = vsnode->get_input_port_name(i); port_left = vsnode->get_input_port_type(i); for (List<VisualShader::Connection>::Element *E = connections.front(); E; E = E->next()) { - if (E->get().to_node == p_id && E->get().to_port == i) { + if (E->get().to_node == p_id && E->get().to_port == j) { port_left_used = true; } } } - bool valid_right = i < vsnode->get_output_port_count(); + bool valid_right = true; VisualShaderNode::PortType port_right = VisualShaderNode::PORT_TYPE_SCALAR; String name_right; - if (valid_right) { - name_right = vsnode->get_output_port_name(i); - port_right = vsnode->get_output_port_type(i); + + if (expanded_type == VisualShaderNode::PORT_TYPE_SCALAR) { + valid_right = i < vsnode->get_output_port_count(); + if (valid_right) { + name_right = vsnode->get_output_port_name(i); + port_right = vsnode->get_output_port_type(i); + } + } else { + name_right = vector_expanded_name[expanded_port_counter++]; } HBoxContainer *hb = memnew(HBoxContainer); @@ -684,17 +723,29 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) { } } - if (valid_right && visual_shader->get_shader_type() == VisualShader::TYPE_FRAGMENT && port_right != VisualShaderNode::PORT_TYPE_TRANSFORM && port_right != VisualShaderNode::PORT_TYPE_SAMPLER) { - TextureButton *preview = memnew(TextureButton); - preview->set_toggle_mode(true); - preview->set_normal_texture(VisualShaderEditor::get_singleton()->get_theme_icon("GuiVisibilityHidden", "EditorIcons")); - preview->set_pressed_texture(VisualShaderEditor::get_singleton()->get_theme_icon("GuiVisibilityVisible", "EditorIcons")); - preview->set_v_size_flags(Control::SIZE_SHRINK_CENTER); + if (valid_right) { + if (vsnode->is_output_port_expandable(i)) { + TextureButton *expand = memnew(TextureButton); + expand->set_toggle_mode(true); + expand->set_normal_texture(VisualShaderEditor::get_singleton()->get_theme_icon("GuiTreeArrowDown", "EditorIcons")); + expand->set_pressed_texture(VisualShaderEditor::get_singleton()->get_theme_icon("GuiTreeArrowRight", "EditorIcons")); + expand->set_v_size_flags(Control::SIZE_SHRINK_CENTER); + expand->set_pressed(vsnode->_is_output_port_expanded(i)); + expand->connect("pressed", callable_mp(VisualShaderEditor::get_singleton(), &VisualShaderEditor::_expand_output_port), varray(p_id, i, !vsnode->_is_output_port_expanded(i)), CONNECT_DEFERRED); + hb->add_child(expand); + } + if (visual_shader->get_shader_type() == VisualShader::TYPE_FRAGMENT && port_right != VisualShaderNode::PORT_TYPE_TRANSFORM && port_right != VisualShaderNode::PORT_TYPE_SAMPLER) { + TextureButton *preview = memnew(TextureButton); + preview->set_toggle_mode(true); + preview->set_normal_texture(VisualShaderEditor::get_singleton()->get_theme_icon("GuiVisibilityHidden", "EditorIcons")); + preview->set_pressed_texture(VisualShaderEditor::get_singleton()->get_theme_icon("GuiVisibilityVisible", "EditorIcons")); + preview->set_v_size_flags(Control::SIZE_SHRINK_CENTER); - register_output_port(p_id, i, preview); + register_output_port(p_id, j, preview); - preview->connect("pressed", callable_mp(VisualShaderEditor::get_singleton(), &VisualShaderEditor::_preview_select_port), varray(p_id, i), CONNECT_DEFERRED); - hb->add_child(preview); + preview->connect("pressed", callable_mp(VisualShaderEditor::get_singleton(), &VisualShaderEditor::_preview_select_port), varray(p_id, j), CONNECT_DEFERRED); + hb->add_child(preview); + } } if (is_group) { @@ -706,7 +757,40 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) { node->add_child(hb); + if (expanded_type != VisualShaderNode::PORT_TYPE_SCALAR) { + continue; + } + node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], valid_right, port_right, type_color[port_right]); + + if (vsnode->_is_output_port_expanded(i)) { + if (vsnode->get_output_port_type(i) == VisualShaderNode::PORT_TYPE_VECTOR) { + port_offset++; + valid_left = (i + 1) < vsnode->get_input_port_count(); + port_left = VisualShaderNode::PORT_TYPE_SCALAR; + if (valid_left) { + port_left = vsnode->get_input_port_type(i + 1); + } + node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[0]); + port_offset++; + + valid_left = (i + 2) < vsnode->get_input_port_count(); + port_left = VisualShaderNode::PORT_TYPE_SCALAR; + if (valid_left) { + port_left = vsnode->get_input_port_type(i + 2); + } + node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[1]); + port_offset++; + + valid_left = (i + 3) < vsnode->get_input_port_count(); + port_left = VisualShaderNode::PORT_TYPE_SCALAR; + if (valid_left) { + port_left = vsnode->get_input_port_type(i + 3); + } + node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[2]); + expanded_type = VisualShaderNode::PORT_TYPE_VECTOR; + } + } } if (vsnode->get_output_port_for_preview() >= 0) { @@ -764,6 +848,10 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) { expression_syntax_highlighter->add_color_region("/*", "*/", comment_color, false); expression_syntax_highlighter->add_color_region("//", "", comment_color, true); + expression_box->clear_comment_delimiters(); + expression_box->add_comment_delimiter("/*", "*/", false); + expression_box->add_comment_delimiter("//", "", true); + expression_box->set_text(expression); expression_box->set_context_menu_enabled(false); expression_box->set_draw_line_numbers(true); @@ -773,6 +861,9 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) { if (!uniform.is_valid()) { VisualShaderEditor::get_singleton()->graph->add_child(node); + if (is_comment) { + VisualShaderEditor::get_singleton()->graph->move_child(node, 0); // to prevents a bug where comment node overlaps its content + } VisualShaderEditor::get_singleton()->_update_created_node(node); if (is_resizable) { VisualShaderEditor::get_singleton()->call_deferred("_set_node_size", (int)p_type, p_id, size); @@ -1288,6 +1379,7 @@ void VisualShaderEditor::_update_graph() { graph_plugin->clear_links(); graph_plugin->make_dirty(true); + graph_plugin->update_theme(); for (int n_i = 0; n_i < nodes.size(); n_i++) { graph_plugin->add_node(type, nodes[n_i]); @@ -1436,6 +1528,92 @@ void VisualShaderEditor::_change_output_port_name(const String &p_text, Object * undo_redo->commit_action(); } +void VisualShaderEditor::_expand_output_port(int p_node, int p_port, bool p_expand) { + VisualShader::Type type = get_current_shader_type(); + + Ref<VisualShaderNode> node = visual_shader->get_node(type, p_node); + ERR_FAIL_COND(!node.is_valid()); + + if (p_expand) { + undo_redo->create_action(TTR("Expand Output Port")); + } else { + undo_redo->create_action(TTR("Shrink Output Port")); + } + + undo_redo->add_do_method(node.ptr(), "_set_output_port_expanded", p_port, p_expand); + undo_redo->add_undo_method(node.ptr(), "_set_output_port_expanded", p_port, !p_expand); + + int type_size = 0; + if (node->get_output_port_type(p_port) == VisualShaderNode::PORT_TYPE_VECTOR) { + type_size = 3; + } + + List<VisualShader::Connection> conns; + visual_shader->get_node_connections(type, &conns); + + for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) { + int from_node = E->get().from_node; + int from_port = E->get().from_port; + int to_node = E->get().to_node; + int to_port = E->get().to_port; + + if (from_node == p_node) { + if (p_expand) { + if (from_port > p_port) { // reconnect ports after expanded ports + undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port); + undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port, to_node, to_port); + + undo_redo->add_do_method(graph_plugin.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port); + undo_redo->add_undo_method(graph_plugin.ptr(), "connect_nodes", type, from_node, from_port, to_node, to_port); + + undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port + type_size, to_node, to_port); + undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port + type_size, to_node, to_port); + + undo_redo->add_do_method(graph_plugin.ptr(), "connect_nodes", type, from_node, from_port + type_size, to_node, to_port); + undo_redo->add_undo_method(graph_plugin.ptr(), "disconnect_nodes", type, from_node, from_port + type_size, to_node, to_port); + } + } else { + if (from_port > p_port + type_size) { // reconnect ports after expanded ports + undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port); + undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port, to_node, to_port); + + undo_redo->add_do_method(graph_plugin.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port); + undo_redo->add_undo_method(graph_plugin.ptr(), "connect_nodes", type, from_node, from_port, to_node, to_port); + + undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", type, from_node, from_port - type_size, to_node, to_port); + undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port - type_size, to_node, to_port); + + undo_redo->add_do_method(graph_plugin.ptr(), "connect_nodes", type, from_node, from_port - type_size, to_node, to_port); + undo_redo->add_undo_method(graph_plugin.ptr(), "disconnect_nodes", type, from_node, from_port - type_size, to_node, to_port); + } else if (from_port > p_port) { // disconnect component ports + undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port); + undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port, to_node, to_port); + + undo_redo->add_do_method(graph_plugin.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port); + undo_redo->add_undo_method(graph_plugin.ptr(), "connect_nodes", type, from_node, from_port, to_node, to_port); + } + } + } + } + + int preview_port = node->get_output_port_for_preview(); + if (p_expand) { + if (preview_port > p_port) { + undo_redo->add_do_method(node.ptr(), "set_output_port_for_preview", preview_port + type_size); + undo_redo->add_undo_method(node.ptr(), "set_output_port_for_preview", preview_port); + } + } else { + if (preview_port > p_port + type_size) { + undo_redo->add_do_method(node.ptr(), "set_output_port_for_preview", preview_port - type_size); + undo_redo->add_undo_method(node.ptr(), "set_output_port_for_preview", preview_port); + } + } + + undo_redo->add_do_method(graph_plugin.ptr(), "update_node", type, p_node); + undo_redo->add_undo_method(graph_plugin.ptr(), "update_node", type, p_node); + undo_redo->commit_action(); +} + void VisualShaderEditor::_remove_input_port(int p_node, int p_port) { VisualShader::Type type = get_current_shader_type(); Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node); @@ -1812,47 +1990,6 @@ void VisualShaderEditor::_edit_port_default_input(Object *p_button, int p_node, editing_port = p_port; } -void VisualShaderEditor::_add_custom_node(const String &p_path) { - int idx = -1; - - for (int i = custom_node_option_idx; i < add_options.size(); i++) { - if (add_options[i].script.is_valid()) { - if (add_options[i].script->get_path() == p_path) { - idx = i; - break; - } - } - } - if (idx != -1) { - _add_node(idx); - } -} - -void VisualShaderEditor::_add_cubemap_node(const String &p_path) { - VisualShaderNodeCubemap *cubemap = (VisualShaderNodeCubemap *)_add_node(cubemap_node_option_idx, -1); - cubemap->set_cube_map(ResourceLoader::load(p_path)); -} - -void VisualShaderEditor::_add_texture2d_node(const String &p_path) { - VisualShaderNodeTexture *texture2d = (VisualShaderNodeTexture *)_add_node(texture2d_node_option_idx, -1); - texture2d->set_texture(ResourceLoader::load(p_path)); -} - -void VisualShaderEditor::_add_texture2d_array_node(const String &p_path) { - VisualShaderNodeTexture2DArray *texture2d_array = (VisualShaderNodeTexture2DArray *)_add_node(texture2d_array_node_option_idx, -1); - texture2d_array->set_texture_array(ResourceLoader::load(p_path)); -} - -void VisualShaderEditor::_add_texture3d_node(const String &p_path) { - VisualShaderNodeTexture3D *texture3d = (VisualShaderNodeTexture3D *)_add_node(texture3d_node_option_idx, -1); - texture3d->set_texture(ResourceLoader::load(p_path)); -} - -void VisualShaderEditor::_add_curve_node(const String &p_path) { - VisualShaderNodeCurveTexture *curve = (VisualShaderNodeCurveTexture *)_add_node(curve_node_option_idx, -1); - curve->set_texture(ResourceLoader::load(p_path)); -} - void VisualShaderEditor::_setup_node(VisualShaderNode *p_node, int p_op_idx) { // FLOAT_OP { @@ -2041,8 +2178,8 @@ void VisualShaderEditor::_setup_node(VisualShaderNode *p_node, int p_op_idx) { } } -VisualShaderNode *VisualShaderEditor::_add_node(int p_idx, int p_op_idx) { - ERR_FAIL_INDEX_V(p_idx, add_options.size(), nullptr); +void VisualShaderEditor::_add_node(int p_idx, int p_op_idx, String p_resource_path, int p_node_idx) { + ERR_FAIL_INDEX(p_idx, add_options.size()); Ref<VisualShaderNode> vsnode; @@ -2050,7 +2187,7 @@ VisualShaderNode *VisualShaderEditor::_add_node(int p_idx, int p_op_idx) { if (!is_custom && add_options[p_idx].type != String()) { VisualShaderNode *vsn = Object::cast_to<VisualShaderNode>(ClassDB::instance(add_options[p_idx].type)); - ERR_FAIL_COND_V(!vsn, nullptr); + ERR_FAIL_COND(!vsn); VisualShaderNodeFloatConstant *constant = Object::cast_to<VisualShaderNodeFloatConstant>(vsn); @@ -2072,10 +2209,10 @@ VisualShaderNode *VisualShaderEditor::_add_node(int p_idx, int p_op_idx) { vsnode = Ref<VisualShaderNode>(vsn); } else { - ERR_FAIL_COND_V(add_options[p_idx].script.is_null(), nullptr); + ERR_FAIL_COND(add_options[p_idx].script.is_null()); String base_type = add_options[p_idx].script->get_instance_base_type(); VisualShaderNode *vsn = Object::cast_to<VisualShaderNode>(ClassDB::instance(base_type)); - ERR_FAIL_COND_V(!vsn, nullptr); + ERR_FAIL_COND(!vsn); vsnode = Ref<VisualShaderNode>(vsn); vsnode->set_script(add_options[p_idx].script); } @@ -2094,7 +2231,11 @@ VisualShaderNode *VisualShaderEditor::_add_node(int p_idx, int p_op_idx) { int id_to_use = visual_shader->get_valid_node_id(type); - undo_redo->create_action(TTR("Add Node to Visual Shader")); + if (p_resource_path.is_empty()) { + undo_redo->create_action(TTR("Add Node to Visual Shader")); + } else { + id_to_use += p_node_idx; + } undo_redo->add_do_method(visual_shader.ptr(), "add_node", type, vsnode, position, id_to_use); undo_redo->add_undo_method(visual_shader.ptr(), "remove_node", type, id_to_use); undo_redo->add_do_method(graph_plugin.ptr(), "add_node", type, id_to_use); @@ -2209,8 +2350,30 @@ VisualShaderNode *VisualShaderEditor::_add_node(int p_idx, int p_op_idx) { graph_plugin->call_deferred("update_curve", id_to_use); } - undo_redo->commit_action(); - return vsnode.ptr(); + if (p_resource_path.is_empty()) { + undo_redo->commit_action(); + } else { + //post-initialization + + VisualShaderNodeTexture *texture2d = Object::cast_to<VisualShaderNodeTexture>(vsnode.ptr()); + VisualShaderNodeTexture3D *texture3d = Object::cast_to<VisualShaderNodeTexture3D>(vsnode.ptr()); + + if (texture2d || texture3d || curve) { + undo_redo->add_do_method(vsnode.ptr(), "set_texture", ResourceLoader::load(p_resource_path)); + return; + } + + VisualShaderNodeCubemap *cubemap = Object::cast_to<VisualShaderNodeCubemap>(vsnode.ptr()); + if (cubemap) { + undo_redo->add_do_method(vsnode.ptr(), "set_cube_map", ResourceLoader::load(p_resource_path)); + return; + } + + VisualShaderNodeTexture2DArray *texture2d_array = Object::cast_to<VisualShaderNodeTexture2DArray>(vsnode.ptr()); + if (texture2d_array) { + undo_redo->add_do_method(vsnode.ptr(), "set_texture_array", ResourceLoader::load(p_resource_path)); + } + } } void VisualShaderEditor::_node_dragged(const Vector2 &p_from, const Vector2 &p_to, int p_node) { @@ -2786,9 +2949,6 @@ void VisualShaderEditor::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { highend_label->set_modulate(get_theme_color("vulkan_color", "Editor")); - error_panel->add_theme_style_override("panel", get_theme_stylebox("bg", "Tree")); - error_label->add_theme_color_override("font_color", get_theme_color("error_color", "Editor")); - node_filter->set_right_icon(Control::get_theme_icon("Search", "EditorIcons")); preview_shader->set_icon(Control::get_theme_icon("Shader", "EditorIcons")); @@ -2825,9 +2985,14 @@ void VisualShaderEditor::_notification(int p_what) { syntax_highlighter->add_color_region("/*", "*/", comment_color, false); syntax_highlighter->add_color_region("//", "", comment_color, true); - error_text->add_theme_font_override("font", get_theme_font("status_source", "EditorFonts")); - error_text->add_theme_font_size_override("font_size", get_theme_font_size("status_source_size", "EditorFonts")); - error_text->add_theme_color_override("font_color", get_theme_color("error_color", "Editor")); + preview_text->clear_comment_delimiters(); + preview_text->add_comment_delimiter("/*", "*/", false); + preview_text->add_comment_delimiter("//", "", true); + + error_panel->add_theme_style_override("panel", get_theme_stylebox("panel", "Panel")); + error_label->add_theme_font_override("font", get_theme_font("status_source", "EditorFonts")); + error_label->add_theme_font_size_override("font_size", get_theme_font_size("status_source_size", "EditorFonts")); + error_label->add_theme_color_override("font_color", get_theme_color("error_color", "Editor")); } tools->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("Tools", "EditorIcons")); @@ -3333,47 +3498,56 @@ void VisualShaderEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da saved_node_pos_dirty = true; _add_node(idx, add_options[idx].sub_func); } else if (d.has("files")) { + undo_redo->create_action(TTR("Add Node(s) to Visual Shader")); + if (d["files"].get_type() == Variant::PACKED_STRING_ARRAY) { - int j = 0; PackedStringArray arr = d["files"]; for (int i = 0; i < arr.size(); i++) { String type = ResourceLoader::get_resource_type(arr[i]); if (type == "GDScript") { Ref<Script> script = ResourceLoader::load(arr[i]); if (script->get_instance_base_type() == "VisualShaderNodeCustom") { - saved_node_pos = p_point + Vector2(0, j * 210 * EDSCALE); + saved_node_pos = p_point + Vector2(0, i * 250 * EDSCALE); saved_node_pos_dirty = true; - _add_custom_node(arr[i]); - j++; + + int idx = -1; + + for (int j = custom_node_option_idx; j < add_options.size(); j++) { + if (add_options[j].script.is_valid()) { + if (add_options[j].script->get_path() == arr[i]) { + idx = j; + break; + } + } + } + if (idx != -1) { + _add_node(idx, -1, arr[i], i); + } } } else if (type == "CurveTexture") { - saved_node_pos = p_point + Vector2(0, j * 210 * EDSCALE); + saved_node_pos = p_point + Vector2(0, i * 250 * EDSCALE); saved_node_pos_dirty = true; - _add_curve_node(arr[i]); - j++; + _add_node(curve_node_option_idx, -1, arr[i], i); } else if (ClassDB::get_parent_class(type) == "Texture2D") { - saved_node_pos = p_point + Vector2(0, j * 210 * EDSCALE); + saved_node_pos = p_point + Vector2(0, i * 250 * EDSCALE); saved_node_pos_dirty = true; - _add_texture2d_node(arr[i]); - j++; + _add_node(texture2d_node_option_idx, -1, arr[i], i); } else if (type == "Texture2DArray") { - saved_node_pos = p_point + Vector2(0, j * 210 * EDSCALE); + saved_node_pos = p_point + Vector2(0, i * 250 * EDSCALE); saved_node_pos_dirty = true; - _add_texture2d_array_node(arr[i]); - j++; + _add_node(texture2d_array_node_option_idx, -1, arr[i], i); } else if (ClassDB::get_parent_class(type) == "Texture3D") { - saved_node_pos = p_point + Vector2(0, j * 210 * EDSCALE); + saved_node_pos = p_point + Vector2(0, i * 250 * EDSCALE); saved_node_pos_dirty = true; - _add_texture3d_node(arr[i]); - j++; + _add_node(texture3d_node_option_idx, -1, arr[i], i); } else if (type == "Cubemap") { - saved_node_pos = p_point + Vector2(0, j * 210 * EDSCALE); + saved_node_pos = p_point + Vector2(0, i * 250 * EDSCALE); saved_node_pos_dirty = true; - _add_cubemap_node(arr[i]); - j++; + _add_node(cubemap_node_option_idx, -1, arr[i], i); } } } + undo_redo->commit_action(); } } } @@ -3429,17 +3603,18 @@ void VisualShaderEditor::_update_preview() { Error err = sl.compile(code, ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(visual_shader->get_mode())), ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(visual_shader->get_mode())), ShaderLanguage::VaryingFunctionNames(), ShaderTypes::get_singleton()->get_types(), _get_global_variable_type); for (int i = 0; i < preview_text->get_line_count(); i++) { - preview_text->set_line_as_marked(i, false); + preview_text->set_line_background_color(i, Color(0, 0, 0, 0)); } if (err != OK) { - preview_text->set_line_as_marked(sl.get_error_line() - 1, true); - error_text->set_visible(true); + Color error_line_color = EDITOR_GET("text_editor/highlighting/mark_color"); + preview_text->set_line_background_color(sl.get_error_line() - 1, error_line_color); + error_panel->show(); String text = "error(" + itos(sl.get_error_line()) + "): " + sl.get_error_text(); - error_text->set_text(text); + error_label->set_text(text); shader_error = true; } else { - error_text->set_visible(false); + error_panel->hide(); shader_error = false; } } @@ -3469,6 +3644,7 @@ void VisualShaderEditor::_bind_methods() { ClassDB::bind_method("_float_constant_selected", &VisualShaderEditor::_float_constant_selected); ClassDB::bind_method("_update_constant", &VisualShaderEditor::_update_constant); 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); @@ -3600,6 +3776,7 @@ VisualShaderEditor::VisualShaderEditor() { preview_vbox = memnew(VBoxContainer); preview_window->add_child(preview_vbox); + preview_vbox->add_theme_constant_override("separation", 0); preview_text = memnew(CodeEdit); syntax_highlighter.instance(); @@ -3609,10 +3786,13 @@ VisualShaderEditor::VisualShaderEditor() { preview_text->set_draw_line_numbers(true); preview_text->set_readonly(true); - error_text = memnew(Label); - preview_vbox->add_child(error_text); - error_text->set_autowrap(true); - error_text->set_visible(false); + error_panel = memnew(PanelContainer); + preview_vbox->add_child(error_panel); + error_panel->set_visible(false); + + error_label = memnew(Label); + error_panel->add_child(error_label); + error_label->set_autowrap(true); /////////////////////////////////////// // POPUP MENU @@ -4090,6 +4270,7 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("TransformDecompose", "Transform", "Composition", "VisualShaderNodeTransformDecompose", TTR("Decomposes transform to four vectors."))); add_options.push_back(AddOption("Determinant", "Transform", "Functions", "VisualShaderNodeDeterminant", TTR("Calculates the determinant of a transform."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("GetBillboardMatrix", "Transform", "Functions", "VisualShaderNodeBillboard", TTR("Calculates how the object should face the camera to be applied on Model View Matrix output port for 3D objects."), -1, VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("Inverse", "Transform", "Functions", "VisualShaderNodeTransformFunc", TTR("Calculates the inverse of a transform."), VisualShaderNodeTransformFunc::FUNC_INVERSE, VisualShaderNode::PORT_TYPE_TRANSFORM)); add_options.push_back(AddOption("Transpose", "Transform", "Functions", "VisualShaderNodeTransformFunc", TTR("Calculates the transpose of a transform."), VisualShaderNodeTransformFunc::FUNC_TRANSPOSE, VisualShaderNode::PORT_TYPE_TRANSFORM)); @@ -4192,13 +4373,6 @@ VisualShaderEditor::VisualShaderEditor() { _update_options_menu(); - error_panel = memnew(PanelContainer); - add_child(error_panel); - error_label = memnew(Label); - error_panel->add_child(error_label); - error_label->set_text("eh"); - error_panel->hide(); - undo_redo = EditorNode::get_singleton()->get_undo_redo(); Ref<VisualShaderNodePluginDefault> default_plugin; diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h index 6d57d38cab..f12d05f7ed 100644 --- a/editor/plugins/visual_shader_editor_plugin.h +++ b/editor/plugins/visual_shader_editor_plugin.h @@ -83,6 +83,8 @@ private: List<VisualShader::Connection> connections; bool dirty = false; + Color vector_expanded_color[3]; + protected: static void _bind_methods(); @@ -119,6 +121,7 @@ public: void set_expression(VisualShader::Type p_type, int p_node_id, const String &p_expression); int get_constant_index(float p_constant) const; void update_node_size(int p_node_id); + void update_theme(); VisualShader::Type get_shader_type() const; VisualShaderGraphPlugin(); @@ -143,16 +146,14 @@ class VisualShaderEditor : public VBoxContainer { OptionButton *edit_type_particles; OptionButton *edit_type_sky; - PanelContainer *error_panel; - Label *error_label; - bool pending_update_preview; bool shader_error; Window *preview_window; VBoxContainer *preview_vbox; CodeEdit *preview_text; Ref<CodeHighlighter> syntax_highlighter; - Label *error_text; + PanelContainer *error_panel; + Label *error_label; UndoRedo *undo_redo; Point2 saved_node_pos; @@ -290,15 +291,8 @@ class VisualShaderEditor : public VBoxContainer { void _draw_color_over_button(Object *obj, Color p_color); - void _add_custom_node(const String &p_path); - void _add_cubemap_node(const String &p_path); - void _add_texture2d_node(const String &p_path); - void _add_texture2d_array_node(const String &p_path); - void _add_texture3d_node(const String &p_path); - void _add_curve_node(const String &p_path); - void _setup_node(VisualShaderNode *p_node, int p_op_idx); - VisualShaderNode *_add_node(int p_idx, int p_op_idx = -1); + void _add_node(int p_idx, int p_op_idx = -1, String p_resource_path = "", int p_node_idx = -1); void _update_options_menu(); void _set_mode(int p_which); @@ -408,6 +402,7 @@ class VisualShaderEditor : public VBoxContainer { void _remove_output_port(int p_node, int p_port); void _change_output_port_type(int p_type, int p_node, int p_port); void _change_output_port_name(const String &p_text, Object *p_line_edit, int p_node, int p_port); + void _expand_output_port(int p_node, int p_port, bool p_expand); void _expression_focus_out(Object *code_edit, int p_node); |