mirror of
https://github.com/godotengine/godot.git
synced 2025-12-31 01:49:10 +03:00
Merge pull request #113648 from kitbdev/fix-multisplit-incorrect-shrinking
Fix SplitContainer incorrect child shrink logic
This commit is contained in:
@@ -353,6 +353,7 @@ void SplitContainer::_set_desired_sizes(const PackedInt32Array &p_desired_sizes,
|
||||
real_t min_size = 0;
|
||||
real_t stretch_ratio = 0.0;
|
||||
real_t final_size = 0;
|
||||
bool priority = false;
|
||||
};
|
||||
|
||||
// First pass, determine the total stretch amount.
|
||||
@@ -364,6 +365,7 @@ void SplitContainer::_set_desired_sizes(const PackedInt32Array &p_desired_sizes,
|
||||
sdata.min_size = child->get_combined_minimum_size()[axis];
|
||||
sdata.final_size = MAX(sdata.min_size, p_desired_sizes.is_empty() ? 0 : p_desired_sizes[i]);
|
||||
total_desired_size += sdata.final_size;
|
||||
sdata.priority = i == p_priority_index;
|
||||
// Treat the priority child as not expanded, so it doesn't shrink with other expanded children.
|
||||
if (i != p_priority_index && child->get_stretch_ratio() > 0 && (vertical ? child->get_v_size_flags() : child->get_h_size_flags()).has_flag(SIZE_EXPAND)) {
|
||||
sdata.stretch_ratio = child->get_stretch_ratio();
|
||||
@@ -419,13 +421,14 @@ void SplitContainer::_set_desired_sizes(const PackedInt32Array &p_desired_sizes,
|
||||
}
|
||||
|
||||
// Shrink non-expanding children.
|
||||
bool skip_priority_child = true;
|
||||
while (available_space < 0) {
|
||||
// Get largest and target sizes.
|
||||
// Get largest and target sizes. The target size is the second largest size.
|
||||
real_t largest_size = 0;
|
||||
real_t target_size = 0;
|
||||
int largest_count = 0;
|
||||
for (const StretchData &sdata : stretch_data) {
|
||||
if (sdata.final_size <= sdata.min_size) {
|
||||
if (sdata.final_size <= sdata.min_size || (skip_priority_child && sdata.priority)) {
|
||||
continue;
|
||||
}
|
||||
if (sdata.final_size > largest_size) {
|
||||
@@ -434,18 +437,24 @@ void SplitContainer::_set_desired_sizes(const PackedInt32Array &p_desired_sizes,
|
||||
largest_count = 1;
|
||||
} else if (sdata.final_size == largest_size) {
|
||||
largest_count++;
|
||||
} else if (sdata.final_size < largest_size) {
|
||||
target_size = MAX(sdata.final_size, target_size);
|
||||
} else if (sdata.final_size < largest_size && sdata.final_size > target_size) {
|
||||
target_size = sdata.final_size;
|
||||
}
|
||||
}
|
||||
if (largest_size <= 0) {
|
||||
break;
|
||||
if (skip_priority_child) {
|
||||
// Retry with priority child.
|
||||
skip_priority_child = false;
|
||||
continue;
|
||||
} else {
|
||||
// No more children to shrink.
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Don't shrink smaller than needed.
|
||||
target_size = MAX(target_size, available_space / largest_count);
|
||||
target_size = MIN(target_size, largest_size + (available_space / largest_count));
|
||||
target_size = MAX(target_size, largest_size + available_space / largest_count);
|
||||
for (StretchData &sdata : stretch_data) {
|
||||
if (sdata.final_size <= sdata.min_size) {
|
||||
if (sdata.final_size <= sdata.min_size || (skip_priority_child && sdata.priority)) {
|
||||
continue;
|
||||
}
|
||||
// Shrink all largest elements.
|
||||
|
||||
@@ -2265,6 +2265,93 @@ TEST_CASE("[SceneTree][SplitContainer] More children") {
|
||||
CHECK_RECTS(get_rects_multi(split_container, pos, sep.x), get_child_rects(split_container));
|
||||
}
|
||||
|
||||
SUBCASE("[SplitContainer] Showing child with not enough space shrinks the largest child first") {
|
||||
set_size_flags(split_container, { -1, -1, -1 }); // None expanded.
|
||||
|
||||
// Second child is largest.
|
||||
child_a->set_visible(false);
|
||||
Vector<int> pos = { 360 };
|
||||
split_container->set_split_offsets(pos);
|
||||
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(split_container->get_split_offsets() == pos);
|
||||
CHECK_RECTS(get_rects_multi(split_container, pos, sep.x), get_child_rects(split_container));
|
||||
|
||||
child_a->set_size(Vector2(100, 100));
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(split_container->get_split_offsets() == pos);
|
||||
CHECK_RECTS(get_rects_multi(split_container, pos, sep.x), get_child_rects(split_container));
|
||||
|
||||
child_a->set_visible(true);
|
||||
pos = { 100, 360 };
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(split_container->get_split_offsets() == pos);
|
||||
CHECK_RECTS(get_rects_multi(split_container, pos, sep.x), get_child_rects(split_container));
|
||||
|
||||
// Last child is largest.
|
||||
child_a->set_visible(false);
|
||||
pos = { 60 };
|
||||
split_container->set_split_offsets(pos);
|
||||
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(split_container->get_split_offsets() == pos);
|
||||
CHECK_RECTS(get_rects_multi(split_container, pos, sep.x), get_child_rects(split_container));
|
||||
|
||||
child_a->set_size(Vector2(100, 100));
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(split_container->get_split_offsets() == pos);
|
||||
CHECK_RECTS(get_rects_multi(split_container, pos, sep.x), get_child_rects(split_container));
|
||||
|
||||
child_a->set_visible(true);
|
||||
pos = { 100, 160 + sep.x };
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(split_container->get_split_offsets() == pos);
|
||||
CHECK_RECTS(get_rects_multi(split_container, pos, sep.x), get_child_rects(split_container));
|
||||
|
||||
// Both visible children are the same size.
|
||||
child_a->set_visible(false);
|
||||
pos = { (int)split_container->get_size().x / 2 - sep.x / 2 };
|
||||
split_container->set_split_offsets(pos);
|
||||
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(split_container->get_split_offsets() == pos);
|
||||
CHECK_RECTS(get_rects_multi(split_container, pos, sep.x), get_child_rects(split_container));
|
||||
CHECK(child_b->get_size().x == child_c->get_size().x);
|
||||
|
||||
child_a->set_size(Vector2(100, 100));
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(split_container->get_split_offsets() == pos);
|
||||
CHECK_RECTS(get_rects_multi(split_container, pos, sep.x), get_child_rects(split_container));
|
||||
|
||||
child_a->set_visible(true);
|
||||
pos = { 100, (int)split_container->get_size().x / 2 + 50 };
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(split_container->get_split_offsets() == pos);
|
||||
CHECK_RECTS(get_rects_multi(split_container, pos, sep.x), get_child_rects(split_container));
|
||||
CHECK(child_b->get_size().x == child_c->get_size().x);
|
||||
|
||||
// Second child is slightly larger than the last child.
|
||||
child_a->set_visible(false);
|
||||
pos = { (int)split_container->get_size().x / 2 - sep.x / 2 + 20 };
|
||||
split_container->set_split_offsets(pos);
|
||||
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(split_container->get_split_offsets() == pos);
|
||||
CHECK_RECTS(get_rects_multi(split_container, pos, sep.x), get_child_rects(split_container));
|
||||
|
||||
child_a->set_size(Vector2(100, 100));
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(split_container->get_split_offsets() == pos);
|
||||
CHECK_RECTS(get_rects_multi(split_container, pos, sep.x), get_child_rects(split_container));
|
||||
|
||||
child_a->set_visible(true);
|
||||
pos = { 100, (int)split_container->get_size().x / 2 + 50 };
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(split_container->get_split_offsets() == pos);
|
||||
CHECK_RECTS(get_rects_multi(split_container, pos, sep.x), get_child_rects(split_container));
|
||||
CHECK(child_b->get_size().x == child_c->get_size().x);
|
||||
}
|
||||
|
||||
memdelete(split_container);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user