Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HorizontalLayout restricting window size #5391

Closed
JamieH01 opened this issue Jun 11, 2024 · 19 comments
Closed

HorizontalLayout restricting window size #5391

JamieH01 opened this issue Jun 11, 2024 · 19 comments
Labels
a:layouts Related to the layouting and positioning of the elements (mO,bT)

Comments

@JamieH01
Copy link

When using HorizontalLayout inside a window, the preferred-width of the window is ignored and is set to the width of the layout, being unable to be resized. If the layout has width: root.width, the window will be wider, but still smaller than it should be and unable to be resized. min-width has no effect.

export component AppWindow inherits Window {
    title: "My App";
    background: @linear-gradient(45deg, #000000 0%, #222222 30%, #222222 70%, #000000 100%);
    preferred-width: 1280px;
    preferred-height: 720px;

    
    HorizontalLayout {
         Rectangle {
            width: 50px;
            height: 50px;
            background: blue;
        }
    }
}

im currently using linux with x11, but this also happened on windows.

@Slightlyclueless
Copy link

Can confirm the windows issue.

@JamieH01
Copy link
Author

forgot to mention that the exact same issue happens with GridLayout.

@ogoffart
Copy link
Member

Thanks for filling a bug.

This is the expected behavior. Layout have maximum and minimum size and are constrained by their contents by default. So if the inside of the layout is fixed, this fixes the size of the all window.

By changing the alignment property on the layout you can have different result.

What is your expectation with this code?

@JamieH01
Copy link
Author

i at least can understand overwriting preferred width, but i expect min-width to set the minimum width of the window.

@Slightlyclueless
Copy link

Hi, I don't see how this would be expected behaviour. I would not expect setting the dimensions of a child object to forcibly turn off resizing of the parent object, I would just expect it make the rectangle 50px by 50px

@JamieH01
Copy link
Author

JamieH01 commented Jun 12, 2024

update: the next day some of the behavior has changed.
min width on the window will make it start at that width, but the window can still be resized to be smaller. this does not happen with min height. the contents of the horizontal layout was changed to a for loop instead of hard coded, but changing it back to a hard coded rectangle doesnt change anything.

@JamieH01
Copy link
Author

after more testing, ive narrowed it down to this behavior:
the main window has these properties

    min-width: 1280px;
    preferred-height: 720px;
    min-height: 200px;

and a vertical layout.
the effect this has: the window height starts hat 720px and cannot be made smaller than 200px. this is expected. The window width starts at 1280px, but it can also be resized down to 200px. if min-width is omitted, the width is locked at 200px.
the width variables seem completely broken, and this cannot be intended behavior.

@ogoffart
Copy link
Member

The problem is that the layout gives a maximum size to the window, and that maximum size is smaller than the minimum. This is the same as writting

export component Test inherits Window {
    min-width: 1000px;
    max-width: 300px;
}

The current behavior with the winit backend is that you can resize the window between 1000 and 300. Admittedly, it's not great.
But what would you expect from such constraints? Force the minimum size?

@Slightlyclueless

I would not expect setting the dimensions of a child object to forcibly turn off resizing of the parent object, I would just expect it make the rectangle 50px by 50px

The layout propagates constrains from the child to the parents. And then it propagates sizes from the parents to the child.
So if there is only one rectangle that is 50x50, that's the constraint on the window and the window can't change size.

@Justyna-JustCode
Copy link
Contributor

I also noticed that the window size is violated when using an empty layout.

export component MainWindow inherits Window {
    preferred-width: 400px;
    preferred-height: 200px;
    
    VerticalLayout {
        Text { text: "Text"; } // suprisingly working fine without this line
        HorizontalLayout {} // <- this line causing a problem
    }
}

Maybe it's not a big deal in the example code, but, for example, it might be in a custom component where children are not mandatory:

component LabeledElement inherits Rectangle {
    in property <string> label;
    VerticalLayout {
        Text { text: root.label; }

        HorizontalLayout {
            @children
        }
    }
}

@JamieH01
Copy link
Author

JamieH01 commented Jun 18, 2024

In my opinion, it is completely unintuitive and ridiculous for the size constraint of a child to effect the window. The child object is a child. it is meant to sit inside its parent. the expected behavior is for the height and width of the child to affect the child, and for the height and width of the parent to effect the parent. If this is intended behavior, then i believe that this framework is incredibly poorly designed and does not attempt to communicate how its constraints work at all.
even then, this still ignores this issue from my original comment:

If the layout has width: root.width, the window will be wider, but still smaller than it should be and unable to be resized.

attempts to work around this restriction has still lead to issues.
However, it is still so hard for me to believe that this is intended behavior, because this does not happen with vertical height. this only happens with the horizontal width.

@ogoffart
Copy link
Member

The documentation might needs clarification, but the constraints propagate from the child to the parent. We try to document it in https://releases.slint.dev/1.6.0/docs/slint/src/language/concepts/layouting and we discussed it in https://slint.dev/blog/changes-to-the-slint-language-part-2

If you don't want this behavior, why use a layout at all? I believe then this does what you want (removed the layout)

export component AppWindow inherits Window {
    title: "My App";
    background: @linear-gradient(45deg, #000000 0%, #222222 30%, #222222 70%, #000000 100%);
    preferred-width: 1280px;
    preferred-height: 720px;

    Rectangle {
       width: 50px;
       height: 50px;
       background: blue;
    }
}

How could we improve the docs?

@ogoffart ogoffart added the a:layouts Related to the layouting and positioning of the elements (mO,bT) label Jun 19, 2024
@JamieH01
Copy link
Author

JamieH01 commented Jun 19, 2024

By default, they stretch or shrink to take the whole space.

this is literally the exact opposite of both what is happening, and what youre insisting is intended behavior.

@JamieH01
Copy link
Author

No matter what angle you look at this from, something doesnt work. If the child is meant to propagate properties to the parent (ignoring that this is the opposite of how a parent-child relationship works) then why does this only affect this one specific example and only with the width of the window?

@ogoffart
Copy link
Member

The layout has two roles:

  1. compute the constraints of things inside it and propagate that to the parent.
  2. based on the the size of the parent, resize and place the children within it.

These are two different passes. The first pass propagate the constraints from children to parent. The second pass propagates the size down.

In this case, by fixing the width and height of the rectangle, you are saying the Rectangle can only have this one size and cannot be resized. The constraints are passed to the Window which cannot be resized and will have that size.

I'm closing this issue because this is not actionable.

By default, they stretch or shrink to take the whole space.

This is correct, but in that case, the whole space is always 50x50 though, since it is restricted by the constraints you set.

why does this only affect this one specific example and only with the width of the window?

It affect every example where there is a layout in a window.
It has an issue if the minimum is bigger than the maximum though. But I'm not sure anything can be fixed in that regards.

@laundmo
Copy link

laundmo commented Jul 2, 2024

I'm closing this issue because this is not actionable.

To me, it seems like the only way this issue could be "not actionable" is if Slint works in the most unclear roundabout and badly named way.

A Rectangle with a set size inside a window should only have one effect on the window: It can't be smaller than the rectangle.

The issue is that it disables the entire windows resizing.

So if the current behaviour is intended, i want to ask this:

How do I make a window, which contains a rectangle of 50x50px size, with the window starting at 1280x720px and being resizeable down?
It would limit the window's size to be at smallest 50x50 of course, to fulfil the constraint given by the Rectangle, but I should be able to increase the size.

@ogoffart
Copy link
Member

ogoffart commented Jul 3, 2024

Several ways:

  • no layouts:
component App inherits Window {
    preferred-width: 1280px;
    preferred-height: 720px;
    min-width: 50px;
    min-height: 50px;
    Rectangle { background: red; width: 50px; height: 50px; } 
}
  • two layouts (horizontal and vertical) with alignment set.
component App inherits Window {
    preferred-width: 1280px;
    preferred-height: 720px;
    HorizontalLayout {
        alignment: center;
        VerticalLayout {
            alignment: center;
            Rectangle { background: red; width: 50px; height: 50px; } 
        }
    }
}

@nemicha
Copy link

nemicha commented Jul 18, 2024

I have a similar problem and also don't think that this is intended behavior. In my example I have a menu bar on the left side and content on the right side hence using a HorizontalLayout. The withdraw and height of the content can vary and I still expect the window to keep it's current size, but it doesn't.

The expectation is that the layout only manages the layout of it's children and not the one of the main window. No app in Windows, Linux, MacOS behaves like this. If the User resizes the window it should stay exactly at this size. This can be even seen when the window is maximized, it won't be maximized anymore (actually this happened on Ubuntu 20.04. On Linux Mint Cinnamon it works when maximized). How is this to be expected in any OS?

import { Button } from "std-widgets.slint";

component Dashboard {
    VerticalLayout {
        Rectangle {
            background: red;
            Text {
                text: "Dashboard";
            }
        }
    }
}

export component MainPage inherits Window {
    preferred-width: 320px;
    preferred-height: 240px;
    background: green;

    out property <string> menu: 0;
    HorizontalLayout {
        Rectangle {
            background: blue;
            width: 140px;

            VerticalLayout {
                Button {
                    height: 40px;
                    text: "dashboard";
                    clicked => {menu = "dashboard"}
                }

                Button {
                    height: 40px;
                    text: "nothing";
                    clicked => {menu =""}
                }
            }
        }

        if (menu == "dashboard") : Dashboard{}
    }
}

Edit: Some additionals:
When the preferred width and height are changed to just width and height it works as expected, but then the window is not resizable. I would like to have a resizable window.

@ogoffart
Copy link
Member

@nemicha Not sure what you expect, but you can add alignment: start; to the layouts.

@nemicha
Copy link

nemicha commented Jul 19, 2024

@ogoffart Thanks, adding alignment: start; stops automatic resizing to a smaller size. But when the content is bigger, the window is still resized to fit the content. I would expect no resize at all. The application window should never be resized by it's content even if the content is not shown completely. For me this feels off and I'm not used to this from other UIs like .NET for example. Is there any overflow: hidden; setting?

In the documentation for LayoutAlignment it's stated like this:

stretch: Use the minimum size of all elements in a layout, distribute remaining space based on *-stretch among all elements.

Doesn't say that it will resize the element to make it bigger and thus resizing the parent.

In VerticalLayout and HorizontalLayout it states:

These layouts place their children next to each other vertically or horizontally. The size of elements can either be fixed with the width or height property, or if they aren’t set they will be computed by the layout respecting the minimum and maximum sizes and the stretch factor.

This doesn't mention that the parent element will be resized depending on the content of the layout. In my opinion parent always tells the children what to do, not the opposite. Otherwise there should be an overflow: hidden; or similar property.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a:layouts Related to the layouting and positioning of the elements (mO,bT)
Projects
None yet
Development

No branches or pull requests

6 participants