Search Unity

VisualElement layout/content rects contain NaN values initially & transform.scale breaks children

Discussion in 'UI Toolkit' started by Pelican_7, May 13, 2019.

  1. Pelican_7

    Pelican_7

    Joined:
    Nov 25, 2014
    Posts:
    190
    Hi,

    I have a system built with UIElements in which I instantiate a VisualElement tree (from Uxml/Uss) in response to a button press and place it within my window hierarchy. I am then animating this VisualElement on-screen using the Scheduler API. However, I'm come across two difficullties, which I'm not sure how to resolve.

    1. When a VisualElement is initialized, its layout/content rects contain NaN values. However, I need the element's rect immediately so I can calculate its size and perform my animation correctly (sliding off-screen to on-screen etc.). How can I force the VisualElement to calculate its rect after initialization? I have tried using the MarkDirtyRepaint method but as the documentation states, this waits until the next frame so the values are still NaN. In case it's relevant, the layout of the element is determined by a simple style sheet, like so:
    Code (CSharp):
    1. .fit-to-parent
    2. {
    3.     position: absolute;
    4.     left: 0;
    5.     top: 0;
    6.     right: 0;
    7.     bottom: 0;
    8. }
    2. I can animate a VisualElement's transform.position and transform.rotation with no problems, however when I animate its transform.scale property, all of the element's children appear to break. For example, a child label doesn't render anymore and a child button's hit state becomes the entire window. Is there something else I need to do when altering a VisualElement's transform.scale property (to make it recalculate its rect perhaps)?

    Any help would be much appreciated. Thanks.
    -andy.
     
  2. patrickf

    patrickf

    Unity Technologies

    Joined:
    Oct 24, 2016
    Posts:
    57
    Hi,
    There is no way to force the layout to be computed immediately. To be notified of the new dimensions of your element after the layout computations, you can register a callback to the GeometryChangedEvent:

    element.RegisterCallback<GeometryChangedEvent>(myCallback);


    Regarding animating the VisualElement.transform, it is not something that is very well supported at the moment. If you could file a bug, it would help us track and fix the particular problems you are encountering.
     
    antonsem likes this.
  3. Pelican_7

    Pelican_7

    Joined:
    Nov 25, 2014
    Posts:
    190
    Thanks a lot @patrickf. I'll file a bug on the scaling issue.

    Regarding the force layout, could it be something the UIElements team might consider exposing in a future version? For example, the current UI system has the method LayoutRebuilder.ForceRebuildLayoutImmediate for "special layout calculation needs". Additionally, take a platform like iOS - the UIView class has the setNeedsLayout()/layoutIfNeeded() methods for this purpose. I appreciate the GeometryChangedEvent/MarkDirtyRepaint will be sufficient for many use-cases but there are instances where a user may need to make a layout happen immediately, such as in the frame of instantiating a child element to make a calculation about its rect.
     
  4. patrickf

    patrickf

    Unity Technologies

    Joined:
    Oct 24, 2016
    Posts:
    57
    Thank you for your feedback @Pelican_7. I'll add your suggestion to our backlog.
     
    Pelican_7 likes this.
  5. cecarlsen

    cecarlsen

    Joined:
    Jun 30, 2006
    Posts:
    864
    I am running into a related issue.

    I have a overlay Label with position set to absolute. I update the text. Immediately after I need to know how wide the label has become, so I can align it to the left side of another element. It would be silly to update the entire UI since I only care about the label.

    Is there a way around this?

    EDIT:
    I have posted in a new thread:
    https://forum.unity.com/threads/width-of-label.732476/
     
    Last edited: Aug 22, 2019
  6. tonytopper

    tonytopper

    Joined:
    Jun 25, 2018
    Posts:
    226
    One problem of practicality with GeometryChangeEvent is the margin of the item doesn't seem to have been set yet. As well as the padding of the element that the element sits inside of.


    Either that or
    style.paddingLeft.value.value 
    and
    style.marginRight.value.value
    are bugged.
     
  7. antoine-unity

    antoine-unity

    Unity Technologies

    Joined:
    Sep 10, 2015
    Posts:
    780
    You'd need to use the properties from
    element.resolvedStyle
    to get proper values.
    https://docs.unity3d.com/ScriptReference/UIElements.IResolvedStyle.html
     
    tonytopper likes this.
  8. tonytopper

    tonytopper

    Joined:
    Jun 25, 2018
    Posts:
    226
    Thanks, yeah, I was able to eventually figure that out.

    While this works, this API smells.

    I see IComputedStyle, IStyle, IResolvedStyle, and ICustomStyle though I think IComputedStyle is a typo of some sort cause it's only mentioned on https://docs.unity3d.com/ScriptReference/UIElements.IStyle.html

    While I am sure IStyle has a purpose, this naming is so obviously backward. I implore Unity devs to be more thoughtful in the future.
     
    TaHoSi likes this.
  9. antoine-unity

    antoine-unity

    Unity Technologies

    Joined:
    Sep 10, 2015
    Posts:
    780
    You're right about the documentation typo. However there is clear purpose behind the distinction between the
    style
    and
    resolvedStyle
    properties. The first one are inline styles, specific to that element instance, that you configure in C# or UXML.
    The resolved styles represent the final values after USS selectors have been computed and layout has been processed, which is what is desirable most of the time.