Obtain GUI component within onValueChangedObservable

I want to add the same Observer to multiple GUI components’ onValueChangedObservable and have the Observer determine which component was used.

For example, two ColorPickers displayed, one each for diffuseColor and specularColor. Based on which color picker results in a valuechanged observable, then change a mesh’s diffuseColor or specularColor.

Currently, the Observer doesn’t pass any useful information in the EventState

On GUI components slider and ColorPicker. It would be helpful for it to pass the specific GUI component used in state.target or .currentTarget. Currently, those values are undefined.

I could then derive the attribute to change based on the component name.

Is there another built-in mechanism to obtain the component?

I tnought about adding a “pick” observer to capture the component, but not sure if that is the best (or workable) path.

Hi HiGreg, welcome, and thanks for posting your question!

Could you please provide a playground example of what you’re trying to do?


Version 2 gets property names from material itself:

I haven’t figured out scalable GUI, so some width/top values are hard coded.

Note the use of “lastGUIControl” being set by onPointer observables. I tried all the observables that seemed relevant. onPointerDown and onPointerEnter fired before onValueChanged. They also had usable state.target and .currentTarget values (what’s the difference?) and so were usable for capturing the specific picker control.

Edit to add: ideally, onValueChanged would supply EventState similar to onPointerDown. That is, onValueChanged would (should?) include target/currentTarget (whichever are appropriate) that indicates the picker control itself. Note that I also tried UI.onControlPickedObservable, but it fires after picker.onValueChanged.

I’m also not sure if my solution handles every case (such as keyboard navigation of controls). Overall, adding state.target / .currentTarget to onValueChanged would (in my opinion) be much more robust.

Gotcha, that was helpful, thanks!

Looking at the playground, I see you were trying to determine which material property to modify when you got value changed events, and worked around the problem by wiring up to the onPointerDownObservable and onPointerEnterObservable to set lastGUIControl.

During each loop, your code is creating a new function to pass into picker.onValueChangedObservable.add(), so there’s an opportunity to have each instance of that function know which material property it should write to. You can’t just do sphere.material[textureName+“Color”] because textureName is not scoped to the body of the loop, so each instance of your callback would use the same, shared, latest value (“ambient” in this case). To fix this, you can capture the current value of textureName before creating the callback each time you loop, and use that value instead inside the callback. Once you do that, you no longer need to try to capture the lastGUIControl, since each of the callbacks you created will just know which material property to write to.

Take a look at this version of the playground to see it in action: https://playground.babylonjs.com/#ZMLBW6#3

I hope that helps!

You’re absolutely right.

The playground I wrote didn’t match my words because I misunderstood the loop and the creation of a new observer instance.

In the newer playground below, I create static Observer functions. This exhibits the impact of the lack of state in onValueChanged and matches my intent.

What about passing in “textureName” to the onValueChanged observable (without reusing the function)?

I’m on my phone other wise I would make you a playground. You would get the texture name via “closure” and alter the correct property as you have done - as a computed property.

@HiGreg - thanks for the update - yes, I can see how it’d be helpful if state.target was consistently provided in the observable callbacks of the UI controls. I’m going to add that to the list of future improvements to consider, and in the meantime, I recommend having a different callback for each of the controls.

Thanks for reaching out!