Skip to content

GUI AnchorLayout broken when using ScrollArea #2818

Open

Description

@DragonMoffon

While trying to right align and left aligned different elements in a scroll area the UIAnchorLayout caused all of the UI to disappear. To test I made this small minimal snippet, and discovered that mixing UIBoxLayout and UIAnchorLayout led to the UIAnchorLayout children to draw over the UIBoxLayout.

from arcade import Window, gui
from arcade.gui import experimental


class TestWindow(Window):
    def __init__(self):
        super().__init__()
        self.manager = gui.UIManager()
        self.manager.enable()

        self.entry_box_1 = gui.UIBoxLayout(
            vertical=False,
            width=self.width,
            height=150,
            space_between=15,
            children=(gui.UIDummy() for _ in range(5)),
        )
        self.entry_box_2 = gui.UIAnchorLayout(
            width=self.width, height=150, children=(gui.UIDummy())
        )
        self.entry_box_2.add(gui.UIDummy(), anchor_x="left")

        self.scroll_box = gui.UIBoxLayout(
            width=self.width,
            height=4 * self.height,
            vertical=True,
            children=(
                self.entry_box_1,
                self.entry_box_2,
            ),
        )
        self.scroll_area = experimental.UIScrollArea(
            x=0,
            y=0,
            width=self.width,
            height=self.height,
            canvas_size=(self.width, 4 * self.height),
            children=(self.scroll_box,),
        )
        self.manager.add(self.scroll_area)

    def on_draw(self):
        self.clear()
        self.manager.draw()

    def on_key_press(self, symbol: int, modifiers: int):
        self.manager.debug()


win = TestWindow()
win.run()

Result:
Image

Expected:
Image

Activity

  1. added
    guiRelated to arcade GUI (sub module arcade.gui)
    on Jan 29, 2026
  2. self-assigned this
    on Feb 15, 2026
  3. eruvanos commented on Feb 15, 2026

    @eruvanos
    Member

    Within the code Layouts are given a size, but the size_hint is not set to None, which means the defaults apply and the sizes are overwritten.

    UIBoxLayouts have default: size_hint=(0,0) and calculate size_hint_minbased on children. UIAnchorLayout have defaultsize_hint=(1,1) and use a fixed size_hint_min which is None by default.

    In the code scroll_box checks minimal size of children (a UIBoxLayout with dummies ; UIAnchorLayout: 0,0)
    and resizes, which causes the UIAnchorLayout to collabs and it gets drawn above the UIBoxLayout.


    Another thing:

    self.entry_box_2 = gui.UIAnchorLayout(
      width=self.width, height=150, children=(gui.UIDummy())  # there is a `,` missing -> (gui.UIDummy(),) 
    )

    While the code does not crash, the constructor gets only the dummy and not a tuple, which causes the internal code to loop over the children of UIDummy, but there are non.


    The "fixed" code:

    import arcade
    from arcade import Window, gui
    from arcade.gui import experimental
    
    
    class TestWindow(Window):
        def __init__(self):
            super().__init__()
            self.manager = gui.UIManager()
            self.manager.enable()
    
            self.entry_box_1 = gui.UIBoxLayout(
                vertical=False,
                width=self.width,
                height=150,
                space_between=15,
                children=(gui.UIDummy() for _ in range(5)),
            )
            self.entry_box_2 = gui.UIAnchorLayout(
                width=self.width, height=150, children=(gui.UIDummy(),), # 👈 fixed tuple creation
                size_hint=None # 👈 added to fix size
            )
            self.entry_box_2.add(gui.UIDummy(), anchor_x="left") 
            self.entry_box_2.add(gui.UIDummy(), anchor_x="right") # 👈 added another dummy on the right
    
            self.scroll_box = gui.UIBoxLayout(
                width=self.width,
                height=4 * self.height,
                vertical=True,
                children=(
                    self.entry_box_1,
                    self.entry_box_2,
                ),
                size_hint=None # 👈 added to prevent shrinking to child size
            )
    
            self.scroll_area = experimental.UIScrollArea(
                x=0,
                y=0,
                width=self.width,
                height=self.height,
                canvas_size=(self.width, 4 * self.height),
                children=(self.scroll_box,)
            )
            self.manager.add(self.scroll_area)
    
        def on_draw(self):
            self.clear()
            self.manager.draw()
    
        def on_key_press(self, symbol: int, modifiers: int):
            self.manager.debug()
    
    
    win = TestWindow()
    win.run()
  4. eruvanos commented on Feb 15, 2026

    @eruvanos
    Member

    Result:

    Image

    Things that came to my mind to improve the code:

    • Layouts might print a warning when width/height is given, but size_hint not set
    • UIAnchorLayout could also make use of the children to calculate size_hint_min so it is consistent and works better in UIBoxLayouts.
    • Constructor could warn if a UIWidget is given (unsure, there might be use cases to do that, but rare ones)

    What do you thing?

  5. DragonMoffon commented on Mar 15, 2026

    @DragonMoffon
    CollaboratorAuthor

    I def think the first two should be implemented as warnings as they cause very intuitive issues and are almost always going to be unintentional. The last one I could see being useful when you have one child of a layout that you want to fill the layout fully. Maybe the last one should just be a type hint so type-checking complains but it's not actually prohibited?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

guiRelated to arcade GUI (sub module arcade.gui)

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions