Open ogoffart opened 1 year ago
I think acceptance criteria for this should include the following:
The reason why I think we should offer a web component based API is because pyscript demonstrates beautifully how well this can be used to embed languages in the DOM.
Check out https://github.com/pyscript/pyscript/blob/main/docs/tutorials/getting-started.md#trying-before-installing for an example how simple and seamless this can be.
See for the HTMLElement subclass that implements the pyscript tag: https://github.com/pyscript/pyscript/blob/main/pyscriptjs/src/components/pyscript.ts#L10
See how this is registered: https://github.com/pyscript/pyscript/blob/a6280268383d92fe180f32b500a7829aeac7f877/pyscriptjs/src/components/elements.ts#L16
That's using window.customElements: https://developer.mozilla.org/en-US/docs/Web/API/Window/customElements
We discussed different options on how to approach this. We identified three different entry points for how to use Slint in a JavaScript environment:
<slint>
tagAll three should have the same JavaScript API to modify properties, and set and invoke callbacks.
Major differences between these three:
The JS API should offer access to all properties as "native" JavaScript properties via getters/setters. To solve the situation where a property <bool> hide;
in Slint might clash with a function hide()
we may want to provide, we also provide string based access to properties and callbacks:
function set_property(name: string, value: any) {}
function get_property(name: string): any {}
function set_callback(name: string, cb: (...args: any[]) => any) {
function invoke_callback(name: string, ...args: any[]): any
For the first two entry points, we want to offer an API for dynamically compiling a .slint
file and instantiating exported components:
let slint = ...; //
let component = await slint.build_component_from_string("export App := Window { ... }");
let component = await slint.build_component_from_url("https://.../foo.slint");
let instance = component.create();
(Those names taken from the Rust/C+ interpreter API, maybe we should rethink them?)
I'll post comments with ideas for the three different approaches.
The existing API to compile a .slint
file and instantiate a component looks like this:
let slint = require("slint-ui");
require("slint-ui");
let ui = require("../ui/main.slint");
let main = new ui.Main();
main.run();
We may want to reconsider this approach. In any case, we want to offer the aforementioned API for loading files dynamically.
<slint>
TagLet the example code speak for itself :-)
<script src="slint.js"></script>
<slint id="some_id">
export App := Window {
callback launch-rocket();
property <color> rocket-color;
...
}
</slint>
<script>
let app = document.querySelector("slint#some_id");
// when is app ready? We need a promise to wait for
// maybe custom element API offers a solution
await app.init();
app.on_launch_rocket = () => {
};
app.rocket_color = "red";
// No run() needed
// No show() needed??
});
</script>
export class SlintTag inherits HTMLElement {
constructor() {
let source = somehow_unescape(this.innerHTML);
this.innerHTML = "<canvas>";
let ui = slint.load_from_source(source);
let app = new ui.__root();
app.show(this.querySelector("canvas"));
// .. add properties and callback to this ..
}
function set_property() {}
function get_propert() {}
/// static api here
}
<script src="slint.js"></script>
<script>
let ui = await slint.load_from_source("MyWindow := ...");
// let ui = await slint.load_from_url("...");
let main = new ui.Main();
main.show(document.getElementById("canvas"))
//slint.run_event_loop(); <-- no need (we do it internally)
// Throw exception when already shown:
try { main.show(document.getElementById("othercanvas")); }
main.hide(); // <-- now it can be shown again
main.foo = 42;
let main = new ui();
main.show(...);
main.inner.rocket_color = "red";
</script>
<canvas id="canvas">
We want to have a tab in the online editor that allows scripting the design. There are three different ways of evaluating such a script:
<iframe>
.eval()
.The first two approaches require wrapping the source in data urls / blobs.
The iframe
approach may be more invasive in the editor as each script change has to replace the iframe. The web worker approach perfectly isolates, but adds the complexity of the asynchronous worker message passing API (unclear how to solve this well).
We currently have the wasm interpreter that we use in the documentation and the online editor. We should add API so that we can also set/get properties and callback from JS on the browser