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.
</member>
<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 name="flat" type="bool" setter="set_flat" getter="is_flat" default="false">
Flat buttons don't display decoration.
@ -116,6 +116,9 @@
<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.
</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">
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.

View file

@ -202,6 +202,13 @@
Returns the icon of the item at the given [param index].
</description>
</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">
<return 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].
</description>
</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">
<return type="void" />
<param index="0" name="index" type="int" />
@ -573,6 +588,9 @@
<theme_item name="h_separation" data_type="constant" type="int" default="4">
The horizontal space between the item's elements.
</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">
Width of the single indentation level.
</theme_item>

View file

@ -46,14 +46,21 @@
<return type="Texture2D" />
<param index="0" name="tab_idx" type="int" />
<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>
</method>
<method name="get_tab_icon" qualifiers="const">
<return type="Texture2D" />
<param index="0" name="tab_idx" type="int" />
<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>
</method>
<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].
</description>
</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">
<return type="void" />
<param index="0" name="tab_idx" type="int" />
@ -323,6 +338,9 @@
<theme_item name="h_separation" data_type="constant" type="int" default="4">
The horizontal separation between the elements inside tabs.
</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">
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.

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)">
Font color of the other, unselected tabs.
</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">
Space between tab's name and its icon.
</theme_item>

View file

@ -518,6 +518,9 @@
<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.
</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">
The horizontal margin at the start of an item. This is used when folding is enabled for the item.
</theme_item>

View file

@ -183,7 +183,7 @@
<return type="int" />
<param index="0" name="column" type="int" />
<description>
Returns the column's icon's maximum width.
Returns the maximum allowed width of the icon in the given [param column].
</description>
</method>
<method name="get_icon_modulate" qualifiers="const">
@ -545,7 +545,7 @@
<param index="0" name="column" type="int" />
<param index="1" name="width" type="int" />
<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>
</method>
<method name="set_icon_modulate">

View file

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

View file

@ -146,7 +146,7 @@ private:
HashMap<String, StringName> _script_class_file_to_path;
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:
EditorPlugin *get_editor(Object *p_object);

View file

