Add custom_maximum_size property to Control#116640
Add custom_maximum_size property to Control#116640StarryWorm wants to merge 3 commits intogodotengine:masterfrom
custom_maximum_size property to Control#116640Conversation
KoBeWi
left a comment
There was a problem hiding this comment.
This looks like a more general way to implement godotengine/godot-proposals#13568 and similar. It will require follow-ups that adjusts behavior of other nodes (similar to what is already done in ScrollContainer), but it's a nice base I guess.
Seems like you need to do some cleanup (#116640 (comment)) before the implementation can be reviewed properly.
|
I tested again after the changes to |
|
That should solve it. It was a stupid bug because I used |
|
I don't believe there should be any case where So that you can test it too, going forward, here's the steps to recreate the scenario mentioned in all the proposals for this feature (I'd make an MRP but since the internal code is still a WIP, it's more reliable this way):
As you type notice that it ignores the |
As stated in the PR description, some Regarding your example, it actually works as intended. If you look at your So this isn't a bug, it's a UX problem.
The propagate flag will still only be for immediate children, but with If you want to see it for yourself, just create a |
|
I do see now that it does work but only for immediate children, which you're right, is just not a good workflow/UX yet. It absolutely should have all descendants respect the value, and that should be the default. We wouldn't want it to break compatibility (or this PR would probably have to wait until we have a consensus on that), so whatever the defaults would be would need to show no change to existing projects. Considering max size just doesn't exist atm, no value should be the same behavior as without this PR. I think, because this is a wide-reaching change that will require a lot of testing, it may be good to include a test project so we can see how it behaves with different workflows, especially the ones outlined in the proposals (so they can be closed). The two main ones are the one I had above about multiple I haven't tested the |
|
Changed the default value of This is overridden to
@markdibarry please try your scenario again and let me know if it fails. Works as "would be expected" on my machine.
Yep, which now should work fully as expected.
This is already handled with the new |
|
Regarding tests, I will add unit tests to the engine for the scenarios to help guard against regressions in the future, and will provide a test project so people can see for themselves how it works. |
|
(chef's kiss) Wunderbar. Tested on those two scenarios and both are working as expected! Wasn't trying to nitpick, just wanted to make sure it covered both of those two proposals completely. I just know first hand if it doesn't "just work" for 90% of cases out of the box, there's gonna tons of confusion from users and an avalanche of issues created around it. This seems like, at least while we trial run:
Now that we can see the main cases covered, we can dig in further for regressions and refine the workflow. Great work! |
|
Oh, don't worry, I am absolutely delighted you pushed the PR to its limits. Nitpicking is why the PR process exists. Ideally, given how this is set up, there is 0 chance of regression. All the tests also run fine on my machine, which should cover any potential regressions due to this change. As said, though, I will add as many tests as I can think of, both to prevent regressions caused by this PR and future regressions against this PR. |
|
Added unit tests for everything - in the appropriate commit for all of them. |
KoBeWi
left a comment
There was a problem hiding this comment.
Looks fine overall. The modified controls (ScrollContainer, (RichText)Label) seem to behave as expected with maximum size, if you fulfill the requirements. I also tried some custom behavior using maximum size and it works too. The implementation seems to mostly mirror minimum size.
Definitely needs more reviews and discussion, as it's a rather major change, but it looks promising from what I tested.
For Label, this is relevant when autowrap_mode != AUTOWRAP_OFF or when text_overrun_behavior != OVERRUN_NO_TRIMMING
For RichTextLabel, this is relevant when fit_content = true and autowrap_mode != AUTOWRAP_OFF
This needs to be documented somewhere. Maybe description of each Control could have a paragraph about how it handles maximum size (only for controls that do handle it somehow).
|
Regarding documentation, I have added it to the properties for which it is relevant. I don't think adding a paragraph at the top of the documentation for the classes ( Also, since we are getting closer to a PR ready for merging, I want to make sure the commits are okay as is. I have it split into three because they all do something "separate": adding a |
AdriaandeJongh
left a comment
There was a problem hiding this comment.
Really look forward to this feature and took a bit of time to review today but will review more later:
propagate_maximum_sizeis a bit of a tricky boolean and I'm not sure we should introduce it with this PR. It propagates the max size to individual nodes which is a bit weird in the context of BoxContainers:
… and it also propagates to grandchildren and beyond, even if those children are not in a container:
I would argue that the custom maximum size should primarily be set on the thing that has the potential to grow, in the examples above: the Label and not the PanelContainer. And with that approach we'd never need any propagation and thus removing one extra property and another layer of complexity – as well as remove something that may confuse new users (eg. "why aren't my labels growing?! ohh it's because 6 parents up something propagated all the way down here").
-
I'd reorder first four properties inside the Layout section of Control to: custom min size, custom max size, (propagate max size here, if there's consensus on keeping it), and then clip contents. (and then layout direction, mode, etc)
-
Other than the above, I played around with Label and RichTextLabel and loved it! Precisely what I've been needing for the last two years.
Will play with this a bit more later.
How is it weird? The contents of a
I don't really see a real-world use case for this. I could be missing some, but as far as I understand it, Now, what could be done is to make Not having it propagate for Edit: I strongly believe the user should still be able to toggle propagation on or off for more advanced layouts. This also goes back to Yuri's proposal of merging Side note: This reminds me that I need to specialize |
|
@AdriaandeJongh To piggy-back off what StarryWorm was saying, the behavior you're suggesting sounds more confusing to users tbh. I agree that ideally we could avoid the bool if possible, but the intuitive behavior IMO is children of a container with
What you suggest means more consistency at the cost of the common scenario and uncommon scenario both being complex to use. I guess we'd have to determine which we're more comfortable with. The
@StarryWorm Yeah, that makes sense to me. |
|
Changelog:
List of affected `Container` subtypes and details
|
|
That's odd... The Windows editor artifact (which has the same compilation flags that could impact this) doesn't have that issue. The implementation should be completely platform independent too... :/ Clearly a |
I've actually tested this and I have the same issue on the Windows editor artifact |
|
Wait what? Now I'm very confused... I did:
|
This is precisely what I did. |
These exact same steps, I tested and it happens even when I create a new project. I'm confident that it broke after the last changes that you made 2 days ago. |
|
I mean, I have a rough idea of what could have caused the issue. |
|
Problem identified: the Modern theme is what breaks it. My editor runs on the Classic theme (not a fan of the Modern), and it works with that one. |
This property mirrors `custom_minimum_size` and enables the user to set a size the `Control`. Enabling `propagate_maximum_size` forces all children to respect this node's maximum_size. Not all `Control` types handle this gracefully, which may result in content clipping. Additionally, changes `Label` configuration warning as there is now a proper way of doing autowrapping.
This change enables `ScrollContainer` to make full use of the newly added `custom_maximum_size`. This mode behaves like `SCROLL_MODE_AUTO` but prioritizes expanding the size of the `ScrollContainer` up to its `custom_maximum_size` before displaying scroll bars.
These will be able to use `custom_maximum_size` for autowrapping and trimming, reflecting expected behavior.
|
Fixed, also included the language changes from @markdibarry and @AThousandShips |
| The node's position, relative to its containing node. It corresponds to the rectangle's top-left corner. The property is not affected by [member pivot_offset]. | ||
| </member> | ||
| <member name="propagate_maximum_size" type="bool" setter="set_propagate_maximum_size" getter="is_propagating_maximum_size" default="false"> | ||
| Enables whether [Control] based children should have their maximum size limited by this node. |
There was a problem hiding this comment.
| Enables whether [Control] based children should have their maximum size limited by this node. | |
| If [code]true[/code], [member custom_maximum_size] propagates to all child [Control] nodes until a child node sets their own [member custom_maximum_size] or until that child node disables [member propagate_maximum_size]. |
| <members> | ||
| <member name="autowrap_mode" type="int" setter="set_autowrap_mode" getter="get_autowrap_mode" enum="TextServer.AutowrapMode" default="0"> | ||
| If set to something other than [constant TextServer.AUTOWRAP_OFF], the text gets wrapped inside the node's bounding rectangle. If you resize the node, it will change its height automatically to show all the text. | ||
| [b]Note:[/b] It is recommended to define an autowrapping width via [member Control.custom_maximum_size] when using something other than [constant TextServer.AUTOWRAP_OFF]. |
There was a problem hiding this comment.
| [b]Note:[/b] It is recommended to define an autowrapping width via [member Control.custom_maximum_size] when using something other than [constant TextServer.AUTOWRAP_OFF]. | |
| [b]Note:[/b] Labels with autowrapping enabled must have a custom maximum width configured to work correctly, either through the Label's own [member Control.custom_maximum_size] or as a result of a propagated maximum size from a parent Control with [Control.propagate_maximum_size] enabled. |
| <members> | ||
| <member name="autowrap_mode" type="int" setter="set_autowrap_mode" getter="get_autowrap_mode" enum="TextServer.AutowrapMode" default="3"> | ||
| If set to something other than [constant TextServer.AUTOWRAP_OFF], the text gets wrapped inside the node's bounding rectangle. | ||
| [b]Note:[/b] It is recommended to define an autowrapping width via [member Control.custom_maximum_size] when using something other than [constant TextServer.AUTOWRAP_OFF] with [member fit_content] set to [code]true[/code]. |
There was a problem hiding this comment.
| [b]Note:[/b] It is recommended to define an autowrapping width via [member Control.custom_maximum_size] when using something other than [constant TextServer.AUTOWRAP_OFF] with [member fit_content] set to [code]true[/code]. | |
| [b]Note:[/b] RichTextLabels with autowrapping and [member fit_content] enabled must have a custom maximum width configured to work correctly, either through the RichTextLabel's own [member Control.custom_maximum_size] or as a result of a propagated maximum size from a parent Control with [Control.propagate_maximum_size] enabled. |
| </member> | ||
| <member name="fit_content" type="bool" setter="set_fit_content" getter="is_fit_content_enabled" default="false"> | ||
| If [code]true[/code], the label's minimum size will be automatically updated to fit its content, matching the behavior of [Label]. | ||
| [b]Note:[/b] It is recommended to define an autowrapping width via [member Control.custom_maximum_size] when set to [code]true[/code] with [member autowrap_mode] set to something other than [constant TextServer.AUTOWRAP_OFF]. |
There was a problem hiding this comment.
| [b]Note:[/b] It is recommended to define an autowrapping width via [member Control.custom_maximum_size] when set to [code]true[/code] with [member autowrap_mode] set to something other than [constant TextServer.AUTOWRAP_OFF]. | |
| [b]Note:[/b] RichTextLabels with autowrapping and [member fit_content] enabled must have a custom maximum width configured to work correctly, either through the RichTextLabel's own [member Control.custom_maximum_size] or as a result of a propagated maximum size from a parent Control with [Control.propagate_maximum_size] enabled. |
AdriaandeJongh
left a comment
There was a problem hiding this comment.
I haven't looked at the code, but the functionality and usability look good overall. The additional properties make the Layout section of Controls a bit of a mess, but another PR can attempt to reorganize and restructure all that.
There's some iffy behavior about the container sizing (size_flags_horizontal and size_flags_vertical) where expand (obviously) can't expand beyond the custom maximum size, and then the position of the Control inside the container is essentially 'undefined' – and I bet different depending on the Container attempting to position it. I guess that's somewhat expected behavior, but it makes the whole layout system a bit less 'clean'. 🤷🏼


custom_maximum_sizetoControlgodot-proposals#13534autowrap_maximum_widthtoLabelandRichTextLabelgodot-proposals#13568 , Implement a new property namedScrollContainer.max_size#94171, AddSizeContainer#112364Adds a
custom_maximum_sizeproperty toControl. This property will limit the size of theControl.This property is
Size2, and only applies to dimensions for which it is greater than 0.This property is directly exposed in the Inspector and can be modified freely. The
custom_maximum_sizeproperty overridescustom_minimum_sizeif it is smaller.custom_minimum_size = (100, 100)withcustom_maximum_size = (200, 50)will result insize = (100, 50)(expandable to(200,50)if needed by its contents)Furthermore, a new
propagate_maximum_sizeboolean property is added toControl, which forces all children to respect thecustom_maximum_sizedefined on thatControl.false.trueforContainer.falseforScrollContainer.Certain
Containersubtypes may limit the size of their children even further than the maximum size in order to bring the behavior in line with existing minimum size behavior.MarginContainerwith a child withsize = (50,50)and margins of 5px on every side will havesize = (60,60)currently. In this PR, if aMarginContainerhascustom_maximum_size = (60, 60)with 5px margins, the children will havemaximum_size = (50,50)(whenpropagate_maximum_size = true)List of affected `Container` subtypes and details
FoldableContainer: now takes into account thepaneltheme property.MarginContainer: now takes into accountmargin_*properties.PanelContainer: now takes into account thepaneltheme property.ScrollContainer: now takes into account thepaneltheme property and works withSCROLL_MODE_RESERVE.TabContainer: now takes into account thepanel, thetabbar_background, and theTabBaritself.The second commit enables
ScrollContainerto make use of this newcustom_maximum_sizeby adding a newSCROLL_MODE_MAXIMIZE_FIRST. This new mode makes theScrollContainerincrease itssizeto fit the contents up to itscustom_maximum_sizefirst, and if the contents are larger thancustom_maximum_size, it behaves likeSCROLL_MODE_AUTO. If nocustom_maximum_sizeis defined for that axis, it will behave likeSCROLL_MODE_DISABLED.The third commit enables
LabelandRichTextLabelto work with the newcustom_maximum_size. This new behavior will override the old behavior ofcustom_minimum_sizeif both are defined. Old behavior (when onlycustom_minimum_sizeis defined) is preserved.Label, this is relevant whenautowrap_mode != AUTOWRAP_OFFor whentext_overrun_behavior != OVERRUN_NO_TRIMMINGRichTextLabel, this is relevant whenfit_content = trueandautowrap_mode != AUTOWRAP_OFFWarning
Merge notes: This PR and #112741 are interlinked. The code section in question is the addition of
custom_maximum_sizeto size computation inControl::_size_changed(this PR), which becomesControl::_compute_position_and_size_with_grow(that PR)AI disclaimer: GPT Codex 5.3 was used to help me figure out some of the
ScrollContainer,Label, andRichTextLabelimplementations. All AI-generated content was thoroughly reviewed by me and tested piece-wise. Most of the implementation is human-made.