Merge pull request #106164 from lodetrick/refactor-bottom-panel
Refactor editor `EditorBottomPanel` to be a `TabContainer`
This commit is contained in:
@ -430,7 +430,7 @@
|
|||||||
<param index="1" name="title" type="String" />
|
<param index="1" name="title" type="String" />
|
||||||
<param index="2" name="shortcut" type="Shortcut" default="null" />
|
<param index="2" name="shortcut" type="Shortcut" default="null" />
|
||||||
<description>
|
<description>
|
||||||
Adds a control to the bottom panel (together with Output, Debug, Animation, etc.). Returns a reference to the button added. It's up to you to hide/show the button when needed. When your plugin is deactivated, make sure to remove your custom control with [method remove_control_from_bottom_panel] and free it with [method Node.queue_free].
|
Adds a control to the bottom panel (together with Output, Debug, Animation, etc.). Returns a reference to a button that is outside the scene tree. It's up to you to hide/show the button when needed. When your plugin is deactivated, make sure to remove your custom control with [method remove_control_from_bottom_panel] and free it with [method Node.queue_free].
|
||||||
Optionally, you can specify a shortcut parameter. When pressed, this shortcut will toggle the bottom panel's visibility. See the default editor bottom panel shortcuts in the Editor Settings for inspiration. Per convention, they all use [kbd]Alt[/kbd] modifier.
|
Optionally, you can specify a shortcut parameter. When pressed, this shortcut will toggle the bottom panel's visibility. See the default editor bottom panel shortcuts in the Editor Settings for inspiration. Per convention, they all use [kbd]Alt[/kbd] modifier.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
|
|||||||
@ -55,8 +55,7 @@ DebuggerEditorPlugin::DebuggerEditorPlugin(PopupMenu *p_debug_menu) {
|
|||||||
file_server = memnew(EditorFileServer);
|
file_server = memnew(EditorFileServer);
|
||||||
|
|
||||||
EditorDebuggerNode *debugger = memnew(EditorDebuggerNode);
|
EditorDebuggerNode *debugger = memnew(EditorDebuggerNode);
|
||||||
Button *db = EditorNode::get_bottom_panel()->add_item(TTRC("Debugger"), debugger, ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_debugger_bottom_panel", TTRC("Toggle Debugger Bottom Panel"), KeyModifierMask::ALT | Key::D));
|
EditorNode::get_bottom_panel()->add_item(TTRC("Debugger"), debugger, ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_debugger_bottom_panel", TTRC("Toggle Debugger Bottom Panel"), KeyModifierMask::ALT | Key::D));
|
||||||
debugger->set_tool_button(db);
|
|
||||||
|
|
||||||
// Main editor debug menu.
|
// Main editor debug menu.
|
||||||
debug_menu = p_debug_menu;
|
debug_menu = p_debug_menu;
|
||||||
|
|||||||
@ -441,26 +441,35 @@ void EditorDebuggerNode::_update_errors() {
|
|||||||
dbg->update_tabs();
|
dbg->update_tabs();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (error_count == 0 && warning_count == 0) {
|
|
||||||
debugger_button->set_text(TTR("Debugger"));
|
|
||||||
debugger_button->remove_theme_color_override(SceneStringName(font_color));
|
|
||||||
debugger_button->set_button_icon(Ref<Texture2D>());
|
|
||||||
} else {
|
|
||||||
debugger_button->set_text(TTR("Debugger") + " (" + itos(error_count + warning_count) + ")");
|
|
||||||
if (error_count >= 1 && warning_count >= 1) {
|
|
||||||
debugger_button->set_button_icon(get_editor_theme_icon(SNAME("ErrorWarning")));
|
|
||||||
// Use error color to represent the highest level of severity reported.
|
|
||||||
debugger_button->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
|
|
||||||
} else if (error_count >= 1) {
|
|
||||||
debugger_button->set_button_icon(get_editor_theme_icon(SNAME("Error")));
|
|
||||||
debugger_button->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
|
|
||||||
} else {
|
|
||||||
debugger_button->set_button_icon(get_editor_theme_icon(SNAME("Warning")));
|
|
||||||
debugger_button->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
last_error_count = error_count;
|
last_error_count = error_count;
|
||||||
last_warning_count = warning_count;
|
last_warning_count = warning_count;
|
||||||
|
|
||||||
|
// TODO: Replace logic when EditorDock class is merged to be more flexible.
|
||||||
|
TabContainer *parent = Object::cast_to<TabContainer>(get_parent());
|
||||||
|
if (!parent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int idx = parent->get_tab_idx_from_control(this);
|
||||||
|
|
||||||
|
if (error_count == 0 && warning_count == 0) {
|
||||||
|
set_name(TTR("Debugger"));
|
||||||
|
parent->set_tab_icon(idx, Ref<Texture2D>());
|
||||||
|
parent->get_tab_bar()->set_font_color_override_all(idx, Color(0, 0, 0, 0));
|
||||||
|
} else {
|
||||||
|
set_name(TTR("Debugger") + " (" + itos(error_count + warning_count) + ")");
|
||||||
|
if (error_count >= 1 && warning_count >= 1) {
|
||||||
|
parent->set_tab_icon(idx, get_editor_theme_icon(SNAME("ErrorWarning")));
|
||||||
|
// Use error color to represent the highest level of severity reported.
|
||||||
|
parent->get_tab_bar()->set_font_color_override_all(idx, get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
|
||||||
|
} else if (error_count >= 1) {
|
||||||
|
parent->set_tab_icon(idx, get_editor_theme_icon(SNAME("Error")));
|
||||||
|
parent->get_tab_bar()->set_font_color_override_all(idx, get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
|
||||||
|
} else {
|
||||||
|
parent->set_tab_icon(idx, get_editor_theme_icon(SNAME("Warning")));
|
||||||
|
parent->get_tab_bar()->set_font_color_override_all(idx, get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -93,7 +93,6 @@ private:
|
|||||||
|
|
||||||
Ref<EditorDebuggerServer> server;
|
Ref<EditorDebuggerServer> server;
|
||||||
TabContainer *tabs = nullptr;
|
TabContainer *tabs = nullptr;
|
||||||
Button *debugger_button = nullptr;
|
|
||||||
MenuButton *script_menu = nullptr;
|
MenuButton *script_menu = nullptr;
|
||||||
|
|
||||||
Ref<Script> stack_script; // Why?!?
|
Ref<Script> stack_script; // Why?!?
|
||||||
@ -180,10 +179,6 @@ public:
|
|||||||
|
|
||||||
void set_script_debug_button(MenuButton *p_button);
|
void set_script_debug_button(MenuButton *p_button);
|
||||||
|
|
||||||
void set_tool_button(Button *p_button) {
|
|
||||||
debugger_button = p_button;
|
|
||||||
}
|
|
||||||
|
|
||||||
String get_var_value(const String &p_var) const;
|
String get_var_value(const String &p_var) const;
|
||||||
Ref<Script> get_dump_stack_script() const { return stack_script; } // Why do we need this?
|
Ref<Script> get_dump_stack_script() const { return stack_script; } // Why do we need this?
|
||||||
|
|
||||||
|
|||||||
@ -298,20 +298,14 @@ void EditorDockManager::_dock_container_gui_input(const Ref<InputEvent> &p_input
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Right click context menu.
|
EditorDock *hovered_dock = Object::cast_to<EditorDock>(p_dock_container->get_tab_control(tab_id));
|
||||||
dock_context_popup->set_dock(Object::cast_to<EditorDock>(p_dock_container->get_tab_control(tab_id)));
|
if (hovered_dock == nullptr) {
|
||||||
dock_context_popup->set_position(p_dock_container->get_screen_position() + mb->get_position());
|
return;
|
||||||
dock_context_popup->popup();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void EditorDockManager::_bottom_dock_button_gui_input(const Ref<InputEvent> &p_input, EditorDock *p_dock, Button *p_bottom_button) {
|
|
||||||
Ref<InputEventMouseButton> mb = p_input;
|
|
||||||
|
|
||||||
if (mb.is_valid() && mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) {
|
|
||||||
// Right click context menu.
|
// Right click context menu.
|
||||||
dock_context_popup->set_dock(p_dock);
|
dock_context_popup->set_dock(hovered_dock);
|
||||||
dock_context_popup->set_position(p_bottom_button->get_screen_position() + mb->get_position());
|
dock_context_popup->set_position(p_dock_container->get_tab_bar()->get_screen_position() + mb->get_position());
|
||||||
dock_context_popup->popup();
|
dock_context_popup->popup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -467,8 +461,7 @@ void EditorDockManager::_dock_move_to_bottom(EditorDock *p_dock, bool p_visible)
|
|||||||
p_dock->update_layout(EditorDock::DOCK_LAYOUT_HORIZONTAL);
|
p_dock->update_layout(EditorDock::DOCK_LAYOUT_HORIZONTAL);
|
||||||
|
|
||||||
// Force docks moved to the bottom to appear first in the list, and give them their associated shortcut to toggle their bottom panel.
|
// Force docks moved to the bottom to appear first in the list, and give them their associated shortcut to toggle their bottom panel.
|
||||||
Button *bottom_button = EditorNode::get_bottom_panel()->add_item(p_dock->get_display_title(), p_dock, p_dock->shortcut, true);
|
EditorNode::get_bottom_panel()->add_item(p_dock->get_display_title(), p_dock, p_dock->shortcut, true);
|
||||||
bottom_button->connect(SceneStringName(gui_input), callable_mp(this, &EditorDockManager::_bottom_dock_button_gui_input).bind(bottom_button).bind(p_dock));
|
|
||||||
EditorNode::get_bottom_panel()->make_item_visible(p_dock, p_visible);
|
EditorNode::get_bottom_panel()->make_item_visible(p_dock, p_visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -83,6 +83,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
friend class DockContextPopup;
|
friend class DockContextPopup;
|
||||||
friend class EditorDockDragHint;
|
friend class EditorDockDragHint;
|
||||||
|
friend class EditorBottomPanel; // TODO: Temporary until DOCK_SLOT_BOTTOM registered. Used to connect signals.
|
||||||
|
|
||||||
static inline EditorDockManager *singleton = nullptr;
|
static inline EditorDockManager *singleton = nullptr;
|
||||||
|
|
||||||
@ -106,7 +107,6 @@ private:
|
|||||||
void _dock_drag_stopped();
|
void _dock_drag_stopped();
|
||||||
void _dock_split_dragged(int p_offset);
|
void _dock_split_dragged(int p_offset);
|
||||||
void _dock_container_gui_input(const Ref<InputEvent> &p_input, TabContainer *p_dock_container);
|
void _dock_container_gui_input(const Ref<InputEvent> &p_input, TabContainer *p_dock_container);
|
||||||
void _bottom_dock_button_gui_input(const Ref<InputEvent> &p_input, EditorDock *p_dock, Button *p_bottom_button);
|
|
||||||
void _dock_container_update_visibility(TabContainer *p_dock_container);
|
void _dock_container_update_visibility(TabContainer *p_dock_container);
|
||||||
void _update_layout();
|
void _update_layout();
|
||||||
|
|
||||||
|
|||||||
@ -42,6 +42,7 @@
|
|||||||
#include "editor/themes/editor_scale.h"
|
#include "editor/themes/editor_scale.h"
|
||||||
#include "modules/regex/regex.h"
|
#include "modules/regex/regex.h"
|
||||||
#include "scene/gui/separator.h"
|
#include "scene/gui/separator.h"
|
||||||
|
#include "scene/gui/tab_container.h"
|
||||||
#include "scene/main/timer.h"
|
#include "scene/main/timer.h"
|
||||||
#include "scene/resources/font.h"
|
#include "scene/resources/font.h"
|
||||||
|
|
||||||
@ -233,7 +234,7 @@ void EditorLog::_clear_request() {
|
|||||||
log->clear();
|
log->clear();
|
||||||
messages.clear();
|
messages.clear();
|
||||||
_reset_message_counts();
|
_reset_message_counts();
|
||||||
tool_button->set_button_icon(Ref<Texture2D>());
|
_set_dock_tab_icon(Ref<Texture2D>());
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorLog::_copy_request() {
|
void EditorLog::_copy_request() {
|
||||||
@ -284,8 +285,13 @@ void EditorLog::add_message(const String &p_msg, MessageType p_type) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorLog::set_tool_button(Button *p_tool_button) {
|
void EditorLog::_set_dock_tab_icon(Ref<Texture2D> p_icon) {
|
||||||
tool_button = p_tool_button;
|
// This is the sole reason to include "tab_container.h" here.
|
||||||
|
TabContainer *parent = Object::cast_to<TabContainer>(get_parent());
|
||||||
|
if (parent) {
|
||||||
|
int idx = parent->get_tab_idx_from_control(this);
|
||||||
|
parent->set_tab_icon(idx, p_icon);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorLog::register_undo_redo(UndoRedo *p_undo_redo) {
|
void EditorLog::register_undo_redo(UndoRedo *p_undo_redo) {
|
||||||
@ -411,7 +417,7 @@ void EditorLog::_add_log_line(LogMessage &p_message, bool p_replace_previous) {
|
|||||||
log->push_bold();
|
log->push_bold();
|
||||||
log->add_text(" ERROR: ");
|
log->add_text(" ERROR: ");
|
||||||
log->pop(); // bold
|
log->pop(); // bold
|
||||||
tool_button->set_button_icon(icon);
|
_set_dock_tab_icon(icon);
|
||||||
} break;
|
} break;
|
||||||
case MSG_TYPE_WARNING: {
|
case MSG_TYPE_WARNING: {
|
||||||
log->push_color(theme_cache.warning_color);
|
log->push_color(theme_cache.warning_color);
|
||||||
@ -420,7 +426,7 @@ void EditorLog::_add_log_line(LogMessage &p_message, bool p_replace_previous) {
|
|||||||
log->push_bold();
|
log->push_bold();
|
||||||
log->add_text(" WARNING: ");
|
log->add_text(" WARNING: ");
|
||||||
log->pop(); // bold
|
log->pop(); // bold
|
||||||
tool_button->set_button_icon(icon);
|
_set_dock_tab_icon(icon);
|
||||||
} break;
|
} break;
|
||||||
case MSG_TYPE_EDITOR: {
|
case MSG_TYPE_EDITOR: {
|
||||||
// Distinguish editor messages from messages printed by the project
|
// Distinguish editor messages from messages printed by the project
|
||||||
|
|||||||
@ -143,9 +143,6 @@ private:
|
|||||||
// Reusable RichTextLabel for BBCode parsing during search
|
// Reusable RichTextLabel for BBCode parsing during search
|
||||||
RichTextLabel *bbcode_parser = nullptr;
|
RichTextLabel *bbcode_parser = nullptr;
|
||||||
|
|
||||||
// Reference to the "Output" button on the toolbar so we can update its icon when warnings or errors are encountered.
|
|
||||||
Button *tool_button = nullptr;
|
|
||||||
|
|
||||||
bool is_loading_state = false; // Used to disable saving requests while loading (some signals from buttons will try to trigger a save, which happens during loading).
|
bool is_loading_state = false; // Used to disable saving requests while loading (some signals from buttons will try to trigger a save, which happens during loading).
|
||||||
Timer *save_state_timer = nullptr;
|
Timer *save_state_timer = nullptr;
|
||||||
|
|
||||||
@ -169,6 +166,7 @@ private:
|
|||||||
|
|
||||||
void _process_message(const String &p_msg, MessageType p_type, bool p_clear);
|
void _process_message(const String &p_msg, MessageType p_type, bool p_clear);
|
||||||
void _reset_message_counts();
|
void _reset_message_counts();
|
||||||
|
void _set_dock_tab_icon(Ref<Texture2D> p_icon);
|
||||||
|
|
||||||
void _set_collapse(bool p_collapse);
|
void _set_collapse(bool p_collapse);
|
||||||
|
|
||||||
@ -184,7 +182,6 @@ protected:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
void add_message(const String &p_msg, MessageType p_type = MSG_TYPE_STD);
|
void add_message(const String &p_msg, MessageType p_type = MSG_TYPE_STD);
|
||||||
void set_tool_button(Button *p_tool_button);
|
|
||||||
void register_undo_redo(UndoRedo *p_undo_redo);
|
void register_undo_redo(UndoRedo *p_undo_redo);
|
||||||
void deinit();
|
void deinit();
|
||||||
|
|
||||||
|
|||||||
@ -677,7 +677,7 @@ void EditorNode::_update_theme(bool p_skip_creation) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
editor_main_screen->add_theme_style_override(SceneStringName(panel), theme->get_stylebox(SNAME("Content"), EditorStringName(EditorStyles)));
|
editor_main_screen->add_theme_style_override(SceneStringName(panel), theme->get_stylebox(SNAME("Content"), EditorStringName(EditorStyles)));
|
||||||
bottom_panel->add_theme_style_override(SceneStringName(panel), theme->get_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles)));
|
bottom_panel->_theme_changed();
|
||||||
distraction_free->set_button_icon(theme->get_icon(SNAME("DistractionFree"), EditorStringName(EditorIcons)));
|
distraction_free->set_button_icon(theme->get_icon(SNAME("DistractionFree"), EditorStringName(EditorIcons)));
|
||||||
distraction_free->add_theme_style_override(SceneStringName(pressed), theme->get_stylebox(CoreStringName(normal), "FlatMenuButton"));
|
distraction_free->add_theme_style_override(SceneStringName(pressed), theme->get_stylebox(CoreStringName(normal), "FlatMenuButton"));
|
||||||
|
|
||||||
@ -8696,12 +8696,12 @@ EditorNode::EditorNode() {
|
|||||||
// Bottom panels.
|
// Bottom panels.
|
||||||
|
|
||||||
bottom_panel = memnew(EditorBottomPanel);
|
bottom_panel = memnew(EditorBottomPanel);
|
||||||
|
bottom_panel->set_theme_type_variation("BottomPanel");
|
||||||
center_split->add_child(bottom_panel);
|
center_split->add_child(bottom_panel);
|
||||||
center_split->set_dragger_visibility(SplitContainer::DRAGGER_HIDDEN);
|
center_split->set_dragger_visibility(SplitContainer::DRAGGER_HIDDEN);
|
||||||
|
|
||||||
log = memnew(EditorLog);
|
log = memnew(EditorLog);
|
||||||
Button *output_button = bottom_panel->add_item(TTRC("Output"), log, ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_output_bottom_panel", TTRC("Toggle Output Bottom Panel"), KeyModifierMask::ALT | Key::O));
|
bottom_panel->add_item(TTRC("Output"), log, ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_output_bottom_panel", TTRC("Toggle Output Bottom Panel"), KeyModifierMask::ALT | Key::O));
|
||||||
log->set_tool_button(output_button);
|
|
||||||
|
|
||||||
center_split->connect(SceneStringName(resized), callable_mp(this, &EditorNode::_vp_resized));
|
center_split->connect(SceneStringName(resized), callable_mp(this, &EditorNode::_vp_resized));
|
||||||
|
|
||||||
|
|||||||
@ -31,6 +31,7 @@
|
|||||||
#include "editor_bottom_panel.h"
|
#include "editor_bottom_panel.h"
|
||||||
|
|
||||||
#include "editor/debugger/editor_debugger_node.h"
|
#include "editor/debugger/editor_debugger_node.h"
|
||||||
|
#include "editor/docks/editor_dock_manager.h"
|
||||||
#include "editor/editor_node.h"
|
#include "editor/editor_node.h"
|
||||||
#include "editor/editor_string_names.h"
|
#include "editor/editor_string_names.h"
|
||||||
#include "editor/gui/editor_toaster.h"
|
#include "editor/gui/editor_toaster.h"
|
||||||
@ -39,7 +40,6 @@
|
|||||||
#include "editor/themes/editor_scale.h"
|
#include "editor/themes/editor_scale.h"
|
||||||
#include "scene/gui/box_container.h"
|
#include "scene/gui/box_container.h"
|
||||||
#include "scene/gui/button.h"
|
#include "scene/gui/button.h"
|
||||||
#include "scene/gui/scroll_container.h"
|
|
||||||
#include "scene/gui/split_container.h"
|
#include "scene/gui/split_container.h"
|
||||||
|
|
||||||
void EditorBottomPanel::_notification(int p_what) {
|
void EditorBottomPanel::_notification(int p_what) {
|
||||||
@ -47,297 +47,167 @@ void EditorBottomPanel::_notification(int p_what) {
|
|||||||
case NOTIFICATION_THEME_CHANGED: {
|
case NOTIFICATION_THEME_CHANGED: {
|
||||||
pin_button->set_button_icon(get_editor_theme_icon(SNAME("Pin")));
|
pin_button->set_button_icon(get_editor_theme_icon(SNAME("Pin")));
|
||||||
expand_button->set_button_icon(get_editor_theme_icon(SNAME("ExpandBottomDock")));
|
expand_button->set_button_icon(get_editor_theme_icon(SNAME("ExpandBottomDock")));
|
||||||
left_button->set_button_icon(get_editor_theme_icon(SNAME("Back")));
|
|
||||||
right_button->set_button_icon(get_editor_theme_icon(SNAME("Forward")));
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case NOTIFICATION_TRANSLATION_CHANGED:
|
|
||||||
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
|
|
||||||
if (is_layout_rtl()) {
|
|
||||||
bottom_hbox->move_child(left_button, button_scroll->get_index() + 1);
|
|
||||||
bottom_hbox->move_child(right_button, 0);
|
|
||||||
} else {
|
|
||||||
bottom_hbox->move_child(right_button, button_scroll->get_index() + 1);
|
|
||||||
bottom_hbox->move_child(left_button, 0);
|
|
||||||
}
|
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorBottomPanel::_switch_by_control(bool p_visible, Control *p_control, bool p_ignore_lock) {
|
void EditorBottomPanel::_on_tab_changed(int p_idx) {
|
||||||
for (int i = 0; i < items.size(); i++) {
|
callable_mp(this, &EditorBottomPanel::_repaint).call_deferred();
|
||||||
if (items[i].control == p_control) {
|
|
||||||
_switch_to_item(p_visible, i, p_ignore_lock);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorBottomPanel::_scroll(bool p_right) {
|
void EditorBottomPanel::_theme_changed() {
|
||||||
HScrollBar *h_scroll = button_scroll->get_h_scroll_bar();
|
Ref<StyleBox> bottom_tabbar_style = get_theme_stylebox("tabbar_background", "BottomPanel")->duplicate();
|
||||||
if (Input::get_singleton()->is_key_pressed(Key::CTRL)) {
|
bottom_tabbar_style->set_content_margin(SIDE_RIGHT, bottom_hbox->get_minimum_size().x + bottom_tabbar_style->get_content_margin(SIDE_LEFT));
|
||||||
h_scroll->set_value(p_right ? h_scroll->get_max() : 0);
|
add_theme_style_override("tabbar_background", bottom_tabbar_style);
|
||||||
} else if (Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
|
|
||||||
h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() * (p_right ? 1 : -1));
|
if (get_current_tab() == -1) {
|
||||||
|
// Hide panel when not showing anything.
|
||||||
|
remove_theme_style_override(SceneStringName(panel));
|
||||||
} else {
|
} else {
|
||||||
h_scroll->set_value(h_scroll->get_value() + (h_scroll->get_page() * 0.5) * (p_right ? 1 : -1));
|
add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorBottomPanel::_update_scroll_buttons() {
|
void EditorBottomPanel::_repaint() {
|
||||||
bool show_arrows = button_hbox->get_size().width > button_scroll->get_size().width;
|
bool panel_collapsed = get_current_tab() == -1;
|
||||||
left_button->set_visible(show_arrows);
|
if (panel_collapsed == (get_previous_tab() == -1)) {
|
||||||
right_button->set_visible(show_arrows);
|
|
||||||
|
|
||||||
if (show_arrows) {
|
|
||||||
_update_disabled_buttons();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditorBottomPanel::_update_disabled_buttons() {
|
|
||||||
HScrollBar *h_scroll = button_scroll->get_h_scroll_bar();
|
|
||||||
left_button->set_disabled(h_scroll->get_value() == 0);
|
|
||||||
right_button->set_disabled(h_scroll->get_value() + h_scroll->get_page() == h_scroll->get_max());
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditorBottomPanel::_ensure_control_visible(ObjectID p_id) {
|
|
||||||
Control *c = ObjectDB::get_instance<Control>(p_id);
|
|
||||||
if (!c) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
button_scroll->ensure_control_visible(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditorBottomPanel::_switch_to_item(bool p_visible, int p_idx, bool p_ignore_lock) {
|
|
||||||
ERR_FAIL_INDEX(p_idx, items.size());
|
|
||||||
|
|
||||||
if (items[p_idx].control->is_visible() == p_visible) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SplitContainer *center_split = Object::cast_to<SplitContainer>(get_parent());
|
SplitContainer *center_split = Object::cast_to<SplitContainer>(get_parent());
|
||||||
ERR_FAIL_NULL(center_split);
|
ERR_FAIL_NULL(center_split);
|
||||||
|
|
||||||
if (p_visible) {
|
center_split->set_dragger_visibility(panel_collapsed ? SplitContainer::DRAGGER_HIDDEN : SplitContainer::DRAGGER_VISIBLE);
|
||||||
|
center_split->set_collapsed(panel_collapsed);
|
||||||
|
|
||||||
|
pin_button->set_visible(!panel_collapsed);
|
||||||
|
expand_button->set_visible(!panel_collapsed);
|
||||||
|
if (expand_button->is_pressed()) {
|
||||||
|
EditorNode::get_top_split()->set_visible(panel_collapsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
_theme_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorBottomPanel::save_layout_to_config(Ref<ConfigFile> p_config_file, const String &p_section) const {
|
||||||
|
p_config_file->set_value(p_section, "selected_bottom_panel_item", get_current_tab() != -1 ? Variant(get_current_tab()) : Variant());
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorBottomPanel::load_layout_from_config(Ref<ConfigFile> p_config_file, const String &p_section) {
|
||||||
|
if (p_config_file->has_section_key(p_section, "selected_bottom_panel_item")) {
|
||||||
|
int stored_current_tab = p_config_file->get_value(p_section, "selected_bottom_panel_item");
|
||||||
|
|
||||||
|
if (stored_current_tab >= 0 && stored_current_tab < get_tab_bar()->get_tab_count()) {
|
||||||
|
// Make sure we don't try to open contextual editors which are not enabled in the current context.
|
||||||
|
if (!get_tab_bar()->is_tab_hidden(stored_current_tab)) {
|
||||||
|
set_current_tab(stored_current_tab);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is no active tab we need to collapse the panel.
|
||||||
|
set_current_tab(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorBottomPanel::make_item_visible(Control *p_item, bool p_visible, bool p_ignore_lock) {
|
||||||
|
// Don't allow changing tabs involuntarily when tabs are locked.
|
||||||
if (!p_ignore_lock && lock_panel_switching && pin_button->is_visible()) {
|
if (!p_ignore_lock && lock_panel_switching && pin_button->is_visible()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < items.size(); i++) {
|
p_item->set_visible(p_visible);
|
||||||
items[i].button->set_pressed_no_signal(i == p_idx);
|
}
|
||||||
items[i].control->set_visible(i == p_idx);
|
|
||||||
|
void EditorBottomPanel::move_item_to_end(Control *p_item) {
|
||||||
|
move_child(p_item, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorBottomPanel::hide_bottom_panel() {
|
||||||
|
set_current_tab(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorBottomPanel::toggle_last_opened_bottom_panel() {
|
||||||
|
set_current_tab(get_current_tab() == -1 ? get_previous_tab() : -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorBottomPanel::shortcut_input(const Ref<InputEvent> &p_event) {
|
||||||
|
if (p_event.is_null() || !p_event->is_pressed() || p_event->is_echo()) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
center_split->set_dragger_visibility(SplitContainer::DRAGGER_VISIBLE);
|
for (uint32_t i = 0; i < dock_shortcuts.size(); i++) {
|
||||||
center_split->set_collapsed(false);
|
if (dock_shortcuts[i].is_valid() && dock_shortcuts[i]->matches_event(p_event)) {
|
||||||
pin_button->show();
|
bottom_docks[i]->set_visible(!bottom_docks[i]->is_visible());
|
||||||
|
break;
|
||||||
expand_button->show();
|
|
||||||
if (expand_button->is_pressed()) {
|
|
||||||
EditorNode::get_top_split()->hide();
|
|
||||||
}
|
|
||||||
callable_mp(this, &EditorBottomPanel::_ensure_control_visible).call_deferred(items[p_idx].button->get_instance_id());
|
|
||||||
} else {
|
|
||||||
items[p_idx].button->set_pressed_no_signal(false);
|
|
||||||
items[p_idx].control->set_visible(false);
|
|
||||||
center_split->set_dragger_visibility(SplitContainer::DRAGGER_HIDDEN);
|
|
||||||
center_split->set_collapsed(true);
|
|
||||||
pin_button->hide();
|
|
||||||
|
|
||||||
expand_button->hide();
|
|
||||||
if (expand_button->is_pressed()) {
|
|
||||||
EditorNode::get_top_split()->show();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
last_opened_control = items[p_idx].control;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorBottomPanel::_pin_button_toggled(bool p_pressed) {
|
void EditorBottomPanel::_pin_button_toggled(bool p_pressed) {
|
||||||
lock_panel_switching = p_pressed;
|
lock_panel_switching = p_pressed;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorBottomPanel::_expand_button_toggled(bool p_pressed) {
|
|
||||||
EditorNode::get_top_split()->set_visible(!p_pressed);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EditorBottomPanel::_button_drag_hover(const Vector2 &, const Variant &, Button *p_button, Control *p_control) {
|
|
||||||
if (!p_button->is_pressed()) {
|
|
||||||
_switch_by_control(true, p_control, true);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditorBottomPanel::save_layout_to_config(Ref<ConfigFile> p_config_file, const String &p_section) const {
|
|
||||||
int selected_item_idx = -1;
|
|
||||||
for (int i = 0; i < items.size(); i++) {
|
|
||||||
if (items[i].button->is_pressed()) {
|
|
||||||
selected_item_idx = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (selected_item_idx != -1) {
|
|
||||||
p_config_file->set_value(p_section, "selected_bottom_panel_item", selected_item_idx);
|
|
||||||
} else {
|
|
||||||
p_config_file->set_value(p_section, "selected_bottom_panel_item", Variant());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditorBottomPanel::load_layout_from_config(Ref<ConfigFile> p_config_file, const String &p_section) {
|
|
||||||
bool has_active_tab = false;
|
|
||||||
if (p_config_file->has_section_key(p_section, "selected_bottom_panel_item")) {
|
|
||||||
int selected_item_idx = p_config_file->get_value(p_section, "selected_bottom_panel_item");
|
|
||||||
if (selected_item_idx >= 0 && selected_item_idx < items.size()) {
|
|
||||||
// Make sure we don't try to open contextual editors which are not enabled in the current context.
|
|
||||||
if (items[selected_item_idx].button->is_visible()) {
|
|
||||||
_switch_to_item(true, selected_item_idx);
|
|
||||||
has_active_tab = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If there is no active tab we need to collapse the panel.
|
|
||||||
if (!has_active_tab) {
|
|
||||||
items[0].control->show(); // _switch_to_item() can collapse only visible tabs.
|
|
||||||
_switch_to_item(false, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button *EditorBottomPanel::add_item(String p_text, Control *p_item, const Ref<Shortcut> &p_shortcut, bool p_at_front) {
|
|
||||||
Button *tb = memnew(Button);
|
|
||||||
tb->set_theme_type_variation("BottomPanelButton");
|
|
||||||
tb->connect(SceneStringName(toggled), callable_mp(this, &EditorBottomPanel::_switch_by_control).bind(p_item, true));
|
|
||||||
tb->set_drag_forwarding(Callable(), callable_mp(this, &EditorBottomPanel::_button_drag_hover).bind(tb, p_item), Callable());
|
|
||||||
tb->set_text(p_text);
|
|
||||||
tb->set_shortcut(p_shortcut);
|
|
||||||
tb->set_toggle_mode(true);
|
|
||||||
tb->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
|
|
||||||
item_vbox->add_child(p_item);
|
|
||||||
|
|
||||||
bottom_hbox->move_to_front();
|
|
||||||
button_hbox->add_child(tb);
|
|
||||||
if (p_at_front) {
|
|
||||||
button_hbox->move_child(tb, 0);
|
|
||||||
}
|
|
||||||
p_item->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
|
||||||
p_item->hide();
|
|
||||||
|
|
||||||
BottomPanelItem bpi;
|
|
||||||
bpi.button = tb;
|
|
||||||
bpi.control = p_item;
|
|
||||||
bpi.name = p_text;
|
|
||||||
if (p_at_front) {
|
|
||||||
items.insert(0, bpi);
|
|
||||||
} else {
|
|
||||||
items.push_back(bpi);
|
|
||||||
}
|
|
||||||
|
|
||||||
return tb;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditorBottomPanel::remove_item(Control *p_item) {
|
|
||||||
bool was_visible = false;
|
|
||||||
for (int i = 0; i < items.size(); i++) {
|
|
||||||
if (items[i].control == p_item) {
|
|
||||||
if (p_item->is_visible_in_tree()) {
|
|
||||||
was_visible = true;
|
|
||||||
}
|
|
||||||
item_vbox->remove_child(items[i].control);
|
|
||||||
button_hbox->remove_child(items[i].button);
|
|
||||||
memdelete(items[i].button);
|
|
||||||
items.remove_at(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (was_visible) {
|
|
||||||
// Open the first panel to ensure that if the removed dock was visible, the bottom
|
|
||||||
// panel will not collapse.
|
|
||||||
_switch_to_item(true, 0, true);
|
|
||||||
} else if (last_opened_control == p_item) {
|
|
||||||
// When a dock is removed by plugins, it might not have been visible, and it
|
|
||||||
// might have been the last_opened_control. We need to make sure to reset the last opened control.
|
|
||||||
last_opened_control = items[0].control;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditorBottomPanel::make_item_visible(Control *p_item, bool p_visible, bool p_ignore_lock) {
|
|
||||||
_switch_by_control(p_visible, p_item, p_ignore_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditorBottomPanel::move_item_to_end(Control *p_item) {
|
|
||||||
for (int i = 0; i < items.size(); i++) {
|
|
||||||
if (items[i].control == p_item) {
|
|
||||||
items[i].button->move_to_front();
|
|
||||||
SWAP(items.write[i], items.write[items.size() - 1]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditorBottomPanel::hide_bottom_panel() {
|
|
||||||
for (int i = 0; i < items.size(); i++) {
|
|
||||||
if (items[i].control->is_visible()) {
|
|
||||||
_switch_to_item(false, i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditorBottomPanel::toggle_last_opened_bottom_panel() {
|
|
||||||
// Select by control instead of index, so that the last bottom panel is opened correctly
|
|
||||||
// if it's been reordered since.
|
|
||||||
if (last_opened_control) {
|
|
||||||
_switch_by_control(!last_opened_control->is_visible(), last_opened_control, true);
|
|
||||||
} else {
|
|
||||||
// Open the first panel in the list if no panel was opened this session.
|
|
||||||
_switch_to_item(true, 0, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditorBottomPanel::set_expanded(bool p_expanded) {
|
void EditorBottomPanel::set_expanded(bool p_expanded) {
|
||||||
expand_button->set_pressed(p_expanded);
|
expand_button->set_pressed(p_expanded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EditorBottomPanel::_expand_button_toggled(bool p_pressed) {
|
||||||
|
EditorNode::get_top_split()->set_visible(!p_pressed);
|
||||||
|
}
|
||||||
|
|
||||||
|
Button *EditorBottomPanel::add_item(String p_text, Control *p_item, const Ref<Shortcut> &p_shortcut, bool p_at_front) {
|
||||||
|
p_item->set_name(p_text);
|
||||||
|
add_child(p_item);
|
||||||
|
if (p_at_front) {
|
||||||
|
move_child(p_item, 0);
|
||||||
|
}
|
||||||
|
bottom_docks.push_back(p_item);
|
||||||
|
dock_shortcuts.push_back(p_shortcut);
|
||||||
|
|
||||||
|
set_process_shortcut_input(is_processing_shortcut_input() || p_shortcut.is_valid());
|
||||||
|
|
||||||
|
// Still return a dummy button for compatibility reasons.
|
||||||
|
Button *tb = memnew(Button);
|
||||||
|
tb->set_toggle_mode(true);
|
||||||
|
tb->connect(SceneStringName(visibility_changed), callable_mp(this, &EditorBottomPanel::_on_button_visibility_changed).bind(tb, p_item));
|
||||||
|
legacy_buttons.push_back(tb);
|
||||||
|
return tb;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorBottomPanel::remove_item(Control *p_item) {
|
||||||
|
int item_idx = bottom_docks.find(p_item);
|
||||||
|
ERR_FAIL_COND_MSG(item_idx == -1, vformat("Cannot remove unknown dock \"%s\" from the bottom panel.", p_item->get_name()));
|
||||||
|
|
||||||
|
bottom_docks.remove_at(item_idx);
|
||||||
|
dock_shortcuts.remove_at(item_idx);
|
||||||
|
|
||||||
|
legacy_buttons[item_idx]->queue_free();
|
||||||
|
legacy_buttons.remove_at(item_idx);
|
||||||
|
|
||||||
|
remove_child(p_item);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorBottomPanel::_on_button_visibility_changed(Button *p_button, Control *p_control) {
|
||||||
|
int tab_index = get_tab_idx_from_control(p_control);
|
||||||
|
if (tab_index == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore the tab if the button is hidden.
|
||||||
|
get_tab_bar()->set_tab_hidden(tab_index, !p_button->is_visible());
|
||||||
|
}
|
||||||
|
|
||||||
EditorBottomPanel::EditorBottomPanel() {
|
EditorBottomPanel::EditorBottomPanel() {
|
||||||
item_vbox = memnew(VBoxContainer);
|
get_tab_bar()->connect(SceneStringName(gui_input), callable_mp(EditorDockManager::get_singleton(), &EditorDockManager::_dock_container_gui_input).bind(this));
|
||||||
add_child(item_vbox);
|
get_tab_bar()->connect("tab_changed", callable_mp(this, &EditorBottomPanel::_on_tab_changed));
|
||||||
|
set_tabs_position(TabPosition::POSITION_BOTTOM);
|
||||||
|
set_deselect_enabled(true);
|
||||||
|
|
||||||
bottom_hbox = memnew(HBoxContainer);
|
bottom_hbox = memnew(HBoxContainer);
|
||||||
bottom_hbox->set_custom_minimum_size(Size2(0, 24 * EDSCALE)); // Adjust for the height of the "Expand Bottom Dock" icon.
|
bottom_hbox->set_anchors_and_offsets_preset(Control::PRESET_RIGHT_WIDE);
|
||||||
item_vbox->add_child(bottom_hbox);
|
bottom_hbox->set_h_grow_direction(Control::GROW_DIRECTION_END);
|
||||||
|
get_tab_bar()->add_child(bottom_hbox);
|
||||||
left_button = memnew(Button);
|
|
||||||
left_button->set_tooltip_text(TTRC("Scroll Left\nHold Ctrl to scroll to the begin.\nHold Shift to scroll one page."));
|
|
||||||
left_button->set_accessibility_name(TTRC("Scroll Left"));
|
|
||||||
left_button->set_theme_type_variation("BottomPanelButton");
|
|
||||||
left_button->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
|
|
||||||
left_button->connect(SceneStringName(pressed), callable_mp(this, &EditorBottomPanel::_scroll).bind(false));
|
|
||||||
bottom_hbox->add_child(left_button);
|
|
||||||
left_button->hide();
|
|
||||||
|
|
||||||
button_scroll = memnew(ScrollContainer);
|
|
||||||
button_scroll->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
|
||||||
button_scroll->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_SHOW_NEVER);
|
|
||||||
button_scroll->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
|
|
||||||
button_scroll->get_h_scroll_bar()->connect(CoreStringName(changed), callable_mp(this, &EditorBottomPanel::_update_scroll_buttons), CONNECT_DEFERRED);
|
|
||||||
button_scroll->get_h_scroll_bar()->connect(SceneStringName(value_changed), callable_mp(this, &EditorBottomPanel::_update_disabled_buttons).unbind(1), CONNECT_DEFERRED);
|
|
||||||
bottom_hbox->add_child(button_scroll);
|
|
||||||
|
|
||||||
right_button = memnew(Button);
|
|
||||||
right_button->set_tooltip_text(TTRC("Scroll Right\nHold Ctrl to scroll to the end.\nHold Shift to scroll one page."));
|
|
||||||
right_button->set_accessibility_name(TTRC("Scroll Right"));
|
|
||||||
right_button->set_theme_type_variation("BottomPanelButton");
|
|
||||||
right_button->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
|
|
||||||
right_button->connect(SceneStringName(pressed), callable_mp(this, &EditorBottomPanel::_scroll).bind(true));
|
|
||||||
bottom_hbox->add_child(right_button);
|
|
||||||
right_button->hide();
|
|
||||||
|
|
||||||
callable_mp(this, &EditorBottomPanel::_update_scroll_buttons).call_deferred();
|
|
||||||
|
|
||||||
button_hbox = memnew(HBoxContainer);
|
|
||||||
button_hbox->set_h_size_flags(Control::SIZE_EXPAND | Control::SIZE_SHRINK_BEGIN);
|
|
||||||
button_scroll->add_child(button_hbox);
|
|
||||||
|
|
||||||
editor_toaster = memnew(EditorToaster);
|
editor_toaster = memnew(EditorToaster);
|
||||||
bottom_hbox->add_child(editor_toaster);
|
bottom_hbox->add_child(editor_toaster);
|
||||||
@ -355,7 +225,7 @@ EditorBottomPanel::EditorBottomPanel() {
|
|||||||
pin_button = memnew(Button);
|
pin_button = memnew(Button);
|
||||||
bottom_hbox->add_child(pin_button);
|
bottom_hbox->add_child(pin_button);
|
||||||
pin_button->hide();
|
pin_button->hide();
|
||||||
pin_button->set_theme_type_variation("FlatMenuButton");
|
pin_button->set_theme_type_variation("BottomPanelButton");
|
||||||
pin_button->set_toggle_mode(true);
|
pin_button->set_toggle_mode(true);
|
||||||
pin_button->set_tooltip_text(TTRC("Pin Bottom Panel Switching"));
|
pin_button->set_tooltip_text(TTRC("Pin Bottom Panel Switching"));
|
||||||
pin_button->connect(SceneStringName(toggled), callable_mp(this, &EditorBottomPanel::_pin_button_toggled));
|
pin_button->connect(SceneStringName(toggled), callable_mp(this, &EditorBottomPanel::_pin_button_toggled));
|
||||||
@ -363,9 +233,17 @@ EditorBottomPanel::EditorBottomPanel() {
|
|||||||
expand_button = memnew(Button);
|
expand_button = memnew(Button);
|
||||||
bottom_hbox->add_child(expand_button);
|
bottom_hbox->add_child(expand_button);
|
||||||
expand_button->hide();
|
expand_button->hide();
|
||||||
expand_button->set_theme_type_variation("FlatMenuButton");
|
expand_button->set_theme_type_variation("BottomPanelButton");
|
||||||
expand_button->set_toggle_mode(true);
|
expand_button->set_toggle_mode(true);
|
||||||
expand_button->set_accessibility_name(TTRC("Expand Bottom Panel"));
|
expand_button->set_accessibility_name(TTRC("Expand Bottom Panel"));
|
||||||
expand_button->set_shortcut(ED_SHORTCUT_AND_COMMAND("editor/bottom_panel_expand", TTRC("Expand Bottom Panel"), KeyModifierMask::SHIFT | Key::F12));
|
expand_button->set_shortcut(ED_SHORTCUT_AND_COMMAND("editor/bottom_panel_expand", TTRC("Expand Bottom Panel"), KeyModifierMask::SHIFT | Key::F12));
|
||||||
expand_button->connect(SceneStringName(toggled), callable_mp(this, &EditorBottomPanel::_expand_button_toggled));
|
expand_button->connect(SceneStringName(toggled), callable_mp(this, &EditorBottomPanel::_expand_button_toggled));
|
||||||
|
|
||||||
|
callable_mp(this, &EditorBottomPanel::_repaint).call_deferred();
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorBottomPanel::~EditorBottomPanel() {
|
||||||
|
for (Button *b : legacy_buttons) {
|
||||||
|
memdelete(b);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,52 +30,38 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "scene/gui/panel_container.h"
|
#include "scene/gui/tab_container.h"
|
||||||
|
|
||||||
class Button;
|
class Button;
|
||||||
class ConfigFile;
|
class ConfigFile;
|
||||||
class EditorToaster;
|
class EditorToaster;
|
||||||
class HBoxContainer;
|
class HBoxContainer;
|
||||||
class VBoxContainer;
|
|
||||||
class ScrollContainer;
|
|
||||||
|
|
||||||
class EditorBottomPanel : public PanelContainer {
|
class EditorBottomPanel : public TabContainer {
|
||||||
GDCLASS(EditorBottomPanel, PanelContainer);
|
GDCLASS(EditorBottomPanel, TabContainer);
|
||||||
|
|
||||||
struct BottomPanelItem {
|
|
||||||
String name;
|
|
||||||
Control *control = nullptr;
|
|
||||||
Button *button = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
Vector<BottomPanelItem> items;
|
|
||||||
bool lock_panel_switching = false;
|
|
||||||
|
|
||||||
VBoxContainer *item_vbox = nullptr;
|
|
||||||
HBoxContainer *bottom_hbox = nullptr;
|
HBoxContainer *bottom_hbox = nullptr;
|
||||||
Button *left_button = nullptr;
|
|
||||||
Button *right_button = nullptr;
|
|
||||||
ScrollContainer *button_scroll = nullptr;
|
|
||||||
HBoxContainer *button_hbox = nullptr;
|
|
||||||
EditorToaster *editor_toaster = nullptr;
|
EditorToaster *editor_toaster = nullptr;
|
||||||
Button *pin_button = nullptr;
|
Button *pin_button = nullptr;
|
||||||
Button *expand_button = nullptr;
|
Button *expand_button = nullptr;
|
||||||
Control *last_opened_control = nullptr;
|
|
||||||
|
|
||||||
void _switch_by_control(bool p_visible, Control *p_control, bool p_ignore_lock = false);
|
bool lock_panel_switching = false;
|
||||||
void _switch_to_item(bool p_visible, int p_idx, bool p_ignore_lock = false);
|
LocalVector<Control *> bottom_docks;
|
||||||
|
LocalVector<Ref<Shortcut>> dock_shortcuts;
|
||||||
|
|
||||||
|
LocalVector<Button *> legacy_buttons;
|
||||||
|
void _on_button_visibility_changed(Button *p_button, Control *p_control);
|
||||||
|
|
||||||
|
void _repaint();
|
||||||
|
void _on_tab_changed(int p_idx);
|
||||||
void _pin_button_toggled(bool p_pressed);
|
void _pin_button_toggled(bool p_pressed);
|
||||||
void _expand_button_toggled(bool p_pressed);
|
void _expand_button_toggled(bool p_pressed);
|
||||||
void _scroll(bool p_right);
|
|
||||||
void _update_scroll_buttons();
|
|
||||||
void _update_disabled_buttons();
|
|
||||||
void _ensure_control_visible(ObjectID p_id);
|
|
||||||
|
|
||||||
bool _button_drag_hover(const Vector2 &, const Variant &, Button *p_button, Control *p_control);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void _notification(int p_what);
|
void _notification(int p_what);
|
||||||
|
|
||||||
|
virtual void shortcut_input(const Ref<InputEvent> &p_event) override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void save_layout_to_config(Ref<ConfigFile> p_config_file, const String &p_section) const;
|
void save_layout_to_config(Ref<ConfigFile> p_config_file, const String &p_section) const;
|
||||||
void load_layout_from_config(Ref<ConfigFile> p_config_file, const String &p_section);
|
void load_layout_from_config(Ref<ConfigFile> p_config_file, const String &p_section);
|
||||||
@ -87,6 +73,8 @@ public:
|
|||||||
void hide_bottom_panel();
|
void hide_bottom_panel();
|
||||||
void toggle_last_opened_bottom_panel();
|
void toggle_last_opened_bottom_panel();
|
||||||
void set_expanded(bool p_expanded);
|
void set_expanded(bool p_expanded);
|
||||||
|
void _theme_changed();
|
||||||
|
|
||||||
EditorBottomPanel();
|
EditorBottomPanel();
|
||||||
|
~EditorBottomPanel();
|
||||||
};
|
};
|
||||||
|
|||||||
@ -4095,13 +4095,8 @@ void ScriptEditor::_start_find_in_files(bool with_replace) {
|
|||||||
find_in_files->set_replace_text(find_in_files_dialog->get_replace_text());
|
find_in_files->set_replace_text(find_in_files_dialog->get_replace_text());
|
||||||
find_in_files->start_search();
|
find_in_files->start_search();
|
||||||
|
|
||||||
if (find_in_files_button->get_index() != find_in_files_button->get_parent()->get_child_count()) {
|
EditorNode::get_bottom_panel()->move_item_to_end(find_in_files);
|
||||||
find_in_files_button->get_parent()->move_child(find_in_files_button, -1);
|
|
||||||
}
|
|
||||||
if (!find_in_files_button->is_visible()) {
|
|
||||||
find_in_files_button->show();
|
find_in_files_button->show();
|
||||||
}
|
|
||||||
|
|
||||||
EditorNode::get_bottom_panel()->make_item_visible(find_in_files);
|
EditorNode::get_bottom_panel()->make_item_visible(find_in_files);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -921,7 +921,7 @@ ShaderEditorPlugin::ShaderEditorPlugin() {
|
|||||||
empty.instantiate();
|
empty.instantiate();
|
||||||
shader_tabs->add_theme_style_override(SceneStringName(panel), empty);
|
shader_tabs->add_theme_style_override(SceneStringName(panel), empty);
|
||||||
|
|
||||||
button = EditorNode::get_bottom_panel()->add_item(TTRC("Shader Editor"), window_wrapper, ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_shader_editor_bottom_panel", TTRC("Toggle Shader Editor Bottom Panel"), KeyModifierMask::ALT | Key::S));
|
EditorNode::get_bottom_panel()->add_item(TTRC("Shader Editor"), window_wrapper, ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_shader_editor_bottom_panel", TTRC("Toggle Shader Editor Bottom Panel"), KeyModifierMask::ALT | Key::S));
|
||||||
|
|
||||||
shader_create_dialog = memnew(ShaderCreateDialog);
|
shader_create_dialog = memnew(ShaderCreateDialog);
|
||||||
files_split->add_child(shader_create_dialog);
|
files_split->add_child(shader_create_dialog);
|
||||||
|
|||||||
@ -88,7 +88,6 @@ class ShaderEditorPlugin : public EditorPlugin {
|
|||||||
TabContainer *shader_tabs = nullptr;
|
TabContainer *shader_tabs = nullptr;
|
||||||
HBoxContainer *empty_menu = nullptr;
|
HBoxContainer *empty_menu = nullptr;
|
||||||
|
|
||||||
Button *button = nullptr;
|
|
||||||
MenuButton *file_menu = nullptr;
|
MenuButton *file_menu = nullptr;
|
||||||
PopupMenu *context_menu = nullptr;
|
PopupMenu *context_menu = nullptr;
|
||||||
|
|
||||||
|
|||||||
@ -1635,13 +1635,43 @@ void ThemeClassic::populate_editor_styles(const Ref<EditorTheme> &p_theme, Edito
|
|||||||
|
|
||||||
// Bottom panel.
|
// Bottom panel.
|
||||||
Ref<StyleBoxFlat> style_bottom_panel = p_config.content_panel_style->duplicate();
|
Ref<StyleBoxFlat> style_bottom_panel = p_config.content_panel_style->duplicate();
|
||||||
|
style_bottom_panel->set_border_width(SIDE_BOTTOM, 0);
|
||||||
style_bottom_panel->set_corner_radius_all(p_config.corner_radius * EDSCALE);
|
style_bottom_panel->set_corner_radius_all(p_config.corner_radius * EDSCALE);
|
||||||
|
style_bottom_panel->set_corner_radius(CORNER_BOTTOM_LEFT, 0);
|
||||||
|
style_bottom_panel->set_corner_radius(CORNER_BOTTOM_RIGHT, 0);
|
||||||
|
|
||||||
|
Ref<StyleBoxFlat> style_bottom_panel_hidden = style_bottom_panel->duplicate();
|
||||||
|
style_bottom_panel_hidden->set_content_margin(SIDE_TOP, 0);
|
||||||
|
|
||||||
|
Ref<StyleBoxFlat> style_bottom_panel_tabbar = p_config.content_panel_style->duplicate();
|
||||||
|
style_bottom_panel_tabbar->set_content_margin(SIDE_TOP, 0);
|
||||||
|
Ref<StyleBoxFlat> style_bottom_tab = menu_transparent_style->duplicate();
|
||||||
|
style_bottom_tab->set_content_margin(SIDE_TOP, (p_config.increased_margin + 2) * EDSCALE);
|
||||||
|
style_bottom_tab->set_content_margin(SIDE_BOTTOM, (p_config.increased_margin + 2) * EDSCALE);
|
||||||
|
|
||||||
|
Ref<StyleBoxFlat> style_bottom_tab_selected = style_bottom_tab->duplicate();
|
||||||
|
style_bottom_tab_selected->set_bg_color(p_config.dark_color_1);
|
||||||
|
|
||||||
|
Ref<StyleBoxFlat> style_bottom_tab_hover = style_bottom_tab->duplicate();
|
||||||
|
style_bottom_tab_hover->set_bg_color(p_config.button_style_hover->get_bg_color());
|
||||||
|
|
||||||
p_theme->set_stylebox("BottomPanel", EditorStringName(EditorStyles), style_bottom_panel);
|
p_theme->set_stylebox("BottomPanel", EditorStringName(EditorStyles), style_bottom_panel);
|
||||||
|
p_theme->set_type_variation("BottomPanel", "TabContainer");
|
||||||
|
p_theme->set_stylebox(SceneStringName(panel), "BottomPanel", style_bottom_panel_hidden);
|
||||||
|
p_theme->set_stylebox("tabbar_background", "BottomPanel", style_bottom_panel_tabbar);
|
||||||
|
p_theme->set_stylebox("tab_selected", "BottomPanel", style_bottom_tab_selected);
|
||||||
|
p_theme->set_stylebox("tab_hovered", "BottomPanel", style_bottom_tab_hover);
|
||||||
|
p_theme->set_stylebox("tab_focus", "BottomPanel", menu_transparent_style);
|
||||||
|
p_theme->set_stylebox("tab_unselected", "BottomPanel", style_bottom_tab);
|
||||||
|
p_theme->set_color("font_unselected_color", "BottomPanel", p_config.font_color);
|
||||||
|
p_theme->set_color("font_hovered_color", "BottomPanel", p_config.font_hover_color);
|
||||||
|
p_theme->set_color("font_selected_color", "BottomPanel", p_config.accent_color);
|
||||||
|
p_theme->set_constant("tab_separation", "BottomPanel", p_config.separation_margin);
|
||||||
p_theme->set_type_variation("BottomPanelButton", "FlatMenuButton");
|
p_theme->set_type_variation("BottomPanelButton", "FlatMenuButton");
|
||||||
p_theme->set_stylebox(CoreStringName(normal), "BottomPanelButton", menu_transparent_style);
|
p_theme->set_stylebox(CoreStringName(normal), "BottomPanelButton", menu_transparent_style);
|
||||||
p_theme->set_stylebox(SceneStringName(pressed), "BottomPanelButton", menu_transparent_style);
|
p_theme->set_stylebox(SceneStringName(pressed), "BottomPanelButton", style_bottom_tab_selected);
|
||||||
p_theme->set_stylebox("hover_pressed", "BottomPanelButton", main_screen_button_hover);
|
p_theme->set_stylebox("hover_pressed", "BottomPanelButton", style_bottom_tab_hover);
|
||||||
p_theme->set_stylebox(SceneStringName(hover), "BottomPanelButton", main_screen_button_hover);
|
p_theme->set_stylebox(SceneStringName(hover), "BottomPanelButton", style_bottom_tab_hover);
|
||||||
// Don't tint the icon even when in "pressed" state.
|
// Don't tint the icon even when in "pressed" state.
|
||||||
p_theme->set_color("icon_pressed_color", "BottomPanelButton", Color(1, 1, 1, 1));
|
p_theme->set_color("icon_pressed_color", "BottomPanelButton", Color(1, 1, 1, 1));
|
||||||
Color icon_hover_color = p_config.icon_normal_color * (p_config.dark_icon_and_font ? 1.15 : 1.0);
|
Color icon_hover_color = p_config.icon_normal_color * (p_config.dark_icon_and_font ? 1.15 : 1.0);
|
||||||
|
|||||||
@ -1617,8 +1617,33 @@ void ThemeModern::populate_editor_styles(const Ref<EditorTheme> &p_theme, Editor
|
|||||||
|
|
||||||
// Bottom panel.
|
// Bottom panel.
|
||||||
Ref<StyleBoxFlat> style_bottom_panel = p_config.content_panel_style->duplicate();
|
Ref<StyleBoxFlat> style_bottom_panel = p_config.content_panel_style->duplicate();
|
||||||
|
style_bottom_panel->set_border_width(SIDE_BOTTOM, 0);
|
||||||
style_bottom_panel->set_corner_radius_all(p_config.corner_radius * EDSCALE);
|
style_bottom_panel->set_corner_radius_all(p_config.corner_radius * EDSCALE);
|
||||||
|
style_bottom_panel->set_corner_radius(CORNER_BOTTOM_LEFT, 0);
|
||||||
|
style_bottom_panel->set_corner_radius(CORNER_BOTTOM_RIGHT, 0);
|
||||||
|
|
||||||
|
Ref<StyleBoxFlat> style_bottom_panel_hidden = style_bottom_panel->duplicate();
|
||||||
|
style_bottom_panel_hidden->set_content_margin(SIDE_TOP, 0);
|
||||||
|
Ref<StyleBoxFlat> style_bottom_panel_tabbar = p_config.content_panel_style->duplicate();
|
||||||
|
style_bottom_panel_tabbar->set_content_margin(SIDE_TOP, 0);
|
||||||
|
|
||||||
|
Ref<StyleBoxEmpty> style_bottom_tab = p_config.base_empty_style->duplicate();
|
||||||
|
style_bottom_tab->set_content_margin_individual(p_config.base_margin * 2 * EDSCALE, p_config.base_margin * 1.2 * EDSCALE, p_config.base_margin * 2 * EDSCALE, p_config.base_margin * 1.2 * EDSCALE);
|
||||||
|
Ref<StyleBoxFlat> bottom_panel_button_hover = p_config.flat_button_hover->duplicate();
|
||||||
|
bottom_panel_button_hover->set_content_margin_individual(p_config.base_margin * 2 * EDSCALE, p_config.base_margin * 1.2 * EDSCALE, p_config.base_margin * 2 * EDSCALE, p_config.base_margin * 1.2 * EDSCALE);
|
||||||
|
|
||||||
p_theme->set_stylebox("BottomPanel", EditorStringName(EditorStyles), style_bottom_panel);
|
p_theme->set_stylebox("BottomPanel", EditorStringName(EditorStyles), style_bottom_panel);
|
||||||
|
p_theme->set_type_variation("BottomPanel", "TabContainer");
|
||||||
|
p_theme->set_stylebox(SceneStringName(panel), "BottomPanel", style_bottom_panel_hidden);
|
||||||
|
p_theme->set_stylebox("tabbar_background", "BottomPanel", style_bottom_panel_tabbar);
|
||||||
|
p_theme->set_stylebox("tab_selected", "BottomPanel", bottom_panel_button_hover);
|
||||||
|
p_theme->set_stylebox("tab_hovered", "BottomPanel", bottom_panel_button_hover);
|
||||||
|
p_theme->set_stylebox("tab_focus", "BottomPanel", p_config.base_empty_style);
|
||||||
|
p_theme->set_stylebox("tab_unselected", "BottomPanel", style_bottom_tab);
|
||||||
|
p_theme->set_color("font_unselected_color", "BottomPanel", p_config.font_color);
|
||||||
|
p_theme->set_color("font_hovered_color", "BottomPanel", p_config.font_hover_color);
|
||||||
|
p_theme->set_color("font_selected_color", "BottomPanel", p_config.font_hover_color);
|
||||||
|
p_theme->set_constant("tab_separation", "BottomPanel", p_config.separation_margin);
|
||||||
|
|
||||||
p_theme->set_type_variation("BottomPanelButton", "FlatMenuButton");
|
p_theme->set_type_variation("BottomPanelButton", "FlatMenuButton");
|
||||||
|
|
||||||
@ -1627,8 +1652,6 @@ void ThemeModern::populate_editor_styles(const Ref<EditorTheme> &p_theme, Editor
|
|||||||
bottom_panel_button->set_content_margin_individual(p_config.base_margin * 2 * EDSCALE, p_config.base_margin * 1.2 * EDSCALE, p_config.base_margin * 2 * EDSCALE, p_config.base_margin * 1.2 * EDSCALE);
|
bottom_panel_button->set_content_margin_individual(p_config.base_margin * 2 * EDSCALE, p_config.base_margin * 1.2 * EDSCALE, p_config.base_margin * 2 * EDSCALE, p_config.base_margin * 1.2 * EDSCALE);
|
||||||
p_theme->set_stylebox(CoreStringName(normal), "BottomPanelButton", bottom_panel_button);
|
p_theme->set_stylebox(CoreStringName(normal), "BottomPanelButton", bottom_panel_button);
|
||||||
|
|
||||||
Ref<StyleBoxFlat> bottom_panel_button_hover = p_config.flat_button_hover->duplicate();
|
|
||||||
bottom_panel_button_hover->set_content_margin_individual(p_config.base_margin * 2 * EDSCALE, p_config.base_margin * 1.2 * EDSCALE, p_config.base_margin * 2 * EDSCALE, p_config.base_margin * 1.2 * EDSCALE);
|
|
||||||
p_theme->set_stylebox(SceneStringName(hover), "BottomPanelButton", bottom_panel_button_hover);
|
p_theme->set_stylebox(SceneStringName(hover), "BottomPanelButton", bottom_panel_button_hover);
|
||||||
p_theme->set_stylebox(SceneStringName(pressed), "BottomPanelButton", bottom_panel_button_hover);
|
p_theme->set_stylebox(SceneStringName(pressed), "BottomPanelButton", bottom_panel_button_hover);
|
||||||
|
|
||||||
@ -1640,8 +1663,11 @@ void ThemeModern::populate_editor_styles(const Ref<EditorTheme> &p_theme, Editor
|
|||||||
p_theme->set_color("icon_pressed_color", "BottomPanelButton", Color(1, 1, 1, 1));
|
p_theme->set_color("icon_pressed_color", "BottomPanelButton", Color(1, 1, 1, 1));
|
||||||
Color icon_hover_color = p_config.icon_normal_color * (p_config.dark_icon_and_font ? 1.15 : 1.0);
|
Color icon_hover_color = p_config.icon_normal_color * (p_config.dark_icon_and_font ? 1.15 : 1.0);
|
||||||
icon_hover_color.a = 1.0;
|
icon_hover_color.a = 1.0;
|
||||||
|
p_theme->set_color("icon_hover_color", "BottomPanel", icon_hover_color);
|
||||||
|
p_theme->set_color("icon_hover_pressed_color", "BottomPanel", icon_hover_color);
|
||||||
p_theme->set_color("icon_hover_color", "BottomPanelButton", icon_hover_color);
|
p_theme->set_color("icon_hover_color", "BottomPanelButton", icon_hover_color);
|
||||||
p_theme->set_color("icon_hover_pressed_color", "BottomPanelButton", icon_hover_color);
|
p_theme->set_color("icon_hover_pressed_color", "BottomPanelButton", p_config.accent_color);
|
||||||
|
p_theme->set_color("icon_pressed_color", "BottomPanelButton", p_config.accent_color);
|
||||||
|
|
||||||
// Audio bus.
|
// Audio bus.
|
||||||
Ref<StyleBoxFlat> audio_bus = p_config.base_style->duplicate();
|
Ref<StyleBoxFlat> audio_bus = p_config.base_style->duplicate();
|
||||||
|
|||||||
@ -276,7 +276,7 @@ void VersionControlEditorPlugin::_commit() {
|
|||||||
|
|
||||||
EditorVCSInterface::get_singleton()->commit(msg);
|
EditorVCSInterface::get_singleton()->commit(msg);
|
||||||
|
|
||||||
version_control_dock_button->set_pressed(false);
|
EditorNode::get_bottom_panel()->make_item_visible(version_control_dock, false);
|
||||||
|
|
||||||
commit_message->release_focus();
|
commit_message->release_focus();
|
||||||
commit_button->release_focus();
|
commit_button->release_focus();
|
||||||
@ -488,7 +488,7 @@ void VersionControlEditorPlugin::_move_all(Object *p_tree) {
|
|||||||
void VersionControlEditorPlugin::_load_diff(Object *p_tree) {
|
void VersionControlEditorPlugin::_load_diff(Object *p_tree) {
|
||||||
CHECK_PLUGIN_INITIALIZED();
|
CHECK_PLUGIN_INITIALIZED();
|
||||||
|
|
||||||
version_control_dock_button->set_pressed(true);
|
EditorNode::get_bottom_panel()->make_item_visible(version_control_dock, true, true);
|
||||||
|
|
||||||
Tree *tree = Object::cast_to<Tree>(p_tree);
|
Tree *tree = Object::cast_to<Tree>(p_tree);
|
||||||
if (tree == staged_files) {
|
if (tree == staged_files) {
|
||||||
@ -910,7 +910,7 @@ void VersionControlEditorPlugin::fetch_available_vcs_plugin_names() {
|
|||||||
void VersionControlEditorPlugin::register_editor() {
|
void VersionControlEditorPlugin::register_editor() {
|
||||||
EditorDockManager::get_singleton()->add_dock(version_commit_dock);
|
EditorDockManager::get_singleton()->add_dock(version_commit_dock);
|
||||||
|
|
||||||
version_control_dock_button = EditorNode::get_bottom_panel()->add_item(TTRC("Version Control"), version_control_dock, ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_version_control_bottom_panel", TTRC("Toggle Version Control Bottom Panel")));
|
EditorNode::get_bottom_panel()->add_item(TTRC("Version Control"), version_control_dock, ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_version_control_bottom_panel", TTRC("Toggle Version Control Bottom Panel")));
|
||||||
|
|
||||||
_set_vcs_ui_state(true);
|
_set_vcs_ui_state(true);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -130,7 +130,6 @@ private:
|
|||||||
Button *commit_button = nullptr;
|
Button *commit_button = nullptr;
|
||||||
|
|
||||||
VBoxContainer *version_control_dock = nullptr;
|
VBoxContainer *version_control_dock = nullptr;
|
||||||
Button *version_control_dock_button = nullptr;
|
|
||||||
Label *diff_title = nullptr;
|
Label *diff_title = nullptr;
|
||||||
RichTextLabel *diff = nullptr;
|
RichTextLabel *diff = nullptr;
|
||||||
OptionButton *diff_view_type_select = nullptr;
|
OptionButton *diff_view_type_select = nullptr;
|
||||||
|
|||||||
Reference in New Issue
Block a user