From d143fa3e7fc7445f2976a49a182b9fc04f9fb0b7 Mon Sep 17 00:00:00 2001 From: "George L. Albany" Date: Fri, 29 Jul 2022 12:49:14 -0400 Subject: [PATCH 1/3] Moved item update behavior to `update_auto_height` Expose `update_auto_height` to allow updating the auto_height_value early --- doc/classes/ItemList.xml | 7 + scene/gui/item_list.cpp | 278 ++++++++++++++++++++------------------- scene/gui/item_list.h | 2 + 3 files changed, 154 insertions(+), 133 deletions(-) diff --git a/doc/classes/ItemList.xml b/doc/classes/ItemList.xml index c485d26e0c76..f451e4ebeec0 100644 --- a/doc/classes/ItemList.xml +++ b/doc/classes/ItemList.xml @@ -333,6 +333,13 @@ Sorts items in the list by their text. + + + + Updates the list's auto height value based on its items. If [member auto_height] is [code]true[/code], [method Control.get_minimum_size] may return the auto height if [member Control.custom_minimum_size] is less than the auto height. + Scrollbar is adjusted accordingly if necessary. + + diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index c44feddd388f..e88093bf63cc 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -1039,139 +1039,7 @@ void ItemList::_notification(int p_what) { RenderingServer::get_singleton()->canvas_item_add_clip_ignore(get_canvas_item(), false); } - if (shape_changed) { - float max_column_width = 0.0; - - //1- compute item minimum sizes - for (int i = 0; i < items.size(); i++) { - Size2 minsize; - if (items[i].icon.is_valid()) { - if (fixed_icon_size.x > 0 && fixed_icon_size.y > 0) { - minsize = fixed_icon_size * icon_scale; - } else { - minsize = items[i].get_icon_size() * icon_scale; - } - - if (!items[i].text.is_empty()) { - if (icon_mode == ICON_MODE_TOP) { - minsize.y += theme_cache.icon_margin; - } else { - minsize.x += theme_cache.icon_margin; - } - } - } - - if (!items[i].text.is_empty()) { - int max_width = -1; - if (fixed_column_width) { - max_width = fixed_column_width; - } else if (same_column_width) { - max_width = items[i].rect_cache.size.x; - } - items.write[i].text_buf->set_width(max_width); - Size2 s = items[i].text_buf->get_size(); - - if (icon_mode == ICON_MODE_TOP) { - minsize.x = MAX(minsize.x, s.width); - if (max_text_lines > 0) { - minsize.y += s.height + theme_cache.line_separation * max_text_lines; - } else { - minsize.y += s.height; - } - - } else { - minsize.y = MAX(minsize.y, s.height); - minsize.x += s.width; - } - } - - if (fixed_column_width > 0) { - minsize.x = fixed_column_width; - } - max_column_width = MAX(max_column_width, minsize.x); - - // elements need to adapt to the selected size - minsize.y += theme_cache.v_separation; - minsize.x += theme_cache.h_separation; - items.write[i].rect_cache.size = minsize; - items.write[i].min_rect_cache.size = minsize; - } - - int fit_size = size.x - theme_cache.panel_style->get_minimum_size().width - mw; - - //2-attempt best fit - current_columns = 0x7FFFFFFF; - if (max_columns > 0) { - current_columns = max_columns; - } - - while (true) { - //repeat until all fits - bool all_fit = true; - Vector2 ofs; - int col = 0; - int max_h = 0; - separators.clear(); - for (int i = 0; i < items.size(); i++) { - if (current_columns > 1 && items[i].rect_cache.size.width + ofs.x > fit_size) { - //went past - current_columns = MAX(col, 1); - all_fit = false; - break; - } - - if (same_column_width) { - items.write[i].rect_cache.size.x = max_column_width; - } - items.write[i].rect_cache.position = ofs; - max_h = MAX(max_h, items[i].rect_cache.size.y); - ofs.x += items[i].rect_cache.size.x + theme_cache.h_separation; - col++; - if (col == current_columns) { - if (i < items.size() - 1) { - separators.push_back(ofs.y + max_h + theme_cache.v_separation / 2); - } - - for (int j = i; j >= 0 && col > 0; j--, col--) { - items.write[j].rect_cache.size.y = max_h; - } - - ofs.x = 0; - ofs.y += max_h + theme_cache.v_separation; - col = 0; - max_h = 0; - } - } - - for (int j = items.size() - 1; j >= 0 && col > 0; j--, col--) { - items.write[j].rect_cache.size.y = max_h; - } - - if (all_fit) { - float page = MAX(0, size.height - theme_cache.panel_style->get_minimum_size().height); - float max = MAX(page, ofs.y + max_h); - if (auto_height) { - auto_height_value = ofs.y + max_h + theme_cache.panel_style->get_minimum_size().height; - } - scroll_bar->set_max(max); - scroll_bar->set_page(page); - if (max <= page) { - scroll_bar->set_value(0); - scroll_bar->hide(); - } else { - scroll_bar->show(); - - if (do_autoscroll_to_bottom) { - scroll_bar->set_value(max); - } - } - break; - } - } - - update_minimum_size(); - shape_changed = false; - } + update_auto_height(); if (scroll_bar->is_visible()) { width -= mw; @@ -1617,6 +1485,148 @@ void ItemList::set_text_overrun_behavior(TextServer::OverrunBehavior p_behavior) } } +void ItemList::update_auto_height() { + if (!shape_changed) { + return; + } + + int mw = scroll_bar->get_minimum_size().x; + + Size2 size = get_size(); + + float max_column_width = 0.0; + + //1- compute item minimum sizes + for (int i = 0; i < items.size(); i++) { + Size2 minsize; + if (items[i].icon.is_valid()) { + if (fixed_icon_size.x > 0 && fixed_icon_size.y > 0) { + minsize = fixed_icon_size * icon_scale; + } else { + minsize = items[i].get_icon_size() * icon_scale; + } + + if (!items[i].text.is_empty()) { + if (icon_mode == ICON_MODE_TOP) { + minsize.y += theme_cache.icon_margin; + } else { + minsize.x += theme_cache.icon_margin; + } + } + } + + if (!items[i].text.is_empty()) { + int max_width = -1; + if (fixed_column_width) { + max_width = fixed_column_width; + } else if (same_column_width) { + max_width = items[i].rect_cache.size.x; + } + items.write[i].text_buf->set_width(max_width); + Size2 s = items[i].text_buf->get_size(); + + if (icon_mode == ICON_MODE_TOP) { + minsize.x = MAX(minsize.x, s.width); + if (max_text_lines > 0) { + minsize.y += s.height + theme_cache.line_separation * max_text_lines; + } else { + minsize.y += s.height; + } + + } else { + minsize.y = MAX(minsize.y, s.height); + minsize.x += s.width; + } + } + + if (fixed_column_width > 0) { + minsize.x = fixed_column_width; + } + max_column_width = MAX(max_column_width, minsize.x); + + // elements need to adapt to the selected size + minsize.y += theme_cache.v_separation; + minsize.x += theme_cache.h_separation; + items.write[i].rect_cache.size = minsize; + items.write[i].min_rect_cache.size = minsize; + } + + int fit_size = size.x - theme_cache.panel_style->get_minimum_size().width - mw; + + //2-attempt best fit + current_columns = 0x7FFFFFFF; + if (max_columns > 0) { + current_columns = max_columns; + } + + while (true) { + //repeat until all fits + bool all_fit = true; + Vector2 ofs; + int col = 0; + int max_h = 0; + separators.clear(); + for (int i = 0; i < items.size(); i++) { + if (current_columns > 1 && items[i].rect_cache.size.width + ofs.x > fit_size) { + //went past + current_columns = MAX(col, 1); + all_fit = false; + break; + } + + if (same_column_width) { + items.write[i].rect_cache.size.x = max_column_width; + } + items.write[i].rect_cache.position = ofs; + max_h = MAX(max_h, items[i].rect_cache.size.y); + ofs.x += items[i].rect_cache.size.x + theme_cache.h_separation; + col++; + if (col == current_columns) { + if (i < items.size() - 1) { + separators.push_back(ofs.y + max_h + theme_cache.v_separation / 2); + } + + for (int j = i; j >= 0 && col > 0; j--, col--) { + items.write[j].rect_cache.size.y = max_h; + } + + ofs.x = 0; + ofs.y += max_h + theme_cache.v_separation; + col = 0; + max_h = 0; + } + } + + for (int j = items.size() - 1; j >= 0 && col > 0; j--, col--) { + items.write[j].rect_cache.size.y = max_h; + } + + if (all_fit) { + float page = MAX(0, size.height - theme_cache.panel_style->get_minimum_size().height); + float max = MAX(page, ofs.y + max_h); + if (auto_height) { + auto_height_value = ofs.y + max_h + theme_cache.panel_style->get_minimum_size().height; + } + scroll_bar->set_max(max); + scroll_bar->set_page(page); + if (max <= page) { + scroll_bar->set_value(0); + scroll_bar->hide(); + } else { + scroll_bar->show(); + + if (do_autoscroll_to_bottom) { + scroll_bar->set_value(max); + } + } + break; + } + } + + update_minimum_size(); + shape_changed = false; +} + TextServer::OverrunBehavior ItemList::get_text_overrun_behavior() const { return text_overrun_behavior; } @@ -1805,6 +1815,8 @@ void ItemList::_bind_methods() { ClassDB::bind_method(D_METHOD("set_text_overrun_behavior", "overrun_behavior"), &ItemList::set_text_overrun_behavior); ClassDB::bind_method(D_METHOD("get_text_overrun_behavior"), &ItemList::get_text_overrun_behavior); + ClassDB::bind_method(D_METHOD("update_auto_height"), &ItemList::update_auto_height); + ADD_PROPERTY(PropertyInfo(Variant::INT, "select_mode", PROPERTY_HINT_ENUM, "Single,Multi"), "set_select_mode", "get_select_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_reselect"), "set_allow_reselect", "get_allow_reselect"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_rmb_select"), "set_allow_rmb_select", "get_allow_rmb_select"); diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h index 4b1b9d9282f2..b19ff67cf1ef 100644 --- a/scene/gui/item_list.h +++ b/scene/gui/item_list.h @@ -272,6 +272,8 @@ class ItemList : public Control { void set_autoscroll_to_bottom(const bool p_enable); + void update_auto_height(); + VScrollBar *get_v_scroll_bar() { return scroll_bar; } ItemList(); From c3d4e72039639f7d022ff6e90949a426460683fa Mon Sep 17 00:00:00 2001 From: "George L. Albany" Date: Fri, 29 Jul 2022 12:49:14 -0400 Subject: [PATCH 2/3] Moved item update behavior to `update_auto_height` Expose `update_auto_height` to allow updating the auto_height_value early --- doc/classes/ItemList.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/classes/ItemList.xml b/doc/classes/ItemList.xml index f451e4ebeec0..7862a9839d56 100644 --- a/doc/classes/ItemList.xml +++ b/doc/classes/ItemList.xml @@ -337,7 +337,7 @@ Updates the list's auto height value based on its items. If [member auto_height] is [code]true[/code], [method Control.get_minimum_size] may return the auto height if [member Control.custom_minimum_size] is less than the auto height. - Scrollbar is adjusted accordingly if necessary. + Scrollbar is adjusted accordingly if neccessary. From 8576e73bfeb45d7792fa46ce6b629b72cc8f56e8 Mon Sep 17 00:00:00 2001 From: "George L. Albany" Date: Tue, 13 Dec 2022 17:30:30 -0500 Subject: [PATCH 3/3] Expose ItemList auto_height_value as `get_auto_height_value()` --- doc/classes/ItemList.xml | 8 ++++++++ scene/gui/item_list.cpp | 6 ++++++ scene/gui/item_list.h | 2 ++ 3 files changed, 16 insertions(+) diff --git a/doc/classes/ItemList.xml b/doc/classes/ItemList.xml index 7862a9839d56..b2f4aa841502 100644 --- a/doc/classes/ItemList.xml +++ b/doc/classes/ItemList.xml @@ -57,6 +57,13 @@ Ensure current selection is visible, adjusting the scroll position as necessary. + + + + Returns the calculated minimum height of the list according to its items if [member auto_height] is [code]true[/code], excluding [member Control.custom_minimum_size]. If [member auto_height] is [code]true[/code], this returns the last auto height value. + See [method update_auto_height] for updating the value before the next draw. + + @@ -338,6 +345,7 @@ Updates the list's auto height value based on its items. If [member auto_height] is [code]true[/code], [method Control.get_minimum_size] may return the auto height if [member Control.custom_minimum_size] is less than the auto height. Scrollbar is adjusted accordingly if neccessary. + See [method get_auto_height_value]. diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index e88093bf63cc..c268eca63c30 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -1474,6 +1474,10 @@ bool ItemList::has_auto_height() const { return auto_height; } +float ItemList::get_auto_height_value() const { + return auto_height_value; +} + void ItemList::set_text_overrun_behavior(TextServer::OverrunBehavior p_behavior) { if (text_overrun_behavior != p_behavior) { text_overrun_behavior = p_behavior; @@ -1804,6 +1808,8 @@ void ItemList::_bind_methods() { ClassDB::bind_method(D_METHOD("set_auto_height", "enable"), &ItemList::set_auto_height); ClassDB::bind_method(D_METHOD("has_auto_height"), &ItemList::has_auto_height); + ClassDB::bind_method(D_METHOD("get_auto_height_value"), &ItemList::get_auto_height_value); + ClassDB::bind_method(D_METHOD("is_anything_selected"), &ItemList::is_anything_selected); ClassDB::bind_method(D_METHOD("get_item_at_position", "position", "exact"), &ItemList::get_item_at_position, DEFVAL(false)); diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h index b19ff67cf1ef..5d4973996864 100644 --- a/scene/gui/item_list.h +++ b/scene/gui/item_list.h @@ -268,6 +268,8 @@ class ItemList : public Control { void set_auto_height(bool p_enable); bool has_auto_height() const; + float get_auto_height_value() const; + Size2 get_minimum_size() const override; void set_autoscroll_to_bottom(const bool p_enable);