[SOLVED] Clicking anywhere outside a Toggle deselects it

Hi !

I have some Toggle UI elements that have a common ToggleGroup. When I click on any of them, the Toggle highlights correctly and stays highlighted as intended, but as soon as I click anywhere else on the screen, that toggle unhighlights.

I’d like that toggle to remain highlighted even if I click somewhere else on the screen. (and of course, keeping the original Toggle logic intact, that is, a newly clicked Toggle will highlight itself and unhighlight the previous one)

Thank you very much in advance for your help !

Agreed. I just ran into this. It is baffling to me that a Toggle would behave in any other way. Clicking anywhere other than in the toggle, or amongst a set of toggles in a toggle group, should have no impact on the state whatsoever. It can probably be worked around, but I can’t imagine what the use case would be of the existing behaviour. Unnecessary fettling required.

Try changing the navigation mode Redirecting to latest version of com.unity.ugui

2 Likes

Has anyone found a solution for this? I tried fiddling with the navigation modes of the buttons within the toggle group but it seemed to have no effect.

1 Like

I think karl_jones is talking about disabling all button access. I’ve been looking into this as well and there seems to be no good solutions so far. Some posts I found helpful had a couple of different approaches:

// Using the global EventSystem to update to the previously selected Selectable

// Using a button hack to manually set the button state

I haven’t tried these yet but it seems to be an ongoing issue.

There is also a good blog post here: Stupid Unity UI Navigation Tricks - dylanwolf.com
He outlines a similar solution with a global listener.

3 Likes

Thank you very much everyone for the replies (and the solutions) and I’m sorry for answering this late.

I think is sad that, after all this time, this unnatural UI behaviour still exists in Unity right out of the box. Not sure if it’s a bug or an intended feature but i think it’s something that can be improved.

Wait what was the solution? Why is this marked as solved?

1 Like

Hi shawnballay1

I’m sorry. You’re totally right. The “solution” (which is not a complete solution but a workaround) is what dylanfries wrote:

What worked for me was the second link (Using a button hack to manually set the button state). But the solution is still ugly unfortunately (adding a script on each button)

1 Like

I had the same issue for a while and just discovered where my mistake was.
My toggles are prefabs that are loaded dynamically during gameplay.
The prefab had the checkbox Graphic deactivated. It turns out the “checkbox” should always be turned on, and will show only if the toggle isOn at runtime

2 Likes

I didn’t have time to fully think this through, and it was for a prototype, but this script might help others in a pinch.
it only works for buttons using the “Colour Tint” transition type. Stick it on a standard btn and call toggleSelected on click. Honestly its a bit dirty but it helped me out.

public class StickyBtnState : MonoBehaviour
{
    bool selected = false;
    public Button btn;
    public Color normalColor = Color.white;
    public Color selectedColor = Color.grey;

    private ColorBlock colors;

    public void Awake()
    {
        btn = gameObject.GetComponent<Button>();
        colors = btn.colors;
    }

    public void toggleSelected()
    {
        selected = !selected;

        if (selected)
        {
            //var colors = btn.colors;
            colors.normalColor = selectedColor;
            colors.selectedColor = selectedColor;
            btn.colors = colors;
        }
        else
        {
            //var colors = btn.colors;
            colors.normalColor = normalColor;
            colors.selectedColor = normalColor;
            btn.colors = colors;
        }
    }
}
1 Like

For anyone that’s struggling with this problem: I decided to make a custom Toggle and ToggleGroup to suit my needs. Got the code here. You might want to add some things or customize it to your liking, but it should give you an idea. I used an animator, but you could use Color tints as well of course.

The Toggle class

using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;

