How do I create a single-click pulldown menu with UnityGUI?

Hi!

I want to create a menu that works like a regular Windows or Mac pulldown menu (i.e. File → Save As). I’ve searched around the forums a bit, but all scripts I’ve found require two clicks:

click 1: open pulldown menu
click 2: select item

That’s one click too many! I want to open the menu on mouse down, allow the user to make a selection and then close the menu again on mouse up.

I tried using a RepeatButton, like so:

if (GUI.RepeatButton(new Rect(x, y, w, h), "File"))
{
   selected = GUI.Toolbar(...); // sub menu
}

Unfortunately, the above doesn’t work, since the menu closes as soon as the user moves the cursor away from the [File] button. You can work around that, but then the next problem is that the sub menu buttons don’t respond since as far as the UnityGUI is concerned, the [File] button hasn’t been released yet.

So… any GUI experts out there who know how to crack this one?

you need to use a different coding structure and have states.
when you click the button, the state is set / reset and if the state is set at the time, the menu is shown

I don’t think that solves my problem:

bool isMenuOpen = false;

void OnGUI()
{
   if (GUI.RepeatButton(..., "File")) // main menu
      isMenuOpen = true;
   if (isMenuOpen)
   {
      if (GUI.Button(..., "Save As")); // submenu
        isMenuOpen = false;
   }
}

I still need two clicks to activate the “Save As” action. Plus, my menu doesn’t fold up when the user clicks outside of the pulldown.

I’m beginning to think that UnityGUI simply isn’t suited to build pulldown menus that behave like the standard OS controls… perhaps I should just roll my own.

You desire complex behavior so your code needs to handle complex situations.

I just explained how you can keep it open.
Its up to you to implement the rest too.

Why you would want a straight save as is a my to me though as it is meant to be a submenu, isn’t it?

as for clicking outside: massive button that covers the whole screen to detect “click outside” could be an option.

I’m sorry, but replies like this are just not helpful at all.

I don’t need to hear Caption Obvious… I’m asking for suggestions on how to address my problem. If you don’t have any, then that’s okay.

It is (look at the pop-up menu in the file requester I made for this), but you have to use the right functions; clearly using buttons isn’t going to work. I used a SelectionGrid for the menu.

–Eric

That looks exactly like what I want! Care to share how that works? I’m still missing the crucial bit of keeping the menu open. :frowning:

Attempt 1:

if (GUI.RepeatButton(new Rect(10, 100, 200, 20), "File")) // Main menu
{
	string[] submenu = { "Open", "Save", "Save As..." };
	GUI.SelectionGrid(new Rect(10, 120, 200, 80), 0, submenu, 1); // Sub menu
}

Problem: menu closes as soon as the mouse moves off the file button.

Attempt 2:

private bool m_menuOpen = false;

void OnGUI()
{
	if (GUI.RepeatButton(new Rect(10, 100, 200, 20), "File"))
		m_menuOpen = true;
		
	if (m_menuOpen)
	{
		string[] submenu = { "Open", "Save", "Save As..." };
		GUI.SelectionGrid(new Rect(10, 100, 200, 80), 0, submenu, 1);
	}
		
	if (Input.GetMouseButtonUp(0)) // Ensures the menu always closes
		m_menuOpen = false;
}

Problem: focus stays on the “File” button.

So… there’s something you do differently that tells the GUI to transfer the focus from the double arrow button at the end of your combo to the selection grid that contains the directories.

What’s your secret? :wink:

Simple use GUIX

http://www.ennanzus-interactive.com/developer/GUIX/

Actually you can click anywhere in that bar rather than just the double arrow. But anyway, that’s a label rather than a button and I’m checking the label’s rect for the mouse position. I’m not sure if there’s a reason for that or if I just forgot about RepeatButton, but it sounds like the focus thing might be the reason…

–Eric

Yeah, I ended up doing about the same:

	Rect menuRect = new Rect(10, 100, 200, 20);
	Vector3 mousePos = Input.mousePosition;
	mousePos.y = Screen.height - mousePos.y;

	// Manual hittest against menu rect to open/close the menu		
	if (mousePos.x >= menuRect.x  mousePos.x <= (menuRect.x + menuRect.width) 
		mousePos.y >= menuRect.y  mousePos.y <= (menuRect.y + menuRect.height))
	{
		if (Input.GetMouseButtonDown(0))
			m_menuOpen = true;
		if (Input.GetMouseButtonUp(0))
			m_menuOpen = false;	
	}
		
	if (m_menuOpen)
	{
		// Menu is open.
		// Use a selection grid that contains both the main menu item and the sub menu.
		// (tricks Unity into giving this item focus)
		string[] submenu = { "File", "Open", "Save", "Save As..." };
		m_subSel = GUI.SelectionGrid(new Rect(10, 100, 200, 90), m_subSel, submenu, 1);
			
		if (GUI.changed)
			m_menuOpen = false; // Close menu once user makes a selection
	}
	else
	{
		// Menu is closed.
		// Use a repeat button to get rollover effect for free.
		GUI.RepeatButton(menuRect, "File", GUI.skin.button);
	}

It’s still not 100% what I want, but it will do for now.

You can use Rect.Contains instead of a bunch of if/then logic to see if the mouse position is inside a rect. Also it’s recommended to use the Event class for OnGUI stuff rather than Input, such as Event.current.mousePosition (which is in the correct OnGUI coordinate system).

–Eric

Thanks for those tasty tidbits, Eric! They’ll sure make my life easier :slight_smile: