Accessing the SelectionState of a button?

Hello All,
I have used this forum many times, and this is the first time I have ever not found what I was looking for exactly, so thank you for everyone’s assistance over the last few months! In advance I would like to apologize if this is a stupid question, but I would really appreciate an answer to this specific question rather than a suggestion to do this in a different method like using the “Animation” transition type etc.

------ REQUEST ------

I would like to print the selection state of a button, once I can do this I can move forward and utilize that information to do what I require so no need to complicate the request.

------ RESEARCH ------

First off I started by adding a script to a button and I started trying to access the selection state, and I thought I was getting close when I found this:

this.GetComponent<Button>().spriteState.pressedSprite

It turns out that this only really prints out the sprite you are using if you are utilizing the below referenced “Sprite Swap” transition type:

6442535--721139--upload_2020-10-21_15-21-25.png

I require the “Color Tint” transition type as referenced below:

6442535--721142--upload_2020-10-21_15-22-55.png

Next I decided to look at the Button definition to search for clues, here I found a class called “Selectable” which is highlighted in the following image.

Going one level deeper into “Selectable” I found the following at the bottom:

6442535--721151--upload_2020-10-21_15-33-56.png

What I would truly love to do is simply put a script on a button that is very simple and prints the SelectionState in the update method.

I can do the rest once I learn how to do this, honestly I think my struggle is coming from learning this on my own and likely missing out of some of the more complex topics that would make this more obvious.

5 Likes

Not 100% sure but I think that is tracked by the event system.

https://docs.unity3d.com/Packages/com.unity.ugui@1.0/api/UnityEngine.EventSystems.EventSystem.html

I think the expression would be something like:

GameObject sel = EventSystem.current.currentSelectedGameObject;

Caveat: I haven’t tried this recently.

This only shows what is selected, not the SelectionState which is what I am trying to access.

Well the UI code is open sourced on Github, you could probably take Button and make a PowerStackSystemsButton and do what you want with it. :slight_smile:

I imagine such a hack would be tiny (such as providing a getter), in case you ever wanted to update to a newer UI in the future: you could easily re-back-cross-port your hack.

This feels like overkill to simply print a single number that is certainly stored somewhere already.

Welcome to object oriented design.

Kurt I appreciate your effort, but you simply aren’t answering the question that was asked. I understand that what you are suggesting is an option, what I am asking is if there is a method of accessing this stored information by adding a script to the button, not by duplicating the premade button I was very specific in my original post. If you don’t know how to do that it is ok seriously, but it is neither helpful or clarifying to state most of what you have stated. If it is not possible for some specific reason that would be helpful to know as well, otherwise just leave the thread it is totally fine.

3 Likes

I don’t think there’s a way of directly accessing the current “selected” state, other than a comparison like EventSystem.current.currentSelectedGameObject == myObject

However, rather than checking the selected state in Update, you can attach a script to the button that implements ISelectHandler and IDeselectHandler to get callbacks when the selection state changes like this:

ButtonSelectionTracker : MonoBehaviour, ISelectHandler, IDeselectHandler {
  public bool IsSelected { get; private set; } = false;

  OnSelect(BaseEventData data) {
    IsSelected = true;
  }

  OnDeselect(BaseEventData data) {
    IsSelected = false;
  }
}

You can then fire off your own C# event, or just have another script read IsSelected from this script.

4 Likes

You already went digging through the UI code… you posted some screencaps above.

Did you notice this line?

protected SelectionState currentSelectionState ...

Now I don’t know but I been told, protected has certain implications as far as access.

Those implications told ME something, and as a professional engineer I suggested a reasonable work-around.

Alternatively, it appears that most if not all transitions pass through this method:

protected virtual void DoStateTransition(SelectionState state, bool instant) {}

Again, protected virtual also has implications that may suggest a solution you could engineer.

If those solutions are unpalatable to you, I’m afraid I cannot help you. Good luck!

1 Like

This is somewhat confusing to me, how can there be no way to access the current SelectionState if it is used to set the tint of the graphic and it is shown within the Selected method? Sorry if this is something I should understand already. My problem with using anything you have referenced is that it only tracks what was last clicked, it is not nearly as up to date and preintegrated with the UI system in Unity. The SelectionState number output would be perfect for many implementations for me if I could simply access the 0,1,2,3,4 listed.

It is clear that I have offended you, that is not my goal. Thanks for commenting have a nice day.

As Kurt noted, the selection state variable in Selectable is marked as protected. That means the Unity developers have not chosen to make it directly accessible to other classes. You could make a class that derives from Selectable (or transitively, from Button) and use that instead of the basic Button. In that case your derived type could access its own selection state or even expose it through a public member. Seems easier to go with the ISelectHandler route I detailed above though, but it’s up to you.

I am a little lost on how I would go about implementing that so I think I have some studying to do, again I am not super experienced, but I will do my best to figure it out.

The concept you are looking for is “object inheritance” or creating subclasses. It seems like you all are talking past each other a little bit. Protected means it is accessible to subclasses, so you can make your own class that extends one of the Selectable or Button classes and then you would get access to it.

The suggestion is that perhaps you are monkeying with things that were not meant to be monkeyed with as it was not left particularly accessible by unity devs. Buyer beware and all that. It may be simpler to find another workaround (which you have likely done by now).

Hm, I’ve been trying to code a button that can tint more than just one element. This is pretty easy to do for Select, Deselect and Click.

[SerializeField] private Image[] targetImages;
//Properties
public Image[] TargetImages{get{return targetImages;}}
 //Public Methods
public override void OnSelect(BaseEventData eventData)
{
     base.OnSelect(eventData);
     tintTargetImages(SelectionState.Selected);
 }
public override void OnDeselect(BaseEventData eventData)
{
      base.OnDeselect(eventData);
      tintTargetImages(SelectionState.Normal);
}
public override void OnSubmit(BaseEventData eventData)
{
      base.OnSubmit(eventData);
      tintTargetImages(SelectionState.Pressed);
}

But how do you do it for the non-interactable and the highlight phase of the button? Is there some overridable function, or Action I could use to do this?

What I would truly love to do is simply put a script on a button that is very simple and prints the SelectionState in the update method.
Through reflection:

using System.Reflection;
using UnityEngine;
using UnityEngine.UI;

public class SelectableStateReader : MonoBehaviour
{
    public Selectable AnySelectable;
    private PropertyInfo _selectableStateInfo = null;
 
    private void Awake()
    {
         _selectableStateInfo = typeof(Selectable).GetProperty("currentSelectionState", BindingFlags.NonPublic | BindingFlags.Instance);
    }

    private void Update()
    {
        Debug.Log(_selectableStateInfo.GetValue(AnySelectable));
    }

If you want to actually use the value you can do it like that:

private void Update()
{
    int selectableState = (int)_selectableStateInfo.GetValue(AnySelectable);
    switch (selectableState)
    {
        case 0:
             //Normal Selection State
             break;
         case 1:
             //Highlighted Selection State
             break;
          case 2:
             //Pressed Selection State
             break;
          case 3:
             //Selected Selection State
             break;
          case 4:
             //Disabled Selection State
             break;
    }
}

Works with anything inheriting from Selectable. Buttons, dropdowns, etc.

7 Likes

Thanks this worked for me with minor adjustments.

1 Like