pygame_gui icon indicating copy to clipboard operation
pygame_gui copied to clipboard

Elements Get Resized When one side is anchored to a moving object

Open GimLala opened this issue 1 year ago • 7 comments

Describe the bug When an element with dynamic sizes is anchored to both the left and right sides, if the size of the container changes, or if it has anchored targets and one of them moves, then the element gets permanently resized without updating to the new size that it should be.

To Reproduce Code to reproduce the behaviour:

import pygame
import pygame_gui
from pygame_gui.core import UIContainer
from pygame_gui.elements import UIButton


pygame.init()

pygame.display.set_caption('Quick Start')
window_surface = pygame.display.set_mode((800, 600))

background = pygame.Surface(window_surface.get_size())
background.fill(pygame.Color('#000000'))

manager = pygame_gui.UIManager((900, 700))

buttons = []
resizing_test = UIContainer(pygame.Rect(40, 50, 700, 500), manager=manager)

hello_button = UIButton(relative_rect=pygame.Rect((-400, 275), (-1, -1)),
                        text='Say Hello 1',
                        manager=manager, container=resizing_test,
                        object_id="SayHello1",
                        anchors={"right": "right"}
                        )
hello_button.set_hold_range((400, 400))
buttons.append(hello_button)
hello_button = UIButton(relative_rect=pygame.Rect((50, 40), (-1, -1)),
                        text='Say Hello 2',
                        manager=manager, container=resizing_test,
                        object_id="SayHello2",
                        anchors={"left": "left", "right": "right", "left_target": hello_button, "top_target": hello_button})
hello_button.set_hold_range((400, 400))
buttons.append(hello_button)
hello_button.set_hold_range((400, 400))

clock = pygame.time.Clock()
is_running = True
pressed = False
element = None

while is_running:
    time_delta = clock.tick(60) / 1000.0
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            is_running = False

        elif event.type == pygame_gui.UI_BUTTON_PRESSED:
            pressed = False
            element = None

        elif event.type == pygame_gui.UI_BUTTON_START_PRESS:
            pressed = True
            element = event.ui_element

        manager.process_events(event)

    if element and element.in_hold_range(pygame.mouse.get_pos()) and pressed:
        pos = pygame.mouse.get_pos()
        dif = pygame.Vector2(pos) - pygame.Vector2(element.rect.center)
        new_pos = dif + pygame.Vector2(element.relative_rect.topleft)
        element.set_relative_position(new_pos)

    manager.update(time_delta)

    window_surface.blit(background, (0, 0))

    pygame.draw.rect(window_surface, "White", resizing_test.rect, 1)
    for button in buttons:
        pygame.draw.rect(window_surface, "Orange", button.rect, 1)

    manager.draw_ui(window_surface)

    pygame.display.update()

Run this code, then drag around the first button, the second button should start resizing. Also note that when the second button itself is dragged it does not resize. I am pretty sure it has to do with update_absolute_rect_position being called in the former case while in the latter the relative rect itself is updated.

Suggested Solution update_absolute_rect_position should take into account dynamic sizes

Expected behaviour I expected the button to move while staying at the size it was before moving

Screenshots If applicable, add screenshots to help explain your problem.

Platform and software (please complete the following information):

  • OS: Windows
  • Pygame GUI version (Tested on main branch during development)
  • Pygame ce version 2.4.0

GimLala avatar Feb 25 '24 12:02 GimLala

Changing some variables around this behaviour is unaffected by dynamic dimensions. What exactly were you trying to do here or expecting to happen?

Did you mean to set hello button 2's anchors like:

anchors={
        "left": "left",
        "right": "right",
        "left_target": hello_button,
        "right_target": hello_button,
        "top_target": hello_button,
    },

? As you have it now one side of the button is anchored to the 'resizing_test' container and the other is anchored to "hello_button 1" so I would expect the button to change width dimension when "hello button 1" is moved.

Anyway here is the whole program re-written:

import pygame
import pygame_gui
from pygame_gui.core import UIContainer
from pygame_gui.elements import UIButton


pygame.init()

pygame.display.set_caption("Quick Start")
window_surface = pygame.display.set_mode((800, 600))

background = pygame.Surface(window_surface.get_size())
background.fill(pygame.Color("#000000"))

manager = pygame_gui.UIManager((900, 700))

buttons = []
resizing_test = UIContainer(pygame.Rect(40, 50, 700, 500), manager=manager)

hello_button_1 = UIButton(
    relative_rect=pygame.Rect((-400, 275), (130, 30)),
    text="Say Hello 1",
    manager=manager,
    container=resizing_test,
    object_id="SayHello1",
    anchors={"right": "right"},
)
hello_button_1.set_hold_range((400, 400))
buttons.append(hello_button_1)
hello_button_2 = UIButton(
    relative_rect=pygame.Rect((50, 40), (130, 30)),
    text="Say Hello 2",
    manager=manager,
    container=resizing_test,
    object_id="SayHello2",
    anchors={
        "left": "left",
        "right": "right",
        "left_target": hello_button_1,
        "right_target": hello_button_1,
        "top_target": hello_button_1,
    },
)
hello_button_2.set_hold_range((400, 400))
buttons.append(hello_button_2)

clock = pygame.time.Clock()
is_running = True
pressed = False
element = None

while is_running:
    time_delta = clock.tick(60) / 1000.0
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            is_running = False

        elif event.type == pygame_gui.UI_BUTTON_PRESSED:
            pressed = False
            element = None

        elif event.type == pygame_gui.UI_BUTTON_START_PRESS:
            pressed = True
            element = event.ui_element

        manager.process_events(event)

    if element and element.in_hold_range(pygame.mouse.get_pos()) and pressed:
        pos = pygame.mouse.get_pos()
        dif = pygame.Vector2(pos) - pygame.Vector2(element.rect.center)
        new_pos = dif + pygame.Vector2(element.relative_rect.topleft)
        element.set_relative_position(new_pos)

    manager.update(time_delta)

    window_surface.blit(background, (0, 0))

    pygame.draw.rect(window_surface, "White", resizing_test.rect, 1)
    for button in buttons:
        pygame.draw.rect(window_surface, "Orange", button.rect, 1)

    manager.draw_ui(window_surface)

    pygame.display.update()

MyreMylar avatar Feb 26 '24 19:02 MyreMylar

I intended the anchors exactly as I had written them, with one side anchored to the container. My expectation was, that since it's a dynamically sized element, once it gets resized, it would realize that it doesn't need all that space and resize itself back to how it was, changing it's relative rect. However, now I think it's reasonable to say that I just shouldn't anchor it to the container if that anchor plays no role anyway. So, the issue is resolved. Yet one question remains: Why did the element not resize itself it was dynamic? doesn't it check regularly whether it's the size that it needs to be and resizes itself automatically? does it also mean that it would permanently lose its ability to be dynamically sized once I resize it using set_dimensions?

GimLala avatar Feb 29 '24 06:02 GimLala

I discovered another problem, if you move the hello_button_1 too fast to the right past the point where hello_button_2 is anchored to the container, then even though the rect becomes of size 0, the image of the button doesn't completely disappear, causing a partial ghost image. The faster you move the button, the more the image of hello_button_2 gets left over

GimLala avatar Feb 29 '24 13:02 GimLala

Even weirder is that when you change the theming after it has been resized, whether expanded or reduced in size, it resets its own size back to how it would be if it had not been resized

GimLala avatar Feb 29 '24 13:02 GimLala

Okay, I found another bug again. When you change the horizontal alignment of the buttons from center (or left) to right, then the text moves to the left even though it shouldn't because there isn't enough space, so the text partially disappears.

My intuition about there being potential for bugs was right! But I think they're all related in some way to how the dynamic sizing conflicts with anchoring.

GimLala avatar Feb 29 '24 13:02 GimLala

Have you used the latest version of the main branch? This may be related to #514

LondonClass avatar Mar 03 '24 06:03 LondonClass

Okay, I found another bug again. When you change the horizontal alignment of the buttons from center (or left) to right, then the text moves to the left even though it shouldn't because there isn't enough space, so the text partially disappears.

My intuition about there being potential for bugs was right! But I think they're all related in some way to how the dynamic sizing conflicts with anchoring.

When you figure out what the issue is please post some code and describe it clearly so I can have a look.

MyreMylar avatar Mar 03 '24 09:03 MyreMylar

I'm going to close this for now as the original issue seems resolved - while other issues are mentioned they need a solid method of reproduction or example code for me to try and figure out any bugs.

MyreMylar avatar Apr 07 '24 17:04 MyreMylar