Author Topic: Using EventDelegate in my scripts and have inspector work?  (Read 8670 times)

wom

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 34
    • View Profile
Using EventDelegate in my scripts and have inspector work?
« on: March 12, 2014, 01:35:57 AM »
I'd like to use List<EventDelegate> in my own scripts and hook them up with the inspector, like the onClick events for UIButton etc.

If I declare a field of type List<EventDelegate> on my own scripts - the inspector just shows the usual collection interrface in the editor - which is no good for hooking up to arbitrary methods like NGUI components can.

I'm going to try implementing a PropertyDrawer to call NGUIEditorTools.DrawEvents() but not sure if I'm barking up the wrong tree or what - has anyone done this before?

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Using EventDelegate in my scripts and have inspector work?
« Reply #1 on: March 13, 2014, 12:20:34 AM »
The problem with property drawers is that they are for specific types. Ie: you create a property drawer for class ABC. The event delegate list is just that though -- a list. Is there a way to specify that a specific field should be using a specific property drawer?

wom

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 34
    • View Profile
Re: Using EventDelegate in my scripts and have inspector work?
« Reply #2 on: March 13, 2014, 01:58:25 AM »
The event delegate list is just that though -- a list. Is there a way to specify that a specific field should be using a specific property drawer?

Yep - the generic list didn't work when I tried it. 
There is also the ability to specify "draw this field with that property drawer" by annotating your script field with an attribute.  But that didn't work either.
Probably because List isn't Serializable? I dunno, whatever.

So I made concrete serializable class:
  1. [System.Serializable]
  2. public class EventDelegateList : List<EventDelegate> {
  3.   // nothing here
  4. }
  5.  

That serves to get the PropertyDrawer infrastructure going, but then I couldn't easily get the NGUIEditorTools drawing code to work when called from a PropertyDrawer.  It might be easy, I dunno - I think I'm just going to register my events programmatically and move on with my stuff.

wom

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 34
    • View Profile
Re: Using EventDelegate in my scripts and have inspector work?
« Reply #3 on: March 13, 2014, 04:32:08 AM »
For anyone else looking to do similar, I ended up using a free extension from the asset store: Editor Delegates
Delegates with parameters!  I wonder if that causes allocation though....

AtomicBob

  • Jr. Member
  • **
  • Thank You
  • -Given: 3
  • -Receive: 0
  • Posts: 69
    • View Profile
Re: Using EventDelegate in my scripts and have inspector work?
« Reply #4 on: April 24, 2014, 11:56:14 AM »
I'm poking around with exactly what wom wanted to do in the OP. I'm using a List<EventDelegate> in a custom class that I want to draw in the same way NGUI does. This is actually accomplished quite easily with PropertyDrawers. ArenMook, to answer your question: yes, you can decorate individual fields with an attribute that tells it to use a specific PropertyDrawer. That's exactly what they're for. You don't have to create them on a class-by-class basis.

All that's missing for this to work is a variant of NGUIEditorTools.DrawEvents that can be used by PropertyDrawers (i.e. it can't use GUILayout and EditorGUILayout).

Here's what's need. 1) an attribute we can decorate List<EventDelegate> with:

  1. public class EventDelegateAttribute : PropertyAttribute
  2. {
  3.         public readonly string name;
  4.  
  5.         public EventDelegateAttribute ( string name )
  6.         {
  7.                 this.name = name;
  8.         }
  9. }
  10.  

2) a PropertyDrawer for the above attribute:

  1. [CustomPropertyDrawer(typeof(EventDelegateAttribute))]
  2. public class EventDelegateDrawer : PropertyDrawer
  3. {
  4.         public override float GetPropertyHeight ( SerializedProperty prop, GUIContent label )
  5.         {
  6.                 return NGUIEditorTools.GetEventsDrawerPropertyHeight(prop, label);
  7.         }
  8.  
  9.         public override void OnGUI ( Rect position, SerializedProperty prop, GUIContent label )
  10.         {
  11.                 NGUIEditorTools.DrawEventsPropertyDrawer(position, prop, label);
  12.         }
  13. }
  14.  

