webui-dev / webui

Use any web browser or WebView as GUI, with your preferred language in the backend and HTML5 in the frontend, all in a lightweight portable lib.
https://webui.me
MIT License
2.36k stars 144 forks source link

Binding clarification/improvements #336

Open mitchcapper opened 3 months ago

mitchcapper commented 3 months ago

Thanks for such a smart concept of a project. I recently did a pr to update the nice C# implementation that @Juff-Ma put together to conform to more standardized c# constructs. I put together a more complex sample showing some of the features here.

The bind functionality is a bit interesting. With the same function being called to either attach to a click handler or to register a backer library bound JS function is is a bit unclear what the expected behavior is. I believe the wrapper interface (webuiinterface*) is to make it a bit easier than the non-interface ones. Still it sounds like webui_bind when called with the ID of something not in the DOM registers the function to webui.call("FuncName") and aliases of webui.FuncName and window.FuncName. Calling the interface version does not though, there we only see bound functions from webui.call("*"). Now it isn't hard to write similar wrappers although as this is likely functionality most libraries would want it might be better to implement that at a native library level. I did try with the nightly from a few weeks ago but seemed to get the same result.

While webui is almost by very nature async given it uses web sockets, the basic ABI binding is clearly not. It does almost require callback interop though so it isn't as if supporting some level of async wouldn't be possible. I think the place that may have the biggest benefit would be for bound call responses. There are already separate methods for setting the response, but right now they trigger as soon as the main event returns. Not a big deal but it would be nice if there was a deferral style option.

There are a few other things that would be nice too, another bound one is arg length. Without writing a proxy JS side for calls I don't see a way of determining how many args we actually get called with.

I think given the target as a UI frontend the other major one is just expanding from click events to any eventListener event. Again library authors can work around these things very easily with javascript and language based wrappers, but it is great how many languages WebUI supports and keeping implementation requirements by libs to a minimum probably helps keep it higher.

mitchcapper commented 3 months ago

I looked at doing a PR to add a deferral option for events but when investigating the relevant code realized the fact the code executes a singular event at a time and with the locks there wouldn't be a easy way for background handlers. Including even if holding onto the relevant info for afterwards (and either allowing the user to take a deferral or having a binding the user specifies they would always set the result when finished). While one could do those things, given the one event at a time until a result is returned would eliminate the benefits.

This isn't a huge deal for the .net library we can add our own bind function and relevant JS code and just handle the magic in JS land while allowing async actions in .net as well. Once we have a sample will post it back.

As a minor side note I assumed above that the normal bind (non interface) calls were properly binding functions into the global namespace in JS but they don't either.

AlbertShown commented 3 months ago

I believe the wrapper interface (webuiinterface*) is to make it a bit easier than the non-interface ones

Yes, webui_interface_xxx functions are simply a wrapper of webui APIs in a simplified format to make things easier for wrappers.

sounds like webui_bind when called with the ID of something not in the DOM registers the function to webui.call("FuncName")

This is in purpose, if the user do webui_bind("Foo"), it does not mean there is a DOM object called Foo, but simply he wants to make webui.call("Foo") available, therefor, webui will create Foo object, so Foo() will be available globally.

Calling the interface version does not though there we only see bound functions from webui.call("*")

Can you please clarify more this section?

It does almost require callback interop though so it isn't as if supporting some level of async wouldn't be possible

Do you mean webui.call('ID') . then ((response) => { ... });

determining how many args we actually get called

Good idea, we should add this option.

expanding from click events to any eventListener event

Can you please clarify more this section?

executes a singular event at a time and with the locks

The old versions of webui does not use locks, so all UI events gets fired right away, but this caused many wrappers to crash, like the V wrapper for example. Also makes webui.call() start to miss some events when it get called quickly, like 100 times per second (every 10ms)... so, adding locks, and fire one event at a time solved all those issues. But I see your point, probably we should add an option to make events fired simultaneously, this can be handy for some projects.

binding functions into the global namespace in JS but they don't either

Normally, webui_bind("Bar") will create a DOM object in global namespace window, and it can be used simply by Bar();.