mirror of https://github.com/anoshenko/rui.git
				
				
				
			Bug fixing
This commit is contained in:
		
							parent
							
								
									fcea1c89a3
								
							
						
					
					
						commit
						9478b1ee4f
					
				
							
								
								
									
										80
									
								
								animation.go
								
								
								
								
							
							
						
						
									
										80
									
								
								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) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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"
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										44
									
								
								border.go
								
								
								
								
							
							
						
						
									
										44
									
								
								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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										81
									
								
								bounds.go
								
								
								
								
							
							
						
						
									
										81
									
								
								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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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("<summary>")
 | 
			
		||||
			buffer.WriteString(value)
 | 
			
		||||
			buffer.WriteString("</summary>")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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))
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										22
									
								
								outline.go
								
								
								
								
							
							
						
						
									
										22
									
								
								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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										22
									
								
								radius.go
								
								
								
								
							
							
						
						
									
										22
									
								
								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) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										45
									
								
								ruiWriter.go
								
								
								
								
							
							
						
						
									
										45
									
								
								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
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										23
									
								
								shadow.go
								
								
								
								
							
							
						
						
									
										23
									
								
								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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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":
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										14
									
								
								textView.go
								
								
								
								
							
							
						
						
									
										14
									
								
								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 {
 | 
			
		||||
			if !GetNotTranslate(textView, "") {
 | 
			
		||||
				text, _ = textView.session.GetString(text)
 | 
			
		||||
			}
 | 
			
		||||
			buffer.WriteString(textToJS(text))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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"
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										59
									
								
								view.go
								
								
								
								
							
							
						
						
									
										59
									
								
								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)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										236
									
								
								viewClip.go
								
								
								
								
							
							
						
						
									
										236
									
								
								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} {
 | 
			
		||||
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
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tag := range []string{X, Y} {
 | 
			
		||||
		if value, ok := clip.properties[tag]; ok {
 | 
			
		||||
			writeProperty(tag, value)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	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.WriteString(rx.cssString("50%"))
 | 
			
		||||
	buffer.WriteRune(' ')
 | 
			
		||||
		buffer.WriteString(ry.cssString("0"))
 | 
			
		||||
	}
 | 
			
		||||
	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 ""
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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(", ")
 | 
			
		||||
			}
 | 
			
		||||
	writer.endObject()
 | 
			
		||||
			buffer.WriteString(tag)
 | 
			
		||||
			buffer.WriteString(" = ")
 | 
			
		||||
			writePropertyValue(buffer, tag, value, indent)
 | 
			
		||||
			comma = true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	buffer.WriteString(" }")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (filter *viewFilter) cssStyle(session Session) string {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										405
									
								
								viewStyle.go
								
								
								
								
							
							
						
						
									
										405
									
								
								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()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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 == "" {
 | 
			
		||||
				tag = strings.ToLower(strings.Trim(tag, " \t"))
 | 
			
		||||
				if tag == "" {
 | 
			
		||||
					ErrorLog("Invalid transition property name")
 | 
			
		||||
					} else {
 | 
			
		||||
					result = false
 | 
			
		||||
				} else if val == nil {
 | 
			
		||||
					delete(style.transitions, tag)
 | 
			
		||||
				} else if animation, ok := val.(Animation); ok {
 | 
			
		||||
					style.transitions[tag] = animation
 | 
			
		||||
						result = true
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					notCompatibleType(Transition, val)
 | 
			
		||||
					result = false
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return result
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
		}
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
| 
						 | 
				
			
			@ -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()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue