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