Skip to content

Commit 06ce2bf

Browse files
committed
Fix bottom panel sizing with multiple options
1 parent 9e60984 commit 06ce2bf

File tree

2 files changed

+181
-5
lines changed

2 files changed

+181
-5
lines changed

editor/gui/editor_bottom_panel.cpp

+161-5
Original file line numberDiff line numberDiff line change
@@ -37,23 +37,35 @@
3737
#include "editor/gui/editor_toaster.h"
3838
#include "editor/gui/editor_version_button.h"
3939
#include "editor/themes/editor_scale.h"
40+
#include "editor/themes/editor_theme_manager.h"
4041
#include "scene/gui/box_container.h"
4142
#include "scene/gui/button.h"
43+
#include "scene/gui/menu_button.h"
4244
#include "scene/gui/split_container.h"
45+
#include "scene/resources/style_box_flat.h"
4346

4447
void EditorBottomPanel::_notification(int p_what) {
4548
switch (p_what) {
4649
case NOTIFICATION_THEME_CHANGED: {
4750
pin_button->set_button_icon(get_editor_theme_icon(SNAME("Pin")));
4851
expand_button->set_button_icon(get_editor_theme_icon(SNAME("ExpandBottomDock")));
52+
button_menu->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl")));
53+
54+
if (get_theme_color(SNAME("base_color"), EditorStringName(Editor)) != Color(0, 0, 0)) {
55+
stylebox_shadow_left->set_border_color(get_theme_color(SNAME("dark_color_1")) * Color(1, 1, 1, EditorThemeManager::is_dark_theme() ? 0.4 : 0.3));
56+
stylebox_shadow_right->set_border_color(get_theme_color(SNAME("dark_color_1")) * Color(1, 1, 1, EditorThemeManager::is_dark_theme() ? 0.4 : 0.3));
57+
} else {
58+
stylebox_shadow_left->set_border_color(Color(0.3, 0.3, 0.3, 0.45));
59+
stylebox_shadow_right->set_border_color(Color(0.3, 0.3, 0.3, 0.45));
60+
}
4961
} break;
5062
}
5163
}
5264

5365
void EditorBottomPanel::_switch_by_control(bool p_visible, Control *p_control, bool p_ignore_lock) {
5466
for (int i = 0; i < items.size(); i++) {
5567
if (items[i].control == p_control) {
56-
_switch_to_item(p_visible, i, p_ignore_lock);
68+
callable_mp(this, &EditorBottomPanel::_switch_to_item).call_deferred(p_visible, i, p_ignore_lock);
5769
return;
5870
}
5971
}
@@ -62,7 +74,12 @@ void EditorBottomPanel::_switch_by_control(bool p_visible, Control *p_control, b
6274
void EditorBottomPanel::_switch_to_item(bool p_visible, int p_idx, bool p_ignore_lock) {
6375
ERR_FAIL_INDEX(p_idx, items.size());
6476

77+
if (get_tree()->is_connected("process_frame", callable_mp(this, &EditorBottomPanel::_focus_pressed_button))) {
78+
get_tree()->disconnect("process_frame", callable_mp(this, &EditorBottomPanel::_focus_pressed_button));
79+
}
80+
6581
if (items[p_idx].control->is_visible() == p_visible) {
82+
get_tree()->connect("process_frame", callable_mp(this, &EditorBottomPanel::_focus_pressed_button).bind(items[p_idx].button->get_instance_id()), CONNECT_ONE_SHOT);
6683
return;
6784
}
6885

@@ -84,7 +101,7 @@ void EditorBottomPanel::_switch_to_item(bool p_visible, int p_idx, bool p_ignore
84101
} else {
85102
add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles)));
86103
}
87-
104+
get_tree()->connect("process_frame", callable_mp(this, &EditorBottomPanel::_focus_pressed_button).bind(items[p_idx].button->get_instance_id()), CONNECT_ONE_SHOT);
88105
center_split->set_dragger_visibility(SplitContainer::DRAGGER_VISIBLE);
89106
center_split->set_collapsed(false);
90107
pin_button->show();
@@ -125,6 +142,92 @@ bool EditorBottomPanel::_button_drag_hover(const Vector2 &, const Variant &, But
125142
return false;
126143
}
127144

