Search Unity

EditorGUILayout.ScrollView not allowing access to all enclosed elements

Discussion in 'Immediate Mode GUI (IMGUI)' started by djTomServo2, Dec 15, 2014.

  1. djTomServo2

    djTomServo2

    Joined:
    Mar 22, 2013
    Posts:
    14
    Hey folks, I'm trying to do what I think is a pretty simple operation, but it seems like I'm missing some ideas or concepts about how EditorGUIs should be structured.

    The idea is very simple, I'm trying to build a custom editor window with a vertically scrolling list of items, but I'm unable to get the scrollview's area to contain all the items, that is, when I have more items in the scrollview than the window size, I can't scroll down to see all of them. Here's my code:

    Code (CSharp):
    1.  
    2. // cScrollSize is actually calculated from the number of elements
    3. int cScrollSize = 800;
    4.  
    5. // If I set the GUILayout.Height to cScrollSize, THEN I can see all the elements
    6. // but I may have as many as 128 elements, which will be much more than the
    7. // vertical resolution of my monitor, so obviously that's not a solution
    8. mScrollPos = EditorGUILayout.BeginScrollView(mScrollPos, true, true,
    9.     new GUILayoutOption[] {
    10.         GUILayout.Width(400),
    11.         GUILayout.Height(500),
    12.         GUILayout.ExpandHeight(true)
    13.     });
    14.  
    15. // Is the vertical layout necessary, or can I add content directly under the scrollview?
    16. mRectLogCtrls = EditorGUILayout.BeginVertical(new GUILayoutOption[] {
    17.     GUILayout.Width(800),
    18.     GUILayout.Height(cScrollSize)
    19. });
    20.  
    21. GUI.Label(new Rect(0, 0, 200, 30), "Input Channels");
    22. int start = 20;
    23. int cY = start;
    24. for(int cid=0;cid<mController.InputChannels.Count;++cid)
    25. {
    26.     cY = start + (20 * cid);
    27.     GUI.Label(new Rect(15, cY, 100, 20),
    28.         mController.InputChannels[cid].ChannelName);
    29.     GUI.Toggle(new Rect(115, cY, 100, 20),
    30.         mController.InputChannels[cid].ChannelActive,
    31.         "Active");
    32.     GUI.Toggle(new Rect(215, cY, 100, 20),
    33.         mController.InputChannels[cid].ChannelManual,
    34.         "Manual");
    35.     GUI.TextField(new Rect(315, cY, 100, 20),
    36.         mController.InputChannels[cid].ChannelAssignment);
    37. }
    38.  
    39. cY += 20;
    40. GUI.Label(new Rect(0, cY, 200, 30), "Output Channels");
    41. cY += 20;
    42. start = cY;
    43. for (int cid = 0; cid < mController.OutputChannels.Count; ++cid)
    44. {
    45.     cY = start + (20 * cid);
    46.     GUI.Label(new Rect(15, cY, 100, 20),
    47.         mController.OutputChannels[cid].ChannelName);
    48.     GUI.Toggle(new Rect(115, cY, 100, 20),
    49.         mController.OutputChannels[cid].ChannelActive,
    50.         "Active");
    51.     GUI.Toggle(new Rect(215, cY, 100, 20),
    52.         mController.OutputChannels[cid].ChannelManual,
    53.         "Manual");
    54.     GUI.TextField(new Rect(315, cY, 100, 20),
    55.         mController.OutputChannels[cid].ChannelAssignment);
    56. }
    57. EditorGUILayout.EndVertical();
    58. EditorGUILayout.EndScrollView();
    59.  
    This code gives me the following result:
    BrokenScrollview.png
    The only way I can make all the elements in the scrollview accessible is to set the size of the scrollview to the size of the content, but it seems like that defeats the purpose of a scrollview. Am I ordering my other controls correctly, am I missing something, is there something obvious? I apologize if this is a simple questions, I've searched different forums and it seems like I'm the only person having this problem. Thanks in advance!
     
  2. AdamScura

    AdamScura

    Joined:
    Mar 25, 2012
    Posts:
    55
    Get ready for a head-slap moment! ...

    You are confusing BeginScrollView() by giving it both Height(500) and ExpandHeight(). Remove Height(500) and it should work.

    You can actually simplify that code a lot. EditorGUILayout.BeginScrollView() expands itself to fit the available space by default, and only adds scrollbars when necessary. So you can literally do this and get the intended result

    Code (CSharp):
    1.  
    2. mScrollPos = EditorGUILayout.BeginScrollView(mScrollPos);
    3. // ...
    4. EditorGUILayout.EndScrollView();
    5.  
    No you don't need it. Layout is vertical by default. But your could probably make your layout more flexible by using BeginHorizontal for each row instead of absolute-positioning the controls.

    Writing editor windows can seem daunting at first. And as you get more experience you can look forward to finding a treasure trove of Unity quirks and bugs :)

    BTW: That project looks interesting. What are you making?
     
    JamesTheMains likes this.
  3. djTomServo2

    djTomServo2

    Joined:
    Mar 22, 2013
    Posts:
    14
    Wow, thanks for the complete response! I'll give this a shot today, yeah I'll admit it's definitely like drinking from the firehose, there are so many options and classes, but hey, if it means flexibility, I'm all for it!

    As for the specific project, it's a show control interface for a theme park ride. The idea is that we have all the show graphics and projections rendered in Unity, and we're a Simex control board and an Advantech Digital I/O card to trigger phyiscal elements from Unity as well as using physical controls to change states in Unity. It's definitely pretty cool stuff, unlike anything i've ever worked on before!
     
  4. AdamScura

    AdamScura

    Joined:
    Mar 25, 2012
    Posts:
    55
    That's awesome. I love to see people finding new uses for Unity :)
     
  5. AdamScura

    AdamScura

    Joined:
    Mar 25, 2012
    Posts:
    55
    You got me thinking... I'm building a GUI framework that allows me to make editor windows with clean object-oriented code. I was thinking how I would approach your situation with my GUI framework.

    Instead looping over all your controls in OnGUI, you would just bind to the data and let the framework take care of the rest. Something like this:

    Code (CSharp):
    1.  
    2. private Root root;
    3.  
    4. private void OnEnable()
    5. {
    6.     // Create a root for the GUI hierarchy
    7.     root = new root();
    8.  
    9.     // DataContext is the object to bind properties on
    10.     root.SetDataContext(mController);
    11.  
    12.     // Create a GridView and bind it to a row source
    13.     GridView gridView1 = root.Add<GridView>().BindRows("InputChannels");
    14.  
    15.     // Setup the column templates and bind to the fields of InputChannels
    16.     gridView1.AddColumn().SetHeader("Name").Add<Label>().BindText(".ChannelName");
    17.     gridView1.AddColumn().SetHeader("Active").Add<Toggle>().BindValue(".ChannelActive");
    18.     gridView1.AddColumn().SetHeader("Manual").Add<Toggle>().BindValue(".ChannelManual");
    19.     gridView1.AddColumn().SetHeader("Assignment").Add<TextField>().BindProperty(".ChannelAssignment");
    20. }
    21.  
    22. private void OnGUI()
    23. {
    24.     // Let Root take care of OnGUI
    25.     Root.OnGUI(this);
    26. }
    27.  
    My GUI framework would automatically layout the controls in a grid and keep all the data in sync. I'm debating if I should release it on the asset store. What do you think?
     
    Last edited: Dec 16, 2014