Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose ItemList's auto height value with get_auto_height_value #70034

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions doc/classes/ItemList.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@
Ensure current selection is visible, adjusting the scroll position as necessary.
</description>
</method>
<method name="get_auto_height_value" qualifiers="const">
<return type="float" />
<description>
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.
</description>
</method>
<method name="get_item_at_position" qualifiers="const">
<return type="int" />
<param index="0" name="position" type="Vector2" />
Expand Down Expand Up @@ -333,6 +340,14 @@
Sorts items in the list by their text.
</description>
</method>
<method name="update_auto_height">
<return type="void" />
<description>
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].
</description>
</method>
</methods>
<members>
<member name="allow_reselect" type="bool" setter="set_allow_reselect" getter="get_allow_reselect" default="false">
Expand Down
284 changes: 151 additions & 133 deletions scene/gui/item_list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -1606,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;
Expand All @@ -1617,6 +1489,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;
}
Expand Down Expand Up @@ -1794,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));
Expand All @@ -1805,6 +1821,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");
Expand Down
4 changes: 4 additions & 0 deletions scene/gui/item_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -268,10 +268,14 @@ 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);

void update_auto_height();

VScrollBar *get_v_scroll_bar() { return scroll_bar; }

ItemList();
Expand Down