Make icons of scripted and custom classes fit the editor UI

Also:
- Add an option to limit the icon size in PopupMenu.
This is similar to how this works in Tree and TreeItem.
- Add the same option to TabBar.
- Add a theme constant for Tree, PopupMenu, Button, and
TabBar to apply this limit on the control level.

Co-authored-by: Daylily-Zeleen <daylily-zeleen@foxmail.com>
This commit is contained in:
Yuri Sizov 2023-03-31 21:17:59 +02:00
parent 9fae65404a
commit 1522762dc9
28 changed files with 339 additions and 89 deletions

View File

@ -48,7 +48,7 @@
When this property is enabled, text that is too large to fit the button is clipped, when disabled the Button will always be wide enough to hold the text. When this property is enabled, text that is too large to fit the button is clipped, when disabled the Button will always be wide enough to hold the text.
</member> </member>
<member name="expand_icon" type="bool" setter="set_expand_icon" getter="is_expand_icon" default="false"> <member name="expand_icon" type="bool" setter="set_expand_icon" getter="is_expand_icon" default="false">
When enabled, the button's icon will expand/shrink to fit the button's size while keeping its aspect. When enabled, the button's icon will expand/shrink to fit the button's size while keeping its aspect. See also [theme_item icon_max_width].
</member> </member>
<member name="flat" type="bool" setter="set_flat" getter="is_flat" default="false"> <member name="flat" type="bool" setter="set_flat" getter="is_flat" default="false">
Flat buttons don't display decoration. Flat buttons don't display decoration.
@ -116,6 +116,9 @@
<theme_item name="h_separation" data_type="constant" type="int" default="2"> <theme_item name="h_separation" data_type="constant" type="int" default="2">
The horizontal space between [Button]'s icon and text. Negative values will be treated as [code]0[/code] when used. The horizontal space between [Button]'s icon and text. Negative values will be treated as [code]0[/code] when used.
</theme_item> </theme_item>
<theme_item name="icon_max_width" data_type="constant" type="int" default="0">
The maximum allowed width of the [Button]'s icon. This limit is applied on top of the default size of the icon, or its expanded size if [member expand_icon] is [code]true[/code]. The height is adjusted according to the icon's ratio.
</theme_item>
<theme_item name="outline_size" data_type="constant" type="int" default="0"> <theme_item name="outline_size" data_type="constant" type="int" default="0">
The size of the text outline. The size of the text outline.
[b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended. [b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended.

View File

@ -202,6 +202,13 @@
Returns the icon of the item at the given [param index]. Returns the icon of the item at the given [param index].
</description> </description>
</method> </method>
<method name="get_item_icon_max_width" qualifiers="const">
<return type="int" />
<param index="0" name="index" type="int" />
<description>
Returns the maximum allowed width of the icon for the item at the given [param index].
</description>
</method>
<method name="get_item_id" qualifiers="const"> <method name="get_item_id" qualifiers="const">
<return type="int" /> <return type="int" />
<param index="0" name="index" type="int" /> <param index="0" name="index" type="int" />
@ -397,6 +404,14 @@
Replaces the [Texture2D] icon of the item at the given [param index]. Replaces the [Texture2D] icon of the item at the given [param index].
</description> </description>
</method> </method>
<method name="set_item_icon_max_width">
<return type="void" />
<param index="0" name="index" type="int" />
<param index="1" name="width" type="int" />
<description>
Sets the maximum allowed width of the icon for the item at the given [param index]. This limit is applied on top of the default size of the icon and on top of [theme_item icon_max_width]. The height is adjusted according to the icon's ratio.
</description>
</method>
<method name="set_item_id"> <method name="set_item_id">
<return type="void" /> <return type="void" />
<param index="0" name="index" type="int" /> <param index="0" name="index" type="int" />
@ -573,6 +588,9 @@
<theme_item name="h_separation" data_type="constant" type="int" default="4"> <theme_item name="h_separation" data_type="constant" type="int" default="4">
The horizontal space between the item's elements. The horizontal space between the item's elements.
</theme_item> </theme_item>
<theme_item name="icon_max_width" data_type="constant" type="int" default="0">
The maximum allowed width of the item's icon. This limit is applied on top of the default size of the icon, but before the value set with [method set_item_icon_max_width]. The height is adjusted according to the icon's ratio.
</theme_item>
<theme_item name="indent" data_type="constant" type="int" default="10"> <theme_item name="indent" data_type="constant" type="int" default="10">
Width of the single indentation level. Width of the single indentation level.
</theme_item> </theme_item>

View File

@ -46,14 +46,21 @@
<return type="Texture2D" /> <return type="Texture2D" />
<param index="0" name="tab_idx" type="int" /> <param index="0" name="tab_idx" type="int" />
<description> <description>
Returns the [Texture2D] for the right button of the tab at index [param tab_idx] or [code]null[/code] if the button has no [Texture2D]. Returns the icon for the right button of the tab at index [param tab_idx] or [code]null[/code] if the right button has no icon.
</description> </description>
</method> </method>
<method name="get_tab_icon" qualifiers="const"> <method name="get_tab_icon" qualifiers="const">
<return type="Texture2D" /> <return type="Texture2D" />
<param index="0" name="tab_idx" type="int" /> <param index="0" name="tab_idx" type="int" />
<description> <description>
Returns the [Texture2D] for the tab at index [param tab_idx] or [code]null[/code] if the tab has no [Texture2D]. Returns the icon for the tab at index [param tab_idx] or [code]null[/code] if the tab has no icon.
</description>
</method>
<method name="get_tab_icon_max_width" qualifiers="const">
<return type="int" />
<param index="0" name="tab_idx" type="int" />
<description>
Returns the maximum allowed width of the icon for the tab at index [param tab_idx].
</description> </description>
</method> </method>
<method name="get_tab_idx_at_point" qualifiers="const"> <method name="get_tab_idx_at_point" qualifiers="const">
@ -158,6 +165,14 @@
Sets an [param icon] for the tab at index [param tab_idx]. Sets an [param icon] for the tab at index [param tab_idx].
</description> </description>
</method> </method>
<method name="set_tab_icon_max_width">
<return type="void" />
<param index="0" name="tab_idx" type="int" />
<param index="1" name="width" type="int" />
<description>
Sets the maximum allowed width of the icon for the tab at index [param tab_idx]. This limit is applied on top of the default size of the icon and on top of [theme_item icon_max_width]. The height is adjusted according to the icon's ratio.
</description>
</method>
<method name="set_tab_language"> <method name="set_tab_language">
<return type="void" /> <return type="void" />
<param index="0" name="tab_idx" type="int" /> <param index="0" name="tab_idx" type="int" />
@ -323,6 +338,9 @@
<theme_item name="h_separation" data_type="constant" type="int" default="4"> <theme_item name="h_separation" data_type="constant" type="int" default="4">
The horizontal separation between the elements inside tabs. The horizontal separation between the elements inside tabs.
</theme_item> </theme_item>
<theme_item name="icon_max_width" data_type="constant" type="int" default="0">
The maximum allowed width of the tab's icon. This limit is applied on top of the default size of the icon, but before the value set with [method set_tab_icon_max_width]. The height is adjusted according to the icon's ratio.
</theme_item>
<theme_item name="outline_size" data_type="constant" type="int" default="0"> <theme_item name="outline_size" data_type="constant" type="int" default="0">
The size of the tab text outline. The size of the tab text outline.
[b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended. [b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended.

View File

@ -209,6 +209,9 @@
<theme_item name="font_unselected_color" data_type="color" type="Color" default="Color(0.7, 0.7, 0.7, 1)"> <theme_item name="font_unselected_color" data_type="color" type="Color" default="Color(0.7, 0.7, 0.7, 1)">
Font color of the other, unselected tabs. Font color of the other, unselected tabs.
</theme_item> </theme_item>
<theme_item name="icon_max_width" data_type="constant" type="int" default="0">
The maximum allowed width of the tab's icon. This limit is applied on top of the default size of the icon, but before the value set with [method TabBar.set_tab_icon_max_width]. The height is adjusted according to the icon's ratio.
</theme_item>
<theme_item name="icon_separation" data_type="constant" type="int" default="4"> <theme_item name="icon_separation" data_type="constant" type="int" default="4">
Space between tab's name and its icon. Space between tab's name and its icon.
</theme_item> </theme_item>

View File

@ -518,6 +518,9 @@
<theme_item name="h_separation" data_type="constant" type="int" default="4"> <theme_item name="h_separation" data_type="constant" type="int" default="4">
The horizontal space between item cells. This is also used as the margin at the start of an item when folding is disabled. The horizontal space between item cells. This is also used as the margin at the start of an item when folding is disabled.
</theme_item> </theme_item>
<theme_item name="icon_max_width" data_type="constant" type="int" default="0">
The maximum allowed width of the icon in item's cells. This limit is applied on top of the default size of the icon, but before the value set with [method TreeItem.set_icon_max_width]. The height is adjusted according to the icon's ratio.
</theme_item>
<theme_item name="item_margin" data_type="constant" type="int" default="16"> <theme_item name="item_margin" data_type="constant" type="int" default="16">
The horizontal margin at the start of an item. This is used when folding is enabled for the item. The horizontal margin at the start of an item. This is used when folding is enabled for the item.
</theme_item> </theme_item>

View File

@ -183,7 +183,7 @@
<return type="int" /> <return type="int" />
<param index="0" name="column" type="int" /> <param index="0" name="column" type="int" />
<description> <description>
Returns the column's icon's maximum width. Returns the maximum allowed width of the icon in the given [param column].
</description> </description>
</method> </method>
<method name="get_icon_modulate" qualifiers="const"> <method name="get_icon_modulate" qualifiers="const">
@ -545,7 +545,7 @@
<param index="0" name="column" type="int" /> <param index="0" name="column" type="int" />
<param index="1" name="width" type="int" /> <param index="1" name="width" type="int" />
<description> <description>
Sets the given column's icon's maximum width. Sets the maximum allowed width of the icon in the given [param column]. This limit is applied on top of the default size of the icon and on top of [theme_item Tree.icon_max_width]. The height is adjusted according to the icon's ratio.
</description> </description>
</method> </method>
<method name="set_icon_modulate"> <method name="set_icon_modulate">

View File

@ -462,6 +462,11 @@ void CreateDialog::_notification(int p_what) {
} break; } break;
case NOTIFICATION_THEME_CHANGED: { case NOTIFICATION_THEME_CHANGED: {
const int icon_width = get_theme_constant(SNAME("class_icon_size"), SNAME("Editor"));
search_options->add_theme_constant_override("icon_max_width", icon_width);
favorites->add_theme_constant_override("icon_max_width", icon_width);
recent->set_fixed_icon_size(Size2(icon_width, icon_width));
_update_theme(); _update_theme();
} break; } break;
} }

View File

@ -1030,13 +1030,11 @@ void EditorData::script_class_load_icon_paths() {
} }
} }
Ref<ImageTexture> EditorData::_load_script_icon(const String &p_path) const { Ref<Texture2D> EditorData::_load_script_icon(const String &p_path) const {
if (p_path.length()) { if (!p_path.is_empty() && ResourceLoader::exists(p_path)) {
Ref<Image> img = memnew(Image); Ref<Texture2D> icon = ResourceLoader::load(p_path);
Error err = ImageLoader::load_image(p_path, img); if (icon.is_valid()) {
if (err == OK) { return icon;
img->resize(16 * EDSCALE, 16 * EDSCALE, Image::INTERPOLATE_LANCZOS);
return ImageTexture::create_from_image(img);
} }
} }
return nullptr; return nullptr;
@ -1051,9 +1049,9 @@ Ref<Texture2D> EditorData::get_script_icon(const Ref<Script> &p_script) {
Ref<Script> base_scr = p_script; Ref<Script> base_scr = p_script;
while (base_scr.is_valid()) { while (base_scr.is_valid()) {
// Check for scripted classes. // Check for scripted classes.
StringName name = script_class_get_name(base_scr->get_path()); StringName class_name = script_class_get_name(base_scr->get_path());
String icon_path = script_class_get_icon_path(name); String icon_path = script_class_get_icon_path(class_name);
Ref<ImageTexture> icon = _load_script_icon(icon_path); Ref<Texture2D> icon = _load_script_icon(icon_path);
if (icon.is_valid()) { if (icon.is_valid()) {
_script_icon_cache[p_script] = icon; _script_icon_cache[p_script] = icon;
return icon; return icon;

View File

@ -146,7 +146,7 @@ private:
HashMap<String, StringName> _script_class_file_to_path; HashMap<String, StringName> _script_class_file_to_path;
HashMap<Ref<Script>, Ref<Texture>> _script_icon_cache; HashMap<Ref<Script>, Ref<Texture>> _script_icon_cache;
Ref<ImageTexture> _load_script_icon(const String &p_path) const; Ref<Texture2D> _load_script_icon(const String &p_path) const;
public: public:
EditorPlugin *get_editor(Object *p_object); EditorPlugin *get_editor(Object *p_object);

View File

@ -668,7 +668,7 @@ void EditorHelp::_update_doc() {
String inherits = cd.inherits; String inherits = cd.inherits;
while (!inherits.is_empty()) { while (!inherits.is_empty()) {
_add_type_icon(inherits, 0, "ArrowRight"); _add_type_icon(inherits, theme_cache.doc_font_size, "ArrowRight");
class_desc->add_text(non_breaking_space); // Otherwise icon borrows hyperlink from _add_type(). class_desc->add_text(non_breaking_space); // Otherwise icon borrows hyperlink from _add_type().
_add_type(inherits); _add_type(inherits);
@ -701,7 +701,7 @@ void EditorHelp::_update_doc() {
if (prev) { if (prev) {
class_desc->add_text(" , "); class_desc->add_text(" , ");
} }
_add_type_icon(E.value.name, 0, "ArrowRight"); _add_type_icon(E.value.name, theme_cache.doc_font_size, "ArrowRight");
class_desc->add_text(non_breaking_space); // Otherwise icon borrows hyperlink from _add_type(). class_desc->add_text(non_breaking_space); // Otherwise icon borrows hyperlink from _add_type().
_add_type(E.value.name); _add_type(E.value.name);
prev = true; prev = true;

View File

@ -1134,17 +1134,21 @@ void EditorInspectorCategory::_notification(int p_what) {
int font_size = get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts")); int font_size = get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts"));
int hs = get_theme_constant(SNAME("h_separation"), SNAME("Tree")); int hs = get_theme_constant(SNAME("h_separation"), SNAME("Tree"));
int icon_size = get_theme_constant(SNAME("class_icon_size"), SNAME("Editor"));
int w = font->get_string_size(label, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width; int w = font->get_string_size(label, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width;
if (icon.is_valid()) { if (icon.is_valid()) {
w += hs + icon->get_width(); w += hs + icon_size;
} }
int ofs = (get_size().width - w) / 2; int ofs = (get_size().width - w) / 2;
if (icon.is_valid()) { if (icon.is_valid()) {
draw_texture(icon, Point2(ofs, (get_size().height - icon->get_height()) / 2).floor()); Size2 rect_size = Size2(icon_size, icon_size);
ofs += hs + icon->get_width(); Point2 rect_pos = Point2(ofs, (get_size().height - icon_size) / 2).floor();
draw_texture_rect(icon, Rect2(rect_pos, rect_size));
ofs += hs + icon_size;
} }
Color color = get_theme_color(SNAME("font_color"), SNAME("Tree")); Color color = get_theme_color(SNAME("font_color"), SNAME("Tree"));

View File

@ -681,10 +681,6 @@ void EditorNode::_notification(int p_what) {
editor_data.clear_edited_scenes(); editor_data.clear_edited_scenes();
} break; } break;
case Control::NOTIFICATION_THEME_CHANGED: {
scene_tab_add_ph->set_custom_minimum_size(scene_tab_add->get_minimum_size());
} break;
case NOTIFICATION_READY: { case NOTIFICATION_READY: {
{ {
_initializing_plugins = true; _initializing_plugins = true;
@ -772,6 +768,9 @@ void EditorNode::_notification(int p_what) {
bottom_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("BottomPanel"), SNAME("EditorStyles"))); bottom_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("BottomPanel"), SNAME("EditorStyles")));
tabbar_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("tabbar_background"), SNAME("TabContainer"))); tabbar_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("tabbar_background"), SNAME("TabContainer")));
scene_tabs->add_theme_constant_override("icon_max_width", gui_base->get_theme_constant(SNAME("class_icon_size"), SNAME("Editor")));
scene_tab_add_ph->set_custom_minimum_size(scene_tab_add->get_minimum_size());
main_menu->add_theme_style_override("hover", gui_base->get_theme_stylebox(SNAME("MenuHover"), SNAME("EditorStyles"))); main_menu->add_theme_style_override("hover", gui_base->get_theme_stylebox(SNAME("MenuHover"), SNAME("EditorStyles")));
} }
@ -7114,6 +7113,7 @@ EditorNode::EditorNode() {
scene_tabs->set_select_with_rmb(true); scene_tabs->set_select_with_rmb(true);
scene_tabs->add_tab("unsaved"); scene_tabs->add_tab("unsaved");
scene_tabs->set_tab_close_display_policy((TabBar::CloseButtonDisplayPolicy)EDITOR_GET("interface/scene_tabs/display_close_button").operator int()); scene_tabs->set_tab_close_display_policy((TabBar::CloseButtonDisplayPolicy)EDITOR_GET("interface/scene_tabs/display_close_button").operator int());
scene_tabs->add_theme_constant_override("icon_max_width", gui_base->get_theme_constant(SNAME("class_icon_size"), SNAME("Editor")));
scene_tabs->set_max_tab_width(int(EDITOR_GET("interface/scene_tabs/maximum_width")) * EDSCALE); scene_tabs->set_max_tab_width(int(EDITOR_GET("interface/scene_tabs/maximum_width")) * EDSCALE);
scene_tabs->set_drag_to_rearrange_enabled(true); scene_tabs->set_drag_to_rearrange_enabled(true);
scene_tabs->set_auto_translate(false); scene_tabs->set_auto_translate(false);

View File

@ -201,8 +201,12 @@ void EditorPath::_notification(int p_what) {
case NOTIFICATION_THEME_CHANGED: { case NOTIFICATION_THEME_CHANGED: {
update_path(); update_path();
sub_objects_icon->set_texture(get_theme_icon(SNAME("arrow"), SNAME("OptionButton"))); int icon_size = get_theme_constant(SNAME("class_icon_size"), SNAME("Editor"));
current_object_icon->set_custom_minimum_size(Size2(icon_size, icon_size));
current_object_label->add_theme_font_override("font", get_theme_font(SNAME("main"), SNAME("EditorFonts"))); current_object_label->add_theme_font_override("font", get_theme_font(SNAME("main"), SNAME("EditorFonts")));
sub_objects_icon->set_texture(get_theme_icon(SNAME("arrow"), SNAME("OptionButton")));
sub_objects_menu->add_theme_constant_override("icon_max_width", icon_size);
} break; } break;
case NOTIFICATION_READY: { case NOTIFICATION_READY: {
@ -227,7 +231,8 @@ EditorPath::EditorPath(EditorSelectionHistory *p_history) {
main_mc->add_child(main_hb); main_mc->add_child(main_hb);
current_object_icon = memnew(TextureRect); current_object_icon = memnew(TextureRect);
current_object_icon->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED); current_object_icon->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
current_object_icon->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
main_hb->add_child(current_object_icon); main_hb->add_child(current_object_icon);
current_object_label = memnew(Label); current_object_label = memnew(Label);

View File

@ -784,6 +784,7 @@ void EditorResourcePicker::_notification(int p_what) {
[[fallthrough]]; [[fallthrough]];
} }
case NOTIFICATION_THEME_CHANGED: { case NOTIFICATION_THEME_CHANGED: {
assign_button->add_theme_constant_override("icon_max_width", get_theme_constant(SNAME("class_icon_size"), SNAME("Editor")));
edit_button->set_icon(get_theme_icon(SNAME("select_arrow"), SNAME("Tree"))); edit_button->set_icon(get_theme_icon(SNAME("select_arrow"), SNAME("Tree")));
} break; } break;
@ -923,6 +924,7 @@ EditorResourcePicker::EditorResourcePicker(bool p_hide_assign_button_controls) {
assign_button = memnew(Button); assign_button = memnew(Button);
assign_button->set_flat(true); assign_button->set_flat(true);
assign_button->set_h_size_flags(SIZE_EXPAND_FILL); assign_button->set_h_size_flags(SIZE_EXPAND_FILL);
assign_button->set_expand_icon(true);
assign_button->set_clip_text(true); assign_button->set_clip_text(true);
assign_button->set_auto_translate(false); assign_button->set_auto_translate(false);
SET_DRAG_FORWARDING_GCD(assign_button, EditorResourcePicker); SET_DRAG_FORWARDING_GCD(assign_button, EditorResourcePicker);

View File

@ -587,9 +587,11 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
} else { } else {
theme->set_color("highend_color", "Editor", Color(1.0, 0.0, 0.0)); theme->set_color("highend_color", "Editor", Color(1.0, 0.0, 0.0));
} }
const int thumb_size = EDITOR_GET("filesystem/file_dialog/thumbnail_size"); const int thumb_size = EDITOR_GET("filesystem/file_dialog/thumbnail_size");
theme->set_constant("scale", "Editor", EDSCALE); theme->set_constant("scale", "Editor", EDSCALE);
theme->set_constant("thumb_size", "Editor", thumb_size); theme->set_constant("thumb_size", "Editor", thumb_size);
theme->set_constant("class_icon_size", "Editor", 16 * EDSCALE);
theme->set_constant("dark_theme", "Editor", dark_theme); theme->set_constant("dark_theme", "Editor", dark_theme);
theme->set_constant("color_picker_button_height", "Editor", 28 * EDSCALE); theme->set_constant("color_picker_button_height", "Editor", 28 * EDSCALE);

View File

@ -1278,13 +1278,6 @@ void SceneTreeDock::_notification(int p_what) {
spatial_editor_plugin->get_spatial_editor()->connect("item_lock_status_changed", callable_mp(scene_tree, &SceneTreeEditor::_update_tree).bind(false)); spatial_editor_plugin->get_spatial_editor()->connect("item_lock_status_changed", callable_mp(scene_tree, &SceneTreeEditor::_update_tree).bind(false));
spatial_editor_plugin->get_spatial_editor()->connect("item_group_status_changed", callable_mp(scene_tree, &SceneTreeEditor::_update_tree).bind(false)); spatial_editor_plugin->get_spatial_editor()->connect("item_group_status_changed", callable_mp(scene_tree, &SceneTreeEditor::_update_tree).bind(false));
button_add->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
button_instance->set_icon(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")));
button_create_script->set_icon(get_theme_icon(SNAME("ScriptCreate"), SNAME("EditorIcons")));
button_detach_script->set_icon(get_theme_icon(SNAME("ScriptRemove"), SNAME("EditorIcons")));
button_tree_menu->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons")));
filter->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
filter->set_clear_button_enabled(true); filter->set_clear_button_enabled(true);
// create_root_dialog // create_root_dialog
@ -1366,19 +1359,35 @@ void SceneTreeDock::_notification(int p_what) {
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
scene_tree->set_auto_expand_selected(EDITOR_GET("docks/scene_tree/auto_expand_to_selected"), false); scene_tree->set_auto_expand_selected(EDITOR_GET("docks/scene_tree/auto_expand_to_selected"), false);
} break;
case NOTIFICATION_THEME_CHANGED: {
button_add->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); button_add->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
button_instance->set_icon(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons"))); button_instance->set_icon(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")));
button_create_script->set_icon(get_theme_icon(SNAME("ScriptCreate"), SNAME("EditorIcons"))); button_create_script->set_icon(get_theme_icon(SNAME("ScriptCreate"), SNAME("EditorIcons")));
button_detach_script->set_icon(get_theme_icon(SNAME("ScriptRemove"), SNAME("EditorIcons"))); button_detach_script->set_icon(get_theme_icon(SNAME("ScriptRemove"), SNAME("EditorIcons")));
button_tree_menu->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons"))); button_tree_menu->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons")));
button_2d->set_icon(get_theme_icon(SNAME("Node2D"), SNAME("EditorIcons")));
button_3d->set_icon(get_theme_icon(SNAME("Node3D"), SNAME("EditorIcons")));
button_ui->set_icon(get_theme_icon(SNAME("Control"), SNAME("EditorIcons")));
button_custom->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
button_clipboard->set_icon(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons")));
filter->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); filter->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
filter->set_clear_button_enabled(true);
// These buttons are created on READY, because reasons...
if (button_2d) {
button_2d->set_icon(get_theme_icon(SNAME("Node2D"), SNAME("EditorIcons")));
}
if (button_3d) {
button_3d->set_icon(get_theme_icon(SNAME("Node3D"), SNAME("EditorIcons")));
}
if (button_ui) {
button_ui->set_icon(get_theme_icon(SNAME("Control"), SNAME("EditorIcons")));
}
if (button_custom) {
button_custom->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
}
if (button_clipboard) {
button_clipboard->set_icon(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons")));
}
menu_subresources->add_theme_constant_override("icon_max_width", get_theme_constant(SNAME("class_icon_size"), SNAME("Editor")));
} break; } break;
case NOTIFICATION_PROCESS: { case NOTIFICATION_PROCESS: {

View File

@ -878,6 +878,8 @@ void SceneTreeEditor::_notification(int p_what) {
} break; } break;
case NOTIFICATION_THEME_CHANGED: { case NOTIFICATION_THEME_CHANGED: {
tree->add_theme_constant_override("icon_max_width", get_theme_constant(SNAME("class_icon_size"), SNAME("Editor")));
_update_tree(); _update_tree();
} break; } break;
} }

View File

@ -83,6 +83,7 @@ void Button::_update_theme_item_cache() {
theme_cache.icon = get_theme_icon(SNAME("icon")); theme_cache.icon = get_theme_icon(SNAME("icon"));
theme_cache.h_separation = get_theme_constant(SNAME("h_separation")); theme_cache.h_separation = get_theme_constant(SNAME("h_separation"));
theme_cache.icon_max_width = get_theme_constant(SNAME("icon_max_width"));
} }
void Button::_notification(int p_what) { void Button::_notification(int p_what) {
@ -252,7 +253,6 @@ void Button::_notification(int p_what) {
float icon_ofs_region = 0.0; float icon_ofs_region = 0.0;
Point2 style_offset; Point2 style_offset;
Size2 icon_size = _icon->get_size();
if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_LEFT) { if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_LEFT) {
style_offset.x = style->get_margin(SIDE_LEFT); style_offset.x = style->get_margin(SIDE_LEFT);
if (_internal_margin[SIDE_LEFT] > 0) { if (_internal_margin[SIDE_LEFT] > 0) {
@ -268,6 +268,7 @@ void Button::_notification(int p_what) {
} }
style_offset.y = style->get_margin(SIDE_TOP); style_offset.y = style->get_margin(SIDE_TOP);
Size2 icon_size = _icon->get_size();
if (expand_icon) { if (expand_icon) {
Size2 _size = get_size() - style->get_offset() * 2; Size2 _size = get_size() - style->get_offset() * 2;
int icon_text_separation = text.is_empty() ? 0 : theme_cache.h_separation; int icon_text_separation = text.is_empty() ? 0 : theme_cache.h_separation;
@ -285,6 +286,7 @@ void Button::_notification(int p_what) {
icon_size = Size2(icon_width, icon_height); icon_size = Size2(icon_width, icon_height);
} }
icon_size = _fit_icon_size(icon_size);
if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_LEFT) { if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_LEFT) {
icon_region = Rect2(style_offset + Point2(icon_ofs_region, Math::floor((valign - icon_size.y) * 0.5)), icon_size); icon_region = Rect2(style_offset + Point2(icon_ofs_region, Math::floor((valign - icon_size.y) * 0.5)), icon_size);
@ -365,6 +367,18 @@ void Button::_notification(int p_what) {
} }
} }
Size2 Button::_fit_icon_size(const Size2 &p_size) const {
int max_width = theme_cache.icon_max_width;
Size2 icon_size = p_size;
if (max_width > 0 && icon_size.width > max_width) {
icon_size.height = icon_size.height * max_width / icon_size.width;
icon_size.width = max_width;
}
return icon_size;
}
Size2 Button::get_minimum_size_for_text_and_icon(const String &p_text, Ref<Texture2D> p_icon) const { Size2 Button::get_minimum_size_for_text_and_icon(const String &p_text, Ref<Texture2D> p_icon) const {
Ref<TextParagraph> paragraph; Ref<TextParagraph> paragraph;
if (p_text.is_empty()) { if (p_text.is_empty()) {
@ -380,15 +394,16 @@ Size2 Button::get_minimum_size_for_text_and_icon(const String &p_text, Ref<Textu
} }
if (!expand_icon && p_icon.is_valid()) { if (!expand_icon && p_icon.is_valid()) {
minsize.height = MAX(minsize.height, p_icon->get_height()); Size2 icon_size = _fit_icon_size(p_icon->get_size());
minsize.height = MAX(minsize.height, icon_size.height);
if (icon_alignment != HORIZONTAL_ALIGNMENT_CENTER) { if (icon_alignment != HORIZONTAL_ALIGNMENT_CENTER) {
minsize.width += p_icon->get_width(); minsize.width += icon_size.width;
if (!xl_text.is_empty() || !p_text.is_empty()) { if (!xl_text.is_empty() || !p_text.is_empty()) {
minsize.width += MAX(0, theme_cache.h_separation); minsize.width += MAX(0, theme_cache.h_separation);
} }
} else { } else {
minsize.width = MAX(minsize.width, p_icon->get_width()); minsize.width = MAX(minsize.width, icon_size.width);
} }
} }

View File

@ -89,8 +89,11 @@ private:
Ref<Texture2D> icon; Ref<Texture2D> icon;
int h_separation = 0; int h_separation = 0;
int icon_max_width = 0;
} theme_cache; } theme_cache;
Size2 _fit_icon_size(const Size2 &p_size) const;
void _shape(Ref<TextParagraph> p_paragraph = Ref<TextParagraph>(), String p_text = ""); void _shape(Ref<TextParagraph> p_paragraph = Ref<TextParagraph>(), String p_text = "");
protected: protected:

View File

@ -47,6 +47,26 @@ String PopupMenu::_get_accel_text(const Item &p_item) const {
return String(); return String();
} }
Size2 PopupMenu::_get_item_icon_size(int p_item) const {
const PopupMenu::Item &item = items[p_item];
Size2 icon_size = item.get_icon_size();
int max_width = 0;
if (theme_cache.icon_max_width > 0) {
max_width = theme_cache.icon_max_width;
}
if (item.icon_max_width > 0 && (max_width == 0 || item.icon_max_width < max_width)) {
max_width = item.icon_max_width;
}
if (max_width > 0 && icon_size.width > max_width) {
icon_size.height = icon_size.height * max_width / icon_size.width;
icon_size.width = max_width;
}
return icon_size;
}
Size2 PopupMenu::_get_contents_minimum_size() const { Size2 PopupMenu::_get_contents_minimum_size() const {
Size2 minsize = theme_cache.panel_style->get_minimum_size(); // Accounts for margin in the margin container Size2 minsize = theme_cache.panel_style->get_minimum_size(); // Accounts for margin in the margin container
minsize.x += scroll_container->get_v_scroll_bar()->get_size().width * 2; // Adds a buffer so that the scrollbar does not render over the top of content minsize.x += scroll_container->get_v_scroll_bar()->get_size().width * 2; // Adds a buffer so that the scrollbar does not render over the top of content
@ -61,7 +81,7 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
Size2 item_size; Size2 item_size;
const_cast<PopupMenu *>(this)->_shape_item(i); const_cast<PopupMenu *>(this)->_shape_item(i);
Size2 icon_size = items[i].get_icon_size(); Size2 icon_size = _get_item_icon_size(i);
item_size.height = _get_item_height(i); item_size.height = _get_item_height(i);
icon_w = MAX(icon_size.width, icon_w); icon_w = MAX(icon_size.width, icon_w);
@ -109,7 +129,8 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
int PopupMenu::_get_item_height(int p_item) const { int PopupMenu::_get_item_height(int p_item) const {
ERR_FAIL_INDEX_V(p_item, items.size(), 0); ERR_FAIL_INDEX_V(p_item, items.size(), 0);
int icon_height = items[p_item].get_icon_size().height; Size2 icon_size = _get_item_icon_size(p_item);
int icon_height = icon_size.height;
if (items[p_item].checkable_type && !items[p_item].separator) { if (items[p_item].checkable_type && !items[p_item].separator) {
icon_height = MAX(icon_height, MAX(theme_cache.checked->get_height(), theme_cache.radio_checked->get_height())); icon_height = MAX(icon_height, MAX(theme_cache.checked->get_height(), theme_cache.radio_checked->get_height()));
} }
@ -540,7 +561,8 @@ void PopupMenu::_draw_items() {
continue; continue;
} }
icon_ofs = MAX(items[i].get_icon_size().width, icon_ofs); Size2 icon_size = _get_item_icon_size(i);
icon_ofs = MAX(icon_size.width, icon_ofs);
if (items[i].checkable_type) { if (items[i].checkable_type) {
has_check = true; has_check = true;
@ -569,7 +591,7 @@ void PopupMenu::_draw_items() {
_shape_item(i); _shape_item(i);
Point2 item_ofs = ofs; Point2 item_ofs = ofs;
Size2 icon_size = items[i].get_icon_size(); Size2 icon_size = _get_item_icon_size(i);
float h = _get_item_height(i); float h = _get_item_height(i);
if (i == mouse_over) { if (i == mouse_over) {
@ -631,21 +653,26 @@ void PopupMenu::_draw_items() {
// Icon // Icon
if (!items[i].icon.is_null()) { if (!items[i].icon.is_null()) {
const Point2 icon_offset = Point2(0, Math::floor((h - icon_size.height) / 2.0));
Point2 icon_pos;
if (items[i].separator) { if (items[i].separator) {
separator_ofs -= (icon_size.width + theme_cache.h_separation) / 2; separator_ofs -= (icon_size.width + theme_cache.h_separation) / 2;
if (rtl) { if (rtl) {
items[i].icon->draw(ci, Size2(control->get_size().width - item_ofs.x - separator_ofs - icon_size.width, item_ofs.y) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color); icon_pos = Size2(control->get_size().width - item_ofs.x - separator_ofs - icon_size.width, item_ofs.y);
} else { } else {
items[i].icon->draw(ci, item_ofs + Size2(separator_ofs, 0) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color); icon_pos = item_ofs + Size2(separator_ofs, 0);
} }
} else { } else {
if (rtl) { if (rtl) {
items[i].icon->draw(ci, Size2(control->get_size().width - item_ofs.x - check_ofs - icon_size.width, item_ofs.y) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color); icon_pos = Size2(control->get_size().width - item_ofs.x - check_ofs - icon_size.width, item_ofs.y);
} else { } else {
items[i].icon->draw(ci, item_ofs + Size2(check_ofs, 0) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color); icon_pos = item_ofs + Size2(check_ofs, 0);
} }
} }
items[i].icon->draw_rect(ci, Rect2(icon_pos + icon_offset, icon_size), false, icon_color);
} }
// Submenu arrow on right hand side. // Submenu arrow on right hand side.
@ -802,6 +829,7 @@ void PopupMenu::_update_theme_item_cache() {
theme_cache.indent = get_theme_constant(SNAME("indent")); theme_cache.indent = get_theme_constant(SNAME("indent"));
theme_cache.item_start_padding = get_theme_constant(SNAME("item_start_padding")); theme_cache.item_start_padding = get_theme_constant(SNAME("item_start_padding"));
theme_cache.item_end_padding = get_theme_constant(SNAME("item_end_padding")); theme_cache.item_end_padding = get_theme_constant(SNAME("item_end_padding"));
theme_cache.icon_max_width = get_theme_constant(SNAME("icon_max_width"));
theme_cache.checked = get_theme_icon(SNAME("checked")); theme_cache.checked = get_theme_icon(SNAME("checked"));
theme_cache.checked_disabled = get_theme_icon(SNAME("checked_disabled")); theme_cache.checked_disabled = get_theme_icon(SNAME("checked_disabled"));
@ -946,8 +974,10 @@ void PopupMenu::add_item(const String &p_label, int p_id, Key p_accel) {
Item item; Item item;
ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel); ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel);
items.push_back(item); items.push_back(item);
_shape_item(items.size() - 1); _shape_item(items.size() - 1);
control->queue_redraw(); control->queue_redraw();
child_controls_changed(); child_controls_changed();
notify_property_list_changed(); notify_property_list_changed();
_menu_changed(); _menu_changed();
@ -958,8 +988,10 @@ void PopupMenu::add_icon_item(const Ref<Texture2D> &p_icon, const String &p_labe
ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel); ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel);
item.icon = p_icon; item.icon = p_icon;
items.push_back(item); items.push_back(item);
_shape_item(items.size() - 1); _shape_item(items.size() - 1);
control->queue_redraw(); control->queue_redraw();
child_controls_changed(); child_controls_changed();
notify_property_list_changed(); notify_property_list_changed();
_menu_changed(); _menu_changed();
@ -970,8 +1002,10 @@ void PopupMenu::add_check_item(const String &p_label, int p_id, Key p_accel) {
ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel); ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel);
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item); items.push_back(item);
_shape_item(items.size() - 1); _shape_item(items.size() - 1);
control->queue_redraw(); control->queue_redraw();
child_controls_changed(); child_controls_changed();
_menu_changed(); _menu_changed();
} }
@ -982,8 +1016,10 @@ void PopupMenu::add_icon_check_item(const Ref<Texture2D> &p_icon, const String &
item.icon = p_icon; item.icon = p_icon;
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item); items.push_back(item);
_shape_item(items.size() - 1); _shape_item(items.size() - 1);
control->queue_redraw(); control->queue_redraw();
child_controls_changed(); child_controls_changed();
} }
@ -992,8 +1028,10 @@ void PopupMenu::add_radio_check_item(const String &p_label, int p_id, Key p_acce
ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel); ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel);
item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
items.push_back(item); items.push_back(item);
_shape_item(items.size() - 1); _shape_item(items.size() - 1);
control->queue_redraw(); control->queue_redraw();
child_controls_changed(); child_controls_changed();
_menu_changed(); _menu_changed();
} }
@ -1004,8 +1042,10 @@ void PopupMenu::add_icon_radio_check_item(const Ref<Texture2D> &p_icon, const St
item.icon = p_icon; item.icon = p_icon;
item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
items.push_back(item); items.push_back(item);
_shape_item(items.size() - 1); _shape_item(items.size() - 1);
control->queue_redraw(); control->queue_redraw();
child_controls_changed(); child_controls_changed();
_menu_changed(); _menu_changed();
} }
@ -1016,8 +1056,10 @@ void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int
item.max_states = p_max_states; item.max_states = p_max_states;
item.state = p_default_state; item.state = p_default_state;
items.push_back(item); items.push_back(item);
_shape_item(items.size() - 1); _shape_item(items.size() - 1);
control->queue_redraw(); control->queue_redraw();
child_controls_changed(); child_controls_changed();
_menu_changed(); _menu_changed();
} }
@ -1035,8 +1077,10 @@ void PopupMenu::add_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_g
Item item; Item item;
ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global); ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
items.push_back(item); items.push_back(item);
_shape_item(items.size() - 1); _shape_item(items.size() - 1);
control->queue_redraw(); control->queue_redraw();
child_controls_changed(); child_controls_changed();
_menu_changed(); _menu_changed();
} }
@ -1046,8 +1090,10 @@ void PopupMenu::add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortc
ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global); ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
item.icon = p_icon; item.icon = p_icon;
items.push_back(item); items.push_back(item);
_shape_item(items.size() - 1); _shape_item(items.size() - 1);
control->queue_redraw(); control->queue_redraw();
child_controls_changed(); child_controls_changed();
_menu_changed(); _menu_changed();
} }
@ -1057,8 +1103,10 @@ void PopupMenu::add_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bo
ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global); ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item); items.push_back(item);
_shape_item(items.size() - 1); _shape_item(items.size() - 1);
control->queue_redraw(); control->queue_redraw();
child_controls_changed(); child_controls_changed();
_menu_changed(); _menu_changed();
} }
@ -1069,8 +1117,10 @@ void PopupMenu::add_icon_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<
item.icon = p_icon; item.icon = p_icon;
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item); items.push_back(item);
_shape_item(items.size() - 1); _shape_item(items.size() - 1);
control->queue_redraw(); control->queue_redraw();
child_controls_changed(); child_controls_changed();
_menu_changed(); _menu_changed();
} }
@ -1080,8 +1130,10 @@ void PopupMenu::add_radio_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_
ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global); ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
items.push_back(item); items.push_back(item);
_shape_item(items.size() - 1); _shape_item(items.size() - 1);
control->queue_redraw(); control->queue_redraw();
child_controls_changed(); child_controls_changed();
_menu_changed(); _menu_changed();
} }
@ -1092,8 +1144,10 @@ void PopupMenu::add_icon_radio_check_shortcut(const Ref<Texture2D> &p_icon, cons
item.icon = p_icon; item.icon = p_icon;
item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
items.push_back(item); items.push_back(item);
_shape_item(items.size() - 1); _shape_item(items.size() - 1);
control->queue_redraw(); control->queue_redraw();
child_controls_changed(); child_controls_changed();
_menu_changed(); _menu_changed();
} }
@ -1105,8 +1159,10 @@ void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu,
item.id = p_id == -1 ? items.size() : p_id; item.id = p_id == -1 ? items.size() : p_id;
item.submenu = p_submenu; item.submenu = p_submenu;
items.push_back(item); items.push_back(item);
_shape_item(items.size() - 1); _shape_item(items.size() - 1);
control->queue_redraw(); control->queue_redraw();
child_controls_changed(); child_controls_changed();
_menu_changed(); _menu_changed();
} }
@ -1176,6 +1232,23 @@ void PopupMenu::set_item_icon(int p_idx, const Ref<Texture2D> &p_icon) {
_menu_changed(); _menu_changed();
} }
void PopupMenu::set_item_icon_max_width(int p_idx, int p_width) {
if (p_idx < 0) {
p_idx += get_item_count();
}
ERR_FAIL_INDEX(p_idx, items.size());
if (items[p_idx].icon_max_width == p_width) {
return;
}
items.write[p_idx].icon_max_width = p_width;
control->queue_redraw();
child_controls_changed();
_menu_changed();
}
void PopupMenu::set_item_checked(int p_idx, bool p_checked) { void PopupMenu::set_item_checked(int p_idx, bool p_checked) {
if (p_idx < 0) { if (p_idx < 0) {
p_idx += get_item_count(); p_idx += get_item_count();
@ -1314,6 +1387,11 @@ Ref<Texture2D> PopupMenu::get_item_icon(int p_idx) const {
return items[p_idx].icon; return items[p_idx].icon;
} }
int PopupMenu::get_item_icon_max_width(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, items.size(), 0);
return items[p_idx].icon_max_width;
}
Key PopupMenu::get_item_accelerator(int p_idx) const { Key PopupMenu::get_item_accelerator(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, items.size(), Key::NONE); ERR_FAIL_INDEX_V(p_idx, items.size(), Key::NONE);
return items[p_idx].accel; return items[p_idx].accel;
@ -2023,6 +2101,7 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_item_text_direction", "index", "direction"), &PopupMenu::set_item_text_direction); ClassDB::bind_method(D_METHOD("set_item_text_direction", "index", "direction"), &PopupMenu::set_item_text_direction);
ClassDB::bind_method(D_METHOD("set_item_language", "index", "language"), &PopupMenu::set_item_language); ClassDB::bind_method(D_METHOD("set_item_language", "index", "language"), &PopupMenu::set_item_language);
ClassDB::bind_method(D_METHOD("set_item_icon", "index", "icon"), &PopupMenu::set_item_icon); ClassDB::bind_method(D_METHOD("set_item_icon", "index", "icon"), &PopupMenu::set_item_icon);
ClassDB::bind_method(D_METHOD("set_item_icon_max_width", "index", "width"), &PopupMenu::set_item_icon_max_width);
ClassDB::bind_method(D_METHOD("set_item_checked", "index", "checked"), &PopupMenu::set_item_checked); ClassDB::bind_method(D_METHOD("set_item_checked", "index", "checked"), &PopupMenu::set_item_checked);
ClassDB::bind_method(D_METHOD("set_item_id", "index", "id"), &PopupMenu::set_item_id); ClassDB::bind_method(D_METHOD("set_item_id", "index", "id"), &PopupMenu::set_item_id);
ClassDB::bind_method(D_METHOD("set_item_accelerator", "index", "accel"), &PopupMenu::set_item_accelerator); ClassDB::bind_method(D_METHOD("set_item_accelerator", "index", "accel"), &PopupMenu::set_item_accelerator);
@ -2045,6 +2124,7 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_item_text_direction", "index"), &PopupMenu::get_item_text_direction); ClassDB::bind_method(D_METHOD("get_item_text_direction", "index"), &PopupMenu::get_item_text_direction);
ClassDB::bind_method(D_METHOD("get_item_language", "index"), &PopupMenu::get_item_language); ClassDB::bind_method(D_METHOD("get_item_language", "index"), &PopupMenu::get_item_language);
ClassDB::bind_method(D_METHOD("get_item_icon", "index"), &PopupMenu::get_item_icon); ClassDB::bind_method(D_METHOD("get_item_icon", "index"), &PopupMenu::get_item_icon);
ClassDB::bind_method(D_METHOD("get_item_icon_max_width", "index"), &PopupMenu::get_item_icon_max_width);
ClassDB::bind_method(D_METHOD("is_item_checked", "index"), &PopupMenu::is_item_checked); ClassDB::bind_method(D_METHOD("is_item_checked", "index"), &PopupMenu::is_item_checked);
ClassDB::bind_method(D_METHOD("get_item_id", "index"), &PopupMenu::get_item_id); ClassDB::bind_method(D_METHOD("get_item_id", "index"), &PopupMenu::get_item_id);
ClassDB::bind_method(D_METHOD("get_item_index", "id"), &PopupMenu::get_item_index); ClassDB::bind_method(D_METHOD("get_item_index", "id"), &PopupMenu::get_item_index);

View File

@ -42,6 +42,7 @@ class PopupMenu : public Popup {
struct Item { struct Item {
Ref<Texture2D> icon; Ref<Texture2D> icon;
int icon_max_width = 0;
String text; String text;
String xl_text; String xl_text;
Ref<TextLine> text_buf; Ref<TextLine> text_buf;
@ -103,6 +104,7 @@ class PopupMenu : public Popup {
int _get_item_height(int p_item) const; int _get_item_height(int p_item) const;
int _get_items_total_height() const; int _get_items_total_height() const;
Size2 _get_item_icon_size(int p_item) const;
void _shape_item(int p_item); void _shape_item(int p_item);
@ -144,6 +146,7 @@ class PopupMenu : public Popup {
int indent = 0; int indent = 0;
int item_start_padding = 0; int item_start_padding = 0;
int item_end_padding = 0; int item_end_padding = 0;
int icon_max_width = 0;
Ref<Texture2D> checked; Ref<Texture2D> checked;
Ref<Texture2D> checked_disabled; Ref<Texture2D> checked_disabled;
@ -222,6 +225,7 @@ public:
void set_item_text_direction(int p_idx, Control::TextDirection p_text_direction); void set_item_text_direction(int p_idx, Control::TextDirection p_text_direction);
void set_item_language(int p_idx, const String &p_language); void set_item_language(int p_idx, const String &p_language);
void set_item_icon(int p_idx, const Ref<Texture2D> &p_icon); void set_item_icon(int p_idx, const Ref<Texture2D> &p_icon);
void set_item_icon_max_width(int p_idx, int p_width);
void set_item_checked(int p_idx, bool p_checked); void set_item_checked(int p_idx, bool p_checked);
void set_item_id(int p_idx, int p_id); void set_item_id(int p_idx, int p_id);
void set_item_accelerator(int p_idx, Key p_accel); void set_item_accelerator(int p_idx, Key p_accel);
@ -245,6 +249,7 @@ public:
String get_item_language(int p_idx) const; String get_item_language(int p_idx) const;
int get_item_idx_from_text(const String &text) const; int get_item_idx_from_text(const String &text) const;
Ref<Texture2D> get_item_icon(int p_idx) const; Ref<Texture2D> get_item_icon(int p_idx) const;
int get_item_icon_max_width(int p_idx) const;
bool is_item_checked(int p_idx) const; bool is_item_checked(int p_idx) const;
int get_item_id(int p_idx) const; int get_item_id(int p_idx) const;
int get_item_index(int p_id) const; int get_item_index(int p_id) const;

View File

@ -63,10 +63,10 @@ Size2 TabBar::get_minimum_size() const {
} }
ms.width += style->get_minimum_size().width; ms.width += style->get_minimum_size().width;
Ref<Texture2D> tex = tabs[i].icon; if (tabs[i].icon.is_valid()) {
if (tex.is_valid()) { const Size2 icon_size = _get_tab_icon_size(i);
ms.height = MAX(ms.height, tex->get_size().height + y_margin); ms.height = MAX(ms.height, icon_size.height + y_margin);
ms.width += tex->get_size().width + theme_cache.h_separation; ms.width += icon_size.width + theme_cache.h_separation;
} }
if (!tabs[i].text.is_empty()) { if (!tabs[i].text.is_empty()) {
@ -304,6 +304,7 @@ void TabBar::_update_theme_item_cache() {
Control::_update_theme_item_cache(); Control::_update_theme_item_cache();
theme_cache.h_separation = get_theme_constant(SNAME("h_separation")); theme_cache.h_separation = get_theme_constant(SNAME("h_separation"));
theme_cache.icon_max_width = get_theme_constant(SNAME("icon_max_width"));
theme_cache.tab_unselected_style = get_theme_stylebox(SNAME("tab_unselected")); theme_cache.tab_unselected_style = get_theme_stylebox(SNAME("tab_unselected"));
theme_cache.tab_selected_style = get_theme_stylebox(SNAME("tab_selected")); theme_cache.tab_selected_style = get_theme_stylebox(SNAME("tab_selected"));
@ -492,9 +493,11 @@ void TabBar::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_in
// Draw the icon. // Draw the icon.
Ref<Texture2D> icon = tabs[p_index].icon; Ref<Texture2D> icon = tabs[p_index].icon;
if (icon.is_valid()) { if (icon.is_valid()) {
icon->draw(ci, Point2i(rtl ? p_x - icon->get_width() : p_x, p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - icon->get_height()) / 2)); const Size2 icon_size = _get_tab_icon_size(p_index);
const Point2 icon_pos = Point2i(rtl ? p_x - icon_size.width : p_x, p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - icon_size.height) / 2);
icon->draw_rect(ci, Rect2(icon_pos, icon_size));
p_x = rtl ? p_x - icon->get_width() - theme_cache.h_separation : p_x + icon->get_width() + theme_cache.h_separation; p_x = rtl ? p_x - icon_size.width - theme_cache.h_separation : p_x + icon_size.width + theme_cache.h_separation;
} }
// Draw the text. // Draw the text.
@ -719,6 +722,29 @@ Ref<Texture2D> TabBar::get_tab_icon(int p_tab) const {
return tabs[p_tab].icon; return tabs[p_tab].icon;
} }
void TabBar::set_tab_icon_max_width(int p_tab, int p_width) {
ERR_FAIL_INDEX(p_tab, tabs.size());
if (tabs[p_tab].icon_max_width == p_width) {
return;
}
tabs.write[p_tab].icon_max_width = p_width;
_update_cache();
_ensure_no_over_offset();
if (scroll_to_selected) {
ensure_tab_visible(current);
}
queue_redraw();
update_minimum_size();
}
int TabBar::get_tab_icon_max_width(int p_tab) const {
ERR_FAIL_INDEX_V(p_tab, tabs.size(), 0);
return tabs[p_tab].icon_max_width;
}
void TabBar::set_tab_disabled(int p_tab, bool p_disabled) { void TabBar::set_tab_disabled(int p_tab, bool p_disabled) {
ERR_FAIL_INDEX(p_tab, tabs.size()); ERR_FAIL_INDEX(p_tab, tabs.size());
@ -1023,9 +1049,14 @@ Variant TabBar::get_drag_data(const Point2 &p_point) {
HBoxContainer *drag_preview = memnew(HBoxContainer); HBoxContainer *drag_preview = memnew(HBoxContainer);
if (!tabs[tab_over].icon.is_null()) { if (!tabs[tab_over].icon.is_null()) {
const Size2 icon_size = _get_tab_icon_size(tab_over);
TextureRect *tf = memnew(TextureRect); TextureRect *tf = memnew(TextureRect);
tf->set_texture(tabs[tab_over].icon); tf->set_texture(tabs[tab_over].icon);
tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED); tf->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
tf->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
tf->set_custom_minimum_size(icon_size);
drag_preview->add_child(tf); drag_preview->add_child(tf);
} }
@ -1270,9 +1301,9 @@ int TabBar::get_tab_width(int p_idx) const {
} }
int x = style->get_minimum_size().width; int x = style->get_minimum_size().width;
Ref<Texture2D> tex = tabs[p_idx].icon; if (tabs[p_idx].icon.is_valid()) {
if (tex.is_valid()) { const Size2 icon_size = _get_tab_icon_size(p_idx);
x += tex->get_width() + theme_cache.h_separation; x += icon_size.width + theme_cache.h_separation;
} }
if (!tabs[p_idx].text.is_empty()) { if (!tabs[p_idx].text.is_empty()) {
@ -1305,6 +1336,27 @@ int TabBar::get_tab_width(int p_idx) const {
return x; return x;
} }
Size2 TabBar::_get_tab_icon_size(int p_index) const {
ERR_FAIL_INDEX_V(p_index, tabs.size(), Size2());
const TabBar::Tab &tab = tabs[p_index];
Size2 icon_size = tab.icon->get_size();
int icon_max_width = 0;
if (theme_cache.icon_max_width > 0) {
icon_max_width = theme_cache.icon_max_width;
}
if (tab.icon_max_width > 0 && (icon_max_width == 0 || tab.icon_max_width < icon_max_width)) {
icon_max_width = tab.icon_max_width;
}
if (icon_max_width > 0 && icon_size.width > icon_max_width) {
icon_size.height = icon_size.height * icon_max_width / icon_size.width;
icon_size.width = icon_max_width;
}
return icon_size;
}
void TabBar::_ensure_no_over_offset() { void TabBar::_ensure_no_over_offset() {
if (!is_inside_tree() || !buttons_visible) { if (!is_inside_tree() || !buttons_visible) {
return; return;
@ -1547,6 +1599,8 @@ void TabBar::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tab_language", "tab_idx"), &TabBar::get_tab_language); ClassDB::bind_method(D_METHOD("get_tab_language", "tab_idx"), &TabBar::get_tab_language);
ClassDB::bind_method(D_METHOD("set_tab_icon", "tab_idx", "icon"), &TabBar::set_tab_icon); ClassDB::bind_method(D_METHOD("set_tab_icon", "tab_idx", "icon"), &TabBar::set_tab_icon);
ClassDB::bind_method(D_METHOD("get_tab_icon", "tab_idx"), &TabBar::get_tab_icon); ClassDB::bind_method(D_METHOD("get_tab_icon", "tab_idx"), &TabBar::get_tab_icon);
ClassDB::bind_method(D_METHOD("set_tab_icon_max_width", "tab_idx", "width"), &TabBar::set_tab_icon_max_width);
ClassDB::bind_method(D_METHOD("get_tab_icon_max_width", "tab_idx"), &TabBar::get_tab_icon_max_width);
ClassDB::bind_method(D_METHOD("set_tab_button_icon", "tab_idx", "icon"), &TabBar::set_tab_button_icon); ClassDB::bind_method(D_METHOD("set_tab_button_icon", "tab_idx", "icon"), &TabBar::set_tab_button_icon);
ClassDB::bind_method(D_METHOD("get_tab_button_icon", "tab_idx"), &TabBar::get_tab_button_icon); ClassDB::bind_method(D_METHOD("get_tab_button_icon", "tab_idx"), &TabBar::get_tab_button_icon);
ClassDB::bind_method(D_METHOD("set_tab_disabled", "tab_idx", "disabled"), &TabBar::set_tab_disabled); ClassDB::bind_method(D_METHOD("set_tab_disabled", "tab_idx", "disabled"), &TabBar::set_tab_disabled);

View File

@ -62,6 +62,8 @@ private:
Ref<TextLine> text_buf; Ref<TextLine> text_buf;
Ref<Texture2D> icon; Ref<Texture2D> icon;
int icon_max_width = 0;
bool disabled = false; bool disabled = false;
bool hidden = false; bool hidden = false;
int ofs_cache = 0; int ofs_cache = 0;
@ -106,6 +108,7 @@ private:
struct ThemeCache { struct ThemeCache {
int h_separation = 0; int h_separation = 0;
int icon_max_width = 0;
Ref<StyleBox> tab_unselected_style; Ref<StyleBox> tab_unselected_style;
Ref<StyleBox> tab_selected_style; Ref<StyleBox> tab_selected_style;
@ -133,6 +136,7 @@ private:
} theme_cache; } theme_cache;
int get_tab_width(int p_idx) const; int get_tab_width(int p_idx) const;
Size2 _get_tab_icon_size(int p_idx) const;
void _ensure_no_over_offset(); void _ensure_no_over_offset();
void _update_hover(); void _update_hover();
@ -171,6 +175,9 @@ public:
void set_tab_icon(int p_tab, const Ref<Texture2D> &p_icon); void set_tab_icon(int p_tab, const Ref<Texture2D> &p_icon);
Ref<Texture2D> get_tab_icon(int p_tab) const; Ref<Texture2D> get_tab_icon(int p_tab) const;
void set_tab_icon_max_width(int p_tab, int p_width);
int get_tab_icon_max_width(int p_tab) const;
void set_tab_disabled(int p_tab, bool p_disabled); void set_tab_disabled(int p_tab, bool p_disabled);
bool is_tab_disabled(int p_tab) const; bool is_tab_disabled(int p_tab) const;

View File

@ -146,6 +146,7 @@ void TabContainer::_update_theme_item_cache() {
// TabBar overrides. // TabBar overrides.
theme_cache.icon_separation = get_theme_constant(SNAME("icon_separation")); theme_cache.icon_separation = get_theme_constant(SNAME("icon_separation"));
theme_cache.icon_max_width = get_theme_constant(SNAME("icon_max_width"));
theme_cache.outline_size = get_theme_constant(SNAME("outline_size")); theme_cache.outline_size = get_theme_constant(SNAME("outline_size"));
theme_cache.tab_unselected_style = get_theme_stylebox(SNAME("tab_unselected")); theme_cache.tab_unselected_style = get_theme_stylebox(SNAME("tab_unselected"));
@ -245,6 +246,7 @@ void TabContainer::_on_theme_changed() {
tab_bar->add_theme_font_size_override(SNAME("font_size"), theme_cache.tab_font_size); tab_bar->add_theme_font_size_override(SNAME("font_size"), theme_cache.tab_font_size);
tab_bar->add_theme_constant_override(SNAME("h_separation"), theme_cache.icon_separation); tab_bar->add_theme_constant_override(SNAME("h_separation"), theme_cache.icon_separation);
tab_bar->add_theme_constant_override(SNAME("icon_max_width"), theme_cache.icon_max_width);
tab_bar->add_theme_constant_override(SNAME("outline_size"), theme_cache.outline_size); tab_bar->add_theme_constant_override(SNAME("outline_size"), theme_cache.outline_size);
_update_margins(); _update_margins();

View File

@ -59,6 +59,7 @@ class TabContainer : public Container {
// TabBar overrides. // TabBar overrides.
int icon_separation = 0; int icon_separation = 0;
int icon_max_width = 0;
int outline_size = 0; int outline_size = 0;
Ref<StyleBox> tab_unselected_style; Ref<StyleBox> tab_unselected_style;

View File

@ -1340,10 +1340,7 @@ Size2 TreeItem::get_minimum_size(int p_column) {
size.width += parent_tree->theme_cache.checked->get_width() + parent_tree->theme_cache.h_separation; size.width += parent_tree->theme_cache.checked->get_width() + parent_tree->theme_cache.h_separation;
} }
if (cell.icon.is_valid()) { if (cell.icon.is_valid()) {
Size2i icon_size = cell.get_icon_size(); Size2i icon_size = parent_tree->_get_cell_icon_size(cell);
if (cell.icon_max_w > 0 && icon_size.width > cell.icon_max_w) {
icon_size.width = cell.icon_max_w;
}
size.width += icon_size.width + parent_tree->theme_cache.h_separation; size.width += icon_size.width + parent_tree->theme_cache.h_separation;
size.height = MAX(size.height, icon_size.height); size.height = MAX(size.height, icon_size.height);
} }
@ -1628,6 +1625,7 @@ void Tree::_update_theme_item_cache() {
theme_cache.v_separation = get_theme_constant(SNAME("v_separation")); theme_cache.v_separation = get_theme_constant(SNAME("v_separation"));
theme_cache.item_margin = get_theme_constant(SNAME("item_margin")); theme_cache.item_margin = get_theme_constant(SNAME("item_margin"));
theme_cache.button_margin = get_theme_constant(SNAME("button_margin")); theme_cache.button_margin = get_theme_constant(SNAME("button_margin"));
theme_cache.icon_max_width = get_theme_constant(SNAME("icon_max_width"));
theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color")); theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color"));
theme_cache.font_outline_size = get_theme_constant(SNAME("outline_size")); theme_cache.font_outline_size = get_theme_constant(SNAME("outline_size"));
@ -1654,6 +1652,25 @@ void Tree::_update_theme_item_cache() {
theme_cache.base_scale = get_theme_default_base_scale(); theme_cache.base_scale = get_theme_default_base_scale();
} }
Size2 Tree::_get_cell_icon_size(const TreeItem::Cell &p_cell) const {
Size2i icon_size = p_cell.get_icon_size();
int max_width = 0;
if (theme_cache.icon_max_width > 0) {
max_width = theme_cache.icon_max_width;
}
if (p_cell.icon_max_w > 0 && (max_width == 0 || p_cell.icon_max_w < max_width)) {
max_width = p_cell.icon_max_w;
}
if (max_width > 0 && icon_size.width > max_width) {
icon_size.height = icon_size.height * max_width / icon_size.width;
icon_size.width = max_width;
}
return icon_size;
}
int Tree::compute_item_height(TreeItem *p_item) const { int Tree::compute_item_height(TreeItem *p_item) const {
if ((p_item == root && hide_root) || !p_item->is_visible()) { if ((p_item == root && hide_root) || !p_item->is_visible()) {
return 0; return 0;
@ -1688,10 +1705,7 @@ int Tree::compute_item_height(TreeItem *p_item) const {
case TreeItem::CELL_MODE_ICON: { case TreeItem::CELL_MODE_ICON: {
Ref<Texture2D> icon = p_item->cells[i].icon; Ref<Texture2D> icon = p_item->cells[i].icon;
if (!icon.is_null()) { if (!icon.is_null()) {
Size2i s = p_item->cells[i].get_icon_size(); Size2i s = _get_cell_icon_size(p_item->cells[i]);
if (p_item->cells[i].icon_max_w > 0 && s.width > p_item->cells[i].icon_max_w) {
s.height = s.height * p_item->cells[i].icon_max_w / s.width;
}
if (s.height > height) { if (s.height > height) {
height = s.height; height = s.height;
} }
@ -1745,10 +1759,7 @@ void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Co
int w = 0; int w = 0;
if (!p_cell.icon.is_null()) { if (!p_cell.icon.is_null()) {
Size2i bmsize = p_cell.get_icon_size(); Size2i bmsize = _get_cell_icon_size(p_cell);
if (p_cell.icon_max_w > 0 && bmsize.width > p_cell.icon_max_w) {
bmsize.width = p_cell.icon_max_w;
}
w += bmsize.width + theme_cache.h_separation; w += bmsize.width + theme_cache.h_separation;
if (rect.size.width > 0 && (w + ts.width) > rect.size.width) { if (rect.size.width > 0 && (w + ts.width) > rect.size.width) {
ts.width = rect.size.width - w; ts.width = rect.size.width - w;
@ -1788,12 +1799,7 @@ void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Co
} }
if (!p_cell.icon.is_null()) { if (!p_cell.icon.is_null()) {
Size2i bmsize = p_cell.get_icon_size(); Size2i bmsize = _get_cell_icon_size(p_cell);
if (p_cell.icon_max_w > 0 && bmsize.width > p_cell.icon_max_w) {
bmsize.height = bmsize.height * p_cell.icon_max_w / bmsize.width;
bmsize.width = p_cell.icon_max_w;
}
p_cell.draw_icon(ci, rect.position + Size2i(0, Math::floor((real_t)(rect.size.y - bmsize.y) / 2)), bmsize, p_icon_color); p_cell.draw_icon(ci, rect.position + Size2i(0, Math::floor((real_t)(rect.size.y - bmsize.y) / 2)), bmsize, p_icon_color);
rect.position.x += bmsize.x + theme_cache.h_separation; rect.position.x += bmsize.x + theme_cache.h_separation;
@ -2206,12 +2212,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
if (p_item->cells[i].icon.is_null()) { if (p_item->cells[i].icon.is_null()) {
break; break;
} }
Size2i icon_size = p_item->cells[i].get_icon_size(); Size2i icon_size = _get_cell_icon_size(p_item->cells[i]);
if (p_item->cells[i].icon_max_w > 0 && icon_size.width > p_item->cells[i].icon_max_w) {
icon_size.height = icon_size.height * p_item->cells[i].icon_max_w / icon_size.width;
icon_size.width = p_item->cells[i].icon_max_w;
}
Point2i icon_ofs = (item_rect.size - icon_size) / 2; Point2i icon_ofs = (item_rect.size - icon_size) / 2;
icon_ofs += item_rect.position; icon_ofs += item_rect.position;
@ -3795,8 +3796,9 @@ bool Tree::edit_selected() {
popup_rect.size = rect.size; popup_rect.size = rect.size;
// Account for icon. // Account for icon.
popup_rect.position.x += c.get_icon_size().x; Size2 icon_size = _get_cell_icon_size(c);
popup_rect.size.x -= c.get_icon_size().x; popup_rect.position.x += icon_size.x;
popup_rect.size.x -= icon_size.x;
text_editor->clear(); text_editor->clear();
text_editor->set_text(c.mode == TreeItem::CELL_MODE_STRING ? c.text : String::num(c.val, Math::range_step_decimals(c.step))); text_editor->set_text(c.mode == TreeItem::CELL_MODE_STRING ? c.text : String::num(c.val, Math::range_step_decimals(c.step)));

View File

@ -530,21 +530,24 @@ private:
Color font_outline_color; Color font_outline_color;
float base_scale = 1.0; float base_scale = 1.0;
int font_outline_size = 0;
int h_separation = 0; int h_separation = 0;
int v_separation = 0; int v_separation = 0;
int item_margin = 0; int item_margin = 0;
int button_margin = 0; int button_margin = 0;
int icon_max_width = 0;
Point2 offset; Point2 offset;
int draw_relationship_lines = 0; int draw_relationship_lines = 0;
int relationship_line_width = 0; int relationship_line_width = 0;
int parent_hl_line_width = 0; int parent_hl_line_width = 0;
int children_hl_line_width = 0; int children_hl_line_width = 0;
int parent_hl_line_margin = 0; int parent_hl_line_margin = 0;
int draw_guides = 0; int draw_guides = 0;
int scroll_border = 0; int scroll_border = 0;
int scroll_speed = 0; int scroll_speed = 0;
int font_outline_size = 0;
} theme_cache; } theme_cache;
struct Cache { struct Cache {
@ -573,6 +576,7 @@ private:
} cache; } cache;
int _get_title_button_height() const; int _get_title_button_height() const;
Size2 _get_cell_icon_size(const TreeItem::Cell &p_cell) const;
void _scroll_moved(float p_value); void _scroll_moved(float p_value);
HScrollBar *h_scroll = nullptr; HScrollBar *h_scroll = nullptr;

View File

@ -180,6 +180,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("icon_disabled_color", "Button", Color(1, 1, 1, 0.4)); theme->set_color("icon_disabled_color", "Button", Color(1, 1, 1, 0.4));
theme->set_constant("h_separation", "Button", 2 * scale); theme->set_constant("h_separation", "Button", 2 * scale);
theme->set_constant("icon_max_width", "Button", 0);
// MenuBar // MenuBar
theme->set_stylebox("normal", "MenuBar", button_normal); theme->set_stylebox("normal", "MenuBar", button_normal);
@ -688,6 +689,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("separator_outline_size", "PopupMenu", 0); theme->set_constant("separator_outline_size", "PopupMenu", 0);
theme->set_constant("item_start_padding", "PopupMenu", 2 * scale); theme->set_constant("item_start_padding", "PopupMenu", 2 * scale);
theme->set_constant("item_end_padding", "PopupMenu", 2 * scale); theme->set_constant("item_end_padding", "PopupMenu", 2 * scale);
theme->set_constant("icon_max_width", "PopupMenu", 0);
// GraphNode // GraphNode
Ref<StyleBoxFlat> graphnode_normal = make_flat_stylebox(style_normal_color, 18, 42, 18, 12); Ref<StyleBoxFlat> graphnode_normal = make_flat_stylebox(style_normal_color, 18, 42, 18, 12);
@ -780,6 +782,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("scroll_border", "Tree", 4); theme->set_constant("scroll_border", "Tree", 4);
theme->set_constant("scroll_speed", "Tree", 12); theme->set_constant("scroll_speed", "Tree", 12);
theme->set_constant("outline_size", "Tree", 0); theme->set_constant("outline_size", "Tree", 0);
theme->set_constant("icon_max_width", "Tree", 0);
// ItemList // ItemList
@ -842,6 +845,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("side_margin", "TabContainer", 8 * scale); theme->set_constant("side_margin", "TabContainer", 8 * scale);
theme->set_constant("icon_separation", "TabContainer", 4 * scale); theme->set_constant("icon_separation", "TabContainer", 4 * scale);
theme->set_constant("icon_max_width", "TabContainer", 0);
theme->set_constant("outline_size", "TabContainer", 0); theme->set_constant("outline_size", "TabContainer", 0);
// TabBar // TabBar
@ -869,6 +873,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("drop_mark_color", "TabBar", Color(1, 1, 1)); theme->set_color("drop_mark_color", "TabBar", Color(1, 1, 1));
theme->set_constant("h_separation", "TabBar", 4 * scale); theme->set_constant("h_separation", "TabBar", 4 * scale);
theme->set_constant("icon_max_width", "TabBar", 0);
theme->set_constant("outline_size", "TabBar", 0); theme->set_constant("outline_size", "TabBar", 0);
// Separators // Separators