Path: blob/master/editor/docks/dock_tab_container.cpp
20934 views
/**************************************************************************/1/* dock_tab_container.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "dock_tab_container.h"3132#include "editor/docks/editor_dock.h"33#include "editor/docks/editor_dock_manager.h"34#include "editor/editor_node.h"35#include "editor/editor_string_names.h"36#include "editor/settings/editor_settings.h"37#include "editor/themes/editor_scale.h"38#include "scene/resources/style_box_flat.h"3940bool EditorDockDragHint::can_drop_data(const Point2 &p_point, const Variant &p_data) const {41return can_drop_dock;42}4344void EditorDockDragHint::drop_data(const Point2 &p_point, const Variant &p_data) {45// Drop dock into last spot if not over tabbar.46if (drop_tabbar->get_rect().has_point(p_point)) {47drop_tabbar->_handle_drop_data("tab_container_tab", p_point, p_data, callable_mp(this, &EditorDockDragHint::_drag_move_tab), callable_mp(this, &EditorDockDragHint::_drag_move_tab_from));48} else {49EditorDockManager *dock_manager = EditorDockManager::get_singleton();50dock_manager->_move_dock(dock_manager->_get_dock_tab_dragged(), dock_container, drop_tabbar->get_tab_count());51}52}5354void EditorDockDragHint::_drag_move_tab(int p_from_index, int p_to_index) {55dock_container->get_dock(p_from_index)->set_tab_index(p_to_index, true);56}5758void EditorDockDragHint::_drag_move_tab_from(TabBar *p_from_tabbar, int p_from_index, int p_to_index) {59EditorDockManager *dock_manager = EditorDockManager::get_singleton();60dock_manager->_move_dock(dock_manager->_get_dock_tab_dragged(), dock_container, p_to_index);61}6263void EditorDockDragHint::gui_input(const Ref<InputEvent> &p_event) {64ERR_FAIL_COND(p_event.is_null());6566Ref<InputEventMouseMotion> mm = p_event;67if (mm.is_valid()) {68Point2 pos = mm->get_position();6970// Redraw when inside the tabbar and just exited.71if (mouse_inside_tabbar) {72queue_redraw();73}74mouse_inside_tabbar = drop_tabbar->get_rect().has_point(pos);75}76}7778void EditorDockDragHint::set_slot(DockTabContainer *p_slot) {79dock_container = p_slot;80drop_tabbar = p_slot->get_tab_bar();81}8283void EditorDockDragHint::_notification(int p_what) {84switch (p_what) {85case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {86if (EditorSettings::get_singleton()->check_changed_settings_in_group("interface/theme")) {87dock_drop_highlight->set_corner_radius_all(EDSCALE * EDITOR_GET("interface/theme/corner_radius").operator int());88if (mouse_inside) {89queue_redraw();90}91}92} break;9394case NOTIFICATION_THEME_CHANGED: {95valid_drop_color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));96} break;9798case NOTIFICATION_MOUSE_ENTER:99case NOTIFICATION_MOUSE_EXIT: {100mouse_inside = p_what == NOTIFICATION_MOUSE_ENTER;101queue_redraw();102} break;103104case NOTIFICATION_DRAG_BEGIN: {105EditorDock *dragged_dock = EditorDockManager::get_singleton()->_get_dock_tab_dragged();106if (!dragged_dock) {107return;108}109110can_drop_dock = dragged_dock->get_available_layouts() & dock_container->layout;111112dock_drop_highlight->set_border_color(valid_drop_color);113dock_drop_highlight->set_bg_color(valid_drop_color * Color(1, 1, 1, 0.1));114} break;115case NOTIFICATION_DRAG_END: {116EditorDockManager::get_singleton()->_dock_drag_stopped();117can_drop_dock = false;118mouse_inside = false;119hide();120} break;121122case NOTIFICATION_DRAW: {123if (!mouse_inside || !can_drop_dock) {124return;125}126127// Draw highlights around docks that can be dropped.128Rect2 dock_rect = Rect2(Point2(), get_size()).grow(2 * EDSCALE);129draw_style_box(dock_drop_highlight, dock_rect);130131// Only display tabbar hint if the mouse is over the tabbar.132if (drop_tabbar->get_global_rect().has_point(get_global_mouse_position())) {133draw_set_transform(drop_tabbar->get_position()); // The TabBar isn't always on top.134drop_tabbar->_draw_tab_drop(get_canvas_item());135}136} break;137}138}139140EditorDockDragHint::EditorDockDragHint() {141set_as_top_level(true);142hide();143144dock_drop_highlight.instantiate();145dock_drop_highlight->set_corner_radius_all(EDSCALE * EDITOR_GET("interface/theme/corner_radius").operator int());146dock_drop_highlight->set_border_width_all(Math::round(2 * EDSCALE));147}148149void DockTabContainer::_pre_popup() {150dock_context_popup->set_dock(get_dock(get_current_tab()));151}152153void DockTabContainer::_tab_rmb_clicked(int p_tab_idx) {154EditorDock *hovered_dock = get_dock(p_tab_idx);155if (!hovered_dock) {156return;157}158159// Right click context menu.160dock_context_popup->set_dock(hovered_dock);161dock_context_popup->set_position(get_tab_bar()->get_screen_position() + get_tab_bar()->get_local_mouse_position());162dock_context_popup->popup();163}164165void DockTabContainer::_notification(int p_what) {166if (p_what == NOTIFICATION_POSTINITIALIZE) {167connect("pre_popup_pressed", callable_mp(this, &DockTabContainer::_pre_popup));168connect("child_order_changed", callable_mp(this, &DockTabContainer::update_visibility));169}170}171172void DockTabContainer::update_visibility() {173// Hide the dock container if there are no tabs.174set_visible(EditorDockManager::get_singleton()->are_docks_visible() && get_tab_count() > 0);175}176177DockTabContainer::TabStyle DockTabContainer::get_tab_style() const {178return (TabStyle)EDITOR_GET("interface/editor/dock_tab_style").operator int();179}180181bool DockTabContainer::can_switch_dock() const {182return EditorDockManager::get_singleton()->are_docks_visible();183}184185void DockTabContainer::save_docks_to_config(Ref<ConfigFile> p_layout, const String &p_section) {186PackedStringArray names;187names.reserve_exact(get_tab_count());188for (int i = 0; i < get_tab_count(); i++) {189const String name = get_dock(i)->get_effective_layout_key();190names.append(name);191}192193const String config_key = DockTabContainer::get_config_key(dock_slot);194if (!names.is_empty()) {195p_layout->set_value(p_section, config_key, String(",").join(names));196} else if (p_layout->has_section_key(p_section, config_key)) {197p_layout->erase_section_key(p_section, config_key);198}199200const String tab_key = config_key + "_selected_tab_idx";201int selected_tab_idx = get_current_tab();202if (selected_tab_idx >= 0) {203p_layout->set_value(p_section, tab_key, selected_tab_idx);204} else if (p_layout->has_section_key(p_section, tab_key)) {205p_layout->erase_section_key(p_section, tab_key);206}207}208209void DockTabContainer::load_selected_tab(int p_idx) {210EditorDock *selected_dock = get_dock(p_idx);211if (!selected_dock) {212return;213}214set_block_signals(true);215set_current_tab(p_idx);216set_block_signals(false);217}218219void DockTabContainer::set_dock_context_popup(DockContextPopup *p_popup) {220dock_context_popup = p_popup;221set_popup(dock_context_popup);222}223224void DockTabContainer::move_dock_index(EditorDock *p_dock, int p_to_index, bool p_set_current) {225set_block_signals(true);226int target_index = CLAMP(p_to_index, 0, get_tab_count() - 1);227move_child(p_dock, get_dock(target_index)->get_index(false));228229if (p_set_current) {230set_current_tab(target_index);231}232set_block_signals(false);233}234235EditorDock *DockTabContainer::get_dock(int p_idx) const {236return Object::cast_to<EditorDock>(get_tab_control(p_idx));237}238239void DockTabContainer::show_drag_hint() {240if (!is_visible_in_tree()) {241return;242}243drag_hint->set_rect(get_global_rect());244drag_hint->show();245}246247DockTabContainer::DockTabContainer(EditorDock::DockSlot p_slot) {248ERR_FAIL_INDEX(p_slot, EditorDock::DOCK_SLOT_MAX);249dock_slot = p_slot;250251set_drag_to_rearrange_enabled(true);252set_tabs_rearrange_group(1);253hide();254255drag_hint = memnew(EditorDockDragHint);256drag_hint->set_slot(this);257drag_hint->hide();258EditorNode::get_singleton()->get_gui_base()->add_child(drag_hint);259260get_tab_bar()->set_switch_on_release(true);261get_tab_bar()->connect("tab_rmb_clicked", callable_mp(this, &DockTabContainer::_tab_rmb_clicked));262}263264SideDockTabContainer::SideDockTabContainer(EditorDock::DockSlot p_slot) :265DockTabContainer(p_slot) {266set_custom_minimum_size(Size2(170 * EDSCALE, 0));267set_v_size_flags(Control::SIZE_EXPAND_FILL);268set_use_hidden_tabs_for_min_size(true);269}270271272