Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

How to get the actual width and height of an uielement?

Discussion in 'UI Toolkit' started by Xarbrough, Feb 1, 2020.

  1. Xarbrough

    Xarbrough

    Joined:
    Dec 11, 2014
    Posts:
    1,184
    I have a window which resizes based on its content. To calculate the absolute size I need to first add up a few known values but then also add the size of the UnityEditor.UIElements.Toolbar at the top. I can imagine two ways of getting this information:

    1) The the Toolbar class has a fixed/default styling which should know the height before even having an instance.

    2) If the Toolbar instance already exists, I should be able to query its style, but the height property may not be set because it is dynamically stretching, so ideally, I would want to ask for the actually rendered size.
     
    Maxim7537 likes this.
  2. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,203
    To get the current dimensions of an element you have:
    element.resolvedStyle.width (recommended)
    element.resolvedStyle.height (recommennded)
    element.worldBound (relative to the EditorWindow)
    element.transform
    element.layout

    But yes, most of these won't be computed on the first frame after creating an element so you have to wait until it has already computed its size and position by registering for the GeometryChangeEvent (on the element you want to read the size from).
     
  3. Xarbrough

    Xarbrough

    Joined:
    Dec 11, 2014
    Posts:
    1,184
    Thank you! The properties do work the way you described. Too bad for me, but waiting for the first render frame won't work since I need to calculate the window size before opening it as a popup. But still good info for the future!
     
  4. Lance-Grooms

    Lance-Grooms

    Joined:
    Mar 24, 2016
    Posts:
    26
    I have a similar issue. Here is the work-around approach I took:

    Code (CSharp):
    1. public void OnPostVisualCreation()
    2. {
    3.     // Make invisble so you don't see the size re-adjustment
    4.     // (Non-visible objects still go through transforms in the layout engine)
    5.     Visual.visible = false;
    6.     Visual.schedule.Execute(WaitOneFrame);
    7. }
    8.  
    9. private void WaitOneFrame(TimerState obj)
    10. {
    11.     // Because waiting once wasn't working
    12.     Visual.schedule.Execute(AutoSize);
    13. }
    14.  
    15. private void AutoSize(TimerState obj)
    16. {
    17.     // Do any measurements, size adjustments you need (NaNs not an issue now)
    18.     Visual.MarkDirtyRepaint();
    19.     Visual.visible = true;
    20. }
    It would be really nice if you could just manually force a layout transformation on a particular VisualElement, but as it's been said, this is not currently possible.
     
  5. Prodigga

    Prodigga

    Joined:
    Apr 13, 2011
    Posts:
    1,121
    Just wanted to +1 here, as I ran into the same issue as @Xarbrough. I wanted to size my EditorWindow (small utility popup) to the height of the content, but no way to get height of content until after first update.
     
    DREBOTgamestudio likes this.
  6. MostHated

    MostHated

    Joined:
    Nov 29, 2015
    Posts:
    1,218
    You should be able to get the layout data after the layout pass but before the first frame by doing the following:

    Code (CSharp):
    1. root.RegisterCallback<GeometryChangedEvent>(GeometryChangedCallback);
    2. //[...]
    3. private void GeometryChangedCallback(GeometryChangedEvent evt)
    4. {
    5.     root.UnregisterCallback<GeometryChangedEvent>(GeometryChangedCallback);
    6.     // Do what you need to do here, as geometry should be calculated.
    7. }
    I was originally doing similar with this:

    Code (CSharp):
    1. root.schedule.Execute(ExecuteDeferredTask).StartingIn(0);
    But then was informed by @uMathieu that the GeometryChangedEvent example would do the trick (source: PropertyField children can only be queried after initial UI build)
     
  7. Lance-Grooms

    Lance-Grooms

    Joined:
    Mar 24, 2016
    Posts:
    26
    This is the optimal solution if all the layout information is known at the time of the event. Obviously for dynamic cases, this is not true.

    In my case, for example, I am simulating a generic modal popup dialog which is dynamically populated with title and content strings. But really any dynamic custom control that does it's own absolute layout could run into this issue.
     
    Prodigga likes this.
  8. arielsan

    arielsan

    Joined:
    Dec 3, 2013
    Posts:
    47
    I was having issues forcing a ScrollView to go the the last element after I added it because its new size wasn't calculated yet or something. I had to use a callback to GeometryChangedEvent and it worked fine.

    Thanks!!
     
  9. CaseyHofland

    CaseyHofland

    Joined:
    Mar 18, 2016
    Posts:
    551
    Hey hey, this is still a problem. I'm making a popupwindow and there is no real way of finding the height. When I use "Geometry changed event" it will blatantly return an incorrect result.

    Resolved Height.png
    Relevant code

    Clearly too short.png
    Clearly too short
     
  10. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,203
    > Hey hey, this is still a problem. I'm making a popupwindow and there is no real way of finding the height. When I use "Geometry changed event"
    The height of your `rootVisualElement` is indeed resolved already (it's not NAN anymore). It just needs some padding/margins or the EditorWindow needs to give this element some breathing room. The element, in your case, will only resolve to the absolute minimum dimensions it needs in order to fit its flex-layouted children. It's not aware of the EditorWindow it's in. For regular, dockable, EditorWindows, for example, you'd need to also account for the tab bar area in your EditorWindow height calculation on top of the rootVisualElement size.
     
    CaseyHofland likes this.
  11. CaseyHofland

    CaseyHofland

    Joined:
    Mar 18, 2016
    Posts:
    551
    I see, thank you so much!

    I’m using UnityEditor.PopupWindow btw, I saw in a tutorial on the documentation that this was the way to do it but it still feels clumsy, hope a better API for editor popups comes along. Otherwise I’m really enjoying the UIElements API!
     
  12. CaseyHofland

    CaseyHofland

    Joined:
    Mar 18, 2016
    Posts:
    551
    Unfortunately, this code gives me the same results:

    Code (CSharp):
    1.             public override Vector2 GetWindowSize()
    2.             {
    3.                 var resolvedStyle = editorWindow.rootVisualElement.resolvedStyle;
    4.                 return new(280f, resolvedStyle.height + resolvedStyle.paddingTop + resolvedStyle.paddingBottom + resolvedStyle.marginTop + resolvedStyle.marginBottom + resolvedStyle.borderTopWidth + resolvedStyle.borderBottomWidth);
    5.             }
    So the padding is not coming from the root visual element, but from something higher up. For now I'll just do
    +6
    .
     
  13. dlorre

    dlorre

    Joined:
    Apr 12, 2020
    Posts:
    700
    You can inspect this with the debugger, it should be on GameView:

    upload_2023-7-26_9-16-42.png
     
  14. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,203
    Indeed, you'll need to account for some additional margins. It's not clean but it's the best you can do for now. Most use cases (and the API kinda assumes this) have the UI respond to the EditorWindow size and not the other way around.