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

Question ScrollView fighting which one?

Discussion in 'Editor & General Support' started by piggybank1974, May 9, 2021.

  1. piggybank1974

    piggybank1974

    Joined:
    Dec 15, 2015
    Posts:
    621
    I'm not sure what the difference between GUILayout.BeginScrollView and GUI.BeginScrollView is but I cannot get GUILayout.BeginScrollView to work properly it does not display all the items.

    I've been trying all GUILayout options.

    Code (CSharp):
    1.  
    2. GUILayout.BeginVertical();
    3. mScrollViewPosition = EditorGUILayout.BeginScrollView(mScrollViewPosition, false, true, GUILayout.Width(500), GUILayout.ExpandHeight(true), GUILayout.MaxHeight(300));
    4.  
    5. for (Int32 A = 0; A < mButtons.Count; A++)
    6.  GUI.Button(mButtons[A].Bounds, mButtons[A].Name);
    7.  
    8. EditorGUILayout.EndScrollView();
    9. GUILayout.EndVertical();
    10.  
    now if I change it and use the GUI.BeginScrollView I can get it to work,

    Code (CSharp):
    1.  
    2. GUILayout.BeginVertical();
    3. mScrollViewPosition = GUI.BeginScrollView(new Rect(0, 250, 500, 200), mScrollViewPosition, new Rect(0, 0, 500, mButtons[mButtons.Count - 1].Bounds.y + mButtons[mButtons.Count - 1].Bounds.height), false, true);
    4.  
    5.  for (Int32 A = 0; A < mButtons.Count; A++)
    6.   GUI.Button(mButtons[A].Bounds, mButtons[A].Name);
    7.  
    8. EditorGUILayout.EndScrollView();
    9. GUILayout.EndVertical();
    10.  
    but I keep getting the following error in the console

    EndLayoutGroup: BeginLayoutGroup must be called first.
    UnityEngine.GUIUtility:processEvent (int,intptr,bool&)

    but everything still works fine. its doing my crusty loaf in.

    Cheers
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,713
    I see you mixing
    GUILayout
    with
    EditorGUILayout
    ... are you sure that's doable?
     
  3. Madgvox

    Madgvox

    Joined:
    Apr 13, 2014
    Posts:
    1,315
    It's doable and necessary for editor IMGUI. EditorGUILayout doesn't have all of the functions that GUILayout has. EditorGUILayout is built to extend GUILayout, not replace it.

    OP, you want to be using EditorGUILayout.BeginScrollView here. The issue you're running into is that your buttons are not GUILayout and are instead GUI, which is why they aren't being laid out properly inside the scrollview. You should use GUILayout.Button instead.

    Where are you getting your button bounds from? If you can't use GUILayout for your buttons, you'll need to reserve the layout area using GUILayoutUtility.GetRect, and then draw those buttons into that area.
     
    Kurt-Dekker likes this.
  4. piggybank1974

    piggybank1974

    Joined:
    Dec 15, 2015
    Posts:
    621
    @Madgvox

    Thanks for the information the problem is with GUILayout.Button I cannot calculate the position, and if I remember correctly, I couldn't get it to work otherwise, I've just copied "the button generated code" from my project, I'm working from home so later I can test it out, your idea to make sure I'm correct.

    Code (CSharp):
    1.              
    2. if (Buttons != null && Buttons.Count > 0)
    3.  {
    4.   Int32 mID = 0;
    5.   Int32 mNumberOfButtons = Buttons.Count;
    6.   Int32 mNumberOfColumns = 4;
    7.   Int32 mNumberOfRows = Mathf.CeilToInt((Single)Buttons.Count / (Single)mNumberOfColumns);
    8.  
    9.   //float bb = EditorGUIUtility.currentViewWidth;
    10.   Single mButtonWidth = ( (this.maxSize.x - 10) / mNumberOfColumns); //
    11.   Single mButtonHeight = mButtonWidth * 0.66F;
    12.                
    13.    for (Int32 row = 0; row < mNumberOfRows; row++)
    14.     for (Int32 column = 0; column < mNumberOfColumns; column++)
    15.      if (mNumberOfButtons-- > 0)
    16.       {
    17.        Buttons[((Buttons.Count - 1) - mNumberOfButtons)].ID = ++mID;
    18.        Buttons[((Buttons.Count - 1) - mNumberOfButtons)].Bounds = new Rect(column * mButtonWidth, displayoffsety + (row * mButtonHeight), mButtonWidth, mButtonHeight);
    19.       }
    20.  }
    21.  
    I've not come across GUILayoutUtility.GetRect before as this is the first time I've tried to create an Editor Window, in unity I've created editor code before though.

    What really annoys me with unity is the lack of good tutorial support, as they change things, they are really behind the curve when it comes to tutorials, and you have to dig all over to find answers, I would have thought scrolling a bunch of buttons dynamically created or otherwise it would tell you what to use but it does not.

    I just took a look at GUILayoutUtility.GetRect example but I cannot see how this would scroll?

    In this instance why should I use EditorGUILayout.BeginScrollView over GUI.BeginScrollView as I do get more control.

    Actually, I just realised why I was using GUI.Button over the other, as these buttons are created dynamically I could not figure out a nice way of knowing which button was pressed, I'm not sure why they would create a system whereby you have zero chance of knowing which button is pressed is ludicrous at least have some sort of callback function that rant over anyways.

    so in the GUI method I created the following code:

    Code (CSharp):
    1.  
    2.  if (mEvent.type == EventType.MouseDown && mEvent.button == 0)
    3.   {
    4.    if (mButtons != null && mButtons.Count > 0)
    5.     for (Int32 A = 0; A < mButtons.Count; A++)  
    6.      if (mButtons[A].Bounds.Contains(mEvent.mousePosition) == true)
    7.       {
    8.         mSelectedActiveObjectIndex = A;  
    9.         break;
    10.       }  
    11.   }
    12.  
    this gives me feedback as to what button was pressed, I call this just before I displaying the buttons

    Cheers
     
    Last edited: May 10, 2021
  5. Madgvox

    Madgvox

    Joined:
    Apr 13, 2014
    Posts:
    1,315
    The purpose of the GUILayout class is to do the layout for you -- it is not intended for freeform drawing like the GUI class is.

    You can duplicate this logic using the Layout classes, you just need to work within the bounds the Layout functions.

    For example, you can create a grid of buttons in Layout using a pattern like the following:

    Code (CSharp):
    1. GUILayout.BeginVertical();
    2.  
    3. for( int y = 0; y < numRows; y++ ) {
    4.     GUILayout.BeginHorizontal();
    5.  
    6.     for( int x = 0; x < numColumns; x++ ) {
    7.         if( GUILayout.Button( buttonName ) ) {
    8.             // do something with the button press
    9.         }
    10.     }
    11.  
    12.     GUILayout.EndHorizontal();
    13. }
    14.  
    15. GUILayout.EndVertical();
    There is certainly a learning curve for IMGUI and there has never been very good resources for it. I learned it by digging through the unity reference source, scrounging the documentation, and trial and error. For awhile I was considering creating a series of tutorials to teach Editor IMGUI, but tools like Odin inspector and UI Elements have been slowly phasing out the niche for it.

    The GetRect wouldn't scroll itself, it would go inside of the scroll view:

    Code (CSharp):
    1. EditorGUILayout.BeginScrollView(...);
    2.  
    3. var btnRect = GUILayoutUtility.GetRect(...);
    4.  
    5. // construct your button matrix within the bounds of btnRect using GUI methods (not GUILayout)
    6.  
    7. EditorGUILayout.EndScrollView();
    Because GUI.BeginScrollView is not compatible with GUILayout.BeginVertical. GUILayout is an extension of GUI that manages laying out the elements for you so that you don't have to do it yourself. GUI is not directly compatible with GUILayout because GUI is not in the layout. In order to switch context from GUILayout (Layout based) to GUI (Rect based), you need to use GUILayoutUtility.GetRect. In other words, GetRect allows you to reserve a rectangle in the Layout context that you can use in the Rect context. To switch contexts from GUI to GUILayout, you need to use GUILayout.BeginArea.

    Internally, editor windows will define a BeginArea and BeginVertical block covering the entire window size by default -- this is what allows you to call things like GUILayout.Button with no wrapper methods in an EditorWindow's OnGUI.

    I think you are misunderstanding the IM part of IMGUI. IM means "immediate", as in, everything is processed immediately. There is no intrinsic concept of a callback function for a button because there is no persistent concept of a "button". In order to know whether a button has been pressed in IMGUI, you just check for it ... immediately:

    Code (CSharp):
    1. if( GUILayout.Button( "Click me!" ) ) {
    2.     // the button was just clicked
    3. } else {
    4.     // the button was not just clicked
    5. }
    So putting this all together, a Layout-based version of what you're trying to create might look like this:

    Code (CSharp):
    1. GUILayout.BeginVertical();
    2. mScrollViewPosition = EditorGUILayout.BeginScrollView(mScrollViewPosition, false, true, GUILayout.Width(500), GUILayout.ExpandHeight(true), GUILayout.MaxHeight(300));
    3.  
    4. int mID = 0;
    5.  
    6. int mNumberOfColumns = 4;
    7. int mNumberOfRows = Mathf.CeilToInt(mButtons.Count / (float)mNumberOfColumns);
    8.  
    9. for (int y = 0; y < mNumberOfRows; y++) {
    10.     GUILayout.BeginHorizontal();
    11.  
    12.     for (int x = 0; x < mNumberOfColumns; x++) {
    13.         if (GUILayout.Button(mButtons[mID].Name)) {
    14.             mSelectedActiveObjectIndex = mID;
    15.         }
    16.  
    17.         mID++;
    18.     }
    19.  
    20.     GUILayout.EndHorizontal();
    21. }
    22. EditorGUILayout.EndScrollView();
    23.  
    24. GUILayout.EndVertical();
    25.  
     
  6. piggybank1974

    piggybank1974

    Joined:
    Dec 15, 2015
    Posts:
    621
    @Madgvox

    First I want to thank you for taking the time with this you replay must have taken a while to create.

    I think because I did not post the full EditorWindow code your only seeing part of it and trying to figure out the answers for me, if it helps to POST the full code I will then we are on the same lines at least.

    like I said I can't use the GUILayout.Button ALL buttons are created dynamically and there is no way to put hidden ID's, I have no way of linking back to the mButtons list for the index number

    the last part of the code does that by looking bounds of the button that why I came up with it good or bad.

    Now I suppose I could look at the name, to determine the index but if the names are not unique that puts a spanner in the works.

    if your willing to take some further time I'll willing to figure this out with you.

    Cheers again for your support.
     
  7. Madgvox

    Madgvox

    Joined:
    Apr 13, 2014
    Posts:
    1,315
    I don't understand how this, which iterates over something called mButtons and sets the active index when pressed:

    Code (CSharp):
    1.  if (mEvent.type == EventType.MouseDown && mEvent.button == 0)
    2.   {
    3.    if (mButtons != null && mButtons.Count > 0)
    4.     for (Int32 A = 0; A < mButtons.Count; A++)
    5.      if (mButtons[A].Bounds.Contains(mEvent.mousePosition) == true)
    6.       {
    7.         mSelectedActiveObjectIndex = A;
    8.         break;
    9.       }
    10.   }
    Is different than this, which iterates over something called mButtons and sets the active index when pressed:

    Code (CSharp):
    1. GUILayout.BeginVertical();
    2. mScrollViewPosition = EditorGUILayout.BeginScrollView(mScrollViewPosition, false, true, GUILayout.Width(500), GUILayout.ExpandHeight(true), GUILayout.MaxHeight(300));
    3. int mID = 0;
    4. int mNumberOfColumns = 4;
    5. int mNumberOfRows = Mathf.CeilToInt(mButtons.Count / (float)mNumberOfColumns);
    6. for (int y = 0; y < mNumberOfRows; y++) {
    7.     GUILayout.BeginHorizontal();
    8.     for (int x = 0; x < mNumberOfColumns; x++) {
    9.         if (GUILayout.Button(mButtons[mID].Name)) {
    10.             mSelectedActiveObjectIndex = mID;
    11.         }
    12.         mID++;
    13.     }
    14.     GUILayout.EndHorizontal();
    15. }
    16. EditorGUILayout.EndScrollView();
    17. GUILayout.EndVertical();
    Why would one of those work but the other does not, in your architecture?

    You need to do useful work with your buttons in some way, or there's no point in having them. I'm failing to see what's preventing you from using a GUILayout solution -- not that a GUI solution wouldn't work, mind you -- just that you are currently mixing the two in ways that do not work at all.

    **Looking back over my example code I realized that you would need to do a bounds check on the mButtons array, but that is easily fixed and does not invalidate the approach.

    My goal is to help you understand how IMGUI works generally and how Unity's IMGUI works in particular, since you're currently attempting to use the GUI/GUILayout classes in ways that don't work. I would like to understand what it is you're trying to accomplish at a high level, as I may be able to provide recommendations. Digging around in code can only go so far if I'm missing the wider context.
     
    Last edited: May 10, 2021
  8. piggybank1974

    piggybank1974

    Joined:
    Dec 15, 2015
    Posts:
    621
    @Madgvox


    I was going to reply last night when I sore your little reply but I had a problem with the guy that’s buying my flat so I had to sort that first. I was shocked that you had updated your replay again.


    Back in 2010 I created a tilemap editor, when I started in unity I upgraded it to use a plugin system, which is how I do all my games/prototypes with if they are 2D anyways.


    At the back in 2019/2020 I started using a scene based serialisable system, with a scriptable object, basically it binary saves the changes only, which cut down a map for example from 300k to 30k or so.


    The problem sometimes I have is if I update the map on my editor, and upload it again, I never really created a decent way of changing the differences, although it’s an easy workaround I thought I wonder if I can create an Editor Window, and create the maps that way still be using the serialisable system.


    So here is the Prefab Plotter.


    First I think I was getting confused on how it actually works ‘buttons I mean’ once I sat down last night with a cupa and said I’m an idiot and said “an old English saying, could see the wood for the trees” I think I got into my head I needed a reference to each individual button I pressed but that NOT how they created the system.


    I rewrote some of the code and now its working better, using some of the pointers you provided.


    So what it does, it scans the Assets folder for prefabs, if it finds at least one in the folder, the folder gets added to the DirectoryItem list, then if you select a folder from the Combobox ‘I’m originally, what they call it now a Winforms developer so they are comboboxes to me not dropdowns’ it populates it with the buttons.


    If a button gets pressed it loads the prefab from the folder then you can go to the scene and start placing the prefabs. Originally I was using the Selection.Object but I found that the PrefabUtility class I was using wiped it out after the first time you place the GameObject in the scene.


    Also if you have a prefab selected the clear button colour goes red else it’s green, it’s just a visual representation if you have anything selected.


    I’m sure it should be in a custom draw or something as the alignment is not right on everything but I’ve not used one of them so far.


    I know it’s only going to be used by myself, but I wanted to use the experience to better understand the editor window.


    I’ve also added some screenshot of the editor and a serialisable scene, I’ve used in the past. And I’ve also added the editor here.


    Again a want to thank you for your efforts If it was not for people like yourself none of this would be possible.
     

    Attached Files: