godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
90.48k stars 21.07k forks source link

Control.set_anchors_preset not work as expect #66651

Open CsloudX opened 2 years ago

CsloudX commented 2 years ago

Godot version

v4.0.beta2.official [f8745f2f7]

System information

Windows 10 64bit

Issue description

In the docs, it say: image

In the 2D editor, I can set the anchors preset as expect: GIF

And I want set preset with code: image

But there wasn't any effect: GIF

Steps to reproduce

Please run the reproduction project

Minimal reproduction project

AnchorTest.zip

dominiks commented 2 years ago

I think using the default true for the second parameter causes Godot to adjust the offsets so that no change is visible. If you call

box.set_anchors_preset(Control.PRESET_FULL_RECT)
box.offset_left = 0
box.offset_right = 0
box.offset_bottom = 0
box.offset_top = 0

You get the expected outcome. The anchors are automatically set to 0 when using the preset via the editor.

Calling box.set_anchors_preset(Control.PRESET_FULL_RECT, false) will show you an expanded rectangle but with the old offsets you provided.

Edited to add: grafik

CsloudX commented 2 years ago

@dominiks Thanks

dominiks commented 2 years ago

But I have to agree that this does not work as expected and I would have ran into the same issue, even though it's documented. I'm not sure if there is a technical reason for this behaviour or if set_anchors_and_offsets_preset is supposed to mirror the behaviour of the editor button.

markcagatandavis commented 11 months ago

This seems to still be an issue, I get some weird formatting when I try to use the set_anchors_and_offsets_preset.

Setting by above advised steps resolves this, but annoying:

image

image

image

image

pc9098 commented 4 months ago

This seems to still be an issue, I get some weird formatting when I try to use the set_anchors_and_offsets_preset.

Setting by above advised steps resolves this, but annoying:

I've tested a bit. Your issue seems to be the same as mine. The problem you show in your images is that the node UI/VBoxContainer is in the editor as "Layout Mode: Position" or "Layout Mode: Top Left". If you cahnge it to "Layout Mode: Center" you will see it will work. This is not an intended feature but a bug

pc9098 commented 4 months ago

Changing the propierty anchors_presetsolves everything. But this propierty is not meant to be used scripting, it's supposed to be internal but it was exposed by mistake.

From :

Both anchors_preset and layout_mode are not supposed to be used in scripting. They are marked as PROPERTY_USAGE_INTERNAL, and are only relevant for the editor. They exist as properties because that's the only way to have them in the inspector and in a specific order.

pc9098 commented 4 months ago

Found where the mistake was. This is the function set_anchor in Control.cpp, image

The problem is in

if (!p_keep_offset) { data.offset[p_side] = previous_pos - data.anchor[p_side] * parent_range; if (p_push_opposite_anchor) { data.offset[(p_side + 2) % 4] = previous_opposite_pos - data.anchor[(p_side + 2) % 4] * parent_range; } }

I feel like it is indeed changing the anchor_preset but also changing the offset just as much so it looks like nothing really happended. I'll try to code a solution for this

EDIT: pretty sure data.offset[p_side] = 0 I'll test tomorrow

EDIT2: tested it and set_anchor() works perfectly with PRESET_LEFT and PRESET_TOP. Will rewrite a few things so it works with RIGHT and BOTTOM.

EDIT3: Since all this code looks good (to me) this would mean the offset of RIGHT and/or BOT is changed somewhere else, or it is badly redrawn. I cannot think of any else. I give up to some legend to show up

pc9098 commented 4 months ago

DONE! I finally got a way to set_anchor() to work! This is the code of the function set_anchor (which is in godot/scene/gui/Control.cpp):

image

