I simply need to get the controlID of a GUI/GUILayout.Button, without forcefully assigning it, so that I can compare it with the GUIUtility.hotControl value.
Clearly Unity’s GUI keeps a controlID for each GUI element, so this should be a simple task, but I can’t find a solution. Also, consider that I need this in an Editor panel, so I have access to Editor classes.
You might be interested in this piece of code. It solves a bug when using overlapping buttons (unity usually fires the button click event for the overlapped button, not for the topmost button), also works for touch devices and solves the problem when using scrollviews with buttons, too (it will only fire on button click, not on dragging the scroll view):
/*
*
* **** GUIButton CLASS ****
*
* this versions sends only events to the topmost button ...
*
*
* Fixes the bugs from the original GUI.Button function
* Based on the script from Joe Strout:
* http://forum.unity3d.com/threads/96563-corrected-GUI.Button-code-%28works-properly-with-layered-controls%29?p=629284#post629284
*
*
* The difference in this script is that it will only fire events (click and rollover!)
* for the topmost button when using overlapping buttons inside the same GUI.depth!
* Therefore the script finds the topmost button during the layout process, so it
* can decide which button REALLY has been clicked.
*
* Benefits:
* 1. The script will only hover the topmost button!
* (doesn't matter wheter the topmost button is defined via GUI.depth or via drawing order!)
* 2. The script will only send events to the topmost button (as opposed to Joe's original script)
* 3. The script works for overlapping buttons inside same GUI.depth levels,
* as well as for overlapping buttons using different GUI.depth values
* 4. The script also works when overlapping buttons over buttons inside scrollviews, etc.
*
* Usage: just like GUI.Button() ... for example:
*
* if ( GUIButton.Button(new Rect(0,0,100,100), "button_caption", GUI.skin.customStyles[0]) )
* {
* Debug.Log( "Button clicked ..." );
* }
*
*
*
* Original script (c) by Joe Strout!
*
* Code changes:
* Copyright (c) 2012-2013 by Frank Baumgartner, Baumgartner New Media GmbH, fb@b-nm.at
*
*
* */
using UnityEngine;
using System.Collections;
public static class GUIButton
{
private static int highestDepthID = 0;
private static EventType lastEventType = EventType.Layout;
private static int frame = 0;
private static int lastEventFrame = 0;
public static bool noButtonActive = false;
public static Vector2 lastClickPos = Vector2.zero;
public static int lastDownButtonHash = 0;
public static bool Button(Rect bounds, string caption2, GUIStyle btnStyle = null )
{
string caption = "";
// this made problems when adding/removing controls during UI's:
// int controlID = GUIUtility.GetControlID( bounds.GetHashCode(), FocusType.Passive );
// this one works also with dynamic GUI's !
int controlID = GUIUtility.GetControlID( FocusType.Passive );
bool isMouseOver = bounds.Contains(Event.current.mousePosition);
int depth = (1000 - GUI.depth) * 1000 + controlID;
if ( isMouseOver && depth > highestDepthID ) highestDepthID = depth;
bool isTopmostMouseOver = (highestDepthID == depth);
bool wasDragging = TouchDragDetector.isTouchDragging();
if ( isMouseOver ) noButtonActive = false;
#if (UNITY_IPHONE || UNITY_ANDROID) && !UNITY_EDITOR
bool paintMouseOver = isTopmostMouseOver && (Input.touchCount > 0) && !wasDragging;
#else
bool paintMouseOver = isTopmostMouseOver;
#endif
if ( btnStyle == null )
{
btnStyle = GUI.skin.FindStyle("button");
}
if ( Event.current.type == EventType.Layout && lastEventType != EventType.Layout )
{
highestDepthID = 0;
frame++;
noButtonActive = true;
}
lastEventType = Event.current.type;
if ( Event.current.type == EventType.Repaint )
{
bool isDown = (GUIUtility.hotControl == controlID);
if ( caption == null || caption == "" )
{
// optimized: only 1 drawcall instead of two!
if ( paintMouseOver && btnStyle.hover != null && btnStyle.hover.background != null )
{
GUI.DrawTexture( bounds, btnStyle.hover.background, ScaleMode.StretchToFill );
}
else if ( isDown && btnStyle.active != null && btnStyle.active.background != null )
{
GUI.DrawTexture( bounds, btnStyle.active.background, ScaleMode.StretchToFill );
}
else if ( btnStyle.normal != null && btnStyle.normal.background != null )
{
GUI.DrawTexture( bounds, btnStyle.normal.background, ScaleMode.StretchToFill );
}
}
else
{
// Works but is slow: always creates 2 drawcalls: (1 bg and 1 for text)
btnStyle.Draw(bounds, new GUIContent(caption), paintMouseOver, isDown, false, false);
}
}
// Workaround:
// ignore duplicate mouseUp events. These can occur when running
// unity editor with unity remote on iOS ... (anybody knows WHY?)
// if ( frame <= (1+lastEventFrame) ) return false;
if ( frame <= (10+lastEventFrame) ) return false; // increased to 10 due to double clicks on ipad 3 ...
switch ( Event.current.GetTypeForControl(controlID) )
{
case EventType.mouseDown:
{
// Debug.Log ("mouseclick: topmost=" + isTopmostMouseOver + " wasDragging=" + wasDragging + " controlID=" + controlID + " caption=" + caption );
if ( isTopmostMouseOver && !wasDragging )
{
GUIUtility.hotControl = controlID;
}
break;
}
case EventType.mouseUp:
{
GUIUtility.hotControl = 0;
if ( isTopmostMouseOver && !wasDragging )
{
lastClickPos.x = Input.mousePosition.x;
lastClickPos.y = Input.mousePosition.y;
lastEventFrame = frame;
// log events for statistics ...
StaticActivityLogger.logButtonEvent();
return true;
}
break;
}
}
return false;
}
}
Its a nasty use of side effects and not generally recommended good technique, but it does seem to work with the current version of Unity.
Be aware that you must do the ID fetch just before the actual draw of the button or other component.