forked from mbk-lab/rui_orig
2
0
Fork 0

Updated wasm support

This commit is contained in:
anoshenko 2022-11-01 20:13:09 +03:00
parent b4032b31e0
commit b26525e892
10 changed files with 271 additions and 270 deletions

View File

@ -200,7 +200,7 @@ func (app *application) startSession(params DataObject, events chan DataObject,
session := newSession(app, app.nextSessionID(), "", params)
session.setBrige(events, brige)
if !session.setContent(app.createContentFunc(session), session) {
if !session.setContent(app.createContentFunc(session)) {
return nil, ""
}

View File

@ -4,6 +4,7 @@ package rui
import (
_ "embed"
"strings"
"syscall/js"
)
@ -15,35 +16,88 @@ type wasmApp struct {
createContentFunc func(Session) SessionContent
session Session
brige webBrige
close chan DataObject
}
func (app *wasmApp) Finish() {
app.session.close()
}
func (app *wasmApp) startSession(this js.Value, args []js.Value) interface{} {
if app.createContentFunc == nil || len(args) == 1 {
return nil
}
func wasmLog(text string) {
js.Global().Call("log", text)
}
params := ParseDataText(args[0].String())
session := newSession(app, 0, "", params)
session.setBrige(make(chan DataObject), app.brige)
if !session.setContent(app.createContentFunc(session), session) {
return nil
}
func (app *wasmApp) handleMessage(this js.Value, args []js.Value) any {
if len(args) > 0 {
if obj := ParseDataText(args[0].String()); obj != nil {
switch command := obj.Tag(); command {
/*
case "startSession":
answer := ""
if session, answer = app.startSession(obj, events, brige); session != nil {
if !brige.writeMessage(answer) {
return
}
session.onStart()
go sessionEventHandler(session, events, brige)
}
app.session = session
case "reconnect":
if sessionText, ok := obj.PropertyValue("session"); ok {
if sessionID, err := strconv.Atoi(sessionText); err == nil {
if session = app.sessions[sessionID]; session != nil {
session.setBrige(events, brige)
answer := allocStringBuilder()
defer freeStringBuilder(answer)
answer := allocStringBuilder()
defer freeStringBuilder(answer)
session.writeInitScript(answer)
if !brige.writeMessage(answer.String()) {
return
}
session.onReconnect()
go sessionEventHandler(session, events, brige)
return
}
DebugLogF("Session #%d not exists", sessionID)
} else {
ErrorLog(`strconv.Atoi(sessionText) error: ` + err.Error())
}
} else {
ErrorLog(`"session" key not found`)
}
session.writeInitScript(answer)
answerText := answer.String()
answer := ""
if session, answer = app.startSession(obj, events, brige); session != nil {
if !brige.writeMessage(answer) {
return
}
session.onStart()
go sessionEventHandler(session, events, brige)
}
if ProtocolInDebugLog {
DebugLog("Start session:")
DebugLog(answerText)
case "disconnect":
session.onDisconnect()
return
case "session-close":
session.onFinish()
session.App().removeSession(session.ID())
brige.close()
*/
case "answer":
app.session.handleAnswer(obj)
case "imageLoaded":
app.session.imageManager().imageLoaded(obj, app.session)
case "imageError":
app.session.imageManager().imageLoadError(obj, app.session)
default:
app.session.handleEvent(command, obj)
}
}
}
return nil
}
@ -51,117 +105,75 @@ func (app *wasmApp) startSession(this js.Value, args []js.Value) interface{} {
func (app *wasmApp) removeSession(id int) {
}
func (app *wasmApp) createSession() Session {
session := newSession(app, 0, "", ParseDataText(js.Global().Call("sessionInfo", "").String()))
session.setBrige(app.close, app.brige)
session.setContent(app.createContentFunc(session))
return session
}
func (app *wasmApp) init() {
document := js.Global().Get("document")
body := document.Call("querySelector", "body")
script := document.Call("createElement", "script")
script.Set("type", "text/javascript")
script.Set("textContent", defaultScripts+wasmScripts)
body.Call("appendChild", script)
js.Global().Set("sendMessage", js.FuncOf(app.handleMessage))
app.close = make(chan DataObject)
app.session = app.createSession()
style := document.Call("createElement", "style")
css := appStyles + app.session.getCurrentTheme().cssText(app.session)
css = strings.ReplaceAll(css, `\n`, "\n")
css = strings.ReplaceAll(css, `\t`, "\t")
style.Set("textContent", css)
document.Call("querySelector", "head").Call("appendChild", style)
buffer := allocStringBuilder()
defer freeStringBuilder(buffer)
div := document.Call("createElement", "div")
div.Set("className", "ruiRoot")
div.Set("id", "ruiRootView")
viewHTML(app.session.RootView(), buffer)
div.Set("innerHTML", buffer.String())
body.Call("appendChild", div)
div = document.Call("createElement", "div")
div.Set("className", "ruiPopupLayer")
div.Set("id", "ruiPopupLayer")
div.Set("onclick", "clickOutsidePopup(event)")
div.Set("style", "visibility: hidden;")
body.Call("appendChild", div)
div = document.Call("createElement", "a")
div.Set("id", "ruiDownloader")
div.Set("download", "")
div.Set("style", "display: none;")
body.Call("appendChild", div)
}
// StartApp - create the new wasmApp and start it
func StartApp(addr string, createContentFunc func(Session) SessionContent, params AppParams) {
app := new(wasmApp)
app.params = params
app.createContentFunc = createContentFunc
SetDebugLog(wasmLog)
SetErrorLog(wasmLog)
if createContentFunc == nil {
return
}
app := new(wasmApp)
app.params = params
app.createContentFunc = createContentFunc
app.brige = createWasmBrige()
js.Global().Set("startSession", js.FuncOf(app.startSession))
/*
script := defaultScripts + wasmScripts
script = strings.ReplaceAll(script, "\\", `\\`)
script = strings.ReplaceAll(script, "\n", `\n`)
script = strings.ReplaceAll(script, "\t", `\t`)
script = strings.ReplaceAll(script, "\"", `\"`)
script = strings.ReplaceAll(script, "'", `\'`)
js.Global().Call("execScript", `document.getElementById('ruiscript').text += "`+script+`"`)
*/
document := js.Global().Get("document")
body := document.Call("querySelector", "body")
body.Set("innerHTML", `<div class="ruiRoot" id="ruiRootView"></div>
<div class="ruiPopupLayer" id="ruiPopupLayer" style="visibility: hidden;" onclick="clickOutsidePopup(event)"></div>
<a id="ruiDownloader" download style="display: none;"></a>`)
//js.Global().Call("execScript", "initSession()")
js.Global().Call("initSession", "")
//window.Call("execScript", "initSession()")
for true {
if message, ok := app.brige.readMessage(); ok && app.session != nil {
if ProtocolInDebugLog {
DebugLog(message)
}
if obj := ParseDataText(message); obj != nil {
switch command := obj.Tag(); command {
/*
case "startSession":
answer := ""
if session, answer = app.startSession(obj, events, brige); session != nil {
if !brige.writeMessage(answer) {
return
}
session.onStart()
go sessionEventHandler(session, events, brige)
}
case "reconnect":
if sessionText, ok := obj.PropertyValue("session"); ok {
if sessionID, err := strconv.Atoi(sessionText); err == nil {
if session = app.sessions[sessionID]; session != nil {
session.setBrige(events, brige)
answer := allocStringBuilder()
defer freeStringBuilder(answer)
session.writeInitScript(answer)
if !brige.writeMessage(answer.String()) {
return
}
session.onReconnect()
go sessionEventHandler(session, events, brige)
return
}
DebugLogF("Session #%d not exists", sessionID)
} else {
ErrorLog(`strconv.Atoi(sessionText) error: ` + err.Error())
}
} else {
ErrorLog(`"session" key not found`)
}
answer := ""
if session, answer = app.startSession(obj, events, brige); session != nil {
if !brige.writeMessage(answer) {
return
}
session.onStart()
go sessionEventHandler(session, events, brige)
}
case "disconnect":
session.onDisconnect()
return
case "session-close":
session.onFinish()
session.App().removeSession(session.ID())
brige.close()
*/
case "answer":
app.session.handleAnswer(obj)
case "imageLoaded":
app.session.imageManager().imageLoaded(obj, app.session)
case "imageError":
app.session.imageManager().imageLoadError(obj, app.session)
default:
app.session.handleEvent(command, obj)
}
}
}
}
app.init()
<-app.close
}
func FinishApp() {
@ -171,31 +183,3 @@ func FinishApp() {
func OpenBrowser(url string) bool {
return false
}
/*
func OpenBrowser(url string) bool {
var err error
switch runtime.GOOS {
case "linux":
for _, provider := range []string{"xdg-open", "x-www-browser", "www-browser"} {
if _, err = exec.LookPath(provider); err == nil {
if exec.Command(provider, url).Start(); err == nil {
return true
}
}
}
case "windows":
err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
case "darwin":
err = exec.Command("open", url).Start()
default:
err = fmt.Errorf("unsupported platform")
}
return err != nil
}
*/

View File

@ -14,6 +14,47 @@ window.onblur = function(event) {
sendMessage( "session-pause{session=" + sessionID +"}" );
}
function sessionInfo() {
const touch_screen = (('ontouchstart' in document.documentElement) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0)) ? "1" : "0";
var message = "startSession{touch=" + touch_screen
const style = window.getComputedStyle(document.body);
if (style) {
var direction = style.getPropertyValue('direction');
if (direction) {
message += ",direction=" + direction
}
}
const lang = window.navigator.language;
if (lang) {
message += ",language=\"" + lang + "\"";
}
const langs = window.navigator.languages;
if (langs) {
message += ",languages=\"" + langs + "\"";
}
const userAgent = window.navigator.userAgent
if (userAgent) {
message += ",user-agent=\"" + userAgent + "\"";
}
const darkThemeMq = window.matchMedia("(prefers-color-scheme: dark)");
if (darkThemeMq.matches) {
message += ",dark=1";
}
const pixelRatio = window.devicePixelRatio;
if (pixelRatio) {
message += ",pixel-ratio=" + pixelRatio;
}
return message + "}";
}
function getIntAttribute(element, tag) {
let value = element.getAttribute(tag);
if (value) {
@ -1276,6 +1317,10 @@ function startDowndload(url, filename) {
}
}
function setTitle(title) {
document.title = title;
}
function setTitleColor(color) {
var metas = document.getElementsByTagName('meta');
if (metas) {
@ -1292,6 +1337,10 @@ function setTitleColor(color) {
document.getElementsByTagName('head')[0].appendChild(meta);
}
function openURL(url) {
window.open(url, "_blank");
}
function detailsEvent(element) {
sendMessage("details-open{session=" + sessionID + ",id=" + element.id + ",open=" + (element.open ? "1}" : "0}"));
}
@ -1742,4 +1791,8 @@ function getPropertyValue(answerID, elementId, name) {
}
sendMessage('answer{answerID=' + answerID + ', value=""}')
}
function appendStyles(styles) {
document.querySelector('style').textContent += styles
}

View File

@ -26,44 +26,7 @@ window.onload = function() {
};
function socketOpen() {
const touch_screen = (('ontouchstart' in document.documentElement) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0)) ? "1" : "0";
var message = "startSession{touch=" + touch_screen
const style = window.getComputedStyle(document.body);
if (style) {
var direction = style.getPropertyValue('direction');
if (direction) {
message += ",direction=" + direction
}
}
const lang = window.navigator.language;
if (lang) {
message += ",language=\"" + lang + "\"";
}
const langs = window.navigator.languages;
if (langs) {
message += ",languages=\"" + langs + "\"";
}
const userAgent = window.navigator.userAgent
if (userAgent) {
message += ",user-agent=\"" + userAgent + "\"";
}
const darkThemeMq = window.matchMedia("(prefers-color-scheme: dark)");
if (darkThemeMq.matches) {
message += ",dark=1";
}
const pixelRatio = window.devicePixelRatio;
if (pixelRatio) {
message += ",pixel-ratio=" + pixelRatio;
}
sendMessage( message + "}" );
sendMessage( sessionInfo() );
}
function socketReopen() {

View File

@ -1,47 +1,8 @@
function initSession() {
const touch_screen = (('ontouchstart' in document.documentElement) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0)) ? "1" : "0";
var message = "sessionInfo{touch=" + touch_screen
const style = window.getComputedStyle(document.body);
if (style) {
var direction = style.getPropertyValue('direction');
if (direction) {
message += ",direction=" + direction
}
}
const lang = window.navigator.language;
if (lang) {
message += ",language=\"" + lang + "\"";
}
const langs = window.navigator.languages;
if (langs) {
message += ",languages=\"" + langs + "\"";
}
const userAgent = window.navigator.userAgent
if (userAgent) {
message += ",user-agent=\"" + userAgent + "\"";
}
const darkThemeMq = window.matchMedia("(prefers-color-scheme: dark)");
if (darkThemeMq.matches) {
message += ",dark=1";
}
const pixelRatio = window.devicePixelRatio;
if (pixelRatio) {
message += ",pixel-ratio=" + pixelRatio;
}
startSession( message + "}" );
function log(s) {
console.log(s);
}
window.onfocus = function(event) {
windowFocus = true
sendMessage( "session-resume{session=" + sessionID +"}" );

View File

@ -2,7 +2,6 @@ package rui
import (
"bytes"
"fmt"
"math/rand"
"net/http"
"os"
@ -24,7 +23,7 @@ func (session *sessionData) startDownload(file downloadFile) {
currentDownloadId++
id := strconv.Itoa(currentDownloadId)
downloadFiles[id] = file
session.runScript(fmt.Sprintf(`startDowndload("%s", "%s")`, id, file.filename))
session.runFunc("startDowndload", id, file.filename)
}
func serveDownloadFile(id string, w http.ResponseWriter, r *http.Request) bool {

View File

@ -67,7 +67,7 @@ type Session interface {
// Content returns the SessionContent of session
Content() SessionContent
setContent(content SessionContent, self Session) bool
setContent(content SessionContent) bool
// SetTitle sets the text of the browser title/tab
SetTitle(title string)
@ -91,6 +91,7 @@ type Session interface {
// OpenURL opens the url in the new browser tab
OpenURL(url string)
getCurrentTheme() Theme
registerAnimation(props []AnimatedProperty) string
resolveConstants(value string) (string, bool)
@ -245,10 +246,10 @@ func (session *sessionData) Content() SessionContent {
return session.content
}
func (session *sessionData) setContent(content SessionContent, self Session) bool {
func (session *sessionData) setContent(content SessionContent) bool {
if content != nil {
session.content = content
session.rootView = content.CreateRootView(self)
session.rootView = content.CreateRootView(session)
if session.rootView != nil {
session.rootView.setParentID("ruiRootView")
return true
@ -554,11 +555,11 @@ func (session *sessionData) handleEvent(command string, data DataObject) {
func (session *sessionData) SetTitle(title string) {
title, _ = session.GetString(title)
session.runScript(`document.title = "` + title + `";`)
session.runFunc("setTitle", title)
}
func (session *sessionData) SetTitleColor(color Color) {
session.runScript(`setTitleColor("` + color.cssString() + `");`)
session.runFunc("setTitleColor", color.cssString())
}
func (session *sessionData) RemoteAddr() string {
@ -570,5 +571,5 @@ func (session *sessionData) OpenURL(urlStr string) {
ErrorLog(err.Error())
return
}
session.runScript(`window.open("` + urlStr + `", "_blank");`)
session.runFunc("openURL", urlStr)
}

View File

@ -325,15 +325,16 @@ func (session *sessionData) SetLanguage(lang string) {
if lang != session.language {
session.language = lang
if session.rootView != nil {
if session.rootView != nil && session.brige != nil {
buffer := allocStringBuilder()
defer freeStringBuilder(buffer)
buffer.WriteString(`document.getElementById('ruiRootView').innerHTML = '`)
//buffer.WriteString(`document.getElementById('ruiRootView').innerHTML = '`)
viewHTML(session.rootView, buffer)
buffer.WriteString("';\nscanElementsSize();")
//buffer.WriteString("';\nscanElementsSize();")
session.runScript(buffer.String())
//session.runScript(buffer.String())
session.brige.updateInnerHTML("ruiRootView", buffer.String())
}
}
}

View File

@ -3,11 +3,10 @@
package rui
import (
"fmt"
"strconv"
"sync"
"syscall/js"
"github.com/gorilla/websocket"
)
type wasmBrige struct {
@ -18,11 +17,6 @@ type wasmBrige struct {
closed bool
}
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 8096,
}
func createWasmBrige() webBrige {
brige := new(wasmBrige)
brige.queue = make(chan string, 1000)
@ -30,23 +24,58 @@ func createWasmBrige() webBrige {
brige.answer = make(map[int]chan DataObject)
brige.closed = false
js.Global().Set("sendMessage", js.FuncOf(brige.sendMessage))
return brige
}
func (brige *wasmBrige) sendMessage(this js.Value, args []js.Value) interface{} {
if len(args) > 0 {
brige.queue <- args[0].String()
func (brige *wasmBrige) startUpdateScript(htmlID string) bool {
return false
}
func (brige *wasmBrige) finishUpdateScript(htmlID string) {
}
func (brige *wasmBrige) runFunc(funcName string, args ...any) bool {
if ProtocolInDebugLog {
text := funcName + "("
for i, arg := range args {
if i > 0 {
text += fmt.Sprintf(", `%v`", arg)
} else {
text += fmt.Sprintf("`%v`", arg)
}
}
DebugLog(text + ")")
}
return nil
js.Global().Call(funcName, args...)
return true
}
func (brige *wasmBrige) updateInnerHTML(htmlID, html string) {
brige.runFunc("updateInnerHTML", htmlID, html)
}
func (brige *wasmBrige) appendToInnerHTML(htmlID, html string) {
brige.runFunc("appendToInnerHTML", htmlID, html)
}
func (brige *wasmBrige) updateCSSProperty(htmlID, property, value string) {
brige.runFunc("updateCSSProperty", htmlID, property, value)
}
func (brige *wasmBrige) updateProperty(htmlID, property string, value any) {
brige.runFunc("updateProperty", htmlID, property, value)
}
func (brige *wasmBrige) removeProperty(htmlID, property string) {
brige.runFunc("removeProperty", htmlID, property)
}
func (brige *wasmBrige) close() {
}
func (brige *wasmBrige) readMessage() (string, bool) {
return <-brige.queue, true
return "", false
}
func (brige *wasmBrige) writeMessage(script string) bool {
@ -61,25 +90,36 @@ func (brige *wasmBrige) writeMessage(script string) bool {
return true
}
func (brige *wasmBrige) runGetterScript(script string) DataObject {
brige.answerMutex.Lock()
answerID := brige.answerID
brige.answerID++
brige.answerMutex.Unlock()
/*
func (brige *wasmBrige) runGetterScript(script string) DataObject {
brige.answerMutex.Lock()
answerID := brige.answerID
brige.answerID++
brige.answerMutex.Unlock()
answer := make(chan DataObject)
brige.answer[answerID] = answer
errorText := ""
answer := make(chan DataObject)
brige.answer[answerID] = answer
errorText := ""
js.Global().Set("answerID", strconv.Itoa(answerID))
js.Global().Set("answerID", strconv.Itoa(answerID))
window := js.Global().Get("window")
window.Call("execScript", script)
window := js.Global().Get("window")
window.Call("execScript", script)
result := NewDataObject("error")
result.SetPropertyValue("text", errorText)
delete(brige.answer, answerID)
return result
result := NewDataObject("error")
result.SetPropertyValue("text", errorText)
delete(brige.answer, answerID)
return result
}
*/
func (brige *wasmBrige) canvasTextMetrics(htmlID, font, text string) TextMetrics {
// TODO
return TextMetrics{}
}
func (brige *wasmBrige) htmlPropertyValue(htmlID, name string) string {
return ""
}
func (brige *wasmBrige) answerReceived(answer DataObject) {

View File

@ -175,7 +175,6 @@ func (brige *wsBrige) updateCSSProperty(htmlID, property, value string) {
} else {
brige.runFunc("updateCSSProperty", htmlID, property, value)
}
}
func (brige *wsBrige) updateProperty(htmlID, property string, value any) {