void Control::set_anchor(Side p_side, real_t p_anchor, bool p_keep_offset, bool p_push_opposite_anchor) {
    ERR_MAIN_THREAD_GUARD;
    ERR_FAIL_INDEX((int)p_side, 4);

    Rect2 parent_rect = get_parent_anchorable_rect();
    // real_t parent_range = (p_side == SIDE_LEFT || p_side == SIDE_RIGHT) ? parent_rect.size.x : parent_rect.size.y;
    // real_t previous_pos = data.offset[p_side] + data.anchor[p_side] * parent_range;
    // real_t previous_opposite_pos = data.offset[(p_side + 2) % 4] + data.anchor[(p_side + 2) % 4] * parent_range;

    data.anchor[p_side] = p_anchor;

    if (((p_side == SIDE_LEFT || p_side == SIDE_TOP) && data.anchor[p_side] > data.anchor[(p_side + 2) % 4]) || ((p_side == SIDE_RIGHT || p_side == SIDE_BOTTOM) && data.anchor[p_side] < data.anchor[(p_side + 2) % 4])) {
        if (p_push_opposite_anchor) {
            data.anchor[(p_side + 2) % 4] = data.anchor[p_side];
        } else {
            data.anchor[p_side] = data.anchor[(p_side + 2) % 4];
        }
    }

    if (!p_keep_offset) {
        if(p_side == SIDE_LEFT) {data.offset[0] = 0;};
        if(p_side == SIDE_TOP) {data.offset[1] = 0;};
        if(p_side == SIDE_RIGHT) {data.offset[2] = (data.anchor[2] - 1) * (parent_rect.size.x - parent_rect.position.x);};
        if(p_side == SIDE_BOTTOM) {data.offset[3] = (data.anchor[3] - 1) * (parent_rect.size.y - parent_rect.position.y);};
    }

    if (is_inside_tree()) {
        _size_changed();
    }

    queue_redraw();
}

I gotta say. It could be much better for some reasons:

  1. Making a bunch of "if" with all possibilities (left, top, right, bottom) is kind of sad

  2. Other functions (lets call them functions X's which I don't know all of them but I think queue_redraw or _size_changed are function X's) depend on the offsets. set_anchor changes the offset when 3º argument is false (by default is false). But when we are talking about RIGHT and BOTTOM and 3º argument is false, offset[3] (which is right) and offset[4] which is bottom) are not zero always, they are zero just when 2º argument is 0. This is because functions X's are done in such a way that they think it is apropiate that they shouldn't be zero when there is no margin when we are talking about SIDE_LEFT or SIDE_BOTTOM. I honestly disagree with how functions X's work, because if you are setting 3º argument to false then the offset should be zero always because margins are zero. The code I wrote works with those functions X's in mind but I gotta insist, it would be better if we just did offset = 0 and change those functions X's.

I haven't tried set anchor_preset or any other. They may work.

I got no time to test if it worked, so If nobody test it in a month I will be back to test it. I would like to do a pull request or call testes or something, but I don't know how github works. Anyone can tell me what the next thing to do? Is it to do a pull request?

groud commented 4 months ago

So, sorry I'm just picking up with this right now. I am not sure the API is clear, and it seems that the current state of the API + editor is causing some confusion. There might be a few bugs but I need to investigate it a bit further.

To make it clearer:

From a quick test, there are indeed a few bugs here and there, and I need to figure out what broke things. I think the set_anchor function is likely fine.

I'll have a deeper look to that when I get the time to.

pc9098 commented 4 months ago

Ok, so offset is different from margin. Let's suppose we have a very simple scene of controls. Is there an intuitive way to see how much the offset is? Here are some examples of what I feel is intuitive: image You can see that those properties can be easily drawn, but I am not so sure about offset.

I discovered offset[SIDE_RIGHT] (and offset[SIDE_BOTTOM]) follow this formula. But still, I can't figure a way to draw it: image

I already did a pull request that solves set_anchor completely.

I rather have other property different from offset because it feels inintuitive (at this moment), so I may propose to change how distance with anchors are calculated, and erease completely offset from code (maybe substitute it to something more intuitive).

What do you think?

groud commented 4 months ago

Ok, so offset is different from margin. Let's suppose we have a very simple scene of controls. Is there an intuitive way to see how much the offset is? Here are some examples of what I feel is intuitive:

Hmm, this drawing does not seem right to me. The anchors are usually within the boundaries of the parent, and they don't have anything to do with the parent positioning (they only impact the child control ones). Here is a better representation: Copie d'écran_20240604_091529 (here the parent is the whole window but it works the same for having a parent control)

The position is not depicted on that picture, but it is basically the position of the child control relative to the parent's one. So here, and in general, position = Vector2(left_anchor * parent_size + left_offset, top_anchor * parent_size + top_offset)

pc9098 commented 4 months ago

Ok now I understand. Totally my bad! I was using wrongly offset. I will update my PR. I may also fix set_anchor_preset and the others.