Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Tables in UIToolkit

Discussion in 'UI Toolkit' started by fherbst, Apr 2, 2020.

  1. fherbst

    fherbst

    Joined:
    Jun 24, 2012
    Posts:
    802
    I'd like to understand how we're supposed to build tables in UIElements (now UIToolkit).
    The very limited examples don't show anything like that, and Unity themselves are creating "tables" in ways that make any web developer cringe.

    For example, this table in package manager:
    upload_2020-4-2_11-23-55.png

    Is actually 3 vertical VisualElements (one for the names, one for the versions, one for the installed text) that are in a horizontal VisualElement and have a forced px height:
    upload_2020-4-2_11-25-43.png
    (besides other issues, this breaks if any element has childs that are higher than 14px and it breaks if any of the texts is an empty string)

    This makes it impossible to e.g. design a "Row" template UXML and reuse that for generating a table.

    TLDR; Is there a way to create a flexible table in UXML?
     
    PrimalCoder, Lohoris-U and tacman1123 like this.
  2. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,231
    There is no built-in way to create a flexible table in UXML. As such, it depends what your requirements are. If you know you'll have variable height rows, then go with a row-based table - where you use flexbox to make sure all rows line up and manually (using C#) control the widths of each cell within each column to make sure the columns line up. But making the table column-based (as you see in the Package Manager) is also valid, especially if the rows are always constant height.

    We will have more automatic/robust table or grid controls in the future. For now, you have to create something yourself.

    A note if you need a table for a very large amount of data: look into ListView (Window > UI > UIElements Samples > ListView). It requires all rows to have the same height but it provide virtualization of the rows, meaning it re-uses UI elements as you scroll instead of creating all the elements for the entire data set. You can combine ListView with C#-managed cell/column widths to create a virtualized table view.
     
  3. fherbst

    fherbst

    Joined:
    Jun 24, 2012
    Posts:
    802
    OK, thank you. So as expected the answer is unfortunately "there's no way to have a row template for dynamic content with UXML" but "can be created as custom thing with C#".

    As said, making the table column-based defeats the purpose of having templates for each row - or how would I then instantiate the table with either 4 or 50 rows besides building it fully in code?
     
  4. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,231
    Maybe I wasn't clear enough. Let me try again. You can have the entire row be defined in UXML. You can then use UXML Instances within UXML to define the table itself - still all with just UXML. Now:

    1. if your table has fixed-width columns you can just give each cell within your row a USS class and give all of them the same width

    2. if you table has dynamic widths for each column, this is where C# comes in. You just need to cycle through all cells within the nth column (by going row by row and selecting the nth cell) and force its style.width to be the same as all its nth column brothers and sisters.
     
  5. fherbst

    fherbst

    Joined:
    Jun 24, 2012
    Posts:
    802
    I did understand that part. But for my understanding that's a ton of work that should be on the layout system, especially when it comes to multiline text. Maybe (hopefully) I'm getting that wrong though.

    Take the table from package manager as example. It is supposed to have dynamic column widths for all columns.
    Could you maybe provide an example for how to turn that exact behaviour into a row-based UXML-template-based table? Bonus points for adding a column on the right that has dynamic multi-line text and fills the remaining space to full width.
     
    Tony_Max likes this.
  6. Kleptine

    Kleptine

    Joined:
    Dec 23, 2013
    Posts:
    274
    Are there any table implementations that we could grab from existing packages? I'm specifically looking for one with resizable columns. Doesn't seem like there's much of a community on Github either -- I kind of expected this would be something someone had made before.
     
  7. MostHated

    MostHated

    Joined:
    Nov 29, 2015
    Posts:
    1,235
    I had posted this in another thread about finding the size of elements, but it's relevant here as I use it to resize my columns. I keep a Dictionary (or list in some cases) of the column data, then use the .Aggregate() function (the .Select() function can be used if you need to x => x.ToString() or similar if the key is some other type within the collection) to count the current number of characters in each column, then multiply that by whatever number best fits your current font (will be different based on if a font is monospaced or not, as well as font size) then + N for a bit of additional padding as seen below.

    Code (CSharp):
    1. // -- Automatically adjust the width of the name column to the width of the longest entry ---
    2. // -- The element keys are the text in each row for a particular column ---
    3. var elementKeys = customPackages.Keys.ToList();
    4. // -- Using the list of strings, check the length of each and return the value of the longest one ---
    5. var labelWidth = elementKeys
    6.     .Select(x => x)
    7.     .Aggregate("", (max, cur) => max.Length > cur.Length ? max : cur).Length;
    8.  
    9. // -- Multiply the length by the approx width of each character + small extra padding ---
    10. var calculatedLabelWidth = labelWidth * 5.5f + 20;
    11.  
    12. // -- Apply that to the appropriate column's width for each row ---
    13. psPackageNameHeader.style.width = calculatedLabelWidth;
    14. psPackageName.style.width = calculatedLabelWidth;
    15. // etc, etc...
    The example above is used in the clip below. I have found the benefit of doing it this way as it can be done dynamically at any time. My results have been pretty reliable and consistent. Obviously, this may not work for everyone and in every situation, but if nothing else it helps give some ideas on alternative methods in which a goal can be achieved, then good deal.

     
    Last edited: Nov 4, 2020
    ChGuidi, myanko, JG-Denver and 2 others like this.
  8. Tony_Max

    Tony_Max

    Joined:
    Feb 7, 2017
    Posts:
    349
    More than 2.5 years later and we still need to go to forum to realise that there is no such a base thing as table or grid view, instead of this find tones of argues. I really wonder why if it is so simple to implement your own grid then why it is still not implemented built-in. I mean library premade visual elements aren't perfect, so can we just get one more imperfect but frequently used for draft solutions element? Even if there are cons which stop devs from creating grid layout.
     
    Gasimo and Lohoris-U like this.
  9. Tony_Max

    Tony_Max

    Joined:
    Feb 7, 2017
    Posts:
    349
    And no, it is not just that. You also need to figure out what cell has max width, but unfortunately you can't do it at the first frame, so as you recommend on another thread we should register
    GeometryChangedEvent
    callback which makes UI code full of boilerplate garbage.
     
  10. moshe_skillreal

    moshe_skillreal

    Joined:
    Apr 13, 2021
    Posts:
    3
    2022 LTS is out with UI Toolkit, is there any better answer now?
     
  11. MechaWolf99

    MechaWolf99

    Joined:
    Aug 22, 2017
    Posts:
    293
    Yup, I think the MultiColumnListView should do!
     
    PrimalCoder and EZaca like this.
  12. PrimalCoder

    PrimalCoder

    Joined:
    Aug 12, 2014
    Posts:
    11
    Sort of, for many situations. One thing the MultiColumnListView handles very poorly is column sizing. There are two options when it comes to column sizes: Fixed width, or stretch to fill all available space. And that's okay for a large number of situations, especially when your focus is on Editor UI.

    But it's also very limiting in some other situations. For example, in the window on the right I have a MultiColumnListView that I could know the necessary column sizes for ahead of time and set them accordingly. In the one on the left where the MultiColumnListView standing in for a table was generated through Markdown* my only option is to have each column take up an exactly equal percentage of the total width, and there is no option to have the table shrink to fit the contents. It's a full-width table with equal-sized columns, or nothing.

    Not to mention the fact that MultiColumnListView is kind of buggy with respect to layouts, sizing, and scrollbars :(

    upload_2024-3-24_13-21-13.png

    * Markdown does have a way to specify column widths as a percentage, but it can't be used here because the MultiColumnListView control doesn't respect percentages for column width, only fixed widths and "stretchable"
     
  13. MostHated

    MostHated

    Joined:
    Nov 29, 2015
    Posts:
    1,235
    I know it's a bit excessive, and *should* not be necessary to have to do, but in that situation, I end up using the GeometryChangedEvent to iterate the contents of each column and get the length of the longest text string, calculate a width based on the number of characters and the font size, add a few extra points for padding, then use that as the column width. It's not always perfect, especially for non-monospaced fonts, but it's been close enough for my needs.

    ---
    Edit: Oh.. I already mentioned about doing something like that a few years ago, in this very thread, lol. I should have realized when I receive an email that this thread was updated, as it means I have posted.. or I could have just scrolled up first.
     
    PrimalCoder likes this.
  14. PrimalCoder

    PrimalCoder

    Joined:
    Aug 12, 2014
    Posts:
    11
    That's an interesting idea. I'm experimenting with something similar, but rather than calculating the width directly from the text, I'm using TextElement.MeasureTextSize() to calculate the size under the assumption that it will be more accurate for non-monospaced fonts and take font style into account etc.

    I then sum the maximum column sizes for all columns and assign that to the MultiColumnListView's max-width style property so that small tables don't stretch horizontally more than necessary.

    And... It works okay, I guess.



    It's a promising avenue to pursue, if I don't get frustrated and just quit using Unity altogether because of all of the stupid bugs in UI Toolkit anyways ;)
     
  15. MostHated

    MostHated

    Joined:
    Nov 29, 2015
    Posts:
    1,235
    I know I had tried using the MeasureTextSize call at one point, but there was something about it that either wasn't working for me, or at all (possibly because I was working with Editors and not runtime), but I can't quite remember what it was. I might have to revisit that at some point, as I had forgotten all about it until you mentioned it. It might have something to do with referencing a struct somewhere that was internal at the time and I didn't feel like digging a whole lot more into it than I already had.
     
    PrimalCoder likes this.