diff options
25 files changed, 216 insertions, 256 deletions
diff --git a/drivers/gles2/shader_compiler_gles2.cpp b/drivers/gles2/shader_compiler_gles2.cpp index 24b89aedc2..5dec6f2fee 100644 --- a/drivers/gles2/shader_compiler_gles2.cpp +++ b/drivers/gles2/shader_compiler_gles2.cpp @@ -80,7 +80,7 @@ static String _opstr(SL::Operator p_op) { static String _mkid(const String &p_id) { - String id = "m_" + p_id; + String id = "m_" + p_id.replace("__", "_dus_"); return id.replace("__", "_dus_"); //doubleunderscore is reserved in glsl } diff --git a/drivers/gles3/shader_compiler_gles3.cpp b/drivers/gles3/shader_compiler_gles3.cpp index f43943bdff..4e4d896bd7 100644 --- a/drivers/gles3/shader_compiler_gles3.cpp +++ b/drivers/gles3/shader_compiler_gles3.cpp @@ -166,7 +166,7 @@ static String _opstr(SL::Operator p_op) { static String _mkid(const String &p_id) { - String id = "m_" + p_id; + String id = "m_" + p_id.replace("__", "_dus_"); return id.replace("__", "_dus_"); //doubleunderscore is reserved in glsl } diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 4f684c7bdc..e05ace53da 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -277,7 +277,8 @@ void FindReplaceBar::_replace_all() { } text_edit->set_v_scroll(vsval); - set_error(vformat(TTR("Replaced %d occurrence(s)."), rc)); + matches_label->add_color_override("font_color", rc > 0 ? get_color("font_color", "Label") : get_color("error_color", "Editor")); + matches_label->set_text(vformat(TTR("%d replaced."), rc)); text_edit->call_deferred("connect", "text_changed", this, "_editor_text_changed"); results_count = -1; diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 56da7d93fa..7c1e58862e 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -1596,7 +1596,7 @@ void EditorInspector::update_tree() { if (capitalize_paths) cat = cat.capitalize(); - if (!filter.is_subsequence_ofi(cat) && !filter.is_subsequence_ofi(name)) + if (!filter.is_subsequence_ofi(cat) && !filter.is_subsequence_ofi(name) && property_prefix.to_lower().find(filter.to_lower()) == -1) continue; } diff --git a/editor/editor_profiler.cpp b/editor/editor_profiler.cpp index 4807f8839c..3fdeaff19d 100644 --- a/editor/editor_profiler.cpp +++ b/editor/editor_profiler.cpp @@ -102,26 +102,28 @@ void EditorProfiler::clear() { } static String _get_percent_txt(float p_value, float p_total) { - if (p_total == 0) + if (p_total == 0) { p_total = 0.00001; + } + return String::num((p_value / p_total) * 100, 1) + "%"; } String EditorProfiler::_get_time_as_text(const Metric &m, float p_time, int p_calls) { - int dmode = display_mode->get_selected(); + const int dmode = display_mode->get_selected(); if (dmode == DISPLAY_FRAME_TIME) { - return rtos(p_time); + return rtos(p_time * 1000).pad_decimals(2) + " ms"; } else if (dmode == DISPLAY_AVERAGE_TIME) { - if (p_calls == 0) - return "0"; - else - return rtos(p_time / p_calls); + if (p_calls == 0) { + return "0.00 ms"; + } else { + return rtos((p_time / p_calls) * 1000).pad_decimals(2) + " ms"; + } } else if (dmode == DISPLAY_FRAME_PERCENT) { return _get_percent_txt(p_time, m.frame_time); } else if (dmode == DISPLAY_PHYSICS_FRAME_PERCENT) { - return _get_percent_txt(p_time, m.physics_frame_time); } @@ -729,7 +731,7 @@ EditorProfiler::EditorProfiler() { h_split->set_v_size_flags(SIZE_EXPAND_FILL); variables = memnew(Tree); - variables->set_custom_minimum_size(Size2(300, 0) * EDSCALE); + variables->set_custom_minimum_size(Size2(320, 0) * EDSCALE); variables->set_hide_folding(true); h_split->add_child(variables); variables->set_hide_root(true); @@ -737,10 +739,10 @@ EditorProfiler::EditorProfiler() { variables->set_column_titles_visible(true); variables->set_column_title(0, TTR("Name")); variables->set_column_expand(0, true); - variables->set_column_min_width(0, 60); + variables->set_column_min_width(0, 60 * EDSCALE); variables->set_column_title(1, TTR("Time")); variables->set_column_expand(1, false); - variables->set_column_min_width(1, 60 * EDSCALE); + variables->set_column_min_width(1, 100 * EDSCALE); variables->set_column_title(2, TTR("Calls")); variables->set_column_expand(2, false); variables->set_column_min_width(2, 60 * EDSCALE); @@ -749,7 +751,6 @@ EditorProfiler::EditorProfiler() { graph = memnew(TextureRect); graph->set_expand(true); graph->set_mouse_filter(MOUSE_FILTER_STOP); - //graph->set_ignore_mouse(false); graph->connect("draw", this, "_graph_tex_draw"); graph->connect("gui_input", this, "_graph_tex_input"); graph->connect("mouse_exited", this, "_graph_tex_mouse_exit"); @@ -760,13 +761,10 @@ EditorProfiler::EditorProfiler() { int metric_size = CLAMP(int(EDITOR_DEF("debugger/profiler_frame_history_size", 600)), 60, 1024); frame_metrics.resize(metric_size); last_metric = -1; - //cursor_metric=-1; hover_metric = -1; EDITOR_DEF("debugger/profiler_frame_max_functions", 64); - //display_mode=DISPLAY_FRAME_TIME; - frame_delay = memnew(Timer); frame_delay->set_wait_time(0.1); frame_delay->set_one_shot(true); @@ -784,6 +782,4 @@ EditorProfiler::EditorProfiler() { seeking = false; graph_height = 1; - - //activate->set_disabled(true); } diff --git a/editor/editor_sectioned_inspector.cpp b/editor/editor_sectioned_inspector.cpp index 28825b45e1..2090c12c91 100644 --- a/editor/editor_sectioned_inspector.cpp +++ b/editor/editor_sectioned_inspector.cpp @@ -245,6 +245,9 @@ void SectionedInspector::update_category_list() { if (pi.name.find(":") != -1 || pi.name == "script" || pi.name == "resource_name" || pi.name == "resource_path" || pi.name == "resource_local_to_scene" || pi.name.begins_with("_global_script")) continue; + if (!filter.empty() && !filter.is_subsequence_ofi(pi.name) && !filter.is_subsequence_ofi(pi.name.replace("/", " ").capitalize())) + continue; + int sp = pi.name.find("/"); if (sp == -1) pi.name = "global/" + pi.name; @@ -252,9 +255,6 @@ void SectionedInspector::update_category_list() { Vector<String> sectionarr = pi.name.split("/"); String metasection; - if (!filter.empty() && !filter.is_subsequence_ofi(sectionarr[sectionarr.size() - 1].capitalize())) - continue; - int sc = MIN(2, sectionarr.size() - 1); for (int i = 0; i < sc; i++) { diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 621f531687..8037045e77 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -89,7 +89,11 @@ Ref<ImageTexture> editor_generate_icon(int p_index, bool p_convert_color, float // dumb gizmo check bool is_gizmo = String(editor_icons_names[p_index]).begins_with("Gizmo"); - ImageLoaderSVG::create_image_from_string(img, editor_icons_sources[p_index], p_scale, true, p_convert_color); + // Upsample icon generation only if the editor scale isn't an integer multiplier. + // Generating upsampled icons is slower, and the benefit is hardly visible + // with integer editor scales. + const bool upsample = !Math::is_equal_approx(Math::round(p_scale), p_scale); + ImageLoaderSVG::create_image_from_string(img, editor_icons_sources[p_index], p_scale, upsample, p_convert_color); if ((p_scale - (float)((int)p_scale)) > 0.0 || is_gizmo || p_force_filter) icon->create_from_image(img); // in this case filter really helps @@ -106,7 +110,15 @@ Ref<ImageTexture> editor_generate_icon(int p_index, bool p_convert_color, float void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme = true, int p_thumb_size = 32, bool p_only_thumbs = false) { #ifdef SVG_ENABLED + // The default icon theme is designed to be used for a dark theme. + // This dictionary stores color codes to convert to other colors + // for better readability on a light theme. Dictionary dark_icon_color_dictionary; + + // The names of the icons to never convert, even if one of their colors + // are contained in the dictionary above. + Set<StringName> exceptions; + if (!p_dark_theme) { // convert color: FROM TO ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#e0e0e0", "#5a5a5a"); // common icon color @@ -172,9 +184,31 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme = ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#69ec9a", "#2ce573"); // VS rid ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#79f3e8", "#12d5c3"); // VS object ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#77edb1", "#57e99f"); // VS dict + + exceptions.insert("EditorPivot"); + exceptions.insert("EditorHandle"); + exceptions.insert("Editor3DHandle"); + exceptions.insert("Godot"); + exceptions.insert("PanoramaSky"); + exceptions.insert("ProceduralSky"); + exceptions.insert("EditorControlAnchor"); + exceptions.insert("DefaultProjectIcon"); + exceptions.insert("GuiCloseCustomizable"); + exceptions.insert("GuiGraphNodePort"); + exceptions.insert("GuiResizer"); + exceptions.insert("ZoomMore"); + exceptions.insert("ZoomLess"); + exceptions.insert("ZoomReset"); + exceptions.insert("LockViewport"); + exceptions.insert("GroupViewport"); + exceptions.insert("StatusError"); + exceptions.insert("StatusSuccess"); + exceptions.insert("StatusWarning"); + exceptions.insert("NodeWarning"); + exceptions.insert("OverbrightIndicator"); } - // these ones should be converted even if we are using a dark theme + // These ones should be converted even if we are using a dark theme. const Color error_color = p_theme->get_color("error_color", "Editor"); const Color success_color = p_theme->get_color("success_color", "Editor"); const Color warning_color = p_theme->get_color("warning_color", "Editor"); @@ -182,65 +216,44 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme = dark_icon_color_dictionary[Color::html("#45ff8b")] = success_color; dark_icon_color_dictionary[Color::html("#dbab09")] = warning_color; - List<String> exceptions; - exceptions.push_back("EditorPivot"); - exceptions.push_back("EditorHandle"); - exceptions.push_back("Editor3DHandle"); - exceptions.push_back("Godot"); - exceptions.push_back("PanoramaSky"); - exceptions.push_back("ProceduralSky"); - exceptions.push_back("EditorControlAnchor"); - exceptions.push_back("DefaultProjectIcon"); - exceptions.push_back("GuiCloseCustomizable"); - exceptions.push_back("GuiGraphNodePort"); - exceptions.push_back("GuiResizer"); - exceptions.push_back("ZoomMore"); - exceptions.push_back("ZoomLess"); - exceptions.push_back("ZoomReset"); - exceptions.push_back("LockViewport"); - exceptions.push_back("GroupViewport"); - exceptions.push_back("StatusError"); - exceptions.push_back("StatusSuccess"); - exceptions.push_back("StatusWarning"); - exceptions.push_back("NodeWarning"); - exceptions.push_back("OverbrightIndicator"); - ImageLoaderSVG::set_convert_colors(&dark_icon_color_dictionary); - // generate icons - if (!p_only_thumbs) + // Generate icons. + if (!p_only_thumbs) { for (int i = 0; i < editor_icons_count; i++) { - List<String>::Element *is_exception = exceptions.find(editor_icons_names[i]); - if (is_exception) exceptions.erase(is_exception); - Ref<ImageTexture> icon = editor_generate_icon(i, !is_exception); + const int is_exception = exceptions.has(editor_icons_names[i]); + const Ref<ImageTexture> icon = editor_generate_icon(i, !is_exception); + p_theme->set_icon(editor_icons_names[i], "EditorIcons", icon); } + } - // generate thumb files with the given thumb size - bool force_filter = p_thumb_size != 64 && p_thumb_size != 32; // we don't need filter with original resolution + // Generate thumbnail icons with the given thumbnail size. + // We don't need filtering when generating at one of the default resolutions. + const bool force_filter = p_thumb_size != 64 && p_thumb_size != 32; if (p_thumb_size >= 64) { - float scale = (float)p_thumb_size / 64.0 * EDSCALE; + const float scale = (float)p_thumb_size / 64.0 * EDSCALE; for (int i = 0; i < editor_bg_thumbs_count; i++) { - int index = editor_bg_thumbs_indices[i]; - List<String>::Element *is_exception = exceptions.find(editor_icons_names[index]); - if (is_exception) exceptions.erase(is_exception); - Ref<ImageTexture> icon = editor_generate_icon(index, !p_dark_theme && !is_exception, scale, force_filter); + const int index = editor_bg_thumbs_indices[i]; + const int is_exception = exceptions.has(editor_icons_names[index]); + const Ref<ImageTexture> icon = editor_generate_icon(index, !p_dark_theme && !is_exception, scale, force_filter); + p_theme->set_icon(editor_icons_names[index], "EditorIcons", icon); } } else { - float scale = (float)p_thumb_size / 32.0 * EDSCALE; + const float scale = (float)p_thumb_size / 32.0 * EDSCALE; for (int i = 0; i < editor_md_thumbs_count; i++) { - int index = editor_md_thumbs_indices[i]; - List<String>::Element *is_exception = exceptions.find(editor_icons_names[index]); - if (is_exception) exceptions.erase(is_exception); - Ref<ImageTexture> icon = editor_generate_icon(index, !p_dark_theme && !is_exception, scale, force_filter); + const int index = editor_md_thumbs_indices[i]; + const bool is_exception = exceptions.has(editor_icons_names[index]); + const Ref<ImageTexture> icon = editor_generate_icon(index, !p_dark_theme && !is_exception, scale, force_filter); + p_theme->set_icon(editor_icons_names[index], "EditorIcons", icon); } } ImageLoaderSVG::set_convert_colors(NULL); #else - print_line("SVG support disabled, editor icons won't be rendered."); + WARN_PRINT("SVG support disabled, editor icons won't be rendered."); #endif } diff --git a/editor/rename_dialog.cpp b/editor/rename_dialog.cpp index 32fcdab4c6..7586f6eac1 100644 --- a/editor/rename_dialog.cpp +++ b/editor/rename_dialog.cpp @@ -109,9 +109,13 @@ RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_und const int feature_min_height = 160 * EDSCALE; - CheckButton *chk_collapse_features = memnew(CheckButton); - chk_collapse_features->set_text(TTR("Advanced Options")); - vbc->add_child(chk_collapse_features); + cbut_regex = memnew(CheckButton); + cbut_regex->set_text(TTR("Use Regular Expressions")); + vbc->add_child(cbut_regex); + + CheckButton *cbut_collapse_features = memnew(CheckButton); + cbut_collapse_features->set_text(TTR("Advanced Options")); + vbc->add_child(cbut_collapse_features); tabc_features = memnew(TabContainer); tabc_features->set_tab_align(TabContainer::ALIGN_LEFT); @@ -195,7 +199,7 @@ RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_und grd_substitute->add_child(but_insert_count); chk_per_level_counter = memnew(CheckBox); - chk_per_level_counter->set_text(TTR("Per Level counter")); + chk_per_level_counter->set_text(TTR("Per-level Counter")); chk_per_level_counter->set_tooltip(TTR("If set the counter restarts for each group of child nodes")); vbc_substitute->add_child(chk_per_level_counter); @@ -233,18 +237,6 @@ RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_und spn_count_padding->set_step(1); hbc_count_options->add_child(spn_count_padding); - // ---- Tab RegEx - - VBoxContainer *vbc_regex = memnew(VBoxContainer); - vbc_regex->set_h_size_flags(SIZE_EXPAND_FILL); - vbc_regex->set_name(TTR("Regular Expressions")); - vbc_regex->set_custom_minimum_size(Size2(0, feature_min_height)); - tabc_features->add_child(vbc_regex); - - cbut_regex = memnew(CheckBox); - cbut_regex->set_text(TTR("Regular Expressions")); - vbc_regex->add_child(cbut_regex); - // ---- Tab Process VBoxContainer *vbc_process = memnew(VBoxContainer); @@ -268,8 +260,8 @@ RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_und opt_style = memnew(OptionButton); opt_style->add_item(TTR("Keep")); - opt_style->add_item(TTR("CamelCase to under_scored")); - opt_style->add_item(TTR("under_scored to CamelCase")); + opt_style->add_item(TTR("PascalCase to snake_case")); + opt_style->add_item(TTR("snake_case to PascalCase")); hbc_style->add_child(opt_style); // ------ Case @@ -299,7 +291,7 @@ RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_und lbl_preview = memnew(Label); lbl_preview->set_text(""); - lbl_preview->add_color_override("font_color", Color(1, 0.5f, 0, 1)); + lbl_preview->add_color_override("font_color", EditorNode::get_singleton()->get_gui_base()->get_color("error_color", "Editor")); vbc->add_child(lbl_preview); // ---- Dialog related @@ -314,7 +306,7 @@ RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_und // ---- Connections - chk_collapse_features->connect("toggled", this, "_features_toggled"); + cbut_collapse_features->connect("toggled", this, "_features_toggled"); // Substitite Buttons @@ -414,9 +406,12 @@ void RenameDialog::_update_preview(String new_text) { lbl_preview->set_text(new_name); if (new_name == preview_node->get_name()) { - lbl_preview->add_color_override("font_color", Color(0, 0.5f, 0.25f, 1)); + // New name is identical to the old one. Don't color it as much to avoid distracting the user. + const Color accent_color = EditorNode::get_singleton()->get_gui_base()->get_color("accent_color", "Editor"); + const Color text_color = EditorNode::get_singleton()->get_gui_base()->get_color("default_color", "RichTextLabel"); + lbl_preview->add_color_override("font_color", accent_color.linear_interpolate(text_color, 0.5)); } else { - lbl_preview->add_color_override("font_color", Color(0, 1, 0.5f, 1)); + lbl_preview->add_color_override("font_color", EditorNode::get_singleton()->get_gui_base()->get_color("success_color", "Editor")); } } @@ -501,9 +496,9 @@ void RenameDialog::_error_handler(void *p_self, const char *p_func, const char * } self->has_errors = true; - self->lbl_preview_title->set_text(TTR("Error")); - self->lbl_preview->add_color_override("font_color", Color(1, 0.25f, 0, 1)); - self->lbl_preview->set_text(err_str); + self->lbl_preview_title->set_text(TTR("Regular Expression Error")); + self->lbl_preview->add_color_override("font_color", EditorNode::get_singleton()->get_gui_base()->get_color("error_color", "Editor")); + self->lbl_preview->set_text(vformat(TTR("At character %s"), err_str)); } String RenameDialog::_regex(const String &pattern, const String &subject, const String &replacement) { @@ -520,18 +515,18 @@ String RenameDialog::_postprocess(const String &subject) { String result = subject; if (style_id == 1) { + // PascalCase to snake_case - // CamelCase to Under_Line result = result.camelcase_to_underscore(true); result = _regex("_+", result, "_"); } else if (style_id == 2) { + // snake_case to PascalCase - // Under_Line to CamelCase RegEx pattern("_+(.?)"); Array matches = pattern.search_all(result); - // _ name would become empty. Ignore + // The name `_` would become empty; ignore it. if (matches.size() && result != "_") { String buffer; int start = 0; diff --git a/editor/rename_dialog.h b/editor/rename_dialog.h index 692e56f1a4..2825cb2cd2 100644 --- a/editor/rename_dialog.h +++ b/editor/rename_dialog.h @@ -75,7 +75,7 @@ class RenameDialog : public ConfirmationDialog { TabContainer *tabc_features; CheckBox *cbut_substitute; - CheckBox *cbut_regex; + CheckButton *cbut_regex; CheckBox *cbut_process; CheckBox *chk_per_level_counter; diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp index 35d5fe5f70..a982724d4c 100644 --- a/editor/script_create_dialog.cpp +++ b/editor/script_create_dialog.cpp @@ -644,7 +644,7 @@ void ScriptCreateDialog::_update_dialog() { } if (script_ok) { - _msg_script_valid(true, TTR("Script is valid.")); + _msg_script_valid(true, TTR("Script path/name is valid.")); } // Does script have named classes? diff --git a/editor/script_editor_debugger.cpp b/editor/script_editor_debugger.cpp index 71a946b256..ab4501bb8a 100644 --- a/editor/script_editor_debugger.cpp +++ b/editor/script_editor_debugger.cpp @@ -484,8 +484,10 @@ int ScriptEditorDebugger::_update_scene_tree(TreeItem *parent, const Array &node void ScriptEditorDebugger::_video_mem_request() { - ERR_FAIL_COND(connection.is_null()); - ERR_FAIL_COND(!connection->is_connected_to_host()); + if (connection.is_null() || !connection->is_connected_to_host()) { + // Video RAM usage is only available while a project is being debugged. + return; + } Array msg; msg.push_back("request_video_mem"); @@ -806,25 +808,25 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da p.write[i] = arr[i]; if (i < perf_items.size()) { - float v = p[i]; - String vs = rtos(v); - String tt = vs; + const float value = p[i]; + String label = rtos(value); + String tooltip = label; switch (Performance::MonitorType((int)perf_items[i]->get_metadata(1))) { case Performance::MONITOR_TYPE_MEMORY: { - vs = String::humanize_size(v); - tt = vs; + label = String::humanize_size(value); + tooltip = label; } break; case Performance::MONITOR_TYPE_TIME: { - tt += " seconds"; - vs += " s"; + label = rtos(value * 1000).pad_decimals(2) + " ms"; + tooltip = label; } break; default: { - tt += " " + perf_items[i]->get_text(0); + tooltip += " " + perf_items[i]->get_text(0); } break; } - perf_items[i]->set_text(1, vs); - perf_items[i]->set_tooltip(1, tt); + perf_items[i]->set_text(1, label); + perf_items[i]->set_tooltip(1, tooltip); if (p[i] > perf_max[i]) perf_max.write[i] = p[i]; } @@ -1323,6 +1325,7 @@ void ScriptEditorDebugger::_notification(int p_what) { inspect_scene_tree->clear(); le_set->set_disabled(true); le_clear->set_disabled(false); + vmem_refresh->set_disabled(false); error_tree->clear(); error_count = 0; warning_count = 0; @@ -1523,6 +1526,7 @@ void ScriptEditorDebugger::stop() { le_clear->set_disabled(false); le_set->set_disabled(true); profiler->set_enabled(true); + vmem_refresh->set_disabled(true); inspect_scene_tree->clear(); inspector->edit(NULL); @@ -1622,6 +1626,7 @@ void ScriptEditorDebugger::_output_clear() { void ScriptEditorDebugger::_export_csv() { file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE); + file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM); file_dialog_mode = SAVE_CSV; file_dialog->popup_centered_ratio(); } @@ -2187,6 +2192,13 @@ void ScriptEditorDebugger::_item_menu_id_pressed(int p_option) { } } +void ScriptEditorDebugger::_tab_changed(int p_tab) { + if (tabs->get_tab_title(p_tab) == TTR("Video RAM")) { + // "Video RAM" tab was clicked, refresh the data it's dislaying when entering the tab. + _video_mem_request(); + } +} + void ScriptEditorDebugger::_bind_methods() { ClassDB::bind_method(D_METHOD("_stack_dump_frame_selected"), &ScriptEditorDebugger::_stack_dump_frame_selected); @@ -2218,6 +2230,7 @@ void ScriptEditorDebugger::_bind_methods() { ClassDB::bind_method(D_METHOD("_error_tree_item_rmb_selected"), &ScriptEditorDebugger::_error_tree_item_rmb_selected); ClassDB::bind_method(D_METHOD("_item_menu_id_pressed"), &ScriptEditorDebugger::_item_menu_id_pressed); + ClassDB::bind_method(D_METHOD("_tab_changed"), &ScriptEditorDebugger::_tab_changed); ClassDB::bind_method(D_METHOD("_paused"), &ScriptEditorDebugger::_paused); @@ -2258,13 +2271,13 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { tabs->add_style_override("panel", editor->get_gui_base()->get_stylebox("DebuggerPanel", "EditorStyles")); tabs->add_style_override("tab_fg", editor->get_gui_base()->get_stylebox("DebuggerTabFG", "EditorStyles")); tabs->add_style_override("tab_bg", editor->get_gui_base()->get_stylebox("DebuggerTabBG", "EditorStyles")); + tabs->connect("tab_changed", this, "_tab_changed"); add_child(tabs); { //debugger VBoxContainer *vbc = memnew(VBoxContainer); vbc->set_name(TTR("Debugger")); - //tabs->add_child(vbc); Control *dbg = vbc; HBoxContainer *hbc = memnew(HBoxContainer); @@ -2522,6 +2535,7 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { vmem_total->set_custom_minimum_size(Size2(100, 0) * EDSCALE); vmem_hb->add_child(vmem_total); vmem_refresh = memnew(ToolButton); + vmem_refresh->set_disabled(true); vmem_hb->add_child(vmem_refresh); vmem_vb->add_child(vmem_hb); vmem_refresh->connect("pressed", this, "_video_mem_request"); @@ -2534,20 +2548,20 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { vmmc->set_v_size_flags(SIZE_EXPAND_FILL); vmem_vb->add_child(vmmc); - vmem_vb->set_name(TTR("Video Mem")); + vmem_vb->set_name(TTR("Video RAM")); vmem_tree->set_columns(4); vmem_tree->set_column_titles_visible(true); vmem_tree->set_column_title(0, TTR("Resource Path")); vmem_tree->set_column_expand(0, true); vmem_tree->set_column_expand(1, false); vmem_tree->set_column_title(1, TTR("Type")); - vmem_tree->set_column_min_width(1, 100); + vmem_tree->set_column_min_width(1, 100 * EDSCALE); vmem_tree->set_column_expand(2, false); vmem_tree->set_column_title(2, TTR("Format")); - vmem_tree->set_column_min_width(2, 150); + vmem_tree->set_column_min_width(2, 150 * EDSCALE); vmem_tree->set_column_expand(3, false); vmem_tree->set_column_title(3, TTR("Usage")); - vmem_tree->set_column_min_width(3, 80); + vmem_tree->set_column_min_width(3, 80 * EDSCALE); vmem_tree->set_hide_root(true); tabs->add_child(vmem_vb); diff --git a/editor/script_editor_debugger.h b/editor/script_editor_debugger.h index 7d91e247b6..589a011bff 100644 --- a/editor/script_editor_debugger.h +++ b/editor/script_editor_debugger.h @@ -226,6 +226,7 @@ private: void _error_tree_item_rmb_selected(const Vector2 &p_pos); void _item_menu_id_pressed(int p_option); + void _tab_changed(int p_tab); void _export_csv(); diff --git a/main/main.cpp b/main/main.cpp index 88d4dcc1fc..3cc809b813 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1900,6 +1900,8 @@ bool Main::start() { ProgressDialog *progress_dialog = memnew(ProgressDialog); pmanager->add_child(progress_dialog); sml->get_root()->add_child(pmanager); + // Speed up rendering slightly by disabling 3D features while in the project manager. + sml->get_root()->set_usage(Viewport::USAGE_2D_NO_SAMPLING); OS::get_singleton()->set_context(OS::CONTEXT_PROJECTMAN); project_manager = true; } diff --git a/modules/gdnative/SCsub b/modules/gdnative/SCsub index 0cdd585558..a18c75fa27 100644 --- a/modules/gdnative/SCsub +++ b/modules/gdnative/SCsub @@ -30,23 +30,3 @@ _, gensource = env_gdnative.CommandNoCache(['include/gdnative_api_struct.gen.h', env_gdnative.add_source_files(env.modules_sources, [gensource]) env.use_ptrcall = True - - -if ARGUMENTS.get('gdnative_wrapper', False): - gensource, = env_gdnative.CommandNoCache('gdnative_wrapper_code.gen.cpp', 'gdnative_api.json', run_in_subprocess(gdnative_builders.build_gdnative_wrapper_code)) - - gd_wrapper_env = env.Clone() - gd_wrapper_env.Prepend(CPPPATH=['#modules/gdnative/include/']) - - if gd_wrapper_env['use_lto']: - if not env.msvc: - gd_wrapper_env.Append(CCFLAGS=['-fno-lto']) - gd_wrapper_env.Append(LINKFLAGS=['-fno-lto']) - else: - gd_wrapper_env.Append(CCFLAGS=['/GL-']) - gd_wrapper_env.Append(LINKFLAGS=['/LTCG:OFF']) - - if not env.msvc: - gd_wrapper_env.Append(CCFLAGS=['-fPIC']) - - lib = gd_wrapper_env.add_library("#bin/gdnative_wrapper_code", [gensource]) diff --git a/modules/gdnative/gdnative_builders.py b/modules/gdnative/gdnative_builders.py index 20c1a2233c..0d95a65b7e 100644 --- a/modules/gdnative/gdnative_builders.py +++ b/modules/gdnative/gdnative_builders.py @@ -12,30 +12,6 @@ def _spaced(e): def _build_gdnative_api_struct_header(api): - gdnative_api_init_macro = [ - '\textern const godot_gdnative_core_api_struct *_gdnative_wrapper_api_struct;' - ] - - for ext in api['extensions']: - name = ext['name'] - gdnative_api_init_macro.append( - '\textern const godot_gdnative_ext_{0}_api_struct *_gdnative_wrapper_{0}_api_struct;'.format(name)) - - gdnative_api_init_macro.append('\t_gdnative_wrapper_api_struct = options->api_struct;') - gdnative_api_init_macro.append('\tfor (unsigned int i = 0; i < _gdnative_wrapper_api_struct->num_extensions; i++) { ') - gdnative_api_init_macro.append('\t\tswitch (_gdnative_wrapper_api_struct->extensions[i]->type) {') - - for ext in api['extensions']: - name = ext['name'] - gdnative_api_init_macro.append( - '\t\t\tcase GDNATIVE_EXT_%s:' % ext['type']) - gdnative_api_init_macro.append( - '\t\t\t\t_gdnative_wrapper_{0}_api_struct = (godot_gdnative_ext_{0}_api_struct *)' - ' _gdnative_wrapper_api_struct->extensions[i];'.format(name)) - gdnative_api_init_macro.append('\t\t\t\tbreak;') - gdnative_api_init_macro.append('\t\t}') - gdnative_api_init_macro.append('\t}') - out = [ '/* THIS FILE IS GENERATED DO NOT EDIT */', '#ifndef GODOT_GDNATIVE_API_STRUCT_H', @@ -49,8 +25,6 @@ def _build_gdnative_api_struct_header(api): '#include <pluginscript/godot_pluginscript.h>', '#include <videodecoder/godot_videodecoder.h>', '', - '#define GDNATIVE_API_INIT(options) do { \\\n' + ' \\\n'.join(gdnative_api_init_macro) + ' \\\n } while (0)', - '', '#ifdef __cplusplus', 'extern "C" {', '#endif', @@ -238,76 +212,5 @@ def build_gdnative_api_struct(target, source, env): fd.write(_build_gdnative_api_struct_source(api)) -def _build_gdnative_wrapper_code(api): - out = [ - '/* THIS FILE IS GENERATED DO NOT EDIT */', - '', - '#include <gdnative/gdnative.h>', - '#include <nativescript/godot_nativescript.h>', - '#include <pluginscript/godot_pluginscript.h>', - '#include <arvr/godot_arvr.h>', - '#include <videodecoder/godot_videodecoder.h>', - '', - '#include <gdnative_api_struct.gen.h>', - '', - '#ifdef __cplusplus', - 'extern "C" {', - '#endif', - '', - 'godot_gdnative_core_api_struct *_gdnative_wrapper_api_struct = 0;', - ] - - for ext in api['extensions']: - name = ext['name'] - out.append('godot_gdnative_ext_' + name + '_api_struct *_gdnative_wrapper_' + name + '_api_struct = 0;') - - out += [''] - - for funcdef in api['core']['api']: - args = ', '.join(['%s%s' % (_spaced(t), n) for t, n in funcdef['arguments']]) - out.append('%s%s(%s) {' % (_spaced(funcdef['return_type']), funcdef['name'], args)) - - args = ', '.join(['%s' % n for t, n in funcdef['arguments']]) - - return_line = '\treturn ' if funcdef['return_type'] != 'void' else '\t' - return_line += '_gdnative_wrapper_api_struct->' + funcdef['name'] + '(' + args + ');' - - out.append(return_line) - out.append('}') - out.append('') - - for ext in api['extensions']: - name = ext['name'] - for funcdef in ext['api']: - args = ', '.join(['%s%s' % (_spaced(t), n) for t, n in funcdef['arguments']]) - out.append('%s%s(%s) {' % (_spaced(funcdef['return_type']), funcdef['name'], args)) - - args = ', '.join(['%s' % n for t, n in funcdef['arguments']]) - - return_line = '\treturn ' if funcdef['return_type'] != 'void' else '\t' - return_line += '_gdnative_wrapper_' + name + '_api_struct->' + funcdef['name'] + '(' + args + ');' - - out.append(return_line) - out.append('}') - out.append('') - - out += [ - '#ifdef __cplusplus', - '}', - '#endif' - ] - - return '\n'.join(out) - - -def build_gdnative_wrapper_code(target, source, env): - with open(source[0], 'r') as fd: - api = json.load(fd) - - wrapper_file = target[0] - with open(wrapper_file, 'w') as fd: - fd.write(_build_gdnative_wrapper_code(api)) - - if __name__ == '__main__': subprocess_main(globals()) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs index 5023725f17..5d16260f5d 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs @@ -9,7 +9,7 @@ namespace Godot public T GetNodeOrNull<T>(NodePath path) where T : class { - return GetNode(path) as T; + return GetNodeOrNull(path) as T; } public T GetChild<T>(int idx) where T : class diff --git a/modules/opensimplex/doc_classes/OpenSimplexNoise.xml b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml index f4f54901fb..f500925f75 100644 --- a/modules/opensimplex/doc_classes/OpenSimplexNoise.xml +++ b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml @@ -117,7 +117,8 @@ Difference in period between [member octaves]. </member> <member name="octaves" type="int" setter="set_octaves" getter="get_octaves" default="3"> - Number of OpenSimplex noise layers that are sampled to get the fractal noise. + Number of OpenSimplex noise layers that are sampled to get the fractal noise. Higher values result in more detailed noise but take more time to generate. + [b]Note:[/b] The maximum allowed value is 9. </member> <member name="period" type="float" setter="set_period" getter="get_period" default="64.0"> Period of the base octave. A lower period results in a higher-frequency noise (more value changes across the same distance). diff --git a/modules/opensimplex/open_simplex_noise.cpp b/modules/opensimplex/open_simplex_noise.cpp index c99588aefa..bd187e6b5b 100644 --- a/modules/opensimplex/open_simplex_noise.cpp +++ b/modules/opensimplex/open_simplex_noise.cpp @@ -47,7 +47,7 @@ OpenSimplexNoise::~OpenSimplexNoise() { } void OpenSimplexNoise::_init_seeds() { - for (int i = 0; i < 6; ++i) { + for (int i = 0; i < MAX_OCTAVES; ++i) { open_simplex_noise(seed + i * 2, &(contexts[i])); } } @@ -71,7 +71,10 @@ int OpenSimplexNoise::get_seed() { void OpenSimplexNoise::set_octaves(int p_octaves) { if (p_octaves == octaves) return; - octaves = CLAMP(p_octaves, 1, 6); + + ERR_FAIL_COND_MSG(p_octaves > MAX_OCTAVES, vformat("The number of OpenSimplexNoise octaves is limited to %d; ignoring the new value.", MAX_OCTAVES)); + + octaves = CLAMP(p_octaves, 1, MAX_OCTAVES); emit_changed(); } @@ -182,7 +185,7 @@ void OpenSimplexNoise::_bind_methods() { ClassDB::bind_method(D_METHOD("get_noise_3dv", "pos"), &OpenSimplexNoise::get_noise_3dv); ADD_PROPERTY(PropertyInfo(Variant::INT, "seed"), "set_seed", "get_seed"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "octaves", PROPERTY_HINT_RANGE, "1,6,1"), "set_octaves", "get_octaves"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "octaves", PROPERTY_HINT_RANGE, vformat("1,%d,1", MAX_OCTAVES)), "set_octaves", "get_octaves"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "period", PROPERTY_HINT_RANGE, "0.1,256.0,0.1"), "set_period", "get_period"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "persistence", PROPERTY_HINT_RANGE, "0.0,1.0,0.001"), "set_persistence", "get_persistence"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "lacunarity", PROPERTY_HINT_RANGE, "0.1,4.0,0.01"), "set_lacunarity", "get_lacunarity"); diff --git a/modules/opensimplex/open_simplex_noise.h b/modules/opensimplex/open_simplex_noise.h index 89b12253b9..dce62bc1f9 100644 --- a/modules/opensimplex/open_simplex_noise.h +++ b/modules/opensimplex/open_simplex_noise.h @@ -37,11 +37,16 @@ #include "thirdparty/misc/open-simplex-noise.h" +// The maximum number of octaves allowed. Note that these are statically allocated. +// Higher values become exponentially slower, so this shouldn't be set too high +// to avoid freezing the editor for long periods of time. +#define MAX_OCTAVES 9 + class OpenSimplexNoise : public Resource { GDCLASS(OpenSimplexNoise, Resource); OBJ_SAVE_TYPE(OpenSimplexNoise); - osn_context contexts[6]; + osn_context contexts[MAX_OCTAVES]; int seed; float persistence; // Controls details, value in [0,1]. Higher increases grain, lower increases smoothness. diff --git a/modules/svg/image_loader_svg.cpp b/modules/svg/image_loader_svg.cpp index 57097aaa06..11ae2f81bf 100644 --- a/modules/svg/image_loader_svg.cpp +++ b/modules/svg/image_loader_svg.cpp @@ -103,15 +103,17 @@ Error ImageLoaderSVG::_create_image(Ref<Image> p_image, const PoolVector<uint8_t ERR_PRINT("SVG Corrupted"); return ERR_FILE_CORRUPT; } - if (convert_colors) + + if (convert_colors) { _convert_colors(svg_image); + } - float upscale = upsample ? 2.0 : 1.0; + const float upscale = upsample ? 2.0 : 1.0; - int w = (int)(svg_image->width * p_scale * upscale); + const int w = (int)(svg_image->width * p_scale * upscale); ERR_FAIL_COND_V_MSG(w > Image::MAX_WIDTH, ERR_PARAMETER_RANGE_ERROR, vformat("Can't create image from SVG with scale %s, the resulting image size exceeds max width.", rtos(p_scale))); - int h = (int)(svg_image->height * p_scale * upscale); + const int h = (int)(svg_image->height * p_scale * upscale); ERR_FAIL_COND_V_MSG(h > Image::MAX_HEIGHT, ERR_PARAMETER_RANGE_ERROR, vformat("Can't create image from SVG with scale %s, the resulting image size exceeds max height.", rtos(p_scale))); PoolVector<uint8_t> dst_image; @@ -123,8 +125,9 @@ Error ImageLoaderSVG::_create_image(Ref<Image> p_image, const PoolVector<uint8_t dw.release(); p_image->create(w, h, false, Image::FORMAT_RGBA8, dst_image); - if (upsample) + if (upsample) { p_image->shrink_x2(); + } nsvgDelete(svg_image); diff --git a/platform/osx/detect.py b/platform/osx/detect.py index 7882253e7a..fe839199e8 100644 --- a/platform/osx/detect.py +++ b/platform/osx/detect.py @@ -27,6 +27,9 @@ def get_opts(): ('MACOS_SDK_PATH', 'Path to the macOS SDK', ''), EnumVariable('debug_symbols', 'Add debugging symbols to release builds', 'yes', ('yes', 'no', 'full')), BoolVariable('separate_debug_symbols', 'Create a separate file containing debugging symbols', False), + BoolVariable('use_ubsan', 'Use LLVM/GCC compiler undefined behavior sanitizer (UBSAN)', False), + BoolVariable('use_asan', 'Use LLVM/GCC compiler address sanitizer (ASAN))', False), + BoolVariable('use_tsan', 'Use LLVM/GCC compiler thread sanitizer (TSAN))', False), ] @@ -122,6 +125,21 @@ def configure(env): env["CC"] = "clang" env["LINK"] = "clang++" + if env['use_ubsan'] or env['use_asan'] or env['use_tsan']: + env.extra_suffix += "s" + + if env['use_ubsan']: + env.Append(CCFLAGS=['-fsanitize=undefined']) + env.Append(LINKFLAGS=['-fsanitize=undefined']) + + if env['use_asan']: + env.Append(CCFLAGS=['-fsanitize=address']) + env.Append(LINKFLAGS=['-fsanitize=address']) + + if env['use_tsan']: + env.Append(CCFLAGS=['-fsanitize=thread']) + env.Append(LINKFLAGS=['-fsanitize=thread']) + ## Dependencies if env['builtin_libtheora']: diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index 6a214b8669..53fe11b3bb 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -115,6 +115,20 @@ static Vector2 get_mouse_pos(NSPoint locationInWindow, CGFloat backingScaleFacto return Vector2(mouse_x, mouse_y); } +static NSCursor *cursorFromSelector(SEL selector, SEL fallback = nil) { + if ([NSCursor respondsToSelector:selector]) { + id object = [NSCursor performSelector:selector]; + if ([object isKindOfClass:[NSCursor class]]) { + return object; + } + } + if (fallback) { + // Fallback should be a reasonable default, no need to check. + return [NSCursor performSelector:fallback]; + } + return [NSCursor arrowCursor]; +} + @interface GodotApplication : NSApplication @end @@ -1813,15 +1827,15 @@ void OS_OSX::set_cursor_shape(CursorShape p_shape) { case CURSOR_BUSY: [[NSCursor arrowCursor] set]; break; case CURSOR_DRAG: [[NSCursor closedHandCursor] set]; break; case CURSOR_CAN_DROP: [[NSCursor openHandCursor] set]; break; - case CURSOR_FORBIDDEN: [[NSCursor arrowCursor] set]; break; - case CURSOR_VSIZE: [[NSCursor resizeUpDownCursor] set]; break; - case CURSOR_HSIZE: [[NSCursor resizeLeftRightCursor] set]; break; - case CURSOR_BDIAGSIZE: [[NSCursor arrowCursor] set]; break; - case CURSOR_FDIAGSIZE: [[NSCursor arrowCursor] set]; break; + case CURSOR_FORBIDDEN: [[NSCursor operationNotAllowedCursor] set]; break; + case CURSOR_VSIZE: [cursorFromSelector(@selector(_windowResizeNorthSouthCursor), @selector(resizeUpDownCursor)) set]; break; + case CURSOR_HSIZE: [cursorFromSelector(@selector(_windowResizeEastWestCursor), @selector(resizeLeftRightCursor)) set]; break; + case CURSOR_BDIAGSIZE: [cursorFromSelector(@selector(_windowResizeNorthEastSouthWestCursor)) set]; break; + case CURSOR_FDIAGSIZE: [cursorFromSelector(@selector(_windowResizeNorthWestSouthEastCursor)) set]; break; case CURSOR_MOVE: [[NSCursor arrowCursor] set]; break; case CURSOR_VSPLIT: [[NSCursor resizeUpDownCursor] set]; break; case CURSOR_HSPLIT: [[NSCursor resizeLeftRightCursor] set]; break; - case CURSOR_HELP: [[NSCursor arrowCursor] set]; break; + case CURSOR_HELP: [cursorFromSelector(@selector(_helpCursor)) set]; break; default: { }; } diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index 8598a953b4..3f46afa8e8 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -36,7 +36,14 @@ Size2 OptionButton::get_minimum_size() const { Size2 minsize = Button::get_minimum_size(); if (has_icon("arrow")) { - minsize.width += Control::get_icon("arrow")->get_width() + get_constant("hseparation"); + const Size2 padding = get_stylebox("normal")->get_minimum_size(); + const Size2 arrow_size = Control::get_icon("arrow")->get_size(); + + Size2 content_size = minsize - padding; + content_size.width += arrow_size.width + get_constant("hseparation"); + content_size.height = MAX(content_size.height, arrow_size.height); + + minsize = content_size + padding; } return minsize; diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index c3ddc41813..b045ff4fe1 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -785,23 +785,25 @@ TabContainer::TabAlign TabContainer::get_tab_align() const { return align; } -void TabContainer::set_tabs_visible(bool p_visibe) { +void TabContainer::set_tabs_visible(bool p_visible) { - if (p_visibe == tabs_visible) + if (p_visible == tabs_visible) return; - tabs_visible = p_visibe; + tabs_visible = p_visible; Vector<Control *> tabs = _get_tabs(); for (int i = 0; i < tabs.size(); i++) { Control *c = tabs[i]; - if (p_visibe) + if (p_visible) c->set_margin(MARGIN_TOP, _get_top_margin()); else c->set_margin(MARGIN_TOP, 0); } + update(); + minimum_size_changed(); } bool TabContainer::are_tabs_visible() const { @@ -936,8 +938,10 @@ Size2 TabContainer::get_minimum_size() const { Ref<StyleBox> tab_disabled = get_stylebox("tab_disabled"); Ref<Font> font = get_font("font"); - ms.y += MAX(MAX(tab_bg->get_minimum_size().y, tab_fg->get_minimum_size().y), tab_disabled->get_minimum_size().y); - ms.y += font->get_height(); + if (tabs_visible) { + ms.y += MAX(MAX(tab_bg->get_minimum_size().y, tab_fg->get_minimum_size().y), tab_disabled->get_minimum_size().y); + ms.y += font->get_height(); + } Ref<StyleBox> sb = get_stylebox("panel"); ms += sb->get_minimum_size(); diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h index e69c2ae289..c5a9045ca6 100644 --- a/scene/gui/tab_container.h +++ b/scene/gui/tab_container.h @@ -87,7 +87,7 @@ public: void set_tab_align(TabAlign p_align); TabAlign get_tab_align() const; - void set_tabs_visible(bool p_visibe); + void set_tabs_visible(bool p_visible); bool are_tabs_visible() const; void set_tab_title(int p_tab, const String &p_title); |