Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable async bind for Go #932

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 28 additions & 3 deletions examples/bind.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,32 @@
package main

import "github.com/webview/webview"
import (
"time"

"github.com/webview/webview"
)

const html = `<button id="increment">Tap me</button>
<div>You tapped <span id="count">0</span> time(s).</div>
<button id="compute">Compute</button>
<div>Result of computation: <span id="compute-result">0</span></div>
<script>
const [incrementElement, countElement] =
document.querySelectorAll("#increment, #count");
const [incrementElement, countElement, computeElement, computeResultElement] =
document.querySelectorAll("#increment, #count, #compute, #compute-result");
document.addEventListener("DOMContentLoaded", () => {
incrementElement.addEventListener("click", () => {
window.increment().then(result => {
countElement.textContent = result.count;
});
});
computeElement.addEventListener("click", () => {
computeElement.disabled = true;
window.compute(6, 7).then(result => {
console.log(result);
computeResultElement.textContent = result;
computeElement.disabled = false;
});
});
});
</script>`

Expand All @@ -33,6 +47,17 @@ func main() {
return IncrementResult{Count: count}
})

// A binding that performs a long-running computation and returns the result
w.Bind("compute", func(a, b int) chan webview.BindCallbackResult {
ch := make(chan webview.BindCallbackResult)
go func() {
defer close(ch)
time.Sleep(1 * time.Second)
ch <- webview.BindCallbackResult{Value: a * b, Error: nil}
}()
return ch
})

w.SetHtml(html)
w.Run()
}
74 changes: 58 additions & 16 deletions webview.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@ const (
HintMax = C.WEBVIEW_HINT_MAX
)

type BindCallbackResult struct {
// Error is an error returned by the callback. If error is not nil - the
// result of the callback is ignored.
Error error

// Value is a value returned by the callback. If error is not nil - the
// result of the callback is ignored.
Value interface{}
}

type WebView interface {

// Run runs the main loop until it's terminated. After this function exits -
Expand Down Expand Up @@ -108,7 +118,8 @@ type WebView interface {
// JavaScript function.
//
// f must be a function
// f must return either value and error or just error
// f can return either value and error or just error
// f can return a channel of BindCallbackResult as the value
Bind(name string, f interface{}) error
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really wanted to change this to something like f func(id string, params ...interface{}) but I'm not sure if that's any better to use.

}

Expand Down Expand Up @@ -222,21 +233,33 @@ func _webviewBindingGoCallback(w C.webview_t, id *C.char, req *C.char, index uin
m.Lock()
f := bindings[uintptr(index)]
m.Unlock()
jsString := func(v interface{}) string { b, _ := json.Marshal(v); return string(b) }
status, result := 0, ""
if res, err := f(C.GoString(id), C.GoString(req)); err != nil {
status = -1
result = jsString(err.Error())
} else if b, err := json.Marshal(res); err != nil {
status = -1
result = jsString(err.Error())
} else {
status = 0
result = string(b)
}
s := C.CString(result)
defer C.free(unsafe.Pointer(s))
C.webview_return(w, id, C.int(status), s)

// retain a reference to the id and params strings
reqId := C.GoString(id)
reqParams := C.GoString(req)

// run the callback in a separate goroutine
go func() {
jsString := func(v interface{}) string { b, _ := json.Marshal(v); return string(b) }

status, result := 0, ""
if res, err := f(reqId, reqParams); err != nil {
status = -1
result = jsString(err.Error())
} else if b, err := json.Marshal(res); err != nil {
status = -1
result = jsString(err.Error())
} else {
status = 0
result = string(b)
}

cId := C.CString(reqId)
defer C.free(unsafe.Pointer(cId))
s := C.CString(result)
defer C.free(unsafe.Pointer(s))
C.webview_return(w, cId, C.int(status), s)
}()
}

func (w *webview) Bind(name string, f interface{}) error {
Expand Down Expand Up @@ -287,6 +310,25 @@ func (w *webview) Bind(name string, f interface{}) error {
return nil, res[0].Interface().(error)
}
return nil, nil
} else if res[0].Type().Kind() == reflect.Chan {
if res[0].Type().Elem() != reflect.TypeOf(BindCallbackResult{}) {
return nil, errors.New("channel must be of type CallbackResult")
}

// Wait for the channel to receive a value
val, ok := res[0].Recv()

if !ok {
return nil, errors.New("channel closed")
}

callbackResult := val.Interface().(BindCallbackResult)

if callbackResult.Error != nil {
return nil, callbackResult.Error
}

return callbackResult.Value, nil
}
return res[0].Interface(), nil
case 2:
Expand Down
21 changes: 16 additions & 5 deletions webview_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,20 @@ func Example() {
w := New(true)
defer w.Destroy()
w.SetTitle("Hello")
w.Bind("noop", func() string {
w.Bind("noop", func(id string) {
log.Println("hello")
return "hello"
w.Return(id, StatusSuccess, "hello")
})
w.Bind("add", func(a, b int) int {
return a + b
w.Bind("add", func(id string, a, b int) {
w.Return(id, StatusSuccess, a+b)
})
w.Bind("subtract", func(a, b int) chan BindCallbackResult {
ch := make(chan BindCallbackResult)
go func() {
defer close(ch)
ch <- BindCallbackResult{Value: a - b, Error: nil}
}()
return ch
})
w.Bind("quit", func() {
w.Terminate()
Expand All @@ -31,7 +39,10 @@ func Example() {
console.log('noop res', res);
add(1, 2).then(function(res) {
console.log('add res', res);
quit();
subtract(5, 3).then(function(res) {
console.log('subtract res', res);
quit();
});
});
});
};
Expand Down