Search Unity

Accessing the SelectionState of a button?

Discussion in 'Scripting' started by gavinthomasjones, Oct 21, 2020.

  1. gavinthomasjones

    gavinthomasjones

    Joined:
    Jun 21, 2013
    Posts:
    11
    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:

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

    I require the "Color Tint" transition type as referenced below:

    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.

    upload_2020-10-21_15-33-0.png

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

    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.
     

    Attached Files:

    flyhight1997 and DragonCoder like this.
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,674
  3. gavinthomasjones

    gavinthomasjones

    Joined:
    Jun 21, 2013
    Posts:
    11
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,674
    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. :)

    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.
     
  5. gavinthomasjones

    gavinthomasjones

    Joined:
    Jun 21, 2013
    Posts:
    11
    This feels like overkill to simply print a single number that is certainly stored somewhere already.
     
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,674
    Welcome to object oriented design.
     
  7. gavinthomasjones

    gavinthomasjones

    Joined:
    Jun 21, 2013
    Posts:
    11
    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.
     
    N3c4o0o likes this.
  8. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,908
    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:
    Code (CSharp):
    1. ButtonSelectionTracker : MonoBehaviour, ISelectHandler, IDeselectHandler {
    2.   public bool IsSelected { get; private set; } = false;
    3.  
    4.   OnSelect(BaseEventData data) {
    5.     IsSelected = true;
    6.   }
    7.  
    8.   OnDeselect(BaseEventData data) {
    9.     IsSelected = false;
    10.   }
    11. }
    You can then fire off your own C# event, or just have another script read IsSelected from this script.

    https://docs.unity3d.com/2017.4/Documentation/ScriptReference/UI.Selectable.OnSelect.html
    https://docs.unity3d.com/2017.4/Documentation/ScriptReference/UI.Selectable.OnDeselect.html
     
    mikeohc likes this.
  9. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,674
    You already went digging through the UI code... you posted some screencaps above.

    Did you notice this line?

    Code (csharp):
    1. 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:

    Code (csharp):
    1. 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!
     
    LilGames likes this.
  10. gavinthomasjones

    gavinthomasjones

    Joined:
    Jun 21, 2013
    Posts:
    11
    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.
     
  11. gavinthomasjones

    gavinthomasjones

    Joined:
    Jun 21, 2013
    Posts:
    11

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

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,908
    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.
     
  13. gavinthomasjones

    gavinthomasjones

    Joined:
    Jun 21, 2013
    Posts:
    11
    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.
     
  14. dylanfries

    dylanfries

    Joined:
    Jul 11, 2012
    Posts:
    16
    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).
     
  15. Omti1990

    Omti1990

    Joined:
    Jun 22, 2018
    Posts:
    9
    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.
    Code (CSharp):
    1.  
    2.  
    3. [SerializeField] private Image[] targetImages;
    4. //Properties
    5. public Image[] TargetImages{get{return targetImages;}}
    6.  //Public Methods
    7. public override void OnSelect(BaseEventData eventData)
    8. {
    9.      base.OnSelect(eventData);
    10.      tintTargetImages(SelectionState.Selected);
    11.  }
    12. public override void OnDeselect(BaseEventData eventData)
    13. {
    14.       base.OnDeselect(eventData);
    15.       tintTargetImages(SelectionState.Normal);
    16. }
    17. public override void OnSubmit(BaseEventData eventData)
    18. {
    19.       base.OnSubmit(eventData);
    20.       tintTargetImages(SelectionState.Pressed);
    21. }
    22.  
    23.  
    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?
     
  16. ArcticPinou

    ArcticPinou

    Joined:
    Dec 24, 2015
    Posts:
    11
    "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:

    Code (CSharp):
    1.  
    2. using System.Reflection;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5.  
    6. public class SelectableStateReader : MonoBehaviour
    7. {
    8.     public Selectable AnySelectable;
    9.     private PropertyInfo _selectableStateInfo = null;
    10.  
    11.     private void Awake()
    12.     {
    13.          _selectableStateInfo = typeof(Selectable).GetProperty("currentSelectionState", BindingFlags.NonPublic | BindingFlags.Instance);
    14.     }
    15.  
    16.     private void Update()
    17.     {
    18.         Debug.Log(_selectableStateInfo.GetValue(AnySelectable));
    19.     }
    20.  
    If you want to actually use the value you can do it like that:
    Code (CSharp):
    1.  
    2. private void Update()
    3. {
    4.     int selectableState = (int)_selectableStateInfo.GetValue(AnySelectable);
    5.     switch (selectableState)
    6.     {
    7.         case 0:
    8.              //Normal Selection State
    9.              break;
    10.          case 1:
    11.              //Highlighted Selection State
    12.              break;
    13.           case 2:
    14.              //Pressed Selection State
    15.              break;
    16.           case 3:
    17.              //Selected Selection State
    18.              break;
    19.           case 4:
    20.              //Disabled Selection State
    21.              break;
    22.     }
    23. }
    Works with anything inheriting from Selectable. Buttons, dropdowns, etc.
     
    Last edited: May 12, 2021
  17. jesusluvsyooh

    jesusluvsyooh

    Joined:
    Jan 10, 2012
    Posts:
    377
    Thanks this worked for me with minor adjustments.