Add FlowContainer wrap options for center alignment.

This commit is contained in:
Koyper 2023-01-29 11:31:31 -06:00
parent 780e1a5040
commit e3efd51592
3 changed files with 88 additions and 10 deletions

View File

@ -21,6 +21,9 @@
<member name="alignment" type="int" setter="set_alignment" getter="get_alignment" enum="FlowContainer.AlignmentMode" default="0"> <member name="alignment" type="int" setter="set_alignment" getter="get_alignment" enum="FlowContainer.AlignmentMode" default="0">
The alignment of the container's children (must be one of [constant ALIGNMENT_BEGIN], [constant ALIGNMENT_CENTER], or [constant ALIGNMENT_END]). The alignment of the container's children (must be one of [constant ALIGNMENT_BEGIN], [constant ALIGNMENT_CENTER], or [constant ALIGNMENT_END]).
</member> </member>
<member name="last_wrap_alignment" type="int" setter="set_last_wrap_alignment" getter="get_last_wrap_alignment" enum="FlowContainer.LastWrapAlignmentMode" default="0">
The wrap behavior of the last, partially filled row or column (must be one of [constant LAST_WRAP_ALIGNMENT_INHERIT], [constant LAST_WRAP_ALIGNMENT_BEGIN], [constant LAST_WRAP_ALIGNMENT_CENTER], or [constant LAST_WRAP_ALIGNMENT_END]).
</member>
<member name="reverse_fill" type="bool" setter="set_reverse_fill" getter="is_reverse_fill" default="false"> <member name="reverse_fill" type="bool" setter="set_reverse_fill" getter="is_reverse_fill" default="false">
If [code]true[/code], reverses fill direction. Horizontal [FlowContainer]s will fill rows bottom to top, vertical [FlowContainer]s will fill columns right to left. If [code]true[/code], reverses fill direction. Horizontal [FlowContainer]s will fill rows bottom to top, vertical [FlowContainer]s will fill columns right to left.
When using a vertical [FlowContainer] with a right to left [member Control.layout_direction], columns will fill left to right instead. When using a vertical [FlowContainer] with a right to left [member Control.layout_direction], columns will fill left to right instead.
@ -40,6 +43,18 @@
<constant name="ALIGNMENT_END" value="2" enum="AlignmentMode"> <constant name="ALIGNMENT_END" value="2" enum="AlignmentMode">
The child controls will be arranged at the end of the container, i.e. bottom if orientation is vertical, right if orientation is horizontal (left for RTL layout). The child controls will be arranged at the end of the container, i.e. bottom if orientation is vertical, right if orientation is horizontal (left for RTL layout).
</constant> </constant>
<constant name="LAST_WRAP_ALIGNMENT_INHERIT" value="0" enum="LastWrapAlignmentMode">
The last partially filled row or column will wrap aligned to the previous row or column in accordance with [member alignment].
</constant>
<constant name="LAST_WRAP_ALIGNMENT_BEGIN" value="1" enum="LastWrapAlignmentMode">
The last partially filled row or column will wrap aligned to the beginning of the previous row or column.
</constant>
<constant name="LAST_WRAP_ALIGNMENT_CENTER" value="2" enum="LastWrapAlignmentMode">
The last partially filled row or column will wrap aligned to the center of the previous row or column.
</constant>
<constant name="LAST_WRAP_ALIGNMENT_END" value="3" enum="LastWrapAlignmentMode">
The last partially filled row or column will wrap aligned to the end of the previous row or column.
</constant>
</constants> </constants>
<theme_items> <theme_items>
<theme_item name="h_separation" data_type="constant" type="int" default="4"> <theme_item name="h_separation" data_type="constant" type="int" default="4">

View File

