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

Make GraphNode handle children with EXPAND flag #39810

Merged
merged 1 commit into from
Mar 16, 2021
Merged
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
132 changes: 114 additions & 18 deletions scene/gui/graph_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@

#include "core/string/translation.h"

struct _MinSizeCache {
int min_size;
bool will_stretch;
int final_size;
};

bool GraphNode::_set(const StringName &p_name, const Variant &p_value) {
String str = p_name;
if (str.begins_with("opentype_features/")) {
Expand Down Expand Up @@ -171,54 +177,144 @@ void GraphNode::_get_property_list(List<PropertyInfo> *p_list) const {
}

void GraphNode::_resort() {
int sep = get_theme_constant("separation");
/** First pass, determine minimum size AND amount of stretchable elements */

Size2i new_size = get_size();
Ref<StyleBox> sb = get_theme_stylebox("frame");
bool first = true;

Size2 minsize;
int sep = get_theme_constant("separation");

bool first = true;
int children_count = 0;
int stretch_min = 0;
int stretch_avail = 0;
float stretch_ratio_total = 0;
Map<Control *, _MinSizeCache> min_size_cache;

for (int i = 0; i < get_child_count(); i++) {
Control *c = Object::cast_to<Control>(get_child(i));
if (!c) {
if (!c || !c->is_visible_in_tree()) {
continue;
}
if (c->is_set_as_top_level()) {
continue;
}

Size2i size = c->get_combined_minimum_size();
_MinSizeCache msc;

minsize.y += size.y;
minsize.x = MAX(minsize.x, size.x);
stretch_min += size.height;
msc.min_size = size.height;
msc.will_stretch = c->get_v_size_flags() & SIZE_EXPAND;

if (first) {
first = false;
} else {
minsize.y += sep;
if (msc.will_stretch) {
stretch_avail += msc.min_size;
stretch_ratio_total += c->get_stretch_ratio();
}
msc.final_size = msc.min_size;
min_size_cache[c] = msc;
children_count++;
}

int vofs = 0;
int w = get_size().x - sb->get_minimum_size().x;
if (children_count == 0) {
return;
}

int stretch_max = new_size.height - (children_count - 1) * sep;
int stretch_diff = stretch_max - stretch_min;
if (stretch_diff < 0) {
//avoid negative stretch space
stretch_diff = 0;
}

stretch_avail += stretch_diff - sb->get_margin(SIDE_BOTTOM) - sb->get_margin(SIDE_TOP); //available stretch space.
/** Second, pass sucessively to discard elements that can't be stretched, this will run while stretchable
elements exist */

while (stretch_ratio_total > 0) { // first of all, don't even be here if no stretchable objects exist
bool refit_successful = true; //assume refit-test will go well

for (int i = 0; i < get_child_count(); i++) {
Control *c = Object::cast_to<Control>(get_child(i));
if (!c || !c->is_visible_in_tree()) {
continue;
}
if (c->is_set_as_top_level()) {
continue;
}

ERR_FAIL_COND(!min_size_cache.has(c));
_MinSizeCache &msc = min_size_cache[c];

if (msc.will_stretch) { //wants to stretch
//let's see if it can really stretch

int final_pixel_size = stretch_avail * c->get_stretch_ratio() / stretch_ratio_total;
if (final_pixel_size < msc.min_size) {
//if available stretching area is too small for widget,
//then remove it from stretching area
msc.will_stretch = false;
stretch_ratio_total -= c->get_stretch_ratio();
refit_successful = false;
stretch_avail -= msc.min_size;
msc.final_size = msc.min_size;
break;
} else {
msc.final_size = final_pixel_size;
}
}
}

if (refit_successful) { //uf refit went well, break
break;
}
}

/** Final pass, draw and stretch elements **/

int ofs = sb->get_margin(SIDE_TOP);

first = true;
int idx = 0;
cache_y.clear();
int w = new_size.width - sb->get_minimum_size().x;

for (int i = 0; i < get_child_count(); i++) {
Control *c = Object::cast_to<Control>(get_child(i));
if (!c) {
if (!c || !c->is_visible_in_tree()) {
continue;
}
if (c->is_set_as_top_level()) {
continue;
}

Size2i size = c->get_combined_minimum_size();
_MinSizeCache &msc = min_size_cache[c];

if (first) {
first = false;
} else {
ofs += sep;
}

Rect2 r(sb->get_margin(SIDE_LEFT), sb->get_margin(SIDE_TOP) + vofs, w, size.y);
int from = ofs;
int to = ofs + msc.final_size;

fit_child_in_rect(c, r);
cache_y.push_back(vofs + size.y * 0.5);
if (msc.will_stretch && idx == children_count - 1) {
//adjust so the last one always fits perfect
//compensating for numerical imprecision

vofs += size.y + sep;
to = new_size.height - sb->get_margin(SIDE_BOTTOM);
}

int size = to - from;

Rect2 rect(sb->get_margin(SIDE_LEFT), from, w, size);

fit_child_in_rect(c, rect);
cache_y.push_back(from - sb->get_margin(SIDE_TOP) + size * 0.5);

ofs = to;
idx++;
}

update();
Expand Down