3) NGUI methods for drawing the EventDelegate editor in a PropertyDrawer (that part that's missing):

  1. public partial class NGUIEditorTools
  2. {
  3.         static public float GetEventsDrawerPropertyHeight ( SerializedProperty prop, GUIContent label )
  4.         {
  5.                 //Calculate the space needed for the PropertyDrawer
  6.         }
  7.  
  8.         static public void DrawEventsPropertyDrawer ( Rect position, SerializedProperty prop, GUIContent label )
  9.         {
  10.                 UnityEngine.Object undoObject = prop.serializedObject.targetObject;
  11.                 //'prop' is the SerializePoperty of type List<EventDelegate>
  12.  
  13.                 //Use GUI and EditorGUI methods to draw the events inside 'position'
  14.         }
  15. }
  16.  

Then, when we want to use it, all we have to do is:

  1. [EventDelegate("On Click")]
  2. [SerializeField] private List<EventDelegate> onClick = new List<EventDelegate>;
  3.  

and that even applies to custom classes. I tested it in on of my own:

  1. public class GameScreen : MonoBehaviour
  2. {
  3.         [Serializable] public class NavButtonSettings
  4.         {
  5.                 [HideInInspector] public bool isHidden = false;
  6.                 public bool isClickable = true;
  7.                 public UINavButton type;
  8.                 public UINavButtonOverlay overlay;
  9.                 public Justification overlayJustification = Justification.Center;
  10.                 public string text;
  11.                 public Justification textJustification = Justification.Center;
  12.  
  13.                 [EventDelegate("On Click")]
  14.                 public List<EventDelegate> onClick = new List<EventDelegate>();
  15.         }
  16.  
  17.         /* Will be drawn exactly as you would expect. All the fields except
  18.          * 'onClick' draw in their normal way, but when it gets to 'onClick' the
  19.          * EventDelegateDrawer will be used. Hooray!
  20.          */
  21.         [SerializeField] private List<NavButtonSettings> btnSettings = new List<NavButtonSettings>;
  22. }
  23.  

90% of the actual work needed is just the drawing methods, which can mostly be copied from the existing ones with the *Layout methods replaced. I don't know how interested you'd be in this, ArenMook, but I think it'd be a really cool feature.

I'm in a time-crunch at the moment, so I'm just going to slap together half-assed versions of the 2 needed drawing methods for now. If this get's added later, I'll swap to the built in methods.

AtomicBob

  • Jr. Member
  • **
  • Thank You
  • -Given: 3
  • -Receive: 0
  • Posts: 69
    • View Profile
Re: Using EventDelegate in my scripts and have inspector work?
« Reply #5 on: April 24, 2014, 06:55:01 PM »
I got this working in a way I'm quite happy with. It's a barebones implementation, but it works. I'd post it here but I had to copy some of the code for drawing the EventDelegate editor so I'll email it instead.

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Using EventDelegate in my scripts and have inspector work?
« Reply #6 on: April 25, 2014, 09:11:23 AM »
Yup, thanks, it will be in the next update.

bummzack

  • Newbie
  • *
  • Thank You
  • -Given: 1
  • -Receive: 0
  • Posts: 1
    • View Profile
Re: Using EventDelegate in my scripts and have inspector work?
« Reply #7 on: May 12, 2014, 04:15:05 AM »
Yup, thanks, it will be in the next update.

I saw that this is in the 3.5.9 release-notes. Is there a guide how to use this feature?
The following code doesn't seem to work ('EventDelegate': is not an attribute class).

  1. [EventDelegate("On Click")]
  2. [SerializeField] private List<EventDelegate> onClick = new List<EventDelegate>();
« Last Edit: May 12, 2014, 04:22:15 AM by bummzack »

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Using EventDelegate in my scripts and have inspector work?
« Reply #8 on: May 12, 2014, 04:06:41 PM »
The update added the ability to draw a single event delegate, not a list of delegates. I also don't advise using it as it's limited to functions without any parameters as Unity isn't able to serialize certain values properly via property drawers. For a list and for functions that have parameters, create an editor class for your custom class that would draw it via
  1. NGUIEditorTools.DrawEvents("Event Name", yourScript, yourScript.yourEventList);

Nihily

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 1
  • Posts: 1
    • View Profile
Re: Using EventDelegate in my scripts and have inspector work?
« Reply #9 on: August 09, 2014, 04:21:42 AM »
Using an EventDelegate in an dynamic array in the Inspector, I couldn't used directly a variable to give to the function "NGUIEditorTools.DrawEvents()", I had to use the SerializedProperties.
Besides, create an new editor script for each script that use an EventDelegate is a big waste of time.

So, I followed AtomicBob instructions and started from that. I admit it's not beautiful, but that can be okay for most of people (at least, me).

Since I don't know how is done the EventDelegate class, I'm not sure that all the intern variables are set correctly ! So it may have bugs.
However, I tested it and it seems to work (in Editor and outside). The GUI.changed is also update correctly.

So, you just have to add to :

  1. [EventDelegateAttribute]
  2. public EventDelegate    evt;
  3.  

You can't use List<EventDelegate> but EventDelegate[] should be okay.

A preview and the unitypackage are available enclosed.
Hoping that it will help.

Edit : little update that place more correctly the "-" next to "Notify" according to the indent level.
« Last Edit: August 09, 2014, 08:36:42 AM by Nihily »

lermy3d

  • Newbie
  • *
  • Thank You
  • -Given: 1
  • -Receive: 0
  • Posts: 1
    • View Profile
Re: Using EventDelegate in my scripts and have inspector work?
« Reply #10 on: November 17, 2014, 01:07:47 PM »
Hi guys, this post has been REALLY useful! I would like to thank Nihily for his post. His solution is really good and it was very helpfull.

I also had the idea to re use the EventDelegate in my scripts and when last week I was up to this I realize that the EventDelegateDrawer class in the NGUI v3.7.5 wasn't allowing to assign the function parameters. Thanks to Nihily's post I download the EventDelegateHelper and I updated the built-in EventDelegateDrawer and now it runs smoothly when using EventDelegate in your own scripts. I didn't add the remove button since you can just press 'del' on your keyboard.

I also made a little update to the EventDelegateHelper to improve the position of the remove button when index level was very deep in class hierarchy. And remove the partial class declaration since it was being invasive with NGUI code because you needed to declare EventDelegateHelper as a partial class also inside NGUI, which was not a neat way since every time you update ngui you will have some errors.

So now both codes can now co-exist independently, I'm uploading this updates so you can use either solution. Hope it helps! Thank you all!
« Last Edit: September 03, 2015, 08:19:02 AM by lermy3d »