+1 vote

I have read several threads which say that it isn't possible for children of container nodes to be scaled because the parent controls that property, and yet this simple demo project seems to do it (unless I'm missing something).

Is this just a bug and it isn't supposed to be able to do that? If so, how can one achieve, for example, a tween of a Label's rect_scale from one scale to another if it sits inside, say, a VBoxContainer? I have a hard time believing that such a basic GUI operation is impossible in Godot.

Godot version v3.3.stable.official
in Engine by (62 points)

So I overloaded the set_scale() and draw() functions of the Cancel label and found that when you call_deferred("focus_cursor",current_choice):

end
(1, 1) draw, vis in tree: True
set rect (1.6, 1.6) vis in tree: True
(1.6, 1.6) draw, vis in tree: True

but when you focus_cursor(current_choice):

set rect (1.6, 1.6) vis in tree: True
end
(1, 1) draw, vis in tree: True

Even when you call_deffered("_draw") on the Cancel label just draws it twice at (1,1) scale. Strange.

I remember replying to someone that they could use the scale to animate a VBoxContainer children appearing. So you should be fine using the rect_scale property. One thing for sure is that you can't control the size of the children, because it's controlled with the parent container.

From https://docs.godotengine.org/en/3.3/tutorials/gui/gui_containers.html :

"When a Container-derived node is used, all children Control nodes give up their own positioning ability. This means the Container will control their positioning and any attempt to manually alter these nodes will be either ignored or invalidated the next time their parent is resized."

func _input(event):
    if event.is_action_released("mb_left"):
        $CenterContainer/FileSelectBox/CenterContainer/ConfirmBox/ChoicesBox.rect_size += Vector2.ONE

So when you scale the Cancel label using focus_cursor() then click to resize the parent the scale set in focus_cursor() goes away. Which follows the above logic.

From https://docs.godotengine.org/en/3.3/classes/class_control.html#class-control-property-rect-scale :

Vector2 rect_scale [default: Vector2( 1, 1 )]

The node's scale, relative to its rectsize. Change this property to scale the node around its rectpivotoffset. The Control's hinttooltip will also scale according to this value.

Note: This property is mainly intended to be used for animation purposes. Text inside the Control will look pixelated or blurry when the Control is scaled. To support multiple resolutions in your project, use an appropriate viewport stretch mode as described in the documentation instead of scaling Controls individually.

Note: If the Control node is a child of a Container node, the scale will be reset to Vector2(1, 1) when the scene is instanced. To set the Control's scale when it's instanced, wait for one frame using yield(gettree(), "idleframe") then set its rect_scale property.

when the scene is instanced stands out to me. Obviously it's instanced when the game starts but it's exhibiting the same behavior when it's being made visible as well

Thanks for the very detailed research and response here. I really appreciate it.

If I'm understanding properly (not sure I am), it seems that I cannot change the size of a Control child of a container node, but I can change the scale. However, if ever the container node is resized that scaling will be reset to Vector2.ONE. This leaves us with the mystery of why this scale is also reset after the parent's visibility is changed, with two connected mysteries:

  • This only occurs the first time that the confirmation container node is made visible, and not when it is subsequently made visible again
  • This does not occur for the file select container node even though it is initially not visible and is later made visible

Is this the right takeaway? If so, how does one go about tweening a Label's rect_scale if it is a child of a container node? Is it just impossible?

1 Answer

+1 vote
Best answer
  • it seems that I cannot change the size of a Control child of a container node

You can but it is inadvisable for two reasons: 1) Control nodes are meant for GUIs so they're meant to stay uniform across any size/resolution display. 2) If you resize the child's rect_size in such a way that it increases the rect_size of the parent, the resizing of the child will be overridden by the values calculated for it when the parent is resized (which should always fire last).

  • This only occurs the first time that the confirmation container node is made visible, and not when it is subsequently made visible again

From my tinkering with your code that is not the case. This particular behavior is consistent and has to do with how you are calling the function. (explained below)

  • This does not occur for the file select container node even though it is initially not visible and is later made visible

The main difference here is you are using call_deferred("focus_cursor",current_selection) when you are doing the file select container and focus_cursor(current_choice) when doing the confirmation dialog. In the call_deferred() case, the Cancel label is drawn to the screen at (1,1) scale, then the scale is set to (1.6, 1.6) the drawing code recognizes this change and draws it with (1.6, 1.6) scale.

In the direct call to focus_cursor() the scale is set to (1.6, 1.6) (AFTER the first idle frame AFTER the nodes have been instanced), the drawing code seemingly ignores this change and the Cancel label is drawn at (1,1) scale. This behavior seems to contradict the documentation unless I am misunderstanding their usage of the term instanced.

  • If so, how does one go about tweening a Label's rect_scale if it is a
    child of a container node? Is it just impossible?

I added a tween as a child node to the main control node of your project, stored it in the onready var tweeny and modified your code this way:

func focus_cursor(selection:Control):
    tweeny.interpolate_property(selection,"rect_scale",null,focus_scale,1)
    tweeny.start()

As long as you call_deferred() focus_cursor() you see the see the effect of the tween. Interestingly enough, if you call focus_cursor() directly, you don't see the effect of the tween BUT the scale is set to (1.6, 1.6) which is.... just odd behavior to me.

Long story short if you want to change the rect_scale of a Control node that has a Container for a parent always either set_deferred() that property or call_deferred() the function that handles the tweening of that property.

by (3,389 points)
selected by

This was very helpful.

I feel a bit silly using call_deferred for the file select and not for the confirmation box; I had originally used it for the confirmation box and thought I saw no scaling so assumed it wasn't working then changed it (but left the file select's call as-is), but now I realize my scaling factor was probably too small to visually notice. I should have been eyeing the remote inspector ....

I've switched to this approach and now things are working, but I do have this sinking feeling that I'm doing something which goes against the dev's intentions with UI. Perhaps I'll have to redesign my menus.

For now, thanks!

"I feel a bit silly using call_deferred for the file select and not for the confirmation box"
Hahahaha I thought you knew and were setting up a scenario wondering why it works one way and not the other!!! Regardless, that slight difference made it a great question - I really learned a lot looking into this one. So thank you for your silly error!

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.