therecipe / qt

Qt binding for Go (Golang) with support for Windows / macOS / Linux / FreeBSD / Android / iOS / Sailfish OS / Raspberry Pi / AsteroidOS / Ubuntu Touch / JavaScript / WebAssembly
GNU Lesser General Public License v3.0
10.45k stars 744 forks source link

making therecipe/qt accessible via webidl and web assembly wasm #622

Open omac777 opened 6 years ago

omac777 commented 6 years ago

therecipe/qt is my favourite gui api for golang by far.

I recently discovered golang wasm and having difficulty understanding how to build it into wasm and use it within the web browser.

So here is my change request: could you make a new target web assembly wasm and generate some webidl to expose it to other languages within the web browser?

There would be great value in this for software as a service. It would run on EVERYTHING with less issues. It would be easier to push newer versions of applications.

therecipe commented 6 years ago

Hey

I'm already working on this for the last few days now :) I tried go get it working with Go's experimental wasm support at first, but it seems like you can't callback into go code atm, so we will have to wait until this is possible to get a full wasm build.

But for now I got it working using gopherjs, which was created by the same guy who works on the wasm support for go (so I have high hopes that the transition to a full wasm build will be easy).

The current hybrid js/wasm target will be called "js" and once the go wasm callback issue is resolved the full wasm build target will be called "wasm".

I still have a lot to do to get the "js" target fully working, but I was already able to compile some projects without any changes at all. (only tested the widgets examples for now)

But in the end it should be possible to simply use qtdeploy -docker build js (or "wasm") like usual.

Here are some already working examples, if you are curious ;) line_edits.zip based on the line_edits example. goroutine.zip based on the goroutine example. jsinterop.zip bases on this:

package main

import (
    "os"
    "unsafe"

    "github.com/therecipe/qt/widgets"

    "github.com/gopherjs/gopherjs/js"
)

func main() {

    app := widgets.NewQApplication(len(os.Args), os.Args)

    window := widgets.NewQMainWindow(nil, 0)
    window.SetFixedSize2(250, 200)
    window.SetWindowTitle("jsinterop Example")

    widget := widgets.NewQWidget(nil, 0)
    widget.SetLayout(widgets.NewQVBoxLayout())
    window.SetCentralWidget(widget)

    js.Global.Set("goFunc", func(title string) {
        widgets.QMessageBox_Information(nil, "OK", title, widgets.QMessageBox__Ok, widgets.QMessageBox__Ok)
    })

    button := js.Global.Call("eval", `
            var button = Module.NewQPushButton2("start!", 0);
            var func = function(bool) { alert("clicked the button: " + button.__internal_object__.QAbstractButton.Text()); goFunc("fromJS"); };

            button.__internal_object__.QAbstractButton.ConnectClicked(func);

            button.__internal_object__.Pointer();
        `)

    widget.Layout().AddWidget(widgets.NewQPushButtonFromPointer(unsafe.Pointer(button.Unsafe())))

    window.Show()

    app.Exec()
}

(I also tried to get your advanced clock example working, but I haven't got the generic list/containers working yet)

But I also tested this example and it was useable, but there were frame drops to 30fps and also scrolling doesn't seem to work yet.

But who knows what time will bring, if they get QML working with webgl and go's wasm support isn't to slow, then this could turn out to be a viable alternative for developing web apps. I'm somehow dreaming from such an alternative since I got in contact with cappuccino some time ago. And I also already looked into Go's and Qt's nacl targets last year, but to no avail.

However, I will be on vacation for the next two weeks but if I find enough free time, then I will try to push the changes before June.

Also about webidl, the current approach I took doesn't utilize it (for binding c++ to js). But it should be possible to generate webidl spec files. I'm not really familiar with those though, in the end the use case then could look like this or ?

some other language <-> webidl <-> therecipe/qt (go/js) <-> therecipe/qt (c++/wasm)

because something like this:

some other language <-> webidl <-> therecipe/qt (c++/wasm)

would require the generation of additional runtime code in "some other language" also the "c" style api exposed by the wasm module isn't really pretty either.

You can already directly access the wasm api by using something like Module._QMainWindow_NewQMainWindow() or the go api exposed through gopherjs, by using something like Module.NewQMainWindow(). The wasm api returns a pointer, the gopherjs api returns an js object. (I tested it in the line_edits example)

therecipe commented 6 years ago

I added the initial js support with: https://github.com/therecipe/qt/commit/491342284b02f5b27d5a19d030e5e446727b464d

Most examples work, but it's still highly experimental. I haven't had time to optimize yet and I also had to use some ugly hacks to make it work for now. Therefor the examples all weight > 15mb (uncompressed) and take 5-10min to be deployed.

There is also some major issue when using more then one package that makes use of "moc" structures at the moment. This is because I made the wrong decision at the beginning about how to convert strings between emscripten and js, but I will fix this asap.

I also noticed that the wasm port now supports callbacks, but from a quick glance it seems like you can't directly return values from those callbacks so we will probably have to wait a bit longer for this to work.

After https://hub.docker.com/r/therecipe/qt/builds/bcqiccyrj8kel8xffux2dgm/ is being build (probably around 3-4h), you should be able to test it after pulling the js image with docker pull therecipe/qt:js

Here is the textedit example: textedit.zip