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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -615,19 +615,16 @@ func (canvas *canvasData) fontWithParams(name string, size SizeUnit, params Font
case SizeInPercent:
if params.LineHeight.Value != 100 {
buffer.WriteString("/")
buffer.WriteString(strconv.FormatFloat(params.LineHeight.Value/100, 'g', -1, 64))
writeStrings(buffer, "/", strconv.FormatFloat(params.LineHeight.Value/100, 'g', -1, 64))
}
case SizeInFraction:
if params.LineHeight.Value != 1 {
buffer.WriteString("/")
buffer.WriteString(strconv.FormatFloat(params.LineHeight.Value, 'g', -1, 64))
writeStrings(buffer, "/", strconv.FormatFloat(params.LineHeight.Value, 'g', -1, 64))
}
default:
buffer.WriteString("/")
buffer.WriteString(params.LineHeight.cssString("", canvas.View().Session()))
writeStrings(buffer, "/", params.LineHeight.cssString("", canvas.View().Session()))
}
lead := " "
@ -636,9 +633,7 @@ func (canvas *canvasData) fontWithParams(name string, size SizeUnit, params Font
buffer.WriteString(lead)
lead = ","
if strings.ContainsRune(font, ' ') {
buffer.WriteRune('"')
buffer.WriteString(font)
buffer.WriteRune('"')
writeStrings(buffer, `"`, font, `"`)
} else {
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 (button *checkboxData) htmlCheckbox(buffer *strings.Builder, checked bool) (int, int) {
vAlign := GetCheckboxVerticalAlign(button)
hAlign := GetCheckboxHorizontalAlign(button)
buffer.WriteString(`<div id="`)
buffer.WriteString(button.htmlID())
buffer.WriteString(`checkbox" style="display: grid;`)
writeStrings(buffer, `<div id="`, button.htmlID(), `checkbox" style="display: grid;`)
if hAlign == CenterAlign {
buffer.WriteString(" justify-items: center; grid-column-start: 1; grid-column-end: 2;")
if vAlign == BottomAlign {
@ -253,9 +251,7 @@ func (button *checkboxData) htmlSubviews(self View, buffer *strings.Builder) {
vCheckboxAlign, hCheckboxAlign := checkboxHtml(button, buffer, IsCheckboxChecked(button))
buffer.WriteString(`<div id="`)
buffer.WriteString(button.htmlID())
buffer.WriteString(`content" style="display: grid;`)
writeStrings(buffer, `<div id="`, button.htmlID(), `content" style="display: grid;`)
if hCheckboxAlign == LeftAlign {
buffer.WriteString(" grid-column-start: 2; grid-column-end: 3;")
} 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(" align-items: ")
buffer.WriteString(checkboxVerticalAlignCSS(button))
buffer.WriteRune(';')
buffer.WriteString(" justify-items: ")
buffer.WriteString(checkboxHorizontalAlignCSS(button))
buffer.WriteRune(';')
buffer.WriteString(`">`)
writeStrings(buffer,
" align-items: ", checkboxVerticalAlignCSS(button), `;`,
" justify-items: ", checkboxHorizontalAlignCSS(button), `;">`)
button.viewsContainerData.htmlSubviews(self, buffer)
buffer.WriteString(`</div>`)
}

View File

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

View File

@ -209,11 +209,7 @@ func stringToColor(text string) (Color, error) {
func writeColorCSS(buffer *strings.Builder, lightColor, darkColor Color, session Session) {
if session.lightDarkSupport() {
buffer.WriteString("light-dark(")
buffer.WriteString(lightColor.cssString())
buffer.WriteRune(',')
buffer.WriteString(darkColor.cssString())
buffer.WriteRune(')')
writeStrings(buffer, "light-dark(", lightColor.cssString(), ",", darkColor.cssString(), ")")
} else if session.DarkTheme() {
buffer.WriteString(darkColor.cssString())
} 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) {
picker.viewData.htmlProperties(self, buffer)
buffer.WriteString(` type="color" value="`)
buffer.WriteString(GetColorPickerValue(picker).rgbString())
buffer.WriteByte('"')
buffer.WriteString(` oninput="editViewInputEvent(this)"`)
writeStrings(buffer, ` type="color" value="`, GetColorPickerValue(picker).rgbString(), `"`,
` oninput="editViewInputEvent(this)"`)
if picker.getRaw(ClickEvent) == nil {
buffer.WriteString(` onclick="stopEventPropagation(this, event)"`)
}

View File

@ -88,10 +88,7 @@ func (builder *viewCSSBuilder) add(key, value string) {
builder.buffer.WriteRune(' ')
}
builder.buffer.WriteString(key)
builder.buffer.WriteString(": ")
builder.buffer.WriteString(value)
builder.buffer.WriteRune(';')
writeStrings(builder.buffer, key, ": ", value, ";")
}
}
@ -193,9 +190,7 @@ func (builder *cssStyleBuilder) startMedia(rule string) {
if builder.buffer == nil {
builder.init(0)
}
builder.buffer.WriteString(`@media screen`)
builder.buffer.WriteString(rule)
builder.buffer.WriteString(` {\n`)
writeStrings(builder.buffer, `@media screen`, rule, ` {\n`)
builder.media = true
}
@ -247,9 +242,7 @@ func (builder *cssStyleBuilder) startAnimation(name string) {
}
builder.media = true
builder.buffer.WriteString(`\n@keyframes `)
builder.buffer.WriteString(name)
builder.buffer.WriteString(` {\n`)
writeStrings(builder.buffer, `\n@keyframes `, name, ` {\n`)
}
func (builder *cssStyleBuilder) endAnimation() {
@ -265,9 +258,7 @@ func (builder *cssStyleBuilder) startAnimationFrame(name string) {
builder.init(0)
}
builder.buffer.WriteString(`\t`)
builder.buffer.WriteString(name)
builder.buffer.WriteString(` {\n`)
writeStrings(builder.buffer, `\t`, name, ` {\n`)
}
func (builder *cssStyleBuilder) endAnimationFrame() {
@ -285,11 +276,7 @@ func (builder *cssStyleBuilder) add(key, value string) {
if builder.media {
builder.buffer.WriteString(`\t`)
}
builder.buffer.WriteString(`\t`)
builder.buffer.WriteString(key)
builder.buffer.WriteString(`: `)
builder.buffer.WriteString(value)
builder.buffer.WriteString(`;\n`)
writeStrings(builder.buffer, `\t`, key, `: `, value, `;\n`)
}
}
@ -300,9 +287,7 @@ func (builder *cssStyleBuilder) addWriter(key string, writer func(buffer *string
if builder.media {
builder.buffer.WriteString(`\t`)
}
builder.buffer.WriteString(`\t`)
builder.buffer.WriteString(key)
builder.buffer.WriteString(": ")
writeStrings(builder.buffer, `\t`, key, ": ")
writer(builder.buffer)
builder.buffer.WriteString(`;\n`)
}
@ -318,9 +303,7 @@ func (builder *cssStyleBuilder) addValues(key, separator string, values ...strin
if builder.media {
builder.buffer.WriteString(`\t`)
}
builder.buffer.WriteString(`\t`)
builder.buffer.WriteString(key)
builder.buffer.WriteString(`: `)
writeStrings(builder.buffer, `\t`, key, `: `)
for i, value := range values {
if i > 0 {
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) {
if items := getDataListProperty(view); len(items) > 0 {
session := view.Session()
buffer.WriteString(`<datalist id="`)
buffer.WriteString(dataListID(view))
buffer.WriteString(`">`)
writeStrings(buffer, `<datalist id="`, dataListID(view), `">`)
for _, text := range items {
text = normalizeItem(text, session)
@ -295,9 +294,7 @@ func dataListHtmlSubviews(view View, buffer *strings.Builder, normalizeItem func
if strings.ContainsRune(text, '\n') {
text = strings.ReplaceAll(text, "\n", `\n`)
}
buffer.WriteString(`<option value="`)
buffer.WriteString(textToHtml(text))
buffer.WriteString(`"></option>`)
writeStrings(buffer, `<option value="`, textToHtml(text), `"></option>`)
}
buffer.WriteString(`</datalist>`)
}
@ -305,9 +302,7 @@ func dataListHtmlSubviews(view View, buffer *strings.Builder, normalizeItem func
func dataListHtmlProperties(view View, buffer *strings.Builder) {
if len(getDataListProperty(view)) > 0 {
buffer.WriteString(` list="`)
buffer.WriteString(dataListID(view))
buffer.WriteString(`"`)
writeStrings(buffer, ` list="`, dataListID(view), `"`)
}
}

View File

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

View File

@ -415,9 +415,7 @@ func dragAndDropHtml(view View, buffer *strings.Builder) {
}
if dragData := base64DragData(view); dragData != "" {
buffer.WriteString(`draggable="true" data-drag="`)
buffer.WriteString(dragData)
buffer.WriteString(`" ondragstart="dragStartEvent(this, event)" `)
writeStrings(buffer, `draggable="true" data-drag="`, dragData, `" ondragstart="dragStartEvent(this, event)" `)
} else if len(getOneArgEventListeners[View, DragAndDropEvent](view, nil, DragStartEvent)) > 0 {
buffer.WriteString(` ondragstart="dragStartEvent(this, event)" `)
}
@ -444,9 +442,7 @@ func dragAndDropHtml(view View, buffer *strings.Builder) {
}
if img := GetDragImage(view); img != "" {
buffer.WriteString(` data-drag-image="`)
buffer.WriteString(img)
buffer.WriteString(`" `)
writeStrings(buffer, ` data-drag-image="`, img, `" `)
}
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"}
if n := GetDropEffectAllowed(view); n > 0 && n < len(effects) {
buffer.WriteString(` data-drop-effect-allowed="`)
buffer.WriteString(effects[n])
buffer.WriteString(`" `)
writeStrings(buffer, ` data-drop-effect-allowed="`, effects[n], `" `)
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -236,10 +236,7 @@ func (filter *filterData) cssStyle(session Session) string {
defer freeStringBuilder(buffer)
if value, ok := floatTextProperty(filter, Blur, session, 0); ok {
buffer.WriteString(string(Blur))
buffer.WriteRune('(')
buffer.WriteString(value)
buffer.WriteString("px)")
writeStrings(buffer, string(Blur), "(", value, "px)")
}
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 {
buffer.WriteRune(' ')
}
buffer.WriteString(string(tag))
buffer.WriteRune('(')
buffer.WriteString(value)
buffer.WriteString("%)")
writeStrings(buffer, string(tag), "(", value, "%)")
}
}
@ -258,10 +252,7 @@ func (filter *filterData) cssStyle(session Session) string {
if buffer.Len() > 0 {
buffer.WriteRune(' ')
}
buffer.WriteString(string(HueRotate))
buffer.WriteRune('(')
buffer.WriteString(value.cssString())
buffer.WriteRune(')')
writeStrings(buffer, string(HueRotate), "(", value.cssString(), ")")
}
var lead string

View File

@ -39,10 +39,7 @@ func focusEventsHtml(view View, buffer *strings.Builder) {
if view.Focusable() {
for _, tag := range []PropertyName{FocusEvent, LostFocusEvent} {
if js, ok := eventJsFunc[tag]; ok {
buffer.WriteString(js.jsEvent)
buffer.WriteString(`="`)
buffer.WriteString(js.jsFunc)
buffer.WriteString(`(this, event)" `)
writeStrings(buffer, js.jsEvent, `="`, js.jsFunc, `(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 src, srcset := imageViewSrc(imageView, imageResource); src != "" {
buffer.WriteString(` src="`)
buffer.WriteString(src)
buffer.WriteString(`"`)
writeStrings(buffer, ` src="`, src, `"`)
if srcset != "" {
buffer.WriteString(` srcset="`)
buffer.WriteString(srcset)
buffer.WriteString(`"`)
writeStrings(buffer, ` srcset="`, srcset, `"`)
}
}
}
if text := GetImageViewAltText(imageView); text != "" {
buffer.WriteString(` alt="`)
buffer.WriteString(text)
buffer.WriteString(`"`)
writeStrings(buffer, ` alt="`, text, `"`)
}
buffer.WriteString(` onload="imageLoaded(this, event)"`)

View File

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

View File

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

View File

@ -91,13 +91,8 @@ func (progress *progressBarData) htmlTag() string {
func (progress *progressBarData) htmlProperties(self View, buffer *strings.Builder) {
progress.viewData.htmlProperties(self, buffer)
buffer.WriteString(` max="`)
buffer.WriteString(strconv.FormatFloat(GetProgressBarMax(progress), 'f', -1, 64))
buffer.WriteByte('"')
buffer.WriteString(` value="`)
buffer.WriteString(strconv.FormatFloat(GetProgressBarValue(progress), 'f', -1, 64))
buffer.WriteByte('"')
writeStrings(buffer, ` max="`, strconv.FormatFloat(GetProgressBarMax(progress), 'f', -1, 64), `"`,
` value="`, strconv.FormatFloat(GetProgressBarValue(progress), 'f', -1, 64), `"`)
}
// 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,
indent string, objectTag string, tags []PropertyName) {
buffer.WriteString(objectTag)
buffer.WriteString(" {\n")
writeStrings(buffer, objectTag, " {\n")
indent2 := indent + "\t"
for _, tag := range tags {
if value, ok := properties.properties[tag]; ok {
text := propertyValueToString(tag, value, indent2)
if text != "" {
buffer.WriteString(indent2)
buffer.WriteString(string(tag))
buffer.WriteString(" = ")
buffer.WriteString(text)
buffer.WriteString(",\n")
writeStrings(buffer, indent2, string(tag), " = ", text, ",\n")
}
}
}
buffer.WriteString(indent)
buffer.WriteString("}")
writeStrings(buffer, indent, "}")
}
*/
@ -214,8 +207,7 @@ func (data *dataProperty) writeToBuffer(buffer *strings.Builder, indent string,
data.mutex.Lock()
defer data.mutex.Unlock()
buffer.WriteString(objectName)
buffer.WriteString("{ ")
writeStrings(buffer, objectName, "{ ")
comma := false
for _, tag := range tags {
if value, ok := data.properties[tag]; ok {
@ -224,9 +216,7 @@ func (data *dataProperty) writeToBuffer(buffer *strings.Builder, indent string,
if comma {
buffer.WriteString(", ")
}
buffer.WriteString(string(tag))
buffer.WriteString(" = ")
buffer.WriteString(text)
writeStrings(buffer, string(tag), " = ", text)
comma = true
}
}

View File

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

View File

@ -377,64 +377,44 @@ func (resizable *resizableData) htmlSubviews(self View, buffer *strings.Builder)
top = 2
if leftSide {
buffer.WriteString(`<div onmousedown="startResize(this, -1, -1, event)" style="cursor: nwse-resize; width: `)
buffer.WriteString(w)
buffer.WriteString(`; height: `)
buffer.WriteString(w)
buffer.WriteString(`;`)
writeStrings(buffer, `<div onmousedown="startResize(this, -1, -1, event)" style="cursor: nwse-resize; width: `,
w, `; height: `, w, `;`)
writePos(1, 2, 1, 2)
}
buffer.WriteString(`<div onmousedown="startResize(this, 0, -1, event)" style="cursor: ns-resize; width: 100%; height: `)
buffer.WriteString(w)
buffer.WriteString(`;`)
writeStrings(buffer, `<div onmousedown="startResize(this, 0, -1, event)" style="cursor: ns-resize; width: 100%; height: `, w, `;`)
writePos(left, left+1, 1, 2)
if rightSide {
buffer.WriteString(`<div onmousedown="startResize(this, 1, -1, event)" style="cursor: nesw-resize; width: `)
buffer.WriteString(w)
buffer.WriteString(`; height: `)
buffer.WriteString(w)
buffer.WriteString(`;`)
writeStrings(buffer, `<div onmousedown="startResize(this, 1, -1, event)" style="cursor: nesw-resize; width: `,
w, `; height: `, w, `;`)
writePos(left+1, left+2, 1, 2)
}
}
if leftSide {
buffer.WriteString(`<div onmousedown="startResize(this, -1, 0, event)" style="cursor: ew-resize; width: `)
buffer.WriteString(w)
buffer.WriteString(`; height: 100%;`)
writeStrings(buffer, `<div onmousedown="startResize(this, -1, 0, event)" style="cursor: ew-resize; width: `, w, `; height: 100%;`)
writePos(1, 2, top, top+1)
}
if rightSide {
buffer.WriteString(`<div onmousedown="startResize(this, 1, 0, event)" style="cursor: ew-resize; width: `)
buffer.WriteString(w)
buffer.WriteString(`; height: 100%;`)
writeStrings(buffer, `<div onmousedown="startResize(this, 1, 0, event)" style="cursor: ew-resize; width: `, w, `; height: 100%;`)
writePos(left+1, left+2, top, top+1)
}
if (side & BottomSide) != 0 {
if leftSide {
buffer.WriteString(`<div onmousedown="startResize(this, -1, 1, event)" style="cursor: nesw-resize; width: `)
buffer.WriteString(w)
buffer.WriteString(`; height: `)
buffer.WriteString(w)
buffer.WriteString(`;`)
writeStrings(buffer, `<div onmousedown="startResize(this, -1, 1, event)" style="cursor: nesw-resize; width: `,
w, `; height: `, w, `;`)
writePos(1, 2, top+1, top+2)
}
buffer.WriteString(`<div onmousedown="startResize(this, 0, 1, event)" style="cursor: ns-resize; width: 100%; height: `)
buffer.WriteString(w)
buffer.WriteString(`;`)
writeStrings(buffer, `<div onmousedown="startResize(this, 0, 1, event)" style="cursor: ns-resize; width: 100%; height: `, w, `;`)
writePos(left, left+1, top+1, top+2)
if rightSide {
buffer.WriteString(`<div onmousedown="startResize(this, 1, 1, event)" style="cursor: nwse-resize; width: `)
buffer.WriteString(w)
buffer.WriteString(`; height: `)
buffer.WriteString(w)
buffer.WriteString(`;`)
writeStrings(buffer, `<div onmousedown="startResize(this, 1, 1, event)" style="cursor: nwse-resize; width: `,
w, `; height: `, w, `;`)
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()
defer freeStringBuilder(buffer)
buffer.WriteString(`<div style="width: 18px; height: 18px; background-color: `)
buffer.WriteString(backgroundColor.cssString())
buffer.WriteString(`; border: 1px solid `)
buffer.WriteString(borderColor.cssString())
buffer.WriteString(`; border-radius: 4px;">`)
writeStrings(buffer, `<div style="width: 18px; height: 18px; background-color: `, backgroundColor.cssString(),
`; border: 1px solid `, borderColor.cssString(), `; border-radius: 4px;">`)
if checked {
buffer.WriteString(checkImage)
}

View File

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

View File

@ -585,11 +585,7 @@ func (layout *stackLayoutData) moveToFrontByIndex(index int, onShow []func(View)
buffer := allocStringBuilder()
defer freeStringBuilder(buffer)
buffer.WriteString(`stackTransitionEndEvent('`)
buffer.WriteString(layout.htmlID())
buffer.WriteString(`', 'move-`)
buffer.WriteString(view.htmlID())
buffer.WriteString(`', event)`)
writeStrings(buffer, `stackTransitionEndEvent('`, layout.htmlID(), `', 'move-`, view.htmlID(), `', event)`)
listener := buffer.String()
@ -667,9 +663,7 @@ func (layout *stackLayoutData) Append(view View) {
buffer := allocStringBuilder()
defer freeStringBuilder(buffer)
buffer.WriteString(`<div id="`)
buffer.WriteString(view.htmlID())
buffer.WriteString(`page" class="ruiStackPageLayout">`)
writeStrings(buffer, `<div id="`, view.htmlID(), `page" class="ruiStackPageLayout">`)
viewHTML(view, buffer, "")
buffer.WriteString(`</div>`)
@ -706,9 +700,7 @@ func (layout *stackLayoutData) Insert(view View, index int) {
buffer := allocStringBuilder()
defer freeStringBuilder(buffer)
buffer.WriteString(`<div id="`)
buffer.WriteString(view.htmlID())
buffer.WriteString(`page" class="ruiStackPageLayout" style="visibility: hidden;">`)
writeStrings(buffer, `<div id="`, view.htmlID(), `page" class="ruiStackPageLayout" style="visibility: hidden;">`)
viewHTML(view, buffer, "")
buffer.WriteString(`</div>`)
@ -782,25 +774,15 @@ func (layout *stackLayoutData) Push(view View, onPushFinished ...func()) {
view.setParentID(layout.htmlID())
layout.views = append(layout.views, view)
session := layout.Session()
buffer := allocStringBuilder()
defer freeStringBuilder(buffer)
buffer.WriteString(`<div id="`)
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(';')
session := layout.Session()
transitionCSS := layout.pushTransitionCSS()
buffer.WriteString(" transition: ")
buffer.WriteString(transitionCSS)
buffer.WriteString(`;">`)
writeStrings(buffer, `<div id="`, htmlID, `page" class="ruiStackPageLayout" `,
`ontransitionend="stackTransitionEndEvent('`, layout.htmlID(), `', 'push-`, htmlID, `', event)" `,
`style="z-index: 100; transform: `, transform.transformCSS(layout.session), `; transition: `, transitionCSS, `;">`)
viewHTML(view, buffer, "")
buffer.WriteString(`</div>`)
@ -854,11 +836,7 @@ func (layout *stackLayoutData) Pop(onPopFinished ...func(View)) bool {
buffer := allocStringBuilder()
defer freeStringBuilder(buffer)
buffer.WriteString(`stackTransitionEndEvent('`)
buffer.WriteString(layout.htmlID())
buffer.WriteString(`', 'pop-`)
buffer.WriteString(htmlID)
buffer.WriteString(`', event)`)
writeStrings(buffer, `stackTransitionEndEvent('`, layout.htmlID(), `', 'pop-`, htmlID, `', event)`)
listener := buffer.String()
pageID := htmlID + "page"
@ -892,10 +870,7 @@ func (layout *stackLayoutData) htmlSubviews(self View, buffer *strings.Builder)
if count := len(layout.views); count > 0 {
peek := count - 1
for i, view := range layout.views {
buffer.WriteString(`<div id="`)
buffer.WriteString(view.htmlID())
buffer.WriteString(`page`)
buffer.WriteString(`" class="ruiStackPageLayout"`)
writeStrings(buffer, `<div id="`, view.htmlID(), `page" class="ruiStackPageLayout"`)
if i != peek {
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) {
if content := GetTableContent(table); content != nil {
buffer.WriteString(` data-rows="`)
buffer.WriteString(strconv.Itoa(content.RowCount()))
buffer.WriteString(`" data-columns="`)
buffer.WriteString(strconv.Itoa(content.ColumnCount()))
buffer.WriteRune('"')
writeStrings(buffer, ` data-rows="`, strconv.Itoa(content.RowCount()),
`" data-columns="`, strconv.Itoa(content.ColumnCount()), `"`)
}
if selectionMode := GetTableSelectionMode(table); selectionMode != NoneSelection {
buffer.WriteString(` onfocus="tableViewFocusEvent(this, event)" onblur="tableViewBlurEvent(this, event)" data-focusitemstyle="`)
buffer.WriteString(tableViewCurrentStyle(table))
buffer.WriteString(`" data-bluritemstyle="`)
buffer.WriteString(tableViewCurrentInactiveStyle(table))
buffer.WriteRune('"')
writeStrings(buffer, ` onfocus="tableViewFocusEvent(this, event)" onblur="tableViewBlurEvent(this, event)" data-focusitemstyle="`,
tableViewCurrentStyle(table), `" data-bluritemstyle="`, tableViewCurrentInactiveStyle(table), `"`)
current := tableViewCurrent(table)
@ -1019,17 +1013,13 @@ func (table *tableViewData) htmlProperties(self View, buffer *strings.Builder) {
case RowSelection:
buffer.WriteString(` data-selection="row" onkeydown="tableViewRowKeyDownEvent(this, event)"`)
if current.Row >= 0 {
buffer.WriteString(` data-current="`)
buffer.WriteString(tableViewRowID(table, current.Row))
buffer.WriteRune('"')
writeStrings(buffer, ` data-current="`, tableViewRowID(table, current.Row), `"`)
}
case CellSelection:
buffer.WriteString(` data-selection="cell" onkeydown="tableViewCellKeyDownEvent(this, event)"`)
if current.Row >= 0 && current.Column >= 0 {
buffer.WriteString(` data-current="`)
buffer.WriteString(tableViewCellID(table, current.Row, current.Column))
buffer.WriteRune('"')
writeStrings(buffer, ` data-current="`, tableViewCellID(table, current.Row, current.Column), `"`)
}
}
}
@ -1116,9 +1106,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
}
}
buffer.WriteString(`<tr id="`)
buffer.WriteString(tableViewRowID(table, row))
buffer.WriteRune('"')
writeStrings(buffer, `<tr id="`, tableViewRowID(table, row), `"`)
if selectionMode == RowSelection {
if row == current.Row {
@ -1139,9 +1127,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
}
if cssBuilder.buffer.Len() > 0 {
buffer.WriteString(` style="`)
buffer.WriteString(cssBuilder.buffer.String())
buffer.WriteString(`"`)
writeStrings(buffer, ` style="`, cssBuilder.buffer.String(), `"`)
}
buffer.WriteString(">")
@ -1205,11 +1191,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
view.cssStyle(view, &cssBuilder)
}
buffer.WriteRune('<')
buffer.WriteString(cellTag)
buffer.WriteString(` id="`)
buffer.WriteString(tableViewCellID(table, row, column))
buffer.WriteString(`" class="ruiView`)
writeStrings(buffer, `<`, cellTag, ` id="`, tableViewCellID(table, row, column), `" class="ruiView`)
if selectionMode == CellSelection && row == current.Row && column == current.Column {
buffer.WriteRune(' ')
@ -1229,9 +1211,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
}
if columnSpan > 1 {
buffer.WriteString(` colspan="`)
buffer.WriteString(strconv.Itoa(columnSpan))
buffer.WriteRune('"')
writeStrings(buffer, ` colspan="`, strconv.Itoa(columnSpan), `"`)
for c := column + 1; c < column+columnSpan; c++ {
ignoreCells = append(ignoreCells, struct {
row int
@ -1241,9 +1221,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
}
if rowSpan > 1 {
buffer.WriteString(` rowspan="`)
buffer.WriteString(strconv.Itoa(rowSpan))
buffer.WriteRune('"')
writeStrings(buffer, ` rowspan="`, strconv.Itoa(rowSpan), `"`)
if columnSpan < 1 {
columnSpan = 1
}
@ -1258,9 +1236,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
}
if cssBuilder.buffer.Len() > 0 {
buffer.WriteString(` style="`)
buffer.WriteString(cssBuilder.buffer.String())
buffer.WriteRune('"')
writeStrings(buffer, ` style="`, cssBuilder.buffer.String(), `"`)
}
buffer.WriteRune('>')
@ -1275,18 +1251,14 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
table.cellViews = append(table.cellViews, value)
case Color:
buffer.WriteString(`<div style="display: inline; height: 1em; background-color: `)
buffer.WriteString(value.cssString())
buffer.WriteString(`">&nbsp;&nbsp;&nbsp;&nbsp;</div> `)
buffer.WriteString(value.String())
writeStrings(buffer, `<div style="display: inline; height: 1em; background-color: `,
value.cssString(), `">&nbsp;&nbsp;&nbsp;&nbsp;</div> `, value.String())
if namedColors == nil {
namedColors = NamedColors()
}
for _, namedColor := range namedColors {
if namedColor.Color == value {
buffer.WriteString(" (")
buffer.WriteString(namedColor.Name)
buffer.WriteRune(')')
writeStrings(buffer, " (", namedColor.Name, `)`)
break
}
}
@ -1319,9 +1291,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
}
*/
buffer.WriteString(`</`)
buffer.WriteString(cellTag)
buffer.WriteRune('>')
writeStrings(buffer, `</`, cellTag, `>`)
}
}
@ -1342,9 +1312,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
}
if cssBuilder.buffer.Len() > 0 {
buffer.WriteString(`<col style="`)
buffer.WriteString(cssBuilder.buffer.String())
buffer.WriteString(`">`)
writeStrings(buffer, `<col style="`, cssBuilder.buffer.String(), `">`)
} else {
buffer.WriteString("<col>")
}
@ -1376,12 +1344,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
switch value := value.(type) {
case string:
if style, ok := session.resolveConstants(value); ok {
buffer.WriteString(` class="`)
buffer.WriteString(style)
buffer.WriteString(`" style="vertical-align: `)
buffer.WriteString(vAlign)
buffer.WriteString(`;">`)
writeStrings(buffer, ` class="`, style, `" style="vertical-align: `, vAlign, `;">`)
return table.cellBorderFromStyle(style), table.cellPaddingFromStyle(style)
}
@ -1415,18 +1378,14 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
view.cssStyle(view, &cssBuilder)
if cssBuilder.buffer.Len() > 0 {
buffer.WriteString(` style="`)
buffer.WriteString(cssBuilder.buffer.String())
buffer.WriteString(`"`)
writeStrings(buffer, ` style="`, cssBuilder.buffer.String(), `"`)
}
buffer.WriteRune('>')
return border, padding
}
}
buffer.WriteString(` style="vertical-align: `)
buffer.WriteString(vAlign)
buffer.WriteString(`;">`)
writeStrings(buffer, ` style="vertical-align: `, vAlign, `;">`)
return nil, nil
}
@ -1454,9 +1413,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
}
if rowCount > footHeight+headHeight {
buffer.WriteString(`<tbody style="vertical-align: `)
buffer.WriteString(vAlign)
buffer.WriteString(`;">`)
writeStrings(buffer, `<tbody style="vertical-align: `, vAlign, `;">`)
tableCSS(headHeight, rowCount-footHeight, "td", cellBorder, cellPadding)
buffer.WriteString("</tbody>")
}
@ -1543,17 +1500,13 @@ func (table *tableViewData) writeCellHtml(adapter TableAdapter, row, column int,
table.cellViews = append(table.cellViews, value)
case Color:
buffer.WriteString(`<div style="display: inline; height: 1em; background-color: `)
buffer.WriteString(value.cssString())
buffer.WriteString(`">&nbsp;&nbsp;&nbsp;&nbsp;</div> `)
buffer.WriteString(value.String())
writeStrings(buffer, `<div style="display: inline; height: 1em; background-color: `, value.cssString(),
`">&nbsp;&nbsp;&nbsp;&nbsp;</div> `, value.String())
namedColors := NamedColors()
for _, namedColor := range namedColors {
if namedColor.Color == value {
buffer.WriteString(" (")
buffer.WriteString(namedColor.Name)
buffer.WriteRune(')')
writeStrings(buffer, " (", namedColor.Name, ")")
break
}
}

View File

@ -523,13 +523,9 @@ func (tabsLayout *tabsLayoutData) RemoveView(index int) View {
func (tabsLayout *tabsLayoutData) htmlProperties(self View, buffer *strings.Builder) {
tabsLayout.viewsContainerData.htmlProperties(self, buffer)
buffer.WriteString(` data-inactiveTabStyle="`)
buffer.WriteString(tabsLayoutInactiveTabStyle(tabsLayout))
buffer.WriteString(`" data-activeTabStyle="`)
buffer.WriteString(tabsLayoutActiveTabStyle(tabsLayout))
buffer.WriteString(`" data-current="`)
buffer.WriteString(strconv.Itoa(GetCurrent(tabsLayout)))
buffer.WriteRune('"')
writeStrings(buffer, ` data-inactiveTabStyle="`, tabsLayoutInactiveTabStyle(tabsLayout),
`" data-activeTabStyle="`, tabsLayoutActiveTabStyle(tabsLayout),
`" data-current="`, strconv.Itoa(GetCurrent(tabsLayout)), `"`)
}
func (tabsLayout *tabsLayoutData) cssStyle(self View, builder cssBuilder) {
@ -560,9 +556,7 @@ func (tabsLayout *tabsLayoutData) htmlSubviews(self View, buffer *strings.Builde
if location != HiddenTabs {
buffer.WriteString(`<div class="`)
buffer.WriteString(tabsLayout.tabBarStyle())
buffer.WriteString(`" style="display: flex;`)
writeStrings(buffer, `<div class="`, tabsLayout.tabBarStyle(), `" style="display: flex;`)
switch location {
case LeftTabs, LeftListTabs, TopTabs:
@ -622,37 +616,20 @@ func (tabsLayout *tabsLayoutData) htmlSubviews(self View, buffer *strings.Builde
title, _ = tabsLayout.Session().GetString(title)
}
buffer.WriteString(`<div id="`)
buffer.WriteString(tabsLayoutID)
buffer.WriteByte('-')
buffer.WriteString(strconv.Itoa(n))
buffer.WriteString(`" class="`)
writeStrings(buffer, `<div id="`, tabsLayoutID, `-`, strconv.Itoa(n), `" class="`)
if n == current {
buffer.WriteString(activeStyle)
} else {
buffer.WriteString(inactiveStyle)
}
buffer.WriteString(`" tabindex="0" onclick="tabClickEvent(this, '`)
buffer.WriteString(tabsLayoutID)
buffer.WriteString(`', `)
buffer.WriteString(strconv.Itoa(n))
buffer.WriteString(`, event)" onkeydown="tabKeyClickEvent('`)
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(`">`)
writeStrings(buffer, `" tabindex="0" `,
`onclick="tabClickEvent(this, '`, tabsLayoutID, `', `, strconv.Itoa(n), `, event)" `,
`onkeydown="tabKeyClickEvent('`, tabsLayoutID, `', `, strconv.Itoa(n), `, event)" `,
`style="`, tabStyle, `" data-container="`, tabsLayoutID,
`" data-view="`, tabsLayoutID, `-page`, strconv.Itoa(n), `">`)
if icon != "" {
buffer.WriteString(`<img id="`)
buffer.WriteString(view.htmlID())
writeStrings(buffer, `<img id="`, view.htmlID())
switch location {
case LeftTabs:
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:
buffer.WriteString(`-icon" style="grid-row-start: 1; grid-row-end: 2; grid-column-start: 1; grid-column-end: 2;" src="`)
}
buffer.WriteString(icon)
buffer.WriteString(`">`)
writeStrings(buffer, icon, `">`)
}
buffer.WriteString(`<div id="`)
buffer.WriteString(view.htmlID())
buffer.WriteString(`-title"`)
buffer.WriteString(titleStyle)
buffer.WriteString(textToHtml(title))
buffer.WriteString(`</div>`)
writeStrings(buffer, `<div id="`, view.htmlID(), `-title"`, titleStyle, textToHtml(title), `</div>`)
close, ok := boolProperty(view, TabCloseButton, tabsLayout.session)
if !ok {
close = closeButton
}
if close {
buffer.WriteString(`<div class="ruiTabCloseButton" tabindex="0" onclick="tabCloseClickEvent(this, '`)
buffer.WriteString(tabsLayoutID)
buffer.WriteString(`', `)
buffer.WriteString(strconv.Itoa(n))
buffer.WriteString(`, event)" onkeydown="tabCloseKeyClickEvent('`)
buffer.WriteString(tabsLayoutID)
buffer.WriteString(`', `)
buffer.WriteString(strconv.Itoa(n))
buffer.WriteString(`, event)" style="display: grid; `)
writeStrings(buffer, `<div class="ruiTabCloseButton" tabindex="0" `,
`onclick="tabCloseClickEvent(this, '`, tabsLayoutID, `', `, strconv.Itoa(n), `, event)" `,
`onkeydown="tabCloseKeyClickEvent('`, tabsLayoutID, `', `, strconv.Itoa(n), `, event)" `,
`style="display: grid; `)
switch location {
case LeftTabs:
@ -710,10 +676,7 @@ func (tabsLayout *tabsLayoutData) htmlSubviews(self View, buffer *strings.Builde
}
for n, view := range tabsLayout.views {
buffer.WriteString(`<div id="`)
buffer.WriteString(tabsLayoutID)
buffer.WriteString(`-page`)
buffer.WriteString(strconv.Itoa(n))
writeStrings(buffer, `<div id="`, tabsLayoutID, `-page`, strconv.Itoa(n))
if current != n {
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) {
if isQuotesNeeded(text) {
buffer.WriteRune('"')
buffer.WriteString(replaceEscapeSymbols(text))
buffer.WriteRune('"')
writeStrings(buffer, `"`, replaceEscapeSymbols(text), `"`)
} else {
buffer.WriteString(text)
}
@ -930,9 +928,7 @@ func (theme *theme) String() string {
return
}
buffer.WriteString("\t")
buffer.WriteString(tag)
buffer.WriteString(" = _{\n")
writeStrings(buffer, "\t", tag, " = _{\n")
tags := make([]string, 0, count)
for name := range constants {

View File

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

View File

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

View File

@ -130,3 +130,9 @@ func textToHtml(text string) string {
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()
if size, ok := floatTextProperty(player, VideoWidth, session, 0); ok && size != "0" {
buffer.WriteString(` width="`)
buffer.WriteString(size)
buffer.WriteString(`"`)
writeStrings(buffer, ` width="`, size, `"`)
}
if size, ok := floatTextProperty(player, VideoHeight, session, 0); ok && size != "0" {
buffer.WriteString(` height="`)
buffer.WriteString(size)
buffer.WriteString(`"`)
writeStrings(buffer, ` height="`, size, `"`)
}
if url, ok := stringProperty(player, Poster, session); ok && url != "" {
buffer.WriteString(` poster="`)
buffer.WriteString(url)
buffer.WriteString(`"`)
writeStrings(buffer, ` poster="`, url, `"`)
}
}

44
view.go
View File

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

View File

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

View File

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