Search Unity

Synthesize events?

Discussion in 'UI Toolkit' started by Timboc, Mar 20, 2020.

  1. Timboc

    Timboc

    Joined:
    Jun 22, 2015
    Posts:
    238
    Is it possible to broadcast an event from a PropertyDrawer? So far I've had no luck.
    The docs (https://docs.unity3d.com/2019.3/Documentation/Manual/UIE-Events-Synthesizing.html) are a bit out of date but easy enough to get compiling. So far I've tried:
    Code (CSharp):
    1. using (ChangeEvent<int> refreshEvent = ChangeEvent<int>.GetPooled(0, 1))
    2. {
    3.         root.SendEvent(refreshEvent);
    4. }
    and even
    Code (CSharp):
    1. var evt = new Event()
    2. {
    3.       type = EventType.KeyDown,
    4.       keyCode = KeyCode.X,
    5.       character = 'x',
    6.       modifiers = EventModifiers.FunctionKey
    7. };
    8. using (KeyDownEvent keyDownEvent = KeyDownEvent.GetPooled(evt))
    9. {
    10.       root.SendEvent(keyDownEvent);
    11. }
    In this case root is a VisualTreeAsset CloneTree() however I've tried sending from various elements.
    Then listening in an Editor that has a ProperyField via e.g.
    Code (CSharp):
    1. root.Query<TemplateContainer>().ForEach(ve =>
    2. {
    3.    ve.RegisterCallback<KeyDownEvent>(kde => Debug.LogAssertion("RECEIVED! " + kde.ToString()));
    4.    ve.RegisterCallback<ChangeEvent<int>>(ci => Debug.LogAssertion("RECEIVED! " + ci.ToString()));
    5. });
    I've also tried executing after a delay but nothing I'm trying is working. Am I missing something or this unsupported?

    Many thanks in advance.
     
  2. Timboc

    Timboc

    Joined:
    Jun 22, 2015
    Posts:
    238
    @uDamian when you get a chance, any insight? Trying to keep this as a PropertyDrawer - would be good to know if sending events from them is theoretically possible. Otherwise I need to do this via static methods I guess?
     
  3. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,231
    Haven't had the chance to play with this part of UI Toolkit lately but a few things that come to mind:

    1. Try setting a
    target
    for your event to the element you want to get it and see if that works

    2. Make sure the control/field you want to get the event has focus. If you just send events to the root, they don't really know where to go.

    3. Use the Window > Analysis > UIElements Events Debugger. You need to enable Dev mode (open About window and type "internal" blindly) to see that tool in the menu. It's a bit rough but it should hopefully shed some light on where your events are going. They might also be eaten by any element below or above the ones you registered on (and propagation stopped). This tool should tell you if that happens.
     
    Timboc likes this.
  4. Timboc

    Timboc

    Joined:
    Jun 22, 2015
    Posts:
    238
    Thanks @uDamian, really appreciate the reply. I've attached an image of the tool I'm building which I hope helps clarify my issue. The pictured EditorWindow represents the state of (is bound to) a ScriptableObject. In the following image after selecting a 'clip' a preview panel (circled in blue) gets constructed and is bound to the corresponding serializedProperties of the asset (an element of an array).
    Upon construction (after layout) I do the following:
    Code (CSharp):
    1. tweenSettingsPanel.Query<BindableElement>().ForEach(x =>
    2.                {
    3.                    x.RegisterCallback<ChangeEvent<object>>(f => DoStuff() });
    4.                    x.RegisterCallback<ChangeEvent<int>>(f => DoStuff() });
    5.                    x.RegisterCallback<ChangeEvent<uint>>(f => DoStuff() });
    6.                    x.RegisterCallback<ChangeEvent<Enum>>(f => DoStuff() });
    7.                    x.RegisterCallback<ChangeEvent<string>>(f => DoStuff() });
    8.                    x.RegisterCallback<ChangeEvent<float>>(f => DoStuff() });
    9.                    x.RegisterCallback<ChangeEvent<Vector2>>(f => DoStuff() });
    10.                    x.RegisterCallback<ChangeEvent<Vector3>>(f => DoStuff() });
    11.                    x.RegisterCallback<ChangeEvent<Vector4>>(f => DoStuff() });
    12.                });
    This works for most things except e.g. the 'Target' shown here which is a PropertyField with a corresponding PropertyDrawer. So I'm unclear on how I would set the target or focus for the event given the property drawer has no knowledge/context of the editor window it's displayed in.

    I could e.g. set the target to the root:
    Code (CSharp):
    1. using (ChangeEvent<int> refreshEvent = ChangeEvent<int>.GetPooled(0, 1))
    2. {
    3.         refreshEvent.target = root;
    4.         root.SendEvent(refreshEvent);
    5. }
    but I assume that wouldn't help anything.

    Am I misunderstanding something or just trying to apply the event paradigm in an unconventional way? Many thanks!!
    upload_2020-4-2_16-40-17.png
     
  5. Timboc

    Timboc

    Joined:
    Jun 22, 2015
    Posts:
    238
    Err wait.. I may have just been being dumb - I think if I dispatch it from a BindableElement within the PropertyDrawer (rather than the root itself), it now works. Which is obvious as I'm only registering with BindableElements.
     
  6. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,231
    Note that events propagate up so you should not register all those ChangeEvents on every BindableElement. Instead, register for events at a common parent (root) element, like the inspector container, and then use the ChangeEvent.target to know which field (see its bindingPath) has changed.
     
    Catsoft-Studios and Timboc like this.