diff options
Diffstat (limited to 'editor/editor_node.cpp')
| -rw-r--r-- | editor/editor_node.cpp | 232 |
1 files changed, 182 insertions, 50 deletions
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 8b7014fabe..745cdd59c0 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -58,7 +58,6 @@ #include "scene/gui/tab_container.h" #include "scene/gui/tabs.h" #include "scene/gui/texture_progress.h" -#include "scene/gui/tool_button.h" #include "scene/resources/packed_scene.h" #include "servers/navigation_server_2d.h" #include "servers/navigation_server_3d.h" @@ -104,7 +103,6 @@ #include "editor/import_dock.h" #include "editor/multi_node_edit.h" #include "editor/node_dock.h" -#include "editor/pane_drag.h" #include "editor/plugin_config_dialog.h" #include "editor/plugins/animation_blend_space_1d_editor.h" #include "editor/plugins/animation_blend_space_2d_editor.h" @@ -179,6 +177,111 @@ EditorNode *EditorNode::singleton = nullptr; +void EditorNode::disambiguate_filenames(const Vector<String> p_full_paths, Vector<String> &r_filenames) { + // Keep track of a list of "index sets," i.e. sets of indices + // within disambiguated_scene_names which contain the same name. + Vector<Set<int>> index_sets; + Map<String, int> scene_name_to_set_index; + for (int i = 0; i < r_filenames.size(); i++) { + String scene_name = r_filenames[i]; + if (!scene_name_to_set_index.has(scene_name)) { + index_sets.append(Set<int>()); + scene_name_to_set_index.insert(r_filenames[i], index_sets.size() - 1); + } + index_sets.write[scene_name_to_set_index[scene_name]].insert(i); + } + + // For each index set with a size > 1, we need to disambiguate + for (int i = 0; i < index_sets.size(); i++) { + Set<int> iset = index_sets[i]; + while (iset.size() > 1) { + // Append the parent folder to each scene name + for (Set<int>::Element *E = iset.front(); E; E = E->next()) { + int set_idx = E->get(); + String scene_name = r_filenames[set_idx]; + String full_path = p_full_paths[set_idx]; + + // Get rid of file extensions and res:// prefixes + if (scene_name.rfind(".") >= 0) { + scene_name = scene_name.substr(0, scene_name.rfind(".")); + } + if (full_path.begins_with("res://")) { + full_path = full_path.substr(6); + } + if (full_path.rfind(".") >= 0) { + full_path = full_path.substr(0, full_path.rfind(".")); + } + + int scene_name_size = scene_name.size(); + int full_path_size = full_path.size(); + int difference = full_path_size - scene_name_size; + + // Find just the parent folder of the current path and append it. + // If the current name is foo.tscn, and the full path is /some/folder/foo.tscn + // then slash_idx is the second '/', so that we select just "folder", and + // append that to yield "folder/foo.tscn". + if (difference > 0) { + String parent = full_path.substr(0, difference); + int slash_idx = parent.rfind("/"); + slash_idx = parent.rfind("/", slash_idx - 1); + parent = slash_idx >= 0 ? parent.substr(slash_idx + 1) : parent; + r_filenames.write[set_idx] = parent + r_filenames[set_idx]; + } + } + + // Loop back through scene names and remove non-ambiguous names + bool can_proceed = false; + Set<int>::Element *E = iset.front(); + while (E) { + String scene_name = r_filenames[E->get()]; + bool duplicate_found = false; + for (Set<int>::Element *F = iset.front(); F; F = F->next()) { + if (E->get() == F->get()) { + continue; + } + String other_scene_name = r_filenames[F->get()]; + if (other_scene_name == scene_name) { + duplicate_found = true; + break; + } + } + + Set<int>::Element *to_erase = duplicate_found ? nullptr : E; + + // We need to check that we could actually append anymore names + // if we wanted to for disambiguation. If we can't, then we have + // to abort even with ambiguous names. We clean the full path + // and the scene name first to remove extensions so that this + // comparison actually works. + String path = p_full_paths[E->get()]; + if (path.begins_with("res://")) { + path = path.substr(6); + } + if (path.rfind(".") >= 0) { + path = path.substr(0, path.rfind(".")); + } + if (scene_name.rfind(".") >= 0) { + scene_name = scene_name.substr(0, scene_name.rfind(".")); + } + + // We can proceed iff the full path is longer than the scene name, + // meaning that there is at least one more parent folder we can + // tack onto the name. + can_proceed = can_proceed || (path.size() - scene_name.size()) >= 1; + + E = E->next(); + if (to_erase) { + iset.erase(to_erase); + } + } + + if (!can_proceed) { + break; + } + } + } +} + void EditorNode::_update_scene_tabs() { bool show_rb = EditorSettings::get_singleton()->get("interface/scene_tabs/show_script_button"); @@ -186,6 +289,16 @@ void EditorNode::_update_scene_tabs() { DisplayServer::get_singleton()->global_menu_clear("_dock"); } + // Get all scene names, which may be ambiguous + Vector<String> disambiguated_scene_names; + Vector<String> full_path_names; + for (int i = 0; i < editor_data.get_edited_scene_count(); i++) { + disambiguated_scene_names.append(editor_data.get_scene_title(i)); + full_path_names.append(editor_data.get_scene_path(i)); + } + + disambiguate_filenames(full_path_names, disambiguated_scene_names); + scene_tabs->clear_tabs(); Ref<Texture2D> script_icon = gui_base->get_theme_icon("Script", "EditorIcons"); for (int i = 0; i < editor_data.get_edited_scene_count(); i++) { @@ -197,7 +310,7 @@ void EditorNode::_update_scene_tabs() { int current = editor_data.get_edited_scene(); bool unsaved = (i == current) ? saved_version != editor_data.get_undo_redo().get_version() : editor_data.get_scene_version(i) != 0; - scene_tabs->add_tab(editor_data.get_scene_title(i) + (unsaved ? "(*)" : ""), icon); + scene_tabs->add_tab(disambiguated_scene_names[i] + (unsaved ? "(*)" : ""), icon); if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU)) { DisplayServer::get_singleton()->global_menu_add_item("_dock", editor_data.get_scene_title(i) + (unsaved ? "(*)" : ""), callable_mp(this, &EditorNode::_global_menu_scene), i); @@ -361,7 +474,7 @@ void EditorNode::_notification(int p_what) { bool dof_jitter = GLOBAL_GET("rendering/quality/depth_of_field/depth_of_field_use_jitter"); RS::get_singleton()->camera_effects_set_dof_blur_quality(dof_quality, dof_jitter); RS::get_singleton()->environment_set_ssao_quality(RS::EnvironmentSSAOQuality(int(GLOBAL_GET("rendering/quality/ssao/quality"))), GLOBAL_GET("rendering/quality/ssao/half_size")); - RS::get_singleton()->screen_space_roughness_limiter_set_active(GLOBAL_GET("rendering/quality/screen_filters/screen_space_roughness_limiter"), GLOBAL_GET("rendering/quality/screen_filters/screen_space_roughness_limiter_curve")); + RS::get_singleton()->screen_space_roughness_limiter_set_active(GLOBAL_GET("rendering/quality/screen_filters/screen_space_roughness_limiter_enabled"), GLOBAL_GET("rendering/quality/screen_filters/screen_space_roughness_limiter_amount"), GLOBAL_GET("rendering/quality/screen_filters/screen_space_roughness_limiter_limit")); bool glow_bicubic = int(GLOBAL_GET("rendering/quality/glow/upscale_mode")) > 0; RS::get_singleton()->environment_glow_set_use_bicubic_upscale(glow_bicubic); RS::EnvironmentSSRRoughnessQuality ssr_roughness_quality = RS::EnvironmentSSRRoughnessQuality(int(GLOBAL_GET("rendering/quality/screen_space_reflection/roughness_quality"))); @@ -377,6 +490,12 @@ void EditorNode::_notification(int p_what) { RS::get_singleton()->directional_shadow_quality_set(directional_shadow_quality); float probe_update_speed = GLOBAL_GET("rendering/lightmapper/probe_capture_update_speed"); RS::get_singleton()->lightmap_set_probe_capture_update_speed(probe_update_speed); + RS::EnvironmentSDFGIFramesToConverge frames_to_converge = RS::EnvironmentSDFGIFramesToConverge(int(GLOBAL_GET("rendering/sdfgi/frames_to_converge"))); + RS::get_singleton()->environment_set_sdfgi_frames_to_converge(frames_to_converge); + RS::EnvironmentSDFGIRayCount ray_count = RS::EnvironmentSDFGIRayCount(int(GLOBAL_GET("rendering/sdfgi/probe_ray_count"))); + RS::get_singleton()->environment_set_sdfgi_ray_count(ray_count); + RS::GIProbeQuality gi_probe_quality = RS::GIProbeQuality(int(GLOBAL_GET("rendering/quality/gi_probes/quality"))); + RS::get_singleton()->gi_probe_set_quality(gi_probe_quality); } ResourceImporterTexture::get_singleton()->update_imports(); @@ -430,14 +549,14 @@ void EditorNode::_notification(int p_what) { /* DO NOT LOAD SCENES HERE, WAIT FOR FILE SCANNING AND REIMPORT TO COMPLETE */ } break; - case NOTIFICATION_WM_FOCUS_IN: { + case NOTIFICATION_APPLICATION_FOCUS_IN: { // Restore the original FPS cap after focusing back on the editor OS::get_singleton()->set_low_processor_usage_mode_sleep_usec(int(EDITOR_GET("interface/editor/low_processor_mode_sleep_usec"))); EditorFileSystem::get_singleton()->scan_changes(); } break; - case NOTIFICATION_WM_FOCUS_OUT: { + case NOTIFICATION_APPLICATION_FOCUS_OUT: { // Set a low FPS cap to decrease CPU/GPU usage while the editor is unfocused OS::get_singleton()->set_low_processor_usage_mode_sleep_usec(int(EDITOR_GET("interface/editor/unfocused_low_processor_mode_sleep_usec"))); } break; @@ -485,7 +604,7 @@ void EditorNode::_notification(int p_what) { // update_icons for (int i = 0; i < singleton->main_editor_buttons.size(); i++) { - ToolButton *tb = singleton->main_editor_buttons[i]; + Button *tb = singleton->main_editor_buttons[i]; EditorPlugin *p_editor = singleton->editor_table[i]; Ref<Texture2D> icon = p_editor->get_icon(); @@ -619,7 +738,9 @@ void EditorNode::_fs_changed() { preset.unref(); } if (preset.is_null()) { - export_error = vformat("Invalid export preset name: %s.", preset_name); + export_error = vformat( + "Invalid export preset name: %s. Make sure `export_presets.cfg` is present in the current directory.", + preset_name); } else { Ref<EditorExportPlatform> platform = preset->get_platform(); if (platform.is_null()) { @@ -903,19 +1024,19 @@ void EditorNode::_dialog_display_load_error(String p_file, Error p_error) { if (p_error) { switch (p_error) { case ERR_CANT_OPEN: { - show_accept(vformat(TTR("Can't open '%s'. The file could have been moved or deleted."), p_file.get_file()), TTR("OK")); + show_accept(vformat(TTR("Can't open file '%s'. The file could have been moved or deleted."), p_file.get_file()), TTR("OK")); } break; case ERR_PARSE_ERROR: { - show_accept(vformat(TTR("Error while parsing '%s'."), p_file.get_file()), TTR("OK")); + show_accept(vformat(TTR("Error while parsing file '%s'."), p_file.get_file()), TTR("OK")); } break; case ERR_FILE_CORRUPT: { - show_accept(vformat(TTR("Unexpected end of file '%s'."), p_file.get_file()), TTR("OK")); + show_accept(vformat(TTR("Scene file '%s' appears to be invalid/corrupt."), p_file.get_file()), TTR("OK")); } break; case ERR_FILE_NOT_FOUND: { - show_accept(vformat(TTR("Missing '%s' or its dependencies."), p_file.get_file()), TTR("OK")); + show_accept(vformat(TTR("Missing file '%s' or one its dependencies."), p_file.get_file()), TTR("OK")); } break; default: { - show_accept(vformat(TTR("Error while loading '%s'."), p_file.get_file()), TTR("OK")); + show_accept(vformat(TTR("Error while loading file '%s'."), p_file.get_file()), TTR("OK")); } break; } } @@ -2774,7 +2895,8 @@ void EditorNode::select_editor_by_name(const String &p_name) { void EditorNode::add_editor_plugin(EditorPlugin *p_editor, bool p_config_changed) { if (p_editor->has_main_screen()) { - ToolButton *tb = memnew(ToolButton); + Button *tb = memnew(Button); + tb->set_flat(true); tb->set_toggle_mode(true); tb->connect("pressed", callable_mp(singleton, &EditorNode::_editor_select), varray(singleton->main_editor_buttons.size())); tb->set_text(p_editor->get_name()); @@ -3254,13 +3376,13 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b if (!new_scene) { sdata.unref(); - _dialog_display_load_error(lpath, ERR_FILE_NOT_FOUND); + _dialog_display_load_error(lpath, ERR_FILE_CORRUPT); opening_prev = false; if (prev != -1) { set_current_scene(prev); editor_data.remove_scene(idx); } - return ERR_FILE_NOT_FOUND; + return ERR_FILE_CORRUPT; } if (p_set_inherited) { @@ -3403,10 +3525,14 @@ void EditorNode::_update_recent_scenes() { void EditorNode::_quick_opened() { Vector<String> files = quick_open->get_selected_files(); + bool open_scene_dialog = quick_open->get_base_type() == "PackedScene"; for (int i = 0; i < files.size(); i++) { String res_path = files[i]; - if (quick_open->get_base_type() == "PackedScene") { + List<String> scene_extensions; + ResourceLoader::get_recognized_extensions_for_type("PackedScene", &scene_extensions); + + if (open_scene_dialog || scene_extensions.find(files[i].get_extension())) { open_request(res_path); } else { load_resource(res_path); @@ -3635,22 +3761,14 @@ Ref<Texture2D> EditorNode::get_object_icon(const Object *p_object, const String Ref<Texture2D> EditorNode::get_class_icon(const String &p_class, const String &p_fallback) const { ERR_FAIL_COND_V_MSG(p_class.empty(), nullptr, "Class name cannot be empty."); - if (gui_base->has_theme_icon(p_class, "EditorIcons")) { - return gui_base->get_theme_icon(p_class, "EditorIcons"); - } - if (ScriptServer::is_global_class(p_class)) { - String icon_path = EditorNode::get_editor_data().script_class_get_icon_path(p_class); - Ref<ImageTexture> icon = _load_custom_class_icon(icon_path); - if (icon.is_valid()) { - return icon; - } - - Ref<Script> script = ResourceLoader::load(ScriptServer::get_global_class_path(p_class), "Script"); + Ref<ImageTexture> icon; + Ref<Script> script = EditorNode::get_editor_data().script_class_load_script(p_class); + StringName name = p_class; while (script.is_valid()) { - String current_icon_path; - script->get_language()->get_global_class_name(script->get_path(), nullptr, ¤t_icon_path); + name = EditorNode::get_editor_data().script_class_get_name(script->get_path()); + String current_icon_path = EditorNode::get_editor_data().script_class_get_icon_path(name); icon = _load_custom_class_icon(current_icon_path); if (icon.is_valid()) { return icon; @@ -3659,10 +3777,8 @@ Ref<Texture2D> EditorNode::get_class_icon(const String &p_class, const String &p } if (icon.is_null()) { - icon = gui_base->get_theme_icon(ScriptServer::get_global_class_base(p_class), "EditorIcons"); + icon = gui_base->get_theme_icon(ScriptServer::get_global_class_base(name), "EditorIcons"); } - - return icon; } const Map<String, Vector<EditorData::CustomType>> &p_map = EditorNode::get_editor_data().get_custom_types(); @@ -3677,6 +3793,10 @@ Ref<Texture2D> EditorNode::get_class_icon(const String &p_class, const String &p } } + if (gui_base->has_theme_icon(p_class, "EditorIcons")) { + return gui_base->get_theme_icon(p_class, "EditorIcons"); + } + if (p_fallback.length() && gui_base->has_theme_icon(p_fallback, "EditorIcons")) { return gui_base->get_theme_icon(p_fallback, "EditorIcons"); } @@ -4155,7 +4275,6 @@ void EditorNode::_update_dock_slots_visibility() { } right_hsplit->hide(); - bottom_panel->hide(); } else { for (int i = 0; i < DOCK_SLOT_MAX; i++) { int tabs_visible = 0; @@ -4185,7 +4304,6 @@ void EditorNode::_update_dock_slots_visibility() { dock_slot[i]->set_current_tab(0); } } - bottom_panel->show(); if (right_l_vsplit->is_visible() || right_r_vsplit->is_visible()) { right_hsplit->show(); @@ -4619,8 +4737,9 @@ void EditorNode::_scene_tab_changed(int p_tab) { editor_data.get_undo_redo().commit_action(); } -ToolButton *EditorNode::add_bottom_panel_item(String p_text, Control *p_item) { - ToolButton *tb = memnew(ToolButton); +Button *EditorNode::add_bottom_panel_item(String p_text, Control *p_item) { + Button *tb = memnew(Button); + tb->set_flat(true); tb->connect("toggled", callable_mp(this, &EditorNode::_bottom_panel_switch), varray(bottom_panel_items.size())); tb->set_text(p_text); tb->set_toggle_mode(true); @@ -4782,7 +4901,7 @@ void EditorNode::set_distraction_free_mode(bool p_enter) { } } -bool EditorNode::get_distraction_free_mode() const { +bool EditorNode::is_distraction_free_mode_enabled() const { return distraction_free->is_pressed(); } @@ -5696,7 +5815,8 @@ EditorNode::EditorNode() { dock_select_popup->add_child(dock_vb); HBoxContainer *dock_hb = memnew(HBoxContainer); - dock_tab_move_left = memnew(ToolButton); + dock_tab_move_left = memnew(Button); + dock_tab_move_left->set_flat(true); dock_tab_move_left->set_icon(theme->get_icon("Back", "EditorIcons")); dock_tab_move_left->set_focus_mode(Control::FOCUS_NONE); dock_tab_move_left->connect("pressed", callable_mp(this, &EditorNode::_dock_move_left)); @@ -5708,7 +5828,8 @@ EditorNode::EditorNode() { dock_label->set_align(Label::ALIGN_CENTER); dock_hb->add_child(dock_label); - dock_tab_move_right = memnew(ToolButton); + dock_tab_move_right = memnew(Button); + dock_tab_move_right->set_flat(true); dock_tab_move_right->set_icon(theme->get_icon("Forward", "EditorIcons")); dock_tab_move_right->set_focus_mode(Control::FOCUS_NONE); dock_tab_move_right->connect("pressed", callable_mp(this, &EditorNode::_dock_move_right)); @@ -5802,7 +5923,8 @@ EditorNode::EditorNode() { srt->add_child(tabbar_container); tabbar_container->add_child(scene_tabs); - distraction_free = memnew(ToolButton); + distraction_free = memnew(Button); + distraction_free->set_flat(true); #ifdef OSX_ENABLED distraction_free->set_shortcut(ED_SHORTCUT("editor/distraction_free_mode", TTR("Distraction Free Mode"), KEY_MASK_CMD | KEY_MASK_CTRL | KEY_D)); #else @@ -5813,7 +5935,8 @@ EditorNode::EditorNode() { distraction_free->set_icon(gui_base->get_theme_icon("DistractionFree", "EditorIcons")); distraction_free->set_toggle_mode(true); - scene_tab_add = memnew(ToolButton); + scene_tab_add = memnew(Button); + scene_tab_add->set_flat(true); tabbar_container->add_child(scene_tab_add); tabbar_container->add_child(distraction_free); scene_tab_add->set_tooltip(TTR("Add a new scene.")); @@ -5850,7 +5973,8 @@ EditorNode::EditorNode() { file_menu->add_theme_style_override("hover", gui_base->get_theme_stylebox("MenuHover", "EditorStyles")); left_menu_hb->add_child(file_menu); - prev_scene = memnew(ToolButton); + prev_scene = memnew(Button); + prev_scene->set_flat(true); prev_scene->set_icon(gui_base->get_theme_icon("PrevScene", "EditorIcons")); prev_scene->set_tooltip(TTR("Go to previously opened scene.")); prev_scene->set_disabled(true); @@ -6079,7 +6203,8 @@ EditorNode::EditorNode() { HBoxContainer *play_hb = memnew(HBoxContainer); menu_hb->add_child(play_hb); - play_button = memnew(ToolButton); + play_button = memnew(Button); + play_button->set_flat(true); play_hb->add_child(play_button); play_button->set_toggle_mode(true); play_button->set_icon(gui_base->get_theme_icon("MainPlay", "EditorIcons")); @@ -6092,7 +6217,8 @@ EditorNode::EditorNode() { play_button->set_shortcut(ED_SHORTCUT("editor/play", TTR("Play"), KEY_F5)); #endif - pause_button = memnew(ToolButton); + pause_button = memnew(Button); + pause_button->set_flat(true); pause_button->set_toggle_mode(true); pause_button->set_icon(gui_base->get_theme_icon("Pause", "EditorIcons")); pause_button->set_focus_mode(Control::FOCUS_NONE); @@ -6105,7 +6231,8 @@ EditorNode::EditorNode() { pause_button->set_shortcut(ED_SHORTCUT("editor/pause_scene", TTR("Pause Scene"), KEY_F7)); #endif - stop_button = memnew(ToolButton); + stop_button = memnew(Button); + stop_button->set_flat(true); play_hb->add_child(stop_button); stop_button->set_focus_mode(Control::FOCUS_NONE); stop_button->set_icon(gui_base->get_theme_icon("Stop", "EditorIcons")); @@ -6122,7 +6249,8 @@ EditorNode::EditorNode() { play_hb->add_child(run_native); run_native->connect("native_run", callable_mp(this, &EditorNode::_run_native)); - play_scene_button = memnew(ToolButton); + play_scene_button = memnew(Button); + play_scene_button->set_flat(true); play_hb->add_child(play_scene_button); play_scene_button->set_toggle_mode(true); play_scene_button->set_focus_mode(Control::FOCUS_NONE); @@ -6135,7 +6263,8 @@ EditorNode::EditorNode() { play_scene_button->set_shortcut(ED_SHORTCUT("editor/play_scene", TTR("Play Scene"), KEY_F6)); #endif - play_custom_scene_button = memnew(ToolButton); + play_custom_scene_button = memnew(Button); + play_custom_scene_button->set_flat(true); play_hb->add_child(play_custom_scene_button); play_custom_scene_button->set_toggle_mode(true); play_custom_scene_button->set_focus_mode(Control::FOCUS_NONE); @@ -6296,7 +6425,8 @@ EditorNode::EditorNode() { version_label->set_self_modulate(Color(1, 1, 1, 0.6)); bottom_panel_hb->add_child(version_label); - bottom_panel_raise = memnew(ToolButton); + bottom_panel_raise = memnew(Button); + bottom_panel_raise->set_flat(true); bottom_panel_raise->set_icon(gui_base->get_theme_icon("ExpandBottomDock", "EditorIcons")); bottom_panel_raise->set_shortcut(ED_SHORTCUT("editor/bottom_panel_expand", TTR("Expand Bottom Panel"), KEY_MASK_SHIFT | KEY_F12)); @@ -6307,7 +6437,7 @@ EditorNode::EditorNode() { bottom_panel_raise->connect("toggled", callable_mp(this, &EditorNode::_bottom_panel_raise_toggled)); log = memnew(EditorLog); - ToolButton *output_button = add_bottom_panel_item(TTR("Output"), log); + Button *output_button = add_bottom_panel_item(TTR("Output"), log); log->set_tool_button(output_button); old_split_ofs = 0; @@ -6474,6 +6604,7 @@ EditorNode::EditorNode() { } resource_preview->add_preview_generator(Ref<EditorTexturePreviewPlugin>(memnew(EditorTexturePreviewPlugin))); + resource_preview->add_preview_generator(Ref<EditorImagePreviewPlugin>(memnew(EditorImagePreviewPlugin))); resource_preview->add_preview_generator(Ref<EditorPackedScenePreviewPlugin>(memnew(EditorPackedScenePreviewPlugin))); resource_preview->add_preview_generator(Ref<EditorMaterialPreviewPlugin>(memnew(EditorMaterialPreviewPlugin))); resource_preview->add_preview_generator(Ref<EditorScriptPreviewPlugin>(memnew(EditorScriptPreviewPlugin))); @@ -6568,6 +6699,7 @@ EditorNode::EditorNode() { gui_base->add_child(load_error_dialog); execute_outputs = memnew(RichTextLabel); + execute_outputs->set_selection_enabled(true); execute_output_dialog = memnew(AcceptDialog); execute_output_dialog->add_child(execute_outputs); execute_output_dialog->set_title(""); |
