Optimisation

This commit is contained in:
anoshenko 2022-10-29 20:16:40 +03:00
parent 4523a9a427
commit d096a35af9
22 changed files with 166 additions and 137 deletions

View File

@ -4,7 +4,6 @@ package rui
import ( import (
_ "embed" _ "embed"
"strings"
"syscall/js" "syscall/js"
) )
@ -22,40 +21,6 @@ func (app *wasmApp) Finish() {
app.session.close() app.session.close()
} }
/*
func (app *wasmApp) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if ProtocolInDebugLog {
DebugLogF("%s %s", req.Method, req.URL.Path)
}
switch req.Method {
case "GET":
switch req.URL.Path {
case "/":
w.WriteHeader(http.StatusOK)
io.WriteString(w, app.getStartPage())
case "/ws":
if brige := CreateSocketBrige(w, req); brige != nil {
go app.socketReader(brige)
}
default:
filename := req.URL.Path[1:]
if size := len(filename); size > 0 && filename[size-1] == '/' {
filename = filename[:size-1]
}
if !serveResourceFile(filename, w, req) &&
!serveDownloadFile(filename, w, req) {
w.WriteHeader(http.StatusNotFound)
}
}
}
}
*/
func (app *wasmApp) startSession(this js.Value, args []js.Value) interface{} { func (app *wasmApp) startSession(this js.Value, args []js.Value) interface{} {
if app.createContentFunc == nil || len(args) == 1 { if app.createContentFunc == nil || len(args) == 1 {
return nil return nil
@ -99,16 +64,16 @@ func StartApp(addr string, createContentFunc func(Session) SessionContent, param
app.brige = createWasmBrige() app.brige = createWasmBrige()
js.Global().Set("startSession", js.FuncOf(app.startSession)) js.Global().Set("startSession", js.FuncOf(app.startSession))
script := defaultScripts + wasmScripts /*
script = strings.ReplaceAll(script, "\\", `\\`) script := defaultScripts + wasmScripts
script = strings.ReplaceAll(script, "\n", `\n`) script = strings.ReplaceAll(script, "\\", `\\`)
script = strings.ReplaceAll(script, "\t", `\t`) script = strings.ReplaceAll(script, "\n", `\n`)
script = strings.ReplaceAll(script, "\"", `\"`) script = strings.ReplaceAll(script, "\t", `\t`)
script = strings.ReplaceAll(script, "'", `\'`) script = strings.ReplaceAll(script, "\"", `\"`)
script = strings.ReplaceAll(script, "'", `\'`)
//window := js.Global().Get("window") js.Global().Call("execScript", `document.getElementById('ruiscript').text += "`+script+`"`)
//window.Call("execScript", `document.getElementById('ruiscript').text = "`+script+`"`) */
js.Global().Call("execScript", `document.getElementById('ruiscript').text += "`+script+`"`)
document := js.Global().Get("document") document := js.Global().Get("document")
body := document.Call("querySelector", "body") body := document.Call("querySelector", "body")
@ -116,7 +81,8 @@ func StartApp(addr string, createContentFunc func(Session) SessionContent, param
<div class="ruiPopupLayer" id="ruiPopupLayer" style="visibility: hidden;" onclick="clickOutsidePopup(event)"></div> <div class="ruiPopupLayer" id="ruiPopupLayer" style="visibility: hidden;" onclick="clickOutsidePopup(event)"></div>
<a id="ruiDownloader" download style="display: none;"></a>`) <a id="ruiDownloader" download style="display: none;"></a>`)
js.Global().Call("execScript", "initSession()") //js.Global().Call("execScript", "initSession()")
js.Global().Call("initSession", "")
//window.Call("execScript", "initSession()") //window.Call("execScript", "initSession()")
for true { for true {

View File

@ -1161,6 +1161,12 @@ function blur(elementId) {
} }
} }
function blurCurrent() {
if (document.activeElement != document.body) {
document.activeElement.blur();
}
}
function playerEvent(element, tag) { function playerEvent(element, tag) {
//event.stopPropagation(); //event.stopPropagation();
sendMessage(tag + "{session=" + sessionID + ",id=" + element.id + "}"); sendMessage(tag + "{session=" + sessionID + ",id=" + element.id + "}");

View File

@ -1,7 +1,6 @@
package rui package rui
import ( import (
"fmt"
"strings" "strings"
) )
@ -190,7 +189,7 @@ func (button *checkboxData) changedCheckboxState(state bool) {
defer freeStringBuilder(buffer) defer freeStringBuilder(buffer)
button.htmlCheckbox(buffer, state) button.htmlCheckbox(buffer, state)
button.Session().runScript(fmt.Sprintf(`updateInnerHTML('%v', '%v');`, button.htmlID()+"checkbox", buffer.String())) button.Session().runFunc("updateInnerHTML", button.htmlID()+"checkbox", buffer.String())
} }
func checkboxClickListener(view View) { func checkboxClickListener(view View) {

View File

@ -1,7 +1,6 @@
package rui package rui
import ( import (
"fmt"
"strings" "strings"
) )
@ -114,7 +113,7 @@ func (picker *colorPickerData) set(tag string, value any) bool {
func (picker *colorPickerData) colorChanged(oldColor Color) { func (picker *colorPickerData) colorChanged(oldColor Color) {
if newColor := GetColorPickerValue(picker); oldColor != newColor { if newColor := GetColorPickerValue(picker); oldColor != newColor {
if picker.created { if picker.created {
picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, picker.htmlID(), newColor.rgbString())) picker.session.runFunc("setInputValue", picker.htmlID(), newColor.rgbString())
} }
for _, listener := range picker.colorChangedListeners { for _, listener := range picker.colorChangedListeners {
listener(picker, newColor) listener(picker, newColor)

View File

@ -1,7 +1,6 @@
package rui package rui
import ( import (
"fmt"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -98,7 +97,7 @@ func (picker *datePickerData) remove(tag string) {
delete(picker.properties, DatePickerValue) delete(picker.properties, DatePickerValue)
date := GetDatePickerValue(picker) date := GetDatePickerValue(picker)
if picker.created { if picker.created {
picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, picker.htmlID(), date.Format(dateFormat))) picker.session.runFunc("setInputValue", picker.htmlID(), date.Format(dateFormat))
} }
for _, listener := range picker.dateChangedListeners { for _, listener := range picker.dateChangedListeners {
listener(picker, date) listener(picker, date)
@ -224,7 +223,7 @@ func (picker *datePickerData) set(tag string, value any) bool {
if date, ok := setTimeValue(DatePickerValue); ok { if date, ok := setTimeValue(DatePickerValue); ok {
if date != oldDate { if date != oldDate {
if picker.created { if picker.created {
picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, picker.htmlID(), date.Format(dateFormat))) picker.session.runFunc("setInputValue", picker.htmlID(), date.Format(dateFormat))
} }
for _, listener := range picker.dateChangedListeners { for _, listener := range picker.dateChangedListeners {
listener(picker, date) listener(picker, date)

View File

@ -87,7 +87,7 @@ func (list *dropDownListData) remove(tag string) {
delete(list.properties, Current) delete(list.properties, Current)
if oldCurrent != 0 { if oldCurrent != 0 {
if list.created { if list.created {
list.session.runScript(fmt.Sprintf(`selectDropDownListItem('%s', %d)`, list.htmlID(), 0)) list.session.runFunc("selectDropDownListItem", list.htmlID(), 0)
} }
list.onSelectedItemChanged(0) list.onSelectedItemChanged(0)
} }
@ -135,7 +135,7 @@ func (list *dropDownListData) set(tag string, value any) bool {
if current := GetCurrent(list); oldCurrent != current { if current := GetCurrent(list); oldCurrent != current {
if list.created { if list.created {
list.session.runScript(fmt.Sprintf(`selectDropDownListItem('%s', %d)`, list.htmlID(), current)) list.session.runFunc("selectDropDownListItem", list.htmlID(), current)
} }
list.onSelectedItemChanged(current) list.onSelectedItemChanged(current)
} }

View File

@ -1,7 +1,6 @@
package rui package rui
import ( import (
"fmt"
"strconv" "strconv"
"strings" "strings"
) )
@ -137,7 +136,7 @@ func (edit *editViewData) remove(tag string) {
if oldText != "" { if oldText != "" {
edit.textChanged("") edit.textChanged("")
if edit.created { if edit.created {
edit.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, edit.htmlID(), "")) edit.session.runFunc("setInputValue", edit.htmlID(), "")
} }
} }
} }
@ -211,11 +210,7 @@ func (edit *editViewData) set(tag string, value any) bool {
if GetEditViewType(edit) == MultiLineText { if GetEditViewType(edit) == MultiLineText {
updateInnerHTML(edit.htmlID(), edit.Session()) updateInnerHTML(edit.htmlID(), edit.Session())
} else { } else {
text = strings.ReplaceAll(text, `"`, `\"`) edit.session.runFunc("setInputValue", edit.htmlID(), text)
text = strings.ReplaceAll(text, `'`, `\'`)
text = strings.ReplaceAll(text, "\n", `\n`)
text = strings.ReplaceAll(text, "\r", `\r`)
edit.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, edit.htmlID(), text))
} }
} }
} }
@ -365,14 +360,7 @@ func (edit *editViewData) AppendText(text string) {
if textValue, ok := value.(string); ok { if textValue, ok := value.(string); ok {
textValue += text textValue += text
edit.properties[Text] = textValue edit.properties[Text] = textValue
edit.session.runFunc("appendToInnerHTML", edit.htmlID(), text)
text := strings.ReplaceAll(text, `"`, `\"`)
text = strings.ReplaceAll(text, `'`, `\'`)
text = strings.ReplaceAll(text, "\n", `\n`)
text = strings.ReplaceAll(text, "\r", `\r`)
edit.session.runScript(`appendToInnerHTML("` + edit.htmlID() + `", "` + text + `")`)
edit.textChanged(textValue) edit.textChanged(textValue)
return return
} }

View File

@ -2,7 +2,6 @@ package rui
import ( import (
"encoding/base64" "encoding/base64"
"fmt"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -109,7 +108,7 @@ func (picker *filePickerData) LoadFile(file FileInfo, result func(FileInfo, []by
for i, info := range picker.files { for i, info := range picker.files {
if info.Name == file.Name && info.Size == file.Size && info.LastModified == file.LastModified { if info.Name == file.Name && info.Size == file.Size && info.LastModified == file.LastModified {
picker.loader[i] = result picker.loader[i] = result
picker.Session().runScript(fmt.Sprintf(`loadSelectedFile("%s", %d)`, picker.htmlID(), i)) picker.Session().runFunc("loadSelectedFile", picker.htmlID(), i)
return return
} }
} }

View File

@ -76,7 +76,7 @@ func (manager *imageManager) loadImage(url string, onLoaded func(Image), session
image.listener = onLoaded image.listener = onLoaded
image.loadingStatus = ImageLoading image.loadingStatus = ImageLoading
manager.images[url] = image manager.images[url] = image
session.runScript("loadImage('" + url + "');") session.runFunc("loadImage", url)
return image return image
} }

View File

@ -819,12 +819,6 @@ func (listView *listViewData) updateCheckboxItem(index int, checked bool) {
buffer := allocStringBuilder() buffer := allocStringBuilder()
defer freeStringBuilder(buffer) defer freeStringBuilder(buffer)
buffer.WriteString(`updateInnerHTML('`)
buffer.WriteString(listView.htmlID())
buffer.WriteRune('-')
buffer.WriteString(strconv.Itoa(index))
buffer.WriteString(`', '`)
buffer.WriteString(listView.checkboxItemDiv(listView, checkbox, hCheckboxAlign, vCheckboxAlign)) buffer.WriteString(listView.checkboxItemDiv(listView, checkbox, hCheckboxAlign, vCheckboxAlign))
if checked { if checked {
buffer.WriteString(onDiv) buffer.WriteString(onDiv)
@ -842,9 +836,8 @@ func (listView *listViewData) updateCheckboxItem(index int, checked bool) {
buffer.WriteString("ERROR: invalid item view") buffer.WriteString("ERROR: invalid item view")
} }
} }
buffer.WriteString(`</div></div>');`) buffer.WriteString(`</div></div>`)
session.runFunc("updateInnerHTML", listView.htmlID()+"-"+strconv.Itoa(index), buffer.String())
session.runScript(buffer.String())
} }
func (listView *listViewData) htmlProperties(self View, buffer *strings.Builder) { func (listView *listViewData) htmlProperties(self View, buffer *strings.Builder) {

View File

@ -472,11 +472,7 @@ func (player *mediaPlayerData) propertyChanged(tag string) {
case Muted: case Muted:
value, _ := boolProperty(player, tag, player.Session()) value, _ := boolProperty(player, tag, player.Session())
if value { player.Session().runFunc("setMediaMuted", player.htmlID(), value)
player.Session().runScript("setMediaMuted('" + player.htmlID() + "', true)")
} else {
player.Session().runScript("setMediaMuted('" + player.htmlID() + "', false)")
}
case Preload: case Preload:
value, _ := enumProperty(player, tag, player.Session(), 0) value, _ := enumProperty(player, tag, player.Session(), 0)
@ -659,15 +655,15 @@ func (player *mediaPlayerData) handleCommand(self View, command string, data Dat
} }
func (player *mediaPlayerData) Play() { func (player *mediaPlayerData) Play() {
player.session.runScript(fmt.Sprintf(`mediaPlay('%v');`, player.htmlID())) player.session.runFunc("mediaPlay", player.htmlID())
} }
func (player *mediaPlayerData) Pause() { func (player *mediaPlayerData) Pause() {
player.session.runScript(fmt.Sprintf(`mediaPause('%v');`, player.htmlID())) player.session.runFunc("mediaPause", player.htmlID())
} }
func (player *mediaPlayerData) SetCurrentTime(seconds float64) { func (player *mediaPlayerData) SetCurrentTime(seconds float64) {
player.session.runScript(fmt.Sprintf(`mediaSetSetCurrentTime('%v', %v);`, player.htmlID(), seconds)) player.session.runFunc("mediaSetSetCurrentTime", player.htmlID(), seconds)
} }
func (player *mediaPlayerData) getFloatPlayerProperty(tag string) (float64, bool) { func (player *mediaPlayerData) getFloatPlayerProperty(tag string) (float64, bool) {
@ -731,7 +727,7 @@ func (player *mediaPlayerData) Duration() float64 {
} }
func (player *mediaPlayerData) SetPlaybackRate(rate float64) { func (player *mediaPlayerData) SetPlaybackRate(rate float64) {
player.session.runScript(fmt.Sprintf(`mediaSetPlaybackRate('%v', %v);`, player.htmlID(), rate)) player.session.runFunc("mediaSetPlaybackRate", player.htmlID(), rate)
} }
func (player *mediaPlayerData) PlaybackRate() float64 { func (player *mediaPlayerData) PlaybackRate() float64 {
@ -743,7 +739,7 @@ func (player *mediaPlayerData) PlaybackRate() float64 {
func (player *mediaPlayerData) SetVolume(volume float64) { func (player *mediaPlayerData) SetVolume(volume float64) {
if volume >= 0 && volume <= 1 { if volume >= 0 && volume <= 1 {
player.session.runScript(fmt.Sprintf(`mediaSetVolume('%v', %v);`, player.htmlID(), volume)) player.session.runFunc("mediaSetVolume", player.htmlID(), volume)
} }
} }

View File

@ -1,7 +1,6 @@
package rui package rui
import ( import (
"fmt"
"math" "math"
"strconv" "strconv"
"strings" "strings"
@ -117,7 +116,7 @@ func (picker *numberPickerData) set(tag string, value any) bool {
if f, ok := floatProperty(picker, NumberPickerValue, picker.Session(), min); ok && f != oldValue { if f, ok := floatProperty(picker, NumberPickerValue, picker.Session(), min); ok && f != oldValue {
newValue, _ := floatTextProperty(picker, NumberPickerValue, picker.Session(), min) newValue, _ := floatTextProperty(picker, NumberPickerValue, picker.Session(), min)
if picker.created { if picker.created {
picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, picker.htmlID(), newValue)) picker.session.runFunc("setInputValue", picker.htmlID(), newValue)
} }
for _, listener := range picker.numberChangedListeners { for _, listener := range picker.numberChangedListeners {
listener(picker, f) listener(picker, f)
@ -163,7 +162,7 @@ func (picker *numberPickerData) propertyChanged(tag string) {
case NumberPickerValue: case NumberPickerValue:
value := GetNumberPickerValue(picker) value := GetNumberPickerValue(picker)
picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%f')`, picker.htmlID(), value)) picker.session.runFunc("setInputValue", picker.htmlID(), value)
for _, listener := range picker.numberChangedListeners { for _, listener := range picker.numberChangedListeners {
listener(picker, value) listener(picker, value)
} }

View File

@ -610,19 +610,17 @@ func ShowPopup(view View, param Params) Popup {
func (manager *popupManager) updatePopupLayerInnerHTML(session Session) { func (manager *popupManager) updatePopupLayerInnerHTML(session Session) {
if manager.popups == nil { if manager.popups == nil {
manager.popups = []Popup{} manager.popups = []Popup{}
session.runScript(`updateInnerHTML('ruiPopupLayer', '');`) session.runFunc("updateInnerHTML", "ruiPopupLayer", "")
return return
} }
buffer := allocStringBuilder() buffer := allocStringBuilder()
defer freeStringBuilder(buffer) defer freeStringBuilder(buffer)
buffer.WriteString(`updateInnerHTML('ruiPopupLayer', '`) for _, popup := range manager.popups {
for _, p := range manager.popups { popup.html(buffer)
p.html(buffer)
} }
buffer.WriteString(`');`) session.runFunc("updateInnerHTML", "ruiPopupLayer", buffer.String())
session.runScript(buffer.String())
} }
func (manager *popupManager) showPopup(popup Popup) { func (manager *popupManager) showPopup(popup Popup) {
@ -637,7 +635,7 @@ func (manager *popupManager) showPopup(popup Popup) {
manager.popups = append(manager.popups, popup) manager.popups = append(manager.popups, popup)
} }
session.runScript(`if (document.activeElement != document.body) document.activeElement.blur();`) session.runFunc("blurCurrent")
manager.updatePopupLayerInnerHTML(session) manager.updatePopupLayerInnerHTML(session)
updateCSSProperty("ruiPopupLayer", "visibility", "visible", session) updateCSSProperty("ruiPopupLayer", "visibility", "visible", session)
updateCSSProperty("ruiRoot", "pointer-events", "none", session) updateCSSProperty("ruiRoot", "pointer-events", "none", session)
@ -660,7 +658,7 @@ func (manager *popupManager) dismissPopup(popup Popup) {
manager.popups = []Popup{} manager.popups = []Popup{}
updateCSSProperty("ruiRoot", "pointer-events", "auto", session) updateCSSProperty("ruiRoot", "pointer-events", "auto", session)
updateCSSProperty("ruiPopupLayer", "visibility", "hidden", session) updateCSSProperty("ruiPopupLayer", "visibility", "hidden", session)
session.runScript(`updateInnerHTML('ruiPopupLayer', '');`) session.runFunc("updateInnerHTML", "ruiPopupLayer", "")
} else { } else {
manager.popups = manager.popups[:count-1] manager.popups = manager.popups[:count-1]
manager.updatePopupLayerInnerHTML(session) manager.updatePopupLayerInnerHTML(session)

View File

@ -1,7 +1,5 @@
package rui package rui
import "fmt"
// ScrollEvent is the constant for "scroll-event" property tag. // ScrollEvent is the constant for "scroll-event" property tag.
// The "scroll-event" is fired when the content of the view is scrolled. // The "scroll-event" is fired when the content of the view is scrolled.
// The main listener format: // The main listener format:
@ -59,7 +57,7 @@ func ScrollViewTo(view View, subviewID string, x, y float64) {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID)
} }
if view != nil { if view != nil {
view.Session().runScript(fmt.Sprintf(`scrollTo("%s", %g, %g)`, view.htmlID(), x, y)) view.Session().runFunc("scrollTo", view.htmlID(), x, y)
} }
} }
@ -70,7 +68,7 @@ func ScrollViewToStart(view View, subviewID ...string) {
view = ViewByID(view, subviewID[0]) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
view.Session().runScript(`scrollToStart("` + view.htmlID() + `")`) view.Session().runFunc("scrollToStart", view.htmlID())
} }
} }
@ -81,6 +79,6 @@ func ScrollViewToEnd(view View, subviewID ...string) {
view = ViewByID(view, subviewID[0]) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
view.Session().runScript(`scrollToEnd("` + view.htmlID() + `")`) view.Session().runFunc("scrollToEnd", view.htmlID())
} }
} }

View File

@ -8,6 +8,7 @@ import (
) )
type webBrige interface { type webBrige interface {
runFunc(funcName string, args ...any) bool
readMessage() (string, bool) readMessage() (string, bool)
writeMessage(text string) bool writeMessage(text string) bool
runGetterScript(script string) DataObject runGetterScript(script string) DataObject
@ -96,6 +97,7 @@ type Session interface {
setBrige(events chan DataObject, brige webBrige) setBrige(events chan DataObject, brige webBrige)
writeInitScript(writer *strings.Builder) writeInitScript(writer *strings.Builder)
runFunc(funcName string, args ...any)
runScript(script string) runScript(script string)
runGetterScript(script string) DataObject //, answer chan DataObject) runGetterScript(script string) DataObject //, answer chan DataObject)
handleAnswer(data DataObject) handleAnswer(data DataObject)
@ -321,6 +323,14 @@ func (session *sessionData) imageManager() *imageManager {
return session.images return session.images
} }
func (session *sessionData) runFunc(funcName string, args ...any) {
if session.brige != nil {
session.brige.runFunc(funcName, args...)
} else {
ErrorLog("No connection")
}
}
func (session *sessionData) runScript(script string) { func (session *sessionData) runScript(script string) {
if session.brige != nil { if session.brige != nil {
session.brige.writeMessage(script) session.brige.writeMessage(script)

View File

@ -39,15 +39,13 @@ func sizeConstant(session Session, tag string) (SizeUnit, bool) {
func updateCSSStyle(htmlID string, session Session) { func updateCSSStyle(htmlID string, session Session) {
if !session.ignoreViewUpdates() { if !session.ignoreViewUpdates() {
if view := session.viewByHTMLID(htmlID); view != nil { if view := session.viewByHTMLID(htmlID); view != nil {
var builder viewCSSBuilder builder := viewCSSBuilder{buffer: allocStringBuilder()}
//builder.buffer.WriteString(`updateCSSStyle('`)
builder.buffer = allocStringBuilder() //builder.buffer.WriteString(view.htmlID())
builder.buffer.WriteString(`updateCSSStyle('`) //builder.buffer.WriteString(`', '`)
builder.buffer.WriteString(view.htmlID())
builder.buffer.WriteString(`', '`)
view.cssStyle(view, &builder) view.cssStyle(view, &builder)
builder.buffer.WriteString(`');`) //builder.buffer.WriteString(`');`)
view.Session().runScript(builder.finish()) session.runFunc("updateCSSStyle", view.htmlID(), builder.finish())
} }
} }
} }
@ -66,8 +64,7 @@ func updateInnerHTML(htmlID string, session Session) {
script.Grow(32 * 1024) script.Grow(32 * 1024)
view.htmlSubviews(view, script) view.htmlSubviews(view, script)
view.Session().runScript(fmt.Sprintf(`updateInnerHTML('%v', '%v');`, view.htmlID(), script.String())) session.runFunc("updateInnerHTML", view.htmlID(), script.String())
//view.updateEventHandlers()
} }
} }
} }
@ -75,8 +72,7 @@ func updateInnerHTML(htmlID string, session Session) {
func appendToInnerHTML(htmlID, content string, session Session) { func appendToInnerHTML(htmlID, content string, session Session) {
if !session.ignoreViewUpdates() { if !session.ignoreViewUpdates() {
if view := session.viewByHTMLID(htmlID); view != nil { if view := session.viewByHTMLID(htmlID); view != nil {
view.Session().runScript(fmt.Sprintf(`appendToInnerHTML('%v', '%v');`, view.htmlID(), content)) session.runFunc("appendToInnerHTML", view.htmlID(), content)
//view.updateEventHandlers()
} }
} }
} }
@ -87,7 +83,7 @@ func updateProperty(htmlID, property, value string, session Session) {
buffer.WriteString(fmt.Sprintf(`element.setAttribute('%v', '%v');`, property, value)) buffer.WriteString(fmt.Sprintf(`element.setAttribute('%v', '%v');`, property, value))
buffer.WriteRune('\n') buffer.WriteRune('\n')
} else { } else {
session.runScript(fmt.Sprintf(`updateProperty('%v', '%v', '%v');`, htmlID, property, value)) session.runFunc("updateProperty", htmlID, property, value)
} }
} }
} }
@ -98,7 +94,7 @@ func updateCSSProperty(htmlID, property, value string, session Session) {
buffer.WriteString(fmt.Sprintf(`element.style['%v'] = '%v';`, property, value)) buffer.WriteString(fmt.Sprintf(`element.style['%v'] = '%v';`, property, value))
buffer.WriteRune('\n') buffer.WriteRune('\n')
} else { } else {
session.runScript(fmt.Sprintf(`updateCSSProperty('%v', '%v', '%v');`, htmlID, property, value)) session.runFunc("updateCSSProperty", htmlID, property, value)
} }
} }
} }
@ -112,10 +108,8 @@ func updateBoolProperty(htmlID, property string, value bool, session Session) {
buffer.WriteString(fmt.Sprintf(`element.setAttribute('%v', false);`, property)) buffer.WriteString(fmt.Sprintf(`element.setAttribute('%v', false);`, property))
} }
buffer.WriteRune('\n') buffer.WriteRune('\n')
} else if value {
session.runScript(fmt.Sprintf(`updateProperty('%v', '%v', true);`, htmlID, property))
} else { } else {
session.runScript(fmt.Sprintf(`updateProperty('%v', '%v', false);`, htmlID, property)) session.runFunc("updateProperty", htmlID, property, value)
} }
} }
} }
@ -126,7 +120,7 @@ func removeProperty(htmlID, property string, session Session) {
buffer.WriteString(fmt.Sprintf(`if (element.hasAttribute('%v')) { element.removeAttribute('%v');}`, property, property)) buffer.WriteString(fmt.Sprintf(`if (element.hasAttribute('%v')) { element.removeAttribute('%v');}`, property, property))
buffer.WriteRune('\n') buffer.WriteRune('\n')
} else { } else {
session.runScript(fmt.Sprintf(`removeProperty('%v', '%v');`, htmlID, property)) session.runFunc("removeProperty", htmlID, property)
} }
} }
} }

View File

@ -825,8 +825,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
defer session.setIgnoreViewUpdates(false) defer session.setIgnoreViewUpdates(false)
} }
var cssBuilder viewCSSBuilder cssBuilder := viewCSSBuilder{buffer: allocStringBuilder()}
cssBuilder.buffer = allocStringBuilder()
defer freeStringBuilder(cssBuilder.buffer) defer freeStringBuilder(cssBuilder.buffer)
var view tableCellView var view tableCellView