public class Toggle : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerClickHandler
    {
        [SerializeField] private Animator animator;
        [SerializeField] private UnityEvent onSelect;

        [HideInInspector] public ToggleGroup toggleGroup;

        private bool isSelected = false;
        public bool IsSelected => isSelected;

        private bool isDisabled = false;
        public bool IsDisabled => isDisabled;

        public void OnPointerClick(PointerEventData eventData)
        {
            if (isDisabled)
                return;

            if (isSelected)
            {
                OnDeselect();
            }
            else
            {
                onSelect?.Invoke();
                SetSelected(true);
            }
        }

        public void OnDeselect()
        {
            if (isDisabled)
                return;

            SetSelected(false);
        }

        public void OnPointerEnter(PointerEventData eventData)
        {
            if (isDisabled)
                return;

            if (!isSelected)
            {
                animator.SetTrigger("Highlighted");
            }
        }

        public void OnPointerExit(PointerEventData eventData)
        {
            if (isDisabled)
                return;

            if (!isSelected)
            {
                animator.SetTrigger("Normal");
            }
        }

        public void SetSelected(bool value)
        {
            if (isDisabled)
                return;

            isSelected = value;
            if (isSelected == true)
            {
                if (toggleGroup)
                {
                    toggleGroup.SetCurrentlySelected(this);
                }
                animator.SetTrigger("Selected");
            }
            else
            {
                animator.SetTrigger("Normal");
            }
        }

        public void SetDisabled(bool value)
        {
            isDisabled = value;
            if (isDisabled == true)
            {
                isSelected = false;
                animator.SetTrigger("Disabled");
            }
            else
            {
                animator.SetTrigger("Normal");
            }
        }
    }

And the ToggleGroup:

[RequireComponent(typeof(ClickerBehaviour))]
    public class ToggleGroup : MonoBehaviour
    {
        private Toggle lastSelectedToggle;
        private Toggle currentSelectedToggle;

        private Toggle[] toggles;

        private void Start()
        {
            toggles = GetComponentsInChildren<Toggle>();
            for (int i = 0; i < toggles.Length; i++)
            {
                toggles[i].toggleGroup = this;
            }
        }

        public void SetCurrentlySelected(Toggle value)
        {
            if (currentSelectedToggle != null)
            {
                if (currentSelectedToggle != value)
                {
                    currentSelectedToggle.SetSelected(false);
                    lastSelectedToggle = currentSelectedToggle;
                }
            }
            currentSelectedToggle = value;
        }

        public void DeselectEverything()
        {
            if (currentSelectedToggle != null)
            {
                lastSelectedToggle = currentSelectedToggle;
                currentSelectedToggle = null;
            }

            for (int i = 0; i < toggles.Length; i++)
            {
                toggles[i].SetSelected(false);
            }
        }
    }

The ClickerBehaviour class is just a class that checks if there was a click anywhere on a certain layer mask and then calls the DeselectEverything() function. I wanted all Toggles to clear when I clicked with my right mouse button.
Also, the lastSelectedToggle variable isn’t really used, but I imagine you might find some use for it.

4 Likes

still exists.
im using animation behavier on my toggles, and if i click anywhere else, the animation state goes to “Normal”.

Thanks Sebastiran. I too found Toggle’s to behave unintentionally like this (click outside of the group and all the toggles go off), even in 2019.4.3 LTS. Gonna test this out.

1 Like

I ended up making some better ones kinda based on Sebastrian’s code but with Sprites and color switches, custom fade animation time, pressed/down sprite/color, no deselect bug:

4 Likes

Thanks @holyfot It helped a lot! :wink:

You have an error in the code.
yield return null; should be in the while loop(s).

Besides that - great work! Thanks.

I must say I have found the fastest and the shortest solution to this problem; Here it is:

List<Button> ribbonButtons; // Add all the [buttons] in the same group to this list

    public void SelectRibbonButton(Button but) // Call this functin with the button and pass the button itself as the parameter
    {
        foreach (Button b in ribbonButtons)
        {
            if (b == but)
            {
                b.interactable = false;
            }
            else
            {
                b.interactable = true;
            }
        }
    }

And this is the state of the button:
7293724--882577--Untitled.png

As you can see, you must place your Normal color on the Disabled Color section instead, because the selected button will become disabled (Uninteractable) and the unselected buttons will become enabled (Interactable).

This is the outcome:

Clicking anywhere on the screen won’t change the state of the buttons; Only if you click one of the greyed out buttons, their state will change

Note: The change in color tint you see here is due to the glowing effect I have put on the selected button

1 Like

Uncheck this box on event system worked for me
7307653--885517--upload_2021-7-7_21-12-1.png

8 Likes

Where did you find this checkbox? I can’t see it in my event system game object7334176--891148--upload_2021-7-16_19-6-17.png

1 Like

I found this on the Input System UI Input Module component … which happened to be on the same object as the Event System one.

Using that checkbox stops clicks on empty space deselecting toggle buttons BUT it doesn’t stop clicks on other buttons from changing the Toggle image.

1 Like