Currently selected Handle in editor script

Is there a way of knowing if one of your Handles is currently selected or gets selected in CustomEditor?

I’m currently using Handles.FreeMoveHandle for every control point in my spline-tool. I try to keep track of the selected point via hacky code, where I’m making an intelligent guess during a mouseUp-event, that tries to determine which of the points is the closest of the click. This works “ok”, but I’m getting reports that it’s not 100%.

There’s so much noise that I’m going to bump this once.

If I understand your issue correctly, have you tried checking GUI.changed after drawing each handle? In theory, this should tell you which one was the last one with which the user interacted. Does that help?

1 Like

I don’t know yet, but seems like potential winner. I’m going to test it later when I get home. Thanks!

Any more info on this issue? I am wrestling with the same problem for my Orthello OTPath spline.

The GUI.changed technique is only set when a handle has been moved.changed, not when you just select a handle ( and it gets outlined with a yellow line )

Will go and check if I can cast a ray against the handle.

Update

I have managed to find the selected handle by doing this :

  • Code goes into the OnSceneGUI() of the editor class

  • Check if the GUIUtility.hotControl has changed ( value of 0 = no control selected )

  • If it hotControl changed, find the current handle by ray casting into the world using HandleUtility.GUIPointToWorldRay(Event.current.mousePosition); and checking if that
    ray intersects with any of the created handles.

This worlks because I know where I create my handles and within what radius the specific ray should fall to intersect with the handle.

After I have found the right control, I can cache the controlID for future and quicker lookup. I have not managed to find a way to get a handle’s ControlID right after I have created it. ( anyone??)

Cheers for the info, it pointed me in the right direction.

The easiest way I found to do this is to name the Control using GUI.SetNextControlName ( “…” ).

Unfortunately as some of the Handles ( PositionHandle ) create multiple controls only
the first one will be named, but it easy enough to build your own from the components
provided.

Take the PositionHandle as an example we can add the following function to recreate
it from the other handles provided. As you can see we set the name of each of the
controls, this will let us use the function GUI.GetNameOfFocusedControl () to get the
name of the control and use that to lookup an array or dictionary to find the related
item.

Vector3 PositionHandle ( Vector3 position, Quaternion rotation, string name )
{
// right Axis
GUI.SetNextControlName ( name );
Handles.color = new Color ( 1.0f, 0.0f, 0.0f, 0.75f );
Vector3 newPos = Handles.Slider ( position, rotation*Vector3.right );

// Up Axis
GUI.SetNextControlName ( name );
Handles.color = new Color ( 0.0f, 1.0f, 0.0f, 0.75f );
newPos += Handles.Slider ( position, rotation*Vector3.up ) - position;

// Forward Axis
GUI.SetNextControlName ( name );
Handles.color = new Color ( 0.0f, 0.0f, 1.0f, 0.75f );
newPos += Handles.Slider ( position, rotation*Vector3.forward ) - position;

float sizeMultiplier = 0.0f;

if ( Camera.current.orthographic )
{
sizeMultiplier = Camera.current.orthographicSize * 2.5f;
}
else
{
Plane screen = new Plane ( Camera.current.transform.forward, Camera.current.transform.position );
screen.Raycast ( new Ray ( position, Camera.current.transform.forward ), out sizeMultiplier );
}

GUI.SetNextControlName ( name );
Handles.color = new Color ( 1.0f, 1.0f, 1.0f, 0.75f );
newPos += Handles.FreeMoveHandle ( position, rotation, 0.02f * sizeMultiplier, Vector3.zero, Handles.RectangleCap ) - position;
}

1 Like

Thanks for this post. I wasn’t looking for the handle name, but I did want to check if a handle was pressed before raycasting.

Thanks.

Just a little addition to this thread, in Unity 4.2 (at least) you can set the ID of some of the handles EG:

static Vector3 Slider2D(
int id,
Vector3 handlePos,
Vector3 offset,
Vector3 handleDir,
Vector3 slideDir1,
Vector3 slideDir2,
float handleSize,
DrawCapFunction drawFunc,
Vector2 snap,
bool drawHelper);

This can be used with GUIUtility.hotControl to identify which handle the user is currently interacting with

NECROBUMP

Anyone found a reliable way to get the id of a Handles.FreeMoveHandle and then check if it’s selected?

