Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Can't find Button.onCancel delegate?

Discussion in 'UGUI & TextMesh Pro' started by Senshi, Aug 26, 2014.

  1. Senshi

    Senshi

    Joined:
    Oct 3, 2010
    Posts:
    567
    Hey everyone,

    Does anyone know if Buttons have any way of binding an "onCancel" delegate, much like onClick? I couldn't find anything, but perhaps I'm missing something? I'd really like to refrain from having to create a custom Button class, just so I can override OnCancel() and call the appropriate method on my main script.

    Cheers,
    Patrick
     
  2. Tim-C

    Tim-C

    Unity Technologies

    Joined:
    Feb 6, 2010
    Posts:
    2,225
    Hi, we don't send any cancel events currently.
     
  3. Senshi

    Senshi

    Joined:
    Oct 3, 2010
    Posts:
    567
    Hmm, that stinks. Is there any specific rationale for this? If not, please consider this a feature request. =)
     
  4. Tim-C

    Tim-C

    Unity Technologies

    Joined:
    Feb 6, 2010
    Posts:
    2,225
    Considered and added to roadmap, but not for v1.
     
    Senshi likes this.
  5. Senshi

    Senshi

    Joined:
    Oct 3, 2010
    Posts:
    567
    That's good news regardless, thanks! To anyone finding this in the future and wanting the same, cancel events *are* sent, there just isn't a way to add listeners to it. A simple UnityEvent and overriding OnCancel() is enough. Here is an example class:

    Code (csharp):
    1. usingUnityEngine;
    2. usingUnityEngine.UI;
    3. usingUnityEngine.Events;
    4. usingUnityEngine.EventSystems;
    5.  
    6. public class CancelButton : Button, ICancelHandler{
    7.  
    8.     public UnityEvent onCancel { get; private set; }
    9.  
    10.     void Awake(){
    11.         onCancel = new UnityEvent();
    12.     }
    13.  
    14.     void ICancelHandler.OnCancel(BaseEventData eventData){
    15.         onCancel.Invoke();
    16.     }
    17. }
     
  6. ippdev

    ippdev

    Joined:
    Feb 7, 2010
    Posts:
    3,853
    What is the point of this? I can just set a Cancel button to use any function i want so I don't understand how to use this or what it would do that I can't do already without a two and a half backwards somersault.
     
  7. Senshi

    Senshi

    Joined:
    Oct 3, 2010
    Posts:
    567
    The advantage is seperation of code and less (cross)dependancies. For example, say you are creating a grid of 20 buttons through code. Whenever one of them is clicked, you want to call a function that does something based on which button you clicked. There are two ways of going about this. In psuedo-code:

    MainScript.cs
    Code (csharp):
    1. for(int i=0; i<20; i++){
    2.     GameObject btn = Instantiate(...);
    3.     btn.GetComponent<CustomButton>().SetID(i);
    4. }
    5.  
    6. public void ButtonCallback(int n){
    7.     Debug.Log(n + " clicked!");
    8. }
    CustomButtom.cs
    Code (csharp):
    1. MainScript m;
    2. int id;
    3.  
    4. void Awake(){
    5.     m = GameObject.Find("LevelManager").GetComponent<MainScript>();
    6. }
    7.  
    8. public void SetID(int newID){
    9.     id = newID;
    10. }
    11.  
    12. override void OnClick(){
    13.     m.ButtonCallback(id);
    14. }
    This requires the creation of an extra class, with its own behavior, yet still fully dependant on the MainScript. The alternative is simply this:

    MainScript.cs
    Code (csharp):
    1. for(int i=0; i<20; i++){
    2.     GameObject btn = Instantiate(...);
    3.     btn.GetComponent<CustomButton>().onClick.AddListener(delegate{ButtonCallback(i)});
    4. }
    5.  
    6. public void ButtonCallback(int n){
    7.     Debug.Log(n + " clicked!");
    8. }
    Now of course this is a simplified example, but the use-case here is for when a button needs to perform functionality dependant on/ originating in another script. In addition, you can add listeners from many scripts that the CustomButton will never even know about! Want an extra status light to blink on a click? Just add an extra listener! Considering this is the workflow for a lot of events in the new system already, adding an onCancel delegate is already a lot cleaner than going the first route. Even cleaner still though might be to add a custom EventTriger.Entry.eventID, but I haven't looked into whether or not that's possible yet. On the other hand, this way it lives right next to the onClick delegate, placing the submit and cancel events in the same place.

    Hope that explains my two and a half backwards somersault a bit. =)
     
  8. ippdev

    ippdev

    Joined:
    Feb 7, 2010
    Posts:
    3,853
    I just drop my script in the slot, point at the function from the Event menu and pass it the button. Seems alot simpler to me. Doesn't even take a line of code to accomplish except the script function. What I do with that is another logic animal altogether..so to speak. I can see why you are doing such and others who code in the same manner will find it useful. I just have different patterns I have been using for many years.
     
  9. Senshi

    Senshi

    Joined:
    Oct 3, 2010
    Posts:
    567
    Sure, if you're building your GUI in the editor you can bypass adding events through code altogether. =) However, there are use-cases where you want to build your UI dynamically - say, when adding a button for every empty server room available for instance.

    Also, for this particular use-case Cancel events are still not bindable, even through the editor, so you would have to use a script overriding ICancelHandler.OnCancel() either way. The approach to take when doing so, however, is of course entirely up to you and what best fits your situation.
     
  10. ippdev

    ippdev

    Joined:
    Feb 7, 2010
    Posts:
    3,853
    I am building sample libraries of hundreds of members from several resource folders. I have several thousand buttons on two panels at any given point. I just name the button in the Instantiation loop and query that when I fire the function which uses UI.Button or UI.Toggle as args.