@ -38,6 +38,7 @@ struct _LineData {
int min_line_length = 0; int min_line_length = 0;
int stretch_avail = 0; int stretch_avail = 0;
float stretch_ratio_total = 0; float stretch_ratio_total = 0;
bool is_filled = false;
}; };
void FlowContainer::_resort() { void FlowContainer::_resort() {
@ -58,6 +59,7 @@ void FlowContainer::_resort() {
float line_stretch_ratio_total = 0; float line_stretch_ratio_total = 0;
int current_container_size = vertical ? get_rect().size.y : get_rect().size.x; int current_container_size = vertical ? get_rect().size.y : get_rect().size.x;
int children_in_current_line = 0; int children_in_current_line = 0;
Control *last_child = nullptr;
// First pass for line wrapping and minimum size calculation. // First pass for line wrapping and minimum size calculation.
for (int i = 0; i < get_child_count(); i++) { for (int i = 0; i < get_child_count(); i++) {
@ -77,7 +79,7 @@ void FlowContainer::_resort() {
} }
if (ofs.y + child_msc.y > current_container_size) { if (ofs.y + child_msc.y > current_container_size) {
line_length = ofs.y - theme_cache.v_separation; line_length = ofs.y - theme_cache.v_separation;
lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total }); lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total, true });
// Move in new column (vertical line). // Move in new column (vertical line).
ofs.x += line_height + theme_cache.h_separation; ofs.x += line_height + theme_cache.h_separation;
@ -99,7 +101,7 @@ void FlowContainer::_resort() {
} }
if (ofs.x + child_msc.x > current_container_size) { if (ofs.x + child_msc.x > current_container_size) {
line_length = ofs.x - theme_cache.h_separation; line_length = ofs.x - theme_cache.h_separation;
lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total }); lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total, true });
// Move in new line. // Move in new line.
ofs.y += line_height + theme_cache.v_separation; ofs.y += line_height + theme_cache.v_separation;
@ -116,11 +118,16 @@ void FlowContainer::_resort() {
ofs.x += child_msc.x; ofs.x += child_msc.x;
} }
last_child = child;
children_minsize_cache[child] = child_msc; children_minsize_cache[child] = child_msc;
children_in_current_line++; children_in_current_line++;
} }
line_length = vertical ? (ofs.y) : (ofs.x); line_length = vertical ? (ofs.y) : (ofs.x);
lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total }); 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);
}
lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total, is_filled });
// Second pass for in-line expansion and alignment. // Second pass for in-line expansion and alignment.
@ -158,17 +165,43 @@ void FlowContainer::_resort() {
// but only if the line doesn't contain a child that expands. // 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)) { if (child_idx_in_line == 0 && Math::is_equal_approx(line_data.stretch_ratio_total, 0)) {
int alignment_ofs = 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;
switch (alignment) { switch (alignment) {
case ALIGNMENT_CENTER: case ALIGNMENT_BEGIN: {
alignment_ofs = line_data.stretch_avail / 2; if (last_wrap_alignment != LAST_WRAP_ALIGNMENT_INHERIT && is_not_first_line_and_not_filled) {
break; if (last_wrap_alignment == LAST_WRAP_ALIGNMENT_END) {
case ALIGNMENT_END: alignment_ofs = line_data.stretch_avail - prior_stretch_avail;
alignment_ofs = line_data.stretch_avail; } else if (last_wrap_alignment == LAST_WRAP_ALIGNMENT_CENTER) {
break; alignment_ofs = (line_data.stretch_avail - prior_stretch_avail) * 0.5;
}
}
} break;
case ALIGNMENT_CENTER: {
if (last_wrap_alignment != LAST_WRAP_ALIGNMENT_INHERIT && last_wrap_alignment != LAST_WRAP_ALIGNMENT_CENTER && is_not_first_line_and_not_filled) {
if (last_wrap_alignment == LAST_WRAP_ALIGNMENT_END) {
alignment_ofs = line_data.stretch_avail - (prior_stretch_avail * 0.5);
} else { // Is LAST_WRAP_ALIGNMENT_BEGIN
alignment_ofs = prior_stretch_avail * 0.5;
}
} else {
alignment_ofs = line_data.stretch_avail * 0.5;
}
} break;
case ALIGNMENT_END: {
if (last_wrap_alignment != LAST_WRAP_ALIGNMENT_INHERIT && last_wrap_alignment != LAST_WRAP_ALIGNMENT_END && is_not_first_line_and_not_filled) {
if (last_wrap_alignment == LAST_WRAP_ALIGNMENT_BEGIN) {
alignment_ofs = prior_stretch_avail;
} else { // Is LAST_WRAP_ALIGNMENT_CENTER
alignment_ofs = prior_stretch_avail + (line_data.stretch_avail - prior_stretch_avail) * 0.5;
}
} else {
alignment_ofs = line_data.stretch_avail;
}
} break;
default: default:
break; break;
} }
if (vertical) { /* VERTICAL */ if (vertical) { /* VERTICAL */
ofs.y += alignment_ofs; ofs.y += alignment_ofs;
} else { /* HORIZONTAL */ } else { /* HORIZONTAL */
@ -314,6 +347,18 @@ FlowContainer::AlignmentMode FlowContainer::get_alignment() const {
return alignment; return alignment;
} }
void FlowContainer::set_last_wrap_alignment(LastWrapAlignmentMode p_last_wrap_alignment) {
if (last_wrap_alignment == p_last_wrap_alignment) {
return;
}
last_wrap_alignment = p_last_wrap_alignment;
_resort();
}
FlowContainer::LastWrapAlignmentMode FlowContainer::get_last_wrap_alignment() const {
return last_wrap_alignment;
}
void FlowContainer::set_vertical(bool p_vertical) { void FlowContainer::set_vertical(bool p_vertical) {
ERR_FAIL_COND_MSG(is_fixed, "Can't change orientation of " + get_class() + "."); ERR_FAIL_COND_MSG(is_fixed, "Can't change orientation of " + get_class() + ".");
vertical = p_vertical; vertical = p_vertical;
@ -346,6 +391,8 @@ void FlowContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_alignment", "alignment"), &FlowContainer::set_alignment); ClassDB::bind_method(D_METHOD("set_alignment", "alignment"), &FlowContainer::set_alignment);
ClassDB::bind_method(D_METHOD("get_alignment"), &FlowContainer::get_alignment); ClassDB::bind_method(D_METHOD("get_alignment"), &FlowContainer::get_alignment);
ClassDB::bind_method(D_METHOD("set_last_wrap_alignment", "last_wrap_alignment"), &FlowContainer::set_last_wrap_alignment);
ClassDB::bind_method(D_METHOD("get_last_wrap_alignment"), &FlowContainer::get_last_wrap_alignment);
ClassDB::bind_method(D_METHOD("set_vertical", "vertical"), &FlowContainer::set_vertical); ClassDB::bind_method(D_METHOD("set_vertical", "vertical"), &FlowContainer::set_vertical);
ClassDB::bind_method(D_METHOD("is_vertical"), &FlowContainer::is_vertical); ClassDB::bind_method(D_METHOD("is_vertical"), &FlowContainer::is_vertical);
ClassDB::bind_method(D_METHOD("set_reverse_fill", "reverse_fill"), &FlowContainer::set_reverse_fill); ClassDB::bind_method(D_METHOD("set_reverse_fill", "reverse_fill"), &FlowContainer::set_reverse_fill);
@ -354,8 +401,13 @@ void FlowContainer::_bind_methods() {
BIND_ENUM_CONSTANT(ALIGNMENT_BEGIN); BIND_ENUM_CONSTANT(ALIGNMENT_BEGIN);
BIND_ENUM_CONSTANT(ALIGNMENT_CENTER); BIND_ENUM_CONSTANT(ALIGNMENT_CENTER);
BIND_ENUM_CONSTANT(ALIGNMENT_END); BIND_ENUM_CONSTANT(ALIGNMENT_END);
BIND_ENUM_CONSTANT(LAST_WRAP_ALIGNMENT_INHERIT);
BIND_ENUM_CONSTANT(LAST_WRAP_ALIGNMENT_BEGIN);
BIND_ENUM_CONSTANT(LAST_WRAP_ALIGNMENT_CENTER);
BIND_ENUM_CONSTANT(LAST_WRAP_ALIGNMENT_END);
ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Begin,Center,End"), "set_alignment", "get_alignment"); ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Begin,Center,End"), "set_alignment", "get_alignment");
ADD_PROPERTY(PropertyInfo(Variant::INT, "last_wrap_alignment", PROPERTY_HINT_ENUM, "Inherit,Begin,Center,End"), "set_last_wrap_alignment", "get_last_wrap_alignment");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reverse_fill"), "set_reverse_fill", "is_reverse_fill"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reverse_fill"), "set_reverse_fill", "is_reverse_fill");

View File

@ -42,6 +42,12 @@ public:
ALIGNMENT_CENTER, ALIGNMENT_CENTER,
ALIGNMENT_END ALIGNMENT_END
}; };
enum LastWrapAlignmentMode {
LAST_WRAP_ALIGNMENT_INHERIT,
LAST_WRAP_ALIGNMENT_BEGIN,
LAST_WRAP_ALIGNMENT_CENTER,
LAST_WRAP_ALIGNMENT_END
};
private: private:
int cached_size = 0; int cached_size = 0;
@ -50,6 +56,7 @@ private:
bool vertical = false; bool vertical = false;
bool reverse_fill = false; bool reverse_fill = false;
AlignmentMode alignment = ALIGNMENT_BEGIN; AlignmentMode alignment = ALIGNMENT_BEGIN;
LastWrapAlignmentMode last_wrap_alignment = LAST_WRAP_ALIGNMENT_INHERIT;
struct ThemeCache { struct ThemeCache {
int h_separation = 0; int h_separation = 0;
@ -71,6 +78,9 @@ public:
void set_alignment(AlignmentMode p_alignment); void set_alignment(AlignmentMode p_alignment);
AlignmentMode get_alignment() const; AlignmentMode get_alignment() const;
void set_last_wrap_alignment(LastWrapAlignmentMode p_last_wrap_alignment);
LastWrapAlignmentMode get_last_wrap_alignment() const;
void set_vertical(bool p_vertical); void set_vertical(bool p_vertical);
bool is_vertical() const; bool is_vertical() const;
@ -102,5 +112,6 @@ public:
}; };
VARIANT_ENUM_CAST(FlowContainer::AlignmentMode); VARIANT_ENUM_CAST(FlowContainer::AlignmentMode);
VARIANT_ENUM_CAST(FlowContainer::LastWrapAlignmentMode);
#endif // FLOW_CONTAINER_H #endif // FLOW_CONTAINER_H