feat: updated engine

This commit is contained in:
Sara Gerretsen 2026-07-10 17:04:34 +02:00
parent cbe99774ff
commit f4cf6b3999
6607 changed files with 910135 additions and 430025 deletions

View file

@ -30,6 +30,7 @@
#include "flow_container.h"
#include "core/object/class_db.h"
#include "scene/gui/texture_rect.h"
#include "scene/theme/theme_db.h"
@ -51,6 +52,8 @@ void FlowContainer::_resort() {
bool rtl = is_layout_rtl();
HashMap<Control *, Size2i> children_minsize_cache;
HashMap<Control *, Size2i> children_maxsize_cache;
HashMap<Control *, int> children_stretch_cache;
Vector<_LineData> lines_data;
@ -69,7 +72,9 @@ void FlowContainer::_resort() {
continue;
}
Size2i child_msc = child->get_combined_minimum_size();
// Since we are in a FlowContainer, children will always have up to the full width/height available to them, so we can use the desired size as the minimum size for layout purposes.
Size2i child_msc = child->get_bound_desired_size();
Size2i child_max_size = child->get_combined_maximum_size();
if (vertical) { /* VERTICAL */
if (children_in_current_line > 0) {
@ -118,12 +123,13 @@ void FlowContainer::_resort() {
last_child = child;
children_minsize_cache[child] = child_msc;
children_maxsize_cache[child] = child_max_size;
children_in_current_line++;
}
line_length = vertical ? (ofs.y) : (ofs.x);
bool is_filled = false;
if (last_child != nullptr) {
is_filled = vertical ? (ofs.y + last_child->get_combined_minimum_size().y > current_container_size ? true : false) : (ofs.x + last_child->get_combined_minimum_size().x > current_container_size ? true : false);
is_filled = vertical ? (ofs.y + last_child->get_bound_minimum_size().y > current_container_size ? true : false) : (ofs.x + last_child->get_bound_minimum_size().x > current_container_size ? true : false);
}
lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total, is_filled });
@ -156,9 +162,87 @@ void FlowContainer::_resort() {
line_data = lines_data[current_line_idx];
}
if (child_idx_in_line == 0) {
Vector<Control *> line_children;
for (int j = i; j < get_child_count() && line_children.size() < lines_data[current_line_idx].child_count; j++) {
Control *line_child = as_sortable_control(get_child(j));
if (!line_child) {
continue;
}
line_children.push_back(line_child);
children_stretch_cache[line_child] = 0;
}
int line_remaining_stretch = line_data.stretch_avail;
Vector<Control *> stretch_children;
Vector<bool> stretch_children_active;
line_stretch_ratio_total = 0;
for (int j = 0; j < line_children.size(); j++) {
Control *line_child = line_children[j];
bool can_stretch = vertical ? line_child->get_v_size_flags().has_flag(SIZE_EXPAND) : line_child->get_h_size_flags().has_flag(SIZE_EXPAND);
if (!can_stretch) {
continue;
}
stretch_children.push_back(line_child);
stretch_children_active.push_back(true);
line_stretch_ratio_total += line_child->get_stretch_ratio();
}
while (line_stretch_ratio_total > 0) {
bool refit_successful = true;
for (int j = 0; j < stretch_children.size(); j++) {
if (!stretch_children_active[j]) {
continue;
}
Control *line_child = stretch_children[j];
float stretch_ratio = line_child->get_stretch_ratio();
int child_stretch = line_remaining_stretch * stretch_ratio / line_stretch_ratio_total;
const Size2i &child_min_size = children_minsize_cache[line_child];
const Size2i &child_max_size = children_maxsize_cache[line_child];
int child_axis_min = vertical ? child_min_size.height : child_min_size.width;
int child_axis_max = vertical ? child_max_size.height : child_max_size.width;
int max_stretch = child_axis_max >= 0 ? MAX(child_axis_max - child_axis_min, 0) : INT_MAX;
if (child_stretch > max_stretch) {
children_stretch_cache[line_child] = max_stretch;
stretch_children_active.write[j] = false;
line_stretch_ratio_total -= stretch_ratio;
line_remaining_stretch -= max_stretch;
refit_successful = false;
break;
}
}
if (refit_successful) {
for (int j = 0; j < stretch_children.size(); j++) {
if (!stretch_children_active[j]) {
continue;
}
Control *line_child = stretch_children[j];
children_stretch_cache[line_child] = line_remaining_stretch * line_child->get_stretch_ratio() / line_stretch_ratio_total;
}
break;
}
}
int used_stretch = 0;
for (int j = 0; j < line_children.size(); j++) {
used_stretch += children_stretch_cache[line_children[j]];
}
lines_data.write[current_line_idx].stretch_avail = MAX(line_data.stretch_avail - used_stretch, 0);
line_data = lines_data[current_line_idx];
}
// The first child of each line adds the offset caused by the alignment,
// but only if the line doesn't contain a child that expands.
if (child_idx_in_line == 0 && Math::is_equal_approx(line_data.stretch_ratio_total, 0)) {
// but only if there is remaining space after expansion/capping.
if (child_idx_in_line == 0 && line_data.stretch_avail > 0) {
int alignment_ofs = 0;
bool is_not_first_line_and_not_filled = current_line_idx != 0 && !line_data.is_filled;
float prior_stretch_avail = is_not_first_line_and_not_filled ? lines_data[current_line_idx - 1].stretch_avail : 0.0;
@ -226,7 +310,7 @@ void FlowContainer::_resort() {
}
if (child->get_v_size_flags().has_flag(SIZE_EXPAND)) {
int stretch = line_data.stretch_avail * child->get_stretch_ratio() / line_data.stretch_ratio_total;
int stretch = children_stretch_cache.has(child) ? children_stretch_cache[child] : 0;
child_size.height += stretch;
}
@ -236,7 +320,7 @@ void FlowContainer::_resort() {
}
if (child->get_h_size_flags().has_flag(SIZE_EXPAND)) {
int stretch = line_data.stretch_avail * child->get_stretch_ratio() / line_data.stretch_ratio_total;
int stretch = children_stretch_cache.has(child) ? children_stretch_cache[child] : 0;
child_size.width += stretch;
}
}
@ -249,6 +333,15 @@ void FlowContainer::_resort() {
child_rect.position.x = get_rect().size.x - child_rect.position.x - child_rect.size.width;
}
// Ensure that the child does not exceed the container's size in the flow direction.
// This will only ever apply in the case of having a single child in a line that is larger than the container's minimum size, i.e. a child with a desired size greater than its own minimum size.
// This will result in the child being given a size between its minimum size and its desired size, which is the expected behavior.
if (vertical) {
child_rect.size.height = MIN(child_rect.size.height, current_container_size - ofs.y);
} else {
child_rect.size.width = MIN(child_rect.size.width, current_container_size - ofs.x);
}
fit_child_in_rect(child, child_rect);
if (vertical) { /* VERTICAL */
@ -264,7 +357,7 @@ void FlowContainer::_resort() {
cached_line_max_child_count = lines_data.size() > 0 ? lines_data[0].child_count : 0;
}
Size2 FlowContainer::get_minimum_size() const {
Size2 FlowContainer::_get_minimum_size(bool p_use_desired_sizes) const {
Size2i minimum;
for (int i = 0; i < get_child_count(); i++) {
@ -273,7 +366,7 @@ Size2 FlowContainer::get_minimum_size() const {
continue;
}
Size2i size = c->get_combined_minimum_size();
Size2i size = p_use_desired_sizes ? c->get_bound_desired_size() : c->get_bound_minimum_size();
if (vertical) { /* VERTICAL */
minimum.height = MAX(minimum.height, size.height);
@ -288,6 +381,14 @@ Size2 FlowContainer::get_minimum_size() const {
return minimum;
}
Size2 FlowContainer::get_minimum_size() const {
return _get_minimum_size(false);
}
Size2 FlowContainer::get_desired_size() const {
return _get_minimum_size(true);
}
Vector<int> FlowContainer::get_allowed_size_flags_horizontal() const {
Vector<int> flags;
flags.append(SIZE_FILL);