@ -668,7 +668,7 @@ void EditorHelp::_update_doc() {
String inherits = cd.inherits;
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().
_add_type(inherits);
@ -701,7 +701,7 @@ void EditorHelp::_update_doc() {
if (prev) {
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().
_add_type(E.value.name);
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 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;
if (icon.is_valid()) {
w += hs + icon->get_width();
w += hs + icon_size;
}
int ofs = (get_size().width - w) / 2;
if (icon.is_valid()) {
draw_texture(icon, Point2(ofs, (get_size().height - icon->get_height()) / 2).floor());
ofs += hs + icon->get_width();
Size2 rect_size = Size2(icon_size, icon_size);
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"));

View file

@ -681,10 +681,6 @@ void EditorNode::_notification(int p_what) {
editor_data.clear_edited_scenes();
} break;
case Control::NOTIFICATION_THEME_CHANGED: {
scene_tab_add_ph->set_custom_minimum_size(scene_tab_add->get_minimum_size());
} break;
case NOTIFICATION_READY: {
{
_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")));
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")));
}
@ -7114,6 +7113,7 @@ EditorNode::EditorNode() {
scene_tabs->set_select_with_rmb(true);
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->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_drag_to_rearrange_enabled(true);
scene_tabs->set_auto_translate(false);

View file

@ -201,8 +201,12 @@ void EditorPath::_notification(int p_what) {
case NOTIFICATION_THEME_CHANGED: {
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")));
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;
case NOTIFICATION_READY: {
@ -227,7 +231,8 @@ EditorPath::EditorPath(EditorSelectionHistory *p_history) {
main_mc->add_child(main_hb);
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);
current_object_label = memnew(Label);

View file

@ -784,6 +784,7 @@ void EditorResourcePicker::_notification(int p_what) {
[[fallthrough]];
}
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")));
} break;
@ -923,6 +924,7 @@ EditorResourcePicker::EditorResourcePicker(bool p_hide_assign_button_controls) {
assign_button = memnew(Button);
assign_button->set_flat(true);
assign_button->set_h_size_flags(SIZE_EXPAND_FILL);
assign_button->set_expand_icon(true);
assign_button->set_clip_text(true);
assign_button->set_auto_translate(false);
SET_DRAG_FORWARDING_GCD(assign_button, EditorResourcePicker);

View file

@ -587,9 +587,11 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
} else {
theme->set_color("highend_color", "Editor", Color(1.0, 0.0, 0.0));
}
const int thumb_size = EDITOR_GET("filesystem/file_dialog/thumbnail_size");
theme->set_constant("scale", "Editor", EDSCALE);
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("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_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);
// create_root_dialog
@ -1366,19 +1359,35 @@ void SceneTreeDock::_notification(int p_what) {
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
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_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")));
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_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;
case NOTIFICATION_PROCESS: {

View file

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

View file

@ -83,6 +83,7 @@ void Button::_update_theme_item_cache() {
theme_cache.icon = get_theme_icon(SNAME("icon"));
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) {
@ -252,7 +253,6 @@ void Button::_notification(int p_what) {
float icon_ofs_region = 0.0;
Point2 style_offset;
Size2 icon_size = _icon->get_size();
if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_LEFT) {
style_offset.x = style->get_margin(SIDE_LEFT);
if (_internal_margin[SIDE_LEFT] > 0) {
@ -268,6 +268,7 @@ void Button::_notification(int p_what) {
}
style_offset.y = style->get_margin(SIDE_TOP);
Size2 icon_size = _icon->get_size();
if (expand_icon) {
Size2 _size = get_size() - style->get_offset() * 2;
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 = _fit_icon_size(icon_size);
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);
@ -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 {
Ref<TextParagraph> paragraph;
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()) {
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) {
minsize.width += p_icon->get_width();
minsize.width += icon_size.width;
if (!xl_text.is_empty() || !p_text.is_empty()) {
minsize.width += MAX(0, theme_cache.h_separation);
}
} 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;
int h_separation = 0;
int icon_max_width = 0;
} theme_cache;
Size2 _fit_icon_size(const Size2 &p_size) const;
void _shape(Ref<TextParagraph> p_paragraph = Ref<TextParagraph>(), String p_text = "");
protected:

View file

@ -47,6 +47,26 @@ String PopupMenu::_get_accel_text(const Item &p_item) const {
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 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
@ -61,7 +81,7 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
Size2 item_size;
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);
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 {
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) {
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;
}
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) {
has_check = true;
@ -569,7 +591,7 @@ void PopupMenu::_draw_items() {
_shape_item(i);
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);
if (i == mouse_over) {
@ -631,21 +653,26 @@ void PopupMenu::_draw_items() {
// Icon
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) {
separator_ofs -= (icon_size.width + theme_cache.h_separation) / 2;
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 {
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 {
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 {
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.
@ -802,6 +829,7 @@ void PopupMenu::_update_theme_item_cache() {
theme_cache.indent = get_theme_constant(SNAME("indent"));
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.icon_max_width = get_theme_constant(SNAME("icon_max_width"));
theme_cache.checked = get_theme_icon(SNAME("checked"));
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_SETUP_WITH_ACCEL(p_label, p_id, p_accel);
items.push_back(item);
_shape_item(items.size() - 1);
control->queue_redraw();
child_controls_changed();
notify_property_list_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.icon = p_icon;
items.push_back(item);
_shape_item(items.size() - 1);
control->queue_redraw();
child_controls_changed();
notify_property_list_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.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item);
_shape_item(items.size() - 1);
control->queue_redraw();
child_controls_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.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item);
_shape_item(items.size() - 1);
control->queue_redraw();
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.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
items.push_back(item);
_shape_item(items.size() - 1);
control->queue_redraw();
child_controls_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.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
items.push_back(item);
_shape_item(items.size() - 1);
control->queue_redraw();
child_controls_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.state = p_default_state;
items.push_back(item);
_shape_item(items.size() - 1);
control->queue_redraw();
child_controls_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_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
items.push_back(item);
_shape_item(items.size() - 1);
control->queue_redraw();
child_controls_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.icon = p_icon;
items.push_back(item);
_shape_item(items.size() - 1);
control->queue_redraw();
child_controls_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.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item);
_shape_item(items.size() - 1);
control->queue_redraw();
child_controls_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.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item);
_shape_item(items.size() - 1);
control->queue_redraw();
child_controls_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.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
items.push_back(item);
_shape_item(items.size() - 1);
control->queue_redraw();
child_controls_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.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
items.push_back(item);
_shape_item(items.size() - 1);
control->queue_redraw();
child_controls_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.submenu = p_submenu;
items.push_back(item);
_shape_item(items.size() - 1);
control->queue_redraw();
child_controls_changed();
_menu_changed();
}
@ -1176,6 +1232,23 @@ void PopupMenu::set_item_icon(int p_idx, const Ref<Texture2D> &p_icon) {
_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) {
if (p_idx < 0) {
p_idx += get_item_count();
@ -1314,6 +1387,11 @@ Ref<Texture2D> PopupMenu::get_item_icon(int p_idx) const {
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 {
ERR_FAIL_INDEX_V(p_idx, items.size(), Key::NONE);
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_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_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_id", "index", "id"), &PopupMenu::set_item_id);
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_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_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("get_item_id", "index"), &PopupMenu::get_item_id);
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 {
Ref<Texture2D> icon;
int icon_max_width = 0;
String text;
String xl_text;
Ref<TextLine> text_buf;
@ -103,6 +104,7 @@ class PopupMenu : public Popup {
int _get_item_height(int p_item) const;
int _get_items_total_height() const;
Size2 _get_item_icon_size(int p_item) const;
void _shape_item(int p_item);
@ -144,6 +146,7 @@ class PopupMenu : public Popup {
int indent = 0;
int item_start_padding = 0;
int item_end_padding = 0;
int icon_max_width = 0;
Ref<Texture2D> checked;
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_language(int p_idx, const String &p_language);
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_id(int p_idx, int p_id);
void set_item_accelerator(int p_idx, Key p_accel);
@ -245,6 +249,7 @@ public:
String get_item_language(int p_idx) const;
int get_item_idx_from_text(const String &text) 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;
int get_item_id(int p_idx) 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;
Ref<Texture2D> tex = tabs[i].icon;
if (tex.is_valid()) {
ms.height = MAX(ms.height, tex->get_size().height + y_margin);
ms.width += tex->get_size().width + theme_cache.h_separation;
if (tabs[i].icon.is_valid()) {
const Size2 icon_size = _get_tab_icon_size(i);
ms.height = MAX(ms.height, icon_size.height + y_margin);
ms.width += icon_size.width + theme_cache.h_separation;
}
if (!tabs[i].text.is_empty()) {
@ -304,6 +304,7 @@ void TabBar::_update_theme_item_cache() {
Control::_update_theme_item_cache();
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_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.
Ref<Texture2D> icon = tabs[p_index].icon;
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.
@ -719,6 +722,29 @@ Ref<Texture2D> TabBar::get_tab_icon(int p_tab) const {
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) {
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);
if (!tabs[tab_over].icon.is_null()) {
const Size2 icon_size = _get_tab_icon_size(tab_over);
TextureRect *tf = memnew(TextureRect);
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);
}
@ -1270,9 +1301,9 @@ int TabBar::get_tab_width(int p_idx) const {
}
int x = style->get_minimum_size().width;
Ref<Texture2D> tex = tabs[p_idx].icon;
if (tex.is_valid()) {
x += tex->get_width() + theme_cache.h_separation;
if (tabs[p_idx].icon.is_valid()) {
const Size2 icon_size = _get_tab_icon_size(p_idx);
x += icon_size.width + theme_cache.h_separation;
}
if (!tabs[p_idx].text.is_empty()) {
@ -1305,6 +1336,27 @@ int TabBar::get_tab_width(int p_idx) const {
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() {
if (!is_inside_tree() || !buttons_visible) {
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("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("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("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);

View file

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

View file

@ -146,6 +146,7 @@ void TabContainer::_update_theme_item_cache() {
// TabBar overrides.
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.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_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);
_update_margins();

View file

@ -59,6 +59,7 @@ class TabContainer : public Container {
// TabBar overrides.
int icon_separation = 0;
int icon_max_width = 0;
int outline_size = 0;
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;
}
if (cell.icon.is_valid()) {
Size2i icon_size = cell.get_icon_size();
if (cell.icon_max_w > 0 && icon_size.width > cell.icon_max_w) {
icon_size.width = cell.icon_max_w;
}
Size2i icon_size = parent_tree->_get_cell_icon_size(cell);
size.width += icon_size.width + parent_tree->theme_cache.h_separation;
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.item_margin = get_theme_constant(SNAME("item_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_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();
}
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 {
if ((p_item == root && hide_root) || !p_item->is_visible()) {
return 0;
@ -1688,10 +1705,7 @@ int Tree::compute_item_height(TreeItem *p_item) const {
case TreeItem::CELL_MODE_ICON: {
Ref<Texture2D> icon = p_item->cells[i].icon;
if (!icon.is_null()) {
Size2i s = p_item->cells[i].get_icon_size();
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;
}
Size2i s = _get_cell_icon_size(p_item->cells[i]);
if (s.height > 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;
if (!p_cell.icon.is_null()) {
Size2i bmsize = p_cell.get_icon_size();
if (p_cell.icon_max_w > 0 && bmsize.width > p_cell.icon_max_w) {
bmsize.width = p_cell.icon_max_w;
}
Size2i bmsize = _get_cell_icon_size(p_cell);
w += bmsize.width + theme_cache.h_separation;
if (rect.size.width > 0 && (w + ts.width) > rect.size.width) {
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()) {
Size2i bmsize = p_cell.get_icon_size();
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;
}
Size2i bmsize = _get_cell_icon_size(p_cell);
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;
@ -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()) {
break;
}
Size2i icon_size = p_item->cells[i].get_icon_size();
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;
}
Size2i icon_size = _get_cell_icon_size(p_item->cells[i]);
Point2i icon_ofs = (item_rect.size - icon_size) / 2;
icon_ofs += item_rect.position;
@ -3795,8 +3796,9 @@ bool Tree::edit_selected() {
popup_rect.size = rect.size;
// Account for icon.
popup_rect.position.x += c.get_icon_size().x;
popup_rect.size.x -= c.get_icon_size().x;
Size2 icon_size = _get_cell_icon_size(c);
popup_rect.position.x += icon_size.x;
popup_rect.size.x -= icon_size.x;
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)));

View file

@ -530,21 +530,24 @@ private:
Color font_outline_color;
float base_scale = 1.0;
int font_outline_size = 0;
int h_separation = 0;
int v_separation = 0;
int item_margin = 0;
int button_margin = 0;
int icon_max_width = 0;
Point2 offset;
int draw_relationship_lines = 0;
int relationship_line_width = 0;
int parent_hl_line_width = 0;
int children_hl_line_width = 0;
int parent_hl_line_margin = 0;
int draw_guides = 0;
int scroll_border = 0;
int scroll_speed = 0;
int font_outline_size = 0;
} theme_cache;
struct Cache {
@ -573,6 +576,7 @@ private:
} cache;
int _get_title_button_height() const;
Size2 _get_cell_icon_size(const TreeItem::Cell &p_cell) const;
void _scroll_moved(float p_value);
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_constant("h_separation", "Button", 2 * scale);
theme->set_constant("icon_max_width", "Button", 0);
// MenuBar
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("item_start_padding", "PopupMenu", 2 * scale);
theme->set_constant("item_end_padding", "PopupMenu", 2 * scale);
theme->set_constant("icon_max_width", "PopupMenu", 0);
// GraphNode
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_speed", "Tree", 12);
theme->set_constant("outline_size", "Tree", 0);
theme->set_constant("icon_max_width", "Tree", 0);
// 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("icon_separation", "TabContainer", 4 * scale);
theme->set_constant("icon_max_width", "TabContainer", 0);
theme->set_constant("outline_size", "TabContainer", 0);
// 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_constant("h_separation", "TabBar", 4 * scale);
theme->set_constant("icon_max_width", "TabBar", 0);
theme->set_constant("outline_size", "TabBar", 0);
// Separators