145+
void EditorBottomPanel::_focus_pressed_button(ObjectID btn_id) {
146+
Object *btn_instance = ObjectDB::get_instance(btn_id);
147+
if (!btn_instance) {
148+
return;
149+
}
150+
button_sc->ensure_control_visible(Object::cast_to<Button>(btn_instance));
151+
}
152+
153+
void EditorBottomPanel::_add_item_to_popup() {
154+
button_popup_menu->clear();
155+
for (int i = 0; i < button_hbox->get_child_count(); i++) {
156+
Button *btn = Object::cast_to<Button>(button_hbox->get_child(i));
157+
if (!btn->is_visible()) {
158+
continue;
159+
}
160+
button_popup_menu->add_item(btn->get_text());
161+
button_popup_menu->set_item_metadata(button_popup_menu->get_item_count() - 1, btn->get_index());
162+
}
163+
}
164+
165+
void EditorBottomPanel::_popup_position() {
166+
if (button_popup_menu->is_visible()) {
167+
button_popup_menu->hide();
168+
return;
169+
}
170+
button_popup_menu->reset_size();
171+
Vector2 popup_pos = button_menu->get_screen_rect().position - Vector2(0, button_popup_menu->get_size().y);
172+
if (is_layout_rtl()) {
173+
popup_pos.x -= button_popup_menu->get_size().x - button_menu->get_size().x;
174+
}
175+
button_popup_menu->set_position(popup_pos);
176+
button_popup_menu->popup();
177+
}
178+
179+
void EditorBottomPanel::_popup_item_pressed(int p_idx) {
180+
Button *btn = Object::cast_to<Button>(button_hbox->get_child(button_popup_menu->get_item_metadata(p_idx)));
181+
btn->emit_signal(SceneStringName(toggled), true);
182+
}
183+
184+
void EditorBottomPanel::_set_button_sc_controls() {
185+
if (button_hbox->get_size().x < button_sc->get_size().x) {
186+
button_menu->set_visible(false);
187+
shadow_spacer->set_visible(false);
188+
shadow_panel_left->add_theme_style_override(SceneStringName(panel), stylebox_shadow_empty);
189+
shadow_panel_right->add_theme_style_override(SceneStringName(panel), stylebox_shadow_empty);
190+
} else {
191+
button_menu->set_visible(true);
192+
shadow_spacer->set_visible(true);
193+
if (is_layout_rtl()) {
194+
if (button_sc->get_h_scroll_bar()->get_value() + 3 >= button_sc->get_h_scroll_bar()->get_max() - button_sc->get_size().x) {
195+
shadow_panel_left->add_theme_style_override(SceneStringName(panel), stylebox_shadow_empty);
196+
} else {
197+
shadow_panel_left->add_theme_style_override(SceneStringName(panel), stylebox_shadow_left);
198+
}
199+
if (button_sc->get_h_scroll_bar()->get_value() == 0) {
200+
shadow_panel_right->add_theme_style_override(SceneStringName(panel), stylebox_shadow_empty);
201+
} else {
202+
shadow_panel_right->add_theme_style_override(SceneStringName(panel), stylebox_shadow_right);
203+
}
204+
} else {
205+
if (button_sc->get_h_scroll_bar()->get_value() == 0) {
206+
shadow_panel_left->add_theme_style_override(SceneStringName(panel), stylebox_shadow_empty);
207+
} else {
208+
shadow_panel_left->add_theme_style_override(SceneStringName(panel), stylebox_shadow_left);
209+
}
210+
if (button_sc->get_h_scroll_bar()->get_value() + 3 >= button_sc->get_h_scroll_bar()->get_max() - button_sc->get_size().x) {
211+
shadow_panel_right->add_theme_style_override(SceneStringName(panel), stylebox_shadow_empty);
212+
} else {
213+
shadow_panel_right->add_theme_style_override(SceneStringName(panel), stylebox_shadow_right);
214+
}
215+
}
216+
}
217+
}
218+
219+
void EditorBottomPanel::_sc_input(const Ref<InputEvent> &p_gui_input) {
220+
Ref<InputEventMouseButton> mb = p_gui_input;
221+
222+
if (mb.is_valid()) {
223+
if (mb->is_pressed()) {
224+
if ((mb->get_button_index() == MouseButton::WHEEL_UP && mb->is_alt_pressed()) || (mb->get_button_index() == MouseButton::WHEEL_DOWN && mb->is_alt_pressed())) {
225+
mb->set_factor(mb->get_factor() * 7);
226+
}
227+
}
228+
}
229+
}
230+
128231
void EditorBottomPanel::save_layout_to_config(Ref<ConfigFile> p_config_file, const String &p_section) const {
129232
int selected_item_idx = -1;
130233
for (int i = 0; i < items.size(); i++) {
@@ -162,7 +265,7 @@ void EditorBottomPanel::load_layout_from_config(Ref<ConfigFile> p_config_file, c
162265
Button *EditorBottomPanel::add_item(String p_text, Control *p_item, const Ref<Shortcut> &p_shortcut, bool p_at_front) {
163266
Button *tb = memnew(Button);
164267
tb->set_theme_type_variation("BottomPanelButton");
165-
tb->connect(SceneStringName(toggled), callable_mp(this, &EditorBottomPanel::_switch_by_control).bind(p_item, true));
268+
tb->connect(SceneStringName(toggled), callable_mp(this, &EditorBottomPanel::_switch_by_control).bind(p_item, true), CONNECT_DEFERRED);
166269
tb->set_drag_forwarding(Callable(), callable_mp(this, &EditorBottomPanel::_button_drag_hover).bind(tb, p_item), Callable());
167270
tb->set_text(p_text);
168271
tb->set_shortcut(p_shortcut);
@@ -259,9 +362,62 @@ EditorBottomPanel::EditorBottomPanel() {
259362
bottom_hbox->set_custom_minimum_size(Size2(0, 24 * EDSCALE)); // Adjust for the height of the "Expand Bottom Dock" icon.
260363
item_vbox->add_child(bottom_hbox);
261364

365+
button_tab_bar_hbox = memnew(HBoxContainer);
366+
button_tab_bar_hbox->set_h_size_flags(Control::SIZE_EXPAND_FILL);
367+
button_tab_bar_hbox->add_theme_constant_override("separation", 0);
368+
bottom_hbox->add_child(button_tab_bar_hbox);
369+
370+
button_menu = memnew(Button);
371+
button_menu->set_theme_type_variation("FlatMenuButton");
372+
button_menu->set_toggle_mode(true);
373+
button_menu->set_action_mode(BaseButton::ACTION_MODE_BUTTON_PRESS);
374+
button_menu->set_tooltip_text(TTR("List all options."));
375+
button_menu->connect(SceneStringName(pressed), callable_mp(this, &EditorBottomPanel::_popup_position));
376+
button_tab_bar_hbox->add_child(button_menu);
377+
378+
button_popup_menu = memnew(PopupMenu);
379+
add_child(button_popup_menu);
380+
button_popup_menu->connect(SceneStringName(id_pressed), callable_mp(this, &EditorBottomPanel::_popup_item_pressed));
381+
button_popup_menu->connect("popup_hide", callable_mp((BaseButton *)button_menu, &BaseButton::set_pressed).bind(false));
382+
button_popup_menu->connect("about_to_popup", callable_mp((BaseButton *)button_menu, &BaseButton::set_pressed).bind(true));
383+
384+
shadow_spacer = memnew(Control);
385+
shadow_spacer->set_custom_minimum_size(Size2(2 * EDSCALE, 0));
386+
button_tab_bar_hbox->add_child(shadow_spacer);
387+
388+
stylebox_shadow_empty.instantiate();
389+
stylebox_shadow_left.instantiate();
390+
stylebox_shadow_left->set_draw_center(false);
391+
stylebox_shadow_left->set_border_blend(true);
392+
stylebox_shadow_left->set_border_width_all(0);
393+
stylebox_shadow_right = stylebox_shadow_left->duplicate();
394+
stylebox_shadow_left->set_border_width(Side::SIDE_LEFT, 3 * EDSCALE);
395+
stylebox_shadow_left->set_expand_margin(Side::SIDE_LEFT, 1);
396+
stylebox_shadow_right->set_border_width(Side::SIDE_RIGHT, 3 * EDSCALE);
397+
stylebox_shadow_right->set_expand_margin(Side::SIDE_RIGHT, 1);
398+
399+
shadow_panel_left = memnew(Panel);
400+
shadow_panel_left->set_custom_minimum_size(Size2(4 * EDSCALE, 0));
401+
shadow_panel_left->add_theme_style_override(SceneStringName(panel), stylebox_shadow_left);
402+
button_tab_bar_hbox->add_child(shadow_panel_left);
403+
404+
button_sc = memnew(ScrollContainer);
405+
button_sc->set_h_size_flags(Control::SIZE_EXPAND_FILL);
406+
button_sc->set_follow_focus(true);
407+
button_sc->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_SHOW_NEVER);
408+
button_sc->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
409+
button_sc->connect(SceneStringName(draw), callable_mp(this, &EditorBottomPanel::_set_button_sc_controls));
410+
button_sc->connect(SceneStringName(gui_input), callable_mp(this, &EditorBottomPanel::_sc_input));
411+
button_tab_bar_hbox->add_child(button_sc);
412+
262413
button_hbox = memnew(HBoxContainer);
263-
button_hbox->set_h_size_flags(Control::SIZE_EXPAND_FILL);
264-
bottom_hbox->add_child(button_hbox);
414+
button_sc->add_child(button_hbox);
415+
button_hbox->connect(SceneStringName(resized), callable_mp(this, &EditorBottomPanel::_add_item_to_popup));
416+
417+
shadow_panel_right = memnew(Panel);
418+
shadow_panel_right->set_custom_minimum_size(Size2(4 * EDSCALE, 0));
419+
shadow_panel_right->add_theme_style_override(SceneStringName(panel), stylebox_shadow_right);
420+
button_tab_bar_hbox->add_child(shadow_panel_right);
265421

266422
editor_toaster = memnew(EditorToaster);
267423
bottom_hbox->add_child(editor_toaster);

editor/gui/editor_bottom_panel.h

+20
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ class Button;
3737
class ConfigFile;
3838
class EditorToaster;
3939
class HBoxContainer;
40+
class PopupMenu;
41+
class ScrollContainer;
42+
class StyleBoxEmpty;
43+
class StyleBoxFlat;
4044
class VBoxContainer;
4145

4246
class EditorBottomPanel : public PanelContainer {
@@ -51,18 +55,34 @@ class EditorBottomPanel : public PanelContainer {
5155
Vector<BottomPanelItem> items;
5256
bool lock_panel_switching = false;
5357

58+
Control *shadow_spacer = nullptr;
5459
VBoxContainer *item_vbox = nullptr;
5560
HBoxContainer *bottom_hbox = nullptr;
61+
HBoxContainer *button_tab_bar_hbox = nullptr;
62+
Panel *shadow_panel_left = nullptr;
63+
Panel *shadow_panel_right = nullptr;
64+
ScrollContainer *button_sc = nullptr;
65+
Button *button_menu = nullptr;
66+
PopupMenu *button_popup_menu = nullptr;
5667
HBoxContainer *button_hbox = nullptr;
5768
EditorToaster *editor_toaster = nullptr;
5869
Button *pin_button = nullptr;
5970
Button *expand_button = nullptr;
6071
Control *last_opened_control = nullptr;
72+
Ref<StyleBoxFlat> stylebox_shadow_left;
73+
Ref<StyleBoxFlat> stylebox_shadow_right;
74+
Ref<StyleBoxEmpty> stylebox_shadow_empty;
6175

6276
void _switch_by_control(bool p_visible, Control *p_control, bool p_ignore_lock = false);
6377
void _switch_to_item(bool p_visible, int p_idx, bool p_ignore_lock = false);
6478
void _pin_button_toggled(bool p_pressed);
6579
void _expand_button_toggled(bool p_pressed);
80+
void _focus_pressed_button(ObjectID btn_id);
81+
void _add_item_to_popup();
82+
void _popup_item_pressed(int p_idx);
83+
void _set_button_sc_controls();
84+
void _sc_input(const Ref<InputEvent> &p_gui_input);
85+
void _popup_position();
6686

6787
bool _button_drag_hover(const Vector2 &, const Variant &, Button *p_button, Control *p_control);
6888

0 commit comments

Comments
 (0)