Arranging Controls under Container

Godot Version 4.6

I have a simple controlscene used as a UI element and its shape is that of a regular kite (a square rotated 45deg).

I’m progressively adding these under a GridContainer (but could be any other type) at runtime and its default settings correctly put them in this arrangement:

I’m trying to understand if there’s a smart way to make them fill the container space this way:

The only solution I can think of is to avoid relying in the prefab logic of containers and every time I instance one, placing it on the right spot via script.
Would there be any container (or combination thereof) that could allow me to just do

my_container.add_child(new_instance)

I don’t think there is anything built-in that allows to do that easily. You could try building a custom container.

I got it working in a bit of a hacky way. I have 2 “node types”:

  1. TextureControl - a Control that contains the actual TextureRect, or Button, or whatever you want to display. Important: It needs to be half of your desired size.
  2. FillerControl - a Control that is just the same size as the TextureControl, but has nothing inside and just fills the space between the TextureControls.

These 2 types then fill the GridContainer in an interlocking pattern, like so:


So every time you add a TextureControl, you need to add the FillerControl as well. You can add it before or after the TextureControl, depending which pattern you want.

As long as the GridContainer has an odd number of columns, it will form this nice diamond grid pattern.

If you want your elements to be clickable, you might need to set the mouse_filter property to Ignore on the FillerControls

2 Likes

Oh that’s great, I didn’t know you could build custom containers that way!

I’ll give that a go, and in case I fail, I think your hack would be a good alternative!

yep, that was definitely easier than expected!

less than 30 lines :slight_smile:

@tool
extends Container

@export var x_padding:float = 10:
    set(value):
        x_padding=value; queue_sort()

@export var y_padding:float = 10:
    set(value):
        y_padding=value; queue_sort()

func _notification(what):
    if what == NOTIFICATION_SORT_CHILDREN:
        update_layout()

func update_layout():
    var num_children:int    = get_child_count()
    var flip:bool           = false

    for i in num_children:
        var child := get_child(i)
        var child_size:Vector2  = child.size
        var x_pos:float         = i * (child_size.x + x_padding) * 0.5
        var y_pos:float         = 0
        if flip:
            y_pos = (child_size.y * 0.5) + y_padding
        flip = not flip

        fit_child_in_rect(child, Rect2(Vector2(x_pos,y_pos),Vector2.ZERO))
2 Likes