From 67acb7de6eba660ee1f0fda5f25669d2462b7fe4 Mon Sep 17 00:00:00 2001 From: Michael Alexsander Date: Sun, 17 Oct 2021 13:55:44 -0300 Subject: Rename `Tabs` to `TabBar` --- doc/classes/TabBar.xml | 339 +++++++ doc/classes/Tabs.xml | 339 ------- editor/editor_node.cpp | 146 +-- editor/editor_node.h | 10 +- editor/editor_themes.cpp | 32 +- editor/icons/TabBar.svg | 1 + editor/icons/Tabs.svg | 1 - editor/plugins/theme_editor_plugin.cpp | 6 +- editor/plugins/theme_editor_plugin.h | 4 +- editor/plugins/tiles/tile_map_editor.cpp | 4 +- editor/plugins/tiles/tile_map_editor.h | 4 +- editor/plugins/tiles/tile_set_editor.cpp | 4 +- editor/plugins/tiles/tile_set_editor.h | 4 +- scene/gui/tab_bar.cpp | 1195 +++++++++++++++++++++++ scene/gui/tab_bar.h | 196 ++++ scene/gui/tabs.cpp | 1195 ----------------------- scene/gui/tabs.h | 196 ---- scene/register_scene_types.cpp | 46 +- scene/resources/default_theme/default_theme.cpp | 48 +- 19 files changed, 1885 insertions(+), 1885 deletions(-) create mode 100644 doc/classes/TabBar.xml delete mode 100644 doc/classes/Tabs.xml create mode 100644 editor/icons/TabBar.svg delete mode 100644 editor/icons/Tabs.svg create mode 100644 scene/gui/tab_bar.cpp create mode 100644 scene/gui/tab_bar.h delete mode 100644 scene/gui/tabs.cpp delete mode 100644 scene/gui/tabs.h diff --git a/doc/classes/TabBar.xml b/doc/classes/TabBar.xml new file mode 100644 index 0000000000..3ca124bb58 --- /dev/null +++ b/doc/classes/TabBar.xml @@ -0,0 +1,339 @@ + + + + Tab bar control. + + + Simple tabs control, similar to [TabContainer] but is only in charge of drawing tabs, not interacting with children. + + + + + + + + + + Adds a new tab. + + + + + + + Removes all OpenType features from the tab title. + + + + + + + Moves the scroll view to make the tab visible. + + + + + + Returns [code]true[/code] if the offset buttons (the ones that appear when there's not enough space for all tabs) are visible. + + + + + + Returns the previously active tab index. + + + + + + Returns [code]true[/code] if select with right mouse button is enabled. + + + + + + Returns the number of tabs. + + + + + + + Returns [code]true[/code] if the tab at index [code]tab_idx[/code] is disabled. + + + + + + + Returns the [Texture2D] for the tab at index [code]tab_idx[/code] or [code]null[/code] if the tab has no [Texture2D]. + + + + + + + Returns tab title language code. + + + + + + Returns the number of hidden tabs offsetted to the left. + + + + + + + + Returns OpenType feature [code]tag[/code] of the tab title. + + + + + + + Returns tab [Rect2] with local position and size. + + + + + + + Returns tab title text base writing direction. + + + + + + + Returns the title of the tab at index [code]tab_idx[/code]. + + + + + + Returns the [TabBar]'s rearrange group ID. + + + + + + + + Moves a tab from [code]from[/code] to [code]to[/code]. + + + + + + + Removes the tab at index [code]tab_idx[/code]. + + + + + + + If [code]true[/code], enables selecting a tab with the right mouse button. + + + + + + + + If [code]disabled[/code] is [code]true[/code], disables the tab at index [code]tab_idx[/code], making it non-interactable. + + + + + + + + Sets an [code]icon[/code] for the tab at index [code]tab_idx[/code]. + + + + + + + + Sets language code of tab title used for line-breaking and text shaping algorithms, if left empty current locale is used instead. + + + + + + + + + Sets OpenType feature [code]tag[/code] for the tab title. More info: [url=https://docs.microsoft.com/en-us/typography/opentype/spec/featuretags]OpenType feature tags[/url]. + + + + + + + + Sets tab title base writing direction. + + + + + + + + Sets a [code]title[/code] for the tab at index [code]tab_idx[/code]. + + + + + + + Defines the rearrange group ID. Choose for each [TabBar] the same value to dragging tabs between [TabBar]. Enable drag with [member drag_to_rearrange_enabled]. + + + + + + If [code]true[/code], tabs overflowing this node's width will be hidden, displaying two navigation buttons instead. Otherwise, this node's minimum size is updated so that all tabs are visible. + + + Select tab at index [code]tab_idx[/code]. + + + If [code]true[/code], tabs can be rearranged with mouse drag. + + + if [code]true[/code], the mouse's scroll wheel can be used to navigate the scroll view. + + + The alignment of all tabs. See [enum TabAlign] for details. + + + Sets when the close button will appear on the tabs. See [enum CloseButtonDisplayPolicy] for details. + + + + + + + Emitted when the active tab is rearranged via mouse drag. See [member drag_to_rearrange_enabled]. + + + + + + Emitted when switching to another tab. + + + + + + Emitted when a tab is clicked, even if it is the current tab. + + + + + + Emitted when a tab is closed. + + + + + + Emitted when a tab is hovered by the mouse. + + + + + + Emitted when a tab is right-clicked. + + + + + + Align the tabs to the left. + + + Align the tabs to the center. + + + Align the tabs to the right. + + + Represents the size of the [enum TabAlign] enum. + + + Never show the close buttons. + + + Only show the close button on the currently active tab. + + + Show the close button on all tabs. + + + Represents the size of the [enum CloseButtonDisplayPolicy] enum. + + + + + The icon for the close button (see [member tab_close_display_policy]). + + + Background of the close button when it's being hovered with the cursor. + + + Background of the close button when it's being pressed. + + + Icon for the left arrow button that appears when there are too many tabs to fit in the container width. When the button is disabled (i.e. the first tab is visible), it appears semi-transparent. + + + Icon for the left arrow button that appears when there are too many tabs to fit in the container width. Used when the button is being hovered with the cursor. + + + The font used to draw tab names. + + + Font color of disabled tabs. + + + The tint of text outline of the tab name. + + + Font color of the currently selected tab. + + + Font size of the tab names. + + + Font color of the other, unselected tabs. + + + The horizontal separation between the elements inside tabs. + + + Icon for the right arrow button that appears when there are too many tabs to fit in the container width. When the button is disabled (i.e. the last tab is visible) it appears semi-transparent. + + + Icon for the right arrow button that appears when there are too many tabs to fit in the container width. Used when the button is being hovered with the cursor. + + + The size of the tab text outline. + + + The style of disabled tabs. + + + The style of the currently selected tab. + + + The style of the other, unselected tabs. + + + diff --git a/doc/classes/Tabs.xml b/doc/classes/Tabs.xml deleted file mode 100644 index ded4f0b32f..0000000000 --- a/doc/classes/Tabs.xml +++ /dev/null @@ -1,339 +0,0 @@ - - - - Tabs control. - - - Simple tabs control, similar to [TabContainer] but is only in charge of drawing tabs, not interacting with children. - - - - - - - - - - Adds a new tab. - - - - - - - Removes all OpenType features from the tab title. - - - - - - - Moves the scroll view to make the tab visible. - - - - - - Returns [code]true[/code] if the offset buttons (the ones that appear when there's not enough space for all tabs) are visible. - - - - - - Returns the previously active tab index. - - - - - - Returns [code]true[/code] if select with right mouse button is enabled. - - - - - - Returns the number of tabs. - - - - - - - Returns [code]true[/code] if the tab at index [code]tab_idx[/code] is disabled. - - - - - - - Returns the [Texture2D] for the tab at index [code]tab_idx[/code] or [code]null[/code] if the tab has no [Texture2D]. - - - - - - - Returns tab title language code. - - - - - - Returns the number of hidden tabs offsetted to the left. - - - - - - - - Returns OpenType feature [code]tag[/code] of the tab title. - - - - - - - Returns tab [Rect2] with local position and size. - - - - - - - Returns tab title text base writing direction. - - - - - - - Returns the title of the tab at index [code]tab_idx[/code]. - - - - - - Returns the [Tabs]' rearrange group ID. - - - - - - - - Moves a tab from [code]from[/code] to [code]to[/code]. - - - - - - - Removes the tab at index [code]tab_idx[/code]. - - - - - - - If [code]true[/code], enables selecting a tab with the right mouse button. - - - - - - - - If [code]disabled[/code] is [code]true[/code], disables the tab at index [code]tab_idx[/code], making it non-interactable. - - - - - - - - Sets an [code]icon[/code] for the tab at index [code]tab_idx[/code]. - - - - - - - - Sets language code of tab title used for line-breaking and text shaping algorithms, if left empty current locale is used instead. - - - - - - - - - Sets OpenType feature [code]tag[/code] for the tab title. More info: [url=https://docs.microsoft.com/en-us/typography/opentype/spec/featuretags]OpenType feature tags[/url]. - - - - - - - - Sets tab title base writing direction. - - - - - - - - Sets a [code]title[/code] for the tab at index [code]tab_idx[/code]. - - - - - - - Defines the rearrange group ID. Choose for each [Tabs] the same value to dragging tabs between [Tabs]. Enable drag with [member drag_to_rearrange_enabled]. - - - - - - If [code]true[/code], tabs overflowing this node's width will be hidden, displaying two navigation buttons instead. Otherwise, this node's minimum size is updated so that all tabs are visible. - - - Select tab at index [code]tab_idx[/code]. - - - If [code]true[/code], tabs can be rearranged with mouse drag. - - - if [code]true[/code], the mouse's scroll wheel can be used to navigate the scroll view. - - - The alignment of all tabs. See [enum TabAlign] for details. - - - Sets when the close button will appear on the tabs. See [enum CloseButtonDisplayPolicy] for details. - - - - - - - Emitted when the active tab is rearranged via mouse drag. See [member drag_to_rearrange_enabled]. - - - - - - Emitted when switching to another tab. - - - - - - Emitted when a tab is clicked, even if it is the current tab. - - - - - - Emitted when a tab is closed. - - - - - - Emitted when a tab is hovered by the mouse. - - - - - - Emitted when a tab is right-clicked. - - - - - - Align the tabs to the left. - - - Align the tabs to the center. - - - Align the tabs to the right. - - - Represents the size of the [enum TabAlign] enum. - - - Never show the close buttons. - - - Only show the close button on the currently active tab. - - - Show the close button on all tabs. - - - Represents the size of the [enum CloseButtonDisplayPolicy] enum. - - - - - The icon for the close button (see [member tab_close_display_policy]). - - - Background of the close button when it's being hovered with the cursor. - - - Background of the close button when it's being pressed. - - - Icon for the left arrow button that appears when there are too many tabs to fit in the container width. When the button is disabled (i.e. the first tab is visible), it appears semi-transparent. - - - Icon for the left arrow button that appears when there are too many tabs to fit in the container width. Used when the button is being hovered with the cursor. - - - The font used to draw tab names. - - - Font color of disabled tabs. - - - The tint of text outline of the tab name. - - - Font color of the currently selected tab. - - - Font size of the tab names. - - - Font color of the other, unselected tabs. - - - The horizontal separation between the elements inside tabs. - - - Icon for the right arrow button that appears when there are too many tabs to fit in the container width. When the button is disabled (i.e. the last tab is visible) it appears semi-transparent. - - - Icon for the right arrow button that appears when there are too many tabs to fit in the container width. Used when the button is being hovered with the cursor. - - - The size of the tab text outline. - - - The style of disabled tabs. - - - The style of the currently selected tab. - - - The style of the other, unselected tabs. - - - diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index ffcb75ba2c..891705da98 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -59,8 +59,8 @@ #include "scene/gui/panel.h" #include "scene/gui/panel_container.h" #include "scene/gui/split_container.h" +#include "scene/gui/tab_bar.h" #include "scene/gui/tab_container.h" -#include "scene/gui/tabs.h" #include "scene/gui/texture_progress_bar.h" #include "scene/main/window.h" #include "scene/resources/packed_scene.h" @@ -658,7 +658,7 @@ void EditorNode::_notification(int p_what) { } break; case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { - scene_tabs->set_tab_close_display_policy((bool(EDITOR_GET("interface/scene_tabs/always_show_close_button")) ? Tabs::CLOSE_BUTTON_SHOW_ALWAYS : Tabs::CLOSE_BUTTON_SHOW_ACTIVE_ONLY)); + scene_tabs->set_tab_close_display_policy((bool(EDITOR_GET("interface/scene_tabs/always_show_close_button")) ? TabBar::CLOSE_BUTTON_SHOW_ALWAYS : TabBar::CLOSE_BUTTON_SHOW_ACTIVE_ONLY)); theme = create_custom_theme(theme_base->get_theme()); theme_base->set_theme(theme); @@ -838,7 +838,7 @@ void EditorNode::_resources_changed(const Vector &p_resources) { } if (res->get_import_path() != String()) { - //this is an imported resource, will be reloaded if reimported via the _resources_reimported() callback + // this is an imported resource, will be reloaded if reimported via the _resources_reimported() callback continue; } @@ -942,21 +942,21 @@ void EditorNode::_fs_changed() { } void EditorNode::_resources_reimported(const Vector &p_resources) { - List scenes; //will load later + List scenes; // will load later int current_tab = scene_tabs->get_current_tab(); for (int i = 0; i < p_resources.size(); i++) { String file_type = ResourceLoader::get_resource_type(p_resources[i]); if (file_type == "PackedScene") { scenes.push_back(p_resources[i]); - //reload later if needed, first go with normal resources + // reload later if needed, first go with normal resources continue; } if (!ResourceCache::has(p_resources[i])) { - continue; //not loaded, no need to reload + continue; // not loaded, no need to reload } - //reload normally + // reload normally Resource *resource = ResourceCache::get(p_resources[i]); if (resource) { resource->reload_from_file(); @@ -1140,7 +1140,7 @@ Error EditorNode::load_resource(const String &p_resource, bool p_ignore_broken_d ERR_FAIL_COND_V(!res.is_valid(), ERR_CANT_OPEN); if (!p_ignore_broken_deps && dependency_errors.has(p_resource)) { - //current_option = -1; + // current_option = -1; Vector errors; for (Set::Element *E = dependency_errors[p_resource].front(); E; E = E->next()) { errors.push_back(E->get()); @@ -1216,7 +1216,7 @@ void EditorNode::save_resource_as(const Ref &p_resource, const String List preferred; for (const String &E : extensions) { if (p_resource->is_class("Script") && (E == "tres" || E == "res")) { - //this serves no purpose and confused people + // this serves no purpose and confused people continue; } file->add_filter("*." + E + " ; " + E.to_upper()); @@ -1325,7 +1325,7 @@ void EditorNode::_get_scene_metadata(const String &p_file) { Error err = cf->load(path); if (err != OK || !cf->has_section("editor_states")) { - return; //must not exist + return; // must not exist } List esl; @@ -1349,7 +1349,7 @@ void EditorNode::_set_scene_metadata(const String &p_file, int p_idx) { return; } - scene->set_meta("__editor_run_settings__", Variant()); //clear it (no point in keeping it) + scene->set_meta("__editor_run_settings__", Variant()); // clear it (no point in keeping it) scene->set_meta("__editor_plugin_states__", Variant()); String path = EditorSettings::get_singleton()->get_project_settings_dir().plus_file(p_file.get_file() + "-editstate-" + p_file.md5_text() + ".cfg"); @@ -1392,10 +1392,10 @@ bool EditorNode::_find_and_save_resource(RES p_res, Map &processed, i if (p_res->get_path().is_resource_file()) { if (changed || subchanged) { - //save + // save ResourceSaver::save(p_res->get_path(), p_res, flags); } - processed[p_res] = false; //because it's a file + processed[p_res] = false; // because it's a file return false; } else { processed[p_res] = changed; @@ -1493,7 +1493,7 @@ void EditorNode::_save_scene_with_preview(String p_file, int p_idx) { _find_node_types(editor_data.get_edited_scene_root(), c2d, c3d); save.step(TTR("Creating Thumbnail"), 1); - //current view? + // current view? Ref img; // If neither 3D or 2D nodes are present, make a 1x1 black texture. @@ -1546,12 +1546,12 @@ void EditorNode::_save_scene_with_preview(String p_file, int p_idx) { } img->convert(Image::FORMAT_RGB8); - //save thumbnail directly, as thumbnailer may not update due to actual scene not changing md5 + // save thumbnail directly, as thumbnailer may not update due to actual scene not changing md5 String temp_path = EditorPaths::get_singleton()->get_cache_dir(); String cache_base = ProjectSettings::get_singleton()->globalize_path(p_file).md5_text(); cache_base = temp_path.plus_file("resthumb-" + cache_base); - //does not have it, try to load a cached thumbnail + // does not have it, try to load a cached thumbnail String file = cache_base + ".png"; @@ -1599,7 +1599,7 @@ static bool _find_edited_resources(const Ref &p_resource, Setget_path().is_resource_file()) { //not a subresource, continue + if (res->get_path().is_resource_file()) { // not a subresource, continue continue; } if (_find_edited_resources(res, edited_resources)) { @@ -1612,7 +1612,7 @@ static bool _find_edited_resources(const Ref &p_resource, Setget("filesystem/on_save/compress_binary_resources")) { @@ -1628,7 +1628,7 @@ int EditorNode::_save_external_resources() { if (!res->get_path().is_resource_file()) { continue; } - //not only check if this resource is edited, check contained subresources too + // not only check if this resource is edited, check contained subresources too if (_find_edited_resources(res, edited_subresources)) { ResourceSaver::save(res->get_path(), res, flg); saved++; @@ -1837,7 +1837,7 @@ void EditorNode::_dialog_action(String p_file) { case SETTINGS_PICK_MAIN_SCENE: { ProjectSettings::get_singleton()->set("application/run/main_scene", p_file); ProjectSettings::get_singleton()->save(); - //would be nice to show the project manager opened with the highlighted field.. + // would be nice to show the project manager opened with the highlighted field.. if (pick_main_scene->has_meta("from_native") && (bool)pick_main_scene->get_meta("from_native")) { run_native->resume_run_native(); @@ -1980,7 +1980,7 @@ void EditorNode::_dialog_action(String p_file) { } } break; - default: { //save scene? + default: { // save scene? if (file->get_file_mode() == EditorFileDialog::FILE_MODE_SAVE_FILE) { _save_scene_with_preview(p_file); @@ -2141,7 +2141,7 @@ void EditorNode::_edit_current() { bool is_resource = current_obj->is_class("Resource"); bool is_node = current_obj->is_class("Node"); - String editable_warning; //none by default + String editable_warning; // none by default if (is_resource) { Resource *current_res = Object::cast_to(current_obj); @@ -2243,7 +2243,7 @@ void EditorNode::_edit_current() { for (; plugin_index < editor_table.size(); plugin_index++) { if (editor_table[plugin_index] == main_plugin) { if (!main_editor_buttons[plugin_index]->is_visible()) { - main_plugin = nullptr; //if button is not visible, then no plugin active + main_plugin = nullptr; // if button is not visible, then no plugin active } break; @@ -2329,7 +2329,7 @@ void EditorNode::_run(bool p_current, const String &p_custom) { } if (run_filename == "") { - //evidently, run the scene + // evidently, run the scene if (!ensure_main_scene(false)) { return; } @@ -2412,7 +2412,7 @@ void EditorNode::_android_build_source_selected(const String &p_file) { export_template_manager->install_android_template_from_file(p_file); } void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { - if (!p_confirmed) { //this may be a hack.. + if (!p_confirmed) { // this may be a hack.. current_option = (MenuOptions)p_option; } @@ -2981,7 +2981,7 @@ int EditorNode::_next_unsaved_scene(bool p_valid_filename, int p_start) { void EditorNode::_exit_editor() { exiting = true; - resource_preview->stop(); //stop early to avoid crashes + resource_preview->stop(); // stop early to avoid crashes _save_docks(); // Dim the editor window while it's quitting to make it clearer that it's busy @@ -3087,7 +3087,7 @@ void EditorNode::_editor_select(int p_which) { ERR_FAIL_INDEX(p_which, editor_table.size()); - if (!main_editor_buttons[p_which]->is_visible()) { //button hidden, no editor + if (!main_editor_buttons[p_which]->is_visible()) { // button hidden, no editor return; } @@ -3229,7 +3229,7 @@ void EditorNode::set_addon_plugin_enabled(const String &p_addon, bool p_enabled, if (!p_enabled) { EditorPlugin *addon = plugin_addons[p_addon]; remove_editor_plugin(addon, p_config_changed); - memdelete(addon); //bye + memdelete(addon); // bye plugin_addons.erase(p_addon); _update_addon_config(); return; @@ -3321,10 +3321,10 @@ void EditorNode::_remove_edited_scene(bool p_change_tab) { void EditorNode::_remove_scene(int index, bool p_change_tab) { if (editor_data.get_edited_scene() == index) { - //Scene to remove is current scene + // Scene to remove is current scene _remove_edited_scene(p_change_tab); } else { - //Scene to remove is not active scene + // Scene to remove is not active scene editor_data.remove_scene(index); } } @@ -3338,7 +3338,7 @@ void EditorNode::set_edited_scene(Node *p_scene) { get_editor_data().set_edited_scene_root(p_scene); if (Object::cast_to(p_scene)) { - Object::cast_to(p_scene)->show(); //show popups + Object::cast_to(p_scene)->show(); // show popups } scene_tree_dock->set_edited_scene(p_scene); if (get_tree()) { @@ -3374,7 +3374,7 @@ Dictionary EditorNode::_get_main_scene_state() { void EditorNode::_set_main_scene_state(Dictionary p_state, Node *p_for_scene) { if (get_edited_scene() != p_for_scene && p_for_scene != nullptr) { - return; //not for this scene + return; // not for this scene } changing_scene = false; @@ -3389,7 +3389,7 @@ void EditorNode::_set_main_scene_state(Dictionary p_state, Node *p_for_scene) { if (p_state.has("editor_index")) { int index = p_state["editor_index"]; - if (current < 2) { //if currently in spatial/2d, only switch to spatial/2d. if currently in script, stay there + if (current < 2) { // if currently in spatial/2d, only switch to spatial/2d. if currently in script, stay there if (index < 2 || !get_edited_scene()) { _editor_select(index); } @@ -3398,7 +3398,7 @@ void EditorNode::_set_main_scene_state(Dictionary p_state, Node *p_for_scene) { if (get_edited_scene()) { if (current < 2) { - //use heuristic instead + // use heuristic instead int n2d = 0, n3d = 0; _find_node_types(get_edited_scene(), n2d, n3d); if (n2d > n3d) { @@ -3420,7 +3420,7 @@ void EditorNode::_set_main_scene_state(Dictionary p_state, Node *p_for_scene) { scene_tree_dock->set_filter(p_state["node_filter"]); } - //this should only happen at the very end + // this should only happen at the very end EditorDebuggerNode::get_singleton()->update_live_edit_root(); ScriptEditor::get_singleton()->set_scene_root_script(editor_data.get_scene_root_script(editor_data.get_edited_scene())); @@ -3441,7 +3441,7 @@ void EditorNode::_clear_undo_history() { } void EditorNode::set_current_scene(int p_idx) { - //Save the folding in case the scene gets reloaded. + // Save the folding in case the scene gets reloaded. if (editor_data.get_scene_path(p_idx) != "" && editor_data.get_edited_scene_root(p_idx)) { editor_folding.save_scene_folding(editor_data.get_edited_scene_root(p_idx), editor_data.get_scene_path(p_idx)); } @@ -3469,7 +3469,7 @@ void EditorNode::set_current_scene(int p_idx) { Node *new_scene = editor_data.get_edited_scene_root(); if (Object::cast_to(new_scene)) { - Object::cast_to(new_scene)->show(); //show popups + Object::cast_to(new_scene)->show(); // show popups } scene_tree_dock->set_edited_scene(new_scene); @@ -3488,7 +3488,7 @@ void EditorNode::set_current_scene(int p_idx) { _update_title(); - call_deferred(SNAME("_set_main_scene_state"), state, get_edited_scene()); //do after everything else is done setting up + call_deferred(SNAME("_set_main_scene_state"), state, get_edited_scene()); // do after everything else is done setting up } bool EditorNode::is_scene_open(const String &p_path) { @@ -3590,7 +3590,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b return ERR_FILE_MISSING_DEPENDENCIES; } - dependency_errors.erase(lpath); //at least not self path + dependency_errors.erase(lpath); // at least not self path for (KeyValue> &E : dependency_errors) { String txt = vformat(TTR("Scene '%s' has broken dependencies:"), E.key) + "\n"; @@ -3601,7 +3601,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b } if (ResourceCache::has(lpath)) { - //used from somewhere else? no problem! update state and replace sdata + // used from somewhere else? no problem! update state and replace sdata Ref ps = Ref(Object::cast_to(ResourceCache::get(lpath))); if (ps.is_valid()) { ps->replace_state(sdata->get_state()); @@ -3610,7 +3610,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b } } else { - sdata->set_path(lpath, true); //take over path + sdata->set_path(lpath, true); // take over path } Node *new_scene = sdata->instantiate(PackedScene::GEN_EDIT_STATE_MAIN); @@ -4224,7 +4224,7 @@ void EditorNode::_dock_make_float() { ERR_FAIL_COND(!dock); const Size2i borders = Size2i(4, 4) * EDSCALE; - Size2 dock_size = dock->get_size() + borders * 2; //remember size + Size2 dock_size = dock->get_size() + borders * 2; // remember size Point2 dock_screen_pos = dock->get_global_position() + get_tree()->get_root()->get_position() - borders; print_line("dock pos: " + dock->get_global_position() + " window pos: " + get_tree()->get_root()->get_position()); @@ -4464,7 +4464,7 @@ void EditorNode::_dock_select_draw() { void EditorNode::_save_docks() { if (waiting_for_first_scan) { - return; //scanning, do not touch docks + return; // scanning, do not touch docks } Ref config; config.instantiate(); @@ -4535,7 +4535,7 @@ void EditorNode::_load_docks() { config.instantiate(); Error err = config->load(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("editor_layout.cfg")); if (err != OK) { - //no config + // no config if (overridden_default_layout >= 0) { _layout_menu_option(overridden_default_layout); } @@ -4648,7 +4648,7 @@ void EditorNode::_load_docks_from_config(Ref p_layout, const String for (int j = 0; j < names.size(); j++) { String name = names[j]; - //find it, in a horribly inefficient way + // find it, in a horribly inefficient way int atidx = -1; Control *node = nullptr; for (int k = 0; k < DOCK_SLOT_MAX; k++) { @@ -4662,7 +4662,7 @@ void EditorNode::_load_docks_from_config(Ref p_layout, const String atidx = k; break; } - if (atidx == -1) { //well, it's not anywhere + if (atidx == -1) { // well, it's not anywhere continue; } @@ -4778,7 +4778,7 @@ bool EditorNode::has_scenes_in_session() { } bool EditorNode::ensure_main_scene(bool p_from_native) { - pick_main_scene->set_meta("from_native", p_from_native); //whether from play button or native run + pick_main_scene->set_meta("from_native", p_from_native); // whether from play button or native run String main_scene = GLOBAL_DEF("application/run/main_scene", ""); if (main_scene == "") { @@ -4899,7 +4899,7 @@ void EditorNode::_update_layouts_menu() { config.instantiate(); Error err = config->load(EditorSettings::get_singleton()->get_editor_layouts_config()); if (err != OK) { - return; //no config + return; // no config } List layouts; @@ -4940,7 +4940,7 @@ void EditorNode::_layout_menu_option(int p_id) { config.instantiate(); Error err = config->load(EditorSettings::get_singleton()->get_editor_layouts_config()); if (err != OK) { - return; //no config + return; // no config } _load_docks_from_config(config, editor_layouts->get_item_text(p_id)); @@ -5081,7 +5081,7 @@ void EditorNode::_scene_tab_changed(int p_tab) { bool unsaved = (saved_version != editor_data.get_undo_redo().get_version()); if (p_tab == editor_data.get_edited_scene()) { - return; //pointless + return; // pointless } uint64_t next_scene_version = editor_data.get_scene_version(p_tab); @@ -5284,11 +5284,11 @@ Variant EditorNode::drag_resource(const Ref &p_res, Control *p_from) { Ref preview; { - //todo make proper previews + // todo make proper previews Ref texture = gui_base->get_theme_icon(SNAME("FileBigThumb"), SNAME("EditorIcons")); Ref img = texture->get_image(); img = img->duplicate(); - img->resize(48, 48); //meh + img->resize(48, 48); // meh Ref resized_pic = Ref(memnew(ImageTexture)); resized_pic->create_from_image(img); preview = resized_pic; @@ -5306,7 +5306,7 @@ Variant EditorNode::drag_resource(const Ref &p_res, Control *p_from) { drag_control->add_child(label); - p_from->set_drag_preview(drag_control); //wait until it enters scene + p_from->set_drag_preview(drag_control); // wait until it enters scene label->set_position(Point2((preview->get_width() - label->get_minimum_size().width) / 2, preview->get_height())); @@ -5360,7 +5360,7 @@ Variant EditorNode::drag_files_and_dirs(const Vector &p_paths, Control * } vbox->add_child(label); } - p_from->set_drag_preview(vbox); //wait until it enters scene + p_from->set_drag_preview(vbox); // wait until it enters scene Dictionary drag_data; drag_data["type"] = has_folder ? "files_and_dirs" : "files"; @@ -5477,7 +5477,7 @@ void EditorNode::reload_scene(const String &p_path) { if (scene_idx == -1) { if (get_edited_scene()) { - //scene is not open, so at it might be instantiated. We'll refresh the whole scene later. + // scene is not open, so at it might be instantiated. We'll refresh the whole scene later. editor_data.get_undo_redo().clear_history(); } return; @@ -5488,17 +5488,17 @@ void EditorNode::reload_scene(const String &p_path) { _set_scene_metadata(p_path); } - //remove scene + // remove scene _remove_scene(scene_idx, false); - //reload scene + // reload scene load_scene(p_path, true, false, true, true); - //adjust index so tab is back a the previous position + // adjust index so tab is back a the previous position editor_data.move_edited_scene_to_index(scene_idx); get_undo_redo()->clear_history(); - //recover the tab + // recover the tab scene_tabs->set_current_tab(current_tab); } @@ -5598,7 +5598,7 @@ void EditorNode::_update_video_driver_color() { void EditorNode::_video_driver_selected(int p_which) { String driver = video_driver->get_item_metadata(p_which); - String current = ""; //OS::get_singleton()->get_video_driver_name(OS::get_singleton()->get_current_video_driver()); + String current = ""; // OS::get_singleton()->get_video_driver_name(OS::get_singleton()->get_current_video_driver()); if (driver == current) { return; @@ -5787,9 +5787,9 @@ EditorNode::EditorNode() { PhysicsServer2D::get_singleton()->set_active(false); // no physics by default if editor ScriptServer::set_scripting_enabled(false); // no scripting by default if editor - EditorHelp::generate_doc(); //before any editor classes are created + EditorHelp::generate_doc(); // before any editor classes are created SceneState::set_disable_placeholders(true); - ResourceLoader::clear_translation_remaps(); //no remaps using during editor + ResourceLoader::clear_translation_remaps(); // no remaps using during editor ResourceLoader::clear_path_remaps(); Input *id = Input::get_singleton(); @@ -5803,8 +5803,8 @@ EditorNode::EditorNode() { } if (!found_touchscreen && Input::get_singleton()) { - //only if no touchscreen ui hint, set emulation - id->set_emulate_touch_from_mouse(false); //just disable just in case + // only if no touchscreen ui hint, set emulation + id->set_emulate_touch_from_mouse(false); // just disable just in case } DisplayServer::get_singleton()->cursor_set_custom_image(RES()); } @@ -5871,7 +5871,7 @@ EditorNode::EditorNode() { ResourceLoader::set_error_notify_func(this, _load_error_notify); ResourceLoader::set_dependency_error_notify_func(this, _dependency_error_report); - { //register importers at the beginning, so dialogs are created with the right extensions + { // register importers at the beginning, so dialogs are created with the right extensions Ref import_texture; import_texture.instantiate(); ResourceFormatImporter::get_singleton()->add_importer(import_texture); @@ -5974,7 +5974,7 @@ EditorNode::EditorNode() { EditorFileSystem *efs = memnew(EditorFileSystem); add_child(efs); - //used for previews + // used for previews FileDialog::get_icon_func = _file_dialog_get_icon; FileDialog::register_func = _file_dialog_register; FileDialog::unregister_func = _file_dialog_unregister; @@ -5993,7 +5993,7 @@ EditorNode::EditorNode() { ClassDB::set_class_enabled("RootMotionView", true); - //defs here, use EDITOR_GET in logic + // defs here, use EDITOR_GET in logic EDITOR_DEF_RST("interface/scene_tabs/always_show_close_button", false); EDITOR_DEF_RST("interface/scene_tabs/resize_if_many_tabs", true); EDITOR_DEF_RST("interface/scene_tabs/minimum_width", 50); @@ -6217,13 +6217,13 @@ EditorNode::EditorNode() { tab_preview->set_position(Point2(2, 2) * EDSCALE); tab_preview_panel->add_child(tab_preview); - scene_tabs = memnew(Tabs); + scene_tabs = memnew(TabBar); scene_tabs->add_theme_style_override("tab_selected", gui_base->get_theme_stylebox(SNAME("SceneTabFG"), SNAME("EditorStyles"))); scene_tabs->add_theme_style_override("tab_unselected", gui_base->get_theme_stylebox(SNAME("SceneTabBG"), SNAME("EditorStyles"))); scene_tabs->set_select_with_rmb(true); scene_tabs->add_tab("unsaved"); - scene_tabs->set_tab_align(Tabs::ALIGN_LEFT); - scene_tabs->set_tab_close_display_policy((bool(EDITOR_DEF("interface/scene_tabs/always_show_close_button", false)) ? Tabs::CLOSE_BUTTON_SHOW_ALWAYS : Tabs::CLOSE_BUTTON_SHOW_ACTIVE_ONLY)); + scene_tabs->set_tab_align(TabBar::ALIGN_LEFT); + scene_tabs->set_tab_close_display_policy((bool(EDITOR_DEF("interface/scene_tabs/always_show_close_button", false)) ? TabBar::CLOSE_BUTTON_SHOW_ALWAYS : TabBar::CLOSE_BUTTON_SHOW_ACTIVE_ONLY)); scene_tabs->set_min_width(int(EDITOR_DEF("interface/scene_tabs/minimum_width", 50)) * EDSCALE); scene_tabs->set_drag_to_rearrange_enabled(true); scene_tabs->connect("tab_changed", callable_mp(this, &EditorNode::_scene_tab_changed)); @@ -6924,7 +6924,7 @@ EditorNode::EditorNode() { EditorAudioBuses *audio_bus_editor = EditorAudioBuses::register_editor(); - ScriptTextEditor::register_editor(); //register one for text scripts + ScriptTextEditor::register_editor(); // register one for text scripts TextEditor::register_editor(); if (StreamPeerSSL::is_available()) { @@ -6933,12 +6933,12 @@ EditorNode::EditorNode() { WARN_PRINT("Asset Library not available, as it requires SSL to work."); } - //add interface before adding plugins + // add interface before adding plugins editor_interface = memnew(EditorInterface); add_child(editor_interface); - //more visually meaningful to have this later + // more visually meaningful to have this later raise_bottom_panel_item(AnimationPlayerEditor::get_singleton()); add_editor_plugin(VersionControlEditorPlugin::get_singleton()); diff --git a/editor/editor_node.h b/editor/editor_node.h index 5c89823ad8..348db1cb7d 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -85,7 +85,7 @@ class ProjectSettingsEditor; class RunSettingsDialog; class ScriptCreateDialog; class TabContainer; -class Tabs; +class TabBar; class TextureProgressBar; class Button; class VSplitContainer; @@ -216,7 +216,7 @@ private: TOOL_MENU_BASE = 1000 }; - SubViewport *scene_root; //root of the scene being edited + SubViewport *scene_root; // root of the scene being edited PanelContainer *scene_root_parent; Control *theme_base; @@ -250,7 +250,7 @@ private: // Main tabs - Tabs *scene_tabs; + TabBar *scene_tabs; PopupMenu *scene_tabs_context_menu; Panel *tab_preview_panel; TextureRect *tab_preview; @@ -324,7 +324,7 @@ private: EditorSettingsDialog *settings_config_dialog; ProjectSettingsEditor *project_settings; - bool settings_changed = true; //make it update settings on first frame + bool settings_changed = true; // make it update settings on first frame void _update_from_settings(); PopupMenu *vcs_actions_menu; @@ -775,7 +775,7 @@ public: Node *get_edited_scene() { return editor_data.get_edited_scene_root(); } - SubViewport *get_scene_root() { return scene_root; } //root of the scene being edited + SubViewport *get_scene_root() { return scene_root; } // root of the scene being edited void fix_dependencies(const String &p_for_file); void clear_scene() { _cleanup_scene(); } diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 2d4db48f2a..19210e97b1 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -403,7 +403,7 @@ Ref create_editor_theme(const Ref p_theme) { float prev_icon_saturation = theme->has_color("icon_saturation", "Editor") ? theme->get_color("icon_saturation", "Editor").r : 1.0; - theme->set_color("icon_saturation", "Editor", Color(icon_saturation, icon_saturation, icon_saturation)); //can't save single float in theme, so using color + theme->set_color("icon_saturation", "Editor", Color(icon_saturation, icon_saturation, icon_saturation)); // can't save single float in theme, so using color theme->set_color("accent_color", "Editor", accent_color); theme->set_color("highlight_color", "Editor", highlight_color); theme->set_color("disabled_highlight_color", "Editor", disabled_highlight_color); @@ -549,7 +549,7 @@ Ref create_editor_theme(const Ref p_theme) { Ref style_empty = make_empty_stylebox(default_margin_size, default_margin_size, default_margin_size, default_margin_size); - // Tabs + // TabBar Ref style_tab_selected = style_widget->duplicate(); @@ -950,33 +950,33 @@ Ref create_editor_theme(const Ref p_theme) { theme->set_constant("icon_margin", "ItemList", 6 * EDSCALE); theme->set_constant("line_separation", "ItemList", 3 * EDSCALE); - // Tabs & TabContainer + // TabBar & TabContainer theme->set_stylebox("tab_selected", "TabContainer", style_tab_selected); theme->set_stylebox("tab_unselected", "TabContainer", style_tab_unselected); theme->set_stylebox("tab_disabled", "TabContainer", style_tab_disabled); - theme->set_stylebox("tab_selected", "Tabs", style_tab_selected); - theme->set_stylebox("tab_unselected", "Tabs", style_tab_unselected); - theme->set_stylebox("tab_disabled", "Tabs", style_tab_disabled); + theme->set_stylebox("tab_selected", "TabBar", style_tab_selected); + theme->set_stylebox("tab_unselected", "TabBar", style_tab_unselected); + theme->set_stylebox("tab_disabled", "TabBar", style_tab_disabled); theme->set_color("font_selected_color", "TabContainer", font_color); theme->set_color("font_unselected_color", "TabContainer", font_disabled_color); - theme->set_color("font_selected_color", "Tabs", font_color); - theme->set_color("font_unselected_color", "Tabs", font_disabled_color); + theme->set_color("font_selected_color", "TabBar", font_color); + theme->set_color("font_unselected_color", "TabBar", font_disabled_color); theme->set_icon("menu", "TabContainer", theme->get_icon("GuiTabMenu", "EditorIcons")); theme->set_icon("menu_highlight", "TabContainer", theme->get_icon("GuiTabMenuHl", "EditorIcons")); theme->set_stylebox("SceneTabFG", "EditorStyles", style_tab_selected); theme->set_stylebox("SceneTabBG", "EditorStyles", style_tab_unselected); - theme->set_icon("close", "Tabs", theme->get_icon("GuiClose", "EditorIcons")); - theme->set_stylebox("close_bg_pressed", "Tabs", style_menu); - theme->set_stylebox("close_bg_highlight", "Tabs", style_menu); + theme->set_icon("close", "TabBar", theme->get_icon("GuiClose", "EditorIcons")); + theme->set_stylebox("close_bg_pressed", "TabBar", style_menu); + theme->set_stylebox("close_bg_highlight", "TabBar", style_menu); theme->set_icon("increment", "TabContainer", theme->get_icon("GuiScrollArrowRight", "EditorIcons")); theme->set_icon("decrement", "TabContainer", theme->get_icon("GuiScrollArrowLeft", "EditorIcons")); - theme->set_icon("increment", "Tabs", theme->get_icon("GuiScrollArrowRight", "EditorIcons")); - theme->set_icon("decrement", "Tabs", theme->get_icon("GuiScrollArrowLeft", "EditorIcons")); - theme->set_icon("increment_highlight", "Tabs", theme->get_icon("GuiScrollArrowRightHl", "EditorIcons")); - theme->set_icon("decrement_highlight", "Tabs", theme->get_icon("GuiScrollArrowLeftHl", "EditorIcons")); + theme->set_icon("increment", "TabBar", theme->get_icon("GuiScrollArrowRight", "EditorIcons")); + theme->set_icon("decrement", "TabBar", theme->get_icon("GuiScrollArrowLeft", "EditorIcons")); + theme->set_icon("increment_highlight", "TabBar", theme->get_icon("GuiScrollArrowRightHl", "EditorIcons")); + theme->set_icon("decrement_highlight", "TabBar", theme->get_icon("GuiScrollArrowLeftHl", "EditorIcons")); theme->set_icon("increment_highlight", "TabContainer", theme->get_icon("GuiScrollArrowRightHl", "EditorIcons")); theme->set_icon("decrement_highlight", "TabContainer", theme->get_icon("GuiScrollArrowLeftHl", "EditorIcons")); - theme->set_constant("hseparation", "Tabs", 4 * EDSCALE); + theme->set_constant("hseparation", "TabBar", 4 * EDSCALE); // Content of each tab Ref style_content_panel = style_default->duplicate(); diff --git a/editor/icons/TabBar.svg b/editor/icons/TabBar.svg new file mode 100644 index 0000000000..e20a1a0131 --- /dev/null +++ b/editor/icons/TabBar.svg @@ -0,0 +1 @@ + diff --git a/editor/icons/Tabs.svg b/editor/icons/Tabs.svg deleted file mode 100644 index e20a1a0131..0000000000 --- a/editor/icons/Tabs.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index 19e1b40a0d..127219546d 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -3204,7 +3204,7 @@ void ThemeEditor::_add_preview_tab(ThemeEditorPreview *p_preview_tab, const Stri preview_tabs->add_tab(p_preview_name, p_icon); preview_tabs_content->add_child(p_preview_tab); - preview_tabs->set_tab_right_button(preview_tabs->get_tab_count() - 1, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("close"), SNAME("Tabs"))); + preview_tabs->set_tab_right_button(preview_tabs->get_tab_count() - 1, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("close"), SNAME("TabBar"))); p_preview_tab->connect("control_picked", callable_mp(this, &ThemeEditor::_preview_control_picked)); preview_tabs->set_current_tab(preview_tabs->get_tab_count() - 1); @@ -3328,8 +3328,8 @@ ThemeEditor::ThemeEditor() { preview_tabs_content->set_draw_behind_parent(true); preview_tabs_vb->add_child(preview_tabs_content); - preview_tabs = memnew(Tabs); - preview_tabs->set_tab_align(Tabs::ALIGN_LEFT); + preview_tabs = memnew(TabBar); + preview_tabs->set_tab_align(TabBar::ALIGN_LEFT); preview_tabs->set_h_size_flags(SIZE_EXPAND_FILL); preview_tabbar_hb->add_child(preview_tabs); preview_tabs->connect("tab_changed", callable_mp(this, &ThemeEditor::_change_preview_tab)); diff --git a/editor/plugins/theme_editor_plugin.h b/editor/plugins/theme_editor_plugin.h index 5b0357e3f8..60f9e09536 100644 --- a/editor/plugins/theme_editor_plugin.h +++ b/editor/plugins/theme_editor_plugin.h @@ -34,7 +34,7 @@ #include "scene/gui/margin_container.h" #include "scene/gui/option_button.h" #include "scene/gui/scroll_container.h" -#include "scene/gui/tabs.h" +#include "scene/gui/tab_bar.h" #include "scene/gui/texture_rect.h" #include "scene/resources/theme.h" #include "theme_editor_preview.h" @@ -391,7 +391,7 @@ class ThemeEditor : public VBoxContainer { Ref theme; - Tabs *preview_tabs; + TabBar *preview_tabs; PanelContainer *preview_tabs_content; Button *add_preview_button; EditorFileDialog *preview_scene_dialog; diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp index 5df4f40b55..85db274c32 100644 --- a/editor/plugins/tiles/tile_map_editor.cpp +++ b/editor/plugins/tiles/tile_map_editor.cpp @@ -4049,8 +4049,8 @@ TileMapEditor::TileMapEditor() { tile_map_editor_plugins.push_back(memnew(TileMapEditorTilesPlugin)); tile_map_editor_plugins.push_back(memnew(TileMapEditorTerrainsPlugin)); - // Tabs. - tabs_bar = memnew(Tabs); + // TabBar. + tabs_bar = memnew(TabBar); tabs_bar->set_clip_tabs(false); for (int plugin_index = 0; plugin_index < tile_map_editor_plugins.size(); plugin_index++) { Vector tabs_vector = tile_map_editor_plugins[plugin_index]->get_tabs(); diff --git a/editor/plugins/tiles/tile_map_editor.h b/editor/plugins/tiles/tile_map_editor.h index 54bac160a3..d33dcf54ec 100644 --- a/editor/plugins/tiles/tile_map_editor.h +++ b/editor/plugins/tiles/tile_map_editor.h @@ -38,7 +38,7 @@ #include "editor/editor_node.h" #include "scene/2d/tile_map.h" #include "scene/gui/box_container.h" -#include "scene/gui/tabs.h" +#include "scene/gui/tab_bar.h" class TileMapEditorPlugin : public Object { public: @@ -351,7 +351,7 @@ private: // Bottom panel. Label *missing_tileset_label; - Tabs *tabs_bar; + TabBar *tabs_bar; LocalVector tabs_data; LocalVector tabs_plugins; void _update_bottom_panel(); diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp index 44f5ba29de..b1c8f39b67 100644 --- a/editor/plugins/tiles/tile_set_editor.cpp +++ b/editor/plugins/tiles/tile_set_editor.cpp @@ -642,8 +642,8 @@ TileSetEditor::TileSetEditor() { set_process_internal(true); - // Tabs. - tabs_bar = memnew(Tabs); + // TabBar. + tabs_bar = memnew(TabBar); tabs_bar->set_clip_tabs(false); tabs_bar->add_tab(TTR("Tiles")); tabs_bar->add_tab(TTR("Patterns")); diff --git a/editor/plugins/tiles/tile_set_editor.h b/editor/plugins/tiles/tile_set_editor.h index 1f26560588..bdfc755637 100644 --- a/editor/plugins/tiles/tile_set_editor.h +++ b/editor/plugins/tiles/tile_set_editor.h @@ -48,9 +48,9 @@ private: bool tile_set_changed_needs_update = false; HSplitContainer *split_container; - // Tabs. + // TabBar. HBoxContainer *tile_set_toolbar; - Tabs *tabs_bar; + TabBar *tabs_bar; // Tiles. Label *no_source_selected_label; diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp new file mode 100644 index 0000000000..7c7e3b0547 --- /dev/null +++ b/scene/gui/tab_bar.cpp @@ -0,0 +1,1195 @@ +/*************************************************************************/ +/* tab_bar.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 "tab_bar.h" + +#include "core/object/message_queue.h" +#include "core/string/translation.h" + +#include "scene/gui/box_container.h" +#include "scene/gui/label.h" +#include "scene/gui/texture_rect.h" + +Size2 TabBar::get_minimum_size() const { + Ref tab_unselected = get_theme_stylebox(SNAME("tab_unselected")); + Ref tab_selected = get_theme_stylebox(SNAME("tab_selected")); + Ref tab_disabled = get_theme_stylebox(SNAME("tab_disabled")); + + int y_margin = MAX(MAX(tab_unselected->get_minimum_size().height, tab_selected->get_minimum_size().height), tab_disabled->get_minimum_size().height); + + Size2 ms(0, 0); + + for (int i = 0; i < tabs.size(); i++) { + Ref tex = tabs[i].icon; + if (tex.is_valid()) { + ms.height = MAX(ms.height, tex->get_size().height); + if (tabs[i].text != "") { + ms.width += get_theme_constant(SNAME("hseparation")); + } + } + + ms.width += Math::ceil(tabs[i].text_buf->get_size().x); + ms.height = MAX(ms.height, tabs[i].text_buf->get_size().y + y_margin); + + if (tabs[i].disabled) { + ms.width += tab_disabled->get_minimum_size().width; + } else if (current == i) { + ms.width += tab_selected->get_minimum_size().width; + } else { + ms.width += tab_unselected->get_minimum_size().width; + } + + if (tabs[i].right_button.is_valid()) { + Ref rb = tabs[i].right_button; + Size2 bms = rb->get_size(); + bms.width += get_theme_constant(SNAME("hseparation")); + ms.width += bms.width; + ms.height = MAX(bms.height + tab_unselected->get_minimum_size().height, ms.height); + } + + if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current)) { + Ref cb = get_theme_icon(SNAME("close")); + Size2 bms = cb->get_size(); + bms.width += get_theme_constant(SNAME("hseparation")); + ms.width += bms.width; + ms.height = MAX(bms.height + tab_unselected->get_minimum_size().height, ms.height); + } + } + + if (clip_tabs) { + ms.width = 0; + } + + return ms; +} + +void TabBar::gui_input(const Ref &p_event) { + ERR_FAIL_COND(p_event.is_null()); + + Ref mm = p_event; + + if (mm.is_valid()) { + Point2 pos = mm->get_position(); + + if (buttons_visible) { + Ref incr = get_theme_icon(SNAME("increment")); + Ref decr = get_theme_icon(SNAME("decrement")); + + if (is_layout_rtl()) { + if (pos.x < decr->get_width()) { + if (highlight_arrow != 1) { + highlight_arrow = 1; + update(); + } + } else if (pos.x < incr->get_width() + decr->get_width()) { + if (highlight_arrow != 0) { + highlight_arrow = 0; + update(); + } + } else if (highlight_arrow != -1) { + highlight_arrow = -1; + update(); + } + } else { + int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width(); + if (pos.x > limit_minus_buttons + decr->get_width()) { + if (highlight_arrow != 1) { + highlight_arrow = 1; + update(); + } + } else if (pos.x > limit_minus_buttons) { + if (highlight_arrow != 0) { + highlight_arrow = 0; + update(); + } + } else if (highlight_arrow != -1) { + highlight_arrow = -1; + update(); + } + } + } + + _update_hover(); + return; + } + + Ref mb = p_event; + + if (mb.is_valid()) { + if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP && !mb->is_command_pressed()) { + if (scrolling_enabled && buttons_visible) { + if (offset > 0) { + offset--; + update(); + } + } + } + + if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && !mb->is_command_pressed()) { + if (scrolling_enabled && buttons_visible) { + if (missing_right) { + offset++; + _ensure_no_over_offset(); // Avoid overreaching when scrolling fast. + update(); + } + } + } + + if (rb_pressing && !mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) { + if (rb_hover != -1) { + // pressed + emit_signal(SNAME("tab_rmb_clicked"), rb_hover); + } + + rb_pressing = false; + update(); + } + + if (cb_pressing && !mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) { + if (cb_hover != -1) { + // pressed + emit_signal(SNAME("tab_closed"), cb_hover); + } + + cb_pressing = false; + update(); + } + + if (mb->is_pressed() && (mb->get_button_index() == MOUSE_BUTTON_LEFT || (select_with_rmb && mb->get_button_index() == MOUSE_BUTTON_RIGHT))) { + // clicks + Point2 pos = mb->get_position(); + + if (buttons_visible) { + Ref incr = get_theme_icon(SNAME("increment")); + Ref decr = get_theme_icon(SNAME("decrement")); + + if (is_layout_rtl()) { + if (pos.x < decr->get_width()) { + if (missing_right) { + offset++; + update(); + } + return; + } else if (pos.x < incr->get_width() + decr->get_width()) { + if (offset > 0) { + offset--; + update(); + } + return; + } + } else { + int limit = get_size().width - incr->get_width() - decr->get_width(); + if (pos.x > limit + decr->get_width()) { + if (missing_right) { + offset++; + update(); + } + return; + } else if (pos.x > limit) { + if (offset > 0) { + offset--; + update(); + } + return; + } + } + } + + int found = -1; + for (int i = offset; i <= max_drawn_tab; i++) { + if (tabs[i].rb_rect.has_point(pos)) { + rb_pressing = true; + update(); + return; + } + + if (tabs[i].cb_rect.has_point(pos)) { + cb_pressing = true; + update(); + return; + } + + if (pos.x >= get_tab_rect(i).position.x && pos.x < get_tab_rect(i).position.x + tabs[i].size_cache) { + if (!tabs[i].disabled) { + found = i; + } + break; + } + } + + if (found != -1) { + set_current_tab(found); + emit_signal(SNAME("tab_clicked"), found); + } + } + } +} + +void TabBar::_shape(int p_tab) { + Ref font = get_theme_font(SNAME("font")); + int font_size = get_theme_font_size(SNAME("font_size")); + + tabs.write[p_tab].xl_text = atr(tabs[p_tab].text); + tabs.write[p_tab].text_buf->clear(); + tabs.write[p_tab].text_buf->set_width(-1); + if (tabs[p_tab].text_direction == Control::TEXT_DIRECTION_INHERITED) { + tabs.write[p_tab].text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); + } else { + tabs.write[p_tab].text_buf->set_direction((TextServer::Direction)tabs[p_tab].text_direction); + } + + tabs.write[p_tab].text_buf->add_string(tabs.write[p_tab].xl_text, font, font_size, tabs[p_tab].opentype_features, (tabs[p_tab].language != "") ? tabs[p_tab].language : TranslationServer::get_singleton()->get_tool_locale()); +} + +void TabBar::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { + _update_cache(); + update(); + } break; + case NOTIFICATION_THEME_CHANGED: + case NOTIFICATION_TRANSLATION_CHANGED: { + for (int i = 0; i < tabs.size(); ++i) { + _shape(i); + } + _update_cache(); + minimum_size_changed(); + update(); + } break; + case NOTIFICATION_RESIZED: { + _update_cache(); + _ensure_no_over_offset(); + ensure_tab_visible(current); + } break; + case NOTIFICATION_DRAW: { + _update_cache(); + RID ci = get_canvas_item(); + + Ref tab_unselected = get_theme_stylebox(SNAME("tab_unselected")); + Ref tab_selected = get_theme_stylebox(SNAME("tab_selected")); + Ref tab_disabled = get_theme_stylebox(SNAME("tab_disabled")); + Color font_selected_color = get_theme_color(SNAME("font_selected_color")); + Color font_unselected_color = get_theme_color(SNAME("font_unselected_color")); + Color font_disabled_color = get_theme_color(SNAME("font_disabled_color")); + Ref close = get_theme_icon(SNAME("close")); + Color font_outline_color = get_theme_color(SNAME("font_outline_color")); + int outline_size = get_theme_constant(SNAME("outline_size")); + + Vector2 size = get_size(); + bool rtl = is_layout_rtl(); + + int h = get_size().height; + int w = 0; + int mw = 0; + + for (int i = 0; i < tabs.size(); i++) { + tabs.write[i].ofs_cache = mw; + mw += get_tab_width(i); + } + + if (tab_align == ALIGN_CENTER) { + w = (get_size().width - mw) / 2; + } else if (tab_align == ALIGN_RIGHT) { + w = get_size().width - mw; + } + + if (w < 0) { + w = 0; + } + + Ref incr = get_theme_icon(SNAME("increment")); + Ref decr = get_theme_icon(SNAME("decrement")); + Ref incr_hl = get_theme_icon(SNAME("increment_highlight")); + Ref decr_hl = get_theme_icon(SNAME("decrement_highlight")); + + int limit = get_size().width; + int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width(); + + missing_right = false; + + for (int i = offset; i < tabs.size(); i++) { + tabs.write[i].ofs_cache = w; + + int lsize = tabs[i].size_cache; + + Ref sb; + Color col; + + if (tabs[i].disabled) { + sb = tab_disabled; + col = font_disabled_color; + } else if (i == current) { + sb = tab_selected; + col = font_selected_color; + } else { + sb = tab_unselected; + col = font_unselected_color; + } + + int new_width = w + lsize; + if (new_width > limit || (i < tabs.size() - 1 && new_width > limit_minus_buttons)) { // For the last tab, we accept if the tab covers the buttons. + max_drawn_tab = i - 1; + missing_right = true; + break; + } else { + max_drawn_tab = i; + } + + Rect2 sb_rect; + if (rtl) { + sb_rect = Rect2(size.width - w - tabs[i].size_cache, 0, tabs[i].size_cache, h); + } else { + sb_rect = Rect2(w, 0, tabs[i].size_cache, h); + } + sb->draw(ci, sb_rect); + + w += sb->get_margin(SIDE_LEFT); + + Size2i sb_ms = sb->get_minimum_size(); + Ref icon = tabs[i].icon; + if (icon.is_valid()) { + if (rtl) { + icon->draw(ci, Point2i(size.width - w - icon->get_width(), sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - icon->get_height()) / 2)); + } else { + icon->draw(ci, Point2i(w, sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - icon->get_height()) / 2)); + } + if (tabs[i].text != "") { + w += icon->get_width() + get_theme_constant(SNAME("hseparation")); + } + } + + if (rtl) { + Vector2 text_pos = Point2i(size.width - w - tabs[i].text_buf->get_size().x, sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - tabs[i].text_buf->get_size().y) / 2); + if (outline_size > 0 && font_outline_color.a > 0) { + tabs[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color); + } + tabs[i].text_buf->draw(ci, text_pos, col); + } else { + Vector2 text_pos = Point2i(w, sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - tabs[i].text_buf->get_size().y) / 2); + if (outline_size > 0 && font_outline_color.a > 0) { + tabs[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color); + } + tabs[i].text_buf->draw(ci, text_pos, col); + } + + w += tabs[i].size_text; + + if (tabs[i].right_button.is_valid()) { + Ref style = get_theme_stylebox(SNAME("close_bg_highlight")); + Ref rb = tabs[i].right_button; + + w += get_theme_constant(SNAME("hseparation")); + + Rect2 rb_rect; + rb_rect.size = style->get_minimum_size() + rb->get_size(); + if (rtl) { + rb_rect.position.x = size.width - w - rb_rect.size.x; + } else { + rb_rect.position.x = w; + } + rb_rect.position.y = sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - (rb_rect.size.y)) / 2; + + if (rb_hover == i) { + if (rb_pressing) { + get_theme_stylebox(SNAME("button_pressed"))->draw(ci, rb_rect); + } else { + style->draw(ci, rb_rect); + } + } + + if (rtl) { + rb->draw(ci, Point2i(size.width - w - rb_rect.size.x + style->get_margin(SIDE_LEFT), rb_rect.position.y + style->get_margin(SIDE_TOP))); + } else { + rb->draw(ci, Point2i(w + style->get_margin(SIDE_LEFT), rb_rect.position.y + style->get_margin(SIDE_TOP))); + } + w += rb->get_width(); + tabs.write[i].rb_rect = rb_rect; + } + + if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current)) { + Ref style = get_theme_stylebox(SNAME("close_bg_highlight")); + Ref cb = close; + + w += get_theme_constant(SNAME("hseparation")); + + Rect2 cb_rect; + cb_rect.size = style->get_minimum_size() + cb->get_size(); + if (rtl) { + cb_rect.position.x = size.width - w - cb_rect.size.x; + } else { + cb_rect.position.x = w; + } + cb_rect.position.y = sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - (cb_rect.size.y)) / 2; + + if (!tabs[i].disabled && cb_hover == i) { + if (cb_pressing) { + get_theme_stylebox(SNAME("close_bg_pressed"))->draw(ci, cb_rect); + } else { + style->draw(ci, cb_rect); + } + } + + if (rtl) { + cb->draw(ci, Point2i(size.width - w - cb_rect.size.x + style->get_margin(SIDE_LEFT), cb_rect.position.y + style->get_margin(SIDE_TOP))); + } else { + cb->draw(ci, Point2i(w + style->get_margin(SIDE_LEFT), cb_rect.position.y + style->get_margin(SIDE_TOP))); + } + w += cb->get_width(); + tabs.write[i].cb_rect = cb_rect; + } + + w += sb->get_margin(SIDE_RIGHT); + } + + if (offset > 0 || missing_right) { + int vofs = (get_size().height - incr->get_size().height) / 2; + + if (rtl) { + if (missing_right) { + draw_texture(highlight_arrow == 1 ? decr_hl : decr, Point2(0, vofs)); + } else { + draw_texture(decr, Point2(0, vofs), Color(1, 1, 1, 0.5)); + } + + if (offset > 0) { + draw_texture(highlight_arrow == 0 ? incr_hl : incr, Point2(incr->get_size().width, vofs)); + } else { + draw_texture(incr, Point2(incr->get_size().width, vofs), Color(1, 1, 1, 0.5)); + } + } else { + if (offset > 0) { + draw_texture(highlight_arrow == 0 ? decr_hl : decr, Point2(limit_minus_buttons, vofs)); + } else { + draw_texture(decr, Point2(limit_minus_buttons, vofs), Color(1, 1, 1, 0.5)); + } + + if (missing_right) { + draw_texture(highlight_arrow == 1 ? incr_hl : incr, Point2(limit_minus_buttons + decr->get_size().width, vofs)); + } else { + draw_texture(incr, Point2(limit_minus_buttons + decr->get_size().width, vofs), Color(1, 1, 1, 0.5)); + } + } + + buttons_visible = true; + } else { + buttons_visible = false; + } + } break; + } +} + +int TabBar::get_tab_count() const { + return tabs.size(); +} + +void TabBar::set_current_tab(int p_current) { + if (current == p_current) { + return; + } + ERR_FAIL_INDEX(p_current, get_tab_count()); + + previous = current; + current = p_current; + + _update_cache(); + update(); + + emit_signal(SNAME("tab_changed"), p_current); +} + +int TabBar::get_current_tab() const { + return current; +} + +int TabBar::get_previous_tab() const { + return previous; +} + +int TabBar::get_hovered_tab() const { + return hover; +} + +int TabBar::get_tab_offset() const { + return offset; +} + +bool TabBar::get_offset_buttons_visible() const { + return buttons_visible; +} + +void TabBar::set_tab_title(int p_tab, const String &p_title) { + ERR_FAIL_INDEX(p_tab, tabs.size()); + tabs.write[p_tab].text = p_title; + _shape(p_tab); + update(); + minimum_size_changed(); +} + +String TabBar::get_tab_title(int p_tab) const { + ERR_FAIL_INDEX_V(p_tab, tabs.size(), ""); + return tabs[p_tab].text; +} + +void TabBar::set_tab_text_direction(int p_tab, Control::TextDirection p_text_direction) { + ERR_FAIL_INDEX(p_tab, tabs.size()); + ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); + if (tabs[p_tab].text_direction != p_text_direction) { + tabs.write[p_tab].text_direction = p_text_direction; + _shape(p_tab); + update(); + } +} + +Control::TextDirection TabBar::get_tab_text_direction(int p_tab) const { + ERR_FAIL_INDEX_V(p_tab, tabs.size(), Control::TEXT_DIRECTION_INHERITED); + return tabs[p_tab].text_direction; +} + +void TabBar::clear_tab_opentype_features(int p_tab) { + ERR_FAIL_INDEX(p_tab, tabs.size()); + tabs.write[p_tab].opentype_features.clear(); + _shape(p_tab); + update(); +} + +void TabBar::set_tab_opentype_feature(int p_tab, const String &p_name, int p_value) { + ERR_FAIL_INDEX(p_tab, tabs.size()); + int32_t tag = TS->name_to_tag(p_name); + if (!tabs[p_tab].opentype_features.has(tag) || (int)tabs[p_tab].opentype_features[tag] != p_value) { + tabs.write[p_tab].opentype_features[tag] = p_value; + _shape(p_tab); + update(); + } +} + +int TabBar::get_tab_opentype_feature(int p_tab, const String &p_name) const { + ERR_FAIL_INDEX_V(p_tab, tabs.size(), -1); + int32_t tag = TS->name_to_tag(p_name); + if (!tabs[p_tab].opentype_features.has(tag)) { + return -1; + } + return tabs[p_tab].opentype_features[tag]; +} + +void TabBar::set_tab_language(int p_tab, const String &p_language) { + ERR_FAIL_INDEX(p_tab, tabs.size()); + if (tabs[p_tab].language != p_language) { + tabs.write[p_tab].language = p_language; + _shape(p_tab); + update(); + } +} + +String TabBar::get_tab_language(int p_tab) const { + ERR_FAIL_INDEX_V(p_tab, tabs.size(), ""); + return tabs[p_tab].language; +} + +void TabBar::set_tab_icon(int p_tab, const Ref &p_icon) { + ERR_FAIL_INDEX(p_tab, tabs.size()); + tabs.write[p_tab].icon = p_icon; + update(); + minimum_size_changed(); +} + +Ref TabBar::get_tab_icon(int p_tab) const { + ERR_FAIL_INDEX_V(p_tab, tabs.size(), Ref()); + return tabs[p_tab].icon; +} + +void TabBar::set_tab_disabled(int p_tab, bool p_disabled) { + ERR_FAIL_INDEX(p_tab, tabs.size()); + tabs.write[p_tab].disabled = p_disabled; + update(); +} + +bool TabBar::get_tab_disabled(int p_tab) const { + ERR_FAIL_INDEX_V(p_tab, tabs.size(), false); + return tabs[p_tab].disabled; +} + +void TabBar::set_tab_right_button(int p_tab, const Ref &p_right_button) { + ERR_FAIL_INDEX(p_tab, tabs.size()); + tabs.write[p_tab].right_button = p_right_button; + _update_cache(); + update(); + minimum_size_changed(); +} + +Ref TabBar::get_tab_right_button(int p_tab) const { + ERR_FAIL_INDEX_V(p_tab, tabs.size(), Ref()); + return tabs[p_tab].right_button; +} + +void TabBar::_update_hover() { + if (!is_inside_tree()) { + return; + } + + const Point2 &pos = get_local_mouse_position(); + // test hovering to display right or close button + int hover_now = -1; + int hover_buttons = -1; + for (int i = offset; i < tabs.size(); i++) { + Rect2 rect = get_tab_rect(i); + if (rect.has_point(pos)) { + hover_now = i; + } + if (tabs[i].rb_rect.has_point(pos)) { + rb_hover = i; + cb_hover = -1; + hover_buttons = i; + break; + } else if (!tabs[i].disabled && tabs[i].cb_rect.has_point(pos)) { + cb_hover = i; + rb_hover = -1; + hover_buttons = i; + break; + } + } + if (hover != hover_now) { + hover = hover_now; + emit_signal(SNAME("tab_hovered"), hover); + } + + if (hover_buttons == -1) { // no hover + rb_hover = hover_buttons; + cb_hover = hover_buttons; + } +} + +void TabBar::_update_cache() { + Ref tab_disabled = get_theme_stylebox(SNAME("tab_disabled")); + Ref tab_unselected = get_theme_stylebox(SNAME("tab_unselected")); + Ref tab_selected = get_theme_stylebox(SNAME("tab_selected")); + Ref incr = get_theme_icon(SNAME("increment")); + Ref decr = get_theme_icon(SNAME("decrement")); + int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width(); + + int w = 0; + int mw = 0; + int size_fixed = 0; + int count_resize = 0; + for (int i = 0; i < tabs.size(); i++) { + tabs.write[i].ofs_cache = mw; + tabs.write[i].size_cache = get_tab_width(i); + tabs.write[i].size_text = Math::ceil(tabs[i].text_buf->get_size().x); + tabs.write[i].text_buf->set_width(-1); + mw += tabs[i].size_cache; + if (tabs[i].size_cache <= min_width || i == current) { + size_fixed += tabs[i].size_cache; + } else { + count_resize++; + } + } + int m_width = min_width; + if (count_resize > 0) { + m_width = MAX((limit_minus_buttons - size_fixed) / count_resize, min_width); + } + for (int i = offset; i < tabs.size(); i++) { + Ref sb; + if (tabs[i].disabled) { + sb = tab_disabled; + } else if (i == current) { + sb = tab_selected; + } else { + sb = tab_unselected; + } + int lsize = tabs[i].size_cache; + int slen = tabs[i].size_text; + if (min_width > 0 && mw > limit_minus_buttons && i != current) { + if (lsize > m_width) { + slen = m_width - (sb->get_margin(SIDE_LEFT) + sb->get_margin(SIDE_RIGHT)); + if (tabs[i].icon.is_valid()) { + slen -= tabs[i].icon->get_width(); + slen -= get_theme_constant(SNAME("hseparation")); + } + if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current)) { + Ref cb = get_theme_icon(SNAME("close")); + slen -= cb->get_width(); + slen -= get_theme_constant(SNAME("hseparation")); + } + slen = MAX(slen, 1); + lsize = m_width; + } + } + tabs.write[i].ofs_cache = w; + tabs.write[i].size_cache = lsize; + tabs.write[i].size_text = slen; + tabs.write[i].text_buf->set_width(slen); + w += lsize; + } +} + +void TabBar::_on_mouse_exited() { + rb_hover = -1; + cb_hover = -1; + hover = -1; + highlight_arrow = -1; + update(); +} + +void TabBar::add_tab(const String &p_str, const Ref &p_icon) { + Tab t; + t.text = p_str; + t.xl_text = atr(p_str); + t.text_buf.instantiate(); + t.text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); + t.text_buf->add_string(t.xl_text, get_theme_font(SNAME("font")), get_theme_font_size(SNAME("font_size")), Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); + t.icon = p_icon; + t.disabled = false; + t.ofs_cache = 0; + t.size_cache = 0; + + tabs.push_back(t); + _update_cache(); + call_deferred(SNAME("_update_hover")); + update(); + minimum_size_changed(); +} + +void TabBar::clear_tabs() { + tabs.clear(); + current = 0; + previous = 0; + call_deferred(SNAME("_update_hover")); + update(); +} + +void TabBar::remove_tab(int p_idx) { + ERR_FAIL_INDEX(p_idx, tabs.size()); + tabs.remove(p_idx); + if (current >= p_idx) { + current--; + } + _update_cache(); + call_deferred(SNAME("_update_hover")); + update(); + minimum_size_changed(); + + if (current < 0) { + current = 0; + previous = 0; + } + if (current >= tabs.size()) { + current = tabs.size() - 1; + } + + _ensure_no_over_offset(); +} + +Variant TabBar::get_drag_data(const Point2 &p_point) { + if (!drag_to_rearrange_enabled) { + return Variant(); + } + + int tab_over = get_tab_idx_at_point(p_point); + + if (tab_over < 0) { + return Variant(); + } + + HBoxContainer *drag_preview = memnew(HBoxContainer); + + if (!tabs[tab_over].icon.is_null()) { + TextureRect *tf = memnew(TextureRect); + tf->set_texture(tabs[tab_over].icon); + drag_preview->add_child(tf); + } + Label *label = memnew(Label(tabs[tab_over].xl_text)); + drag_preview->add_child(label); + if (!tabs[tab_over].right_button.is_null()) { + TextureRect *tf = memnew(TextureRect); + tf->set_texture(tabs[tab_over].right_button); + drag_preview->add_child(tf); + } + set_drag_preview(drag_preview); + + Dictionary drag_data; + drag_data["type"] = "tab_element"; + drag_data["tab_element"] = tab_over; + drag_data["from_path"] = get_path(); + return drag_data; +} + +bool TabBar::can_drop_data(const Point2 &p_point, const Variant &p_data) const { + if (!drag_to_rearrange_enabled) { + return false; + } + + Dictionary d = p_data; + if (!d.has("type")) { + return false; + } + + if (String(d["type"]) == "tab_element") { + NodePath from_path = d["from_path"]; + NodePath to_path = get_path(); + if (from_path == to_path) { + return true; + } else if (get_tabs_rearrange_group() != -1) { + // drag and drop between other TabBars + Node *from_node = get_node(from_path); + TabBar *from_tabs = Object::cast_to(from_node); + if (from_tabs && from_tabs->get_tabs_rearrange_group() == get_tabs_rearrange_group()) { + return true; + } + } + } + return false; +} + +void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) { + if (!drag_to_rearrange_enabled) { + return; + } + + int hover_now = get_tab_idx_at_point(p_point); + + Dictionary d = p_data; + if (!d.has("type")) { + return; + } + + if (String(d["type"]) == "tab_element") { + int tab_from_id = d["tab_element"]; + NodePath from_path = d["from_path"]; + NodePath to_path = get_path(); + if (from_path == to_path) { + if (hover_now < 0) { + hover_now = get_tab_count() - 1; + } + move_tab(tab_from_id, hover_now); + emit_signal(SNAME("active_tab_rearranged"), hover_now); + set_current_tab(hover_now); + } else if (get_tabs_rearrange_group() != -1) { + // drag and drop between Tabs + Node *from_node = get_node(from_path); + TabBar *from_tabs = Object::cast_to(from_node); + if (from_tabs && from_tabs->get_tabs_rearrange_group() == get_tabs_rearrange_group()) { + if (tab_from_id >= from_tabs->get_tab_count()) { + return; + } + Tab moving_tab = from_tabs->tabs[tab_from_id]; + if (hover_now < 0) { + hover_now = get_tab_count(); + } + tabs.insert(hover_now, moving_tab); + from_tabs->remove_tab(tab_from_id); + set_current_tab(hover_now); + emit_signal(SNAME("tab_changed"), hover_now); + _update_cache(); + } + } + } + update(); +} + +int TabBar::get_tab_idx_at_point(const Point2 &p_point) const { + int hover_now = -1; + for (int i = offset; i <= max_drawn_tab; i++) { + Rect2 rect = get_tab_rect(i); + if (rect.has_point(p_point)) { + hover_now = i; + } + } + + return hover_now; +} + +void TabBar::set_tab_align(TabAlign p_align) { + ERR_FAIL_INDEX(p_align, ALIGN_MAX); + tab_align = p_align; + update(); +} + +TabBar::TabAlign TabBar::get_tab_align() const { + return tab_align; +} + +void TabBar::set_clip_tabs(bool p_clip_tabs) { + if (clip_tabs == p_clip_tabs) { + return; + } + clip_tabs = p_clip_tabs; + update(); + minimum_size_changed(); +} + +bool TabBar::get_clip_tabs() const { + return clip_tabs; +} + +void TabBar::move_tab(int from, int to) { + if (from == to) { + return; + } + + ERR_FAIL_INDEX(from, tabs.size()); + ERR_FAIL_INDEX(to, tabs.size()); + + Tab tab_from = tabs[from]; + tabs.remove(from); + tabs.insert(to, tab_from); + + _update_cache(); + update(); +} + +int TabBar::get_tab_width(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, tabs.size(), 0); + + Ref tab_unselected = get_theme_stylebox(SNAME("tab_unselected")); + Ref tab_selected = get_theme_stylebox(SNAME("tab_selected")); + Ref tab_disabled = get_theme_stylebox(SNAME("tab_disabled")); + + int x = 0; + + Ref tex = tabs[p_idx].icon; + if (tex.is_valid()) { + x += tex->get_width(); + if (tabs[p_idx].text != "") { + x += get_theme_constant(SNAME("hseparation")); + } + } + + x += Math::ceil(tabs[p_idx].text_buf->get_size().x); + + if (tabs[p_idx].disabled) { + x += tab_disabled->get_minimum_size().width; + } else if (current == p_idx) { + x += tab_selected->get_minimum_size().width; + } else { + x += tab_unselected->get_minimum_size().width; + } + + if (tabs[p_idx].right_button.is_valid()) { + Ref rb = tabs[p_idx].right_button; + x += rb->get_width(); + x += get_theme_constant(SNAME("hseparation")); + } + + if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && p_idx == current)) { + Ref cb = get_theme_icon(SNAME("close")); + x += cb->get_width(); + x += get_theme_constant(SNAME("hseparation")); + } + + return x; +} + +void TabBar::_ensure_no_over_offset() { + if (!is_inside_tree()) { + return; + } + + Ref incr = get_theme_icon(SNAME("increment")); + Ref decr = get_theme_icon(SNAME("decrement")); + + int limit = get_size().width; + int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width(); + + while (offset > 0) { + int total_w = 0; + for (int i = offset - 1; i < tabs.size(); i++) { + total_w += tabs[i].size_cache; + } + + if ((buttons_visible && total_w < limit_minus_buttons) || total_w < limit) { // For the last tab, we accept if the tab covers the buttons. + offset--; + update(); + } else { + break; + } + } +} + +void TabBar::ensure_tab_visible(int p_idx) { + if (!is_inside_tree()) { + return; + } + + if (tabs.size() == 0) { + return; + } + ERR_FAIL_INDEX(p_idx, tabs.size()); + + if (p_idx == offset) { + return; + } + if (p_idx < offset) { + offset = p_idx; + update(); + return; + } + + int prev_offset = offset; + Ref incr = get_theme_icon(SNAME("increment")); + Ref decr = get_theme_icon(SNAME("decrement")); + int limit = get_size().width; + int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width(); + + for (int i = offset; i <= p_idx; i++) { + int total_w = tabs[i].ofs_cache + tabs[i].size_cache; + if (total_w > limit || (buttons_visible && total_w > limit_minus_buttons)) { + offset++; + } + } + + if (prev_offset != offset) { + update(); + } +} + +Rect2 TabBar::get_tab_rect(int p_tab) const { + ERR_FAIL_INDEX_V(p_tab, tabs.size(), Rect2()); + if (is_layout_rtl()) { + return Rect2(get_size().width - tabs[p_tab].ofs_cache - tabs[p_tab].size_cache, 0, tabs[p_tab].size_cache, get_size().height); + } else { + return Rect2(tabs[p_tab].ofs_cache, 0, tabs[p_tab].size_cache, get_size().height); + } +} + +void TabBar::set_tab_close_display_policy(CloseButtonDisplayPolicy p_policy) { + ERR_FAIL_INDEX(p_policy, CLOSE_BUTTON_MAX); + cb_displaypolicy = p_policy; + update(); +} + +TabBar::CloseButtonDisplayPolicy TabBar::get_tab_close_display_policy() const { + return cb_displaypolicy; +} + +void TabBar::set_min_width(int p_width) { + min_width = p_width; +} + +void TabBar::set_scrolling_enabled(bool p_enabled) { + scrolling_enabled = p_enabled; +} + +bool TabBar::get_scrolling_enabled() const { + return scrolling_enabled; +} + +void TabBar::set_drag_to_rearrange_enabled(bool p_enabled) { + drag_to_rearrange_enabled = p_enabled; +} + +bool TabBar::get_drag_to_rearrange_enabled() const { + return drag_to_rearrange_enabled; +} + +void TabBar::set_tabs_rearrange_group(int p_group_id) { + tabs_rearrange_group = p_group_id; +} + +int TabBar::get_tabs_rearrange_group() const { + return tabs_rearrange_group; +} + +void TabBar::set_select_with_rmb(bool p_enabled) { + select_with_rmb = p_enabled; +} + +bool TabBar::get_select_with_rmb() const { + return select_with_rmb; +} + +void TabBar::_bind_methods() { + ClassDB::bind_method(D_METHOD("_update_hover"), &TabBar::_update_hover); + ClassDB::bind_method(D_METHOD("get_tab_count"), &TabBar::get_tab_count); + ClassDB::bind_method(D_METHOD("set_current_tab", "tab_idx"), &TabBar::set_current_tab); + ClassDB::bind_method(D_METHOD("get_current_tab"), &TabBar::get_current_tab); + ClassDB::bind_method(D_METHOD("get_previous_tab"), &TabBar::get_previous_tab); + ClassDB::bind_method(D_METHOD("set_tab_title", "tab_idx", "title"), &TabBar::set_tab_title); + ClassDB::bind_method(D_METHOD("get_tab_title", "tab_idx"), &TabBar::get_tab_title); + ClassDB::bind_method(D_METHOD("set_tab_text_direction", "tab_idx", "direction"), &TabBar::set_tab_text_direction); + ClassDB::bind_method(D_METHOD("get_tab_text_direction", "tab_idx"), &TabBar::get_tab_text_direction); + ClassDB::bind_method(D_METHOD("set_tab_opentype_feature", "tab_idx", "tag", "values"), &TabBar::set_tab_opentype_feature); + ClassDB::bind_method(D_METHOD("get_tab_opentype_feature", "tab_idx", "tag"), &TabBar::get_tab_opentype_feature); + ClassDB::bind_method(D_METHOD("clear_tab_opentype_features", "tab_idx"), &TabBar::clear_tab_opentype_features); + ClassDB::bind_method(D_METHOD("set_tab_language", "tab_idx", "language"), &TabBar::set_tab_language); + ClassDB::bind_method(D_METHOD("get_tab_language", "tab_idx"), &TabBar::get_tab_language); + ClassDB::bind_method(D_METHOD("set_tab_icon", "tab_idx", "icon"), &TabBar::set_tab_icon); + ClassDB::bind_method(D_METHOD("get_tab_icon", "tab_idx"), &TabBar::get_tab_icon); + ClassDB::bind_method(D_METHOD("set_tab_disabled", "tab_idx", "disabled"), &TabBar::set_tab_disabled); + ClassDB::bind_method(D_METHOD("get_tab_disabled", "tab_idx"), &TabBar::get_tab_disabled); + ClassDB::bind_method(D_METHOD("remove_tab", "tab_idx"), &TabBar::remove_tab); + ClassDB::bind_method(D_METHOD("add_tab", "title", "icon"), &TabBar::add_tab, DEFVAL(""), DEFVAL(Ref())); + ClassDB::bind_method(D_METHOD("set_tab_align", "align"), &TabBar::set_tab_align); + ClassDB::bind_method(D_METHOD("get_tab_align"), &TabBar::get_tab_align); + ClassDB::bind_method(D_METHOD("set_clip_tabs", "clip_tabs"), &TabBar::set_clip_tabs); + ClassDB::bind_method(D_METHOD("get_clip_tabs"), &TabBar::get_clip_tabs); + ClassDB::bind_method(D_METHOD("get_tab_offset"), &TabBar::get_tab_offset); + ClassDB::bind_method(D_METHOD("get_offset_buttons_visible"), &TabBar::get_offset_buttons_visible); + ClassDB::bind_method(D_METHOD("ensure_tab_visible", "idx"), &TabBar::ensure_tab_visible); + ClassDB::bind_method(D_METHOD("get_tab_rect", "tab_idx"), &TabBar::get_tab_rect); + ClassDB::bind_method(D_METHOD("move_tab", "from", "to"), &TabBar::move_tab); + ClassDB::bind_method(D_METHOD("set_tab_close_display_policy", "policy"), &TabBar::set_tab_close_display_policy); + ClassDB::bind_method(D_METHOD("get_tab_close_display_policy"), &TabBar::get_tab_close_display_policy); + ClassDB::bind_method(D_METHOD("set_scrolling_enabled", "enabled"), &TabBar::set_scrolling_enabled); + ClassDB::bind_method(D_METHOD("get_scrolling_enabled"), &TabBar::get_scrolling_enabled); + ClassDB::bind_method(D_METHOD("set_drag_to_rearrange_enabled", "enabled"), &TabBar::set_drag_to_rearrange_enabled); + ClassDB::bind_method(D_METHOD("get_drag_to_rearrange_enabled"), &TabBar::get_drag_to_rearrange_enabled); + ClassDB::bind_method(D_METHOD("set_tabs_rearrange_group", "group_id"), &TabBar::set_tabs_rearrange_group); + ClassDB::bind_method(D_METHOD("get_tabs_rearrange_group"), &TabBar::get_tabs_rearrange_group); + + ClassDB::bind_method(D_METHOD("set_select_with_rmb", "enabled"), &TabBar::set_select_with_rmb); + ClassDB::bind_method(D_METHOD("get_select_with_rmb"), &TabBar::get_select_with_rmb); + + ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab"))); + ADD_SIGNAL(MethodInfo("tab_rmb_clicked", PropertyInfo(Variant::INT, "tab"))); + ADD_SIGNAL(MethodInfo("tab_closed", PropertyInfo(Variant::INT, "tab"))); + ADD_SIGNAL(MethodInfo("tab_hovered", PropertyInfo(Variant::INT, "tab"))); + ADD_SIGNAL(MethodInfo("active_tab_rearranged", PropertyInfo(Variant::INT, "idx_to"))); + ADD_SIGNAL(MethodInfo("tab_clicked", PropertyInfo(Variant::INT, "tab"))); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1", PROPERTY_USAGE_EDITOR), "set_current_tab", "get_current_tab"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_align", "get_tab_align"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_tabs"), "set_clip_tabs", "get_clip_tabs"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_close_display_policy", PROPERTY_HINT_ENUM, "Show Never,Show Active Only,Show Always"), "set_tab_close_display_policy", "get_tab_close_display_policy"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scrolling_enabled"), "set_scrolling_enabled", "get_scrolling_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_to_rearrange_enabled"), "set_drag_to_rearrange_enabled", "get_drag_to_rearrange_enabled"); + + BIND_ENUM_CONSTANT(ALIGN_LEFT); + BIND_ENUM_CONSTANT(ALIGN_CENTER); + BIND_ENUM_CONSTANT(ALIGN_RIGHT); + BIND_ENUM_CONSTANT(ALIGN_MAX); + + BIND_ENUM_CONSTANT(CLOSE_BUTTON_SHOW_NEVER); + BIND_ENUM_CONSTANT(CLOSE_BUTTON_SHOW_ACTIVE_ONLY); + BIND_ENUM_CONSTANT(CLOSE_BUTTON_SHOW_ALWAYS); + BIND_ENUM_CONSTANT(CLOSE_BUTTON_MAX); +} + +TabBar::TabBar() { + connect("mouse_exited", callable_mp(this, &TabBar::_on_mouse_exited)); +} diff --git a/scene/gui/tab_bar.h b/scene/gui/tab_bar.h new file mode 100644 index 0000000000..1d84d6dbc8 --- /dev/null +++ b/scene/gui/tab_bar.h @@ -0,0 +1,196 @@ +/*************************************************************************/ +/* tab_bar.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 TAB_BAR_H +#define TAB_BAR_H + +#include "scene/gui/control.h" +#include "scene/resources/text_line.h" + +class TabBar : public Control { + GDCLASS(TabBar, Control); + +public: + enum TabAlign { + ALIGN_LEFT, + ALIGN_CENTER, + ALIGN_RIGHT, + ALIGN_MAX + }; + + enum CloseButtonDisplayPolicy { + CLOSE_BUTTON_SHOW_NEVER, + CLOSE_BUTTON_SHOW_ACTIVE_ONLY, + CLOSE_BUTTON_SHOW_ALWAYS, + CLOSE_BUTTON_MAX + }; + +private: + struct Tab { + String text; + String xl_text; + + Dictionary opentype_features; + String language; + Control::TextDirection text_direction = Control::TEXT_DIRECTION_INHERITED; + + Ref text_buf; + Ref icon; + int ofs_cache = 0; + bool disabled = false; + int size_cache = 0; + int size_text = 0; + int x_cache = 0; + int x_size_cache = 0; + + Ref right_button; + Rect2 rb_rect; + Rect2 cb_rect; + }; + + int offset = 0; + int max_drawn_tab = 0; + int highlight_arrow = -1; + bool buttons_visible = false; + bool missing_right = false; + Vector tabs; + int current = 0; + int previous = 0; + int _get_top_margin() const; + TabAlign tab_align = ALIGN_CENTER; + bool clip_tabs = true; + int rb_hover = -1; + bool rb_pressing = false; + + bool select_with_rmb = false; + + int cb_hover = -1; + bool cb_pressing = false; + CloseButtonDisplayPolicy cb_displaypolicy = CLOSE_BUTTON_SHOW_NEVER; + + int hover = -1; // Hovered tab. + int min_width = 0; + bool scrolling_enabled = true; + bool drag_to_rearrange_enabled = false; + int tabs_rearrange_group = -1; + + int get_tab_width(int p_idx) const; + void _ensure_no_over_offset(); + + void _update_hover(); + void _update_cache(); + + void _on_mouse_exited(); + + void _shape(int p_tab); + +protected: + virtual void gui_input(const Ref &p_event) override; + void _notification(int p_what); + static void _bind_methods(); + + Variant get_drag_data(const Point2 &p_point) override; + bool can_drop_data(const Point2 &p_point, const Variant &p_data) const override; + void drop_data(const Point2 &p_point, const Variant &p_data) override; + int get_tab_idx_at_point(const Point2 &p_point) const; + +public: + void add_tab(const String &p_str = "", const Ref &p_icon = Ref()); + + void set_tab_title(int p_tab, const String &p_title); + String get_tab_title(int p_tab) const; + + void set_tab_text_direction(int p_tab, TextDirection p_text_direction); + TextDirection get_tab_text_direction(int p_tab) const; + + void set_tab_opentype_feature(int p_tab, const String &p_name, int p_value); + int get_tab_opentype_feature(int p_tab, const String &p_name) const; + void clear_tab_opentype_features(int p_tab); + + void set_tab_language(int p_tab, const String &p_language); + String get_tab_language(int p_tab) const; + + void set_tab_icon(int p_tab, const Ref &p_icon); + Ref get_tab_icon(int p_tab) const; + + void set_tab_disabled(int p_tab, bool p_disabled); + bool get_tab_disabled(int p_tab) const; + + void set_tab_right_button(int p_tab, const Ref &p_right_button); + Ref get_tab_right_button(int p_tab) const; + + void set_tab_align(TabAlign p_align); + TabAlign get_tab_align() const; + + void set_clip_tabs(bool p_clip_tabs); + bool get_clip_tabs() const; + + void move_tab(int from, int to); + + void set_tab_close_display_policy(CloseButtonDisplayPolicy p_policy); + CloseButtonDisplayPolicy get_tab_close_display_policy() const; + + int get_tab_count() const; + void set_current_tab(int p_current); + int get_current_tab() const; + int get_previous_tab() const; + int get_hovered_tab() const; + + int get_tab_offset() const; + bool get_offset_buttons_visible() const; + + void remove_tab(int p_idx); + + void clear_tabs(); + + void set_scrolling_enabled(bool p_enabled); + bool get_scrolling_enabled() const; + + void set_drag_to_rearrange_enabled(bool p_enabled); + bool get_drag_to_rearrange_enabled() const; + void set_tabs_rearrange_group(int p_group_id); + int get_tabs_rearrange_group() const; + + void set_select_with_rmb(bool p_enabled); + bool get_select_with_rmb() const; + + void ensure_tab_visible(int p_idx); + void set_min_width(int p_width); + + Rect2 get_tab_rect(int p_tab) const; + Size2 get_minimum_size() const override; + + TabBar(); +}; + +VARIANT_ENUM_CAST(TabBar::TabAlign); +VARIANT_ENUM_CAST(TabBar::CloseButtonDisplayPolicy); + +#endif // TAB_BAR_H diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp deleted file mode 100644 index 0755a79eee..0000000000 --- a/scene/gui/tabs.cpp +++ /dev/null @@ -1,1195 +0,0 @@ -/*************************************************************************/ -/* tabs.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 "tabs.h" - -#include "core/object/message_queue.h" -#include "core/string/translation.h" - -#include "scene/gui/box_container.h" -#include "scene/gui/label.h" -#include "scene/gui/texture_rect.h" - -Size2 Tabs::get_minimum_size() const { - Ref tab_unselected = get_theme_stylebox(SNAME("tab_unselected")); - Ref tab_selected = get_theme_stylebox(SNAME("tab_selected")); - Ref tab_disabled = get_theme_stylebox(SNAME("tab_disabled")); - - int y_margin = MAX(MAX(tab_unselected->get_minimum_size().height, tab_selected->get_minimum_size().height), tab_disabled->get_minimum_size().height); - - Size2 ms(0, 0); - - for (int i = 0; i < tabs.size(); i++) { - Ref tex = tabs[i].icon; - if (tex.is_valid()) { - ms.height = MAX(ms.height, tex->get_size().height); - if (tabs[i].text != "") { - ms.width += get_theme_constant(SNAME("hseparation")); - } - } - - ms.width += Math::ceil(tabs[i].text_buf->get_size().x); - ms.height = MAX(ms.height, tabs[i].text_buf->get_size().y + y_margin); - - if (tabs[i].disabled) { - ms.width += tab_disabled->get_minimum_size().width; - } else if (current == i) { - ms.width += tab_selected->get_minimum_size().width; - } else { - ms.width += tab_unselected->get_minimum_size().width; - } - - if (tabs[i].right_button.is_valid()) { - Ref rb = tabs[i].right_button; - Size2 bms = rb->get_size(); - bms.width += get_theme_constant(SNAME("hseparation")); - ms.width += bms.width; - ms.height = MAX(bms.height + tab_unselected->get_minimum_size().height, ms.height); - } - - if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current)) { - Ref cb = get_theme_icon(SNAME("close")); - Size2 bms = cb->get_size(); - bms.width += get_theme_constant(SNAME("hseparation")); - ms.width += bms.width; - ms.height = MAX(bms.height + tab_unselected->get_minimum_size().height, ms.height); - } - } - - if (clip_tabs) { - ms.width = 0; - } - - return ms; -} - -void Tabs::gui_input(const Ref &p_event) { - ERR_FAIL_COND(p_event.is_null()); - - Ref mm = p_event; - - if (mm.is_valid()) { - Point2 pos = mm->get_position(); - - if (buttons_visible) { - Ref incr = get_theme_icon(SNAME("increment")); - Ref decr = get_theme_icon(SNAME("decrement")); - - if (is_layout_rtl()) { - if (pos.x < decr->get_width()) { - if (highlight_arrow != 1) { - highlight_arrow = 1; - update(); - } - } else if (pos.x < incr->get_width() + decr->get_width()) { - if (highlight_arrow != 0) { - highlight_arrow = 0; - update(); - } - } else if (highlight_arrow != -1) { - highlight_arrow = -1; - update(); - } - } else { - int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width(); - if (pos.x > limit_minus_buttons + decr->get_width()) { - if (highlight_arrow != 1) { - highlight_arrow = 1; - update(); - } - } else if (pos.x > limit_minus_buttons) { - if (highlight_arrow != 0) { - highlight_arrow = 0; - update(); - } - } else if (highlight_arrow != -1) { - highlight_arrow = -1; - update(); - } - } - } - - _update_hover(); - return; - } - - Ref mb = p_event; - - if (mb.is_valid()) { - if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP && !mb->is_command_pressed()) { - if (scrolling_enabled && buttons_visible) { - if (offset > 0) { - offset--; - update(); - } - } - } - - if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && !mb->is_command_pressed()) { - if (scrolling_enabled && buttons_visible) { - if (missing_right) { - offset++; - _ensure_no_over_offset(); // Avoid overreaching when scrolling fast. - update(); - } - } - } - - if (rb_pressing && !mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) { - if (rb_hover != -1) { - //pressed - emit_signal(SNAME("tab_rmb_clicked"), rb_hover); - } - - rb_pressing = false; - update(); - } - - if (cb_pressing && !mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) { - if (cb_hover != -1) { - //pressed - emit_signal(SNAME("tab_closed"), cb_hover); - } - - cb_pressing = false; - update(); - } - - if (mb->is_pressed() && (mb->get_button_index() == MOUSE_BUTTON_LEFT || (select_with_rmb && mb->get_button_index() == MOUSE_BUTTON_RIGHT))) { - // clicks - Point2 pos = mb->get_position(); - - if (buttons_visible) { - Ref incr = get_theme_icon(SNAME("increment")); - Ref decr = get_theme_icon(SNAME("decrement")); - - if (is_layout_rtl()) { - if (pos.x < decr->get_width()) { - if (missing_right) { - offset++; - update(); - } - return; - } else if (pos.x < incr->get_width() + decr->get_width()) { - if (offset > 0) { - offset--; - update(); - } - return; - } - } else { - int limit = get_size().width - incr->get_width() - decr->get_width(); - if (pos.x > limit + decr->get_width()) { - if (missing_right) { - offset++; - update(); - } - return; - } else if (pos.x > limit) { - if (offset > 0) { - offset--; - update(); - } - return; - } - } - } - - int found = -1; - for (int i = offset; i <= max_drawn_tab; i++) { - if (tabs[i].rb_rect.has_point(pos)) { - rb_pressing = true; - update(); - return; - } - - if (tabs[i].cb_rect.has_point(pos)) { - cb_pressing = true; - update(); - return; - } - - if (pos.x >= get_tab_rect(i).position.x && pos.x < get_tab_rect(i).position.x + tabs[i].size_cache) { - if (!tabs[i].disabled) { - found = i; - } - break; - } - } - - if (found != -1) { - set_current_tab(found); - emit_signal(SNAME("tab_clicked"), found); - } - } - } -} - -void Tabs::_shape(int p_tab) { - Ref font = get_theme_font(SNAME("font")); - int font_size = get_theme_font_size(SNAME("font_size")); - - tabs.write[p_tab].xl_text = atr(tabs[p_tab].text); - tabs.write[p_tab].text_buf->clear(); - tabs.write[p_tab].text_buf->set_width(-1); - if (tabs[p_tab].text_direction == Control::TEXT_DIRECTION_INHERITED) { - tabs.write[p_tab].text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); - } else { - tabs.write[p_tab].text_buf->set_direction((TextServer::Direction)tabs[p_tab].text_direction); - } - - tabs.write[p_tab].text_buf->add_string(tabs.write[p_tab].xl_text, font, font_size, tabs[p_tab].opentype_features, (tabs[p_tab].language != "") ? tabs[p_tab].language : TranslationServer::get_singleton()->get_tool_locale()); -} - -void Tabs::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { - _update_cache(); - update(); - } break; - case NOTIFICATION_THEME_CHANGED: - case NOTIFICATION_TRANSLATION_CHANGED: { - for (int i = 0; i < tabs.size(); ++i) { - _shape(i); - } - _update_cache(); - minimum_size_changed(); - update(); - } break; - case NOTIFICATION_RESIZED: { - _update_cache(); - _ensure_no_over_offset(); - ensure_tab_visible(current); - } break; - case NOTIFICATION_DRAW: { - _update_cache(); - RID ci = get_canvas_item(); - - Ref tab_unselected = get_theme_stylebox(SNAME("tab_unselected")); - Ref tab_selected = get_theme_stylebox(SNAME("tab_selected")); - Ref tab_disabled = get_theme_stylebox(SNAME("tab_disabled")); - Color font_selected_color = get_theme_color(SNAME("font_selected_color")); - Color font_unselected_color = get_theme_color(SNAME("font_unselected_color")); - Color font_disabled_color = get_theme_color(SNAME("font_disabled_color")); - Ref close = get_theme_icon(SNAME("close")); - Color font_outline_color = get_theme_color(SNAME("font_outline_color")); - int outline_size = get_theme_constant(SNAME("outline_size")); - - Vector2 size = get_size(); - bool rtl = is_layout_rtl(); - - int h = get_size().height; - int w = 0; - int mw = 0; - - for (int i = 0; i < tabs.size(); i++) { - tabs.write[i].ofs_cache = mw; - mw += get_tab_width(i); - } - - if (tab_align == ALIGN_CENTER) { - w = (get_size().width - mw) / 2; - } else if (tab_align == ALIGN_RIGHT) { - w = get_size().width - mw; - } - - if (w < 0) { - w = 0; - } - - Ref incr = get_theme_icon(SNAME("increment")); - Ref decr = get_theme_icon(SNAME("decrement")); - Ref incr_hl = get_theme_icon(SNAME("increment_highlight")); - Ref decr_hl = get_theme_icon(SNAME("decrement_highlight")); - - int limit = get_size().width; - int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width(); - - missing_right = false; - - for (int i = offset; i < tabs.size(); i++) { - tabs.write[i].ofs_cache = w; - - int lsize = tabs[i].size_cache; - - Ref sb; - Color col; - - if (tabs[i].disabled) { - sb = tab_disabled; - col = font_disabled_color; - } else if (i == current) { - sb = tab_selected; - col = font_selected_color; - } else { - sb = tab_unselected; - col = font_unselected_color; - } - - int new_width = w + lsize; - if (new_width > limit || (i < tabs.size() - 1 && new_width > limit_minus_buttons)) { // For the last tab, we accept if the tab covers the buttons. - max_drawn_tab = i - 1; - missing_right = true; - break; - } else { - max_drawn_tab = i; - } - - Rect2 sb_rect; - if (rtl) { - sb_rect = Rect2(size.width - w - tabs[i].size_cache, 0, tabs[i].size_cache, h); - } else { - sb_rect = Rect2(w, 0, tabs[i].size_cache, h); - } - sb->draw(ci, sb_rect); - - w += sb->get_margin(SIDE_LEFT); - - Size2i sb_ms = sb->get_minimum_size(); - Ref icon = tabs[i].icon; - if (icon.is_valid()) { - if (rtl) { - icon->draw(ci, Point2i(size.width - w - icon->get_width(), sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - icon->get_height()) / 2)); - } else { - icon->draw(ci, Point2i(w, sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - icon->get_height()) / 2)); - } - if (tabs[i].text != "") { - w += icon->get_width() + get_theme_constant(SNAME("hseparation")); - } - } - - if (rtl) { - Vector2 text_pos = Point2i(size.width - w - tabs[i].text_buf->get_size().x, sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - tabs[i].text_buf->get_size().y) / 2); - if (outline_size > 0 && font_outline_color.a > 0) { - tabs[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color); - } - tabs[i].text_buf->draw(ci, text_pos, col); - } else { - Vector2 text_pos = Point2i(w, sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - tabs[i].text_buf->get_size().y) / 2); - if (outline_size > 0 && font_outline_color.a > 0) { - tabs[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color); - } - tabs[i].text_buf->draw(ci, text_pos, col); - } - - w += tabs[i].size_text; - - if (tabs[i].right_button.is_valid()) { - Ref style = get_theme_stylebox(SNAME("close_bg_highlight")); - Ref rb = tabs[i].right_button; - - w += get_theme_constant(SNAME("hseparation")); - - Rect2 rb_rect; - rb_rect.size = style->get_minimum_size() + rb->get_size(); - if (rtl) { - rb_rect.position.x = size.width - w - rb_rect.size.x; - } else { - rb_rect.position.x = w; - } - rb_rect.position.y = sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - (rb_rect.size.y)) / 2; - - if (rb_hover == i) { - if (rb_pressing) { - get_theme_stylebox(SNAME("button_pressed"))->draw(ci, rb_rect); - } else { - style->draw(ci, rb_rect); - } - } - - if (rtl) { - rb->draw(ci, Point2i(size.width - w - rb_rect.size.x + style->get_margin(SIDE_LEFT), rb_rect.position.y + style->get_margin(SIDE_TOP))); - } else { - rb->draw(ci, Point2i(w + style->get_margin(SIDE_LEFT), rb_rect.position.y + style->get_margin(SIDE_TOP))); - } - w += rb->get_width(); - tabs.write[i].rb_rect = rb_rect; - } - - if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current)) { - Ref style = get_theme_stylebox(SNAME("close_bg_highlight")); - Ref cb = close; - - w += get_theme_constant(SNAME("hseparation")); - - Rect2 cb_rect; - cb_rect.size = style->get_minimum_size() + cb->get_size(); - if (rtl) { - cb_rect.position.x = size.width - w - cb_rect.size.x; - } else { - cb_rect.position.x = w; - } - cb_rect.position.y = sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - (cb_rect.size.y)) / 2; - - if (!tabs[i].disabled && cb_hover == i) { - if (cb_pressing) { - get_theme_stylebox(SNAME("close_bg_pressed"))->draw(ci, cb_rect); - } else { - style->draw(ci, cb_rect); - } - } - - if (rtl) { - cb->draw(ci, Point2i(size.width - w - cb_rect.size.x + style->get_margin(SIDE_LEFT), cb_rect.position.y + style->get_margin(SIDE_TOP))); - } else { - cb->draw(ci, Point2i(w + style->get_margin(SIDE_LEFT), cb_rect.position.y + style->get_margin(SIDE_TOP))); - } - w += cb->get_width(); - tabs.write[i].cb_rect = cb_rect; - } - - w += sb->get_margin(SIDE_RIGHT); - } - - if (offset > 0 || missing_right) { - int vofs = (get_size().height - incr->get_size().height) / 2; - - if (rtl) { - if (missing_right) { - draw_texture(highlight_arrow == 1 ? decr_hl : decr, Point2(0, vofs)); - } else { - draw_texture(decr, Point2(0, vofs), Color(1, 1, 1, 0.5)); - } - - if (offset > 0) { - draw_texture(highlight_arrow == 0 ? incr_hl : incr, Point2(incr->get_size().width, vofs)); - } else { - draw_texture(incr, Point2(incr->get_size().width, vofs), Color(1, 1, 1, 0.5)); - } - } else { - if (offset > 0) { - draw_texture(highlight_arrow == 0 ? decr_hl : decr, Point2(limit_minus_buttons, vofs)); - } else { - draw_texture(decr, Point2(limit_minus_buttons, vofs), Color(1, 1, 1, 0.5)); - } - - if (missing_right) { - draw_texture(highlight_arrow == 1 ? incr_hl : incr, Point2(limit_minus_buttons + decr->get_size().width, vofs)); - } else { - draw_texture(incr, Point2(limit_minus_buttons + decr->get_size().width, vofs), Color(1, 1, 1, 0.5)); - } - } - - buttons_visible = true; - } else { - buttons_visible = false; - } - } break; - } -} - -int Tabs::get_tab_count() const { - return tabs.size(); -} - -void Tabs::set_current_tab(int p_current) { - if (current == p_current) { - return; - } - ERR_FAIL_INDEX(p_current, get_tab_count()); - - previous = current; - current = p_current; - - _update_cache(); - update(); - - emit_signal(SNAME("tab_changed"), p_current); -} - -int Tabs::get_current_tab() const { - return current; -} - -int Tabs::get_previous_tab() const { - return previous; -} - -int Tabs::get_hovered_tab() const { - return hover; -} - -int Tabs::get_tab_offset() const { - return offset; -} - -bool Tabs::get_offset_buttons_visible() const { - return buttons_visible; -} - -void Tabs::set_tab_title(int p_tab, const String &p_title) { - ERR_FAIL_INDEX(p_tab, tabs.size()); - tabs.write[p_tab].text = p_title; - _shape(p_tab); - update(); - minimum_size_changed(); -} - -String Tabs::get_tab_title(int p_tab) const { - ERR_FAIL_INDEX_V(p_tab, tabs.size(), ""); - return tabs[p_tab].text; -} - -void Tabs::set_tab_text_direction(int p_tab, Control::TextDirection p_text_direction) { - ERR_FAIL_INDEX(p_tab, tabs.size()); - ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); - if (tabs[p_tab].text_direction != p_text_direction) { - tabs.write[p_tab].text_direction = p_text_direction; - _shape(p_tab); - update(); - } -} - -Control::TextDirection Tabs::get_tab_text_direction(int p_tab) const { - ERR_FAIL_INDEX_V(p_tab, tabs.size(), Control::TEXT_DIRECTION_INHERITED); - return tabs[p_tab].text_direction; -} - -void Tabs::clear_tab_opentype_features(int p_tab) { - ERR_FAIL_INDEX(p_tab, tabs.size()); - tabs.write[p_tab].opentype_features.clear(); - _shape(p_tab); - update(); -} - -void Tabs::set_tab_opentype_feature(int p_tab, const String &p_name, int p_value) { - ERR_FAIL_INDEX(p_tab, tabs.size()); - int32_t tag = TS->name_to_tag(p_name); - if (!tabs[p_tab].opentype_features.has(tag) || (int)tabs[p_tab].opentype_features[tag] != p_value) { - tabs.write[p_tab].opentype_features[tag] = p_value; - _shape(p_tab); - update(); - } -} - -int Tabs::get_tab_opentype_feature(int p_tab, const String &p_name) const { - ERR_FAIL_INDEX_V(p_tab, tabs.size(), -1); - int32_t tag = TS->name_to_tag(p_name); - if (!tabs[p_tab].opentype_features.has(tag)) { - return -1; - } - return tabs[p_tab].opentype_features[tag]; -} - -void Tabs::set_tab_language(int p_tab, const String &p_language) { - ERR_FAIL_INDEX(p_tab, tabs.size()); - if (tabs[p_tab].language != p_language) { - tabs.write[p_tab].language = p_language; - _shape(p_tab); - update(); - } -} - -String Tabs::get_tab_language(int p_tab) const { - ERR_FAIL_INDEX_V(p_tab, tabs.size(), ""); - return tabs[p_tab].language; -} - -void Tabs::set_tab_icon(int p_tab, const Ref &p_icon) { - ERR_FAIL_INDEX(p_tab, tabs.size()); - tabs.write[p_tab].icon = p_icon; - update(); - minimum_size_changed(); -} - -Ref Tabs::get_tab_icon(int p_tab) const { - ERR_FAIL_INDEX_V(p_tab, tabs.size(), Ref()); - return tabs[p_tab].icon; -} - -void Tabs::set_tab_disabled(int p_tab, bool p_disabled) { - ERR_FAIL_INDEX(p_tab, tabs.size()); - tabs.write[p_tab].disabled = p_disabled; - update(); -} - -bool Tabs::get_tab_disabled(int p_tab) const { - ERR_FAIL_INDEX_V(p_tab, tabs.size(), false); - return tabs[p_tab].disabled; -} - -void Tabs::set_tab_right_button(int p_tab, const Ref &p_right_button) { - ERR_FAIL_INDEX(p_tab, tabs.size()); - tabs.write[p_tab].right_button = p_right_button; - _update_cache(); - update(); - minimum_size_changed(); -} - -Ref Tabs::get_tab_right_button(int p_tab) const { - ERR_FAIL_INDEX_V(p_tab, tabs.size(), Ref()); - return tabs[p_tab].right_button; -} - -void Tabs::_update_hover() { - if (!is_inside_tree()) { - return; - } - - const Point2 &pos = get_local_mouse_position(); - // test hovering to display right or close button - int hover_now = -1; - int hover_buttons = -1; - for (int i = offset; i < tabs.size(); i++) { - Rect2 rect = get_tab_rect(i); - if (rect.has_point(pos)) { - hover_now = i; - } - if (tabs[i].rb_rect.has_point(pos)) { - rb_hover = i; - cb_hover = -1; - hover_buttons = i; - break; - } else if (!tabs[i].disabled && tabs[i].cb_rect.has_point(pos)) { - cb_hover = i; - rb_hover = -1; - hover_buttons = i; - break; - } - } - if (hover != hover_now) { - hover = hover_now; - emit_signal(SNAME("tab_hovered"), hover); - } - - if (hover_buttons == -1) { // no hover - rb_hover = hover_buttons; - cb_hover = hover_buttons; - } -} - -void Tabs::_update_cache() { - Ref tab_disabled = get_theme_stylebox(SNAME("tab_disabled")); - Ref tab_unselected = get_theme_stylebox(SNAME("tab_unselected")); - Ref tab_selected = get_theme_stylebox(SNAME("tab_selected")); - Ref incr = get_theme_icon(SNAME("increment")); - Ref decr = get_theme_icon(SNAME("decrement")); - int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width(); - - int w = 0; - int mw = 0; - int size_fixed = 0; - int count_resize = 0; - for (int i = 0; i < tabs.size(); i++) { - tabs.write[i].ofs_cache = mw; - tabs.write[i].size_cache = get_tab_width(i); - tabs.write[i].size_text = Math::ceil(tabs[i].text_buf->get_size().x); - tabs.write[i].text_buf->set_width(-1); - mw += tabs[i].size_cache; - if (tabs[i].size_cache <= min_width || i == current) { - size_fixed += tabs[i].size_cache; - } else { - count_resize++; - } - } - int m_width = min_width; - if (count_resize > 0) { - m_width = MAX((limit_minus_buttons - size_fixed) / count_resize, min_width); - } - for (int i = offset; i < tabs.size(); i++) { - Ref sb; - if (tabs[i].disabled) { - sb = tab_disabled; - } else if (i == current) { - sb = tab_selected; - } else { - sb = tab_unselected; - } - int lsize = tabs[i].size_cache; - int slen = tabs[i].size_text; - if (min_width > 0 && mw > limit_minus_buttons && i != current) { - if (lsize > m_width) { - slen = m_width - (sb->get_margin(SIDE_LEFT) + sb->get_margin(SIDE_RIGHT)); - if (tabs[i].icon.is_valid()) { - slen -= tabs[i].icon->get_width(); - slen -= get_theme_constant(SNAME("hseparation")); - } - if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current)) { - Ref cb = get_theme_icon(SNAME("close")); - slen -= cb->get_width(); - slen -= get_theme_constant(SNAME("hseparation")); - } - slen = MAX(slen, 1); - lsize = m_width; - } - } - tabs.write[i].ofs_cache = w; - tabs.write[i].size_cache = lsize; - tabs.write[i].size_text = slen; - tabs.write[i].text_buf->set_width(slen); - w += lsize; - } -} - -void Tabs::_on_mouse_exited() { - rb_hover = -1; - cb_hover = -1; - hover = -1; - highlight_arrow = -1; - update(); -} - -void Tabs::add_tab(const String &p_str, const Ref &p_icon) { - Tab t; - t.text = p_str; - t.xl_text = atr(p_str); - t.text_buf.instantiate(); - t.text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); - t.text_buf->add_string(t.xl_text, get_theme_font(SNAME("font")), get_theme_font_size(SNAME("font_size")), Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); - t.icon = p_icon; - t.disabled = false; - t.ofs_cache = 0; - t.size_cache = 0; - - tabs.push_back(t); - _update_cache(); - call_deferred(SNAME("_update_hover")); - update(); - minimum_size_changed(); -} - -void Tabs::clear_tabs() { - tabs.clear(); - current = 0; - previous = 0; - call_deferred(SNAME("_update_hover")); - update(); -} - -void Tabs::remove_tab(int p_idx) { - ERR_FAIL_INDEX(p_idx, tabs.size()); - tabs.remove(p_idx); - if (current >= p_idx) { - current--; - } - _update_cache(); - call_deferred(SNAME("_update_hover")); - update(); - minimum_size_changed(); - - if (current < 0) { - current = 0; - previous = 0; - } - if (current >= tabs.size()) { - current = tabs.size() - 1; - } - - _ensure_no_over_offset(); -} - -Variant Tabs::get_drag_data(const Point2 &p_point) { - if (!drag_to_rearrange_enabled) { - return Variant(); - } - - int tab_over = get_tab_idx_at_point(p_point); - - if (tab_over < 0) { - return Variant(); - } - - HBoxContainer *drag_preview = memnew(HBoxContainer); - - if (!tabs[tab_over].icon.is_null()) { - TextureRect *tf = memnew(TextureRect); - tf->set_texture(tabs[tab_over].icon); - drag_preview->add_child(tf); - } - Label *label = memnew(Label(tabs[tab_over].xl_text)); - drag_preview->add_child(label); - if (!tabs[tab_over].right_button.is_null()) { - TextureRect *tf = memnew(TextureRect); - tf->set_texture(tabs[tab_over].right_button); - drag_preview->add_child(tf); - } - set_drag_preview(drag_preview); - - Dictionary drag_data; - drag_data["type"] = "tab_element"; - drag_data["tab_element"] = tab_over; - drag_data["from_path"] = get_path(); - return drag_data; -} - -bool Tabs::can_drop_data(const Point2 &p_point, const Variant &p_data) const { - if (!drag_to_rearrange_enabled) { - return false; - } - - Dictionary d = p_data; - if (!d.has("type")) { - return false; - } - - if (String(d["type"]) == "tab_element") { - NodePath from_path = d["from_path"]; - NodePath to_path = get_path(); - if (from_path == to_path) { - return true; - } else if (get_tabs_rearrange_group() != -1) { - // drag and drop between other Tabs - Node *from_node = get_node(from_path); - Tabs *from_tabs = Object::cast_to(from_node); - if (from_tabs && from_tabs->get_tabs_rearrange_group() == get_tabs_rearrange_group()) { - return true; - } - } - } - return false; -} - -void Tabs::drop_data(const Point2 &p_point, const Variant &p_data) { - if (!drag_to_rearrange_enabled) { - return; - } - - int hover_now = get_tab_idx_at_point(p_point); - - Dictionary d = p_data; - if (!d.has("type")) { - return; - } - - if (String(d["type"]) == "tab_element") { - int tab_from_id = d["tab_element"]; - NodePath from_path = d["from_path"]; - NodePath to_path = get_path(); - if (from_path == to_path) { - if (hover_now < 0) { - hover_now = get_tab_count() - 1; - } - move_tab(tab_from_id, hover_now); - emit_signal(SNAME("active_tab_rearranged"), hover_now); - set_current_tab(hover_now); - } else if (get_tabs_rearrange_group() != -1) { - // drag and drop between Tabs - Node *from_node = get_node(from_path); - Tabs *from_tabs = Object::cast_to(from_node); - if (from_tabs && from_tabs->get_tabs_rearrange_group() == get_tabs_rearrange_group()) { - if (tab_from_id >= from_tabs->get_tab_count()) { - return; - } - Tab moving_tab = from_tabs->tabs[tab_from_id]; - if (hover_now < 0) { - hover_now = get_tab_count(); - } - tabs.insert(hover_now, moving_tab); - from_tabs->remove_tab(tab_from_id); - set_current_tab(hover_now); - emit_signal(SNAME("tab_changed"), hover_now); - _update_cache(); - } - } - } - update(); -} - -int Tabs::get_tab_idx_at_point(const Point2 &p_point) const { - int hover_now = -1; - for (int i = offset; i <= max_drawn_tab; i++) { - Rect2 rect = get_tab_rect(i); - if (rect.has_point(p_point)) { - hover_now = i; - } - } - - return hover_now; -} - -void Tabs::set_tab_align(TabAlign p_align) { - ERR_FAIL_INDEX(p_align, ALIGN_MAX); - tab_align = p_align; - update(); -} - -Tabs::TabAlign Tabs::get_tab_align() const { - return tab_align; -} - -void Tabs::set_clip_tabs(bool p_clip_tabs) { - if (clip_tabs == p_clip_tabs) { - return; - } - clip_tabs = p_clip_tabs; - update(); - minimum_size_changed(); -} - -bool Tabs::get_clip_tabs() const { - return clip_tabs; -} - -void Tabs::move_tab(int from, int to) { - if (from == to) { - return; - } - - ERR_FAIL_INDEX(from, tabs.size()); - ERR_FAIL_INDEX(to, tabs.size()); - - Tab tab_from = tabs[from]; - tabs.remove(from); - tabs.insert(to, tab_from); - - _update_cache(); - update(); -} - -int Tabs::get_tab_width(int p_idx) const { - ERR_FAIL_INDEX_V(p_idx, tabs.size(), 0); - - Ref tab_unselected = get_theme_stylebox(SNAME("tab_unselected")); - Ref tab_selected = get_theme_stylebox(SNAME("tab_selected")); - Ref tab_disabled = get_theme_stylebox(SNAME("tab_disabled")); - - int x = 0; - - Ref tex = tabs[p_idx].icon; - if (tex.is_valid()) { - x += tex->get_width(); - if (tabs[p_idx].text != "") { - x += get_theme_constant(SNAME("hseparation")); - } - } - - x += Math::ceil(tabs[p_idx].text_buf->get_size().x); - - if (tabs[p_idx].disabled) { - x += tab_disabled->get_minimum_size().width; - } else if (current == p_idx) { - x += tab_selected->get_minimum_size().width; - } else { - x += tab_unselected->get_minimum_size().width; - } - - if (tabs[p_idx].right_button.is_valid()) { - Ref rb = tabs[p_idx].right_button; - x += rb->get_width(); - x += get_theme_constant(SNAME("hseparation")); - } - - if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && p_idx == current)) { - Ref cb = get_theme_icon(SNAME("close")); - x += cb->get_width(); - x += get_theme_constant(SNAME("hseparation")); - } - - return x; -} - -void Tabs::_ensure_no_over_offset() { - if (!is_inside_tree()) { - return; - } - - Ref incr = get_theme_icon(SNAME("increment")); - Ref decr = get_theme_icon(SNAME("decrement")); - - int limit = get_size().width; - int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width(); - - while (offset > 0) { - int total_w = 0; - for (int i = offset - 1; i < tabs.size(); i++) { - total_w += tabs[i].size_cache; - } - - if ((buttons_visible && total_w < limit_minus_buttons) || total_w < limit) { // For the last tab, we accept if the tab covers the buttons. - offset--; - update(); - } else { - break; - } - } -} - -void Tabs::ensure_tab_visible(int p_idx) { - if (!is_inside_tree()) { - return; - } - - if (tabs.size() == 0) { - return; - } - ERR_FAIL_INDEX(p_idx, tabs.size()); - - if (p_idx == offset) { - return; - } - if (p_idx < offset) { - offset = p_idx; - update(); - return; - } - - int prev_offset = offset; - Ref incr = get_theme_icon(SNAME("increment")); - Ref decr = get_theme_icon(SNAME("decrement")); - int limit = get_size().width; - int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width(); - - for (int i = offset; i <= p_idx; i++) { - int total_w = tabs[i].ofs_cache + tabs[i].size_cache; - if (total_w > limit || (buttons_visible && total_w > limit_minus_buttons)) { - offset++; - } - } - - if (prev_offset != offset) { - update(); - } -} - -Rect2 Tabs::get_tab_rect(int p_tab) const { - ERR_FAIL_INDEX_V(p_tab, tabs.size(), Rect2()); - if (is_layout_rtl()) { - return Rect2(get_size().width - tabs[p_tab].ofs_cache - tabs[p_tab].size_cache, 0, tabs[p_tab].size_cache, get_size().height); - } else { - return Rect2(tabs[p_tab].ofs_cache, 0, tabs[p_tab].size_cache, get_size().height); - } -} - -void Tabs::set_tab_close_display_policy(CloseButtonDisplayPolicy p_policy) { - ERR_FAIL_INDEX(p_policy, CLOSE_BUTTON_MAX); - cb_displaypolicy = p_policy; - update(); -} - -Tabs::CloseButtonDisplayPolicy Tabs::get_tab_close_display_policy() const { - return cb_displaypolicy; -} - -void Tabs::set_min_width(int p_width) { - min_width = p_width; -} - -void Tabs::set_scrolling_enabled(bool p_enabled) { - scrolling_enabled = p_enabled; -} - -bool Tabs::get_scrolling_enabled() const { - return scrolling_enabled; -} - -void Tabs::set_drag_to_rearrange_enabled(bool p_enabled) { - drag_to_rearrange_enabled = p_enabled; -} - -bool Tabs::get_drag_to_rearrange_enabled() const { - return drag_to_rearrange_enabled; -} - -void Tabs::set_tabs_rearrange_group(int p_group_id) { - tabs_rearrange_group = p_group_id; -} - -int Tabs::get_tabs_rearrange_group() const { - return tabs_rearrange_group; -} - -void Tabs::set_select_with_rmb(bool p_enabled) { - select_with_rmb = p_enabled; -} - -bool Tabs::get_select_with_rmb() const { - return select_with_rmb; -} - -void Tabs::_bind_methods() { - ClassDB::bind_method(D_METHOD("_update_hover"), &Tabs::_update_hover); - ClassDB::bind_method(D_METHOD("get_tab_count"), &Tabs::get_tab_count); - ClassDB::bind_method(D_METHOD("set_current_tab", "tab_idx"), &Tabs::set_current_tab); - ClassDB::bind_method(D_METHOD("get_current_tab"), &Tabs::get_current_tab); - ClassDB::bind_method(D_METHOD("get_previous_tab"), &Tabs::get_previous_tab); - ClassDB::bind_method(D_METHOD("set_tab_title", "tab_idx", "title"), &Tabs::set_tab_title); - ClassDB::bind_method(D_METHOD("get_tab_title", "tab_idx"), &Tabs::get_tab_title); - ClassDB::bind_method(D_METHOD("set_tab_text_direction", "tab_idx", "direction"), &Tabs::set_tab_text_direction); - ClassDB::bind_method(D_METHOD("get_tab_text_direction", "tab_idx"), &Tabs::get_tab_text_direction); - ClassDB::bind_method(D_METHOD("set_tab_opentype_feature", "tab_idx", "tag", "values"), &Tabs::set_tab_opentype_feature); - ClassDB::bind_method(D_METHOD("get_tab_opentype_feature", "tab_idx", "tag"), &Tabs::get_tab_opentype_feature); - ClassDB::bind_method(D_METHOD("clear_tab_opentype_features", "tab_idx"), &Tabs::clear_tab_opentype_features); - ClassDB::bind_method(D_METHOD("set_tab_language", "tab_idx", "language"), &Tabs::set_tab_language); - ClassDB::bind_method(D_METHOD("get_tab_language", "tab_idx"), &Tabs::get_tab_language); - ClassDB::bind_method(D_METHOD("set_tab_icon", "tab_idx", "icon"), &Tabs::set_tab_icon); - ClassDB::bind_method(D_METHOD("get_tab_icon", "tab_idx"), &Tabs::get_tab_icon); - ClassDB::bind_method(D_METHOD("set_tab_disabled", "tab_idx", "disabled"), &Tabs::set_tab_disabled); - ClassDB::bind_method(D_METHOD("get_tab_disabled", "tab_idx"), &Tabs::get_tab_disabled); - ClassDB::bind_method(D_METHOD("remove_tab", "tab_idx"), &Tabs::remove_tab); - ClassDB::bind_method(D_METHOD("add_tab", "title", "icon"), &Tabs::add_tab, DEFVAL(""), DEFVAL(Ref())); - ClassDB::bind_method(D_METHOD("set_tab_align", "align"), &Tabs::set_tab_align); - ClassDB::bind_method(D_METHOD("get_tab_align"), &Tabs::get_tab_align); - ClassDB::bind_method(D_METHOD("set_clip_tabs", "clip_tabs"), &Tabs::set_clip_tabs); - ClassDB::bind_method(D_METHOD("get_clip_tabs"), &Tabs::get_clip_tabs); - ClassDB::bind_method(D_METHOD("get_tab_offset"), &Tabs::get_tab_offset); - ClassDB::bind_method(D_METHOD("get_offset_buttons_visible"), &Tabs::get_offset_buttons_visible); - ClassDB::bind_method(D_METHOD("ensure_tab_visible", "idx"), &Tabs::ensure_tab_visible); - ClassDB::bind_method(D_METHOD("get_tab_rect", "tab_idx"), &Tabs::get_tab_rect); - ClassDB::bind_method(D_METHOD("move_tab", "from", "to"), &Tabs::move_tab); - ClassDB::bind_method(D_METHOD("set_tab_close_display_policy", "policy"), &Tabs::set_tab_close_display_policy); - ClassDB::bind_method(D_METHOD("get_tab_close_display_policy"), &Tabs::get_tab_close_display_policy); - ClassDB::bind_method(D_METHOD("set_scrolling_enabled", "enabled"), &Tabs::set_scrolling_enabled); - ClassDB::bind_method(D_METHOD("get_scrolling_enabled"), &Tabs::get_scrolling_enabled); - ClassDB::bind_method(D_METHOD("set_drag_to_rearrange_enabled", "enabled"), &Tabs::set_drag_to_rearrange_enabled); - ClassDB::bind_method(D_METHOD("get_drag_to_rearrange_enabled"), &Tabs::get_drag_to_rearrange_enabled); - ClassDB::bind_method(D_METHOD("set_tabs_rearrange_group", "group_id"), &Tabs::set_tabs_rearrange_group); - ClassDB::bind_method(D_METHOD("get_tabs_rearrange_group"), &Tabs::get_tabs_rearrange_group); - - ClassDB::bind_method(D_METHOD("set_select_with_rmb", "enabled"), &Tabs::set_select_with_rmb); - ClassDB::bind_method(D_METHOD("get_select_with_rmb"), &Tabs::get_select_with_rmb); - - ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab"))); - ADD_SIGNAL(MethodInfo("tab_rmb_clicked", PropertyInfo(Variant::INT, "tab"))); - ADD_SIGNAL(MethodInfo("tab_closed", PropertyInfo(Variant::INT, "tab"))); - ADD_SIGNAL(MethodInfo("tab_hovered", PropertyInfo(Variant::INT, "tab"))); - ADD_SIGNAL(MethodInfo("active_tab_rearranged", PropertyInfo(Variant::INT, "idx_to"))); - ADD_SIGNAL(MethodInfo("tab_clicked", PropertyInfo(Variant::INT, "tab"))); - - ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1", PROPERTY_USAGE_EDITOR), "set_current_tab", "get_current_tab"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_align", "get_tab_align"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_tabs"), "set_clip_tabs", "get_clip_tabs"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_close_display_policy", PROPERTY_HINT_ENUM, "Show Never,Show Active Only,Show Always"), "set_tab_close_display_policy", "get_tab_close_display_policy"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scrolling_enabled"), "set_scrolling_enabled", "get_scrolling_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_to_rearrange_enabled"), "set_drag_to_rearrange_enabled", "get_drag_to_rearrange_enabled"); - - BIND_ENUM_CONSTANT(ALIGN_LEFT); - BIND_ENUM_CONSTANT(ALIGN_CENTER); - BIND_ENUM_CONSTANT(ALIGN_RIGHT); - BIND_ENUM_CONSTANT(ALIGN_MAX); - - BIND_ENUM_CONSTANT(CLOSE_BUTTON_SHOW_NEVER); - BIND_ENUM_CONSTANT(CLOSE_BUTTON_SHOW_ACTIVE_ONLY); - BIND_ENUM_CONSTANT(CLOSE_BUTTON_SHOW_ALWAYS); - BIND_ENUM_CONSTANT(CLOSE_BUTTON_MAX); -} - -Tabs::Tabs() { - connect("mouse_exited", callable_mp(this, &Tabs::_on_mouse_exited)); -} diff --git a/scene/gui/tabs.h b/scene/gui/tabs.h deleted file mode 100644 index b044453803..0000000000 --- a/scene/gui/tabs.h +++ /dev/null @@ -1,196 +0,0 @@ -/*************************************************************************/ -/* tabs.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 TABS_H -#define TABS_H - -#include "scene/gui/control.h" -#include "scene/resources/text_line.h" - -class Tabs : public Control { - GDCLASS(Tabs, Control); - -public: - enum TabAlign { - ALIGN_LEFT, - ALIGN_CENTER, - ALIGN_RIGHT, - ALIGN_MAX - }; - - enum CloseButtonDisplayPolicy { - CLOSE_BUTTON_SHOW_NEVER, - CLOSE_BUTTON_SHOW_ACTIVE_ONLY, - CLOSE_BUTTON_SHOW_ALWAYS, - CLOSE_BUTTON_MAX - }; - -private: - struct Tab { - String text; - String xl_text; - - Dictionary opentype_features; - String language; - Control::TextDirection text_direction = Control::TEXT_DIRECTION_INHERITED; - - Ref text_buf; - Ref icon; - int ofs_cache = 0; - bool disabled = false; - int size_cache = 0; - int size_text = 0; - int x_cache = 0; - int x_size_cache = 0; - - Ref right_button; - Rect2 rb_rect; - Rect2 cb_rect; - }; - - int offset = 0; - int max_drawn_tab = 0; - int highlight_arrow = -1; - bool buttons_visible = false; - bool missing_right = false; - Vector tabs; - int current = 0; - int previous = 0; - int _get_top_margin() const; - TabAlign tab_align = ALIGN_CENTER; - bool clip_tabs = true; - int rb_hover = -1; - bool rb_pressing = false; - - bool select_with_rmb = false; - - int cb_hover = -1; - bool cb_pressing = false; - CloseButtonDisplayPolicy cb_displaypolicy = CLOSE_BUTTON_SHOW_NEVER; - - int hover = -1; // Hovered tab. - int min_width = 0; - bool scrolling_enabled = true; - bool drag_to_rearrange_enabled = false; - int tabs_rearrange_group = -1; - - int get_tab_width(int p_idx) const; - void _ensure_no_over_offset(); - - void _update_hover(); - void _update_cache(); - - void _on_mouse_exited(); - - void _shape(int p_tab); - -protected: - virtual void gui_input(const Ref &p_event) override; - void _notification(int p_what); - static void _bind_methods(); - - Variant get_drag_data(const Point2 &p_point) override; - bool can_drop_data(const Point2 &p_point, const Variant &p_data) const override; - void drop_data(const Point2 &p_point, const Variant &p_data) override; - int get_tab_idx_at_point(const Point2 &p_point) const; - -public: - void add_tab(const String &p_str = "", const Ref &p_icon = Ref()); - - void set_tab_title(int p_tab, const String &p_title); - String get_tab_title(int p_tab) const; - - void set_tab_text_direction(int p_tab, TextDirection p_text_direction); - TextDirection get_tab_text_direction(int p_tab) const; - - void set_tab_opentype_feature(int p_tab, const String &p_name, int p_value); - int get_tab_opentype_feature(int p_tab, const String &p_name) const; - void clear_tab_opentype_features(int p_tab); - - void set_tab_language(int p_tab, const String &p_language); - String get_tab_language(int p_tab) const; - - void set_tab_icon(int p_tab, const Ref &p_icon); - Ref get_tab_icon(int p_tab) const; - - void set_tab_disabled(int p_tab, bool p_disabled); - bool get_tab_disabled(int p_tab) const; - - void set_tab_right_button(int p_tab, const Ref &p_right_button); - Ref get_tab_right_button(int p_tab) const; - - void set_tab_align(TabAlign p_align); - TabAlign get_tab_align() const; - - void set_clip_tabs(bool p_clip_tabs); - bool get_clip_tabs() const; - - void move_tab(int from, int to); - - void set_tab_close_display_policy(CloseButtonDisplayPolicy p_policy); - CloseButtonDisplayPolicy get_tab_close_display_policy() const; - - int get_tab_count() const; - void set_current_tab(int p_current); - int get_current_tab() const; - int get_previous_tab() const; - int get_hovered_tab() const; - - int get_tab_offset() const; - bool get_offset_buttons_visible() const; - - void remove_tab(int p_idx); - - void clear_tabs(); - - void set_scrolling_enabled(bool p_enabled); - bool get_scrolling_enabled() const; - - void set_drag_to_rearrange_enabled(bool p_enabled); - bool get_drag_to_rearrange_enabled() const; - void set_tabs_rearrange_group(int p_group_id); - int get_tabs_rearrange_group() const; - - void set_select_with_rmb(bool p_enabled); - bool get_select_with_rmb() const; - - void ensure_tab_visible(int p_idx); - void set_min_width(int p_width); - - Rect2 get_tab_rect(int p_tab) const; - Size2 get_minimum_size() const override; - - Tabs(); -}; - -VARIANT_ENUM_CAST(Tabs::TabAlign); -VARIANT_ENUM_CAST(Tabs::CloseButtonDisplayPolicy); - -#endif // TABS_H diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 61c25ae9db..33e0e2cad7 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -115,8 +115,8 @@ #include "scene/gui/spin_box.h" #include "scene/gui/split_container.h" #include "scene/gui/subviewport_container.h" +#include "scene/gui/tab_bar.h" #include "scene/gui/tab_container.h" -#include "scene/gui/tabs.h" #include "scene/gui/text_edit.h" #include "scene/gui/texture_button.h" #include "scene/gui/texture_progress_bar.h" @@ -262,7 +262,7 @@ static Ref resource_loader_shader; void register_scene_types() { SceneStringNames::create(); - OS::get_singleton()->yield(); //may take time to init + OS::get_singleton()->yield(); // may take time to init Node::init_node_hrcr(); @@ -287,7 +287,7 @@ void register_scene_types() { resource_loader_shader.instantiate(); ResourceLoader::add_resource_format_loader(resource_loader_shader, true); - OS::get_singleton()->yield(); //may take time to init + OS::get_singleton()->yield(); // may take time to init GDREGISTER_CLASS(Object); @@ -309,7 +309,7 @@ void register_scene_types() { GDREGISTER_CLASS(ButtonGroup); GDREGISTER_VIRTUAL_CLASS(BaseButton); - OS::get_singleton()->yield(); //may take time to init + OS::get_singleton()->yield(); // may take time to init GDREGISTER_CLASS(Control); GDREGISTER_CLASS(Button); @@ -330,7 +330,7 @@ void register_scene_types() { GDREGISTER_CLASS(Panel); GDREGISTER_VIRTUAL_CLASS(Range); - OS::get_singleton()->yield(); //may take time to init + OS::get_singleton()->yield(); // may take time to init GDREGISTER_CLASS(TextureRect); GDREGISTER_CLASS(ColorRect); @@ -338,7 +338,7 @@ void register_scene_types() { GDREGISTER_CLASS(ReferenceRect); GDREGISTER_CLASS(AspectRatioContainer); GDREGISTER_CLASS(TabContainer); - GDREGISTER_CLASS(Tabs); + GDREGISTER_CLASS(TabBar); GDREGISTER_VIRTUAL_CLASS(Separator); GDREGISTER_CLASS(HSeparator); GDREGISTER_CLASS(VSeparator); @@ -352,7 +352,7 @@ void register_scene_types() { GDREGISTER_CLASS(ScrollContainer); GDREGISTER_CLASS(PanelContainer); - OS::get_singleton()->yield(); //may take time to init + OS::get_singleton()->yield(); // may take time to init GDREGISTER_CLASS(TextureProgressBar); GDREGISTER_CLASS(ItemList); @@ -391,7 +391,7 @@ void register_scene_types() { GDREGISTER_CLASS(GraphNode); GDREGISTER_CLASS(GraphEdit); - OS::get_singleton()->yield(); //may take time to init + OS::get_singleton()->yield(); // may take time to init bool swap_cancel_ok = false; if (DisplayServer::get_singleton()) { @@ -431,9 +431,9 @@ void register_scene_types() { GDREGISTER_CLASS(AnimationNodeTimeSeek); GDREGISTER_CLASS(AnimationNodeTransition); - GDREGISTER_CLASS(ShaderGlobalsOverride); //can be used in any shader + GDREGISTER_CLASS(ShaderGlobalsOverride); // can be used in any shader - OS::get_singleton()->yield(); //may take time to init + OS::get_singleton()->yield(); // may take time to init /* REGISTER 3D */ @@ -486,9 +486,9 @@ void register_scene_types() { GDREGISTER_CLASS(Position3D); GDREGISTER_CLASS(RootMotionView); - ClassDB::set_class_enabled("RootMotionView", false); //disabled by default, enabled by editor + ClassDB::set_class_enabled("RootMotionView", false); // disabled by default, enabled by editor - OS::get_singleton()->yield(); //may take time to init + OS::get_singleton()->yield(); // may take time to init GDREGISTER_VIRTUAL_CLASS(CollisionObject3D); GDREGISTER_VIRTUAL_CLASS(PhysicsBody3D); @@ -533,7 +533,7 @@ void register_scene_types() { GDREGISTER_CLASS(NavigationAgent3D); GDREGISTER_CLASS(NavigationObstacle3D); - OS::get_singleton()->yield(); //may take time to init + OS::get_singleton()->yield(); // may take time to init #endif /* REGISTER SHADER */ @@ -673,7 +673,7 @@ void register_scene_types() { GDREGISTER_CLASS(OccluderPolygon2D); GDREGISTER_CLASS(BackBufferCopy); - OS::get_singleton()->yield(); //may take time to init + OS::get_singleton()->yield(); // may take time to init GDREGISTER_CLASS(Camera2D); GDREGISTER_CLASS(AudioListener2D); @@ -705,7 +705,7 @@ void register_scene_types() { GDREGISTER_CLASS(PhysicalBone2D); GDREGISTER_CLASS(SkeletonModification2DPhysicalBones); - OS::get_singleton()->yield(); //may take time to init + OS::get_singleton()->yield(); // may take time to init /* REGISTER RESOURCES */ @@ -746,7 +746,7 @@ void register_scene_types() { GDREGISTER_CLASS(MeshLibrary); - OS::get_singleton()->yield(); //may take time to init + OS::get_singleton()->yield(); // may take time to init GDREGISTER_VIRTUAL_CLASS(Shape3D); GDREGISTER_CLASS(SeparationRayShape3D); @@ -768,7 +768,7 @@ void register_scene_types() { ClassDB::register_class(); ClassDB::register_class(); - OS::get_singleton()->yield(); //may take time to init + OS::get_singleton()->yield(); // may take time to init GDREGISTER_CLASS(VelocityTracker3D); #endif @@ -823,7 +823,7 @@ void register_scene_types() { GDREGISTER_CLASS(BitMap); GDREGISTER_CLASS(Gradient); - OS::get_singleton()->yield(); //may take time to init + OS::get_singleton()->yield(); // may take time to init GDREGISTER_CLASS(AudioStreamPlayer); GDREGISTER_CLASS(AudioStreamPlayer2D); @@ -833,7 +833,7 @@ void register_scene_types() { GDREGISTER_VIRTUAL_CLASS(VideoStream); GDREGISTER_CLASS(AudioStreamSample); - OS::get_singleton()->yield(); //may take time to init + OS::get_singleton()->yield(); // may take time to init GDREGISTER_VIRTUAL_CLASS(Shape2D); GDREGISTER_CLASS(WorldBoundaryShape2D); @@ -854,13 +854,13 @@ void register_scene_types() { GDREGISTER_CLASS(NavigationAgent2D); GDREGISTER_CLASS(NavigationObstacle2D); - OS::get_singleton()->yield(); //may take time to init + OS::get_singleton()->yield(); // may take time to init GDREGISTER_VIRTUAL_CLASS(SceneState); GDREGISTER_CLASS(PackedScene); GDREGISTER_CLASS(SceneTree); - GDREGISTER_VIRTUAL_CLASS(SceneTreeTimer); //sorry, you can't create it + GDREGISTER_VIRTUAL_CLASS(SceneTreeTimer); // sorry, you can't create it #ifndef DISABLE_DEPRECATED // Dropped in 4.0, near approximation. @@ -1008,7 +1008,7 @@ void register_scene_types() { #endif /* DISABLE_DEPRECATED */ - OS::get_singleton()->yield(); //may take time to init + OS::get_singleton()->yield(); // may take time to init for (int i = 0; i < 20; i++) { GLOBAL_DEF_BASIC(vformat("layer_names/2d_render/layer_%d", i + 1), ""); @@ -1086,7 +1086,7 @@ void unregister_scene_types() { ResourceLoader::remove_resource_format_loader(resource_loader_shader); resource_loader_shader.unref(); - //StandardMaterial3D is not initialised when 3D is disabled, so it shouldn't be cleaned up either + // StandardMaterial3D is not initialised when 3D is disabled, so it shouldn't be cleaned up either #ifndef _3D_DISABLED BaseMaterial3D::finish_shaders(); #endif // _3D_DISABLED diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 54bb7a82cf..9fdfd493c1 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -785,30 +785,30 @@ void fill_default_theme(Ref &theme, const Ref &default_font, const theme->set_constant("icon_separation", "TabContainer", 4 * scale); theme->set_constant("outline_size", "TabContainer", 0); - // Tabs - - theme->set_stylebox("tab_selected", "Tabs", sb_expand(make_stylebox(tab_current_png, 4, 3, 4, 1, 16, 3, 16, 2), 2, 2, 2, 2)); - theme->set_stylebox("tab_unselected", "Tabs", sb_expand(make_stylebox(tab_behind_png, 5, 4, 5, 1, 16, 5, 16, 2), 3, 3, 3, 3)); - theme->set_stylebox("tab_disabled", "Tabs", sb_expand(make_stylebox(tab_disabled_png, 5, 5, 5, 1, 16, 6, 16, 4), 3, 0, 3, 3)); - theme->set_stylebox("close_bg_pressed", "Tabs", make_stylebox(button_pressed_png, 4, 4, 4, 4)); - theme->set_stylebox("close_bg_highlight", "Tabs", make_stylebox(button_normal_png, 4, 4, 4, 4)); - - theme->set_icon("increment", "Tabs", make_icon(scroll_button_right_png)); - theme->set_icon("increment_highlight", "Tabs", make_icon(scroll_button_right_hl_png)); - theme->set_icon("decrement", "Tabs", make_icon(scroll_button_left_png)); - theme->set_icon("decrement_highlight", "Tabs", make_icon(scroll_button_left_hl_png)); - theme->set_icon("close", "Tabs", make_icon(tab_close_png)); - - theme->set_font("font", "Tabs", Ref()); - theme->set_font_size("font_size", "Tabs", -1); - - theme->set_color("font_selected_color", "Tabs", control_font_hover_color); - theme->set_color("font_unselected_color", "Tabs", control_font_low_color); - theme->set_color("font_disabled_color", "Tabs", control_font_disabled_color); - theme->set_color("font_outline_color", "Tabs", Color(1, 1, 1)); - - theme->set_constant("hseparation", "Tabs", 4 * scale); - theme->set_constant("outline_size", "Tabs", 0); + // TabBar + + theme->set_stylebox("tab_selected", "TabBar", sb_expand(make_stylebox(tab_current_png, 4, 3, 4, 1, 16, 3, 16, 2), 2, 2, 2, 2)); + theme->set_stylebox("tab_unselected", "TabBar", sb_expand(make_stylebox(tab_behind_png, 5, 4, 5, 1, 16, 5, 16, 2), 3, 3, 3, 3)); + theme->set_stylebox("tab_disabled", "TabBar", sb_expand(make_stylebox(tab_disabled_png, 5, 5, 5, 1, 16, 6, 16, 4), 3, 0, 3, 3)); + theme->set_stylebox("close_bg_pressed", "TabBar", make_stylebox(button_pressed_png, 4, 4, 4, 4)); + theme->set_stylebox("close_bg_highlight", "TabBar", make_stylebox(button_normal_png, 4, 4, 4, 4)); + + theme->set_icon("increment", "TabBar", make_icon(scroll_button_right_png)); + theme->set_icon("increment_highlight", "TabBar", make_icon(scroll_button_right_hl_png)); + theme->set_icon("decrement", "TabBar", make_icon(scroll_button_left_png)); + theme->set_icon("decrement_highlight", "TabBar", make_icon(scroll_button_left_hl_png)); + theme->set_icon("close", "TabBar", make_icon(tab_close_png)); + + theme->set_font("font", "TabBar", Ref()); + theme->set_font_size("font_size", "TabBar", -1); + + theme->set_color("font_selected_color", "TabBar", control_font_hover_color); + theme->set_color("font_unselected_color", "TabBar", control_font_low_color); + theme->set_color("font_disabled_color", "TabBar", control_font_disabled_color); + theme->set_color("font_outline_color", "TabBar", Color(1, 1, 1)); + + theme->set_constant("hseparation", "TabBar", 4 * scale); + theme->set_constant("outline_size", "TabBar", 0); // Separators -- cgit v1.2.3