| 1 | /**************************************************************************/ |
| 2 | /* editor_scene_tabs.cpp */ |
| 3 | /**************************************************************************/ |
| 4 | /* This file is part of: */ |
| 5 | /* GODOT ENGINE */ |
| 6 | /* https://godotengine.org */ |
| 7 | /**************************************************************************/ |
| 8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
| 9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
| 10 | /* */ |
| 11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
| 12 | /* a copy of this software and associated documentation files (the */ |
| 13 | /* "Software"), to deal in the Software without restriction, including */ |
| 14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
| 15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
| 16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
| 17 | /* the following conditions: */ |
| 18 | /* */ |
| 19 | /* The above copyright notice and this permission notice shall be */ |
| 20 | /* included in all copies or substantial portions of the Software. */ |
| 21 | /* */ |
| 22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
| 23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
| 24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
| 25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
| 26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
| 27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
| 28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
| 29 | /**************************************************************************/ |
| 30 | |
| 31 | #include "editor_scene_tabs.h" |
| 32 | |
| 33 | #include "editor/editor_node.h" |
| 34 | #include "editor/editor_resource_preview.h" |
| 35 | #include "editor/editor_scale.h" |
| 36 | #include "editor/editor_settings.h" |
| 37 | #include "editor/editor_string_names.h" |
| 38 | #include "editor/editor_undo_redo_manager.h" |
| 39 | #include "editor/inspector_dock.h" |
| 40 | #include "scene/gui/box_container.h" |
| 41 | #include "scene/gui/button.h" |
| 42 | #include "scene/gui/panel.h" |
| 43 | #include "scene/gui/panel_container.h" |
| 44 | #include "scene/gui/popup_menu.h" |
| 45 | #include "scene/gui/tab_bar.h" |
| 46 | #include "scene/gui/texture_rect.h" |
| 47 | |
| 48 | EditorSceneTabs *EditorSceneTabs::singleton = nullptr; |
| 49 | |
| 50 | void EditorSceneTabs::_notification(int p_what) { |
| 51 | switch (p_what) { |
| 52 | case NOTIFICATION_THEME_CHANGED: { |
| 53 | tabbar_panel->add_theme_style_override("panel" , get_theme_stylebox(SNAME("tabbar_background" ), SNAME("TabContainer" ))); |
| 54 | scene_tabs->add_theme_constant_override("icon_max_width" , get_theme_constant(SNAME("class_icon_size" ), EditorStringName(Editor))); |
| 55 | |
| 56 | scene_tab_add->set_icon(get_editor_theme_icon(SNAME("Add" ))); |
| 57 | scene_tab_add->add_theme_color_override("icon_normal_color" , Color(0.6f, 0.6f, 0.6f, 0.8f)); |
| 58 | |
| 59 | scene_tab_add_ph->set_custom_minimum_size(scene_tab_add->get_minimum_size()); |
| 60 | } break; |
| 61 | |
| 62 | case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { |
| 63 | scene_tabs->set_tab_close_display_policy((TabBar::CloseButtonDisplayPolicy)EDITOR_GET("interface/scene_tabs/display_close_button" ).operator int()); |
| 64 | scene_tabs->set_max_tab_width(int(EDITOR_GET("interface/scene_tabs/maximum_width" )) * EDSCALE); |
| 65 | } break; |
| 66 | } |
| 67 | } |
| 68 | |
| 69 | void EditorSceneTabs::_scene_tab_changed(int p_tab) { |
| 70 | tab_preview_panel->hide(); |
| 71 | |
| 72 | emit_signal("tab_changed" , p_tab); |
| 73 | } |
| 74 | |
| 75 | void EditorSceneTabs::_scene_tab_script_edited(int p_tab) { |
| 76 | Ref<Script> scr = EditorNode::get_editor_data().get_scene_root_script(p_tab); |
| 77 | if (scr.is_valid()) { |
| 78 | InspectorDock::get_singleton()->edit_resource(scr); |
| 79 | } |
| 80 | } |
| 81 | |
| 82 | void EditorSceneTabs::_scene_tab_closed(int p_tab) { |
| 83 | emit_signal("tab_closed" , p_tab); |
| 84 | } |
| 85 | |
| 86 | void EditorSceneTabs::_scene_tab_hovered(int p_tab) { |
| 87 | if (!bool(EDITOR_GET("interface/scene_tabs/show_thumbnail_on_hover" ))) { |
| 88 | return; |
| 89 | } |
| 90 | int current_tab = scene_tabs->get_current_tab(); |
| 91 | |
| 92 | if (p_tab == current_tab || p_tab < 0) { |
| 93 | tab_preview_panel->hide(); |
| 94 | } else { |
| 95 | String path = EditorNode::get_editor_data().get_scene_path(p_tab); |
| 96 | if (!path.is_empty()) { |
| 97 | EditorResourcePreview::get_singleton()->queue_resource_preview(path, this, "_tab_preview_done" , p_tab); |
| 98 | } |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | void EditorSceneTabs::_scene_tab_exit() { |
| 103 | tab_preview_panel->hide(); |
| 104 | } |
| 105 | |
| 106 | void EditorSceneTabs::_scene_tab_input(const Ref<InputEvent> &p_input) { |
| 107 | int tab_id = scene_tabs->get_hovered_tab(); |
| 108 | Ref<InputEventMouseButton> mb = p_input; |
| 109 | |
| 110 | if (mb.is_valid()) { |
| 111 | if (tab_id >= 0) { |
| 112 | if (mb->get_button_index() == MouseButton::MIDDLE && mb->is_pressed()) { |
| 113 | _scene_tab_closed(tab_id); |
| 114 | } |
| 115 | } else if (mb->get_button_index() == MouseButton::LEFT && mb->is_double_click()) { |
| 116 | int tab_buttons = 0; |
| 117 | if (scene_tabs->get_offset_buttons_visible()) { |
| 118 | tab_buttons = get_theme_icon(SNAME("increment" ), SNAME("TabBar" ))->get_width() + get_theme_icon(SNAME("decrement" ), SNAME("TabBar" ))->get_width(); |
| 119 | } |
| 120 | |
| 121 | if ((is_layout_rtl() && mb->get_position().x > tab_buttons) || (!is_layout_rtl() && mb->get_position().x < scene_tabs->get_size().width - tab_buttons)) { |
| 122 | EditorNode::get_singleton()->trigger_menu_option(EditorNode::FILE_NEW_SCENE, true); |
| 123 | } |
| 124 | } |
| 125 | if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) { |
| 126 | // Context menu. |
| 127 | _update_context_menu(); |
| 128 | |
| 129 | scene_tabs_context_menu->set_position(scene_tabs->get_screen_position() + mb->get_position()); |
| 130 | scene_tabs_context_menu->reset_size(); |
| 131 | scene_tabs_context_menu->popup(); |
| 132 | } |
| 133 | } |
| 134 | } |
| 135 | |
| 136 | void EditorSceneTabs::_reposition_active_tab(int p_to_index) { |
| 137 | EditorNode::get_editor_data().move_edited_scene_to_index(p_to_index); |
| 138 | update_scene_tabs(); |
| 139 | } |
| 140 | |
| 141 | void EditorSceneTabs::() { |
| 142 | scene_tabs_context_menu->clear(); |
| 143 | scene_tabs_context_menu->reset_size(); |
| 144 | |
| 145 | int tab_id = scene_tabs->get_hovered_tab(); |
| 146 | bool no_root_node = !EditorNode::get_editor_data().get_edited_scene_root(tab_id); |
| 147 | |
| 148 | scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/new_scene" ), EditorNode::FILE_NEW_SCENE); |
| 149 | if (tab_id >= 0) { |
| 150 | scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_scene" ), EditorNode::FILE_SAVE_SCENE); |
| 151 | _disable_menu_option_if(EditorNode::FILE_SAVE_SCENE, no_root_node); |
| 152 | scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_scene_as" ), EditorNode::FILE_SAVE_AS_SCENE); |
| 153 | _disable_menu_option_if(EditorNode::FILE_SAVE_AS_SCENE, no_root_node); |
| 154 | } |
| 155 | |
| 156 | scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_all_scenes" ), EditorNode::FILE_SAVE_ALL_SCENES); |
| 157 | bool can_save_all_scenes = false; |
| 158 | for (int i = 0; i < EditorNode::get_editor_data().get_edited_scene_count(); i++) { |
| 159 | if (!EditorNode::get_editor_data().get_scene_path(i).is_empty() && EditorNode::get_editor_data().get_edited_scene_root(i)) { |
| 160 | can_save_all_scenes = true; |
| 161 | break; |
| 162 | } |
| 163 | } |
| 164 | _disable_menu_option_if(EditorNode::FILE_SAVE_ALL_SCENES, !can_save_all_scenes); |
| 165 | |
| 166 | if (tab_id >= 0) { |
| 167 | scene_tabs_context_menu->add_separator(); |
| 168 | scene_tabs_context_menu->add_item(TTR("Show in FileSystem" ), EditorNode::FILE_SHOW_IN_FILESYSTEM); |
| 169 | _disable_menu_option_if(EditorNode::FILE_SHOW_IN_FILESYSTEM, !ResourceLoader::exists(EditorNode::get_editor_data().get_scene_path(tab_id))); |
| 170 | scene_tabs_context_menu->add_item(TTR("Play This Scene" ), EditorNode::FILE_RUN_SCENE); |
| 171 | _disable_menu_option_if(EditorNode::FILE_RUN_SCENE, no_root_node); |
| 172 | |
| 173 | scene_tabs_context_menu->add_separator(); |
| 174 | scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/close_scene" ), EditorNode::FILE_CLOSE); |
| 175 | scene_tabs_context_menu->set_item_text(scene_tabs_context_menu->get_item_index(EditorNode::FILE_CLOSE), TTR("Close Tab" )); |
| 176 | scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/reopen_closed_scene" ), EditorNode::FILE_OPEN_PREV); |
| 177 | scene_tabs_context_menu->set_item_text(scene_tabs_context_menu->get_item_index(EditorNode::FILE_OPEN_PREV), TTR("Undo Close Tab" )); |
| 178 | _disable_menu_option_if(EditorNode::FILE_OPEN_PREV, !EditorNode::get_singleton()->has_previous_scenes()); |
| 179 | scene_tabs_context_menu->add_item(TTR("Close Other Tabs" ), EditorNode::FILE_CLOSE_OTHERS); |
| 180 | _disable_menu_option_if(EditorNode::FILE_CLOSE_OTHERS, EditorNode::get_editor_data().get_edited_scene_count() <= 1); |
| 181 | scene_tabs_context_menu->add_item(TTR("Close Tabs to the Right" ), EditorNode::FILE_CLOSE_RIGHT); |
| 182 | _disable_menu_option_if(EditorNode::FILE_CLOSE_RIGHT, EditorNode::get_editor_data().get_edited_scene_count() == tab_id + 1); |
| 183 | scene_tabs_context_menu->add_item(TTR("Close All Tabs" ), EditorNode::FILE_CLOSE_ALL); |
| 184 | } |
| 185 | } |
| 186 | |
| 187 | void EditorSceneTabs::(int p_option, bool p_condition) { |
| 188 | if (p_condition) { |
| 189 | scene_tabs_context_menu->set_item_disabled(scene_tabs_context_menu->get_item_index(p_option), true); |
| 190 | } |
| 191 | } |
| 192 | |
| 193 | // TODO: This REALLY should be done in a better way than replacing all tabs after almost EVERY action. |
| 194 | void EditorSceneTabs::update_scene_tabs() { |
| 195 | tab_preview_panel->hide(); |
| 196 | |
| 197 | bool show_rb = EDITOR_GET("interface/scene_tabs/show_script_button" ); |
| 198 | |
| 199 | if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU)) { |
| 200 | DisplayServer::get_singleton()->global_menu_clear("_dock" ); |
| 201 | } |
| 202 | |
| 203 | // Get all scene names, which may be ambiguous. |
| 204 | Vector<String> disambiguated_scene_names; |
| 205 | Vector<String> full_path_names; |
| 206 | for (int i = 0; i < EditorNode::get_editor_data().get_edited_scene_count(); i++) { |
| 207 | disambiguated_scene_names.append(EditorNode::get_editor_data().get_scene_title(i)); |
| 208 | full_path_names.append(EditorNode::get_editor_data().get_scene_path(i)); |
| 209 | } |
| 210 | |
| 211 | EditorNode::disambiguate_filenames(full_path_names, disambiguated_scene_names); |
| 212 | |
| 213 | // Workaround to ignore the tab_changed signal from the first added tab. |
| 214 | scene_tabs->disconnect("tab_changed" , callable_mp(this, &EditorSceneTabs::_scene_tab_changed)); |
| 215 | |
| 216 | scene_tabs->clear_tabs(); |
| 217 | Ref<Texture2D> script_icon = get_editor_theme_icon(SNAME("Script" )); |
| 218 | for (int i = 0; i < EditorNode::get_editor_data().get_edited_scene_count(); i++) { |
| 219 | Node *type_node = EditorNode::get_editor_data().get_edited_scene_root(i); |
| 220 | Ref<Texture2D> icon; |
| 221 | if (type_node) { |
| 222 | icon = EditorNode::get_singleton()->get_object_icon(type_node, "Node" ); |
| 223 | } |
| 224 | |
| 225 | bool unsaved = EditorUndoRedoManager::get_singleton()->is_history_unsaved(EditorNode::get_editor_data().get_scene_history_id(i)); |
| 226 | scene_tabs->add_tab(disambiguated_scene_names[i] + (unsaved ? "(*)" : "" ), icon); |
| 227 | |
| 228 | if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU)) { |
| 229 | DisplayServer::get_singleton()->global_menu_add_item("_dock" , EditorNode::get_editor_data().get_scene_title(i) + (unsaved ? "(*)" : "" ), callable_mp(this, &EditorSceneTabs::_global_menu_scene), Callable(), i); |
| 230 | } |
| 231 | |
| 232 | if (show_rb && EditorNode::get_editor_data().get_scene_root_script(i).is_valid()) { |
| 233 | scene_tabs->set_tab_button_icon(i, script_icon); |
| 234 | } |
| 235 | } |
| 236 | |
| 237 | if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU)) { |
| 238 | DisplayServer::get_singleton()->global_menu_add_separator("_dock" ); |
| 239 | DisplayServer::get_singleton()->global_menu_add_item("_dock" , TTR("New Window" ), callable_mp(this, &EditorSceneTabs::_global_menu_new_window)); |
| 240 | } |
| 241 | |
| 242 | if (scene_tabs->get_tab_count() > 0) { |
| 243 | scene_tabs->set_current_tab(EditorNode::get_editor_data().get_edited_scene()); |
| 244 | } |
| 245 | |
| 246 | const Size2 add_button_size = Size2(scene_tab_add->get_size().x, scene_tabs->get_size().y); |
| 247 | if (scene_tabs->get_offset_buttons_visible()) { |
| 248 | // Move the add button to a fixed position. |
| 249 | if (scene_tab_add->get_parent() == scene_tabs) { |
| 250 | scene_tabs->remove_child(scene_tab_add); |
| 251 | scene_tab_add_ph->add_child(scene_tab_add); |
| 252 | scene_tab_add->set_rect(Rect2(Point2(), add_button_size)); |
| 253 | } |
| 254 | } else { |
| 255 | // Move the add button to be after the last tab. |
| 256 | if (scene_tab_add->get_parent() == scene_tab_add_ph) { |
| 257 | scene_tab_add_ph->remove_child(scene_tab_add); |
| 258 | scene_tabs->add_child(scene_tab_add); |
| 259 | } |
| 260 | |
| 261 | if (scene_tabs->get_tab_count() == 0) { |
| 262 | scene_tab_add->set_rect(Rect2(Point2(), add_button_size)); |
| 263 | return; |
| 264 | } |
| 265 | |
| 266 | Rect2 last_tab = scene_tabs->get_tab_rect(scene_tabs->get_tab_count() - 1); |
| 267 | int hsep = scene_tabs->get_theme_constant(SNAME("h_separation" )); |
| 268 | if (scene_tabs->is_layout_rtl()) { |
| 269 | scene_tab_add->set_rect(Rect2(Point2(last_tab.position.x - add_button_size.x - hsep, last_tab.position.y), add_button_size)); |
| 270 | } else { |
| 271 | scene_tab_add->set_rect(Rect2(Point2(last_tab.position.x + last_tab.size.width + hsep, last_tab.position.y), add_button_size)); |
| 272 | } |
| 273 | } |
| 274 | |
| 275 | // Reconnect after everything is done. |
| 276 | scene_tabs->connect("tab_changed" , callable_mp(this, &EditorSceneTabs::_scene_tab_changed)); |
| 277 | } |
| 278 | |
| 279 | void EditorSceneTabs::_tab_preview_done(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, const Variant &p_udata) { |
| 280 | int p_tab = p_udata; |
| 281 | if (p_preview.is_valid()) { |
| 282 | tab_preview->set_texture(p_preview); |
| 283 | |
| 284 | Rect2 rect = scene_tabs->get_tab_rect(p_tab); |
| 285 | rect.position += scene_tabs->get_global_position(); |
| 286 | tab_preview_panel->set_global_position(rect.position + Vector2(0, rect.size.height)); |
| 287 | tab_preview_panel->show(); |
| 288 | } |
| 289 | } |
| 290 | |
| 291 | void EditorSceneTabs::(const Variant &p_tag) { |
| 292 | int idx = (int)p_tag; |
| 293 | scene_tabs->set_current_tab(idx); |
| 294 | } |
| 295 | |
| 296 | void EditorSceneTabs::(const Variant &p_tag) { |
| 297 | if (OS::get_singleton()->get_main_loop()) { |
| 298 | List<String> args; |
| 299 | args.push_back("-p" ); |
| 300 | OS::get_singleton()->create_instance(args); |
| 301 | } |
| 302 | } |
| 303 | |
| 304 | void EditorSceneTabs::shortcut_input(const Ref<InputEvent> &p_event) { |
| 305 | ERR_FAIL_COND(p_event.is_null()); |
| 306 | |
| 307 | Ref<InputEventKey> k = p_event; |
| 308 | if ((k.is_valid() && k->is_pressed() && !k->is_echo()) || Object::cast_to<InputEventShortcut>(*p_event)) { |
| 309 | if (ED_IS_SHORTCUT("editor/next_tab" , p_event)) { |
| 310 | int next_tab = EditorNode::get_editor_data().get_edited_scene() + 1; |
| 311 | next_tab %= EditorNode::get_editor_data().get_edited_scene_count(); |
| 312 | _scene_tab_changed(next_tab); |
| 313 | } |
| 314 | if (ED_IS_SHORTCUT("editor/prev_tab" , p_event)) { |
| 315 | int next_tab = EditorNode::get_editor_data().get_edited_scene() - 1; |
| 316 | next_tab = next_tab >= 0 ? next_tab : EditorNode::get_editor_data().get_edited_scene_count() - 1; |
| 317 | _scene_tab_changed(next_tab); |
| 318 | } |
| 319 | } |
| 320 | } |
| 321 | |
| 322 | void EditorSceneTabs::(Button *p_button) { |
| 323 | tabbar_container->add_child(p_button); |
| 324 | } |
| 325 | |
| 326 | void EditorSceneTabs::set_current_tab(int p_tab) { |
| 327 | scene_tabs->set_current_tab(p_tab); |
| 328 | } |
| 329 | |
| 330 | int EditorSceneTabs::get_current_tab() const { |
| 331 | return scene_tabs->get_current_tab(); |
| 332 | } |
| 333 | |
| 334 | void EditorSceneTabs::_bind_methods() { |
| 335 | ADD_SIGNAL(MethodInfo("tab_changed" , PropertyInfo(Variant::INT, "tab_index" ))); |
| 336 | ADD_SIGNAL(MethodInfo("tab_closed" , PropertyInfo(Variant::INT, "tab_index" ))); |
| 337 | |
| 338 | ClassDB::bind_method("_tab_preview_done" , &EditorSceneTabs::_tab_preview_done); |
| 339 | } |
| 340 | |
| 341 | EditorSceneTabs::EditorSceneTabs() { |
| 342 | singleton = this; |
| 343 | |
| 344 | tabbar_panel = memnew(PanelContainer); |
| 345 | add_child(tabbar_panel); |
| 346 | tabbar_container = memnew(HBoxContainer); |
| 347 | tabbar_panel->add_child(tabbar_container); |
| 348 | |
| 349 | scene_tabs = memnew(TabBar); |
| 350 | scene_tabs->set_select_with_rmb(true); |
| 351 | scene_tabs->add_tab("unsaved" ); |
| 352 | scene_tabs->set_tab_close_display_policy((TabBar::CloseButtonDisplayPolicy)EDITOR_GET("interface/scene_tabs/display_close_button" ).operator int()); |
| 353 | scene_tabs->set_max_tab_width(int(EDITOR_GET("interface/scene_tabs/maximum_width" )) * EDSCALE); |
| 354 | scene_tabs->set_drag_to_rearrange_enabled(true); |
| 355 | scene_tabs->set_auto_translate(false); |
| 356 | scene_tabs->set_h_size_flags(Control::SIZE_EXPAND_FILL); |
| 357 | tabbar_container->add_child(scene_tabs); |
| 358 | |
| 359 | scene_tabs->connect("tab_changed" , callable_mp(this, &EditorSceneTabs::_scene_tab_changed)); |
| 360 | scene_tabs->connect("tab_button_pressed" , callable_mp(this, &EditorSceneTabs::_scene_tab_script_edited)); |
| 361 | scene_tabs->connect("tab_close_pressed" , callable_mp(this, &EditorSceneTabs::_scene_tab_closed)); |
| 362 | scene_tabs->connect("tab_hovered" , callable_mp(this, &EditorSceneTabs::_scene_tab_hovered)); |
| 363 | scene_tabs->connect("mouse_exited" , callable_mp(this, &EditorSceneTabs::_scene_tab_exit)); |
| 364 | scene_tabs->connect("gui_input" , callable_mp(this, &EditorSceneTabs::_scene_tab_input)); |
| 365 | scene_tabs->connect("active_tab_rearranged" , callable_mp(this, &EditorSceneTabs::_reposition_active_tab)); |
| 366 | scene_tabs->connect("resized" , callable_mp(this, &EditorSceneTabs::update_scene_tabs)); |
| 367 | |
| 368 | scene_tabs_context_menu = memnew(PopupMenu); |
| 369 | tabbar_container->add_child(scene_tabs_context_menu); |
| 370 | scene_tabs_context_menu->connect("id_pressed" , callable_mp(EditorNode::get_singleton(), &EditorNode::trigger_menu_option).bind(false)); |
| 371 | |
| 372 | scene_tab_add = memnew(Button); |
| 373 | scene_tab_add->set_flat(true); |
| 374 | scene_tab_add->set_tooltip_text(TTR("Add a new scene." )); |
| 375 | scene_tabs->add_child(scene_tab_add); |
| 376 | scene_tab_add->connect("pressed" , callable_mp(EditorNode::get_singleton(), &EditorNode::trigger_menu_option).bind(EditorNode::FILE_NEW_SCENE, false)); |
| 377 | |
| 378 | scene_tab_add_ph = memnew(Control); |
| 379 | scene_tab_add_ph->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); |
| 380 | scene_tab_add_ph->set_custom_minimum_size(scene_tab_add->get_minimum_size()); |
| 381 | tabbar_container->add_child(scene_tab_add_ph); |
| 382 | |
| 383 | // On-hover tab preview. |
| 384 | |
| 385 | Control *tab_preview_anchor = memnew(Control); |
| 386 | tab_preview_anchor->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); |
| 387 | add_child(tab_preview_anchor); |
| 388 | |
| 389 | tab_preview_panel = memnew(Panel); |
| 390 | tab_preview_panel->set_size(Size2(100, 100) * EDSCALE); |
| 391 | tab_preview_panel->hide(); |
| 392 | tab_preview_panel->set_self_modulate(Color(1, 1, 1, 0.7)); |
| 393 | tab_preview_anchor->add_child(tab_preview_panel); |
| 394 | |
| 395 | tab_preview = memnew(TextureRect); |
| 396 | tab_preview->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED); |
| 397 | tab_preview->set_size(Size2(96, 96) * EDSCALE); |
| 398 | tab_preview->set_position(Point2(2, 2) * EDSCALE); |
| 399 | tab_preview_panel->add_child(tab_preview); |
| 400 | } |
| 401 | |