Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/editor/docks/dock_tab_container.cpp
20934 views
1
/**************************************************************************/
2
/* dock_tab_container.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 "dock_tab_container.h"
32
33
#include "editor/docks/editor_dock.h"
34
#include "editor/docks/editor_dock_manager.h"
35
#include "editor/editor_node.h"
36
#include "editor/editor_string_names.h"
37
#include "editor/settings/editor_settings.h"
38
#include "editor/themes/editor_scale.h"
39
#include "scene/resources/style_box_flat.h"
40
41
bool EditorDockDragHint::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
42
return can_drop_dock;
43
}
44
45
void EditorDockDragHint::drop_data(const Point2 &p_point, const Variant &p_data) {
46
// Drop dock into last spot if not over tabbar.
47
if (drop_tabbar->get_rect().has_point(p_point)) {
48
drop_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));
49
} else {
50
EditorDockManager *dock_manager = EditorDockManager::get_singleton();
51
dock_manager->_move_dock(dock_manager->_get_dock_tab_dragged(), dock_container, drop_tabbar->get_tab_count());
52
}
53
}
54
55
void EditorDockDragHint::_drag_move_tab(int p_from_index, int p_to_index) {
56
dock_container->get_dock(p_from_index)->set_tab_index(p_to_index, true);
57
}
58
59
void EditorDockDragHint::_drag_move_tab_from(TabBar *p_from_tabbar, int p_from_index, int p_to_index) {
60
EditorDockManager *dock_manager = EditorDockManager::get_singleton();
61
dock_manager->_move_dock(dock_manager->_get_dock_tab_dragged(), dock_container, p_to_index);
62
}
63
64
void EditorDockDragHint::gui_input(const Ref<InputEvent> &p_event) {
65
ERR_FAIL_COND(p_event.is_null());
66
67
Ref<InputEventMouseMotion> mm = p_event;
68
if (mm.is_valid()) {
69
Point2 pos = mm->get_position();
70
71
// Redraw when inside the tabbar and just exited.
72
if (mouse_inside_tabbar) {
73
queue_redraw();
74
}
75
mouse_inside_tabbar = drop_tabbar->get_rect().has_point(pos);
76
}
77
}
78
79
void EditorDockDragHint::set_slot(DockTabContainer *p_slot) {
80
dock_container = p_slot;
81
drop_tabbar = p_slot->get_tab_bar();
82
}
83
84
void EditorDockDragHint::_notification(int p_what) {
85
switch (p_what) {
86
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
87
if (EditorSettings::get_singleton()->check_changed_settings_in_group("interface/theme")) {
88
dock_drop_highlight->set_corner_radius_all(EDSCALE * EDITOR_GET("interface/theme/corner_radius").operator int());
89
if (mouse_inside) {
90
queue_redraw();
91
}
92
}
93
} break;
94
95
case NOTIFICATION_THEME_CHANGED: {
96
valid_drop_color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
97
} break;
98
99
case NOTIFICATION_MOUSE_ENTER:
100
case NOTIFICATION_MOUSE_EXIT: {
101
mouse_inside = p_what == NOTIFICATION_MOUSE_ENTER;
102
queue_redraw();
103
} break;
104
105
case NOTIFICATION_DRAG_BEGIN: {
106
EditorDock *dragged_dock = EditorDockManager::get_singleton()->_get_dock_tab_dragged();
107
if (!dragged_dock) {
108
return;
109
}
110
111
can_drop_dock = dragged_dock->get_available_layouts() & dock_container->layout;
112
113
dock_drop_highlight->set_border_color(valid_drop_color);
114
dock_drop_highlight->set_bg_color(valid_drop_color * Color(1, 1, 1, 0.1));
115
} break;
116
case NOTIFICATION_DRAG_END: {
117
EditorDockManager::get_singleton()->_dock_drag_stopped();
118
can_drop_dock = false;
119
mouse_inside = false;
120
hide();
121
} break;
122
123
case NOTIFICATION_DRAW: {
124
if (!mouse_inside || !can_drop_dock) {
125
return;
126
}
127
128
// Draw highlights around docks that can be dropped.
129
Rect2 dock_rect = Rect2(Point2(), get_size()).grow(2 * EDSCALE);
130
draw_style_box(dock_drop_highlight, dock_rect);
131
132
// Only display tabbar hint if the mouse is over the tabbar.
133
if (drop_tabbar->get_global_rect().has_point(get_global_mouse_position())) {
134
draw_set_transform(drop_tabbar->get_position()); // The TabBar isn't always on top.
135
drop_tabbar->_draw_tab_drop(get_canvas_item());
136
}
137
} break;
138
}
139
}
140
141
EditorDockDragHint::EditorDockDragHint() {
142
set_as_top_level(true);
143
hide();
144
145
dock_drop_highlight.instantiate();
146
dock_drop_highlight->set_corner_radius_all(EDSCALE * EDITOR_GET("interface/theme/corner_radius").operator int());
147
dock_drop_highlight->set_border_width_all(Math::round(2 * EDSCALE));
148
}
149
150
void DockTabContainer::_pre_popup() {
151
dock_context_popup->set_dock(get_dock(get_current_tab()));
152
}
153
154
void DockTabContainer::_tab_rmb_clicked(int p_tab_idx) {
155
EditorDock *hovered_dock = get_dock(p_tab_idx);
156
if (!hovered_dock) {
157
return;
158
}
159
160
// Right click context menu.
161
dock_context_popup->set_dock(hovered_dock);
162
dock_context_popup->set_position(get_tab_bar()->get_screen_position() + get_tab_bar()->get_local_mouse_position());
163
dock_context_popup->popup();
164
}
165
166
void DockTabContainer::_notification(int p_what) {
167
if (p_what == NOTIFICATION_POSTINITIALIZE) {
168
connect("pre_popup_pressed", callable_mp(this, &DockTabContainer::_pre_popup));
169
connect("child_order_changed", callable_mp(this, &DockTabContainer::update_visibility));
170
}
171
}
172
173
void DockTabContainer::update_visibility() {
174
// Hide the dock container if there are no tabs.
175
set_visible(EditorDockManager::get_singleton()->are_docks_visible() && get_tab_count() > 0);
176
}
177
178
DockTabContainer::TabStyle DockTabContainer::get_tab_style() const {
179
return (TabStyle)EDITOR_GET("interface/editor/dock_tab_style").operator int();
180
}
181
182
bool DockTabContainer::can_switch_dock() const {
183
return EditorDockManager::get_singleton()->are_docks_visible();
184
}
185
186
void DockTabContainer::save_docks_to_config(Ref<ConfigFile> p_layout, const String &p_section) {
187
PackedStringArray names;
188
names.reserve_exact(get_tab_count());
189
for (int i = 0; i < get_tab_count(); i++) {
190
const String name = get_dock(i)->get_effective_layout_key();
191
names.append(name);
192
}
193
194
const String config_key = DockTabContainer::get_config_key(dock_slot);
195
if (!names.is_empty()) {
196
p_layout->set_value(p_section, config_key, String(",").join(names));
197
} else if (p_layout->has_section_key(p_section, config_key)) {
198
p_layout->erase_section_key(p_section, config_key);
199
}
200
201
const String tab_key = config_key + "_selected_tab_idx";
202
int selected_tab_idx = get_current_tab();
203
if (selected_tab_idx >= 0) {
204
p_layout->set_value(p_section, tab_key, selected_tab_idx);
205
} else if (p_layout->has_section_key(p_section, tab_key)) {
206
p_layout->erase_section_key(p_section, tab_key);
207
}
208
}
209
210
void DockTabContainer::load_selected_tab(int p_idx) {
211
EditorDock *selected_dock = get_dock(p_idx);
212
if (!selected_dock) {
213
return;
214
}
215
set_block_signals(true);
216
set_current_tab(p_idx);
217
set_block_signals(false);
218
}
219
220
void DockTabContainer::set_dock_context_popup(DockContextPopup *p_popup) {
221
dock_context_popup = p_popup;
222
set_popup(dock_context_popup);
223
}
224
225
void DockTabContainer::move_dock_index(EditorDock *p_dock, int p_to_index, bool p_set_current) {
226
set_block_signals(true);
227
int target_index = CLAMP(p_to_index, 0, get_tab_count() - 1);
228
move_child(p_dock, get_dock(target_index)->get_index(false));
229
230
if (p_set_current) {
231
set_current_tab(target_index);
232
}
233
set_block_signals(false);
234
}
235
236
EditorDock *DockTabContainer::get_dock(int p_idx) const {
237
return Object::cast_to<EditorDock>(get_tab_control(p_idx));
238
}
239
240
void DockTabContainer::show_drag_hint() {
241
if (!is_visible_in_tree()) {
242
return;
243
}
244
drag_hint->set_rect(get_global_rect());
245
drag_hint->show();
246
}
247
248
DockTabContainer::DockTabContainer(EditorDock::DockSlot p_slot) {
249
ERR_FAIL_INDEX(p_slot, EditorDock::DOCK_SLOT_MAX);
250
dock_slot = p_slot;
251
252
set_drag_to_rearrange_enabled(true);
253
set_tabs_rearrange_group(1);
254
hide();
255
256
drag_hint = memnew(EditorDockDragHint);
257
drag_hint->set_slot(this);
258
drag_hint->hide();
259
EditorNode::get_singleton()->get_gui_base()->add_child(drag_hint);
260
261
get_tab_bar()->set_switch_on_release(true);
262
get_tab_bar()->connect("tab_rmb_clicked", callable_mp(this, &DockTabContainer::_tab_rmb_clicked));
263
}
264
265
SideDockTabContainer::SideDockTabContainer(EditorDock::DockSlot p_slot) :
266
DockTabContainer(p_slot) {
267
set_custom_minimum_size(Size2(170 * EDSCALE, 0));
268
set_v_size_flags(Control::SIZE_EXPAND_FILL);
269
set_use_hidden_tabs_for_min_size(true);
270
}
271
272