99 lines
3.2 KiB
GDScript
99 lines
3.2 KiB
GDScript
@tool
|
|
extends Container
|
|
|
|
class_name WaterfallFlowContainer
|
|
|
|
# The flow container will fit as many children in a row as it can
|
|
# using their minimum size, and will then continue checked the next row.
|
|
# Does not use SIZE_EXPAND flags of children.
|
|
|
|
# TODO: half-respect vertical SIZE_EXPAND flags by expanding the child to match
|
|
# the tallest child in that row?
|
|
# TODO: Respect scaled children?
|
|
# TODO: Can we find a way to intuitively use a child's horizontal SIZE_EXPAND
|
|
# flag?
|
|
|
|
@export var h_separation: int = 0
|
|
@export var v_separation: int = 0
|
|
@export var columns: int = 2
|
|
|
|
var reported_height_at_last_minimum_size_call: float = 0
|
|
|
|
func _get_minimum_size() -> Vector2:
|
|
var height = calculate_layout(false)
|
|
reported_height_at_last_minimum_size_call = height
|
|
return Vector2(size.x, height)
|
|
|
|
func _notification(what):
|
|
if (what==NOTIFICATION_SORT_CHILDREN):
|
|
var height = calculate_layout(true)
|
|
size.y = height
|
|
if height != reported_height_at_last_minimum_size_call:
|
|
make_parent_reevaluate_our_size()
|
|
# If apply is true, the children will actually be moved to the calculated
|
|
# locations.
|
|
# Returns the resulting height.
|
|
func calculate_layout(apply: bool,childNum :int = get_child_count()) -> float:
|
|
var container_width: float = size.x
|
|
|
|
var fixWidth = (container_width-h_separation*(columns-1))/columns
|
|
var current_column : int = 0
|
|
var columnsHeight : Array = []
|
|
for column in columns :
|
|
columnsHeight.append(0)
|
|
|
|
var prePositionHeight= 0
|
|
for childIndex in childNum :
|
|
var child = get_child(childIndex)
|
|
if child == null :
|
|
continue
|
|
if not child.has_method("get_combined_minimum_size"):
|
|
continue
|
|
if not child.visible:
|
|
continue
|
|
var child_min_size: Vector2 = child.get_combined_minimum_size()
|
|
child.custom_minimum_size = Vector2(fixWidth, child_min_size.y)
|
|
var curPosition = Vector2.ZERO
|
|
|
|
if child is BaseDataControl and child.data.has("waterfall"):
|
|
current_column = child.data.waterfall.column
|
|
var positionHeight = prePositionHeight+child.data.waterfall.diffHeight
|
|
curPosition = Vector2( current_column*(fixWidth+h_separation), positionHeight)
|
|
columnsHeight[current_column] =positionHeight + child_min_size.y
|
|
prePositionHeight = positionHeight
|
|
else :
|
|
var minHeight = columnsHeight[0]
|
|
for column in columns :
|
|
minHeight = min(columnsHeight[column],minHeight)
|
|
for column in columns :
|
|
if minHeight == columnsHeight[column] :
|
|
current_column = column
|
|
break
|
|
|
|
var margin = v_separation
|
|
if child.get_index() < columns :
|
|
margin = 0
|
|
|
|
curPosition = Vector2( current_column*(fixWidth+h_separation), columnsHeight[current_column] + margin)
|
|
if child is BaseDataControl :
|
|
child.data["waterfall"] = {
|
|
"column" : current_column,
|
|
"diffHeight" : (curPosition.y - prePositionHeight)
|
|
|
|
}
|
|
prePositionHeight = curPosition.y
|
|
columnsHeight[current_column] += child_min_size.y + margin
|
|
|
|
if apply:
|
|
fit_child_in_rect(child, Rect2(curPosition, child_min_size))
|
|
|
|
var maxHeight = 0
|
|
for column in columns :
|
|
maxHeight = max(columnsHeight[column],maxHeight)
|
|
return maxHeight
|
|
|
|
func make_parent_reevaluate_our_size():
|
|
# Hacky solution. Once there is a function for this, use it.
|
|
custom_minimum_size = Vector2(0, 20000)
|
|
custom_minimum_size = Vector2(0, 0)
|