Yep, there is.

Just make your own Cap function. It receives ControlID, so you can store it somewhere and check against GUIUtility.hotControl.

Then just call one of the predefined cap functions.

Thanks. I was struggling with this and your idea worked really great.

If any of you needs some reference, here’s a piece of code that allows you to edit simple segmented line by using the standard move gizmo on the selected node.

1 Like

Necro bump. I used the following code to detect ‘clicks’ on FreeMoveHandle:

  private float m_lastClick = -1;
  private float m_doubleClickThreshold = 0.3f;

        private Dictionary<int, int> ControlToNode = new Dictionary<int, int>();
        private Dictionary<int, int> NodeToControl = new Dictionary<int, int>();
....

        void OnSceneGUI()
        {


            for ( int i = 0; i < points.Count; i++ )
            {
                Vector3 pos = transform.TransformPoint(points[i]);
                float handleSize = HandleUtility.GetHandleSize(pos);
                Vector3 newPos;

                    newPos = Handles.FreeMoveHandle( pos, Quaternion.identity, handleSize * 0.09f, Vector3.one,

                        ( controlID, position, rotation, size ) =>
                        {
                            NodeToControl[i] = controlID;
                            ControlToNode[controlID] = i;
                            HandleFunc( controlID, position, rotation, size );
                        }
                }
            }

            if ( Event.current.type == EventType.Used && wasUsed && GUIUtility.hotControl == 0 )
            {
                wasUsed = false;
                Debug.LogFormat( "Click" );

                if ( Time.time < this.m_lastClick + m_doubleClickThreshold && m_lastControl == m_lastHotControl )
                {
                    if ( ControlToNode.ContainsKey( m_lastHotControl) )
                    {
                        Debug.Log( "Doubleclick" );
                    }
                }
                else
                {
                    if ( ControlToNode.ContainsKey( m_lastHotControl) )
                    {
                        Debug.Log( "SingleClick" );
                        m_lastControl = m_lastHotControl;
                        m_lastClick = Time.time;
                    }
                }
            }
            else if ( Event.current.type == EventType.Used )
            {
                wasUsed = true;
                m_lastHotControl = GUIUtility.hotControl;
            }
    }


        void HandleFunc( int controlID, Vector3 position, Quaternion rotation, float size )
        {
            if ( controlID == GUIUtility.hotControl )
                GUI.color = Color.red;
            else
                GUI.color = Color.green;
            Handles.Label( position, new GUIContent( nodeTexture ), m_handleStyle );
            GUI.color = Color.white;
        }
3 Likes

Old thread, but there is something new to the topic. As the previous posts already say you can define the control ID of some handles when you create them inside OnSceneGUI(), for example

 int controlID_thatThisHandleShouldHave = 12345; //typically you obtain unused ID's via GUIUtility.GetControlID() instead of coming up with your own like in this example line
Handles.Slider2D(controlID_thatThisHandleShouldHave, , , , , ); //assign your ID to the created handle

Then afterwards you can check via

if(GUIUtility.hotControl == controlID_thatThisHandleShouldHave)

if the control is selected.
You can combine this info with things like MouseDownEvents, to check if a handle has been clicked .

This works for all methods of the Handles class that take the “id” parameter. The problem is: Not all of the methods take this parameter. The good thing is: As the years go by more and more methods of the Handles class get new overloads that have this parameter. I don’t know how it was back in the day but for example the Handles.FreeMoveHandle method has this parameter now, so there is no need anymore to make an own Cap function as someone further up suggested.

It get’s more complicated with methods like Handles.PositionHandle, because this internally consists of actually seven separate handles (each axis is a separate handle, etc…), so you would have to actually assign 7 separate ID’s. So if you want to detect if the user clicked on a PositionHandle you have the problem, that you actually have to rebuild the whole position handle with its seven separate individual handles in order to assign the ID’s by yourself. Now the new thing: This seems to change. The Unity code on Github (for Unity 2022.2) shows new public overloads for Handles.PositionHandle and Handle.RotationHandle, that finally take the id’s. It takes the PositionHandleIds parameter, which is a struct in which you can assign all 7 control ID’s separatly.

It’s not officially released yet, and it may turn out as an undocumented internal feature, but still could be a huge time saver in some cases.

3 Likes