diff --git a/animation.go b/animation.go index 3f92d64..68d222e 100644 --- a/animation.go +++ b/animation.go @@ -599,11 +599,14 @@ func (animation *animationData) String() string { for _, tag := range animation.AllTags() { if tag != PropertyTag { if value, ok := animation.properties[tag]; ok && value != nil { - buffer.WriteString("\n\t") - buffer.WriteString(string(tag)) - buffer.WriteString(" = ") - writePropertyValue(buffer, tag, value, "\t") - buffer.WriteRune(',') + text := propertyValueToString(tag, value, "\t") + if text != "" { + buffer.WriteString("\n\t") + buffer.WriteString(string(tag)) + buffer.WriteString(" = ") + buffer.WriteString(text) + buffer.WriteRune(',') + } } } } @@ -613,18 +616,21 @@ func (animation *animationData) String() string { buffer.WriteString("{\n") buffer.WriteString(indent) buffer.WriteString("from = ") - writePropertyValue(buffer, "from", prop.From, indent) + buffer.WriteString(propertyValueToString("from", prop.From, indent)) buffer.WriteString(",\n") buffer.WriteString(indent) buffer.WriteString("to = ") - writePropertyValue(buffer, "to", prop.To, indent) + buffer.WriteString(propertyValueToString("to", prop.To, indent)) for key, value := range prop.KeyFrames { - buffer.WriteString(",\n") - buffer.WriteString(indent) tag := strconv.Itoa(key) + "%" - buffer.WriteString(tag) - buffer.WriteString(" = ") - writePropertyValue(buffer, PropertyName(tag), value, indent) + text := propertyValueToString(PropertyName(tag), value, indent) + if text != "" { + buffer.WriteString(",\n") + buffer.WriteString(indent) + buffer.WriteString(tag) + buffer.WriteString(" = ") + buffer.WriteString(text) + } } buffer.WriteString("\n") buffer.WriteString(indent[1:]) @@ -725,12 +731,15 @@ func (animation *animationData) writeTransitionString(tag PropertyName, buffer * writeFloatProperty := func(name PropertyName) bool { if value := animation.getRaw(name); value != nil { - buffer.WriteString(lead) - buffer.WriteString(string(name)) - buffer.WriteString(" = ") - writePropertyValue(buffer, name, value, "") - lead = ", " - return true + text := propertyValueToString(name, value, "") + if text != "" { + buffer.WriteString(lead) + buffer.WriteString(string(name)) + buffer.WriteString(" = ") + buffer.WriteString(text) + lead = ", " + return true + } } return false } @@ -770,11 +779,14 @@ func (animation *animationData) writeAnimationString(tag PropertyName, buffer *s for _, tag := range animation.AllTags() { if tag != PropertyTag { if value := animation.Get(tag); value != nil { - buffer.WriteString("\n" + indent2) - buffer.WriteString(string(tag)) - buffer.WriteString(" = ") - writePropertyValue(buffer, tag, value, indent2) - buffer.WriteRune(',') + text := propertyValueToString(tag, value, indent2) + if text != "" { + buffer.WriteString("\n" + indent2) + buffer.WriteString(string(tag)) + buffer.WriteString(" = ") + buffer.WriteString(text) + buffer.WriteRune(',') + } } } } @@ -785,18 +797,21 @@ func (animation *animationData) writeAnimationString(tag PropertyName, buffer *s indent2 := indent + "\t" buffer.WriteString(indent2) buffer.WriteString("from = ") - writePropertyValue(buffer, "from", prop.From, indent2) + buffer.WriteString(propertyValueToString("from", prop.From, indent2)) buffer.WriteString(",\n") buffer.WriteString(indent2) buffer.WriteString("to = ") - writePropertyValue(buffer, "to", prop.To, indent2) + buffer.WriteString(propertyValueToString("to", prop.To, indent2)) for key, value := range prop.KeyFrames { - buffer.WriteString(",\n") - buffer.WriteString(indent2) - tag := strconv.Itoa(key) + "%" - buffer.WriteString(tag) - buffer.WriteString(" = ") - writePropertyValue(buffer, PropertyName(tag), value, indent2) + 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) + } } buffer.WriteString(",\n") buffer.WriteString(indent) diff --git a/border.go b/border.go index ff07794..08be325 100644 --- a/border.go +++ b/border.go @@ -383,13 +383,16 @@ func (border *borderProperty) writeString(buffer *strings.Builder, indent string comma := false write := func(tag PropertyName, value any) { - if comma { - buffer.WriteString(", ") + text := propertyValueToString(tag, value, indent) + if text != "" { + if comma { + buffer.WriteString(", ") + } + buffer.WriteString(string(tag)) + buffer.WriteString(" = ") + buffer.WriteString(text) + comma = true } - buffer.WriteString(string(tag)) - buffer.WriteString(" = ") - writePropertyValue(buffer, BorderStyle, value, indent) - comma = true } for _, tag := range []PropertyName{Style, Width, ColorTag} { diff --git a/bounds.go b/bounds.go index 3a6931d..2eb7470 100644 --- a/bounds.go +++ b/bounds.go @@ -84,23 +84,6 @@ func (bounds *boundsPropertyData) String() string { return runStringWriter(bounds) } -func (bounds *boundsPropertyData) writeString(buffer *strings.Builder, indent string) { - buffer.WriteString("_{ ") - comma := false - for _, tag := range []PropertyName{Top, Right, Bottom, Left} { - if value, ok := bounds.properties[tag]; ok { - if comma { - buffer.WriteString(", ") - } - buffer.WriteString(string(tag)) - buffer.WriteString(" = ") - writePropertyValue(buffer, tag, value, indent) - comma = true - } - } - buffer.WriteString(" }") -} - func (bounds *boundsPropertyData) Bounds(session Session) Bounds { top, _ := sizeProperty(bounds, Top, session) right, _ := sizeProperty(bounds, Right, session) diff --git a/canvas.go b/canvas.go index f76f6a8..2b3af5a 100644 --- a/canvas.go +++ b/canvas.go @@ -815,40 +815,6 @@ func (canvas *canvasData) FillAndStrokeEllipse(x, y, radiusX, radiusY, rotation } } -/* -func (canvas *canvasData) writePointArgs(x, y float64) { - canvas.script.WriteString(strconv.FormatFloat(x, 'g', -1, 64)) - canvas.script.WriteRune(',') - canvas.script.WriteString(strconv.FormatFloat(y, 'g', -1, 64)) -} - -func (canvas *canvasData) writeStringArgs(text string, script *strings.Builder) { - //rText := []rune(text) - for _, ch := range text { - switch ch { - case '\t': - script.WriteString(`\t`) - case '\n': - script.WriteString(`\n`) - case '\r': - script.WriteString(`\r`) - case '\\': - script.WriteString(`\\`) - case '"': - script.WriteString(`\"`) - case '\'': - script.WriteString(`\'`) - default: - if ch < ' ' { - script.WriteString(fmt.Sprintf("\\x%02X", int(ch))) - } else { - script.WriteRune(ch) - } - } - } -} -*/ - func (canvas *canvasData) FillText(x, y float64, text string) { canvas.session.callCanvasFunc("fillText", text, x, y) } diff --git a/clipShape.go b/clipShape.go index fd2085e..5d1322e 100644 --- a/clipShape.go +++ b/clipShape.go @@ -230,13 +230,16 @@ func (clip *insetClipData) writeString(buffer *strings.Builder, indent string) { comma := false for _, tag := range []PropertyName{Top, Right, Bottom, Left, Radius} { if value, ok := clip.properties[tag]; ok { - if comma { - buffer.WriteString(", ") + text := propertyValueToString(tag, value, indent) + if text != "" { + if comma { + buffer.WriteString(", ") + } + buffer.WriteString(string(tag)) + buffer.WriteString(" = ") + buffer.WriteString(text) + comma = true } - buffer.WriteString(string(tag)) - buffer.WriteString(" = ") - writePropertyValue(buffer, tag, value, indent) - comma = true } } @@ -303,13 +306,16 @@ func (clip *circleClipData) writeString(buffer *strings.Builder, indent string) comma := false for _, tag := range []PropertyName{Radius, X, Y} { if value, ok := clip.properties[tag]; ok { - if comma { - buffer.WriteString(", ") + text := propertyValueToString(tag, value, indent) + if text != "" { + if comma { + buffer.WriteString(", ") + } + buffer.WriteString(string(tag)) + buffer.WriteString(" = ") + buffer.WriteString(text) + comma = true } - buffer.WriteString(string(tag)) - buffer.WriteString(" = ") - writePropertyValue(buffer, tag, value, indent) - comma = true } } @@ -380,13 +386,16 @@ func (clip *ellipseClipData) writeString(buffer *strings.Builder, indent string) comma := false for _, tag := range []PropertyName{RadiusX, RadiusY, X, Y} { if value, ok := clip.properties[tag]; ok { - if comma { - buffer.WriteString(", ") + text := propertyValueToString(tag, value, indent) + if text != "" { + if comma { + buffer.WriteString(", ") + } + buffer.WriteString(string(tag)) + buffer.WriteString(" = ") + buffer.WriteString(text) + comma = true } - buffer.WriteString(string(tag)) - buffer.WriteString(" = ") - writePropertyValue(buffer, tag, value, indent) - comma = true } } @@ -511,11 +520,16 @@ func (clip *polygonClipData) writeString(buffer *strings.Builder, indent string) if points := clip.points(); points != nil { buffer.WriteString(string(Points)) buffer.WriteString(` = "`) - for i, value := range points { - if i > 0 { - buffer.WriteString(", ") + comma := false + for _, value := range points { + text := propertyValueToString("", value, indent) + if text != "" { + if comma { + buffer.WriteString(", ") + } + buffer.WriteString(text) + comma = true } - writePropertyValue(buffer, "", value, indent) } buffer.WriteString(`" `) diff --git a/columnSeparator.go b/columnSeparator.go index 7112f0c..fd3fb2d 100644 --- a/columnSeparator.go +++ b/columnSeparator.go @@ -2,7 +2,6 @@ package rui import ( "fmt" - "strings" ) // ColumnSeparatorProperty is the interface of a view separator data @@ -114,24 +113,6 @@ func normalizeColumnSeparatorTag(tag PropertyName) PropertyName { return tag } -func (separator *columnSeparatorProperty) writeString(buffer *strings.Builder, indent string) { - buffer.WriteString("_{ ") - comma := false - for _, tag := range []PropertyName{Style, Width, ColorTag} { - if value, ok := separator.properties[tag]; ok { - if comma { - buffer.WriteString(", ") - } - buffer.WriteString(string(tag)) - buffer.WriteString(" = ") - writePropertyValue(buffer, BorderStyle, value, indent) - comma = true - } - } - - buffer.WriteString(" }") -} - func (separator *columnSeparatorProperty) String() string { return runStringWriter(separator) } diff --git a/filter.go b/filter.go index 850dc5c..887f953 100644 --- a/filter.go +++ b/filter.go @@ -218,21 +218,7 @@ func (filter *filterData) String() string { } func (filter *filterData) writeString(buffer *strings.Builder, indent string) { - buffer.WriteString("filter { ") - comma := false - tags := filter.AllTags() - for _, tag := range tags { - if value, ok := filter.properties[tag]; ok { - if comma { - buffer.WriteString(", ") - } - buffer.WriteString(string(tag)) - buffer.WriteString(" = ") - writePropertyValue(buffer, tag, value, indent) - comma = true - } - } - buffer.WriteString(" }") + filter.writeToBuffer(buffer, indent, "filter", filter.AllTags()) } func (filter *filterData) cssStyle(session Session) string { diff --git a/outline.go b/outline.go index 7ca284e..188d22d 100644 --- a/outline.go +++ b/outline.go @@ -40,24 +40,6 @@ func (outline *outlinePropertyData) init() { outline.supportedProperties = []PropertyName{Style, Width, ColorTag} } -func (outline *outlinePropertyData) writeString(buffer *strings.Builder, indent string) { - buffer.WriteString("_{ ") - comma := false - for _, tag := range []PropertyName{Style, Width, ColorTag} { - if value, ok := outline.properties[tag]; ok { - if comma { - buffer.WriteString(", ") - } - buffer.WriteString(string(tag)) - buffer.WriteString(" = ") - writePropertyValue(buffer, BorderStyle, value, indent) - comma = true - } - } - - buffer.WriteString(" }") -} - func (outline *outlinePropertyData) String() string { return runStringWriter(outline) } diff --git a/popup.go b/popup.go index d8de62e..5284f59 100644 --- a/popup.go +++ b/popup.go @@ -279,6 +279,7 @@ type popupButton struct { // Popup represents a Popup view type Popup interface { Properties + fmt.Stringer // View returns a content view of the popup View() View @@ -600,6 +601,7 @@ func (popup *popupData) Set(tag PropertyName, value any) bool { switch value := value.(type) { case View: popup.contentView = value + popup.setRaw(Content, popup.contentView) case DataObject: view := CreateViewFromObject(popup.session, value, nil) @@ -607,6 +609,7 @@ func (popup *popupData) Set(tag PropertyName, value any) bool { return false } popup.contentView = view + popup.setRaw(Content, popup.contentView) case string: if len(value) > 0 && value[0] == '@' { @@ -617,6 +620,7 @@ func (popup *popupData) Set(tag PropertyName, value any) bool { } } popup.contentView = NewTextView(popup.session, Params{Text: value}) + popup.setRaw(Content, value) default: notCompatibleType(Buttons, value) @@ -626,12 +630,14 @@ func (popup *popupData) Set(tag PropertyName, value any) bool { if binding := popup.getRaw(Binding); binding != nil { popup.contentView.Set(Binding, binding) } - popup.setRaw(Content, popup.contentView) + popup.propertyChanged(Content) return true case Binding: - popup.contentView.Set(Binding, value) + if popup.contentView != nil { + popup.contentView.Set(Binding, value) + } popup.setRaw(Binding, value) popup.propertyChanged(Binding) return true @@ -777,18 +783,19 @@ func (popup *popupData) animationProperty() AnimationProperty { }) } -func (popup *popupData) AllTags() []PropertyName { - tags := make([]PropertyName, 0, len(popup.properties)+1) - for tag := range popup.properties { - tags = append(tags, tag) +/* + func (popup *popupData) AllTags() []PropertyName { + tags := make([]PropertyName, 0, len(popup.properties)+1) + for tag := range popup.properties { + tags = append(tags, tag) + } + if popup.contentView != nil { + tags = append(tags, Content) + } + slices.Sort(tags) + return tags } - if popup.contentView != nil { - tags = append(tags, Content) - } - slices.Sort(tags) - return tags -} - +*/ func (popup *popupData) View() View { return popup.contentView } @@ -797,6 +804,13 @@ func (popup *popupData) Session() Session { return popup.session } +func (popup *popupData) String() string { + buffer := allocStringBuilder() + defer freeStringBuilder(buffer) + writeViewStyle("Popup", popup, buffer, "", nil) + return buffer.String() +} + func (popup *popupData) setButtons(value any) bool { popupButtonFromObject := func(obj DataObject) popupButton { var button popupButton @@ -1570,3 +1584,13 @@ func valueToPopupEventListeners(value any) ([]popupListener, bool) { return nil, false } + +func getPopupListenerBinding(listeners []popupListener) string { + for _, listener := range listeners { + raw := listener.rawListener() + if text, ok := raw.(string); ok && text != "" { + return text + } + } + return "" +} diff --git a/properties.go b/properties.go index 3bd673d..f0814a3 100644 --- a/properties.go +++ b/properties.go @@ -102,11 +102,14 @@ func (properties *propertyList) writeToBuffer(buffer *strings.Builder, for _, tag := range tags { if value, ok := properties.properties[tag]; ok { - buffer.WriteString(indent2) - buffer.WriteString(string(tag)) - buffer.WriteString(" = ") - writePropertyValue(buffer, tag, value, indent2) - buffer.WriteString(",\n") + text := propertyValueToString(tag, value, indent2) + if text != "" { + buffer.WriteString(indent2) + buffer.WriteString(string(tag)) + buffer.WriteString(" = ") + buffer.WriteString(text) + buffer.WriteString(",\n") + } } } @@ -158,3 +161,28 @@ func (data *dataProperty) Get(tag PropertyName) any { func (data *dataProperty) Remove(tag PropertyName) { data.remove(data, data.normalize(tag)) } + +func (data *dataProperty) writeToBuffer(buffer *strings.Builder, indent string, objectName string, tags []PropertyName) { + buffer.WriteString(objectName) + buffer.WriteString("{ ") + comma := false + for _, tag := range tags { + if value, ok := data.properties[tag]; ok { + text := propertyValueToString(tag, value, indent) + if text != "" { + if comma { + buffer.WriteString(", ") + } + buffer.WriteString(string(tag)) + buffer.WriteString(" = ") + buffer.WriteString(text) + comma = true + } + } + } + buffer.WriteString(" }") +} + +func (data *dataProperty) writeString(buffer *strings.Builder, indent string) { + data.writeToBuffer(buffer, indent, "_", data.AllTags()) +} diff --git a/radius.go b/radius.go index 3108f36..ad87f78 100644 --- a/radius.go +++ b/radius.go @@ -475,24 +475,6 @@ func (radius *radiusPropertyData) init() { } } -func (radius *radiusPropertyData) writeString(buffer *strings.Builder, indent string) { - buffer.WriteString("_{ ") - comma := false - for _, tag := range radius.supportedProperties { - if value, ok := radius.properties[tag]; ok { - if comma { - buffer.WriteString(", ") - } - buffer.WriteString(string(tag)) - buffer.WriteString(" = ") - writePropertyValue(buffer, tag, value, indent) - comma = true - } - } - - buffer.WriteString(" }") -} - func (radius *radiusPropertyData) String() string { return runStringWriter(radius) } diff --git a/ruiWriter.go b/ruiWriter.go index 7fd47b5..f4bc473 100644 --- a/ruiWriter.go +++ b/ruiWriter.go @@ -59,7 +59,7 @@ func (writer *ruiWriterData) writeString(str string) { {old: "\"", new: `\"`}, } for _, s := range replace { - str = strings.Replace(str, s.old, s.new, -1) + str = strings.ReplaceAll(str, s.old, s.new) } writer.buffer.WriteRune('"') writer.buffer.WriteString(str) diff --git a/shadow.go b/shadow.go index ab5ce78..0f87200 100644 --- a/shadow.go +++ b/shadow.go @@ -281,23 +281,6 @@ func (shadow *shadowPropertyData) String() string { return runStringWriter(shadow) } -func (shadow *shadowPropertyData) writeString(buffer *strings.Builder, indent string) { - buffer.WriteString("_{ ") - comma := false - for _, tag := range shadow.AllTags() { - if value, ok := shadow.properties[tag]; ok { - if comma { - buffer.WriteString(", ") - } - buffer.WriteString(string(tag)) - buffer.WriteString(" = ") - writePropertyValue(buffer, tag, value, indent) - comma = true - } - } - buffer.WriteString(" }") -} - func setShadowProperty(properties Properties, tag PropertyName, value any) bool { if value == nil { diff --git a/theme.go b/theme.go index 1e425a8..03870dc 100644 --- a/theme.go +++ b/theme.go @@ -902,19 +902,9 @@ func (theme *theme) String() string { defer freeStringBuilder(buffer) writeString := func(text string) { - if strings.ContainsAny(text, " \t\n\r\\\"'`,;{}[]()") { - replace := []struct{ old, new string }{ - {old: "\\", new: `\\`}, - {old: "\t", new: `\t`}, - {old: "\r", new: `\r`}, - {old: "\n", new: `\n`}, - {old: "\"", new: `\"`}, - } - for _, s := range replace { - text = strings.Replace(text, s.old, s.new, -1) - } + if isQuotesNeeded(text) { buffer.WriteRune('"') - buffer.WriteString(text) + buffer.WriteString(replaceEscapeSymbols(text)) buffer.WriteRune('"') } else { buffer.WriteString(text) diff --git a/transform.go b/transform.go index cb5c10c..f359e38 100644 --- a/transform.go +++ b/transform.go @@ -328,23 +328,6 @@ func (transform *transformPropertyData) String() string { return buffer.String() } -func (transform *transformPropertyData) writeString(buffer *strings.Builder, indent string) { - buffer.WriteString("_{ ") - comma := false - for _, tag := range transform.supportedProperties { - if value, ok := transform.properties[tag]; ok { - if comma { - buffer.WriteString(", ") - } - buffer.WriteString(string(tag)) - buffer.WriteString(" = ") - writePropertyValue(buffer, tag, value, indent) - comma = true - } - } - buffer.WriteString(" }") -} - func transformSet(properties Properties, tag PropertyName, value any) []PropertyName { switch tag { diff --git a/viewStyle.go b/viewStyle.go index 045e29c..de5cec3 100644 --- a/viewStyle.go +++ b/viewStyle.go @@ -551,235 +551,134 @@ func viewStyleGet(style Properties, tag PropertyName) any { return style.getRaw(tag) } -func supportedPropertyValue(value any) bool { - switch value := value.(type) { - case string, bool, float32, float64, int, stringWriter, fmt.Stringer: - return true - - case []string: - return len(value) > 0 - - case []ShadowProperty: - return len(value) > 0 - - case []View: - return len(value) > 0 - - case []any: - return len(value) > 0 - - case []BackgroundElement: - return len(value) > 0 - - case []BackgroundGradientPoint: - return len(value) > 0 - - case []BackgroundGradientAngle: - return len(value) > 0 - - case map[PropertyName]AnimationProperty: - return len(value) > 0 - - case []AnimationProperty: - return len(value) > 0 - - case []noArgListener[View]: - return getNoArgBinding(value) != "" - - case []noArgListener[ImageView]: - return getNoArgBinding(value) != "" - - case []noArgListener[MediaPlayer]: - return getNoArgBinding(value) != "" - - case []oneArgListener[View, KeyEvent]: - return getOneArgBinding(value) != "" - - case []oneArgListener[View, MouseEvent]: - return getOneArgBinding(value) != "" - - case []oneArgListener[View, TouchEvent]: - return getOneArgBinding(value) != "" - - case []oneArgListener[View, PointerEvent]: - return getOneArgBinding(value) != "" - - case []oneArgListener[View, PropertyName]: - return getOneArgBinding(value) != "" - - case []oneArgListener[View, string]: - return getOneArgBinding(value) != "" - - case []oneArgListener[View, Frame]: - return getOneArgBinding(value) != "" - - case []oneArgListener[View, DragAndDropEvent]: - return getOneArgBinding(value) != "" - - case []oneArgListener[Checkbox, bool]: - return getOneArgBinding(value) != "" - - case []oneArgListener[FilePicker, []FileInfo]: - return getOneArgBinding(value) != "" - - case []oneArgListener[ListView, int]: - return getOneArgBinding(value) != "" - - case []oneArgListener[ListView, []int]: - return getOneArgBinding(value) != "" - - case []oneArgListener[MediaPlayer, float64]: - return getOneArgBinding(value) != "" - - case []oneArgListener[TableView, int]: - return getOneArgBinding(value) != "" - - case []oneArgListener[TabsLayout, int]: - return getOneArgBinding(value) != "" - - case []twoArgListener[ColorPicker, Color]: - return getTwoArgBinding(value) != "" - - case []twoArgListener[DatePicker, time.Time]: - return getTwoArgBinding(value) != "" - - case []twoArgListener[TimePicker, time.Time]: - return getTwoArgBinding(value) != "" - - case []twoArgListener[DropDownList, int]: - return getTwoArgBinding(value) != "" - - case []twoArgListener[EditView, string]: - return getTwoArgBinding(value) != "" - - case []twoArgListener[NumberPicker, float64]: - return getTwoArgBinding(value) != "" - - case []twoArgListener[TableView, int]: - return getTwoArgBinding(value) != "" - - case []twoArgListener[TabsLayout, int]: - return getTwoArgBinding(value) != "" - - case []mediaPlayerErrorListener: - return getMediaPlayerErrorListenerBinding(value) != "" - - case map[PropertyName]oneArgListener[View, PropertyName]: - for _, listener := range value { - if text, ok := listener.rawListener().(string); ok && text != "" { +func isQuotesNeeded(text string) bool { + if len(text) == 1 { + simple := (text[0] >= '0' && text[0] <= '9') || (text[0] >= 'A' && text[0] <= 'Z') || (text[0] >= 'a' && text[0] <= 'z') + return !simple + } else { + for _, ch := range text { + if (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || + ch == '+' || ch == '-' || ch == '@' || ch == '/' || ch == '_' || ch == '.' || + ch == ':' || ch == '#' || ch == '%' || ch == 'π' || ch == '°' { + } else { return true } } - return false - - default: - return false } + return false } -func writePropertyValue(buffer *strings.Builder, tag PropertyName, value any, indent string) { +func replaceEscapeSymbols(text string) string { + replace := []struct{ old, new string }{ + {old: "\\", new: `\\`}, + {old: "\t", new: `\t`}, + {old: "\r", new: `\r`}, + {old: "\n", new: `\n`}, + {old: "\"", new: `\"`}, + } + for _, s := range replace { + text = strings.ReplaceAll(text, s.old, s.new) + } + return text +} - writeString := func(text string) { - simple := (tag != Text && tag != Title && tag != Summary) - if simple { - if len(text) == 1 { - simple = (text[0] >= '0' && text[0] <= '9') || (text[0] >= 'A' && text[0] <= 'Z') || (text[0] >= 'a' && text[0] <= 'z') - } else { - for _, ch := range text { - if (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || - ch == '+' || ch == '-' || ch == '@' || ch == '/' || ch == '_' || ch == '.' || - ch == ':' || ch == '#' || ch == '%' || ch == 'π' || ch == '°' { - } else { - simple = false - break - } - } - } - } +func propertyValueToString(tag PropertyName, value any, indent string) string { - if !simple { - replace := []struct{ old, new string }{ - {old: "\\", new: `\\`}, - {old: "\t", new: `\t`}, - {old: "\r", new: `\r`}, - {old: "\n", new: `\n`}, - {old: "\"", new: `\"`}, - } - for _, s := range replace { - text = strings.Replace(text, s.old, s.new, -1) - } + writeString := func(buffer *strings.Builder, text string) string { + if isQuotesNeeded(text) { buffer.WriteRune('"') - buffer.WriteString(text) + buffer.WriteString(replaceEscapeSymbols(text)) buffer.WriteRune('"') } else { buffer.WriteString(text) } + return text } switch value := value.(type) { case string: - writeString(value) + if tag == Text || tag == Title || tag == Summary || isQuotesNeeded(value) { + return `"` + replaceEscapeSymbols(value) + `"` + } + return value case []string: if len(value) == 0 { - buffer.WriteString("[]") - } else { - size := 0 - for _, text := range value { - size += len(text) + 2 - } - - if size < 80 { - lead := "[" - for _, text := range value { - buffer.WriteString(lead) - writeString(text) - lead = ", " - } - } else { - buffer.WriteString("[\n") - for _, text := range value { - buffer.WriteString(indent) - buffer.WriteRune('\t') - writeString(text) - buffer.WriteString(",\n") - } - buffer.WriteString(indent) - } - buffer.WriteRune(']') + return "[]" } + size := 0 + for _, text := range value { + size += len(text) + 2 + } + + buffer := allocStringBuilder() + defer freeStringBuilder(buffer) + + if size < 80 { + lead := "[" + for _, text := range value { + buffer.WriteString(lead) + writeString(buffer, text) + lead = ", " + } + } else { + buffer.WriteString("[\n") + for _, text := range value { + buffer.WriteString(indent) + buffer.WriteRune('\t') + writeString(buffer, text) + buffer.WriteString(",\n") + } + buffer.WriteString(indent) + } + buffer.WriteRune(']') + return buffer.String() + case bool: if value { - buffer.WriteString("true") - } else { - buffer.WriteString("false") + return "true" } + return "false" case float32: - fmt.Fprintf(buffer, "%g", float64(value)) + return fmt.Sprintf("%g", float64(value)) case float64: - fmt.Fprintf(buffer, "%g", value) + return fmt.Sprintf("%g", value) case int: if prop, ok := enumProperties[tag]; ok && value >= 0 && value < len(prop.values) { - buffer.WriteString(prop.values[value]) - } else { - buffer.WriteString(strconv.Itoa(value)) + return prop.values[value] } + return strconv.Itoa(value) case stringWriter: + buffer := allocStringBuilder() + defer freeStringBuilder(buffer) value.writeString(buffer, indent+"\t") + return buffer.String() + + case View: + buffer := allocStringBuilder() + defer freeStringBuilder(buffer) + + writeViewStyle(value.Tag(), value, buffer, indent, value.excludeTags()) + return buffer.String() case fmt.Stringer: - writeString(value.String()) + return value.String() case []ShadowProperty: - switch len(value) { - case 0: - // do nothing + size := len(value) + if size == 0 { + return "" + } + buffer := allocStringBuilder() + defer freeStringBuilder(buffer) + + switch len(value) { case 1: value[0].writeString(buffer, indent) @@ -795,58 +694,73 @@ func writePropertyValue(buffer *strings.Builder, tag PropertyName, value any, in buffer.WriteString(indent) buffer.WriteRune(']') } + return buffer.String() case []View: - switch len(value) { - case 0: - buffer.WriteString("[]") + size := len(value) + if size == 0 { + return "[]" + } - case 1: + buffer := allocStringBuilder() + defer freeStringBuilder(buffer) + + if size == 1 { writeViewStyle(value[0].Tag(), value[0], buffer, indent, value[0].excludeTags()) - - default: + } else { buffer.WriteString("[\n") indent2 := indent + "\t" - for _, v := range value { + for _, view := range value { buffer.WriteString(indent2) - writeViewStyle(v.Tag(), v, buffer, indent2, v.excludeTags()) + writeViewStyle(view.Tag(), view, buffer, indent2, view.excludeTags()) buffer.WriteString(",\n") } buffer.WriteString(indent) buffer.WriteRune(']') } + return buffer.String() case []any: - switch count := len(value); count { - case 0: - buffer.WriteString("[]") + size := len(value) + if size == 0 { + return "[]" + } - case 1: - writePropertyValue(buffer, tag, value[0], indent) + buffer := allocStringBuilder() + defer freeStringBuilder(buffer) - default: + if size == 1 { + return propertyValueToString(tag, value[0], indent) + } else { buffer.WriteString("[ ") comma := false for _, v := range value { - if comma { - buffer.WriteString(", ") + text := propertyValueToString(tag, v, indent) + if text != "" { + if comma { + buffer.WriteString(", ") + } + buffer.WriteString(text) + comma = true } - writePropertyValue(buffer, tag, v, indent) - comma = true } buffer.WriteString(" ]") } + return buffer.String() case []BackgroundElement: - switch len(value) { - case 0: - buffer.WriteString("[]\n") + size := len(value) + if size == 0 { + return "[]" + } - case 1: + buffer := allocStringBuilder() + defer freeStringBuilder(buffer) + + if size == 1 { value[0].writeString(buffer, indent) - - default: + } else { buffer.WriteString("[\n") indent2 := indent + "\t" for _, element := range value { @@ -858,8 +772,12 @@ func writePropertyValue(buffer *strings.Builder, tag PropertyName, value any, in buffer.WriteString(indent) buffer.WriteRune(']') } + return buffer.String() case []BackgroundGradientPoint: + buffer := allocStringBuilder() + defer freeStringBuilder(buffer) + buffer.WriteRune('"') for i, point := range value { if i > 0 { @@ -868,8 +786,12 @@ func writePropertyValue(buffer *strings.Builder, tag PropertyName, value any, in buffer.WriteString(point.String()) } buffer.WriteRune('"') + return buffer.String() case []BackgroundGradientAngle: + buffer := allocStringBuilder() + defer freeStringBuilder(buffer) + buffer.WriteRune('"') for i, point := range value { if i > 0 { @@ -878,19 +800,23 @@ func writePropertyValue(buffer *strings.Builder, tag PropertyName, value any, in buffer.WriteString(point.String()) } buffer.WriteRune('"') + return buffer.String() case map[PropertyName]AnimationProperty: - switch count := len(value); count { - case 0: - buffer.WriteString("[]") + size := len(value) + if size == 0 { + return "[]" + } - case 1: + buffer := allocStringBuilder() + defer freeStringBuilder(buffer) + + if size == 1 { for tag, animation := range value { animation.writeTransitionString(tag, buffer) break } - - default: + } else { tags := make([]PropertyName, 0, len(value)) for tag := range value { tags = append(tags, tag) @@ -908,121 +834,171 @@ func writePropertyValue(buffer *strings.Builder, tag PropertyName, value any, in buffer.WriteString(indent) buffer.WriteRune(']') } + return buffer.String() case []AnimationProperty: - switch count := len(value); count { - case 0: - buffer.WriteString("[]") - - default: - buffer.WriteString("[\n") - indent2 := indent + "\t" - for _, anim := range value { - if anim != nil { - anim.writeAnimationString(Animation, buffer, indent2) - } - } - buffer.WriteString(indent) - buffer.WriteRune(']') + size := len(value) + if size == 0 { + return "[]" } + buffer := allocStringBuilder() + defer freeStringBuilder(buffer) + + buffer.WriteString("[\n") + indent2 := indent + "\t" + for _, anim := range value { + if anim != nil { + anim.writeAnimationString(Animation, buffer, indent2) + } + } + buffer.WriteString(indent) + buffer.WriteRune(']') + + return buffer.String() + case []noArgListener[View]: - buffer.WriteString(getNoArgBinding(value)) + return getNoArgBinding(value) case []noArgListener[ImageView]: - buffer.WriteString(getNoArgBinding(value)) + return getNoArgBinding(value) case []noArgListener[MediaPlayer]: - buffer.WriteString(getNoArgBinding(value)) + return getNoArgBinding(value) case []oneArgListener[View, KeyEvent]: - buffer.WriteString(getOneArgBinding(value)) + return getOneArgBinding(value) case []oneArgListener[View, MouseEvent]: - buffer.WriteString(getOneArgBinding(value)) + return getOneArgBinding(value) case []oneArgListener[View, TouchEvent]: - buffer.WriteString(getOneArgBinding(value)) + return getOneArgBinding(value) case []oneArgListener[View, PointerEvent]: - buffer.WriteString(getOneArgBinding(value)) + return getOneArgBinding(value) case []oneArgListener[View, PropertyName]: - buffer.WriteString(getOneArgBinding(value)) + return getOneArgBinding(value) case []oneArgListener[View, string]: - buffer.WriteString(getOneArgBinding(value)) + return getOneArgBinding(value) case []oneArgListener[View, Frame]: - buffer.WriteString(getOneArgBinding(value)) + return getOneArgBinding(value) case []oneArgListener[View, DragAndDropEvent]: - buffer.WriteString(getOneArgBinding(value)) + return getOneArgBinding(value) case []oneArgListener[Checkbox, bool]: - buffer.WriteString(getOneArgBinding(value)) + return getOneArgBinding(value) case []oneArgListener[FilePicker, []FileInfo]: - buffer.WriteString(getOneArgBinding(value)) + return getOneArgBinding(value) case []oneArgListener[ListView, int]: - buffer.WriteString(getOneArgBinding(value)) + return getOneArgBinding(value) case []oneArgListener[ListView, []int]: - buffer.WriteString(getOneArgBinding(value)) + return getOneArgBinding(value) case []oneArgListener[MediaPlayer, float64]: - buffer.WriteString(getOneArgBinding(value)) + return getOneArgBinding(value) case []oneArgListener[TableView, int]: - buffer.WriteString(getOneArgBinding(value)) + return getOneArgBinding(value) case []oneArgListener[TabsLayout, int]: - buffer.WriteString(getOneArgBinding(value)) + return getOneArgBinding(value) case []twoArgListener[ColorPicker, Color]: - buffer.WriteString(getTwoArgBinding(value)) + return getTwoArgBinding(value) case []twoArgListener[DatePicker, time.Time]: - buffer.WriteString(getTwoArgBinding(value)) + return getTwoArgBinding(value) case []twoArgListener[TimePicker, time.Time]: - buffer.WriteString(getTwoArgBinding(value)) + return getTwoArgBinding(value) case []twoArgListener[DropDownList, int]: - buffer.WriteString(getTwoArgBinding(value)) + return getTwoArgBinding(value) case []twoArgListener[EditView, string]: - buffer.WriteString(getTwoArgBinding(value)) + return getTwoArgBinding(value) case []twoArgListener[NumberPicker, float64]: - buffer.WriteString(getTwoArgBinding(value)) + return getTwoArgBinding(value) case []twoArgListener[TableView, int]: - buffer.WriteString(getTwoArgBinding(value)) + return getTwoArgBinding(value) case []twoArgListener[TabsLayout, int]: - buffer.WriteString(getTwoArgBinding(value)) + return getTwoArgBinding(value) case []mediaPlayerErrorListener: - buffer.WriteString(getMediaPlayerErrorListenerBinding(value)) + return getMediaPlayerErrorListenerBinding(value) + + case []popupListener: + return getPopupListenerBinding(value) case map[PropertyName]oneArgListener[View, PropertyName]: - buffer.WriteString("_{") + if len(value) == 0 { + return "" + } + + buffer := allocStringBuilder() + defer freeStringBuilder(buffer) + + indent2 := indent + "\t" + buffer.WriteString("_{\n") for key, listener := range value { if text, ok := listener.rawListener().(string); ok && text != "" { - buffer.WriteRune('\n') - buffer.WriteString(indent) - buffer.WriteRune('\t') - writeString(string(key)) + buffer.WriteString(indent2) + writeString(buffer, string(key)) buffer.WriteString(" = ") - writeString(text) - buffer.WriteRune(',') + writeString(buffer, text) + buffer.WriteString(",\n") } - buffer.WriteRune('\n') buffer.WriteString(indent) buffer.WriteRune('}') } + return buffer.String() + + case []popupButton: + if len(value) == 0 { + return "" + } + + buffer := allocStringBuilder() + defer freeStringBuilder(buffer) + + lead := indent + "\t_{ title = " + + buffer.WriteString("[\n") + for _, button := range value { + buffer.WriteString(lead) + writeString(buffer, button.title) + switch button.buttonType { + case CancelButton: + buffer.WriteString(`, type = cancel`) + + case DefaultButton: + buffer.WriteString(`, type = default`) + } + if button.onClick != nil { + if text, ok := button.onClick.rawListener().(string); ok { + buffer.WriteString(`, click = `) + writeString(buffer, text) + } + } + buffer.WriteString(" },\n") + } + buffer.WriteString(indent) + buffer.WriteRune(']') + return buffer.String() + + default: + return "" } } @@ -1033,11 +1009,12 @@ func writeViewStyle(name string, view Properties, buffer *strings.Builder, inden writeProperty := func(tag PropertyName, value any) { if !slices.Contains(excludeTags, tag) { - if supportedPropertyValue(value) { + text := propertyValueToString(tag, value, indent) + if text != "" { buffer.WriteString(indent) buffer.WriteString(string(tag)) buffer.WriteString(" = ") - writePropertyValue(buffer, tag, value, indent) + buffer.WriteString(text) buffer.WriteString(",\n") } }