Search Unity

Limit Max Width of Layout Component?

Discussion in 'UGUI & TextMesh Pro' started by Stephan-B, Apr 5, 2015.

  1. andbto

    andbto

    Joined:
    Feb 3, 2017
    Posts:
    1
    This worked nicely for me! Thanks for sharing, @bilalakil
     
    ChristmasGT and bilalakil like this.
  2. L0tan

    L0tan

    Joined:
    Jul 16, 2017
    Posts:
    75
    I know that setting max Widths on a Layout Component are no suppoused to work on every resolution but, in case you want to maintain the "ratio" of a predefined width in a predefined resolution, on the rest of resolutions, and for whatever reasons you can't use anchors (maybe you are restricted by parents or children behaviours), here is my modification on @bilalakil solution:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3.  
    4. [ExecuteAlways]
    5. [RequireComponent(typeof(ContentSizeFitter))]
    6. public class ContentSizeFitterMaxWidth : MonoBehaviour
    7. {
    8.     public float maxWidth;
    9.  
    10.     private float widthRatio;
    11.     private RectTransform _rtfm;
    12.     private ContentSizeFitter _fitter;
    13.     private ILayoutElement _layout;
    14.  
    15.     private void OnValidate() => OnEnable();
    16.  
    17.     private void OnEnable()
    18.     {
    19.         _rtfm = (RectTransform)transform;
    20.         _fitter = GetComponent<ContentSizeFitter>();
    21.         _layout = GetComponent<ILayoutElement>();
    22.  
    23.         widthRatio = maxWidth / Screen.width;
    24.     }
    25.  
    26.     private void Update()
    27.     {
    28.         maxWidth = widthRatio * Screen.width;
    29.  
    30.         _fitter.horizontalFit = _layout.preferredWidth > maxWidth
    31.             ? ContentSizeFitter.FitMode.Unconstrained
    32.             : ContentSizeFitter.FitMode.PreferredSize;
    33.  
    34.         if (_layout.preferredWidth > maxWidth)
    35.         {
    36.             _fitter.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
    37.             _rtfm.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, maxWidth);
    38.         }
    39.         else
    40.             _fitter.horizontalFit = ContentSizeFitter.FitMode.PreferredSize;
    41.     }
    42.  
    43. }
    44.  
     
  3. WavyRancheros

    WavyRancheros

    Joined:
    Feb 5, 2013
    Posts:
    176
    I am sorry to bump this. I need this, BUT instead of wrapping the text to a new line, I want to automatically shorten it with an Ellipsis. Doesn't work.
     
  4. Xtro

    Xtro

    Joined:
    Apr 17, 2013
    Posts:
    609
    I can confirm that this super simple suggestion solved my problem without writing any custom code.

    **EDIT**

    I have spoken too early. It didn't work :(
     
    Last edited: Jun 29, 2022
  5. jpwhite-personal

    jpwhite-personal

    Joined:
    May 9, 2022
    Posts:
    1
    This worked for me. Thanks @bilalakil !
     
    dkydev and bilalakil like this.
  6. Xtro

    Xtro

    Joined:
    Apr 17, 2013
    Posts:
    609
    Short story: This solution worked for me. I used @Ludiq's version because it supports both horizontal and vertical maximum limits.

    Long story:
    I came back to this problem (and this forum thread) again and again over the years while working on different projects. My goal was to find the perfect solution without writing a custom script and by simply trying to understand how Unity layout system works. Today, I think, I fully figured it out and noticed that it's not possible without writing a custom ContentSizeFitter. So, I gave up on my goal and started using @Ludiq's version.

    Thank you very much.
     
    MUGIK and MarcSpraragen like this.
  7. ChristmasGT

    ChristmasGT

    Joined:
    Sep 21, 2011
    Posts:
    11
    Huge help, thank you so much!
     
    bilalakil likes this.
  8. mayberohit

    mayberohit

    Joined:
    Oct 2, 2018
    Posts:
    1
    This worked for me :
    If parent contains Horizontal or Vertical Layout Group, Enable 'Control Child Size' and Disable 'Child Force Expand', then add 'Layout Element' to child and set Preferred Height/Width.
     
    GainfulSage likes this.
  9. losingisfun

    losingisfun

    Joined:
    May 26, 2016
    Posts:
    36
    Thank you, you are a saint.

    For anyone using this on a newer version of unity, the Control Child Size need to be checked for Width and Height for both vertical layout groups.
     
  10. eky

    eky

    Joined:
    Sep 28, 2017
    Posts:
    6
    I found something working for me, I try to explain (it's really easy to reproduct):

    Make one gameObject :
    *stretch : (free = blue cross) don't know if it matter, anyway;
    *Anchors : Min X & Y = 0; Max X &Y = 1;
    *Pivot : X = 0; Y = 0,5;
    *TextMeshPro with Alignement Left;
    *Content Size Fitter : Horizontal = Preferred Size; Vertical = Unconstrained;
    *Horizontal Layout Group : Child Align = Upper Left; Control Child Size = Width; (Left = 13; Spacing = 4 but not really important)

    Then, make 3 gameObject child one the first:
    For All :
    *stretch : top left
    *Anchors : Min X = 0 & Y = 1; Max X = 0 & Y = 1;
    *Pivot : X = 0,5; Y = 0,5;
    *TextMeshPro with >For 1rst Go : (for the example write : 50) & Alignement Right;
    For 2nd Go : (for the example write : /) & Alignement Right;
    For 3rd Go : (for the example write : 50) & Alignement Left;

    That's it. Now just change the value on the textMeshpro of the 1rst Gameobject(of child), just add some 0000 and see the natural extension that you want.
     
  11. sinedsem

    sinedsem

    Joined:
    May 7, 2020
    Posts:
    9
    Can you please share an example of your project? I did like you say and it doesn't work for me.
     
  12. AdamandEveStudios

    AdamandEveStudios

    Joined:
    Apr 19, 2022
    Posts:
    20
    Why is it so hard to do something so simple. This is an incredible waste of energy and time. Have tried everything in this thread and nothing has worked
     
    j1mmie likes this.
  13. GainfulSage

    GainfulSage

    Joined:
    Apr 30, 2015
    Posts:
    106
    So, here is how I got this working without writing code. It's a bit clunky in that it requires using a couple of "empty" GameObjects but otherwise it's fine.

    Let's say you want to center something horizontally. It should have a maximum/preferred width of 200, and a minimum width of 50.

    Create a single parent object and add the HorizontalLayout component. Set it to control the children but not force expand. Then add three new GameObjects as children to the HorizontalLayout GameObject, which I'll call, Left, Center, and Right. Add Layout Element to all three. Set Left and Right to Flexible. Set the Minimum and Preferred values on Center to 50 and 200. Set the UI stuff in Center to flex. Voila - it's now centered, and Preferred is the maximum value and Minimum works as a minimum.

    The good news is that this works. You can wrap one set of three objects in a VerticalLayout and then another set of three objects in a HorizontalLayout and you'll get vertical and horizontal centering with a minimum and maximum.

    There is a certain question of aesthetics here - at least superficially, it's kind of ugly to have the left and right (or top and bottom) GameObjects. But it kind of makes sense, and once you start playing around with Layout Element and the Horizontal/Vertical layouts and get used to them they seem to work fine.

    The only gotcha is that it appears to not update in the editor on window resize sometimes. I notice this more on complex layouts There are two workarounds - if it's something simple like the Left/Center/Right I find that just toggling the sort order in the Vertical/Horizontal Layout will fix this (it reverses which one is left/right but if it's just centering that doesn't matter). Unfortunately it appears there is no notification event from Unity for screen resize, but you can do something like store the screen size Vector2 and then fire a change event when you notice the change. For my project right now I'm doing that but only in the editor (my target is iOS and resize events aren't a thing).

    If anyone finds this helpful or would like more details (e.g. posting a video and/or gist to the script I'm using for resize events) LMK.
     
    Emanx140 likes this.
  14. bilalakil

    bilalakil

    Joined:
    Jan 28, 2018
    Posts:
    77
    That's interesting @GainfulSage ! I'm curious why setting the preferred value to 200 acts as the MAX, instead of... the actual preferred value that it will set itself to (e.g. I would've expected it'd essentially grow itself to take the 200 even if its contents only needed 120).
     
    Ziplock9000 likes this.
  15. GainfulSage

    GainfulSage

    Joined:
    Apr 30, 2015
    Posts:
    106
    It's very confusing, is the short answer lol.

    Basically from what I can tell the Content Size Fitter has to be used with either Vertical/Horizontal Layouts.

    Let's say you have a Panel X and a child TextMeshPro Text Y.

    Code (CSharp):
    1. Panel X
    2.      (component) Vertical Layout Group
    3.      (component) Content Size Fitter
    4.      (child) TextMeshPro Text Y
    5.         (component) Layout Element
    With this configuration you can do things like turn the Layout Element prefs for min/prefs.

    The key thing is that you put a Content Size Fitter on a Vertical/Horizontal Layout Group, and you use Layout Element rather quite a bit for the child to tell the Vertical/Horizontal Layout Group what to do. The other thing is that for Layout Elements and Content Size Fitter to work properly you want to set the V/H Layout Group to Control Child Size on and Child Force Expand to off, which IIRC is the opposite of how they are configured when you add V/H LG by default.

    The really confusing parts IMHO are a) that Unity doesn't really seem to explain or enforce this. So folks can just drop a Content Size Fitter on a game object and it doesn't do anything useful. Even worse, sometimes it appears that the layouts don't update automatically on resize by default in the editor. Sometimes it works! And sometimes you have to force it to update.

    I wound up adding this script to my project as an Editor only tool:

    Code (CSharp):
    1.  
    2. using UnityEditor;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5.  
    6. namespace AppEditor.io.dayboard
    7. {
    8.     public class ForceLayoutUpdate : Editor
    9.     {
    10.         [MenuItem("Tools/Force UI Layout")]
    11.         private static void ForceLayout()
    12.         {
    13.             // Find all active canvas objects in the scene
    14.             Canvas[] canvases = FindObjectsOfType<Canvas>(true);
    15.  
    16.             foreach (Canvas canvas in canvases)
    17.             {
    18.                 // Get all the layout groups in the canvas hierarchy
    19.                 LayoutGroup[] layoutGroups = canvas.GetComponentsInChildren<LayoutGroup>(true);
    20.  
    21.                 foreach (LayoutGroup layoutGroup in layoutGroups)
    22.                 {
    23.                     // Force the layout to update
    24.                     LayoutRebuilder.ForceRebuildLayoutImmediate(layoutGroup.transform as RectTransform);
    25.                 }
    26.             }
    27.         }
    28.     }
    29. }
    30.  
    This adds a menu item to force the layout to refresh. It only runs in the Editor, and only on demand. You could probably convert this to a GameObject that runs in the editor and skip the manual refresh part.
     
  16. GainfulSage

    GainfulSage

    Joined:
    Apr 30, 2015
    Posts:
    106
    Oh, and FWIW, Preferred basically is Max in practice. When things are set up right the calculated value will range between Minimum and Preferred, where the calculated value will be as close to Preferred without going over.
     
  17. TheCaveOfWonders

    TheCaveOfWonders

    Joined:
    Mar 2, 2014
    Posts:
    27
    wow how is this still no there in 2023? come on unity.
     
  18. zevonbiebelbrott

    zevonbiebelbrott

    Joined:
    Feb 14, 2021
    Posts:
    122
    Except when it doesnt.
    NoItDoesnt.png
     
  19. Neil-Corre

    Neil-Corre

    Joined:
    Nov 29, 2015
    Posts:
    44
    It's 2024 and for what it's worth, this simple setup works for me without additional code.
     
  20. andyz

    andyz

    Joined:
    Jan 5, 2010
    Posts:
    2,276
    2024 and no max sizes in Unity's Layout Element! :eek:
     
  21. andyz

    andyz

    Joined:
    Jan 5, 2010
    Posts:
    2,276
    This works but with one big issue. If you have long words and say 2 lines of text you will end up with big space on one side or both sides of the text, depending on alignment. It needs to fit text to max width and then downsize after to the width the text Actually Now Fills. e.g. red arrows show wasted space below. But I guess this would depend on the text element being smart?!

    contentFit.png
     
    Last edited: Feb 14, 2024