extends Control #"soft" because it isn't enforced if the grid wouldn't fit otherwise export var soft_minimum_columns : int = 2 #these should come from the theme or from theme overrides export var grid_hseperation : int = 4 export var grid_vseperation : int = 4 var _node_width = -1 var _node_height = -1 onready var grid_container = $Scroll/VBox/GridContainer func _update_node_size(): if len(grid_container.get_children()) == 0: return -1 #duplicate a node, assuming they are all the same $MinSizeTesting.add_child(grid_container.get_child(0).duplicate()) #set the size to the minimum $MinSizeTesting.get_child(0).rect_size = Vector2(0, 0) #get the smallest possible size they can contain _node_width = $MinSizeTesting.get_child(0).rect_size.x _node_height = $MinSizeTesting.get_child(0).rect_size.y #plus any seperation due to the grid _node_width += grid_hseperation _node_height += grid_vseperation #remove the copied node $MinSizeTesting.get_child(0).queue_free() func _process(_delta: float) -> void: _move_children_to_grid() #don't do anything if there are no children if len(grid_container.get_children()) == 0: return #don't do anything if the node's haven't got a size yet if _node_width == -1 or _node_height == -1: _update_node_size() return #window_size in case an issue occures with resizing var screen_width = min(rect_size.x, OS.window_size.x) var screen_height = min(rect_size.y, OS.window_size.y) #(columns - 1) means that it can still shrink in size grid_container.columns = max(1, int(screen_width/_node_width) - 1) #don't allow more columns than children (wasted space) grid_container.columns = min(grid_container.columns, grid_container.get_child_count()) #this is used to adjust the columns so that maximum space avalible is used for _i in 10: var empty_rows = _calculate_empty_rows(grid_container.columns, screen_height) #check if perfect height already if empty_rows <= 0: break #disallow single width (unless required previously) if grid_container.columns <= soft_minimum_columns: break #check that it won't need to scroll if columns change if _get_rows(grid_container.columns - 1) * _node_height > screen_height: break grid_container.columns -= 1 func _calculate_empty_rows(columns, screen_height): var rows = _get_rows(columns) var height_not_used : int = screen_height - rows * _node_height var empty_rows : int = height_not_used / _node_height return empty_rows func _get_rows(columns): # rows = children // columns + 1 (if remainder) var rows = grid_container.get_child_count() / columns rows += 1 if grid_container.get_child_count() % columns != 0 else 0 return rows func _move_children_to_grid(): #puts the children into the grid container #ignores the first 2 children (container and size tester) for _i in range(2, get_child_count()): var child = get_child(2) remove_child(child) grid_container.add_child(child) #allow getting nodes from the grid (null, no error version) func get_node_from_grid(node_path : NodePath) -> Node: if grid_container.has_node(node_path): return grid_container.get_node(node_path) else: return null