Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

bindingPath

Discussion in 'UI Toolkit' started by Devi-User, Nov 30, 2018.

  1. Devi-User

    Devi-User

    Joined:
    Apr 30, 2016
    Posts:
    61
    I tried to use bindingPath to bind this to a variable. However, I found that binding is always associated with SerializedObjects. Does this really pay off? I could not even bind to EditorWindow after element.Bind (new SerializedObject (window))
    As a whole, I am very confused that we are attached to Unity objects. In fact, this means that I cannot change an object variable without first creating a personal UnityEngine.Object for it. Horror!
    his actually completely negates the creation of a normal architecture in the editor. Maybe someone has already come up with a good practice how to work with it? I thought about container objects but ScriptableObject does not support Generic types. Moreover, it will not allow drawing something non-serializable since the object simply will not have a unityPath.

    Perhaps there is any other approach to binding?
    Initially, I remember ChangeEvent <float> but it confuses me a bit.
    I would like to notify elements about field changes without a specific update call for each element.
    So for example, if I use Slider and FloatField in a pair, I want to see them synchronized. I could call some UI.UpdateValue (id) on the property being changed, but I wouldn’t want to do anything more than this, without specific calls slider.value = value + floatfield.value = value.
    Such a solution, I think, can be done manually, but is there something similar in UIElements?
     
  2. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,231
    Bindings always comes down to the ability to detect changes in your chosen data model. Plain structs and classes that are not serialized by Unity can have their fields changed at any time with no mechanism for change detection.

    This means the only way to "bind" to this type of data is via polling. You can do that as well in UIElements using the scheduler, as shown here (line 164):
    https://github.com/Unity-Technologi.../Assets/QuickIntro/Editor/BasicsDemoWindow.cs

    Video explanation here (at 15:38):


    Going the opposite way (change in field changes the data), you'll have to use the ChangeEvent. That's also explained in the code and video above.

    The binding system in UIElements is not, at its core, tied to UnityEngine.Object. You can create a custom binding system using public APIs that can bind to other types of data, but you'll be mostly on your own.

    We focused on making binding to SerializedObjects because that is the current bindable data type in Unity. It's how you bind IMGUI controls to data and it's also the data model (as MonoBehaviours) for uGUI.

    As other data models become available, we've left room in the architecture to add bindings support for them.
     
    Devi-User likes this.
  3. Devi-User

    Devi-User

    Joined:
    Apr 30, 2016
    Posts:
    61
    As a result, I came to my mind, using what you said.

    Code (CSharp):
    1.  
    2. public static BaseField<T> RegisterGetSetCallbacks<T>(this BaseField<T> field, Func<T> getter, Action<T> setter)
    3. {
    4.     field.OnValueChanged(s => { setter(s.newValue); });
    5.     field.RegisterCallback<BlurEvent>(evt => { field.value = getter(); }, TrickleDown.TrickleDown);
    6.  
    7.     var fieldValue = getter();
    8.     field.value = fieldValue;
    9.     setter(fieldValue);
    10.  
    11.     field.schedule.Execute(() =>
    12.     {
    13.         if (field.focusController.focusedElement != field)
    14.         {
    15.             var val = getter();
    16.             field.value = val;
    17.         }
    18.     }).Every(100);
    19.     return field;
    20. }
    example:
    Code (CSharp):
    1. zoomField.RegisterGetSetCallbacks(() => zoom,
    2.                 value =>
    3.                 {
    4.                     zoom = Mathf.Clamp(value, .5f, 10f);
    5.                     UpdateZoom();
    6.                 });
    It confuses me that it allocates, but as long as there are few elements, this is not critical.
     
    Last edited: Nov 30, 2018
  4. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,231
    Yep, that works. If it proves popular/useful, we can eventually add this helper in the core API.

    You could optimize it a bit by not creating a scheduled task per element but aggregating a polling task for a bunch of elements. You can further eliminate some allocations by replacing your ChangeEvent lambda with a proper method.