17
\$\begingroup\$

I want to create a tool similar to Unity's Terrain tool, which has some nice toggle buttons in the inspector:

Toggle buttons in the inspector Toggled button

How can I achieve similar design to this? I know how to create normal buttons and other UI components in the inspector, but I can not find enough information to make the buttons toggle.

So far I have used normal toggles that produce a checkbox:

var tmp = EditorGUILayout.Toggle( SetAmountFieldContent, _setValue );

if ( tmp != _setValue )
{
  _setValue = tmp;
  if ( _setValue )
    _smoothValue = false;
}

tmp = EditorGUILayout.Toggle( SmoothValueFieldContent, _smoothValue );

if ( tmp != _smoothValue )
{
  _smoothValue = tmp;
  if ( _smoothValue )
    _setValue = false;
}

Setting the toggle GUIStyle to "Button" does not produce the wanted result. The text or image content goes on left of the button instead of inside.

var tmp = EditorGUILayout.Toggle( SetAmountFieldContent, _setValue, "Button" );

Unwanted behaviour on toggle

Also none of the options found in GUISkin does not seem to help.

\$\endgroup\$

6 Answers 6

8
\$\begingroup\$

I solved this problem by using buttons instead of toggles.

First define two button styles before any functions:

private static GUIStyle ToggleButtonStyleNormal = null;
private static GUIStyle ToggleButtonStyleToggled = null;

Then in OnInspectorGui() make sure they are generated if null:

if ( ToggleButtonStyleNormal == null )
{
  ToggleButtonStyleNormal = "Button";
  ToggleButtonStyleToggled = new GUIStyle(ToggleButtonStyleNormal);
  ToggleButtonStyleToggled.normal.background = ToggleButtonStyleToggled.active.background;
}

And then use the idea proposed by @jzx to toggle the styles:

GUILayout.BeginHorizontal(  );

if ( GUILayout.Button( SetAmountFieldContent, _setValue ? ToggleButtonStyleToggled : ToggleButtonStyleNormal ) )
{
  _setValue = true;
  _smoothValue = false;
}

if ( GUILayout.Button( SmoothValueFieldContent, _smoothValue ? ToggleButtonStyleToggled : ToggleButtonStyleNormal ) )
{
  _smoothValue = true;
  _setValue = false;
}

GUILayout.EndHorizontal();

This produces what I wanted:

First toggled button Second toggled button

\$\endgroup\$
2
  • 2
    \$\begingroup\$ Great job! I was looking for days to find the answer for toggle buttons in the Unity3D Editor. Just one question - why don't you just use _smoothValue = !GUILayout.Button( SetAmountFieldContent, _smoothValue ? ToggleButtonStyleToggled : ToggleButtonStyleNormal), instead of having two booleans? That works well for me and the code looks close to using stadnard unity button/toggles \$\endgroup\$ Oct 16, 2017 at 19:31
  • 1
    \$\begingroup\$ @IvayloSlavov I used that as an example for clarity \$\endgroup\$
    – Lasse
    Oct 18, 2017 at 14:20
6
\$\begingroup\$

Here is a basic, but simple way to do it:

 pressed = GUILayout.Toggle(pressed, "Toggle me !", "Button");

taken from here

\$\endgroup\$
1
  • 2
    \$\begingroup\$ This is a pretty dang simple way to do this. \$\endgroup\$
    – nsxdavid
    Mar 10, 2020 at 4:23
5
\$\begingroup\$

Use GUILayout.Toolbar. The docs are misleading, it actually does run the buttons together:

toolbar example

Additionally, you can use styles "buttonleft", "buttonmid", "buttonright" to draw these button style directly.

        var left = GUI.skin.FindStyle("buttonleft");
        GUILayout.Button("left only button", left);
\$\endgroup\$
4
\$\begingroup\$

Toggle returns the current state of the button - either the same state passed in value or the new value as changed by the user. So a better pattern would be...

// TODO: Initialize these with GUIContent
private GUIContent _toggleButtonDepressedLabel;
private GUIContent _toggleButtonDefaultLabel;

// Current toggle state
bool _toggleButtonState;

void DisplayToggle()
{
    var image = _toggleButtonValue
                ? _toggleButtonDepressedLabel
                : _toggleButtonDefaultLabel;
    var newState = EditorGUILayout.Toggle(image, _toggleButtonState);
    if (newState != _toggleButtonState)
    {
        _toggleButtonState = newState;
        OnToggleButtonChanged();
    }
}

void OnGUI ()
{
    DisplayToggle();
}

void OnToggleButtonChanged()
{
    // Do stuff.
}

You can use the same state-dependent swapping pattern for GUIStyles.

private GUIStyle _toggleButtonDepressedStyle;
private GUIStyle _toggleButtonDefaultStyle;
// ...
    var image = _toggleButtonValue
                ? _toggleButtonDepressedLabel
                : _toggleButtonDefaultLabel;
    var style = _toggleButtonValue
                ? _toggleButtonDepressedStyle
                : _toggleButtonDefaultStyle;
    var newState = EditorGUILayout.Toggle(image, _toggleButtonState, style);
\$\endgroup\$
3
\$\begingroup\$

2022 solution here:

I needed to do such a thing for selecting custom tools like in a Collider component inspector.

gif of the mouse cursor clicking the "edit collider" button on the Box Collider inspector

The simple way of doing this is to use

EditorGUILayout.EditorToolbarForTarget(EditorGUIUtility.TrTempContent("Edit"), this);

which draws a series of buttons for all the available tools of the given component.

Internally, this series of button is created like this:

// The whole element is made of a label and one or more selectable buttons in a horizontal layout
using (new EditorGUILayout.HorizontalScope())
{
    // Draw Label
    EditorGUILayout.PrefixLabel("Edit 2");

    // Start Change check to see if we clicked on an already selected button
    EditorGUI.BeginChangeCheck();
    int prevIndex = index;
    // Draw "Toolbar" (a group of selectable buttons). "AppCommand" is the style used for the Tools toolbar
    index = GUILayout.Toolbar(index, new[]{ EditorGUIUtility.IconContent("Profiler.Video") }, "AppCommand");
    // Enter the condition if a button is pressed (even an already selected one)
    if (EditorGUI.EndChangeCheck())
    {
        // if we clicked on the same index, deselect it
        if (index == prevIndex)
            index = -1;
    }
}

which gives me the same result as the first solution:

gif of the mouse cursor clicking the custom buttons

source used:

CapsuleColliderEditor.cs

EditorToolGUI.cs

\$\endgroup\$
0
\$\begingroup\$

You can do this:

private GUIStyle buttonStyle;
private bool enableToogle;

public override void OnInspectorGUI()
{
    buttonStyle = new GUIStyle(GUI.skin.button);
    enableToogle = GUILayout.Toggle(enableToogle, "Toogle Me", buttonStyle);

    if(enableToogle)
    {
        // your stuff here
    }
}

It's very important that you instanciate buttonStyle inside OnInspectorGUI() otherwise you'll get an error. Also, don't do buttonStyle = GUI.skin.button; because you would be copying the skin.button reference and any change on buttonStyle would be changing all buttons styles.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .