How to dynamically create a grid of controls in a CustomEditor

I would like to create a grid of properties corresponding to the patches of the terrain. If, for example, the user creates a 3x3 terrain, then I would like to have 3x3 panels in my CustomEditor, with each containing some properties, e.g. (float)min height, (float)max height, (int) biome type. The panels would have to be wrapped up in horizontal and vertical scrollbars, but to start with I would just like to be able to create a grid of panels.

I already have a method for extracting the patch number / configuration in the terrain, the problem is just how to lay it out in the editor. I’m currently inheriting from the GenericEditor, overriding Initialize and using a base.Initialize(layout) to pull the original terrain properties back in.

I’ve experimented with LayoutElementsContainer horizontalContainer = layout.HorizontalPanel() and then adding properties to the horizontalContainer, but I can’t seem to get this to behave nicely (e.g. horizontalContainer.FloatValue gets squashed into a narrow width, which I can’t figure a way to change).

Here is a sample of my code:

using FlaxEngine;
using FlaxEditor.CustomEditors.Elements;

#if FLAX_EDITOR
using FlaxEditor.CustomEditors.Editors;
using FlaxEditor.CustomEditors;


namespace TerrainSystem
{
    [CustomEditor(typeof(Terrain))]
    public class TS_Editor : GenericEditor
    {
        public override DisplayStyle Style => DisplayStyle.Inline;

        public override void Initialize(LayoutElementsContainer layout)
        {
            base.Initialize(layout);
            Terrain terrain = (Terrain)this.Values[0];

            layout.Label("Procedural Terrain", TextAlignment.Near);
            layout.Space(10);

            FloatValueElement minHeight = layout.FloatValue("Min Height", "Float value for the min height of the terrain.");
            minHeight.Value = 100;
            FloatValueElement maxHeight = layout.FloatValue("Max Height", "Float value for the max height of the terrain.");
            maxHeight.Value = 500;

            ButtonElement btn_HeightMap = layout.Button("Regenerate Height Map", Color.Gray);
            btn_HeightMap.Button.Clicked += () => GenerateHeightMap(terrain, minHeight.Value, maxHeight.Value);

Any suggestions would be much appreciated.
Thanks.

You could use UniformGridPanel to place multiple controls inside a grid:

// Create panel
var panel = layout.CustomContainer<UniformGridPanel>();
panel.CustomControl.Height = 20.0f;
panel.CustomControl.SlotsVertically = 1;
panel.CustomControl.SlotsHorizontally = 2;

// Add controls
var button1 = panel.Button("Action 1");
var button2 = panel.Button("Action 2");
1 Like

Thanks, that looks like just what I wanted. Is it possible to wrap that in scroll bars or does the user just have to drag the properties window to make it wide enough for it all to be legible? It would be really nice if I could set a panel width and height and have scroll bars to navigate them (at the moment panel.CustomControl.Width does not appear to change anything).

Edit:
Sorry, I see that Panel has some scroll bar properties, but I’m not smart enough to figure out how to use them. I tried panel.CustomControl.IsScrollable = true; but it does not appear to change anything.

Currently testing with this:

var panel = layout.CustomContainer<UniformGridPanel>();
Int2 patchArrayDims = new Int2(9, 9);

panel.CustomControl.IsScrollable = true;
panel.CustomControl.Height = 100f * patchArrayDims.Y + 1;
panel.CustomControl.Width = 500f;
panel.CustomControl.SlotsVertically = patchArrayDims.Y + 1;
panel.CustomControl.SlotsHorizontally = patchArrayDims.X + 1;

// Add controls

List<ButtonElement> buttons = new List<ButtonElement>();
for (int patchID = 0; patchID < patchCount; patchID++)
{
    buttons.Add(panel.Button("Action" + patchID.ToString()));
}

In addition to the question of how to wrap this in a scrollable container, there appears to be a bigger issue of how to assign controls to specific slots. For example, if I want to add two FloatValueElement objects to each slot. At the moment it appears that I can just make each slot a button or leave it empty.

Edit
Well, I figured out an ugly fix which was to create UniformGridPanels within each parent UniformGridPanel and add them to lists as I created them to provide an ID system, but it all feels pretty ugly. The CustomControl for the UniformGridPanel really needs allow selection of the active grid to create in, with each grid having its own padding / wrapping options.