Search Unity

Setting maximum width of layout element with content size fitter

Discussion in 'UGUI & TextMesh Pro' started by Bennett-Lynch, Jun 24, 2015.

  1. Bennett-Lynch

    Bennett-Lynch

    Joined:
    Oct 11, 2014
    Posts:
    35
    I am trying to set the maximum width of a layout element that is inside of a content size fitter + vertical layout group system. I want text to begin wrapping at the designated max width, but still shrink the parent transform below the max width if the text is reduced.

    I have seen @runevision offer the following advice in a few places:
    So here is my current system before applying a custom layout element script:



    Then, in theory, if I apply the following layout element to the "Bottom text" it should achieve the desired behavior:


    This does limit the rect that was previously 247 wide to 175 wide:


    But if I reduce the text length below 175, the outer content size fitter does not shrink to accommodate it:


    Can someone please explain what I'm missing? The desired behavior would be to shrink to the top line of text (which is 104 wide), and expand to a maximum width of 175 and begin wrapping if the text is any longer than that. Currently it only expands and does not shrink.
     
    AlexisGervacio, Qray5000 and ercion like this.
  2. schragnasher

    schragnasher

    Joined:
    Oct 7, 2012
    Posts:
    117
    Trying to do the same exact thing, the same exact way, with the same exact results :(
     
  3. schragnasher

    schragnasher

    Joined:
    Oct 7, 2012
    Posts:
    117
    Ok i came across a working setup....randomly Its not perfeect, as i would like my textbox to be in the center of the bubble, but maybe i can work it from this setup somehow. There might be a way to do this better, i just made it worked and then posted this. Its a start.

     
    zephybite0, zIyaGtVm and slumtrimpet like this.
  4. schragnasher

    schragnasher

    Joined:
    Oct 7, 2012
    Posts:
    117
    Yup already got an update, the canvas and raycaster on the top level control are unnecessary.

    EDIT:
    Set Horizontal layout on the BottomSpeechBubble to middle center child alignment, and its exactly as i wanted.
     
  5. Sketchyness

    Sketchyness

    Joined:
    Dec 3, 2013
    Posts:
    2
    Sorry for necroing, but I cannot reproduce this behaviour in 5.3. If I try to create the setup you have above, the TopElement just takes its size from the preferred size (as seen in the pictures below). Layout Properties also claims that Flexible Width and Height is disabled when they are set to 0. If this is intended I do not know, but it seems like weird communication if it is supposed to act as a way of using Preferred Width / Height as max values.







    I do not know if I am misunderstanding anything here, @runevision do you maybe have thoughts on this?
     
  6. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    The Layout system doesn't support telling an element how much width it has available, then asking it how much width it actually needs based on that, and then giving it that width instead. This would require an iterative solver that could require many steps to find a solution, particularly if you have a table where each cell has line wrapping. It only supports asking for minimum, preferred, and flexible widths, and then deciding how much width it gets based on that. The line-wrapping is only calculated after this step, so it can't affect the result.

    This means that ContentSizeFitter and other layout controllers can't fit exactly to accommodate line-wrapping.

    The advise that using preferred-width together with a flexible width of 0 still holds. As you note, the width doesn't exceed the preferred width. The issue you mention is not related to that but is a limitation in the width (and height) allocation system that the layout system uses which has a fixed number of calculation steps rather than an iterative solver.
     
  7. Sketchyness

    Sketchyness

    Joined:
    Dec 3, 2013
    Posts:
    2
    @runevision So you are saying the result @schragnasher got was because he either had enough text to warrant full width (without it looking like it was too wide because of a linebreak), or short enough text that it was never long enough for full width and then a linebreak? I think I understand, and I can understand the reasoning behind the limitation.

    Mine is not the same case though. As I showed above, the text in that example is not wide enough to even fit the minimum width, yet the TopElement still takes the full width of the Preferred Width field.

    You say it asks for minimum, preferred and flexible , but is it then correctly understood that with flexible at 0, only the preferred value is taken into account, because the system will decide then and there to always honor that, effectively making it always fixed, namely the "preferred" value?
     
  8. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    Yes, as long as there is any line that wraps, it will always make use of the full width.

    But in your case, if there is an explicit line break (not line wrapping) before "Bottom line" then it's not related to what I said indeed.

    By TopElement, do you mean his root GameObject, called Canvas?

    In his setup there was 3 nested GameObjects.
    • Canvas
      • BottomSpeechBubble
        • Text
    The top one will have a fixed size that determines the maximum size of the speech bubble. In his setup this size was determined by a LayoutElement with preferred sizes specified plus a ContentSizeFitter which makes it fit the preferred size, but those two components are not needed. The size can just be set in the RectTransform itself. It does need the HorizontalLayoutGroup though and Child Force Expand width and height must be disabled.

    The BottomSpeechBubble is the actual visible container that will resize based on content. It needs a HorizontalLayoutGroup and a LayoutElement that specifies the minimum width and height.

    The Text just needs the Text component.

    So yes, the top most object will not resize - it's size indicates the maximum size of the visible container. The middle object is the one that will resize.
     
  9. valter-home

    valter-home

    Joined:
    Sep 22, 2015
    Posts:
    118
    Thanks, your answer has solved my problem.
     
  10. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    Firstly: runevision, thank you for your forum posts on custom ILayoutGroup etc (they had the extra info I needed to re-implement the Unity interfaces correctly).

    Secondly:

    This isn't entirely true. You don't need an iterative solver to do negotiated sizes, you just need an algorithm that pre-calculates the dependency graph and executes it in order (and issues an error if you try to create a layout with circular dependencies).

    (if you want to see how, I've uploaded my code to asset store with full source - flexbox4unity - it's an HTML5/CSS3 layout implementation of Flexbox, done entirely in Unity's LayoutGroups system)

    I've just written that, and it works great. It removes all the current Unity bugs with horizontal/vertical layout groups, and it gets rid of those fake WARNING messages in the Inspector (the ones saying that e.g. putting a content size fitter inside a layout group is a BAD THING) ... because you can 100% calculate whether there's a conflict or not, in a single sweep of the UI tree.

    If you also switch out the Unity layout algorithm for a more modern one (e.g. I implemented core CSS3 layout) then the unity Layout system works perfectly with negotiated-widths (and ditto for height, and ditto for calculating height-from-width -- which the Unity v2018 docs currently imply is impossible: "calculated heights may depend on widths, but calculated widths can never depend on heights.").

    TL;DR: Unity Layout Groups are a bit sucky, but the underlying system works fine (writing your own ILayoutGroup for instance) and it's relatively easy to fix/replace the layout groups with something that works a lot better. I highly recommend doing it!
     
    Last edited: Apr 1, 2019
  11. d2king10

    d2king10

    Joined:
    Dec 21, 2009
    Posts:
    53
    I know this is a few years bump, but it still comes up as the top post and it drives me nuts that Unity hasn't added this support for the Content Size Fitters yet. This can easily be added by doing the following:

    Create a new script and extend ContentSizeFitter. Add float properties to define the min width/height and max width/height. Override the SetLayoutHorizontal and SetLayoutVertical. Grab the RectTransform size delta in each function then Mathf.Clamp the desired axis based on the min/max size of that axis. Done.

    This makes it work as you would expect. Obviously it still causes issues within Layout Groups but Content Size Fitters have never worked properly in Layout Groups, but at least this way you can easily define the min/max size of text fields and anchor things to them properly without having a bunch of costly Layout Groups trying to mimic the same functionality.
     
    Jamez0r and dharoo like this.
  12. navratillukas

    navratillukas

    Joined:
    Jun 10, 2016
    Posts:
    1
    Thank you very much!
    This simple solution works perfectly. I was going crazy trying to make it behave as I wanted.
     
    Jamez0r likes this.
  13. Jamez0r

    Jamez0r

    Joined:
    Jul 29, 2019
    Posts:
    206
    Thank you for this - also was struggling for a while until I read your post.
     
  14. devnullicus

    devnullicus

    Joined:
    Jan 6, 2021
    Posts:
    5
    Unfortunately, they've set the rectTransform field and property accessor in ContentSizeFitter to private so that you can't get to it to get / set the size delta. Why, Unity?
     
  15. Neohun

    Neohun

    Joined:
    Oct 30, 2018
    Posts:
    77
    Alright, let's put an end to this madness here. Since this is the top thread on google I'm gonna post the code here once and for all for those who struggle with this nonsense. This script basically extends the content size fitter to clamp the sizeDelta of the rectTransform. So instead of "ContentSizeFitter" you'll use this component. Just create a new script and copy paste the following code:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3. using UnityEditor;
    4.  
    5. public class ContentSizeFitterEx : ContentSizeFitter {
    6.  
    7.     // Define the min and max size
    8.     public Vector2 sizeMin = new Vector2(0f, 0f);
    9.     public Vector2 sizeMax = new Vector2(1920f, 1080f);
    10.  
    11.  
    12.     public override void SetLayoutHorizontal() { // Override for width
    13.         base.SetLayoutHorizontal();
    14.         // get the rectTransform
    15.         var rectTransform = transform as RectTransform;
    16.         var sizeDelta = rectTransform.sizeDelta; // get the size delta
    17.         // Clamp the x value based on the min and max size
    18.         sizeDelta.x = Mathf.Clamp(sizeDelta.x, sizeMin.x, sizeMax.x);
    19.         // set the size with current anchors to avoid possible problems.
    20.         rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, sizeDelta.x);
    21.     }
    22.  
    23.  
    24.     public override void SetLayoutVertical() { // Override for height
    25.         base.SetLayoutVertical();
    26.         // get the rectTransform
    27.         var rectTransform = transform as RectTransform;
    28.         var sizeDelta = rectTransform.sizeDelta; // get the size delta
    29.         // Clamp the y value based on the min and max size
    30.         sizeDelta.y = Mathf.Clamp(sizeDelta.y, sizeMin.y, sizeMax.y);
    31.         // set the size with current anchors to avoid possible problems.
    32.         rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, sizeDelta.y);
    33.     }
    34. }
    35.  
    36.  
    37. [CustomEditor(typeof(ContentSizeFitterEx))]
    38. public class ContentSizeFitterExEditor : Editor {
    39.     // override the editor to be able to show the public variables on the inspector.
    40.     public override void OnInspectorGUI() {
    41.         base.OnInspectorGUI();
    42.     }
    43. }

    Edit: The code is updated for a better version that works without messing with anchors to avoid possible problems with different type of anchors.
     
    Last edited: Dec 10, 2023
  16. ysixer6

    ysixer6

    Joined:
    Oct 13, 2020
    Posts:
    1
    Thanks a lot.
    Without anchorMax line, This is what I want.(just version to set max size of ContentSizeFitter)
     
  17. josefyenko1

    josefyenko1

    Joined:
    Oct 22, 2020
    Posts:
    1
  18. andyz

    andyz

    Joined:
    Jan 5, 2010
    Posts:
    2,279
    Thank you random Unity saver, lovely simple 1 component solution - why on earth does the Unity size fitter not have max sizes particularly to wrap text?!