Optimisation

This commit is contained in:
Alexei Anoshenko 2026-09-03 12:39:07 +03:00
parent affbd50d02
commit 61e1152f33
41 changed files with 303 additions and 851 deletions

View File

@ -596,47 +596,30 @@ func (animation *animationData) String() string {
if value := animation.getRaw(tag); value != nil { if value := animation.getRaw(tag); value != nil {
text := propertyValueToString(tag, value, "\t") text := propertyValueToString(tag, value, "\t")
if text != "" { if text != "" {
buffer.WriteString("\n\t") writeStrings(buffer, "\n\t", string(tag), " = ", text, `,`)
buffer.WriteString(string(tag))
buffer.WriteString(" = ")
buffer.WriteString(text)
buffer.WriteRune(',')
} }
} }
} }
} }
writeProperty := func(prop AnimatedProperty, indent string) { writeProperty := func(prop AnimatedProperty, indent string) {
buffer.WriteString(string(prop.Tag)) writeStrings(buffer, string(prop.Tag),
buffer.WriteString("{\n") "{\n", indent, "from = ", propertyValueToString("from", prop.From, indent),
buffer.WriteString(indent) ",\n", indent, "to = ", propertyValueToString("to", prop.To, indent))
buffer.WriteString("from = ")
buffer.WriteString(propertyValueToString("from", prop.From, indent))
buffer.WriteString(",\n")
buffer.WriteString(indent)
buffer.WriteString("to = ")
buffer.WriteString(propertyValueToString("to", prop.To, indent))
for key, value := range prop.KeyFrames { for key, value := range prop.KeyFrames {
tag := strconv.Itoa(key) + "%" tag := strconv.Itoa(key) + "%"
text := propertyValueToString(PropertyName(tag), value, indent) text := propertyValueToString(PropertyName(tag), value, indent)
if text != "" { if text != "" {
buffer.WriteString(",\n") writeStrings(buffer, ",\n", indent, tag, " = ", text)
buffer.WriteString(indent)
buffer.WriteString(tag)
buffer.WriteString(" = ")
buffer.WriteString(text)
} }
} }
buffer.WriteString("\n") writeStrings(buffer, "\n", indent[1:], "}")
buffer.WriteString(indent[1:])
buffer.WriteString("}")
} }
if props := animation.animatedProperties(); len(props) > 0 { if props := animation.animatedProperties(); len(props) > 0 {
buffer.WriteString("\n\t") writeStrings(buffer, "\n\t", string(PropertyTag), " = ")
buffer.WriteString(string(PropertyTag))
buffer.WriteString(" = ")
if len(props) > 1 { if len(props) > 1 {
buffer.WriteString("[\n") buffer.WriteString("[\n")
for _, prop := range props { for _, prop := range props {
@ -728,10 +711,7 @@ func (animation *animationData) writeTransitionString(tag PropertyName, buffer *
if value := animation.getRaw(name); value != nil { if value := animation.getRaw(name); value != nil {
text := propertyValueToString(name, value, "") text := propertyValueToString(name, value, "")
if text != "" { if text != "" {
buffer.WriteString(lead) writeStrings(buffer, lead, string(name), " = ", text)
buffer.WriteString(string(name))
buffer.WriteString(" = ")
buffer.WriteString(text)
lead = ", " lead = ", "
return true return true
} }
@ -748,13 +728,9 @@ func (animation *animationData) writeTransitionString(tag PropertyName, buffer *
if value := animation.getRaw(TimingFunction); value != nil { if value := animation.getRaw(TimingFunction); value != nil {
if timingFunction, ok := value.(string); ok && timingFunction != "" { if timingFunction, ok := value.(string); ok && timingFunction != "" {
buffer.WriteString(lead) writeStrings(buffer, lead, string(TimingFunction), " = ")
buffer.WriteString(string(TimingFunction))
buffer.WriteString(" = ")
if strings.ContainsAny(timingFunction, " ,()") { if strings.ContainsAny(timingFunction, " ,()") {
buffer.WriteRune('"') writeStrings(buffer, `"`, timingFunction, `"`)
buffer.WriteString(timingFunction)
buffer.WriteRune('"')
} else { } else {
buffer.WriteString(timingFunction) buffer.WriteString(timingFunction)
} }
@ -776,49 +752,30 @@ func (animation *animationData) writeAnimationString(tag PropertyName, buffer *s
if value := animation.Get(tag); value != nil { if value := animation.Get(tag); value != nil {
text := propertyValueToString(tag, value, indent2) text := propertyValueToString(tag, value, indent2)
if text != "" { if text != "" {
buffer.WriteRune('\n') writeStrings(buffer, "\n", indent2, string(tag), " = ", text, ",")
buffer.WriteString(indent2)
buffer.WriteString(string(tag))
buffer.WriteString(" = ")
buffer.WriteString(text)
buffer.WriteRune(',')
} }
} }
} }
} }
writeProperty := func(prop AnimatedProperty, indent string) { writeProperty := func(prop AnimatedProperty, indent string) {
buffer.WriteString(string(prop.Tag))
buffer.WriteString("{\n")
indent2 := indent + "\t" indent2 := indent + "\t"
buffer.WriteString(indent2) writeStrings(buffer, string(prop.Tag),
buffer.WriteString("from = ") "{\n", indent2, "from = ", propertyValueToString("from", prop.From, indent2),
buffer.WriteString(propertyValueToString("from", prop.From, indent2)) ",\n", indent2, "to = ", propertyValueToString("to", prop.To, indent2))
buffer.WriteString(",\n")
buffer.WriteString(indent2)
buffer.WriteString("to = ")
buffer.WriteString(propertyValueToString("to", prop.To, indent2))
for key, value := range prop.KeyFrames { for key, value := range prop.KeyFrames {
text := propertyValueToString(PropertyName(tag), value, indent2) text := propertyValueToString(PropertyName(tag), value, indent2)
if text != "" { if text != "" {
buffer.WriteString(",\n") writeStrings(buffer, ",\n", indent2, strconv.Itoa(key), "% = ", text)
buffer.WriteString(indent2)
tag := strconv.Itoa(key) + "%"
buffer.WriteString(tag)
buffer.WriteString(" = ")
buffer.WriteString(text)
} }
} }
buffer.WriteString(",\n")
buffer.WriteString(indent) writeStrings(buffer, ",\n", indent, "},")
buffer.WriteString("},")
} }
if props, ok := animation.Get(PropertyTag).([]AnimatedProperty); ok && props != nil && len(props) > 0 { if props, ok := animation.Get(PropertyTag).([]AnimatedProperty); ok && props != nil && len(props) > 0 {
buffer.WriteRune('\n') writeStrings(buffer, "\n", indent2, string(PropertyTag), " = ")
buffer.WriteString(indent2)
buffer.WriteString(string(PropertyTag))
buffer.WriteString(" = ")
if len(props) > 1 { if len(props) > 1 {
buffer.WriteString("[\n") buffer.WriteString("[\n")
for _, prop := range props { for _, prop := range props {
@ -832,9 +789,7 @@ func (animation *animationData) writeAnimationString(tag PropertyName, buffer *s
writeProperty(props[0], indent2) writeProperty(props[0], indent2)
} }
} }
buffer.WriteRune('\n') writeStrings(buffer, "\n", indent, "},\n")
buffer.WriteString(indent)
buffer.WriteString("},\n")
} }
writeAnimation(animation, buffer, indent) writeAnimation(animation, buffer, indent)

View File

@ -69,59 +69,48 @@ type AppParams struct {
} }
func getStartPage(buffer *strings.Builder, sessionID int, params AppParams) { func getStartPage(buffer *strings.Builder, sessionID int, params AppParams) {
buffer.WriteString(`<head> writeStrings(buffer, `<head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>`) <title>`, params.Title, "</title>")
buffer.WriteString(params.Title)
buffer.WriteString("</title>")
if params.Icon != "" { if params.Icon != "" {
buffer.WriteString(` writeStrings(buffer, `
<link rel="icon" href="`) <link rel="icon" href="`, params.Icon, `">`)
buffer.WriteString(params.Icon)
buffer.WriteString(`">`)
} }
if params.TitleColor != 0 { if params.TitleColor != 0 {
buffer.WriteString(` writeStrings(buffer, `
<meta name="theme-color" content="`) <meta name="theme-color" content="`, params.TitleColor.cssString(), `">`)
buffer.WriteString(params.TitleColor.cssString())
buffer.WriteString(`">`)
} }
buffer.WriteString(` buffer.WriteString(`
<base target="_blank" rel="noopener"> <base target="_blank" rel="noopener">
<meta name="viewport" content="width=device-width">`) <meta name="viewport" content="width=device-width">`)
if params.GoogleFonts != "" { if params.GoogleFonts != "" {
buffer.WriteString(` writeStrings(buffer, `
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="`) <link href="`, params.GoogleFonts, `" rel="stylesheet">`)
buffer.WriteString(params.GoogleFonts)
buffer.WriteString(`" rel="stylesheet">`)
} }
buffer.WriteString(` writeStrings(buffer, `
<style>`) <style>`, appStyles, `</style>
buffer.WriteString(appStyles) <style id="ruiAnimations"></style>
buffer.WriteString(`</style> <script>
<style id="ruiAnimations"></style> const sessionID = `, strconv.Itoa(sessionID), `;
<script>
const sessionID = `)
buffer.WriteString(strconv.Itoa(sessionID))
buffer.WriteString(`;
</script> </script>
<script src="/script.js"></script> <script src="/script.js"></script>
</head> </head>
<body id="body" onkeydown="keyDownEvent(this, event)"> <body id="body" onkeydown="keyDownEvent(this, event)">
<div class="ruiRoot" id="ruiRootView"></div> <div class="ruiRoot" id="ruiRootView"></div>
<div class="ruiPopupLayer" id="ruiPopupLayer" style="visibility: hidden; isolation: isolate; z-index: 10001;"></div> <div class="ruiPopupLayer" id="ruiPopupLayer" style="visibility: hidden; isolation: isolate; z-index: 10001;"></div>
<div class="ruiTooltipLayer" id="ruiTooltipLayer" style="visibility: hidden; opacity: 0; z-index: 10002;"> <div class="ruiTooltipLayer" id="ruiTooltipLayer" style="visibility: hidden; opacity: 0; z-index: 10002;">
<div id="ruiTooltipText" class="ruiTooltipText"></div> <div id="ruiTooltipText" class="ruiTooltipText"></div>
<div id="ruiTooltipTopArrow" class="ruiTooltipTopArrow"></div> <div id="ruiTooltipTopArrow" class="ruiTooltipTopArrow"></div>
<div id="ruiTooltipBottomArrow" class="ruiTooltipBottomArrow"></div> <div id="ruiTooltipBottomArrow" class="ruiTooltipBottomArrow"></div>
</div> </div>
<a id="ruiDownloader" download style="display: none;"></a> <a id="ruiDownloader" download style="display: none;"></a>
</body>`) </body>`)
} }

View File

@ -314,10 +314,7 @@ func (gradient *backgroundConicGradient) cssStyle(session Session) string {
if comma { if comma {
buffer.WriteRune(' ') buffer.WriteRune(' ')
} }
buffer.WriteString("at ") writeStrings(buffer, "at ", x.cssString("50%", session), " ", y.cssString("50%", session))
buffer.WriteString(x.cssString("50%", session))
buffer.WriteString(" ")
buffer.WriteString(y.cssString("50%", session))
comma = true comma = true
} }

View File

@ -135,9 +135,7 @@ func (image *backgroundImage) cssStyle(session Session) string {
buffer := allocStringBuilder() buffer := allocStringBuilder()
defer freeStringBuilder(buffer) defer freeStringBuilder(buffer)
buffer.WriteString(`url(`) writeStrings(buffer, `url(`, src, ")")
buffer.WriteString(src)
buffer.WriteRune(')')
attachment, _ := enumProperty(image, Attachment, session, NoRepeat) attachment, _ := enumProperty(image, Attachment, session, NoRepeat)
values := enumProperties[Attachment].values values := enumProperties[Attachment].values
@ -177,10 +175,7 @@ func (image *backgroundImage) cssStyle(session Session) string {
height, _ := sizeProperty(image, Height, session) height, _ := sizeProperty(image, Height, session)
if width.Type != Auto || height.Type != Auto { if width.Type != Auto || height.Type != Auto {
buffer.WriteString(` / `) writeStrings(buffer, ` / `, width.cssString("auto", session), " ", height.cssString("auto", session))
buffer.WriteString(width.cssString("auto", session))
buffer.WriteRune(' ')
buffer.WriteString(height.cssString("auto", session))
} }
} }

View File

@ -227,18 +227,12 @@ func (gradient *backgroundRadialGradient) cssStyle(session Session) string {
if text, ok := session.resolveConstants(value); ok { if text, ok := session.resolveConstants(value); ok {
values := enumProperties[RadialGradientRadius] values := enumProperties[RadialGradientRadius]
if n, ok := enumStringToInt(text, values.values, false); ok { if n, ok := enumStringToInt(text, values.values, false); ok {
buffer.WriteString(shapeText) writeStrings(buffer, shapeText, values.cssValues[n], " ")
shapeText = "" shapeText = ""
buffer.WriteString(values.cssValues[n])
buffer.WriteString(" ")
} else { } else {
if r, ok := StringToSizeUnit(text); ok && r.Type != Auto { if r, ok := StringToSizeUnit(text); ok && r.Type != Auto {
buffer.WriteString("ellipse ") writeStrings(buffer, "ellipse ", r.cssString("", session), " ", r.cssString("", session), " ")
shapeText = "" shapeText = ""
buffer.WriteString(r.cssString("", session))
buffer.WriteString(" ")
buffer.WriteString(r.cssString("", session))
buffer.WriteString(" ")
} else { } else {
ErrorLog(`Invalid radial gradient radius: ` + text) ErrorLog(`Invalid radial gradient radius: ` + text)
} }
@ -250,29 +244,20 @@ func (gradient *backgroundRadialGradient) cssStyle(session Session) string {
case int: case int:
values := enumProperties[RadialGradientRadius].cssValues values := enumProperties[RadialGradientRadius].cssValues
if value >= 0 && value < len(values) { if value >= 0 && value < len(values) {
buffer.WriteString(shapeText) writeStrings(buffer, shapeText, values[value], " ")
shapeText = "" shapeText = ""
buffer.WriteString(values[value])
buffer.WriteString(" ")
} else { } else {
ErrorLogF(`Invalid radial gradient radius: %d`, value) ErrorLogF(`Invalid radial gradient radius: %d`, value)
} }
case SizeUnit: case SizeUnit:
if value.Type != Auto { if value.Type != Auto {
buffer.WriteString("ellipse ") writeStrings(buffer, "ellipse ", value.cssString("", session), " ", value.cssString("", session), " ")
shapeText = "" shapeText = ""
buffer.WriteString(value.cssString("", session))
buffer.WriteString(" ")
buffer.WriteString(value.cssString("", session))
buffer.WriteString(" ")
} }
case []SizeUnit: case []SizeUnit:
count := len(value) count := max(len(value), 2)
if count > 2 {
count = 2
}
buffer.WriteString("ellipse ") buffer.WriteString("ellipse ")
shapeText = "" shapeText = ""
for i := range count { for i := range count {
@ -281,10 +266,7 @@ func (gradient *backgroundRadialGradient) cssStyle(session Session) string {
} }
case []any: case []any:
count := len(value) count := max(len(value), 2)
if count > 2 {
count = 2
}
buffer.WriteString("ellipse ") buffer.WriteString("ellipse ")
shapeText = "" shapeText = ""
for i := range count { for i := range count {
@ -316,13 +298,7 @@ func (gradient *backgroundRadialGradient) cssStyle(session Session) string {
x, _ := sizeProperty(gradient, CenterX, session) x, _ := sizeProperty(gradient, CenterX, session)
y, _ := sizeProperty(gradient, CenterX, session) y, _ := sizeProperty(gradient, CenterX, session)
if x.Type != Auto || y.Type != Auto { if x.Type != Auto || y.Type != Auto {
if shapeText != "" { writeStrings(buffer, shapeText, "at ", x.cssString("50%", session), " ", y.cssString("50%", session))
buffer.WriteString(shapeText)
}
buffer.WriteString("at ")
buffer.WriteString(x.cssString("50%", session))
buffer.WriteString(" ")
buffer.WriteString(y.cssString("50%", session))
} else if shapeText != "" { } else if shapeText != "" {
buffer.WriteString(shapeText) buffer.WriteString(shapeText)
} }

View File

@ -386,9 +386,7 @@ func (border *borderProperty) writeString(buffer *strings.Builder, indent string
if comma { if comma {
buffer.WriteString(", ") buffer.WriteString(", ")
} }
buffer.WriteString(string(tag)) writeStrings(buffer, string(tag), " = ", text)
buffer.WriteString(" = ")
buffer.WriteString(text)
comma = true comma = true
} }
} }

View File

@ -615,19 +615,16 @@ func (canvas *canvasData) fontWithParams(name string, size SizeUnit, params Font
case SizeInPercent: case SizeInPercent:
if params.LineHeight.Value != 100 { if params.LineHeight.Value != 100 {
buffer.WriteString("/") writeStrings(buffer, "/", strconv.FormatFloat(params.LineHeight.Value/100, 'g', -1, 64))
buffer.WriteString(strconv.FormatFloat(params.LineHeight.Value/100, 'g', -1, 64))
} }
case SizeInFraction: case SizeInFraction:
if params.LineHeight.Value != 1 { if params.LineHeight.Value != 1 {
buffer.WriteString("/") writeStrings(buffer, "/", strconv.FormatFloat(params.LineHeight.Value, 'g', -1, 64))
buffer.WriteString(strconv.FormatFloat(params.LineHeight.Value, 'g', -1, 64))
} }
default: default:
buffer.WriteString("/") writeStrings(buffer, "/", params.LineHeight.cssString("", canvas.View().Session()))
buffer.WriteString(params.LineHeight.cssString("", canvas.View().Session()))
} }
lead := " " lead := " "
@ -636,9 +633,7 @@ func (canvas *canvasData) fontWithParams(name string, size SizeUnit, params Font
buffer.WriteString(lead) buffer.WriteString(lead)
lead = "," lead = ","
if strings.ContainsRune(font, ' ') { if strings.ContainsRune(font, ' ') {
buffer.WriteRune('"') writeStrings(buffer, `"`, font, `"`)
buffer.WriteString(font)
buffer.WriteRune('"')
} else { } else {
buffer.WriteString(font) buffer.WriteString(font)
} }

View File

@ -199,13 +199,11 @@ func (button *checkboxData) cssStyle(self View, builder cssBuilder) {
} }
func checkboxHtml(button View, buffer *strings.Builder, checked bool) (int, int) { func checkboxHtml(button View, buffer *strings.Builder, checked bool) (int, int) {
//func (button *checkboxData) htmlCheckbox(buffer *strings.Builder, checked bool) (int, int) {
vAlign := GetCheckboxVerticalAlign(button) vAlign := GetCheckboxVerticalAlign(button)
hAlign := GetCheckboxHorizontalAlign(button) hAlign := GetCheckboxHorizontalAlign(button)
buffer.WriteString(`<div id="`) writeStrings(buffer, `<div id="`, button.htmlID(), `checkbox" style="display: grid;`)
buffer.WriteString(button.htmlID())
buffer.WriteString(`checkbox" style="display: grid;`)
if hAlign == CenterAlign { if hAlign == CenterAlign {
buffer.WriteString(" justify-items: center; grid-column-start: 1; grid-column-end: 2;") buffer.WriteString(" justify-items: center; grid-column-start: 1; grid-column-end: 2;")
if vAlign == BottomAlign { if vAlign == BottomAlign {
@ -253,9 +251,7 @@ func (button *checkboxData) htmlSubviews(self View, buffer *strings.Builder) {
vCheckboxAlign, hCheckboxAlign := checkboxHtml(button, buffer, IsCheckboxChecked(button)) vCheckboxAlign, hCheckboxAlign := checkboxHtml(button, buffer, IsCheckboxChecked(button))
buffer.WriteString(`<div id="`) writeStrings(buffer, `<div id="`, button.htmlID(), `content" style="display: grid;`)
buffer.WriteString(button.htmlID())
buffer.WriteString(`content" style="display: grid;`)
if hCheckboxAlign == LeftAlign { if hCheckboxAlign == LeftAlign {
buffer.WriteString(" grid-column-start: 2; grid-column-end: 3;") buffer.WriteString(" grid-column-start: 2; grid-column-end: 3;")
} else { } else {
@ -268,15 +264,9 @@ func (button *checkboxData) htmlSubviews(self View, buffer *strings.Builder) {
buffer.WriteString(" grid-row-start: 1; grid-row-end: 2;") buffer.WriteString(" grid-row-start: 1; grid-row-end: 2;")
} }
buffer.WriteString(" align-items: ") writeStrings(buffer,
buffer.WriteString(checkboxVerticalAlignCSS(button)) " align-items: ", checkboxVerticalAlignCSS(button), `;`,
buffer.WriteRune(';') " justify-items: ", checkboxHorizontalAlignCSS(button), `;">`)
buffer.WriteString(" justify-items: ")
buffer.WriteString(checkboxHorizontalAlignCSS(button))
buffer.WriteRune(';')
buffer.WriteString(`">`)
button.viewsContainerData.htmlSubviews(self, buffer) button.viewsContainerData.htmlSubviews(self, buffer)
buffer.WriteString(`</div>`) buffer.WriteString(`</div>`)
} }

View File

@ -249,9 +249,7 @@ func (clip *insetClipData) writeString(buffer *strings.Builder, indent string) {
if comma { if comma {
buffer.WriteString(", ") buffer.WriteString(", ")
} }
buffer.WriteString(string(tag)) writeStrings(buffer, string(tag), " = ", text)
buffer.WriteString(" = ")
buffer.WriteString(text)
comma = true comma = true
} }
} }
@ -325,9 +323,7 @@ func (clip *circleClipData) writeString(buffer *strings.Builder, indent string)
if comma { if comma {
buffer.WriteString(", ") buffer.WriteString(", ")
} }
buffer.WriteString(string(tag)) writeStrings(buffer, string(tag), " = ", text)
buffer.WriteString(" = ")
buffer.WriteString(text)
comma = true comma = true
} }
} }
@ -341,18 +337,12 @@ func (clip *circleClipData) cssStyle(session Session) string {
buffer := allocStringBuilder() buffer := allocStringBuilder()
defer freeStringBuilder(buffer) defer freeStringBuilder(buffer)
buffer.WriteString("circle(")
r, _ := sizeProperty(clip, Radius, session) r, _ := sizeProperty(clip, Radius, session)
buffer.WriteString(r.cssString("50%", session))
buffer.WriteString(" at ")
x, _ := sizeProperty(clip, X, session) x, _ := sizeProperty(clip, X, session)
buffer.WriteString(x.cssString("50%", session))
buffer.WriteRune(' ')
y, _ := sizeProperty(clip, Y, session) y, _ := sizeProperty(clip, Y, session)
buffer.WriteString(y.cssString("50%", session))
buffer.WriteRune(')') writeStrings(buffer, "circle(", r.cssString("50%", session),
" at ", x.cssString("50%", session), " ", y.cssString("50%", session), ")")
return buffer.String() return buffer.String()
} }
@ -405,9 +395,7 @@ func (clip *ellipseClipData) writeString(buffer *strings.Builder, indent string)
if comma { if comma {
buffer.WriteString(", ") buffer.WriteString(", ")
} }
buffer.WriteString(string(tag)) writeStrings(buffer, string(tag), " = ", text)
buffer.WriteString(" = ")
buffer.WriteString(text)
comma = true comma = true
} }
} }
@ -423,19 +411,11 @@ func (clip *ellipseClipData) cssStyle(session Session) string {
rx, _ := sizeProperty(clip, RadiusX, session) rx, _ := sizeProperty(clip, RadiusX, session)
ry, _ := sizeProperty(clip, RadiusX, session) ry, _ := sizeProperty(clip, RadiusX, session)
buffer.WriteString("ellipse(")
buffer.WriteString(rx.cssString("50%", session))
buffer.WriteRune(' ')
buffer.WriteString(ry.cssString("50%", session))
buffer.WriteString(" at ")
x, _ := sizeProperty(clip, X, session) x, _ := sizeProperty(clip, X, session)
buffer.WriteString(x.cssString("50%", session))
buffer.WriteRune(' ')
y, _ := sizeProperty(clip, Y, session) y, _ := sizeProperty(clip, Y, session)
buffer.WriteString(y.cssString("50%", session))
buffer.WriteRune(')') writeStrings(buffer, "ellipse(", rx.cssString("50%", session), " ", ry.cssString("50%", session),
" at ", x.cssString("50%", session), " ", y.cssString("50%", session), ")")
return buffer.String() return buffer.String()
} }

View File

@ -209,11 +209,7 @@ func stringToColor(text string) (Color, error) {
func writeColorCSS(buffer *strings.Builder, lightColor, darkColor Color, session Session) { func writeColorCSS(buffer *strings.Builder, lightColor, darkColor Color, session Session) {
if session.lightDarkSupport() { if session.lightDarkSupport() {
buffer.WriteString("light-dark(") writeStrings(buffer, "light-dark(", lightColor.cssString(), ",", darkColor.cssString(), ")")
buffer.WriteString(lightColor.cssString())
buffer.WriteRune(',')
buffer.WriteString(darkColor.cssString())
buffer.WriteRune(')')
} else if session.DarkTheme() { } else if session.DarkTheme() {
buffer.WriteString(darkColor.cssString()) buffer.WriteString(darkColor.cssString())
} else { } else {

View File

@ -147,11 +147,8 @@ func (picker *colorPickerData) htmlSubviews(self View, buffer *strings.Builder)
func (picker *colorPickerData) htmlProperties(self View, buffer *strings.Builder) { func (picker *colorPickerData) htmlProperties(self View, buffer *strings.Builder) {
picker.viewData.htmlProperties(self, buffer) picker.viewData.htmlProperties(self, buffer)
buffer.WriteString(` type="color" value="`) writeStrings(buffer, ` type="color" value="`, GetColorPickerValue(picker).rgbString(), `"`,
buffer.WriteString(GetColorPickerValue(picker).rgbString()) ` oninput="editViewInputEvent(this)"`)
buffer.WriteByte('"')
buffer.WriteString(` oninput="editViewInputEvent(this)"`)
if picker.getRaw(ClickEvent) == nil { if picker.getRaw(ClickEvent) == nil {
buffer.WriteString(` onclick="stopEventPropagation(this, event)"`) buffer.WriteString(` onclick="stopEventPropagation(this, event)"`)
} }

View File

@ -88,10 +88,7 @@ func (builder *viewCSSBuilder) add(key, value string) {
builder.buffer.WriteRune(' ') builder.buffer.WriteRune(' ')
} }
builder.buffer.WriteString(key) writeStrings(builder.buffer, key, ": ", value, ";")
builder.buffer.WriteString(": ")
builder.buffer.WriteString(value)
builder.buffer.WriteRune(';')
} }
} }
@ -193,9 +190,7 @@ func (builder *cssStyleBuilder) startMedia(rule string) {
if builder.buffer == nil { if builder.buffer == nil {
builder.init(0) builder.init(0)
} }
builder.buffer.WriteString(`@media screen`) writeStrings(builder.buffer, `@media screen`, rule, ` {\n`)
builder.buffer.WriteString(rule)
builder.buffer.WriteString(` {\n`)
builder.media = true builder.media = true
} }
@ -247,9 +242,7 @@ func (builder *cssStyleBuilder) startAnimation(name string) {
} }
builder.media = true builder.media = true
builder.buffer.WriteString(`\n@keyframes `) writeStrings(builder.buffer, `\n@keyframes `, name, ` {\n`)
builder.buffer.WriteString(name)
builder.buffer.WriteString(` {\n`)
} }
func (builder *cssStyleBuilder) endAnimation() { func (builder *cssStyleBuilder) endAnimation() {
@ -265,9 +258,7 @@ func (builder *cssStyleBuilder) startAnimationFrame(name string) {
builder.init(0) builder.init(0)
} }
builder.buffer.WriteString(`\t`) writeStrings(builder.buffer, `\t`, name, ` {\n`)
builder.buffer.WriteString(name)
builder.buffer.WriteString(` {\n`)
} }
func (builder *cssStyleBuilder) endAnimationFrame() { func (builder *cssStyleBuilder) endAnimationFrame() {
@ -285,11 +276,7 @@ func (builder *cssStyleBuilder) add(key, value string) {
if builder.media { if builder.media {
builder.buffer.WriteString(`\t`) builder.buffer.WriteString(`\t`)
} }
builder.buffer.WriteString(`\t`) writeStrings(builder.buffer, `\t`, key, `: `, value, `;\n`)
builder.buffer.WriteString(key)
builder.buffer.WriteString(`: `)
builder.buffer.WriteString(value)
builder.buffer.WriteString(`;\n`)
} }
} }
@ -300,9 +287,7 @@ func (builder *cssStyleBuilder) addWriter(key string, writer func(buffer *string
if builder.media { if builder.media {
builder.buffer.WriteString(`\t`) builder.buffer.WriteString(`\t`)
} }
builder.buffer.WriteString(`\t`) writeStrings(builder.buffer, `\t`, key, ": ")
builder.buffer.WriteString(key)
builder.buffer.WriteString(": ")
writer(builder.buffer) writer(builder.buffer)
builder.buffer.WriteString(`;\n`) builder.buffer.WriteString(`;\n`)
} }
@ -318,9 +303,7 @@ func (builder *cssStyleBuilder) addValues(key, separator string, values ...strin
if builder.media { if builder.media {
builder.buffer.WriteString(`\t`) builder.buffer.WriteString(`\t`)
} }
builder.buffer.WriteString(`\t`) writeStrings(builder.buffer, `\t`, key, `: `)
builder.buffer.WriteString(key)
builder.buffer.WriteString(`: `)
for i, value := range values { for i, value := range values {
if i > 0 { if i > 0 {
builder.buffer.WriteString(separator) builder.buffer.WriteString(separator)

View File

@ -283,9 +283,8 @@ func getDataListProperty(properties Properties) []string {
func dataListHtmlSubviews(view View, buffer *strings.Builder, normalizeItem func(text string, session Session) string) { func dataListHtmlSubviews(view View, buffer *strings.Builder, normalizeItem func(text string, session Session) string) {
if items := getDataListProperty(view); len(items) > 0 { if items := getDataListProperty(view); len(items) > 0 {
session := view.Session() session := view.Session()
buffer.WriteString(`<datalist id="`)
buffer.WriteString(dataListID(view)) writeStrings(buffer, `<datalist id="`, dataListID(view), `">`)
buffer.WriteString(`">`)
for _, text := range items { for _, text := range items {
text = normalizeItem(text, session) text = normalizeItem(text, session)
@ -295,9 +294,7 @@ func dataListHtmlSubviews(view View, buffer *strings.Builder, normalizeItem func
if strings.ContainsRune(text, '\n') { if strings.ContainsRune(text, '\n') {
text = strings.ReplaceAll(text, "\n", `\n`) text = strings.ReplaceAll(text, "\n", `\n`)
} }
buffer.WriteString(`<option value="`) writeStrings(buffer, `<option value="`, textToHtml(text), `"></option>`)
buffer.WriteString(textToHtml(text))
buffer.WriteString(`"></option>`)
} }
buffer.WriteString(`</datalist>`) buffer.WriteString(`</datalist>`)
} }
@ -305,9 +302,7 @@ func dataListHtmlSubviews(view View, buffer *strings.Builder, normalizeItem func
func dataListHtmlProperties(view View, buffer *strings.Builder) { func dataListHtmlProperties(view View, buffer *strings.Builder) {
if len(getDataListProperty(view)) > 0 { if len(getDataListProperty(view)) > 0 {
buffer.WriteString(` list="`) writeStrings(buffer, ` list="`, dataListID(view), `"`)
buffer.WriteString(dataListID(view))
buffer.WriteString(`"`)
} }
} }

View File

@ -319,26 +319,18 @@ func (picker *datePickerData) htmlProperties(self View, buffer *strings.Builder)
buffer.WriteString(` type="date"`) buffer.WriteString(` type="date"`)
if min, ok := getDateProperty(picker, DatePickerMin, Min); ok { if min, ok := getDateProperty(picker, DatePickerMin, Min); ok {
buffer.WriteString(` min="`) writeStrings(buffer, ` min="`, min.Format(dateFormat), `"`)
buffer.WriteString(min.Format(dateFormat))
buffer.WriteByte('"')
} }
if max, ok := getDateProperty(picker, DatePickerMax, Max); ok { if max, ok := getDateProperty(picker, DatePickerMax, Max); ok {
buffer.WriteString(` max="`) writeStrings(buffer, ` max="`, max.Format(dateFormat), `"`)
buffer.WriteString(max.Format(dateFormat))
buffer.WriteByte('"')
} }
if step, ok := intProperty(picker, DatePickerStep, picker.Session(), 0); ok && step > 0 { if step, ok := intProperty(picker, DatePickerStep, picker.Session(), 0); ok && step > 0 {
buffer.WriteString(` step="`) writeStrings(buffer, ` step="`, strconv.Itoa(step), `"`)
buffer.WriteString(strconv.Itoa(step))
buffer.WriteByte('"')
} }
buffer.WriteString(` value="`) writeStrings(buffer, ` value="`, GetDatePickerValue(picker).Format(dateFormat), `"`)
buffer.WriteString(GetDatePickerValue(picker).Format(dateFormat))
buffer.WriteByte('"')
buffer.WriteString(` oninput="editViewInputEvent(this)"`) buffer.WriteString(` oninput="editViewInputEvent(this)"`)
if picker.getRaw(ClickEvent) == nil { if picker.getRaw(ClickEvent) == nil {

View File

@ -415,9 +415,7 @@ func dragAndDropHtml(view View, buffer *strings.Builder) {
} }
if dragData := base64DragData(view); dragData != "" { if dragData := base64DragData(view); dragData != "" {
buffer.WriteString(`draggable="true" data-drag="`) writeStrings(buffer, `draggable="true" data-drag="`, dragData, `" ondragstart="dragStartEvent(this, event)" `)
buffer.WriteString(dragData)
buffer.WriteString(`" ondragstart="dragStartEvent(this, event)" `)
} else if len(getOneArgEventListeners[View, DragAndDropEvent](view, nil, DragStartEvent)) > 0 { } else if len(getOneArgEventListeners[View, DragAndDropEvent](view, nil, DragStartEvent)) > 0 {
buffer.WriteString(` ondragstart="dragStartEvent(this, event)" `) buffer.WriteString(` ondragstart="dragStartEvent(this, event)" `)
} }
@ -444,9 +442,7 @@ func dragAndDropHtml(view View, buffer *strings.Builder) {
} }
if img := GetDragImage(view); img != "" { if img := GetDragImage(view); img != "" {
buffer.WriteString(` data-drag-image="`) writeStrings(buffer, ` data-drag-image="`, img, `" `)
buffer.WriteString(img)
buffer.WriteString(`" `)
} }
if f := GetDragImageXOffset(view); f != 0 { if f := GetDragImageXOffset(view); f != 0 {
@ -463,9 +459,7 @@ func dragAndDropHtml(view View, buffer *strings.Builder) {
effects := []string{"undefined", "copy", "move", "copyMove", "link", "copyLink", "linkMove", "all"} effects := []string{"undefined", "copy", "move", "copyMove", "link", "copyLink", "linkMove", "all"}
if n := GetDropEffectAllowed(view); n > 0 && n < len(effects) { if n := GetDropEffectAllowed(view); n > 0 && n < len(effects) {
buffer.WriteString(` data-drop-effect-allowed="`) writeStrings(buffer, ` data-drop-effect-allowed="`, effects[n], `" `)
buffer.WriteString(effects[n])
buffer.WriteString(`" `)
} }
} }

View File

@ -1,6 +1,7 @@
package rui package rui
import ( import (
"slices"
"strconv" "strconv"
"strings" "strings"
) )
@ -211,13 +212,7 @@ func (list *dropDownListData) htmlSubviews(self View, buffer *strings.Builder) {
disabledItems := GetDropDownDisabledItems(list) disabledItems := GetDropDownDisabledItems(list)
separators := GetDropDownItemSeparators(list) separators := GetDropDownItemSeparators(list)
for i, item := range items { for i, item := range items {
disabled := false disabled := slices.Contains(disabledItems, i)
for _, index := range disabledItems {
if i == index {
disabled = true
break
}
}
if disabled { if disabled {
buffer.WriteString("<option disabled>") buffer.WriteString("<option disabled>")
@ -232,11 +227,8 @@ func (list *dropDownListData) htmlSubviews(self View, buffer *strings.Builder) {
buffer.WriteString(textToHtml(item)) buffer.WriteString(textToHtml(item))
buffer.WriteString("</option>") buffer.WriteString("</option>")
for _, index := range separators { if slices.Contains(separators, i) {
if i == index { buffer.WriteString("<hr>")
buffer.WriteString("<hr>")
break
}
} }
} }
} }

View File

@ -352,9 +352,7 @@ func (edit *editViewData) htmlProperties(self View, buffer *strings.Builder) {
} }
if maxLength := GetMaxLength(edit); maxLength > 0 { if maxLength := GetMaxLength(edit); maxLength > 0 {
buffer.WriteString(` maxlength="`) writeStrings(buffer, ` maxlength="`, strconv.Itoa(maxLength), `"`)
buffer.WriteString(strconv.Itoa(maxLength))
buffer.WriteByte('"')
} }
convertText := func(text string) string { convertText := func(text string) string {
@ -368,23 +366,17 @@ func (edit *editViewData) htmlProperties(self View, buffer *strings.Builder) {
} }
if hint := GetHint(edit); hint != "" { if hint := GetHint(edit); hint != "" {
buffer.WriteString(` placeholder="`) writeStrings(buffer, ` placeholder="`, convertText(hint), `"`)
buffer.WriteString(convertText(hint))
buffer.WriteByte('"')
} }
buffer.WriteString(` oninput="editViewInputEvent(this)"`) buffer.WriteString(` oninput="editViewInputEvent(this)"`)
if pattern := GetEditViewPattern(edit); pattern != "" { if pattern := GetEditViewPattern(edit); pattern != "" {
buffer.WriteString(` pattern="`) writeStrings(buffer, ` pattern="`, convertText(pattern), `"`)
buffer.WriteString(convertText(pattern))
buffer.WriteByte('"')
} }
if editType != MultiLineText { if editType != MultiLineText {
if text := GetText(edit); text != "" { if text := GetText(edit); text != "" {
buffer.WriteString(` value="`) writeStrings(buffer, ` value="`, convertText(text), `"`)
buffer.WriteString(convertText(text))
buffer.WriteByte('"')
} }
} }

View File

@ -45,10 +45,7 @@ func viewEventsHtml[T any](view View, events []PropertyName, buffer *strings.Bui
for _, tag := range events { for _, tag := range events {
if js, ok := eventJsFunc[tag]; ok { if js, ok := eventJsFunc[tag]; ok {
if value := getOneArgEventListeners[View, T](view, nil, tag); len(value) > 0 { if value := getOneArgEventListeners[View, T](view, nil, tag); len(value) > 0 {
buffer.WriteString(js.jsEvent) writeStrings(buffer, js.jsEvent, `="`, js.jsFunc, `(this, event)" `)
buffer.WriteString(`="`)
buffer.WriteString(js.jsFunc)
buffer.WriteString(`(this, event)" `)
} }
} }
} }

View File

@ -258,9 +258,7 @@ func (picker *filePickerData) htmlProperties(self View, buffer *strings.Builder)
picker.viewData.htmlProperties(self, buffer) picker.viewData.htmlProperties(self, buffer)
if accept := acceptPropertyCSS(picker); accept != "" { if accept := acceptPropertyCSS(picker); accept != "" {
buffer.WriteString(` accept="`) writeStrings(buffer, ` accept="`, accept, `"`)
buffer.WriteString(accept)
buffer.WriteRune('"')
} }
buffer.WriteString(` type="file"`) buffer.WriteString(` type="file"`)

View File

@ -236,10 +236,7 @@ func (filter *filterData) cssStyle(session Session) string {
defer freeStringBuilder(buffer) defer freeStringBuilder(buffer)
if value, ok := floatTextProperty(filter, Blur, session, 0); ok { if value, ok := floatTextProperty(filter, Blur, session, 0); ok {
buffer.WriteString(string(Blur)) writeStrings(buffer, string(Blur), "(", value, "px)")
buffer.WriteRune('(')
buffer.WriteString(value)
buffer.WriteString("px)")
} }
for _, tag := range []PropertyName{Brightness, Contrast, Saturate, Grayscale, Invert, Opacity, Sepia} { for _, tag := range []PropertyName{Brightness, Contrast, Saturate, Grayscale, Invert, Opacity, Sepia} {
@ -247,10 +244,7 @@ func (filter *filterData) cssStyle(session Session) string {
if buffer.Len() > 0 { if buffer.Len() > 0 {
buffer.WriteRune(' ') buffer.WriteRune(' ')
} }
buffer.WriteString(string(tag)) writeStrings(buffer, string(tag), "(", value, "%)")
buffer.WriteRune('(')
buffer.WriteString(value)
buffer.WriteString("%)")
} }
} }
@ -258,10 +252,7 @@ func (filter *filterData) cssStyle(session Session) string {
if buffer.Len() > 0 { if buffer.Len() > 0 {
buffer.WriteRune(' ') buffer.WriteRune(' ')
} }
buffer.WriteString(string(HueRotate)) writeStrings(buffer, string(HueRotate), "(", value.cssString(), ")")
buffer.WriteRune('(')
buffer.WriteString(value.cssString())
buffer.WriteRune(')')
} }
var lead string var lead string

View File

@ -39,10 +39,7 @@ func focusEventsHtml(view View, buffer *strings.Builder) {
if view.Focusable() { if view.Focusable() {
for _, tag := range []PropertyName{FocusEvent, LostFocusEvent} { for _, tag := range []PropertyName{FocusEvent, LostFocusEvent} {
if js, ok := eventJsFunc[tag]; ok { if js, ok := eventJsFunc[tag]; ok {
buffer.WriteString(js.jsEvent) writeStrings(buffer, js.jsEvent, `="`, js.jsFunc, `(this, event)" `)
buffer.WriteString(`="`)
buffer.WriteString(js.jsFunc)
buffer.WriteString(`(this, event)" `)
} }
} }
} }

View File

@ -252,21 +252,15 @@ func (imageView *imageViewData) htmlProperties(self View, buffer *strings.Builde
if imageResource, ok := imageProperty(imageView, Source, imageView.Session()); ok && imageResource != "" { if imageResource, ok := imageProperty(imageView, Source, imageView.Session()); ok && imageResource != "" {
if src, srcset := imageViewSrc(imageView, imageResource); src != "" { if src, srcset := imageViewSrc(imageView, imageResource); src != "" {
buffer.WriteString(` src="`) writeStrings(buffer, ` src="`, src, `"`)
buffer.WriteString(src)
buffer.WriteString(`"`)
if srcset != "" { if srcset != "" {
buffer.WriteString(` srcset="`) writeStrings(buffer, ` srcset="`, srcset, `"`)
buffer.WriteString(srcset)
buffer.WriteString(`"`)
} }
} }
} }
if text := GetImageViewAltText(imageView); text != "" { if text := GetImageViewAltText(imageView); text != "" {
buffer.WriteString(` alt="`) writeStrings(buffer, ` alt="`, text, `"`)
buffer.WriteString(text)
buffer.WriteString(`"`)
} }
buffer.WriteString(` onload="imageLoaded(this, event)"`) buffer.WriteString(` onload="imageLoaded(this, event)"`)

View File

@ -494,30 +494,22 @@ func (listView *listViewData) getItemFrames() []Frame {
func (listView *listViewData) itemAlign(buffer *strings.Builder) { func (listView *listViewData) itemAlign(buffer *strings.Builder) {
values := enumProperties[ItemHorizontalAlign].cssValues values := enumProperties[ItemHorizontalAlign].cssValues
if hAlign := GetListItemHorizontalAlign(listView); hAlign >= 0 && hAlign < len(values) { if hAlign := GetListItemHorizontalAlign(listView); hAlign >= 0 && hAlign < len(values) {
buffer.WriteString(" justify-items: ") writeStrings(buffer, " justify-items: ", values[hAlign], ";")
buffer.WriteString(values[hAlign])
buffer.WriteRune(';')
} }
values = enumProperties[ItemVerticalAlign].cssValues values = enumProperties[ItemVerticalAlign].cssValues
if vAlign := GetListItemVerticalAlign(listView); vAlign >= 0 && vAlign < len(values) { if vAlign := GetListItemVerticalAlign(listView); vAlign >= 0 && vAlign < len(values) {
buffer.WriteString(" align-items: ") writeStrings(buffer, " align-items: ", values[vAlign], ";")
buffer.WriteString(values[vAlign])
buffer.WriteRune(';')
} }
} }
func (listView *listViewData) itemSize(buffer *strings.Builder) { func (listView *listViewData) itemSize(buffer *strings.Builder) {
if itemWidth := GetListItemWidth(listView); itemWidth.Type != Auto { if itemWidth := GetListItemWidth(listView); itemWidth.Type != Auto {
buffer.WriteString(` min-width: `) writeStrings(buffer, ` min-width: `, itemWidth.cssString("", listView.Session()), ";")
buffer.WriteString(itemWidth.cssString("", listView.Session()))
buffer.WriteRune(';')
} }
if itemHeight := GetListItemHeight(listView); itemHeight.Type != Auto { if itemHeight := GetListItemHeight(listView); itemHeight.Type != Auto {
buffer.WriteString(` min-height: `) writeStrings(buffer, ` min-height: `, itemHeight.cssString("", listView.Session()), ";")
buffer.WriteString(itemHeight.cssString("", listView.Session()))
buffer.WriteRune(';')
} }
} }
@ -610,9 +602,7 @@ func (listView *listViewData) checkboxItemDiv(hCheckboxAlign, vCheckboxAlign int
} }
if gap, ok := sizeConstant(listView.session, "ruiCheckboxGap"); ok && gap.Type != Auto { if gap, ok := sizeConstant(listView.session, "ruiCheckboxGap"); ok && gap.Type != Auto {
itemStyleBuilder.WriteString(` grid-gap: `) writeStrings(itemStyleBuilder, ` grid-gap: `, gap.cssString("auto", listView.Session()), ";")
itemStyleBuilder.WriteString(gap.cssString("auto", listView.Session()))
itemStyleBuilder.WriteRune(';')
} }
itemStyleBuilder.WriteString(`">`) itemStyleBuilder.WriteString(`">`)
@ -690,12 +680,8 @@ func (listView *listViewData) checkboxSubviews(adapter ListAdapter, buffer *stri
enabledItems := listView.itemEnabledAdapter(adapter) enabledItems := listView.itemEnabledAdapter(adapter)
for i := range count { for i := range count {
buffer.WriteString(`<div id="`) writeStrings(buffer, `<div id="`, listViewID, "-", strconv.Itoa(i),
buffer.WriteString(listViewID) `" class="ruiView `, listView.listItemStyle())
buffer.WriteRune('-')
buffer.WriteString(strconv.Itoa(i))
buffer.WriteString(`" class="ruiView `)
buffer.WriteString(listView.listItemStyle())
if i == current { if i == current {
buffer.WriteRune(' ') buffer.WriteRune(' ')
buffer.WriteString(listViewCurrentInactiveStyle(listView)) buffer.WriteString(listViewCurrentInactiveStyle(listView))
@ -710,15 +696,10 @@ func (listView *listViewData) checkboxSubviews(adapter ListAdapter, buffer *stri
} }
buffer.WriteString(itemDiv) buffer.WriteString(itemDiv)
checked := false checked := slices.Contains(checkedItems, i)
for _, index := range checkedItems { if checked {
if index == i { buffer.WriteString(onDiv)
buffer.WriteString(onDiv) } else {
checked = true
break
}
}
if !checked {
buffer.WriteString(offDiv) buffer.WriteString(offDiv)
} }
buffer.WriteString(contentDiv) buffer.WriteString(contentDiv)
@ -753,18 +734,12 @@ func (listView *listViewData) noneCheckboxSubviews(adapter ListAdapter, buffer *
enabledItems := listView.itemEnabledAdapter(adapter) enabledItems := listView.itemEnabledAdapter(adapter)
for i := range count { for i := range count {
buffer.WriteString(`<div id="`) writeStrings(buffer, `<div id="`, listViewID, "-", strconv.Itoa(i),
buffer.WriteString(listViewID) `" class="ruiView `, listView.listItemStyle())
buffer.WriteRune('-')
buffer.WriteString(strconv.Itoa(i))
buffer.WriteString(`" class="ruiView `)
buffer.WriteString(listView.listItemStyle())
if i == current { if i == current {
buffer.WriteRune(' ') writeStrings(buffer, " ", listViewCurrentInactiveStyle(listView))
buffer.WriteString(listViewCurrentInactiveStyle(listView))
} }
buffer.WriteString(`" `) writeStrings(buffer, `" `, itemStyle)
buffer.WriteString(itemStyle)
if enabledItems != nil && !enabledItems.IsListItemEnabled(i) { if enabledItems != nil && !enabledItems.IsListItemEnabled(i) {
buffer.WriteString(` inert`) buffer.WriteString(` inert`)
} }
@ -814,20 +789,15 @@ func (listView *listViewData) updateCheckboxItem(index int, checked bool) {
func (listView *listViewData) htmlProperties(self View, buffer *strings.Builder) { func (listView *listViewData) htmlProperties(self View, buffer *strings.Builder) {
listView.viewData.htmlProperties(self, buffer) listView.viewData.htmlProperties(self, buffer)
buffer.WriteString(`onfocus="listViewFocusEvent(this, event)" onblur="listViewBlurEvent(this, event)"`)
buffer.WriteString(` onkeydown="listViewKeyDownEvent(this, event)" data-focusitemstyle="`) writeStrings(buffer, `onfocus="listViewFocusEvent(this, event)" onblur="listViewBlurEvent(this, event)"`,
buffer.WriteString(listViewCurrentStyle(listView)) ` onkeydown="listViewKeyDownEvent(this, event)"`,
buffer.WriteString(`" data-bluritemstyle="`) ` data-focusitemstyle="`, listViewCurrentStyle(listView),
buffer.WriteString(listViewCurrentInactiveStyle(listView)) `" data-bluritemstyle="`, listViewCurrentInactiveStyle(listView), `"`)
buffer.WriteString(`"`)
if adapter := listView.getAdapter(); adapter != nil { if adapter := listView.getAdapter(); adapter != nil {
if current := GetCurrent(listView); current >= 0 && current < adapter.ListSize() { if current := GetCurrent(listView); current >= 0 && current < adapter.ListSize() {
buffer.WriteString(` data-current="`) writeStrings(buffer, ` data-current="`, listView.htmlID(), "-", strconv.Itoa(current), `"`)
buffer.WriteString(listView.htmlID())
buffer.WriteRune('-')
buffer.WriteString(strconv.Itoa(current))
buffer.WriteRune('"')
} }
} }
listView.viewData.htmlProperties(self, buffer) listView.viewData.htmlProperties(self, buffer)
@ -852,15 +822,11 @@ func listDiv(listView View, buffer *strings.Builder) {
buffer.WriteString(`<div style="display: flex; align-content: stretch;`) buffer.WriteString(`<div style="display: flex; align-content: stretch;`)
if gap := GetListRowGap(listView); gap.Type != Auto { if gap := GetListRowGap(listView); gap.Type != Auto {
buffer.WriteString(` row-gap: `) writeStrings(buffer, ` row-gap: `, gap.cssString("0", listView.Session()), ";")
buffer.WriteString(gap.cssString("0", listView.Session()))
buffer.WriteRune(';')
} }
if gap := GetListColumnGap(listView); gap.Type != Auto { if gap := GetListColumnGap(listView); gap.Type != Auto {
buffer.WriteString(` column-gap: `) writeStrings(buffer, ` column-gap: `, gap.cssString("0", listView.Session()), ";")
buffer.WriteString(gap.cssString("0", listView.Session()))
buffer.WriteRune(';')
} }
wrap := GetListWrap(listView) wrap := GetListWrap(listView)
@ -881,8 +847,7 @@ func listDiv(listView View, buffer *strings.Builder) {
} }
} }
buffer.WriteString(` flex-flow: `) writeStrings(buffer, ` flex-flow: `, enumProperties[Orientation].cssValues[orientation])
buffer.WriteString(enumProperties[Orientation].cssValues[orientation])
switch wrap { switch wrap {
case ListWrapOn: case ListWrapOn:
@ -930,11 +895,7 @@ func listDiv(listView View, buffer *strings.Builder) {
} }
if value != "" { if value != "" {
buffer.WriteRune(' ') writeStrings(buffer, " ", hAlignTag, `: `, value, ";")
buffer.WriteString(hAlignTag)
buffer.WriteString(`: `)
buffer.WriteString(value)
buffer.WriteRune(';')
} }
value = "" value = ""
@ -963,11 +924,7 @@ func listDiv(listView View, buffer *strings.Builder) {
} }
if value != "" { if value != "" {
buffer.WriteRune(' ') writeStrings(buffer, " ", vAlignTag, `: `, value, ";")
buffer.WriteString(vAlignTag)
buffer.WriteString(`: `)
buffer.WriteString(value)
buffer.WriteRune(';')
} }
buffer.WriteString(`">`) buffer.WriteString(`">`)

View File

@ -946,13 +946,9 @@ func (player *mediaPlayerData) htmlSubviews(self View, buffer *strings.Builder)
session := player.session session := player.session
for _, src := range sources { for _, src := range sources {
if url, ok := session.resolveConstants(src.Url); ok && url != "" { if url, ok := session.resolveConstants(src.Url); ok && url != "" {
buffer.WriteString(`<source src="`) writeStrings(buffer, `<source src="`, url, `"`)
buffer.WriteString(url)
buffer.WriteRune('"')
if mime, ok := session.resolveConstants(src.MimeType); ok && mime != "" { if mime, ok := session.resolveConstants(src.MimeType); ok && mime != "" {
buffer.WriteString(` type="`) writeStrings(buffer, ` type="`, mime, `"`)
buffer.WriteString(mime)
buffer.WriteRune('"')
} }
buffer.WriteRune('>') buffer.WriteRune('>')
} }
@ -972,18 +968,12 @@ func (player *mediaPlayerData) htmlProperties(self View, buffer *strings.Builder
if value, ok := enumProperty(player, Preload, player.session, 0); ok { if value, ok := enumProperty(player, Preload, player.session, 0); ok {
values := enumProperties[Preload].values values := enumProperties[Preload].values
buffer.WriteString(` preload="`) writeStrings(buffer, ` preload="`, values[value], `"`)
buffer.WriteString(values[value])
buffer.WriteRune('"')
} }
for tag, cssTag := range mediaPlayerEvents() { for tag, cssTag := range mediaPlayerEvents() {
if value := player.getRaw(tag); value != nil { if value := player.getRaw(tag); value != nil {
buffer.WriteString(` `) writeStrings(buffer, ` `, cssTag, `="playerEvent(this, '`, string(tag), `')"`)
buffer.WriteString(cssTag)
buffer.WriteString(`="playerEvent(this, '`)
buffer.WriteString(string(tag))
buffer.WriteString(`')"`)
} }
} }

View File

@ -91,13 +91,8 @@ func (progress *progressBarData) htmlTag() string {
func (progress *progressBarData) htmlProperties(self View, buffer *strings.Builder) { func (progress *progressBarData) htmlProperties(self View, buffer *strings.Builder) {
progress.viewData.htmlProperties(self, buffer) progress.viewData.htmlProperties(self, buffer)
buffer.WriteString(` max="`) writeStrings(buffer, ` max="`, strconv.FormatFloat(GetProgressBarMax(progress), 'f', -1, 64), `"`,
buffer.WriteString(strconv.FormatFloat(GetProgressBarMax(progress), 'f', -1, 64)) ` value="`, strconv.FormatFloat(GetProgressBarValue(progress), 'f', -1, 64), `"`)
buffer.WriteByte('"')
buffer.WriteString(` value="`)
buffer.WriteString(strconv.FormatFloat(GetProgressBarValue(progress), 'f', -1, 64))
buffer.WriteByte('"')
} }
// GetProgressBarMax returns the max value of ProgressBar subview. // GetProgressBarMax returns the max value of ProgressBar subview.

View File

@ -132,26 +132,19 @@ func (properties *propertyList) AllTags() []PropertyName {
func (properties *propertyList) writeToBuffer(buffer *strings.Builder, func (properties *propertyList) writeToBuffer(buffer *strings.Builder,
indent string, objectTag string, tags []PropertyName) { indent string, objectTag string, tags []PropertyName) {
buffer.WriteString(objectTag) writeStrings(buffer, objectTag, " {\n")
buffer.WriteString(" {\n")
indent2 := indent + "\t" indent2 := indent + "\t"
for _, tag := range tags { for _, tag := range tags {
if value, ok := properties.properties[tag]; ok { if value, ok := properties.properties[tag]; ok {
text := propertyValueToString(tag, value, indent2) text := propertyValueToString(tag, value, indent2)
if text != "" { if text != "" {
buffer.WriteString(indent2) writeStrings(buffer, indent2, string(tag), " = ", text, ",\n")
buffer.WriteString(string(tag))
buffer.WriteString(" = ")
buffer.WriteString(text)
buffer.WriteString(",\n")
} }
} }
} }
buffer.WriteString(indent) writeStrings(buffer, indent, "}")
buffer.WriteString("}")
} }
*/ */
@ -214,8 +207,7 @@ func (data *dataProperty) writeToBuffer(buffer *strings.Builder, indent string,
data.mutex.Lock() data.mutex.Lock()
defer data.mutex.Unlock() defer data.mutex.Unlock()
buffer.WriteString(objectName) writeStrings(buffer, objectName, "{ ")
buffer.WriteString("{ ")
comma := false comma := false
for _, tag := range tags { for _, tag := range tags {
if value, ok := data.properties[tag]; ok { if value, ok := data.properties[tag]; ok {
@ -224,9 +216,7 @@ func (data *dataProperty) writeToBuffer(buffer *strings.Builder, indent string,
if comma { if comma {
buffer.WriteString(", ") buffer.WriteString(", ")
} }
buffer.WriteString(string(tag)) writeStrings(buffer, string(tag), " = ", text)
buffer.WriteString(" = ")
buffer.WriteString(text)
comma = true comma = true
} }
} }

View File

@ -771,43 +771,31 @@ func (radius BoxRadius) String() string {
buffer.WriteString("_{ ") buffer.WriteString("_{ ")
if radius.TopLeftX.Equal(radius.TopLeftY) { if radius.TopLeftX.Equal(radius.TopLeftY) {
buffer.WriteString("top-left = ") writeStrings(buffer, "top-left = ", radius.TopLeftX.String())
buffer.WriteString(radius.TopLeftX.String())
} else { } else {
buffer.WriteString("top-left-x = ") writeStrings(buffer, "top-left-x = ", radius.TopLeftX.String(),
buffer.WriteString(radius.TopLeftX.String()) ", top-left-y = ", radius.TopLeftY.String())
buffer.WriteString("top-left-y = ")
buffer.WriteString(radius.TopLeftY.String())
} }
if radius.TopRightX.Equal(radius.TopRightY) { if radius.TopRightX.Equal(radius.TopRightY) {
buffer.WriteString(", top-right = ") writeStrings(buffer, ", top-right = ", radius.TopRightX.String())
buffer.WriteString(radius.TopRightX.String())
} else { } else {
buffer.WriteString(", top-right-x = ") writeStrings(buffer, ", top-right-x = ", radius.TopRightX.String(),
buffer.WriteString(radius.TopRightX.String()) ", top-right-y = ", radius.TopRightY.String())
buffer.WriteString(", top-right-y = ")
buffer.WriteString(radius.TopRightY.String())
} }
if radius.BottomLeftX.Equal(radius.BottomLeftY) { if radius.BottomLeftX.Equal(radius.BottomLeftY) {
buffer.WriteString(", bottom-left = ") writeStrings(buffer, ", bottom-left = ", radius.BottomLeftX.String())
buffer.WriteString(radius.BottomLeftX.String())
} else { } else {
buffer.WriteString(", bottom-left-x = ") writeStrings(buffer, ", bottom-left-x = ", radius.BottomLeftX.String(),
buffer.WriteString(radius.BottomLeftX.String()) ", bottom-left-y = ", radius.BottomLeftY.String())
buffer.WriteString(", bottom-left-y = ")
buffer.WriteString(radius.BottomLeftY.String())
} }
if radius.BottomRightX.Equal(radius.BottomRightY) { if radius.BottomRightX.Equal(radius.BottomRightY) {
buffer.WriteString(", bottom-right = ") writeStrings(buffer, ", bottom-right = ", radius.BottomRightX.String())
buffer.WriteString(radius.BottomRightX.String())
} else { } else {
buffer.WriteString(", bottom-right-x = ") writeStrings(buffer, ", bottom-right-x = ", radius.BottomRightX.String(),
buffer.WriteString(radius.BottomRightX.String()) ", bottom-right-y = ", radius.BottomRightY.String())
buffer.WriteString(", bottom-right-y = ")
buffer.WriteString(radius.BottomRightY.String())
} }
buffer.WriteString(" }") buffer.WriteString(" }")
@ -835,32 +823,24 @@ func (radius BoxRadius) cssValue(builder cssBuilder, session Session) {
if radius.AllAnglesIsEqual() { if radius.AllAnglesIsEqual() {
if !radius.TopLeftX.Equal(radius.TopLeftY) { if !radius.TopLeftX.Equal(radius.TopLeftY) {
buffer.WriteString(" / ") writeStrings(buffer, " / ", radius.TopLeftY.cssString("0", session))
buffer.WriteString(radius.TopLeftY.cssString("0", session))
} }
} else { } else {
buffer.WriteRune(' ') writeStrings(buffer, " ", radius.TopRightX.cssString("0", session),
buffer.WriteString(radius.TopRightX.cssString("0", session)) " ", radius.BottomRightX.cssString("0", session),
buffer.WriteRune(' ') " ", radius.BottomLeftX.cssString("0", session))
buffer.WriteString(radius.BottomRightX.cssString("0", session))
buffer.WriteRune(' ')
buffer.WriteString(radius.BottomLeftX.cssString("0", session))
if !radius.TopLeftX.Equal(radius.TopLeftY) || if !radius.TopLeftX.Equal(radius.TopLeftY) ||
!radius.TopRightX.Equal(radius.TopRightY) || !radius.TopRightX.Equal(radius.TopRightY) ||
!radius.BottomLeftX.Equal(radius.BottomLeftY) || !radius.BottomLeftX.Equal(radius.BottomLeftY) ||
!radius.BottomRightX.Equal(radius.BottomRightY) { !radius.BottomRightX.Equal(radius.BottomRightY) {
buffer.WriteString(" / ") writeStrings(buffer, " / ", radius.TopLeftY.cssString("0", session),
buffer.WriteString(radius.TopLeftY.cssString("0", session)) " ", radius.TopRightY.cssString("0", session),
buffer.WriteRune(' ') " ", radius.BottomRightY.cssString("0", session),
buffer.WriteString(radius.TopRightY.cssString("0", session)) " ", radius.BottomLeftY.cssString("0", session))
buffer.WriteRune(' ')
buffer.WriteString(radius.BottomRightY.cssString("0", session))
buffer.WriteRune(' ')
buffer.WriteString(radius.BottomLeftY.cssString("0", session))
} }
} }

View File

@ -377,64 +377,44 @@ func (resizable *resizableData) htmlSubviews(self View, buffer *strings.Builder)
top = 2 top = 2
if leftSide { if leftSide {
buffer.WriteString(`<div onmousedown="startResize(this, -1, -1, event)" style="cursor: nwse-resize; width: `) writeStrings(buffer, `<div onmousedown="startResize(this, -1, -1, event)" style="cursor: nwse-resize; width: `,
buffer.WriteString(w) w, `; height: `, w, `;`)
buffer.WriteString(`; height: `)
buffer.WriteString(w)
buffer.WriteString(`;`)
writePos(1, 2, 1, 2) writePos(1, 2, 1, 2)
} }
buffer.WriteString(`<div onmousedown="startResize(this, 0, -1, event)" style="cursor: ns-resize; width: 100%; height: `) writeStrings(buffer, `<div onmousedown="startResize(this, 0, -1, event)" style="cursor: ns-resize; width: 100%; height: `, w, `;`)
buffer.WriteString(w)
buffer.WriteString(`;`)
writePos(left, left+1, 1, 2) writePos(left, left+1, 1, 2)
if rightSide { if rightSide {
buffer.WriteString(`<div onmousedown="startResize(this, 1, -1, event)" style="cursor: nesw-resize; width: `) writeStrings(buffer, `<div onmousedown="startResize(this, 1, -1, event)" style="cursor: nesw-resize; width: `,
buffer.WriteString(w) w, `; height: `, w, `;`)
buffer.WriteString(`; height: `)
buffer.WriteString(w)
buffer.WriteString(`;`)
writePos(left+1, left+2, 1, 2) writePos(left+1, left+2, 1, 2)
} }
} }
if leftSide { if leftSide {
buffer.WriteString(`<div onmousedown="startResize(this, -1, 0, event)" style="cursor: ew-resize; width: `) writeStrings(buffer, `<div onmousedown="startResize(this, -1, 0, event)" style="cursor: ew-resize; width: `, w, `; height: 100%;`)
buffer.WriteString(w)
buffer.WriteString(`; height: 100%;`)
writePos(1, 2, top, top+1) writePos(1, 2, top, top+1)
} }
if rightSide { if rightSide {
buffer.WriteString(`<div onmousedown="startResize(this, 1, 0, event)" style="cursor: ew-resize; width: `) writeStrings(buffer, `<div onmousedown="startResize(this, 1, 0, event)" style="cursor: ew-resize; width: `, w, `; height: 100%;`)
buffer.WriteString(w)
buffer.WriteString(`; height: 100%;`)
writePos(left+1, left+2, top, top+1) writePos(left+1, left+2, top, top+1)
} }
if (side & BottomSide) != 0 { if (side & BottomSide) != 0 {
if leftSide { if leftSide {
buffer.WriteString(`<div onmousedown="startResize(this, -1, 1, event)" style="cursor: nesw-resize; width: `) writeStrings(buffer, `<div onmousedown="startResize(this, -1, 1, event)" style="cursor: nesw-resize; width: `,
buffer.WriteString(w) w, `; height: `, w, `;`)
buffer.WriteString(`; height: `)
buffer.WriteString(w)
buffer.WriteString(`;`)
writePos(1, 2, top+1, top+2) writePos(1, 2, top+1, top+2)
} }
buffer.WriteString(`<div onmousedown="startResize(this, 0, 1, event)" style="cursor: ns-resize; width: 100%; height: `) writeStrings(buffer, `<div onmousedown="startResize(this, 0, 1, event)" style="cursor: ns-resize; width: 100%; height: `, w, `;`)
buffer.WriteString(w)
buffer.WriteString(`;`)
writePos(left, left+1, top+1, top+2) writePos(left, left+1, top+1, top+2)
if rightSide { if rightSide {
buffer.WriteString(`<div onmousedown="startResize(this, 1, 1, event)" style="cursor: nwse-resize; width: `) writeStrings(buffer, `<div onmousedown="startResize(this, 1, 1, event)" style="cursor: nwse-resize; width: `,
buffer.WriteString(w) w, `; height: `, w, `;`)
buffer.WriteString(`; height: `)
buffer.WriteString(w)
buffer.WriteString(`;`)
writePos(left+1, left+2, top+1, top+2) writePos(left+1, left+2, top+1, top+2)
} }
} }

View File

@ -234,11 +234,8 @@ func (session *sessionData) checkboxImage(checked bool, accentColor Color) strin
buffer := allocStringBuilder() buffer := allocStringBuilder()
defer freeStringBuilder(buffer) defer freeStringBuilder(buffer)
buffer.WriteString(`<div style="width: 18px; height: 18px; background-color: `) writeStrings(buffer, `<div style="width: 18px; height: 18px; background-color: `, backgroundColor.cssString(),
buffer.WriteString(backgroundColor.cssString()) `; border: 1px solid `, borderColor.cssString(), `; border-radius: 4px;">`)
buffer.WriteString(`; border: 1px solid `)
buffer.WriteString(borderColor.cssString())
buffer.WriteString(`; border-radius: 4px;">`)
if checked { if checked {
buffer.WriteString(checkImage) buffer.WriteString(checkImage)
} }

View File

@ -224,14 +224,8 @@ func (shadow *shadowPropertyData) cssStyle(buffer *strings.Builder, session Sess
buffer.WriteString("inset ") buffer.WriteString("inset ")
} }
buffer.WriteString(offsetX.cssString("0", session)) writeStrings(buffer, offsetX.cssString("0", session), " ", offsetY.cssString("0", session), " ",
buffer.WriteByte(' ') blurRadius.cssString("0", session), " ", spreadRadius.cssString("0", session), " ")
buffer.WriteString(offsetY.cssString("0", session))
buffer.WriteByte(' ')
buffer.WriteString(blurRadius.cssString("0", session))
buffer.WriteByte(' ')
buffer.WriteString(spreadRadius.cssString("0", session))
buffer.WriteByte(' ')
writeColorCSS(buffer, lightColor, darkColor, session) writeColorCSS(buffer, lightColor, darkColor, session)
return true return true
} }
@ -249,13 +243,8 @@ func (shadow *shadowPropertyData) cssTextStyle(buffer *strings.Builder, session
return false return false
} }
buffer.WriteString(lead) writeStrings(buffer, lead, offsetX.cssString("0", session), " ", offsetY.cssString("0", session),
buffer.WriteString(offsetX.cssString("0", session)) " ", blurRadius.cssString("0", session), " ")
buffer.WriteByte(' ')
buffer.WriteString(offsetY.cssString("0", session))
buffer.WriteByte(' ')
buffer.WriteString(blurRadius.cssString("0", session))
buffer.WriteByte(' ')
writeColorCSS(buffer, lightColor, darkColor, session) writeColorCSS(buffer, lightColor, darkColor, session)
return true return true
} }

View File

@ -585,11 +585,7 @@ func (layout *stackLayoutData) moveToFrontByIndex(index int, onShow []func(View)
buffer := allocStringBuilder() buffer := allocStringBuilder()
defer freeStringBuilder(buffer) defer freeStringBuilder(buffer)
buffer.WriteString(`stackTransitionEndEvent('`) writeStrings(buffer, `stackTransitionEndEvent('`, layout.htmlID(), `', 'move-`, view.htmlID(), `', event)`)
buffer.WriteString(layout.htmlID())
buffer.WriteString(`', 'move-`)
buffer.WriteString(view.htmlID())
buffer.WriteString(`', event)`)
listener := buffer.String() listener := buffer.String()
@ -667,9 +663,7 @@ func (layout *stackLayoutData) Append(view View) {
buffer := allocStringBuilder() buffer := allocStringBuilder()
defer freeStringBuilder(buffer) defer freeStringBuilder(buffer)
buffer.WriteString(`<div id="`) writeStrings(buffer, `<div id="`, view.htmlID(), `page" class="ruiStackPageLayout">`)
buffer.WriteString(view.htmlID())
buffer.WriteString(`page" class="ruiStackPageLayout">`)
viewHTML(view, buffer, "") viewHTML(view, buffer, "")
buffer.WriteString(`</div>`) buffer.WriteString(`</div>`)
@ -706,9 +700,7 @@ func (layout *stackLayoutData) Insert(view View, index int) {
buffer := allocStringBuilder() buffer := allocStringBuilder()
defer freeStringBuilder(buffer) defer freeStringBuilder(buffer)
buffer.WriteString(`<div id="`) writeStrings(buffer, `<div id="`, view.htmlID(), `page" class="ruiStackPageLayout" style="visibility: hidden;">`)
buffer.WriteString(view.htmlID())
buffer.WriteString(`page" class="ruiStackPageLayout" style="visibility: hidden;">`)
viewHTML(view, buffer, "") viewHTML(view, buffer, "")
buffer.WriteString(`</div>`) buffer.WriteString(`</div>`)
@ -782,25 +774,15 @@ func (layout *stackLayoutData) Push(view View, onPushFinished ...func()) {
view.setParentID(layout.htmlID()) view.setParentID(layout.htmlID())
layout.views = append(layout.views, view) layout.views = append(layout.views, view)
session := layout.Session()
buffer := allocStringBuilder() buffer := allocStringBuilder()
defer freeStringBuilder(buffer) defer freeStringBuilder(buffer)
buffer.WriteString(`<div id="`) session := layout.Session()
buffer.WriteString(htmlID)
buffer.WriteString(`page" class="ruiStackPageLayout" ontransitionend="stackTransitionEndEvent('`)
buffer.WriteString(layout.htmlID())
buffer.WriteString(`', 'push-`)
buffer.WriteString(htmlID)
buffer.WriteString(`', event)" style="z-index: 100; transform: `)
buffer.WriteString(transform.transformCSS(layout.session))
buffer.WriteRune(';')
transitionCSS := layout.pushTransitionCSS() transitionCSS := layout.pushTransitionCSS()
buffer.WriteString(" transition: ")
buffer.WriteString(transitionCSS) writeStrings(buffer, `<div id="`, htmlID, `page" class="ruiStackPageLayout" `,
buffer.WriteString(`;">`) `ontransitionend="stackTransitionEndEvent('`, layout.htmlID(), `', 'push-`, htmlID, `', event)" `,
`style="z-index: 100; transform: `, transform.transformCSS(layout.session), `; transition: `, transitionCSS, `;">`)
viewHTML(view, buffer, "") viewHTML(view, buffer, "")
buffer.WriteString(`</div>`) buffer.WriteString(`</div>`)
@ -854,11 +836,7 @@ func (layout *stackLayoutData) Pop(onPopFinished ...func(View)) bool {
buffer := allocStringBuilder() buffer := allocStringBuilder()
defer freeStringBuilder(buffer) defer freeStringBuilder(buffer)
buffer.WriteString(`stackTransitionEndEvent('`) writeStrings(buffer, `stackTransitionEndEvent('`, layout.htmlID(), `', 'pop-`, htmlID, `', event)`)
buffer.WriteString(layout.htmlID())
buffer.WriteString(`', 'pop-`)
buffer.WriteString(htmlID)
buffer.WriteString(`', event)`)
listener := buffer.String() listener := buffer.String()
pageID := htmlID + "page" pageID := htmlID + "page"
@ -892,10 +870,7 @@ func (layout *stackLayoutData) htmlSubviews(self View, buffer *strings.Builder)
if count := len(layout.views); count > 0 { if count := len(layout.views); count > 0 {
peek := count - 1 peek := count - 1
for i, view := range layout.views { for i, view := range layout.views {
buffer.WriteString(`<div id="`) writeStrings(buffer, `<div id="`, view.htmlID(), `page" class="ruiStackPageLayout"`)
buffer.WriteString(view.htmlID())
buffer.WriteString(`page`)
buffer.WriteString(`" class="ruiStackPageLayout"`)
if i != peek { if i != peek {
buffer.WriteString(` style="visibility: hidden;"`) buffer.WriteString(` style="visibility: hidden;"`)
} }

View File

@ -999,19 +999,13 @@ func tableViewCellID(view View, row, column int) string {
func (table *tableViewData) htmlProperties(self View, buffer *strings.Builder) { func (table *tableViewData) htmlProperties(self View, buffer *strings.Builder) {
if content := GetTableContent(table); content != nil { if content := GetTableContent(table); content != nil {
buffer.WriteString(` data-rows="`) writeStrings(buffer, ` data-rows="`, strconv.Itoa(content.RowCount()),
buffer.WriteString(strconv.Itoa(content.RowCount())) `" data-columns="`, strconv.Itoa(content.ColumnCount()), `"`)
buffer.WriteString(`" data-columns="`)
buffer.WriteString(strconv.Itoa(content.ColumnCount()))
buffer.WriteRune('"')
} }
if selectionMode := GetTableSelectionMode(table); selectionMode != NoneSelection { if selectionMode := GetTableSelectionMode(table); selectionMode != NoneSelection {
buffer.WriteString(` onfocus="tableViewFocusEvent(this, event)" onblur="tableViewBlurEvent(this, event)" data-focusitemstyle="`) writeStrings(buffer, ` onfocus="tableViewFocusEvent(this, event)" onblur="tableViewBlurEvent(this, event)" data-focusitemstyle="`,
buffer.WriteString(tableViewCurrentStyle(table)) tableViewCurrentStyle(table), `" data-bluritemstyle="`, tableViewCurrentInactiveStyle(table), `"`)
buffer.WriteString(`" data-bluritemstyle="`)
buffer.WriteString(tableViewCurrentInactiveStyle(table))
buffer.WriteRune('"')
current := tableViewCurrent(table) current := tableViewCurrent(table)
@ -1019,17 +1013,13 @@ func (table *tableViewData) htmlProperties(self View, buffer *strings.Builder) {
case RowSelection: case RowSelection:
buffer.WriteString(` data-selection="row" onkeydown="tableViewRowKeyDownEvent(this, event)"`) buffer.WriteString(` data-selection="row" onkeydown="tableViewRowKeyDownEvent(this, event)"`)
if current.Row >= 0 { if current.Row >= 0 {
buffer.WriteString(` data-current="`) writeStrings(buffer, ` data-current="`, tableViewRowID(table, current.Row), `"`)
buffer.WriteString(tableViewRowID(table, current.Row))
buffer.WriteRune('"')
} }
case CellSelection: case CellSelection:
buffer.WriteString(` data-selection="cell" onkeydown="tableViewCellKeyDownEvent(this, event)"`) buffer.WriteString(` data-selection="cell" onkeydown="tableViewCellKeyDownEvent(this, event)"`)
if current.Row >= 0 && current.Column >= 0 { if current.Row >= 0 && current.Column >= 0 {
buffer.WriteString(` data-current="`) writeStrings(buffer, ` data-current="`, tableViewCellID(table, current.Row, current.Column), `"`)
buffer.WriteString(tableViewCellID(table, current.Row, current.Column))
buffer.WriteRune('"')
} }
} }
} }
@ -1116,9 +1106,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
} }
} }
buffer.WriteString(`<tr id="`) writeStrings(buffer, `<tr id="`, tableViewRowID(table, row), `"`)
buffer.WriteString(tableViewRowID(table, row))
buffer.WriteRune('"')
if selectionMode == RowSelection { if selectionMode == RowSelection {
if row == current.Row { if row == current.Row {
@ -1139,9 +1127,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
} }
if cssBuilder.buffer.Len() > 0 { if cssBuilder.buffer.Len() > 0 {
buffer.WriteString(` style="`) writeStrings(buffer, ` style="`, cssBuilder.buffer.String(), `"`)
buffer.WriteString(cssBuilder.buffer.String())
buffer.WriteString(`"`)
} }
buffer.WriteString(">") buffer.WriteString(">")
@ -1205,11 +1191,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
view.cssStyle(view, &cssBuilder) view.cssStyle(view, &cssBuilder)
} }
buffer.WriteRune('<') writeStrings(buffer, `<`, cellTag, ` id="`, tableViewCellID(table, row, column), `" class="ruiView`)
buffer.WriteString(cellTag)
buffer.WriteString(` id="`)
buffer.WriteString(tableViewCellID(table, row, column))
buffer.WriteString(`" class="ruiView`)
if selectionMode == CellSelection && row == current.Row && column == current.Column { if selectionMode == CellSelection && row == current.Row && column == current.Column {
buffer.WriteRune(' ') buffer.WriteRune(' ')
@ -1229,9 +1211,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
} }
if columnSpan > 1 { if columnSpan > 1 {
buffer.WriteString(` colspan="`) writeStrings(buffer, ` colspan="`, strconv.Itoa(columnSpan), `"`)
buffer.WriteString(strconv.Itoa(columnSpan))
buffer.WriteRune('"')
for c := column + 1; c < column+columnSpan; c++ { for c := column + 1; c < column+columnSpan; c++ {
ignoreCells = append(ignoreCells, struct { ignoreCells = append(ignoreCells, struct {
row int row int
@ -1241,9 +1221,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
} }
if rowSpan > 1 { if rowSpan > 1 {
buffer.WriteString(` rowspan="`) writeStrings(buffer, ` rowspan="`, strconv.Itoa(rowSpan), `"`)
buffer.WriteString(strconv.Itoa(rowSpan))
buffer.WriteRune('"')
if columnSpan < 1 { if columnSpan < 1 {
columnSpan = 1 columnSpan = 1
} }
@ -1258,9 +1236,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
} }
if cssBuilder.buffer.Len() > 0 { if cssBuilder.buffer.Len() > 0 {
buffer.WriteString(` style="`) writeStrings(buffer, ` style="`, cssBuilder.buffer.String(), `"`)
buffer.WriteString(cssBuilder.buffer.String())
buffer.WriteRune('"')
} }
buffer.WriteRune('>') buffer.WriteRune('>')
@ -1275,18 +1251,14 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
table.cellViews = append(table.cellViews, value) table.cellViews = append(table.cellViews, value)
case Color: case Color:
buffer.WriteString(`<div style="display: inline; height: 1em; background-color: `) writeStrings(buffer, `<div style="display: inline; height: 1em; background-color: `,
buffer.WriteString(value.cssString()) value.cssString(), `">&nbsp;&nbsp;&nbsp;&nbsp;</div> `, value.String())
buffer.WriteString(`">&nbsp;&nbsp;&nbsp;&nbsp;</div> `)
buffer.WriteString(value.String())
if namedColors == nil { if namedColors == nil {
namedColors = NamedColors() namedColors = NamedColors()
} }
for _, namedColor := range namedColors { for _, namedColor := range namedColors {
if namedColor.Color == value { if namedColor.Color == value {
buffer.WriteString(" (") writeStrings(buffer, " (", namedColor.Name, `)`)
buffer.WriteString(namedColor.Name)
buffer.WriteRune(')')
break break
} }
} }
@ -1319,9 +1291,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
} }
*/ */
buffer.WriteString(`</`) writeStrings(buffer, `</`, cellTag, `>`)
buffer.WriteString(cellTag)
buffer.WriteRune('>')
} }
} }
@ -1342,9 +1312,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
} }
if cssBuilder.buffer.Len() > 0 { if cssBuilder.buffer.Len() > 0 {
buffer.WriteString(`<col style="`) writeStrings(buffer, `<col style="`, cssBuilder.buffer.String(), `">`)
buffer.WriteString(cssBuilder.buffer.String())
buffer.WriteString(`">`)
} else { } else {
buffer.WriteString("<col>") buffer.WriteString("<col>")
} }
@ -1376,12 +1344,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
switch value := value.(type) { switch value := value.(type) {
case string: case string:
if style, ok := session.resolveConstants(value); ok { if style, ok := session.resolveConstants(value); ok {
buffer.WriteString(` class="`) writeStrings(buffer, ` class="`, style, `" style="vertical-align: `, vAlign, `;">`)
buffer.WriteString(style)
buffer.WriteString(`" style="vertical-align: `)
buffer.WriteString(vAlign)
buffer.WriteString(`;">`)
return table.cellBorderFromStyle(style), table.cellPaddingFromStyle(style) return table.cellBorderFromStyle(style), table.cellPaddingFromStyle(style)
} }
@ -1415,18 +1378,14 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
view.cssStyle(view, &cssBuilder) view.cssStyle(view, &cssBuilder)
if cssBuilder.buffer.Len() > 0 { if cssBuilder.buffer.Len() > 0 {
buffer.WriteString(` style="`) writeStrings(buffer, ` style="`, cssBuilder.buffer.String(), `"`)
buffer.WriteString(cssBuilder.buffer.String())
buffer.WriteString(`"`)
} }
buffer.WriteRune('>') buffer.WriteRune('>')
return border, padding return border, padding
} }
} }
buffer.WriteString(` style="vertical-align: `) writeStrings(buffer, ` style="vertical-align: `, vAlign, `;">`)
buffer.WriteString(vAlign)
buffer.WriteString(`;">`)
return nil, nil return nil, nil
} }
@ -1454,9 +1413,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
} }
if rowCount > footHeight+headHeight { if rowCount > footHeight+headHeight {
buffer.WriteString(`<tbody style="vertical-align: `) writeStrings(buffer, `<tbody style="vertical-align: `, vAlign, `;">`)
buffer.WriteString(vAlign)
buffer.WriteString(`;">`)
tableCSS(headHeight, rowCount-footHeight, "td", cellBorder, cellPadding) tableCSS(headHeight, rowCount-footHeight, "td", cellBorder, cellPadding)
buffer.WriteString("</tbody>") buffer.WriteString("</tbody>")
} }
@ -1543,17 +1500,13 @@ func (table *tableViewData) writeCellHtml(adapter TableAdapter, row, column int,
table.cellViews = append(table.cellViews, value) table.cellViews = append(table.cellViews, value)
case Color: case Color:
buffer.WriteString(`<div style="display: inline; height: 1em; background-color: `) writeStrings(buffer, `<div style="display: inline; height: 1em; background-color: `, value.cssString(),
buffer.WriteString(value.cssString()) `">&nbsp;&nbsp;&nbsp;&nbsp;</div> `, value.String())
buffer.WriteString(`">&nbsp;&nbsp;&nbsp;&nbsp;</div> `)
buffer.WriteString(value.String())
namedColors := NamedColors() namedColors := NamedColors()
for _, namedColor := range namedColors { for _, namedColor := range namedColors {
if namedColor.Color == value { if namedColor.Color == value {
buffer.WriteString(" (") writeStrings(buffer, " (", namedColor.Name, ")")
buffer.WriteString(namedColor.Name)
buffer.WriteRune(')')
break break
} }
} }

View File

@ -523,13 +523,9 @@ func (tabsLayout *tabsLayoutData) RemoveView(index int) View {
func (tabsLayout *tabsLayoutData) htmlProperties(self View, buffer *strings.Builder) { func (tabsLayout *tabsLayoutData) htmlProperties(self View, buffer *strings.Builder) {
tabsLayout.viewsContainerData.htmlProperties(self, buffer) tabsLayout.viewsContainerData.htmlProperties(self, buffer)
buffer.WriteString(` data-inactiveTabStyle="`) writeStrings(buffer, ` data-inactiveTabStyle="`, tabsLayoutInactiveTabStyle(tabsLayout),
buffer.WriteString(tabsLayoutInactiveTabStyle(tabsLayout)) `" data-activeTabStyle="`, tabsLayoutActiveTabStyle(tabsLayout),
buffer.WriteString(`" data-activeTabStyle="`) `" data-current="`, strconv.Itoa(GetCurrent(tabsLayout)), `"`)
buffer.WriteString(tabsLayoutActiveTabStyle(tabsLayout))
buffer.WriteString(`" data-current="`)
buffer.WriteString(strconv.Itoa(GetCurrent(tabsLayout)))
buffer.WriteRune('"')
} }
func (tabsLayout *tabsLayoutData) cssStyle(self View, builder cssBuilder) { func (tabsLayout *tabsLayoutData) cssStyle(self View, builder cssBuilder) {
@ -560,9 +556,7 @@ func (tabsLayout *tabsLayoutData) htmlSubviews(self View, buffer *strings.Builde
if location != HiddenTabs { if location != HiddenTabs {
buffer.WriteString(`<div class="`) writeStrings(buffer, `<div class="`, tabsLayout.tabBarStyle(), `" style="display: flex;`)
buffer.WriteString(tabsLayout.tabBarStyle())
buffer.WriteString(`" style="display: flex;`)
switch location { switch location {
case LeftTabs, LeftListTabs, TopTabs: case LeftTabs, LeftListTabs, TopTabs:
@ -622,37 +616,20 @@ func (tabsLayout *tabsLayoutData) htmlSubviews(self View, buffer *strings.Builde
title, _ = tabsLayout.Session().GetString(title) title, _ = tabsLayout.Session().GetString(title)
} }
buffer.WriteString(`<div id="`) writeStrings(buffer, `<div id="`, tabsLayoutID, `-`, strconv.Itoa(n), `" class="`)
buffer.WriteString(tabsLayoutID)
buffer.WriteByte('-')
buffer.WriteString(strconv.Itoa(n))
buffer.WriteString(`" class="`)
if n == current { if n == current {
buffer.WriteString(activeStyle) buffer.WriteString(activeStyle)
} else { } else {
buffer.WriteString(inactiveStyle) buffer.WriteString(inactiveStyle)
} }
buffer.WriteString(`" tabindex="0" onclick="tabClickEvent(this, '`) writeStrings(buffer, `" tabindex="0" `,
buffer.WriteString(tabsLayoutID) `onclick="tabClickEvent(this, '`, tabsLayoutID, `', `, strconv.Itoa(n), `, event)" `,
buffer.WriteString(`', `) `onkeydown="tabKeyClickEvent('`, tabsLayoutID, `', `, strconv.Itoa(n), `, event)" `,
buffer.WriteString(strconv.Itoa(n)) `style="`, tabStyle, `" data-container="`, tabsLayoutID,
buffer.WriteString(`, event)" onkeydown="tabKeyClickEvent('`) `" data-view="`, tabsLayoutID, `-page`, strconv.Itoa(n), `">`)
buffer.WriteString(tabsLayoutID)
buffer.WriteString(`', `)
buffer.WriteString(strconv.Itoa(n))
buffer.WriteString(`, event)" style="`)
buffer.WriteString(tabStyle)
buffer.WriteString(`" data-container="`)
buffer.WriteString(tabsLayoutID)
buffer.WriteString(`" data-view="`)
buffer.WriteString(tabsLayoutID)
buffer.WriteString(`-page`)
buffer.WriteString(strconv.Itoa(n))
buffer.WriteString(`">`)
if icon != "" { if icon != "" {
buffer.WriteString(`<img id="`) writeStrings(buffer, `<img id="`, view.htmlID())
buffer.WriteString(view.htmlID())
switch location { switch location {
case LeftTabs: case LeftTabs:
buffer.WriteString(`-icon" style="grid-row-start: 3; grid-row-end: 4; grid-column-start: 1; grid-column-end: 2;" src="`) buffer.WriteString(`-icon" style="grid-row-start: 3; grid-row-end: 4; grid-column-start: 1; grid-column-end: 2;" src="`)
@ -663,31 +640,20 @@ func (tabsLayout *tabsLayoutData) htmlSubviews(self View, buffer *strings.Builde
default: default:
buffer.WriteString(`-icon" style="grid-row-start: 1; grid-row-end: 2; grid-column-start: 1; grid-column-end: 2;" src="`) buffer.WriteString(`-icon" style="grid-row-start: 1; grid-row-end: 2; grid-column-start: 1; grid-column-end: 2;" src="`)
} }
buffer.WriteString(icon) writeStrings(buffer, icon, `">`)
buffer.WriteString(`">`)
} }
buffer.WriteString(`<div id="`) writeStrings(buffer, `<div id="`, view.htmlID(), `-title"`, titleStyle, textToHtml(title), `</div>`)
buffer.WriteString(view.htmlID())
buffer.WriteString(`-title"`)
buffer.WriteString(titleStyle)
buffer.WriteString(textToHtml(title))
buffer.WriteString(`</div>`)
close, ok := boolProperty(view, TabCloseButton, tabsLayout.session) close, ok := boolProperty(view, TabCloseButton, tabsLayout.session)
if !ok { if !ok {
close = closeButton close = closeButton
} }
if close { if close {
buffer.WriteString(`<div class="ruiTabCloseButton" tabindex="0" onclick="tabCloseClickEvent(this, '`) writeStrings(buffer, `<div class="ruiTabCloseButton" tabindex="0" `,
buffer.WriteString(tabsLayoutID) `onclick="tabCloseClickEvent(this, '`, tabsLayoutID, `', `, strconv.Itoa(n), `, event)" `,
buffer.WriteString(`', `) `onkeydown="tabCloseKeyClickEvent('`, tabsLayoutID, `', `, strconv.Itoa(n), `, event)" `,
buffer.WriteString(strconv.Itoa(n)) `style="display: grid; `)
buffer.WriteString(`, event)" onkeydown="tabCloseKeyClickEvent('`)
buffer.WriteString(tabsLayoutID)
buffer.WriteString(`', `)
buffer.WriteString(strconv.Itoa(n))
buffer.WriteString(`, event)" style="display: grid; `)
switch location { switch location {
case LeftTabs: case LeftTabs:
@ -710,10 +676,7 @@ func (tabsLayout *tabsLayoutData) htmlSubviews(self View, buffer *strings.Builde
} }
for n, view := range tabsLayout.views { for n, view := range tabsLayout.views {
buffer.WriteString(`<div id="`) writeStrings(buffer, `<div id="`, tabsLayoutID, `-page`, strconv.Itoa(n))
buffer.WriteString(tabsLayoutID)
buffer.WriteString(`-page`)
buffer.WriteString(strconv.Itoa(n))
if current != n { if current != n {
buffer.WriteString(`" style="display: grid; align-items: stretch; justify-items: stretch; visibility: hidden; `) buffer.WriteString(`" style="display: grid; align-items: stretch; justify-items: stretch; visibility: hidden; `)

View File

@ -916,9 +916,7 @@ func (theme *theme) String() string {
writeString := func(text string) { writeString := func(text string) {
if isQuotesNeeded(text) { if isQuotesNeeded(text) {
buffer.WriteRune('"') writeStrings(buffer, `"`, replaceEscapeSymbols(text), `"`)
buffer.WriteString(replaceEscapeSymbols(text))
buffer.WriteRune('"')
} else { } else {
buffer.WriteString(text) buffer.WriteString(text)
} }
@ -930,9 +928,7 @@ func (theme *theme) String() string {
return return
} }
buffer.WriteString("\t") writeStrings(buffer, "\t", tag, " = _{\n")
buffer.WriteString(tag)
buffer.WriteString(" = _{\n")
tags := make([]string, 0, count) tags := make([]string, 0, count)
for name := range constants { for name := range constants {

View File

@ -294,28 +294,20 @@ func (picker *timePickerData) htmlProperties(self View, buffer *strings.Builder)
buffer.WriteString(` type="time"`) buffer.WriteString(` type="time"`)
if min, ok := getTimeProperty(picker, TimePickerMin, Min); ok { if min, ok := getTimeProperty(picker, TimePickerMin, Min); ok {
buffer.WriteString(` min="`) writeStrings(buffer, ` min="`, min.Format(timeFormat), `"`)
buffer.WriteString(min.Format(timeFormat))
buffer.WriteByte('"')
} }
if max, ok := getTimeProperty(picker, TimePickerMax, Max); ok { if max, ok := getTimeProperty(picker, TimePickerMax, Max); ok {
buffer.WriteString(` max="`) writeStrings(buffer, ` max="`, max.Format(timeFormat), `"`)
buffer.WriteString(max.Format(timeFormat))
buffer.WriteByte('"')
} }
if step, ok := intProperty(picker, TimePickerStep, picker.Session(), 0); ok && step > 0 { if step, ok := intProperty(picker, TimePickerStep, picker.Session(), 0); ok && step > 0 {
buffer.WriteString(` step="`) writeStrings(buffer, ` step="`, strconv.Itoa(step), `"`)
buffer.WriteString(strconv.Itoa(step))
buffer.WriteByte('"')
} }
buffer.WriteString(` value="`) writeStrings(buffer, ` value="`, GetTimePickerValue(picker).Format(timeFormat),
buffer.WriteString(GetTimePickerValue(picker).Format(timeFormat)) `" oninput="editViewInputEvent(this)"`)
buffer.WriteByte('"')
buffer.WriteString(` oninput="editViewInputEvent(this)"`)
if picker.getRaw(ClickEvent) == nil { if picker.getRaw(ClickEvent) == nil {
buffer.WriteString(` onclick="stopEventPropagation(this, event)"`) buffer.WriteString(` onclick="stopEventPropagation(this, event)"`)
} }

View File

@ -467,38 +467,22 @@ func (transform *transformPropertyData) transformCSS(session Session) string {
defer freeStringBuilder(buffer) defer freeStringBuilder(buffer)
if perspective, ok := sizeProperty(transform, Perspective, session); ok && perspective.Type != Auto && perspective.Value != 0 { if perspective, ok := sizeProperty(transform, Perspective, session); ok && perspective.Type != Auto && perspective.Value != 0 {
buffer.WriteString(`perspective(`) writeStrings(buffer, `perspective(`, perspective.cssString("0", session), `) `)
buffer.WriteString(perspective.cssString("0", session))
buffer.WriteString(") ")
} }
skewX, skewY, skewOK := transform.getSkew(session) skewX, skewY, skewOK := transform.getSkew(session)
if skewOK { if skewOK {
buffer.WriteString(`skew(`) writeStrings(buffer, `skew(`, skewX.cssString(), `,`, skewY.cssString(), `) `)
buffer.WriteString(skewX.cssString())
buffer.WriteRune(',')
buffer.WriteString(skewY.cssString())
buffer.WriteString(") ")
} }
x, y, z := transform.getTranslate(session) x, y, z := transform.getTranslate(session)
if z.Type != Auto && z.Value != 0 { if z.Type != Auto && z.Value != 0 {
buffer.WriteString(`translate3d(`) writeStrings(buffer, `translate3d(`, x.cssString("0px", session), `,`, y.cssString("0px", session), `,`, z.cssString("0px", session), `) `)
buffer.WriteString(x.cssString("0px", session))
buffer.WriteRune(',')
buffer.WriteString(y.cssString("0px", session))
buffer.WriteRune(',')
buffer.WriteString(z.cssString("0px", session))
buffer.WriteString(") ")
} else if (x.Type != Auto && x.Value != 0) || (y.Type != Auto && y.Value != 0) { } else if (x.Type != Auto && x.Value != 0) || (y.Type != Auto && y.Value != 0) {
buffer.WriteString(`translate(`) writeStrings(buffer, `translate(`, x.cssString("0px", session), `,`, y.cssString("0px", session), `) `)
buffer.WriteString(x.cssString("0px", session))
buffer.WriteRune(',')
buffer.WriteString(y.cssString("0px", session))
buffer.WriteString(") ")
} }
scaleX, okScaleX := floatTextProperty(transform, ScaleX, session, 1) scaleX, okScaleX := floatTextProperty(transform, ScaleX, session, 1)
@ -506,21 +490,11 @@ func (transform *transformPropertyData) transformCSS(session Session) string {
scaleZ, okScaleZ := floatTextProperty(transform, ScaleZ, session, 1) scaleZ, okScaleZ := floatTextProperty(transform, ScaleZ, session, 1)
if okScaleZ { if okScaleZ {
buffer.WriteString(`scale3d(`) writeStrings(buffer, `scale3d(`, scaleX, `,`, scaleY, `,`, scaleZ, `) `)
buffer.WriteString(scaleX)
buffer.WriteRune(',')
buffer.WriteString(scaleY)
buffer.WriteRune(',')
buffer.WriteString(scaleZ)
buffer.WriteString(") ")
} else if okScaleX || okScaleY { } else if okScaleX || okScaleY {
buffer.WriteString(`scale(`) writeStrings(buffer, `scale(`, scaleX, `,`, scaleY, `) `)
buffer.WriteString(scaleX)
buffer.WriteRune(',')
buffer.WriteString(scaleY)
buffer.WriteString(") ")
} }
if angle, ok := angleProperty(transform, Rotate, session); ok { if angle, ok := angleProperty(transform, Rotate, session); ok {
@ -530,21 +504,11 @@ func (transform *transformPropertyData) transformCSS(session Session) string {
if xOK || yOK || zOK { if xOK || yOK || zOK {
buffer.WriteString(`rotate3d(`) writeStrings(buffer, `rotate3d(`, rotateX, `,`, rotateY, `,`, rotateZ, `,`, angle.cssString(), `) `)
buffer.WriteString(rotateX)
buffer.WriteRune(',')
buffer.WriteString(rotateY)
buffer.WriteRune(',')
buffer.WriteString(rotateZ)
buffer.WriteRune(',')
buffer.WriteString(angle.cssString())
buffer.WriteString(") ")
} else { } else {
buffer.WriteString(`rotate(`) writeStrings(buffer, `rotate(`, angle.cssString(), `) `)
buffer.WriteString(angle.cssString())
buffer.WriteString(") ")
} }
} }

View File

@ -130,3 +130,9 @@ func textToHtml(text string) string {
return text return text
} }
func writeStrings(buffer *strings.Builder, strs ...string) {
for _, str := range strs {
buffer.WriteString(str)
}
}

View File

@ -106,20 +106,14 @@ func (player *videoPlayerData) htmlProperties(self View, buffer *strings.Builder
session := player.Session() session := player.Session()
if size, ok := floatTextProperty(player, VideoWidth, session, 0); ok && size != "0" { if size, ok := floatTextProperty(player, VideoWidth, session, 0); ok && size != "0" {
buffer.WriteString(` width="`) writeStrings(buffer, ` width="`, size, `"`)
buffer.WriteString(size)
buffer.WriteString(`"`)
} }
if size, ok := floatTextProperty(player, VideoHeight, session, 0); ok && size != "0" { if size, ok := floatTextProperty(player, VideoHeight, session, 0); ok && size != "0" {
buffer.WriteString(` height="`) writeStrings(buffer, ` height="`, size, `"`)
buffer.WriteString(size)
buffer.WriteString(`"`)
} }
if url, ok := stringProperty(player, Poster, session); ok && url != "" { if url, ok := stringProperty(player, Poster, session); ok && url != "" {
buffer.WriteString(` poster="`) writeStrings(buffer, ` poster="`, url, `"`)
buffer.WriteString(url)
buffer.WriteString(`"`)
} }
} }

44
view.go
View File

@ -1096,50 +1096,38 @@ func viewHTML(view View, buffer *strings.Builder, htmlTag string) {
if htmlTag == "" { if htmlTag == "" {
htmlTag = view.htmlTag() htmlTag = view.htmlTag()
} }
//viewHTMLTag := view.htmlTag()
buffer.WriteRune('<') writeStrings(buffer, `<`, htmlTag, ` id="`, view.htmlID(), `"`)
buffer.WriteString(htmlTag)
buffer.WriteString(` id="`)
buffer.WriteString(view.htmlID())
buffer.WriteRune('"')
if cls := view.htmlClass(); cls != "" { if cls := view.htmlClass(); cls != "" {
buffer.WriteString(` class="`) writeStrings(buffer, ` class="`, cls, `"`)
buffer.WriteString(cls)
buffer.WriteRune('"')
} }
cssBuilder := viewCSSBuilder{buffer: allocStringBuilder()} cssBuilder := viewCSSBuilder{buffer: allocStringBuilder()}
view.cssStyle(view, &cssBuilder) view.cssStyle(view, &cssBuilder)
if style := cssBuilder.finish(); style != "" { if style := cssBuilder.finish(); style != "" {
buffer.WriteString(` style="`) writeStrings(buffer, ` style="`, style, `" `)
buffer.WriteString(style)
buffer.WriteRune('"')
}
buffer.WriteRune(' ')
view.htmlProperties(view, buffer)
if view.isNoResizeEvent() {
buffer.WriteString(` data-noresize="1" `)
} else { } else {
buffer.WriteRune(' ') buffer.WriteRune(' ')
} }
view.htmlProperties(view, buffer)
if view.isNoResizeEvent() {
buffer.WriteString(` data-noresize="1"`)
}
if tabIndex := GetTabIndex(view); tabIndex >= 0 { if tabIndex := GetTabIndex(view); tabIndex >= 0 {
buffer.WriteString(`tabindex="`) writeStrings(buffer, ` tabindex="`, strconv.Itoa(tabIndex), `"`)
buffer.WriteString(strconv.Itoa(tabIndex))
buffer.WriteString(`" `)
} }
if tooltip := GetTooltip(view); tooltip != "" { if tooltip := GetTooltip(view); tooltip != "" {
buffer.WriteString(`data-tooltip=" `) writeStrings(buffer, ` data-tooltip=" `, tooltip,
buffer.WriteString(tooltip) `" onmouseenter="mouseEnterEvent(this, event)" onmouseleave="mouseLeaveEvent(this, event)"`)
buffer.WriteString(`" onmouseenter="mouseEnterEvent(this, event)" onmouseleave="mouseLeaveEvent(this, event)" `)
} }
buffer.WriteString(`onscroll="scrollEvent(this, event)" `) buffer.WriteString(` onscroll="scrollEvent(this, event)" `)
focusEventsHtml(view, buffer) focusEventsHtml(view, buffer)
keyEventsHtml(view, buffer) keyEventsHtml(view, buffer)
@ -1155,9 +1143,7 @@ func viewHTML(view View, buffer *strings.Builder, htmlTag string) {
buffer.WriteRune('>') buffer.WriteRune('>')
view.htmlSubviews(view, buffer) view.htmlSubviews(view, buffer)
if view.closeHTMLTag() { if view.closeHTMLTag() {
buffer.WriteString(`</`) writeStrings(buffer, `</`, htmlTag, `>`)
buffer.WriteString(htmlTag)
buffer.WriteRune('>')
} }
} }

View File

@ -611,9 +611,7 @@ func propertyValueToString(tag PropertyName, value any, indent string) string {
writeString := func(buffer *strings.Builder, text string) string { writeString := func(buffer *strings.Builder, text string) string {
if isQuotesNeeded(text) { if isQuotesNeeded(text) {
buffer.WriteRune('"') writeStrings(buffer, `"`, replaceEscapeSymbols(text), `"`)
buffer.WriteString(replaceEscapeSymbols(text))
buffer.WriteRune('"')
} else { } else {
buffer.WriteString(text) buffer.WriteString(text)
} }
@ -623,9 +621,7 @@ func propertyValueToString(tag PropertyName, value any, indent string) string {
writeObjectNameString := func(buffer *strings.Builder, text string) string { writeObjectNameString := func(buffer *strings.Builder, text string) string {
textEscaped := replaceEscapeSymbols(text) textEscaped := replaceEscapeSymbols(text)
if isQuotesNeededForObjectName(textEscaped) { if isQuotesNeededForObjectName(textEscaped) {
buffer.WriteRune('"') writeStrings(buffer, `"`, textEscaped, `"`)
buffer.WriteString(textEscaped)
buffer.WriteRune('"')
} else { } else {
buffer.WriteString(text) buffer.WriteString(text)
} }
@ -1091,19 +1087,14 @@ func propertyValueToString(tag PropertyName, value any, indent string) string {
} }
func writeViewStyle(name string, view Properties, buffer *strings.Builder, indent string, excludeTags []PropertyName) { func writeViewStyle(name string, view Properties, buffer *strings.Builder, indent string, excludeTags []PropertyName) {
buffer.WriteString(name) writeStrings(buffer, name, " {\n")
buffer.WriteString(" {\n")
indent += "\t" indent += "\t"
writeProperty := func(tag PropertyName, value any) { writeProperty := func(tag PropertyName, value any) {
if !slices.Contains(excludeTags, tag) { if !slices.Contains(excludeTags, tag) {
text := propertyValueToString(tag, value, indent) text := propertyValueToString(tag, value, indent)
if text != "" { if text != "" {
buffer.WriteString(indent) writeStrings(buffer, indent, string(tag), " = ", text, ",\n")
buffer.WriteString(string(tag))
buffer.WriteString(" = ")
buffer.WriteString(text)
buffer.WriteString(",\n")
} }
} }
} }
@ -1167,8 +1158,7 @@ func writeViewStyle(name string, view Properties, buffer *strings.Builder, inden
} }
indent = indent[:len(indent)-1] indent = indent[:len(indent)-1]
buffer.WriteString(indent) writeStrings(buffer, indent, "}")
buffer.WriteString("}")
} }
func runStringWriter(writer stringWriter) string { func runStringWriter(writer stringWriter) string {

View File

@ -120,9 +120,7 @@ func (bridge *webBridge) startUpdateScript(htmlID string) bool {
} }
buffer := allocStringBuilder() buffer := allocStringBuilder()
bridge.updateScripts[htmlID] = buffer bridge.updateScripts[htmlID] = buffer
buffer.WriteString("{\nlet element = document.getElementById('") writeStrings(buffer, "{\nlet element = document.getElementById('", htmlID, "');\nif (element) {\n")
buffer.WriteString(htmlID)
buffer.WriteString("');\nif (element) {\n")
return true return true
} }
@ -267,11 +265,7 @@ func (bridge *webBridge) updateInnerHTML(htmlID, html string) {
func (bridge *webBridge) updateCSSProperty(htmlID, property, value string) { func (bridge *webBridge) updateCSSProperty(htmlID, property, value string) {
if buffer, ok := bridge.updateScripts[htmlID]; ok { if buffer, ok := bridge.updateScripts[htmlID]; ok {
buffer.WriteString(`element.style['`) writeStrings(buffer, `element.style['`, property, `'] = '`, value, "';\n")
buffer.WriteString(property)
buffer.WriteString(`'] = '`)
buffer.WriteString(value)
buffer.WriteString("';\n")
} else { } else {
bridge.callFunc("updateCSSProperty", htmlID, property, value) bridge.callFunc("updateCSSProperty", htmlID, property, value)
} }
@ -280,11 +274,7 @@ func (bridge *webBridge) updateCSSProperty(htmlID, property, value string) {
func (bridge *webBridge) updateProperty(htmlID, property string, value any) { func (bridge *webBridge) updateProperty(htmlID, property string, value any) {
if buffer, ok := bridge.updateScripts[htmlID]; ok { if buffer, ok := bridge.updateScripts[htmlID]; ok {
if val, ok := bridge.argToString(value); ok { if val, ok := bridge.argToString(value); ok {
buffer.WriteString(`element.setAttribute('`) writeStrings(buffer, `element.setAttribute('`, property, `', `, val, ");\n")
buffer.WriteString(property)
buffer.WriteString(`', `)
buffer.WriteString(val)
buffer.WriteString(");\n")
} }
} else { } else {
bridge.callFunc("updateProperty", htmlID, property, value) bridge.callFunc("updateProperty", htmlID, property, value)
@ -293,11 +283,7 @@ func (bridge *webBridge) updateProperty(htmlID, property string, value any) {
func (bridge *webBridge) removeProperty(htmlID, property string) { func (bridge *webBridge) removeProperty(htmlID, property string) {
if buffer, ok := bridge.updateScripts[htmlID]; ok { if buffer, ok := bridge.updateScripts[htmlID]; ok {
buffer.WriteString(`if (element.hasAttribute('`) writeStrings(buffer, `if (element.hasAttribute('`, property, `')) { element.removeAttribute('`, property, "');}\n")
buffer.WriteString(property)
buffer.WriteString(`')) { element.removeAttribute('`)
buffer.WriteString(property)
buffer.WriteString("');}\n")
} else { } else {
bridge.callFunc("removeProperty", htmlID, property) bridge.callFunc("removeProperty", htmlID, property)
} }
@ -323,15 +309,11 @@ func (bridge *webBridge) setAnimationCSS(css string) {
func (bridge *webBridge) canvasStart(htmlID string) { func (bridge *webBridge) canvasStart(htmlID string) {
bridge.canvasBuffer.Reset() bridge.canvasBuffer.Reset()
bridge.canvasBuffer.WriteString("{\nconst ctx = getCanvasContext('") writeStrings(&bridge.canvasBuffer, "{\nconst ctx = getCanvasContext('", htmlID, `');`)
bridge.canvasBuffer.WriteString(htmlID)
bridge.canvasBuffer.WriteString(`');`)
} }
func (bridge *webBridge) callCanvasFunc(funcName string, args ...any) { func (bridge *webBridge) callCanvasFunc(funcName string, args ...any) {
bridge.canvasBuffer.WriteString("\nctx.") writeStrings(&bridge.canvasBuffer, "\nctx.", funcName, `(`)
bridge.canvasBuffer.WriteString(funcName)
bridge.canvasBuffer.WriteRune('(')
for i, arg := range args { for i, arg := range args {
if i > 0 { if i > 0 {
bridge.canvasBuffer.WriteString(", ") bridge.canvasBuffer.WriteString(", ")
@ -343,22 +325,14 @@ func (bridge *webBridge) callCanvasFunc(funcName string, args ...any) {
} }
func (bridge *webBridge) updateCanvasProperty(property string, value any) { func (bridge *webBridge) updateCanvasProperty(property string, value any) {
bridge.canvasBuffer.WriteString("\nctx.")
bridge.canvasBuffer.WriteString(property)
bridge.canvasBuffer.WriteString(" = ")
argText, _ := bridge.argToString(value) argText, _ := bridge.argToString(value)
bridge.canvasBuffer.WriteString(argText) writeStrings(&bridge.canvasBuffer, "\nctx.", property, " = ", argText, ";")
bridge.canvasBuffer.WriteString(";")
} }
func (bridge *webBridge) createCanvasVar(funcName string, args ...any) any { func (bridge *webBridge) createCanvasVar(funcName string, args ...any) any {
bridge.canvasVarNumber++ bridge.canvasVarNumber++
result := canvasVar{name: fmt.Sprintf("v%d", bridge.canvasVarNumber)} result := canvasVar{name: fmt.Sprintf("v%d", bridge.canvasVarNumber)}
bridge.canvasBuffer.WriteString("\nlet ") writeStrings(&bridge.canvasBuffer, "\nlet ", result.name, " = ctx.", funcName, "(")
bridge.canvasBuffer.WriteString(result.name)
bridge.canvasBuffer.WriteString(" = ctx.")
bridge.canvasBuffer.WriteString(funcName)
bridge.canvasBuffer.WriteRune('(')
for i, arg := range args { for i, arg := range args {
if i > 0 { if i > 0 {
bridge.canvasBuffer.WriteString(", ") bridge.canvasBuffer.WriteString(", ")
@ -373,9 +347,7 @@ func (bridge *webBridge) createCanvasVar(funcName string, args ...any) any {
func (bridge *webBridge) createPath2D(arg string) any { func (bridge *webBridge) createPath2D(arg string) any {
bridge.canvasVarNumber++ bridge.canvasVarNumber++
result := canvasVar{name: fmt.Sprintf("v%d", bridge.canvasVarNumber)} result := canvasVar{name: fmt.Sprintf("v%d", bridge.canvasVarNumber)}
bridge.canvasBuffer.WriteString("\nlet ") writeStrings(&bridge.canvasBuffer, "\nlet ", result.name, ` = new Path2D(`)
bridge.canvasBuffer.WriteString(result.name)
bridge.canvasBuffer.WriteString(` = new Path2D(`)
if arg != "" { if arg != "" {
argText, _ := bridge.argToString(arg) argText, _ := bridge.argToString(arg)
bridge.canvasBuffer.WriteString(argText) bridge.canvasBuffer.WriteString(argText)
@ -389,11 +361,7 @@ func (bridge *webBridge) callCanvasVarFunc(v any, funcName string, args ...any)
if !ok { if !ok {
return return
} }
bridge.canvasBuffer.WriteString("\n") writeStrings(&bridge.canvasBuffer, "\n", varName.name, ".", funcName, "(")
bridge.canvasBuffer.WriteString(varName.name)
bridge.canvasBuffer.WriteRune('.')
bridge.canvasBuffer.WriteString(funcName)
bridge.canvasBuffer.WriteRune('(')
for i, arg := range args { for i, arg := range args {
if i > 0 { if i > 0 {
bridge.canvasBuffer.WriteString(", ") bridge.canvasBuffer.WriteString(", ")
@ -406,17 +374,11 @@ func (bridge *webBridge) callCanvasVarFunc(v any, funcName string, args ...any)
func (bridge *webBridge) callCanvasImageFunc(url string, property string, funcName string, args ...any) { func (bridge *webBridge) callCanvasImageFunc(url string, property string, funcName string, args ...any) {
bridge.canvasBuffer.WriteString("\nimg = images.get('") writeStrings(&bridge.canvasBuffer, "\nimg = images.get('", url, "');\nif (img) {\n")
bridge.canvasBuffer.WriteString(url)
bridge.canvasBuffer.WriteString("');\nif (img) {\n")
if property != "" { if property != "" {
bridge.canvasBuffer.WriteString("ctx.") writeStrings(&bridge.canvasBuffer, "ctx.", property, " = ")
bridge.canvasBuffer.WriteString(property)
bridge.canvasBuffer.WriteString(" = ")
} }
bridge.canvasBuffer.WriteString("ctx.") writeStrings(&bridge.canvasBuffer, "ctx.", funcName, "(img")
bridge.canvasBuffer.WriteString(funcName)
bridge.canvasBuffer.WriteString("(img")
for _, arg := range args { for _, arg := range args {
bridge.canvasBuffer.WriteString(", ") bridge.canvasBuffer.WriteString(", ")
argText, _ := bridge.argToString(arg) argText, _ := bridge.argToString(arg)