View File

@ -151,7 +151,7 @@ func (tabsLayout *tabsLayoutData) remove(tag string) {
return return
} }
if tabsLayout.created { if tabsLayout.created {
tabsLayout.session.runScript(fmt.Sprintf("activateTab('%v', %d);", tabsLayout.htmlID(), 0)) tabsLayout.session.runFunc("activateTab", tabsLayout.htmlID(), 0)
for _, listener := range tabsLayout.tabListener { for _, listener := range tabsLayout.tabListener {
listener(tabsLayout, 0, oldCurrent) listener(tabsLayout, 0, oldCurrent)
} }
@ -235,7 +235,7 @@ func (tabsLayout *tabsLayoutData) set(tag string, value any) bool {
return true return true
} }
if tabsLayout.created { if tabsLayout.created {
tabsLayout.session.runScript(fmt.Sprintf("activateTab('%v', %d);", tabsLayout.htmlID(), current)) tabsLayout.session.runFunc("activateTab", tabsLayout.htmlID(), current)
for _, listener := range tabsLayout.tabListener { for _, listener := range tabsLayout.tabListener {
listener(tabsLayout, current, oldCurrent) listener(tabsLayout, current, oldCurrent)
} }

View File

@ -1,7 +1,6 @@
package rui package rui
import ( import (
"fmt"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -98,7 +97,7 @@ func (picker *timePickerData) remove(tag string) {
delete(picker.properties, TimePickerValue) delete(picker.properties, TimePickerValue)
time := GetTimePickerValue(picker) time := GetTimePickerValue(picker)
if picker.created { if picker.created {
picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, picker.htmlID(), time.Format(timeFormat))) picker.session.runFunc("setInputValue", picker.htmlID(), time.Format(timeFormat))
} }
for _, listener := range picker.timeChangedListeners { for _, listener := range picker.timeChangedListeners {
listener(picker, time) listener(picker, time)
@ -212,7 +211,7 @@ func (picker *timePickerData) set(tag string, value any) bool {
if time, ok := setTimeValue(TimePickerValue); ok { if time, ok := setTimeValue(TimePickerValue); ok {
if time != oldTime { if time != oldTime {
if picker.created { if picker.created {
picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, picker.htmlID(), time.Format(timeFormat))) picker.session.runFunc("setInputValue", picker.htmlID(), time.Format(timeFormat))
} }
for _, listener := range picker.timeChangedListeners { for _, listener := range picker.timeChangedListeners {
listener(picker, time) listener(picker, time)

View File

@ -754,7 +754,7 @@ func viewHTML(view View, buffer *strings.Builder) {
buffer.WriteRune('"') buffer.WriteRune('"')
} }
var cssBuilder viewCSSBuilder cssBuilder := viewCSSBuilder{buffer: allocStringBuilder()}
view.cssStyle(view, &cssBuilder) view.cssStyle(view, &cssBuilder)
if style := cssBuilder.finish(); style != "" { if style := cssBuilder.finish(); style != "" {

View File

@ -803,7 +803,7 @@ func colorStyledProperty(view View, subviewID []string, tag string, inherit bool
// The focused View is the View which will receive keyboard events by default. // The focused View is the View which will receive keyboard events by default.
func FocusView(view View) { func FocusView(view View) {
if view != nil { if view != nil {
view.Session().runScript("focus('" + view.htmlID() + "')") view.Session().runFunc("focus", view.htmlID())
} }
} }
@ -811,21 +811,21 @@ func FocusView(view View) {
// The focused View is the View which will receive keyboard events by default. // The focused View is the View which will receive keyboard events by default.
func FocusViewByID(viewID string, session Session) { func FocusViewByID(viewID string, session Session) {
if viewID != "" { if viewID != "" {
session.runScript("focus('" + viewID + "')") session.runFunc("focus", viewID)
} }
} }
// BlurView removes keyboard focus from the specified View. // BlurView removes keyboard focus from the specified View.
func BlurView(view View) { func BlurView(view View) {
if view != nil { if view != nil {
view.Session().runScript("blur('" + view.htmlID() + "')") view.Session().runFunc("blur", view.htmlID())
} }
} }
// BlurViewByID removes keyboard focus from the View with the specified viewID. // BlurViewByID removes keyboard focus from the View with the specified viewID.
func BlurViewByID(viewID string, session Session) { func BlurViewByID(viewID string, session Session) {
if viewID != "" { if viewID != "" {
session.runScript("blur('" + viewID + "')") session.runFunc("blur", viewID)
} }
} }

View File

@ -3,8 +3,10 @@
package rui package rui
import ( import (
"fmt"
"net/http" "net/http"
"strconv" "strconv"
"strings"
"sync" "sync"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
@ -16,6 +18,7 @@ type wsBrige struct {
answerID int answerID int
answerMutex sync.Mutex answerMutex sync.Mutex
closed bool closed bool
buffer strings.Builder
} }
var upgrader = websocket.Upgrader{ var upgrader = websocket.Upgrader{
@ -43,6 +46,90 @@ func (brige *wsBrige) close() {
brige.conn.Close() brige.conn.Close()
} }
func (brige *wsBrige) argToString(arg any) (string, bool) {
switch arg := arg.(type) {
case string:
arg = strings.ReplaceAll(arg, "\\", `\\`)
arg = strings.ReplaceAll(arg, "'", `\'`)
arg = strings.ReplaceAll(arg, "\n", `\n`)
arg = strings.ReplaceAll(arg, "\r", `\r`)
arg = strings.ReplaceAll(arg, "\t", `\t`)
arg = strings.ReplaceAll(arg, "\b", `\b`)
arg = strings.ReplaceAll(arg, "\f", `\f`)
arg = strings.ReplaceAll(arg, "\v", `\v`)
return `'` + arg + `'`, true
case rune:
switch arg {
case '\t':
return `'\t'`, true
case '\r':
return `'\r'`, true
case '\n':
return `'\n'`, true
case '\b':
return `'\b'`, true
case '\f':
return `'\f'`, true
case '\v':
return `'\v'`, true
case '\'':
return `'\''`, true
case '\\':
return `'\\'`, true
}
if arg < ' ' {
return fmt.Sprintf(`'\x%02d'`, int(arg)), true
}
return `'` + string(arg) + `'`, true
case bool:
if arg {
return "true", true
} else {
return "false", true
}
case float32:
return fmt.Sprintf("%g", float64(arg)), true
case float64:
return fmt.Sprintf("%g", arg), true
default:
if n, ok := isInt(arg); ok {
return fmt.Sprintf("%d", n), true
}
}
ErrorLog("Unsupported agument type")
return "", false
}
func (brige *wsBrige) runFunc(funcName string, args ...any) bool {
brige.buffer.Reset()
brige.buffer.WriteString(funcName)
brige.buffer.WriteRune('(')
for i, arg := range args {
argText, ok := brige.argToString(arg)
if !ok {
return false
}
if i > 0 {
brige.buffer.WriteString(", ")
}
brige.buffer.WriteString(argText)
}
brige.buffer.WriteString(");")
if err := brige.conn.WriteMessage(websocket.TextMessage, []byte(brige.buffer.String())); err != nil {
ErrorLog(err.Error())
return false
}
return true
}
func (brige *wsBrige) readMessage() (string, bool) { func (brige *wsBrige) readMessage() (string, bool) {
_, p, err := brige.conn.ReadMessage() _, p, err := brige.conn.ReadMessage()
if err != nil { if err != nil {