forked from mbk-lab/rui_orig
				
			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 | ||||
|  |  | |||
							
								
								
									
										16
									
								
								textView.go
								
								
								
								
							
							
						
						
									
										16
									
								
								textView.go
								
								
								
								
							|  | @ -32,6 +32,10 @@ func (textView *textViewData) Init(session Session) { | |||
| 	textView.tag = "TextView" | ||||
| } | ||||
| 
 | ||||
| func (textView *textViewData) String() string { | ||||
| 	return getViewString(textView) | ||||
| } | ||||
| 
 | ||||
| func (textView *textViewData) Get(tag string) interface{} { | ||||
| 	return textView.get(strings.ToLower(tag)) | ||||
| } | ||||
|  | @ -103,6 +107,14 @@ func (textView *textViewData) set(tag string, value interface{}) bool { | |||
| 			textView.textOverflowUpdated() | ||||
| 		} | ||||
| 
 | ||||
| 	case NotTranslate: | ||||
| 		if !textView.viewData.set(tag, value) { | ||||
| 			return false | ||||
| 		} | ||||
| 		if textView.created { | ||||
| 			updateInnerHTML(textView.htmlID(), textView.Session()) | ||||
| 		} | ||||
| 
 | ||||
| 	default: | ||||
| 		return textView.viewData.set(tag, value) | ||||
| 	} | ||||
|  | @ -143,7 +155,9 @@ func textToJS(text string) string { | |||
| func (textView *textViewData) htmlSubviews(self View, buffer *strings.Builder) { | ||||
| 	if value := textView.getRaw(Text); value != nil { | ||||
| 		if text, ok := value.(string); ok { | ||||
| 			text, _ = textView.session.GetString(text) | ||||
| 			if !GetNotTranslate(textView, "") { | ||||
| 				text, _ = textView.session.GetString(text) | ||||
| 			} | ||||
| 			buffer.WriteString(textToJS(text)) | ||||
| 		} | ||||
| 	} | ||||
|  |  | |||
|  | @ -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) | ||||
| } | ||||
|  |  | |||
							
								
								
									
										248
									
								
								viewClip.go
								
								
								
								
							
							
						
						
									
										248
									
								
								viewClip.go
								
								
								
								
							|  | @ -9,7 +9,7 @@ import ( | |||
| type ClipShape interface { | ||||
| 	Properties | ||||
| 	fmt.Stringer | ||||
| 	ruiStringer | ||||
| 	stringWriter | ||||
| 	cssStyle(session Session) string | ||||
| 	valid(session Session) bool | ||||
| } | ||||
|  | @ -22,6 +22,10 @@ type ellipseClip struct { | |||
| 	propertyList | ||||
| } | ||||
| 
 | ||||
| type circleClip struct { | ||||
| 	propertyList | ||||
| } | ||||
| 
 | ||||
| type polygonClip struct { | ||||
| 	points []interface{} | ||||
| } | ||||
|  | @ -47,7 +51,7 @@ func InsetClip(top, right, bottom, left SizeUnit, radius RadiusProperty) ClipSha | |||
| 
 | ||||
| // CircleClip creates a circle View clipping area.
 | ||||
| func CircleClip(x, y, radius SizeUnit) ClipShape { | ||||
| 	clip := new(ellipseClip) | ||||
| 	clip := new(circleClip) | ||||
| 	clip.init() | ||||
| 	clip.Set(X, x) | ||||
| 	clip.Set(Y, y) | ||||
|  | @ -112,39 +116,25 @@ func (clip *insetClip) Set(tag string, value interface{}) bool { | |||
| } | ||||
| 
 | ||||
| func (clip *insetClip) String() string { | ||||
| 	writer := newRUIWriter() | ||||
| 	clip.ruiString(writer) | ||||
| 	return writer.finish() | ||||
| 	return runStringWriter(clip) | ||||
| } | ||||
| 
 | ||||
| func (clip *insetClip) ruiString(writer ruiWriter) { | ||||
| 	writer.startObject("inset") | ||||
| 	for _, tag := range []string{Top, Right, Bottom, Left} { | ||||
| func (clip *insetClip) writeString(buffer *strings.Builder, indent string) { | ||||
| 	buffer.WriteString("inset { ") | ||||
| 	comma := false | ||||
| 	for _, tag := range []string{Top, Right, Bottom, Left, Radius} { | ||||
| 		if value, ok := clip.properties[tag]; ok { | ||||
| 			switch value := value.(type) { | ||||
| 			case string: | ||||
| 				writer.writeProperty(tag, value) | ||||
| 
 | ||||
| 			case fmt.Stringer: | ||||
| 				writer.writeProperty(tag, value.String()) | ||||
| 			if comma { | ||||
| 				buffer.WriteString(", ") | ||||
| 			} | ||||
| 			buffer.WriteString(tag) | ||||
| 			buffer.WriteString(" = ") | ||||
| 			writePropertyValue(buffer, tag, value, indent) | ||||
| 			comma = true | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if value := clip.Get(Radius); value != nil { | ||||
| 		switch value := value.(type) { | ||||
| 		case RadiusProperty: | ||||
| 			writer.writeProperty(Radius, value.String()) | ||||
| 
 | ||||
| 		case SizeUnit: | ||||
| 			writer.writeProperty(Radius, value.String()) | ||||
| 
 | ||||
| 		case string: | ||||
| 			writer.writeProperty(Radius, value) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	writer.endObject() | ||||
| 	buffer.WriteString(" }") | ||||
| } | ||||
| 
 | ||||
| func (clip *insetClip) cssStyle(session Session) string { | ||||
|  | @ -178,83 +168,108 @@ func (clip *insetClip) valid(session Session) bool { | |||
| 	return false | ||||
| } | ||||
| 
 | ||||
| func (clip *circleClip) Set(tag string, value interface{}) bool { | ||||
| 	if value == nil { | ||||
| 		clip.Remove(tag) | ||||
| 	} | ||||
| 
 | ||||
| 	switch strings.ToLower(tag) { | ||||
| 	case X, Y, Radius: | ||||
| 		return clip.setSizeProperty(tag, value) | ||||
| 	} | ||||
| 
 | ||||
| 	ErrorLogF(`"%s" property is not supported by the circle clip shape`, tag) | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| func (clip *circleClip) String() string { | ||||
| 	return runStringWriter(clip) | ||||
| } | ||||
| 
 | ||||
| func (clip *circleClip) writeString(buffer *strings.Builder, indent string) { | ||||
| 	buffer.WriteString("circle { ") | ||||
| 	comma := false | ||||
| 	for _, tag := range []string{Radius, X, Y} { | ||||
| 		if value, ok := clip.properties[tag]; ok { | ||||
| 			if comma { | ||||
| 				buffer.WriteString(", ") | ||||
| 			} | ||||
| 			buffer.WriteString(tag) | ||||
| 			buffer.WriteString(" = ") | ||||
| 			writePropertyValue(buffer, tag, value, indent) | ||||
| 			comma = true | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	buffer.WriteString(" }") | ||||
| } | ||||
| 
 | ||||
| func (clip *circleClip) cssStyle(session Session) string { | ||||
| 
 | ||||
| 	buffer := allocStringBuilder() | ||||
| 	defer freeStringBuilder(buffer) | ||||
| 
 | ||||
| 	buffer.WriteString("circle(") | ||||
| 	r, _ := sizeProperty(clip, Radius, session) | ||||
| 	buffer.WriteString(r.cssString("50%")) | ||||
| 
 | ||||
| 	buffer.WriteString(" at ") | ||||
| 	x, _ := sizeProperty(clip, X, session) | ||||
| 	buffer.WriteString(x.cssString("50%")) | ||||
| 	buffer.WriteRune(' ') | ||||
| 
 | ||||
| 	y, _ := sizeProperty(clip, Y, session) | ||||
| 	buffer.WriteString(y.cssString("50%")) | ||||
| 	buffer.WriteRune(')') | ||||
| 
 | ||||
| 	return buffer.String() | ||||
| } | ||||
| 
 | ||||
| func (clip *circleClip) valid(session Session) bool { | ||||
| 	if value, ok := sizeProperty(clip, Radius, session); ok && value.Value == 0 { | ||||
| 		return false | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func (clip *ellipseClip) Set(tag string, value interface{}) bool { | ||||
| 	if value == nil { | ||||
| 		clip.Remove(tag) | ||||
| 	} | ||||
| 
 | ||||
| 	switch strings.ToLower(tag) { | ||||
| 	case X, Y: | ||||
| 	case X, Y, RadiusX, RadiusY: | ||||
| 		return clip.setSizeProperty(tag, value) | ||||
| 
 | ||||
| 	case Radius: | ||||
| 		result := clip.setSizeProperty(tag, value) | ||||
| 		if result { | ||||
| 			delete(clip.properties, RadiusX) | ||||
| 			delete(clip.properties, RadiusY) | ||||
| 		} | ||||
| 		return result | ||||
| 
 | ||||
| 	case RadiusX: | ||||
| 		result := clip.setSizeProperty(tag, value) | ||||
| 		if result { | ||||
| 			if r, ok := clip.properties[Radius]; ok { | ||||
| 				clip.properties[RadiusY] = r | ||||
| 				delete(clip.properties, Radius) | ||||
| 			} | ||||
| 		} | ||||
| 		return result | ||||
| 
 | ||||
| 	case RadiusY: | ||||
| 		result := clip.setSizeProperty(tag, value) | ||||
| 		if result { | ||||
| 			if r, ok := clip.properties[Radius]; ok { | ||||
| 				clip.properties[RadiusX] = r | ||||
| 				delete(clip.properties, Radius) | ||||
| 			} | ||||
| 		} | ||||
| 		return result | ||||
| 		return clip.setSizeProperty(RadiusX, value) && | ||||
| 			clip.setSizeProperty(RadiusY, value) | ||||
| 	} | ||||
| 
 | ||||
| 	ErrorLogF(`"%s" property is not supported by the inset clip shape`, tag) | ||||
| 	ErrorLogF(`"%s" property is not supported by the ellipse clip shape`, tag) | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| func (clip *ellipseClip) String() string { | ||||
| 	writer := newRUIWriter() | ||||
| 	clip.ruiString(writer) | ||||
| 	return writer.finish() | ||||
| 	return runStringWriter(clip) | ||||
| } | ||||
| 
 | ||||
| func (clip *ellipseClip) ruiString(writer ruiWriter) { | ||||
| 	writeProperty := func(tag string, value interface{}) { | ||||
| 		switch value := value.(type) { | ||||
| 		case string: | ||||
| 			writer.writeProperty(tag, value) | ||||
| 
 | ||||
| 		case fmt.Stringer: | ||||
| 			writer.writeProperty(tag, value.String()) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if r, ok := clip.properties[Radius]; ok { | ||||
| 		writer.startObject("circle") | ||||
| 		writeProperty(Radius, r) | ||||
| 	} else { | ||||
| 		writer.startObject("ellipse") | ||||
| 		for _, tag := range []string{RadiusX, RadiusY} { | ||||
| 			if value, ok := clip.properties[tag]; ok { | ||||
| 				writeProperty(tag, value) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	for _, tag := range []string{X, Y} { | ||||
| func (clip *ellipseClip) writeString(buffer *strings.Builder, indent string) { | ||||
| 	buffer.WriteString("ellipse { ") | ||||
| 	comma := false | ||||
| 	for _, tag := range []string{RadiusX, RadiusY, X, Y} { | ||||
| 		if value, ok := clip.properties[tag]; ok { | ||||
| 			writeProperty(tag, value) | ||||
| 			if comma { | ||||
| 				buffer.WriteString(", ") | ||||
| 			} | ||||
| 			buffer.WriteString(tag) | ||||
| 			buffer.WriteString(" = ") | ||||
| 			writePropertyValue(buffer, tag, value, indent) | ||||
| 			comma = true | ||||
| 		} | ||||
| 	} | ||||
| 	writer.endObject() | ||||
| 
 | ||||
| 	buffer.WriteString(" }") | ||||
| } | ||||
| 
 | ||||
| func (clip *ellipseClip) cssStyle(session Session) string { | ||||
|  | @ -262,38 +277,29 @@ func (clip *ellipseClip) cssStyle(session Session) string { | |||
| 	buffer := allocStringBuilder() | ||||
| 	defer freeStringBuilder(buffer) | ||||
| 
 | ||||
| 	if r, ok := sizeProperty(clip, Radius, session); ok { | ||||
| 		buffer.WriteString("circle(") | ||||
| 		buffer.WriteString(r.cssString("0")) | ||||
| 	} else { | ||||
| 		rx, _ := sizeProperty(clip, RadiusX, session) | ||||
| 		ry, _ := sizeProperty(clip, RadiusX, session) | ||||
| 		buffer.WriteString("ellipse(") | ||||
| 		buffer.WriteString(rx.cssString("0")) | ||||
| 		buffer.WriteRune(' ') | ||||
| 		buffer.WriteString(ry.cssString("0")) | ||||
| 	} | ||||
| 	rx, _ := sizeProperty(clip, RadiusX, session) | ||||
| 	ry, _ := sizeProperty(clip, RadiusX, session) | ||||
| 	buffer.WriteString("ellipse(") | ||||
| 	buffer.WriteString(rx.cssString("50%")) | ||||
| 	buffer.WriteRune(' ') | ||||
| 	buffer.WriteString(ry.cssString("50%")) | ||||
| 
 | ||||
| 	buffer.WriteString(" at ") | ||||
| 	x, _ := sizeProperty(clip, X, session) | ||||
| 	buffer.WriteString(x.cssString("0")) | ||||
| 	buffer.WriteString(x.cssString("50%")) | ||||
| 	buffer.WriteRune(' ') | ||||
| 
 | ||||
| 	y, _ := sizeProperty(clip, Y, session) | ||||
| 	buffer.WriteString(y.cssString("0")) | ||||
| 	buffer.WriteString(y.cssString("50%")) | ||||
| 	buffer.WriteRune(')') | ||||
| 
 | ||||
| 	return buffer.String() | ||||
| } | ||||
| 
 | ||||
| func (clip *ellipseClip) valid(session Session) bool { | ||||
| 	if value, ok := sizeProperty(clip, Radius, session); ok && value.Type != Auto && value.Value != 0 { | ||||
| 		return true | ||||
| 	} | ||||
| 
 | ||||
| 	rx, okX := sizeProperty(clip, RadiusX, session) | ||||
| 	ry, okY := sizeProperty(clip, RadiusY, session) | ||||
| 	return okX && okY && rx.Type != Auto && rx.Value != 0 && ry.Type != Auto && ry.Value != 0 | ||||
| 	rx, _ := sizeProperty(clip, RadiusX, session) | ||||
| 	ry, _ := sizeProperty(clip, RadiusY, session) | ||||
| 	return rx.Value != 0 && ry.Value != 0 | ||||
| } | ||||
| 
 | ||||
| func (clip *polygonClip) Get(tag string) interface{} { | ||||
|  | @ -383,47 +389,31 @@ func (clip *polygonClip) AllTags() []string { | |||
| } | ||||
| 
 | ||||
| func (clip *polygonClip) String() string { | ||||
| 	writer := newRUIWriter() | ||||
| 	clip.ruiString(writer) | ||||
| 	return writer.finish() | ||||
| 	return runStringWriter(clip) | ||||
| } | ||||
| 
 | ||||
| func (clip *polygonClip) ruiString(writer ruiWriter) { | ||||
| func (clip *polygonClip) writeString(buffer *strings.Builder, indent string) { | ||||
| 
 | ||||
| 	buffer := allocStringBuilder() | ||||
| 	defer freeStringBuilder(buffer) | ||||
| 
 | ||||
| 	writer.startObject("polygon") | ||||
| 	buffer.WriteString("inset { ") | ||||
| 
 | ||||
| 	if clip.points != nil { | ||||
| 		buffer.WriteString(Points) | ||||
| 		buffer.WriteString(` = "`) | ||||
| 		for i, value := range clip.points { | ||||
| 			if i > 0 { | ||||
| 				buffer.WriteString(", ") | ||||
| 			} | ||||
| 			switch value := value.(type) { | ||||
| 			case string: | ||||
| 				buffer.WriteString(value) | ||||
| 
 | ||||
| 			case fmt.Stringer: | ||||
| 				buffer.WriteString(value.String()) | ||||
| 
 | ||||
| 			default: | ||||
| 				buffer.WriteString("0px") | ||||
| 			} | ||||
| 			writePropertyValue(buffer, "", value, indent) | ||||
| 		} | ||||
| 
 | ||||
| 		writer.writeProperty(Points, buffer.String()) | ||||
| 		buffer.WriteString(`" `) | ||||
| 	} | ||||
| 
 | ||||
| 	writer.endObject() | ||||
| 	buffer.WriteRune('}') | ||||
| } | ||||
| 
 | ||||
| func (clip *polygonClip) cssStyle(session Session) string { | ||||
| 
 | ||||
| 	if clip.points == nil { | ||||
| 		return "" | ||||
| 	} | ||||
| 
 | ||||
| 	count := len(clip.points) | ||||
| 	if count < 2 { | ||||
| 		return "" | ||||
|  |  | |||
|  | @ -77,7 +77,7 @@ const ( | |||
| type ViewFilter interface { | ||||
| 	Properties | ||||
| 	fmt.Stringer | ||||
| 	ruiStringer | ||||
| 	stringWriter | ||||
| 	cssStyle(session Session) string | ||||
| } | ||||
| 
 | ||||
|  | @ -155,17 +155,25 @@ func (filter *viewFilter) Set(tag string, value interface{}) bool { | |||
| } | ||||
| 
 | ||||
| func (filter *viewFilter) String() string { | ||||
| 	writer := newRUIWriter() | ||||
| 	filter.ruiString(writer) | ||||
| 	return writer.finish() | ||||
| 	return runStringWriter(filter) | ||||
| } | ||||
| 
 | ||||
| func (filter *viewFilter) ruiString(writer ruiWriter) { | ||||
| 	writer.startObject("filter") | ||||
| 	for tag, value := range filter.properties { | ||||
| 		writer.writeProperty(tag, value) | ||||
| func (filter *viewFilter) writeString(buffer *strings.Builder, indent string) { | ||||
| 	buffer.WriteString("filter { ") | ||||
| 	comma := false | ||||
| 	tags := filter.AllTags() | ||||
| 	for _, tag := range tags { | ||||
| 		if value, ok := filter.properties[tag]; ok { | ||||
| 			if comma { | ||||
| 				buffer.WriteString(", ") | ||||
| 			} | ||||
| 			buffer.WriteString(tag) | ||||
| 			buffer.WriteString(" = ") | ||||
| 			writePropertyValue(buffer, tag, value, indent) | ||||
| 			comma = true | ||||
| 		} | ||||
| 	} | ||||
| 	writer.endObject() | ||||
| 	buffer.WriteString(" }") | ||||
| } | ||||
| 
 | ||||
| func (filter *viewFilter) cssStyle(session Session) string { | ||||
|  |  | |||
							
								
								
									
										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 == "" { | ||||
| 						ErrorLog("Invalid transition property name") | ||||
| 					} else { | ||||
| 						style.transitions[tag] = animation | ||||
| 						result = true | ||||
| 					} | ||||
| 				tag = strings.ToLower(strings.Trim(tag, " \t")) | ||||
| 				if tag == "" { | ||||
| 					ErrorLog("Invalid transition property name") | ||||
| 					result = false | ||||
| 				} else if val == nil { | ||||
| 					delete(style.transitions, tag) | ||||
| 				} else if animation, ok := val.(Animation); ok { | ||||
| 					style.transitions[tag] = animation | ||||
| 				} else { | ||||
| 					notCompatibleType(Transition, val) | ||||
| 					result = false | ||||
| 				} | ||||
| 			} | ||||
| 			return result | ||||
|  |  | |||
|  | @ -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