diff --git a/scene/gui/split_container.cpp b/scene/gui/split_container.cpp index cdf6523c6cb..727c6a96357 100644 --- a/scene/gui/split_container.cpp +++ b/scene/gui/split_container.cpp @@ -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. diff --git a/tests/scene/test_split_container.h b/tests/scene/test_split_container.h index dc4125b8c48..5ccbb576b8e 100644 --- a/tests/scene/test_split_container.h +++ b/tests/scene/test_split_container.h @@ -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 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); }