From 9478b1ee4fec4dea6ac98de4f7c445cfb9b30f0a Mon Sep 17 00:00:00 2001 From: Alexei Anoshenko Date: Sun, 22 May 2022 12:54:02 +0300 Subject: [PATCH] Bug fixing --- animation.go | 80 ++++++--- audioPlayer.go | 4 + border.go | 44 +++-- bounds.go | 81 +++------ canvasView.go | 4 + checkbox.go | 4 + colorPicker.go | 4 + columnLayout.go | 4 + columnSeparator.go | 22 ++- customView.go | 14 +- datePicker.go | 4 + detailsView.go | 11 ++ dropDownList.go | 4 + editView.go | 4 + filePicker.go | 4 + gridLayout.go | 4 + imageView.go | 4 + listLayout.go | 4 + listView.go | 4 + mediaPlayer.go | 4 + numberPicker.go | 4 + outline.go | 22 ++- progressBar.go | 4 + radius.go | 22 ++- resizable.go | 4 + ruiWriter.go | 45 +++-- shadow.go | 23 ++- stackLayout.go | 4 + tableView.go | 4 + tabsLayout.go | 4 + textView.go | 16 +- timePicker.go | 4 + videoPlayer.go | 4 + view.go | 59 ++----- viewClip.go | 248 +++++++++++++-------------- viewFilter.go | 26 ++- viewStyle.go | 405 +++++++++++++++++++++++++++++++++++++++++++++ viewStyleGet.go | 89 ---------- viewStyleSet.go | 24 +-- viewStyle_test.go | 131 --------------- viewsContainer.go | 4 + 41 files changed, 887 insertions(+), 567 deletions(-) delete mode 100644 viewStyleGet.go delete mode 100644 viewStyle_test.go diff --git a/animation.go b/animation.go index 2158ffb..d11063f 100644 --- a/animation.go +++ b/animation.go @@ -124,7 +124,7 @@ type animationData struct { type Animation interface { Properties fmt.Stringer - ruiStringer + writeTransitionString(tag string, buffer *strings.Builder) animationCSS(session Session) string transitionCSS(buffer *strings.Builder, session Session) hasAnimatedPropery() bool @@ -176,13 +176,21 @@ func (animation *animationData) animationName() string { return animation.keyFramesName } +func (animation *animationData) normalizeTag(tag string) string { + tag = strings.ToLower(tag) + if tag == Direction { + return AnimationDirection + } + return tag +} + func (animation *animationData) Set(tag string, value interface{}) bool { if value == nil { animation.Remove(tag) return true } - switch tag = strings.ToLower(tag); tag { + switch tag = animation.normalizeTag(tag); tag { case ID: if text, ok := value.(string); ok { text = strings.Trim(text, " \t\n\r") @@ -337,7 +345,7 @@ func (animation *animationData) Set(tag string, value interface{}) bool { case IterationCount: return animation.setIntProperty(tag, value) - case AnimationDirection, Direction: + case AnimationDirection: return animation.setEnumProperty(AnimationDirection, value, enumProperties[AnimationDirection].values) default: @@ -348,31 +356,23 @@ func (animation *animationData) Set(tag string, value interface{}) bool { } func (animation *animationData) Remove(tag string) { - tag = strings.ToLower(tag) - if tag == Direction { - tag = AnimationDirection - } - delete(animation.properties, tag) + delete(animation.properties, animation.normalizeTag(tag)) } func (animation *animationData) Get(tag string) interface{} { - tag = strings.ToLower(tag) - if tag == Direction { - tag = AnimationDirection - } - return animation.getRaw(tag) + return animation.getRaw(animation.normalizeTag(tag)) } func (animation *animationData) String() string { - writer := newRUIWriter() - animation.ruiString(writer) - return writer.finish() -} + buffer := allocStringBuilder() + defer freeStringBuilder(buffer) + + buffer.WriteString("animation {") -func (animation *animationData) ruiString(writer ruiWriter) { - writer.startObject("animation") // TODO - writer.endObject() + + buffer.WriteString("}") + return buffer.String() } func (animation *animationData) animationCSS(session Session) string { @@ -448,6 +448,46 @@ func (animation *animationData) transitionCSS(buffer *strings.Builder, session S } } +func (animation *animationData) writeTransitionString(tag string, buffer *strings.Builder) { + buffer.WriteString(tag) + buffer.WriteString("{") + lead := " " + + writeFloatProperty := func(name string) bool { + if value := animation.getRaw(name); value != nil { + buffer.WriteString(lead) + buffer.WriteString(name) + buffer.WriteString(" = ") + writePropertyValue(buffer, name, value, "") + lead = ", " + return true + } + return false + } + + if !writeFloatProperty(Duration) { + buffer.WriteString(" duration = 1") + lead = ", " + } + + writeFloatProperty(Delay) + + if value := animation.getRaw(TimingFunction); value != nil { + if timingFunction, ok := value.(string); ok && timingFunction != "" { + buffer.WriteString(lead) + buffer.WriteString(TimingFunction) + buffer.WriteString(" = ") + if strings.ContainsAny(timingFunction, " ,()") { + buffer.WriteRune('"') + buffer.WriteString(timingFunction) + buffer.WriteRune('"') + } + } + } + + buffer.WriteString(" }") +} + func (animation *animationData) timingFunctionCSS(session Session) string { if timingFunction, ok := stringProperty(animation, TimingFunction, session); ok { if timingFunction, ok = session.resolveConstants(timingFunction); ok && validateTimingFunction(timingFunction) { diff --git a/audioPlayer.go b/audioPlayer.go index cbba431..ab27a64 100644 --- a/audioPlayer.go +++ b/audioPlayer.go @@ -26,6 +26,10 @@ func (player *audioPlayerData) Init(session Session) { player.tag = "AudioPlayer" } +func (player *audioPlayerData) String() string { + return getViewString(player) +} + func (player *audioPlayerData) htmlTag() string { return "audio" } diff --git a/border.go b/border.go index 294da84..18f325d 100644 --- a/border.go +++ b/border.go @@ -48,8 +48,8 @@ const ( // BorderProperty is the interface of a view border data type BorderProperty interface { Properties - ruiStringer fmt.Stringer + stringWriter ViewBorders(session Session) ViewBorders delete(tag string) cssStyle(builder cssBuilder, session Session) @@ -202,12 +202,23 @@ func (border *borderProperty) normalizeTag(tag string) string { return tag } -func (border *borderProperty) ruiString(writer ruiWriter) { - writer.startObject("_") +func (border *borderProperty) writeString(buffer *strings.Builder, indent string) { + buffer.WriteString("_{ ") + comma := false + + write := func(tag string, value interface{}) { + if comma { + buffer.WriteString(", ") + } + buffer.WriteString(tag) + buffer.WriteString(" = ") + writePropertyValue(buffer, BorderStyle, value, indent) + comma = true + } for _, tag := range []string{Style, Width, ColorTag} { if value, ok := border.properties[tag]; ok { - writer.writeProperty(Style, value) + write(tag, value) } } @@ -216,27 +227,32 @@ func (border *borderProperty) ruiString(writer ruiWriter) { width, okWidth := border.properties[side+"-"+Width] color, okColor := border.properties[side+"-"+ColorTag] if okStyle || okWidth || okColor { - writer.startObjectProperty(side, "_") + if comma { + buffer.WriteString(", ") + comma = false + } + + buffer.WriteString(side) + buffer.WriteString(" = _{ ") if okStyle { - writer.writeProperty(Style, style) + write(Style, style) } if okWidth { - writer.writeProperty(Width, width) + write(Width, width) } if okColor { - writer.writeProperty(ColorTag, color) + write(ColorTag, color) } - writer.endObject() + buffer.WriteString(" }") + comma = true } } - // TODO - writer.endObject() + + buffer.WriteString(" }") } func (border *borderProperty) String() string { - writer := newRUIWriter() - border.ruiString(writer) - return writer.finish() + return runStringWriter(border) } func (border *borderProperty) setSingleBorderObject(prefix string, obj DataObject) bool { diff --git a/bounds.go b/bounds.go index f4630f6..102046a 100644 --- a/bounds.go +++ b/bounds.go @@ -8,8 +8,8 @@ import ( // BorderProperty is the interface of a bounds property data type BoundsProperty interface { Properties - ruiStringer fmt.Stringer + stringWriter Bounds(session Session) Bounds } @@ -54,22 +54,25 @@ func (bounds *boundsPropertyData) normalizeTag(tag string) string { return tag } -func (bounds *boundsPropertyData) ruiString(writer ruiWriter) { - writer.startObject("_") - - for _, tag := range []string{Top, Right, Bottom, Left} { - if value, ok := bounds.properties[tag]; ok { - writer.writeProperty(Style, value) - } - } - - writer.endObject() +func (bounds *boundsPropertyData) String() string { + return runStringWriter(bounds) } -func (bounds *boundsPropertyData) String() string { - writer := newRUIWriter() - bounds.ruiString(writer) - return writer.finish() +func (bounds *boundsPropertyData) writeString(buffer *strings.Builder, indent string) { + buffer.WriteString("_{ ") + comma := false + for _, tag := range []string{Top, Right, Bottom, Left} { + if value, ok := bounds.properties[tag]; ok { + if comma { + buffer.WriteString(", ") + } + buffer.WriteString(tag) + buffer.WriteString(" = ") + writePropertyValue(buffer, tag, value, indent) + comma = true + } + } + buffer.WriteString(" }") } func (bounds *boundsPropertyData) Remove(tag string) { @@ -135,50 +138,6 @@ func (bounds *Bounds) SetAll(value SizeUnit) { bounds.Left = value } -func (bounds *Bounds) parse(value string, session Session) bool { - var ok bool - if value, ok = session.resolveConstants(value); !ok { - return false - } - - values := strings.Split(value, ",") - switch len(values) { - case 1: - if bounds.Left, ok = StringToSizeUnit(values[0]); !ok { - return false - } - bounds.Right.Type = bounds.Left.Type - bounds.Right.Value = bounds.Left.Value - bounds.Top.Type = bounds.Left.Type - bounds.Top.Value = bounds.Left.Value - bounds.Bottom.Type = bounds.Left.Type - bounds.Bottom.Value = bounds.Left.Value - return true - - case 5: - if values[4] != "" { - ErrorLog("invalid Bounds value '" + value + "' (needs 1 or 4 elements separeted by comma)") - return false - } - fallthrough - - case 4: - if bounds.Top, ok = StringToSizeUnit(values[0]); ok { - if bounds.Right, ok = StringToSizeUnit(values[1]); ok { - if bounds.Bottom, ok = StringToSizeUnit(values[2]); ok { - if bounds.Left, ok = StringToSizeUnit(values[3]); ok { - return true - } - } - } - } - return false - } - - ErrorLog("invalid Bounds value '" + value + "' (needs 1 or 4 elements separeted by comma)") - return false -} - func (bounds *Bounds) setFromProperties(tag, topTag, rightTag, bottomTag, leftTag string, properties Properties, session Session) { bounds.Top = AutoSize() if size, ok := sizeProperty(properties, tag, session); ok { @@ -202,6 +161,7 @@ func (bounds *Bounds) setFromProperties(tag, topTag, rightTag, bottomTag, leftTa } } +/* func (bounds *Bounds) allFieldsAuto() bool { return bounds.Left.Type == Auto && bounds.Top.Type == Auto && @@ -209,7 +169,6 @@ func (bounds *Bounds) allFieldsAuto() bool { bounds.Bottom.Type == Auto } -/* func (bounds *Bounds) allFieldsZero() bool { return (bounds.Left.Type == Auto || bounds.Left.Value == 0) && (bounds.Top.Type == Auto || bounds.Top.Value == 0) && @@ -231,6 +190,7 @@ func (bounds *Bounds) allFieldsEqual() bool { return false } +/* func (bounds Bounds) writeCSSString(buffer *strings.Builder, textForAuto string) { buffer.WriteString(bounds.Top.cssString(textForAuto)) if !bounds.allFieldsEqual() { @@ -242,6 +202,7 @@ func (bounds Bounds) writeCSSString(buffer *strings.Builder, textForAuto string) buffer.WriteString(bounds.Left.cssString(textForAuto)) } } +*/ // String convert Bounds to string func (bounds *Bounds) String() string { diff --git a/canvasView.go b/canvasView.go index 390f697..c251bbf 100644 --- a/canvasView.go +++ b/canvasView.go @@ -36,6 +36,10 @@ func (canvasView *canvasViewData) Init(session Session) { canvasView.tag = "CanvasView" } +func (canvasView *canvasViewData) String() string { + return getViewString(canvasView) +} + func (canvasView *canvasViewData) normalizeTag(tag string) string { tag = strings.ToLower(tag) switch tag { diff --git a/checkbox.go b/checkbox.go index e337383..9caebf1 100644 --- a/checkbox.go +++ b/checkbox.go @@ -43,6 +43,10 @@ func (button *checkboxData) Init(session Session) { button.checkedListeners = []func(Checkbox, bool){} } +func (button *checkboxData) String() string { + return getViewString(button) +} + func (button *checkboxData) Focusable() bool { return true } diff --git a/colorPicker.go b/colorPicker.go index 7c2ee2b..52febfa 100644 --- a/colorPicker.go +++ b/colorPicker.go @@ -39,6 +39,10 @@ func (picker *colorPickerData) Init(session Session) { picker.properties[Padding] = Px(0) } +func (picker *colorPickerData) String() string { + return getViewString(picker) +} + func (picker *colorPickerData) normalizeTag(tag string) string { tag = strings.ToLower(tag) switch tag { diff --git a/columnLayout.go b/columnLayout.go index f90e6b7..3d1b405 100644 --- a/columnLayout.go +++ b/columnLayout.go @@ -63,6 +63,10 @@ func (ColumnLayout *columnLayoutData) Init(session Session) { //ColumnLayout.systemClass = "ruiColumnLayout" } +func (columnLayout *columnLayoutData) String() string { + return getViewString(columnLayout) +} + func (columnLayout *columnLayoutData) normalizeTag(tag string) string { tag = strings.ToLower(tag) switch tag { diff --git a/columnSeparator.go b/columnSeparator.go index 55f2b76..b3d91fa 100644 --- a/columnSeparator.go +++ b/columnSeparator.go @@ -8,8 +8,8 @@ import ( // ColumnSeparatorProperty is the interface of a view separator data type ColumnSeparatorProperty interface { Properties - ruiStringer fmt.Stringer + stringWriter ViewBorder(session Session) ViewBorder cssValue(session Session) string } @@ -84,20 +84,26 @@ func (separator *columnSeparatorProperty) normalizeTag(tag string) string { return tag } -func (separator *columnSeparatorProperty) ruiString(writer ruiWriter) { - writer.startObject("_") +func (separator *columnSeparatorProperty) writeString(buffer *strings.Builder, indent string) { + buffer.WriteString("_{ ") + comma := false for _, tag := range []string{Style, Width, ColorTag} { if value, ok := separator.properties[tag]; ok { - writer.writeProperty(Style, value) + if comma { + buffer.WriteString(", ") + } + buffer.WriteString(tag) + buffer.WriteString(" = ") + writePropertyValue(buffer, BorderStyle, value, indent) + comma = true } } - writer.endObject() + + buffer.WriteString(" }") } func (separator *columnSeparatorProperty) String() string { - writer := newRUIWriter() - separator.ruiString(writer) - return writer.finish() + return runStringWriter(separator) } func (separator *columnSeparatorProperty) Remove(tag string) { diff --git a/customView.go b/customView.go index 3c31d45..66b864c 100644 --- a/customView.go +++ b/customView.go @@ -91,6 +91,10 @@ func (customView *CustomViewData) Clear() { func (customView *CustomViewData) Init(session Session) { } +func (customView *CustomViewData) cssViewStyle(buffer cssBuilder, session Session) { + customView.superView.cssViewStyle(buffer, session) +} + // Session returns a current Session interface func (customView *CustomViewData) Session() Session { return customView.superView.Session() @@ -249,19 +253,11 @@ func (customView *CustomViewData) RemoveView(index int) View { func (customView *CustomViewData) String() string { if customView.superView != nil { - writer := newRUIWriter() - customView.ruiString(writer) - return writer.finish() + return getViewString(customView) } return customView.tag + " { }" } -func (customView *CustomViewData) ruiString(writer ruiWriter) { - if customView.superView != nil { - ruiViewString(customView.superView, customView.tag, writer) - } -} - func (customView *CustomViewData) setScroll(x, y, width, height float64) { if customView.superView != nil { customView.superView.setScroll(x, y, width, height) diff --git a/datePicker.go b/datePicker.go index 2d0af12..a2f6f67 100644 --- a/datePicker.go +++ b/datePicker.go @@ -44,6 +44,10 @@ func (picker *datePickerData) Init(session Session) { picker.dateChangedListeners = []func(DatePicker, time.Time){} } +func (picker *datePickerData) String() string { + return getViewString(picker) +} + func (picker *datePickerData) Focusable() bool { return true } diff --git a/detailsView.go b/detailsView.go index 2069ad4..5748b12 100644 --- a/detailsView.go +++ b/detailsView.go @@ -117,6 +117,14 @@ func (detailsView *detailsViewData) set(tag string, value interface{}) bool { } } + case NotTranslate: + if !detailsView.viewData.set(tag, value) { + return false + } + if detailsView.created { + updateInnerHTML(detailsView.htmlID(), detailsView.Session()) + } + default: return detailsView.viewsContainerData.Set(tag, value) } @@ -149,6 +157,9 @@ func (detailsView *detailsViewData) htmlSubviews(self View, buffer *strings.Buil if value, ok := detailsView.properties[Summary]; ok { switch value := value.(type) { case string: + if !GetNotTranslate(detailsView, "") { + value, _ = detailsView.session.GetString(value) + } buffer.WriteString("") buffer.WriteString(value) buffer.WriteString("") diff --git a/dropDownList.go b/dropDownList.go index d1505c9..2a35879 100644 --- a/dropDownList.go +++ b/dropDownList.go @@ -41,6 +41,10 @@ func (list *dropDownListData) Init(session Session) { list.dropDownListener = []func(DropDownList, int){} } +func (list *dropDownListData) String() string { + return getViewString(list) +} + func (list *dropDownListData) Focusable() bool { return true } diff --git a/editView.go b/editView.go index 2cad6e5..b1f29b8 100644 --- a/editView.go +++ b/editView.go @@ -63,6 +63,10 @@ func (edit *editViewData) Init(session Session) { edit.tag = "EditView" } +func (edit *editViewData) String() string { + return getViewString(edit) +} + func (edit *editViewData) Focusable() bool { return true } diff --git a/filePicker.go b/filePicker.go index 0086a42..ad49141 100644 --- a/filePicker.go +++ b/filePicker.go @@ -89,6 +89,10 @@ func (picker *filePickerData) Init(session Session) { picker.fileSelectedListeners = []func(FilePicker, []FileInfo){} } +func (picker *filePickerData) String() string { + return getViewString(picker) +} + func (picker *filePickerData) Focusable() bool { return true } diff --git a/gridLayout.go b/gridLayout.go index 9dbfdbf..fd40881 100644 --- a/gridLayout.go +++ b/gridLayout.go @@ -33,6 +33,10 @@ func (gridLayout *gridLayoutData) Init(session Session) { gridLayout.systemClass = "ruiGridLayout" } +func (gridLayout *gridLayoutData) String() string { + return getViewString(gridLayout) +} + func (style *viewStyle) setGridCellSize(tag string, value interface{}) bool { setValues := func(values []string) bool { count := len(values) diff --git a/imageView.go b/imageView.go index e8004ee..e078c98 100644 --- a/imageView.go +++ b/imageView.go @@ -55,6 +55,10 @@ func (imageView *imageViewData) Init(session Session) { } +func (imageView *imageViewData) String() string { + return getViewString(imageView) +} + func (imageView *imageViewData) normalizeTag(tag string) string { tag = strings.ToLower(tag) switch tag { diff --git a/listLayout.go b/listLayout.go index 31ce6d6..d4b27b0 100644 --- a/listLayout.go +++ b/listLayout.go @@ -49,6 +49,10 @@ func (listLayout *listLayoutData) Init(session Session) { listLayout.systemClass = "ruiListLayout" } +func (listLayout *listLayoutData) String() string { + return getViewString(listLayout) +} + func (listLayout *listLayoutData) Remove(tag string) { listLayout.remove(strings.ToLower(tag)) } diff --git a/listView.go b/listView.go index f8f620c..f44f170 100644 --- a/listView.go +++ b/listView.go @@ -91,6 +91,10 @@ func (listView *listViewData) Init(session Session) { listView.checkedListeners = []func(ListView, []int){} } +func (listView *listViewData) String() string { + return getViewString(listView) +} + func (listView *listViewData) Views() []View { return listView.items } diff --git a/mediaPlayer.go b/mediaPlayer.go index 7493f93..357aa79 100644 --- a/mediaPlayer.go +++ b/mediaPlayer.go @@ -168,6 +168,10 @@ func (player *mediaPlayerData) Init(session Session) { player.tag = "MediaPlayer" } +func (player *mediaPlayerData) String() string { + return getViewString(player) +} + func (player *mediaPlayerData) Focusable() bool { return true } diff --git a/numberPicker.go b/numberPicker.go index 0b44789..db1aeaf 100644 --- a/numberPicker.go +++ b/numberPicker.go @@ -51,6 +51,10 @@ func (picker *numberPickerData) Init(session Session) { picker.numberChangedListeners = []func(NumberPicker, float64){} } +func (picker *numberPickerData) String() string { + return getViewString(picker) +} + func (picker *numberPickerData) Focusable() bool { return true } diff --git a/outline.go b/outline.go index 14dc969..939abc7 100644 --- a/outline.go +++ b/outline.go @@ -7,7 +7,7 @@ import ( type OutlineProperty interface { Properties - ruiStringer + stringWriter fmt.Stringer ViewOutline(session Session) ViewOutline } @@ -25,22 +25,26 @@ func NewOutlineProperty(params Params) OutlineProperty { return outline } -func (outline *outlinePropertyData) ruiString(writer ruiWriter) { - writer.startObject("_") - +func (outline *outlinePropertyData) writeString(buffer *strings.Builder, indent string) { + buffer.WriteString("_{ ") + comma := false for _, tag := range []string{Style, Width, ColorTag} { if value, ok := outline.properties[tag]; ok { - writer.writeProperty(Style, value) + if comma { + buffer.WriteString(", ") + } + buffer.WriteString(tag) + buffer.WriteString(" = ") + writePropertyValue(buffer, BorderStyle, value, indent) + comma = true } } - writer.endObject() + buffer.WriteString(" }") } func (outline *outlinePropertyData) String() string { - writer := newRUIWriter() - outline.ruiString(writer) - return writer.finish() + return runStringWriter(outline) } func (outline *outlinePropertyData) normalizeTag(tag string) string { diff --git a/progressBar.go b/progressBar.go index 386af5c..9ee5347 100644 --- a/progressBar.go +++ b/progressBar.go @@ -36,6 +36,10 @@ func (progress *progressBarData) Init(session Session) { progress.tag = "ProgressBar" } +func (progress *progressBarData) String() string { + return getViewString(progress) +} + func (progress *progressBarData) normalizeTag(tag string) string { tag = strings.ToLower(tag) switch tag { diff --git a/radius.go b/radius.go index 7f4b298..17674e8 100644 --- a/radius.go +++ b/radius.go @@ -97,7 +97,7 @@ const ( type RadiusProperty interface { Properties - ruiStringer + stringWriter fmt.Stringer BoxRadius(session Session) BoxRadius } @@ -125,23 +125,27 @@ func (radius *radiusPropertyData) normalizeTag(tag string) string { return strings.TrimPrefix(strings.ToLower(tag), "radius-") } -func (radius *radiusPropertyData) ruiString(writer ruiWriter) { - writer.startObject("_") - +func (radius *radiusPropertyData) writeString(buffer *strings.Builder, indent string) { + buffer.WriteString("_{ ") + comma := false for _, tag := range []string{X, Y, TopLeft, TopLeftX, TopLeftY, TopRight, TopRightX, TopRightY, BottomLeft, BottomLeftX, BottomLeftY, BottomRight, BottomRightX, BottomRightY} { if value, ok := radius.properties[tag]; ok { - writer.writeProperty(Style, value) + if comma { + buffer.WriteString(", ") + } + buffer.WriteString(tag) + buffer.WriteString(" = ") + writePropertyValue(buffer, tag, value, indent) + comma = true } } - writer.endObject() + buffer.WriteString(" }") } func (radius *radiusPropertyData) String() string { - writer := newRUIWriter() - radius.ruiString(writer) - return writer.finish() + return runStringWriter(radius) } func (radius *radiusPropertyData) delete(tags []string) { diff --git a/resizable.go b/resizable.go index 816a206..d4cb8df 100644 --- a/resizable.go +++ b/resizable.go @@ -61,6 +61,10 @@ func (resizable *resizableData) Init(session Session) { resizable.content = []View{} } +func (resizable *resizableData) String() string { + return getViewString(resizable) +} + func (resizable *resizableData) Views() []View { return resizable.content } diff --git a/ruiWriter.go b/ruiWriter.go index e6862fb..bc5471e 100644 --- a/ruiWriter.go +++ b/ruiWriter.go @@ -1,5 +1,6 @@ package rui +/* import ( "fmt" "strconv" @@ -10,6 +11,8 @@ type ruiWriter interface { startObject(tag string) startObjectProperty(tag, objectTag string) endObject() + startArrayProperty(tag string) + endObArray() writeProperty(tag string, value interface{}) finish() string } @@ -41,23 +44,22 @@ func (writer *ruiWriterData) writeIndent() { } func (writer *ruiWriterData) writeString(str string) { - esc := map[string]string{"\t": `\t`, "\r": `\r`, "\n": `\n`, "\"": `"`} - hasEsc := false - for s := range esc { - if strings.Contains(str, s) { - hasEsc = true - break - } - } - if hasEsc || strings.Contains(str, " ") || strings.Contains(str, ",") { + hasEsc := strings.ContainsAny(str, "\t\"\r\n") + if hasEsc || strings.ContainsAny(str, " ,;'`[]{}()") { if !strings.Contains(str, "`") && (hasEsc || strings.Contains(str, `\`)) { writer.buffer.WriteRune('`') writer.buffer.WriteString(str) writer.buffer.WriteRune('`') } else { - str = strings.Replace(str, `\`, `\\`, -1) - for oldStr, newStr := range esc { - str = strings.Replace(str, oldStr, newStr, -1) + 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 { + str = strings.Replace(str, s.old, s.new, -1) } writer.buffer.WriteRune('"') writer.buffer.WriteString(str) @@ -80,6 +82,9 @@ func (writer *ruiWriterData) startObjectProperty(tag, objectTag string) { writer.indent += "\t" writer.writeString(tag) writer.writeString(" = ") + if objectTag == "" { + objectTag = "_" + } writer.writeString(objectTag) writer.buffer.WriteString(" {\n") } @@ -92,6 +97,21 @@ func (writer *ruiWriterData) endObject() { writer.buffer.WriteRune('}') } +func (writer *ruiWriterData) startArrayProperty(tag string) { + writer.writeIndent() + writer.writeString(tag) + writer.buffer.WriteString(" = [\n") + writer.indent += "\t" +} + +func (writer *ruiWriterData) endObArray() { + if len(writer.indent) > 0 { + writer.indent = writer.indent[1:] + } + writer.writeIndent() + writer.buffer.WriteString("],\n") +} + func (writer *ruiWriterData) writeValue(value interface{}) { switch value := value.(type) { @@ -201,3 +221,4 @@ func (writer *ruiWriterData) finish() string { } return result } +*/ diff --git a/shadow.go b/shadow.go index a503509..216f460 100644 --- a/shadow.go +++ b/shadow.go @@ -31,7 +31,7 @@ const ( type ViewShadow interface { Properties fmt.Stringer - ruiStringer + stringWriter cssStyle(buffer *strings.Builder, session Session, lead string) bool cssTextStyle(buffer *strings.Builder, session Session, lead string) bool visible(session Session) bool @@ -205,19 +205,24 @@ func (shadow *viewShadowData) visible(session Session) bool { } func (shadow *viewShadowData) String() string { - writer := newRUIWriter() - shadow.ruiString(writer) - return writer.finish() + return runStringWriter(shadow) } -func (shadow *viewShadowData) ruiString(writer ruiWriter) { - writer.startObject("_") +func (shadow *viewShadowData) writeString(buffer *strings.Builder, indent string) { + buffer.WriteString("_{ ") + comma := false for _, tag := range shadow.AllTags() { - if value := shadow.Get(tag); value != nil { - writer.writeProperty(tag, value) + if value, ok := shadow.properties[tag]; ok { + if comma { + buffer.WriteString(", ") + } + buffer.WriteString(tag) + buffer.WriteString(" = ") + writePropertyValue(buffer, tag, value, indent) + comma = true } } - writer.endObject() + buffer.WriteString(" }") } func (properties *propertyList) setShadow(tag string, value interface{}) bool { diff --git a/stackLayout.go b/stackLayout.go index 81e5ed6..4ecb343 100644 --- a/stackLayout.go +++ b/stackLayout.go @@ -58,6 +58,10 @@ func (layout *stackLayoutData) Init(session Session) { layout.properties[TransitionEndEvent] = []func(View, string){layout.pushFinished, layout.popFinished} } +func (layout *stackLayoutData) String() string { + return getViewString(layout) +} + func (layout *stackLayoutData) pushFinished(view View, tag string) { if tag == "ruiPush" { if layout.pushView != nil { diff --git a/tableView.go b/tableView.go index 929c496..8ec793e 100644 --- a/tableView.go +++ b/tableView.go @@ -268,6 +268,10 @@ func (table *tableViewData) Init(session Session) { table.current.Column = -1 } +func (table *tableViewData) String() string { + return getViewString(table) +} + func (table *tableViewData) normalizeTag(tag string) string { switch tag = strings.ToLower(tag); tag { case "top-cell-padding": diff --git a/tabsLayout.go b/tabsLayout.go index 6a105a6..9ebfe48 100644 --- a/tabsLayout.go +++ b/tabsLayout.go @@ -99,6 +99,10 @@ func (tabsLayout *tabsLayoutData) Init(session Session) { tabsLayout.tabCloseListener = []func(TabsLayout, int){} } +func (tabsLayout *tabsLayoutData) String() string { + return getViewString(tabsLayout) +} + func (tabsLayout *tabsLayoutData) currentItem() int { result, _ := intProperty(tabsLayout, Current, tabsLayout.session, 0) return result diff --git a/textView.go b/textView.go index 41ed5c9..d885d2a 100644 --- a/textView.go +++ b/textView.go @@ -32,6 +32,10 @@ func (textView *textViewData) Init(session Session) { textView.tag = "TextView" } +func (textView *textViewData) String() string { + return getViewString(textView) +} + func (textView *textViewData) Get(tag string) interface{} { return textView.get(strings.ToLower(tag)) } @@ -103,6 +107,14 @@ func (textView *textViewData) set(tag string, value interface{}) bool { textView.textOverflowUpdated() } + case NotTranslate: + if !textView.viewData.set(tag, value) { + return false + } + if textView.created { + updateInnerHTML(textView.htmlID(), textView.Session()) + } + default: return textView.viewData.set(tag, value) } @@ -143,7 +155,9 @@ func textToJS(text string) string { func (textView *textViewData) htmlSubviews(self View, buffer *strings.Builder) { if value := textView.getRaw(Text); value != nil { if text, ok := value.(string); ok { - text, _ = textView.session.GetString(text) + if !GetNotTranslate(textView, "") { + text, _ = textView.session.GetString(text) + } buffer.WriteString(textToJS(text)) } } diff --git a/timePicker.go b/timePicker.go index 518c636..5e412d7 100644 --- a/timePicker.go +++ b/timePicker.go @@ -44,6 +44,10 @@ func (picker *timePickerData) Init(session Session) { picker.timeChangedListeners = []func(TimePicker, time.Time){} } +func (picker *timePickerData) String() string { + return getViewString(picker) +} + func (picker *timePickerData) Focusable() bool { return true } diff --git a/videoPlayer.go b/videoPlayer.go index 0f50f5c..9fa129b 100644 --- a/videoPlayer.go +++ b/videoPlayer.go @@ -45,6 +45,10 @@ func (player *videoPlayerData) Init(session Session) { player.tag = "VideoPlayer" } +func (player *videoPlayerData) String() string { + return getViewString(player) +} + func (player *videoPlayerData) htmlTag() string { return "video" } diff --git a/view.go b/view.go index 3ab207f..7459dbe 100644 --- a/view.go +++ b/view.go @@ -30,9 +30,8 @@ func (frame Frame) Bottom() float64 { // View - base view interface type View interface { - Properties + ViewStyle fmt.Stringer - ruiStringer // Init initializes fields of View by default values Init(session Session) @@ -616,6 +615,13 @@ func (view *viewData) Get(tag string) interface{} { } func (view *viewData) get(tag string) interface{} { + if tag == ID { + if view.viewID != "" { + return view.viewID + } else { + return nil + } + } return view.viewStyle.get(tag) } @@ -834,51 +840,6 @@ func (view *viewData) handleCommand(self View, command string, data DataObject) } -func ruiViewString(view View, viewTag string, writer ruiWriter) { - writer.startObject(viewTag) - - tags := view.AllTags() - count := len(tags) - if count > 0 { - if count > 1 { - tagToStart := func(tag string) { - for i, t := range tags { - if t == tag { - if i > 0 { - for n := i; n > 0; n-- { - tags[n] = tags[n-1] - } - tags[0] = tag - } - return - } - } - } - tagToStart(StyleDisabled) - tagToStart(Style) - tagToStart(ID) - } - - for _, tag := range tags { - if value := view.Get(tag); value != nil { - writer.writeProperty(tag, value) - } - } - } - - writer.endObject() -} - -func (view *viewData) ruiString(writer ruiWriter) { - ruiViewString(view, view.Tag(), writer) -} - -func (view *viewData) String() string { - writer := newRUIWriter() - view.ruiString(writer) - return writer.finish() -} - func (view *viewData) SetChangeListener(tag string, listener func(View, string)) { if listener == nil { delete(view.changeListener, tag) @@ -890,3 +851,7 @@ func (view *viewData) SetChangeListener(tag string, listener func(View, string)) func (view *viewData) HasFocus() bool { return view.hasFocus } + +func (view *viewData) String() string { + return getViewString(view) +} diff --git a/viewClip.go b/viewClip.go index b4dacb5..0fb37ce 100644 --- a/viewClip.go +++ b/viewClip.go @@ -9,7 +9,7 @@ import ( type ClipShape interface { Properties fmt.Stringer - ruiStringer + stringWriter cssStyle(session Session) string valid(session Session) bool } @@ -22,6 +22,10 @@ type ellipseClip struct { propertyList } +type circleClip struct { + propertyList +} + type polygonClip struct { points []interface{} } @@ -47,7 +51,7 @@ func InsetClip(top, right, bottom, left SizeUnit, radius RadiusProperty) ClipSha // CircleClip creates a circle View clipping area. func CircleClip(x, y, radius SizeUnit) ClipShape { - clip := new(ellipseClip) + clip := new(circleClip) clip.init() clip.Set(X, x) clip.Set(Y, y) @@ -112,39 +116,25 @@ func (clip *insetClip) Set(tag string, value interface{}) bool { } func (clip *insetClip) String() string { - writer := newRUIWriter() - clip.ruiString(writer) - return writer.finish() + return runStringWriter(clip) } -func (clip *insetClip) ruiString(writer ruiWriter) { - writer.startObject("inset") - for _, tag := range []string{Top, Right, Bottom, Left} { +func (clip *insetClip) writeString(buffer *strings.Builder, indent string) { + buffer.WriteString("inset { ") + comma := false + for _, tag := range []string{Top, Right, Bottom, Left, Radius} { if value, ok := clip.properties[tag]; ok { - switch value := value.(type) { - case string: - writer.writeProperty(tag, value) - - case fmt.Stringer: - writer.writeProperty(tag, value.String()) + if comma { + buffer.WriteString(", ") } + buffer.WriteString(tag) + buffer.WriteString(" = ") + writePropertyValue(buffer, tag, value, indent) + comma = true } } - if value := clip.Get(Radius); value != nil { - switch value := value.(type) { - case RadiusProperty: - writer.writeProperty(Radius, value.String()) - - case SizeUnit: - writer.writeProperty(Radius, value.String()) - - case string: - writer.writeProperty(Radius, value) - } - } - - writer.endObject() + buffer.WriteString(" }") } func (clip *insetClip) cssStyle(session Session) string { @@ -178,83 +168,108 @@ func (clip *insetClip) valid(session Session) bool { return false } +func (clip *circleClip) Set(tag string, value interface{}) bool { + if value == nil { + clip.Remove(tag) + } + + switch strings.ToLower(tag) { + case X, Y, Radius: + return clip.setSizeProperty(tag, value) + } + + ErrorLogF(`"%s" property is not supported by the circle clip shape`, tag) + return false +} + +func (clip *circleClip) String() string { + return runStringWriter(clip) +} + +func (clip *circleClip) writeString(buffer *strings.Builder, indent string) { + buffer.WriteString("circle { ") + comma := false + for _, tag := range []string{Radius, X, Y} { + if value, ok := clip.properties[tag]; ok { + if comma { + buffer.WriteString(", ") + } + buffer.WriteString(tag) + buffer.WriteString(" = ") + writePropertyValue(buffer, tag, value, indent) + comma = true + } + } + + buffer.WriteString(" }") +} + +func (clip *circleClip) cssStyle(session Session) string { + + buffer := allocStringBuilder() + defer freeStringBuilder(buffer) + + buffer.WriteString("circle(") + r, _ := sizeProperty(clip, Radius, session) + buffer.WriteString(r.cssString("50%")) + + buffer.WriteString(" at ") + x, _ := sizeProperty(clip, X, session) + buffer.WriteString(x.cssString("50%")) + buffer.WriteRune(' ') + + y, _ := sizeProperty(clip, Y, session) + buffer.WriteString(y.cssString("50%")) + buffer.WriteRune(')') + + return buffer.String() +} + +func (clip *circleClip) valid(session Session) bool { + if value, ok := sizeProperty(clip, Radius, session); ok && value.Value == 0 { + return false + } + return true +} + func (clip *ellipseClip) Set(tag string, value interface{}) bool { if value == nil { clip.Remove(tag) } switch strings.ToLower(tag) { - case X, Y: + case X, Y, RadiusX, RadiusY: return clip.setSizeProperty(tag, value) case Radius: - result := clip.setSizeProperty(tag, value) - if result { - delete(clip.properties, RadiusX) - delete(clip.properties, RadiusY) - } - return result - - case RadiusX: - result := clip.setSizeProperty(tag, value) - if result { - if r, ok := clip.properties[Radius]; ok { - clip.properties[RadiusY] = r - delete(clip.properties, Radius) - } - } - return result - - case RadiusY: - result := clip.setSizeProperty(tag, value) - if result { - if r, ok := clip.properties[Radius]; ok { - clip.properties[RadiusX] = r - delete(clip.properties, Radius) - } - } - return result + return clip.setSizeProperty(RadiusX, value) && + clip.setSizeProperty(RadiusY, value) } - ErrorLogF(`"%s" property is not supported by the inset clip shape`, tag) + ErrorLogF(`"%s" property is not supported by the ellipse clip shape`, tag) return false } func (clip *ellipseClip) String() string { - writer := newRUIWriter() - clip.ruiString(writer) - return writer.finish() + return runStringWriter(clip) } -func (clip *ellipseClip) ruiString(writer ruiWriter) { - writeProperty := func(tag string, value interface{}) { - switch value := value.(type) { - case string: - writer.writeProperty(tag, value) - - case fmt.Stringer: - writer.writeProperty(tag, value.String()) - } - } - - if r, ok := clip.properties[Radius]; ok { - writer.startObject("circle") - writeProperty(Radius, r) - } else { - writer.startObject("ellipse") - for _, tag := range []string{RadiusX, RadiusY} { - if value, ok := clip.properties[tag]; ok { - writeProperty(tag, value) - } - } - } - - for _, tag := range []string{X, Y} { +func (clip *ellipseClip) writeString(buffer *strings.Builder, indent string) { + buffer.WriteString("ellipse { ") + comma := false + for _, tag := range []string{RadiusX, RadiusY, X, Y} { if value, ok := clip.properties[tag]; ok { - writeProperty(tag, value) + if comma { + buffer.WriteString(", ") + } + buffer.WriteString(tag) + buffer.WriteString(" = ") + writePropertyValue(buffer, tag, value, indent) + comma = true } } - writer.endObject() + + buffer.WriteString(" }") } func (clip *ellipseClip) cssStyle(session Session) string { @@ -262,38 +277,29 @@ func (clip *ellipseClip) cssStyle(session Session) string { buffer := allocStringBuilder() defer freeStringBuilder(buffer) - if r, ok := sizeProperty(clip, Radius, session); ok { - buffer.WriteString("circle(") - buffer.WriteString(r.cssString("0")) - } else { - rx, _ := sizeProperty(clip, RadiusX, session) - ry, _ := sizeProperty(clip, RadiusX, session) - buffer.WriteString("ellipse(") - buffer.WriteString(rx.cssString("0")) - buffer.WriteRune(' ') - buffer.WriteString(ry.cssString("0")) - } + rx, _ := sizeProperty(clip, RadiusX, session) + ry, _ := sizeProperty(clip, RadiusX, session) + buffer.WriteString("ellipse(") + buffer.WriteString(rx.cssString("50%")) + buffer.WriteRune(' ') + buffer.WriteString(ry.cssString("50%")) buffer.WriteString(" at ") x, _ := sizeProperty(clip, X, session) - buffer.WriteString(x.cssString("0")) + buffer.WriteString(x.cssString("50%")) buffer.WriteRune(' ') y, _ := sizeProperty(clip, Y, session) - buffer.WriteString(y.cssString("0")) + buffer.WriteString(y.cssString("50%")) buffer.WriteRune(')') return buffer.String() } func (clip *ellipseClip) valid(session Session) bool { - if value, ok := sizeProperty(clip, Radius, session); ok && value.Type != Auto && value.Value != 0 { - return true - } - - rx, okX := sizeProperty(clip, RadiusX, session) - ry, okY := sizeProperty(clip, RadiusY, session) - return okX && okY && rx.Type != Auto && rx.Value != 0 && ry.Type != Auto && ry.Value != 0 + rx, _ := sizeProperty(clip, RadiusX, session) + ry, _ := sizeProperty(clip, RadiusY, session) + return rx.Value != 0 && ry.Value != 0 } func (clip *polygonClip) Get(tag string) interface{} { @@ -383,47 +389,31 @@ func (clip *polygonClip) AllTags() []string { } func (clip *polygonClip) String() string { - writer := newRUIWriter() - clip.ruiString(writer) - return writer.finish() + return runStringWriter(clip) } -func (clip *polygonClip) ruiString(writer ruiWriter) { +func (clip *polygonClip) writeString(buffer *strings.Builder, indent string) { - buffer := allocStringBuilder() - defer freeStringBuilder(buffer) - - writer.startObject("polygon") + buffer.WriteString("inset { ") if clip.points != nil { + buffer.WriteString(Points) + buffer.WriteString(` = "`) for i, value := range clip.points { if i > 0 { buffer.WriteString(", ") } - switch value := value.(type) { - case string: - buffer.WriteString(value) - - case fmt.Stringer: - buffer.WriteString(value.String()) - - default: - buffer.WriteString("0px") - } + writePropertyValue(buffer, "", value, indent) } - writer.writeProperty(Points, buffer.String()) + buffer.WriteString(`" `) } - writer.endObject() + buffer.WriteRune('}') } func (clip *polygonClip) cssStyle(session Session) string { - if clip.points == nil { - return "" - } - count := len(clip.points) if count < 2 { return "" diff --git a/viewFilter.go b/viewFilter.go index ec5ea4b..3f0c7e6 100644 --- a/viewFilter.go +++ b/viewFilter.go @@ -77,7 +77,7 @@ const ( type ViewFilter interface { Properties fmt.Stringer - ruiStringer + stringWriter cssStyle(session Session) string } @@ -155,17 +155,25 @@ func (filter *viewFilter) Set(tag string, value interface{}) bool { } func (filter *viewFilter) String() string { - writer := newRUIWriter() - filter.ruiString(writer) - return writer.finish() + return runStringWriter(filter) } -func (filter *viewFilter) ruiString(writer ruiWriter) { - writer.startObject("filter") - for tag, value := range filter.properties { - writer.writeProperty(tag, value) +func (filter *viewFilter) 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(tag) + buffer.WriteString(" = ") + writePropertyValue(buffer, tag, value, indent) + comma = true + } } - writer.endObject() + buffer.WriteString(" }") } func (filter *viewFilter) cssStyle(session Session) string { diff --git a/viewStyle.go b/viewStyle.go index 46c7e55..9f15e15 100644 --- a/viewStyle.go +++ b/viewStyle.go @@ -2,6 +2,7 @@ package rui import ( "fmt" + "sort" "strconv" "strings" ) @@ -22,6 +23,10 @@ type Range struct { First, Last int } +type stringWriter interface { + writeString(buffer *strings.Builder, indent string) +} + // String returns a string representation of the Range struct func (r Range) String() string { if r.First == r.Last { @@ -416,3 +421,403 @@ func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) { } } } + +func valueToOrientation(value interface{}, session Session) (int, bool) { + if value != nil { + switch value := value.(type) { + case int: + return value, true + + case string: + text, ok := session.resolveConstants(value) + if !ok { + return 0, false + } + + text = strings.ToLower(strings.Trim(text, " \t\n\r")) + switch text { + case "vertical": + return TopDownOrientation, true + + case "horizontal": + return StartToEndOrientation, true + } + + if result, ok := enumStringToInt(text, enumProperties[Orientation].values, true); ok { + return result, true + } + } + } + return 0, false +} + +func (style *viewStyle) Get(tag string) interface{} { + return style.get(strings.ToLower(tag)) +} + +func (style *viewStyle) get(tag string) interface{} { + switch tag { + case Border, CellBorder: + return getBorder(&style.propertyList, tag) + + case BorderLeft, BorderRight, BorderTop, BorderBottom, + BorderStyle, BorderLeftStyle, BorderRightStyle, BorderTopStyle, BorderBottomStyle, + BorderColor, BorderLeftColor, BorderRightColor, BorderTopColor, BorderBottomColor, + BorderWidth, BorderLeftWidth, BorderRightWidth, BorderTopWidth, BorderBottomWidth: + if border := getBorder(style, Border); border != nil { + return border.Get(tag) + } + return nil + + case CellBorderLeft, CellBorderRight, CellBorderTop, CellBorderBottom, + CellBorderStyle, CellBorderLeftStyle, CellBorderRightStyle, CellBorderTopStyle, CellBorderBottomStyle, + CellBorderColor, CellBorderLeftColor, CellBorderRightColor, CellBorderTopColor, CellBorderBottomColor, + CellBorderWidth, CellBorderLeftWidth, CellBorderRightWidth, CellBorderTopWidth, CellBorderBottomWidth: + if border := getBorder(style, CellBorder); border != nil { + return border.Get(tag) + } + return nil + + case RadiusX, RadiusY, RadiusTopLeft, RadiusTopLeftX, RadiusTopLeftY, + RadiusTopRight, RadiusTopRightX, RadiusTopRightY, + RadiusBottomLeft, RadiusBottomLeftX, RadiusBottomLeftY, + RadiusBottomRight, RadiusBottomRightX, RadiusBottomRightY: + return getRadiusElement(style, tag) + + case ColumnSeparator: + if val, ok := style.properties[ColumnSeparator]; ok { + return val.(ColumnSeparatorProperty) + } + return nil + + case ColumnSeparatorStyle, ColumnSeparatorWidth, ColumnSeparatorColor: + if val, ok := style.properties[ColumnSeparator]; ok { + separator := val.(ColumnSeparatorProperty) + return separator.Get(tag) + } + return nil + + case Transition: + if len(style.transitions) == 0 { + return nil + } + result := map[string]Animation{} + for tag, animation := range style.transitions { + result[tag] = animation + } + return result + } + + return style.propertyList.getRaw(tag) +} + +func (style *viewStyle) AllTags() []string { + result := style.propertyList.AllTags() + if len(style.transitions) > 0 { + result = append(result, Transition) + } + return result +} + +func supportedPropertyValue(value interface{}) bool { + switch value.(type) { + case string: + case []string: + case bool: + case float32: + case float64: + case int: + case stringWriter: + case fmt.Stringer: + case []ViewShadow: + case []View: + case []interface{}: + case map[string]Animation: + default: + return false + } + return true +} + +func writePropertyValue(buffer *strings.Builder, tag string, value interface{}, indent string) { + + 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 == ':' { + } else { + simple = false + break + } + } + } + } + + 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) + } + buffer.WriteRune('"') + buffer.WriteString(text) + buffer.WriteRune('"') + } else { + buffer.WriteString(text) + } + } + + switch value := value.(type) { + case string: + writeString(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(']') + } + + case bool: + if value { + buffer.WriteString("true") + } else { + buffer.WriteString("false") + } + + case float32: + buffer.WriteString(fmt.Sprintf("%g", float64(value))) + + case float64: + buffer.WriteString(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)) + } + + case stringWriter: + value.writeString(buffer, indent+"\t") + + case fmt.Stringer: + buffer.WriteString(value.String()) + + case []ViewShadow: + switch len(value) { + case 0: + // do nothing + + case 1: + value[0].writeString(buffer, indent) + + default: + buffer.WriteString("[") + indent2 := "\n" + indent + "\t" + for _, shadow := range value { + buffer.WriteString(indent2) + shadow.writeString(buffer, indent) + } + buffer.WriteRune('\n') + buffer.WriteString(indent) + buffer.WriteRune(']') + } + + case []View: + switch len(value) { + case 0: + buffer.WriteString("[]\n") + + case 1: + writeViewStyle(value[0].Tag(), value[0], buffer, indent) + + default: + buffer.WriteString("[\n") + indent2 := indent + "\t" + for _, v := range value { + buffer.WriteString(indent2) + writeViewStyle(v.Tag(), v, buffer, indent2) + buffer.WriteString(",\n") + } + + buffer.WriteString(indent) + buffer.WriteRune(']') + } + + case []interface{}: + switch count := len(value); count { + case 0: + buffer.WriteString("[]") + + case 1: + writePropertyValue(buffer, tag, value[0], indent) + + default: + buffer.WriteString("[ ") + comma := false + for _, v := range value { + if comma { + buffer.WriteString(", ") + } + writePropertyValue(buffer, tag, v, indent) + comma = true + } + buffer.WriteString(" ]") + } + + case map[string]Animation: + switch count := len(value); count { + case 0: + buffer.WriteString("[]") + + case 1: + for tag, animation := range value { + animation.writeTransitionString(tag, buffer) + break + } + + default: + tags := make([]string, 0, len(value)) + for tag := range value { + tags = append(tags, tag) + } + sort.Strings(tags) + buffer.WriteString("[\n") + indent2 := indent + "\t" + for _, tag := range tags { + if animation := value[tag]; animation != nil { + buffer.WriteString(indent2) + animation.writeTransitionString(tag, buffer) + buffer.WriteString("\n") + } + } + buffer.WriteString(indent) + buffer.WriteRune(']') + } + } +} + +func writeViewStyle(name string, view ViewStyle, buffer *strings.Builder, indent string) { + buffer.WriteString(name) + buffer.WriteString(" {\n") + indent += "\t" + + writeProperty := func(tag string, value interface{}) { + if supportedPropertyValue(value) { + buffer.WriteString(indent) + buffer.WriteString(tag) + buffer.WriteString(" = ") + writePropertyValue(buffer, tag, value, indent) + buffer.WriteString(",\n") + } + } + + tags := view.AllTags() + removeTag := func(tag string) { + for i, t := range tags { + if t == tag { + if i == 0 { + tags = tags[1:] + } else if i == len(tags)-1 { + tags = tags[:i] + } else { + tags = append(tags[:i], tags[i+1:]...) + } + return + } + } + } + + tagOrder := []string{ + ID, Row, Column, Top, Right, Bottom, Left, Semantics, Cursor, Visibility, + Opacity, ZIndex, Width, Height, MinWidth, MinHeight, MaxWidth, MaxHeight, + Margin, Padding, BackgroundClip, BackgroundColor, Background, Border, Radius, Outline, Shadow, + Orientation, Wrap, VerticalAlign, HorizontalAlign, CellWidth, CellHeight, + CellVerticalAlign, CellHorizontalAlign, GridRowGap, GridColumnGap, + ColumnCount, ColumnWidth, ColumnSeparator, ColumnGap, AvoidBreak, + Current, Expanded, Side, ResizeBorderWidth, EditViewType, MaxLength, Hint, Text, + TextOverflow, FontName, TextSize, TextColor, TextWeight, Italic, SmallCaps, + Strikethrough, Overline, Underline, TextLineStyle, TextLineThickness, + TextLineColor, TextTransform, TextAlign, WhiteSpace, WordBreak, TextShadow, TextIndent, + LetterSpacing, WordSpacing, LineHeight, TextDirection, WritingMode, VerticalTextOrientation, + } + + for _, tag := range tagOrder { + if value := view.Get(tag); value != nil { + removeTag(tag) + writeProperty(tag, value) + } + } + + finalTags := []string{ + Perspective, PerspectiveOriginX, PerspectiveOriginY, BackfaceVisible, OriginX, OriginY, OriginZ, + TranslateX, TranslateY, TranslateZ, ScaleX, ScaleY, ScaleZ, Rotate, RotateX, RotateY, RotateZ, + SkewX, SkewY, Clip, Filter, Summary, Content, Transition} + for _, tag := range finalTags { + removeTag(tag) + } + + for _, tag := range tags { + if value := view.Get(tag); value != nil { + writeProperty(tag, value) + } + } + + for _, tag := range finalTags { + if value := view.Get(tag); value != nil { + writeProperty(tag, value) + } + } + + indent = indent[:len(indent)-1] + buffer.WriteString(indent) + buffer.WriteString("}") +} + +func getViewString(view View) string { + buffer := allocStringBuilder() + defer freeStringBuilder(buffer) + writeViewStyle(view.Tag(), view, buffer, "") + return buffer.String() + +} + +func runStringWriter(writer stringWriter) string { + buffer := allocStringBuilder() + defer freeStringBuilder(buffer) + writer.writeString(buffer, "") + return buffer.String() +} diff --git a/viewStyleGet.go b/viewStyleGet.go deleted file mode 100644 index 738257e..0000000 --- a/viewStyleGet.go +++ /dev/null @@ -1,89 +0,0 @@ -package rui - -import ( - "strings" -) - -func valueToOrientation(value interface{}, session Session) (int, bool) { - if value != nil { - switch value := value.(type) { - case int: - return value, true - - case string: - text, ok := session.resolveConstants(value) - if !ok { - return 0, false - } - - text = strings.ToLower(strings.Trim(text, " \t\n\r")) - switch text { - case "vertical": - return TopDownOrientation, true - - case "horizontal": - return StartToEndOrientation, true - } - - if result, ok := enumStringToInt(text, enumProperties[Orientation].values, true); ok { - return result, true - } - } - } - return 0, false -} - -/* -func getOrientation(style Properties, session Session) (int, bool) { - return valueToOrientation(style.Get(Orientation), session) -} -*/ -func (style *viewStyle) Get(tag string) interface{} { - return style.get(strings.ToLower(tag)) -} - -func (style *viewStyle) get(tag string) interface{} { - switch tag { - case Border, CellBorder: - return getBorder(&style.propertyList, tag) - - case BorderLeft, BorderRight, BorderTop, BorderBottom, - BorderStyle, BorderLeftStyle, BorderRightStyle, BorderTopStyle, BorderBottomStyle, - BorderColor, BorderLeftColor, BorderRightColor, BorderTopColor, BorderBottomColor, - BorderWidth, BorderLeftWidth, BorderRightWidth, BorderTopWidth, BorderBottomWidth: - if border := getBorder(style, Border); border != nil { - return border.Get(tag) - } - return nil - - case CellBorderLeft, CellBorderRight, CellBorderTop, CellBorderBottom, - CellBorderStyle, CellBorderLeftStyle, CellBorderRightStyle, CellBorderTopStyle, CellBorderBottomStyle, - CellBorderColor, CellBorderLeftColor, CellBorderRightColor, CellBorderTopColor, CellBorderBottomColor, - CellBorderWidth, CellBorderLeftWidth, CellBorderRightWidth, CellBorderTopWidth, CellBorderBottomWidth: - if border := getBorder(style, CellBorder); border != nil { - return border.Get(tag) - } - return nil - - case RadiusX, RadiusY, RadiusTopLeft, RadiusTopLeftX, RadiusTopLeftY, - RadiusTopRight, RadiusTopRightX, RadiusTopRightY, - RadiusBottomLeft, RadiusBottomLeftX, RadiusBottomLeftY, - RadiusBottomRight, RadiusBottomRightX, RadiusBottomRightY: - return getRadiusElement(style, tag) - - case ColumnSeparator: - if val, ok := style.properties[ColumnSeparator]; ok { - return val.(ColumnSeparatorProperty) - } - return nil - - case ColumnSeparatorStyle, ColumnSeparatorWidth, ColumnSeparatorColor: - if val, ok := style.properties[ColumnSeparator]; ok { - separator := val.(ColumnSeparatorProperty) - return separator.Get(tag) - } - return nil - } - - return style.propertyList.getRaw(tag) -} diff --git a/viewStyleSet.go b/viewStyleSet.go index 64c9acf..a2d63ee 100644 --- a/viewStyleSet.go +++ b/viewStyleSet.go @@ -271,12 +271,13 @@ func (style *viewStyle) set(tag string, value interface{}) bool { case Transition: setObject := func(obj DataObject) bool { if obj != nil { - switch obj.Tag() { + tag := strings.ToLower(tag) + switch tag { case "", "_": ErrorLog("Invalid transition property name") default: - style.transitions[obj.Tag()] = parseAnimation(obj) + style.transitions[tag] = parseAnimation(obj) return true } } @@ -285,18 +286,19 @@ func (style *viewStyle) set(tag string, value interface{}) bool { switch value := value.(type) { case Params: - result := false + result := true for tag, val := range value { - if animation, ok := val.(Animation); ok { - tag = strings.ToLower(tag) - if animation == nil || tag == "" { - ErrorLog("Invalid transition property name") - } else { - style.transitions[tag] = animation - result = true - } + tag = strings.ToLower(strings.Trim(tag, " \t")) + if tag == "" { + ErrorLog("Invalid transition property name") + result = false + } else if val == nil { + delete(style.transitions, tag) + } else if animation, ok := val.(Animation); ok { + style.transitions[tag] = animation } else { notCompatibleType(Transition, val) + result = false } } return result diff --git a/viewStyle_test.go b/viewStyle_test.go deleted file mode 100644 index d117585..0000000 --- a/viewStyle_test.go +++ /dev/null @@ -1,131 +0,0 @@ -package rui - -/* -import ( - "strings" - "testing" -) - -func TestViewStyleCreate(t *testing.T) { - - app := new(application) - app.init("") - session := newSession(app, 1, "", false, false) - - var style viewStyle - style.init() - - data := []struct{ property, value string }{ - {Width, "100%"}, - {Height, "400px"}, - {Margin, "4px"}, - {Margin + "-bottom", "auto"}, - {Padding, "1em"}, - {Font, "Arial"}, - {BackgroundColor, "#FF008000"}, - {TextColor, "#FF000000"}, - {TextSize, "1.25em"}, - {TextWeight, "bold"}, - {TextAlign, "center"}, - {TextTransform, "uppercase"}, - {TextIndent, "0.25em"}, - {LetterSpacing, "1.5em"}, - {WordSpacing, "8px"}, - {LineHeight, "2em"}, - {Italic, "on"}, - {TextDecoration, "strikethrough | overline | underline"}, - {SmallCaps, "on"}, - } - - for _, prop := range data { - style.Set(prop.property, prop.value) - } - - style.AddShadow(NewViewShadow(SizeUnit{Auto, 0}, SizeUnit{Auto, 0}, Px(4), Px(6), 0xFF808080)) - - expected := `width: 100%; height: 400px; font-size: 1.25rem; text-indent: 0.25rem; letter-spacing: 1.5rem; word-spacing: 8px; ` + - `line-height: 2rem; padding: 1rem; margin-left: 4px; margin-top: 4px; margin-right: 4px; box-shadow: 0 0 4px 6px rgb(128,128,128); ` + - `background-color: rgb(0,128,0); color: rgb(0,0,0); font-family: Arial; font-weight: bold; font-style: italic; font-variant: small-caps; ` + - `text-align: center; text-decoration: line-through overline underline; text-transform: uppercase;` - - buffer := strings.Builder{} - style.cssViewStyle(&buffer, session) - if text := strings.Trim(buffer.String(), " "); text != expected { - t.Error("\nresult : " + text + "\nexpected: " + expected) - } - - w := newCompactDataWriter() - w.StartObject("_") - style.writeStyle(w) - w.FinishObject() - expected2 := `_{width=100%,height=400px,margin="4px,4px,auto,4px",padding=1em,background-color=#FF008000,shadow=_{color=#FF808080,blur=4px,spread-radius=6px},font=Arial,text-color=#FF000000,text-size=1.25em,text-weight=bold,italic=on,small-caps=on,text-decoration=strikethrough|overline|underline,text-align=center,text-indent=0.25em,letter-spacing=1.5em,word-spacing=8px,line-height=2em,text-transform=uppercase}` - - if text := w.String(); text != expected2 { - t.Error("\n result: " + text + "\nexpected: " + expected2) - } - - var style1 viewStyle - style1.init() - if obj, err := ParseDataText(expected2); err == nil { - style1.parseStyle(obj, new(sessionData)) - buffer.Reset() - style.cssStyle(&buffer) - if text := buffer.String(); text != expected { - t.Error("\n result: " + text + "\nexpected: " + expected) - } - } else { - t.Error(err) - } - - var style2 viewStyle - style2.init() - - style2.textWeight = 4 - style2.textAlign = RightAlign - style2.textTransform = LowerCaseTextTransform - style2.textDecoration = NoneDecoration - style2.italic = Off - style2.smallCaps = Off - - expected = `font-weight: normal; font-style: normal; font-variant: normal; text-align: right; text-decoration: none; text-transform: lowercase; ` - buffer.Reset() - style2.cssStyle(&buffer) - if text := buffer.String(); text != expected { - t.Error("\n result: " + text + "\nexpected: " + expected) - } - - w.Reset() - w.StartObject("_") - style2.writeStyle(w) - w.FinishObject() - expected = `_{text-weight=normal,italic=off,small-caps=off,text-decoration=none,text-align=right,text-transform=lowercase}` - - if text := w.String(); text != expected { - t.Error("\n result: " + text + "\nexpected: " + expected) - } - - style2.textWeight = 5 - style2.textAlign = JustifyTextAlign - style2.textTransform = CapitalizeTextTransform - style2.textDecoration = Inherit - style2.italic = Inherit - style2.smallCaps = Inherit - - expected = `font-weight: 500; text-align: justify; text-transform: capitalize; ` - buffer.Reset() - style2.cssStyle(&buffer) - if text := buffer.String(); text != expected { - t.Error("\n result: " + text + "\nexpected: " + expected) - } - - w.Reset() - w.StartObject("_") - style2.writeStyle(w) - w.FinishObject() - expected = `_{text-weight=5,text-align=justify,text-transform=capitalize}` - - if text := w.String(); text != expected { - t.Error("\n result: " + text + "\nexpected: " + expected) - } -} -*/ diff --git a/viewsContainer.go b/viewsContainer.go index a74d859..1e0b2d8 100644 --- a/viewsContainer.go +++ b/viewsContainer.go @@ -31,6 +31,10 @@ func (container *viewsContainerData) Init(session Session) { container.views = []View{} } +func (container *viewsContainerData) String() string { + return getViewString(container) +} + func (container *viewsContainerData) setParentID(parentID string) { container.viewData.setParentID(parentID) htmlID := container.htmlID()