Merge pull request #106164 from lodetrick/refactor-bottom-panel

Refactor editor `EditorBottomPanel` to be a `TabContainer`
This commit is contained in:
Thaddeus Crews
2025-11-07 10:32:35 -06:00
18 changed files with 272 additions and 358 deletions

View File

@ -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>

View File

@ -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;

View File

@ -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)));
}
}
} }
} }

View File

@ -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?

View File

@ -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);
} }

View File

@ -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();

View File

@ -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

View File

@ -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();

View File

@ -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));

View File

@ -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);
}
} }

View File

@ -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();
}; };

View File

@ -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);
} }

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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();

View File

@ -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);
} }

View File

@ -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;