diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml
index 608173cdb47..78bf864c6c5 100644
--- a/doc/classes/PopupMenu.xml
+++ b/doc/classes/PopupMenu.xml
@@ -717,6 +717,9 @@
The tint of text outline of the labeled separator.
+
+ If not [code]0[/code], the icon gutter will be merged with the checkbox gutter when possible. This acts as a boolean.
+
The horizontal space between the item's elements.
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 4cce8859e61..e43ddf229c3 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -231,7 +231,8 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
real_t body_max_w = 0.0; // Indentation, text, and submenu arrow.
real_t icon_max_w = 0.0;
real_t accel_max_w = 0.0;
- bool has_check = false;
+ bool has_check_gutter = false;
+ bool gutter_compact = theme_cache.gutter_compact;
for (int i = 0; i < items.size(); i++) {
_shape_item(i);
@@ -239,7 +240,10 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
icon_max_w = MAX(_get_item_icon_size(i).width, icon_max_w);
if (items[i].checkable_type && !items[i].separator) {
- has_check = true;
+ has_check_gutter = true;
+ if (items[i].icon.is_valid()) {
+ gutter_compact = false;
+ }
}
if (items[i].accel != Key::NONE || (items[i].shortcut.is_valid() && items[i].shortcut->has_valid_event())) {
@@ -258,12 +262,16 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
minsize.width += theme_cache.item_start_padding + body_max_w + accel_max_w + theme_cache.item_end_padding;
- if (icon_max_w > 0) {
- minsize.width += icon_max_w + theme_cache.h_separation;
- }
- if (has_check) {
- int check_w = MAX(theme_cache.checked->get_width(), theme_cache.radio_checked->get_width());
- minsize.width += check_w + theme_cache.h_separation;
+ const int check_w = MAX(theme_cache.checked->get_width(), theme_cache.radio_checked->get_width());
+ if (gutter_compact) {
+ minsize.width += MAX(icon_max_w, check_w) + theme_cache.h_separation;
+ } else {
+ if (icon_max_w > 0) {
+ minsize.width += icon_max_w + theme_cache.h_separation;
+ }
+ if (has_check_gutter) {
+ minsize.width += check_w + theme_cache.h_separation;
+ }
}
if (is_inside_tree()) {
@@ -801,31 +809,29 @@ void PopupMenu::_draw_items() {
float display_width = control->get_size().width;
// Find the widest icon and whether any items have a checkbox, and store the offsets for each.
- float icon_ofs = 0.0;
- bool has_check = false;
+ real_t icon_max_w = 0.0;
+ real_t check_max_w = 0.0;
+ bool has_check_gutter = false;
+ bool gutter_compact = theme_cache.gutter_compact;
for (int i = 0; i < items.size(); i++) {
if (items[i].separator) {
continue;
}
- Size2 icon_size = _get_item_icon_size(i);
- icon_ofs = MAX(icon_size.width, icon_ofs);
+ icon_max_w = MAX(_get_item_icon_size(i).width, icon_max_w);
if (items[i].checkable_type) {
- has_check = true;
+ has_check_gutter = true;
+ if (items[i].icon.is_valid()) {
+ gutter_compact = false;
+ }
}
}
- if (icon_ofs > 0.0) {
- icon_ofs += theme_cache.h_separation;
- }
-
- float check_ofs = 0.0;
- if (has_check) {
+ if (has_check_gutter) {
for (int i = 0; i < 4; i++) {
- check_ofs = MAX(check_ofs, check[i]->get_width());
- check_ofs = MAX(check_ofs, uncheck[i]->get_width());
+ check_max_w = MAX(check_max_w, check[i]->get_width());
+ check_max_w = MAX(check_max_w, uncheck[i]->get_width());
}
- check_ofs += theme_cache.h_separation;
}
Point2 ofs;
@@ -911,10 +917,11 @@ void PopupMenu::_draw_items() {
separator_ofs += icon_size.width + theme_cache.h_separation;
}
} else {
+ const real_t check_w = (gutter_compact || !has_check_gutter) ? 0 : (check_max_w + theme_cache.h_separation);
if (rtl) {
- icon_pos = Size2(control->get_size().width - item_ofs.x - check_ofs - icon_size.width, item_ofs.y);
+ icon_pos = Size2(control->get_size().width - item_ofs.x - check_w - icon_size.width, item_ofs.y);
} else {
- icon_pos = item_ofs + Size2(check_ofs, 0);
+ icon_pos = item_ofs + Size2(check_w, 0);
}
}
@@ -941,8 +948,16 @@ void PopupMenu::_draw_items() {
items[i].text_buf->draw(ci, text_pos, theme_cache.font_separator_color);
}
} else {
- item_ofs.x += icon_ofs + check_ofs;
-
+ if (gutter_compact) {
+ item_ofs.x += MAX(icon_max_w, check_max_w) + theme_cache.h_separation;
+ } else {
+ if (icon_max_w > 0) {
+ item_ofs.x += icon_max_w + theme_cache.h_separation;
+ }
+ if (has_check_gutter) {
+ item_ofs.x += check_max_w + theme_cache.h_separation;
+ }
+ }
if (rtl) {
Vector2 text_pos = Size2(control->get_size().width - items[i].text_buf->get_size().width - item_ofs.x, item_ofs.y) + Point2(0, Math::floor((h - items[i].text_buf->get_size().y) / 2.0));
if (theme_cache.font_outline_size > 0 && theme_cache.font_outline_color.a > 0) {
@@ -3136,6 +3151,7 @@ void PopupMenu::_bind_methods() {
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, PopupMenu, item_start_padding);
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, PopupMenu, item_end_padding);
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, PopupMenu, icon_max_width);
+ BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, PopupMenu, gutter_compact);
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, PopupMenu, checked);
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, PopupMenu, checked_disabled);
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index 1bfa7af4ddc..37678e03b46 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -185,6 +185,7 @@ class PopupMenu : public Popup {
int item_start_padding = 0;
int item_end_padding = 0;
int icon_max_width = 0;
+ int gutter_compact = 0;
Ref checked;
Ref checked_disabled;
diff --git a/scene/theme/default_theme.cpp b/scene/theme/default_theme.cpp
index 8eb6428ce3f..5b97865a06e 100644
--- a/scene/theme/default_theme.cpp
+++ b/scene/theme/default_theme.cpp
@@ -773,6 +773,7 @@ void fill_default_theme(Ref &theme, const Ref &default_font, const
theme->set_constant("item_start_padding", "PopupMenu", Math::round(2 * scale));
theme->set_constant("item_end_padding", "PopupMenu", Math::round(2 * scale));
theme->set_constant("icon_max_width", "PopupMenu", 0);
+ theme->set_constant("gutter_compact", "PopupMenu", 1);
// GraphNode