mirror of https://github.com/anoshenko/rui.git
				
				
				
			Merge branch '0.9'
This commit is contained in:
		
						commit
						3c4315ed78
					
				
							
								
								
									
										15
									
								
								CHANGELOG.md
								
								
								
								
							
							
						
						
									
										15
									
								
								CHANGELOG.md
								
								
								
								
							| 
						 | 
				
			
			@ -1,3 +1,18 @@
 | 
			
		|||
# v0.9.0
 | 
			
		||||
 | 
			
		||||
* Requires go 1.18 or higher
 | 
			
		||||
* The "interface{}" type replaced by "any"
 | 
			
		||||
* Added SizeFunc interface and Function field to SizeUnit struct
 | 
			
		||||
* Added MaxSize, MinSize, SumSize, SubSize, MulSize, DivSize, ClampSize functions
 | 
			
		||||
* Added "list-row-gap", "list-column-gap", "accent-color", "tab-size", "overflow", 
 | 
			
		||||
"arrow", "arrow-align", "arrow-size", "arrow-width", and "arrow-offset" properties 
 | 
			
		||||
* Added "@ruiArrowSize" and "@ruiArrowWidth" constants to the default theme
 | 
			
		||||
* Added Transition, Transitions, and SetTransition functions to the ViewStyle interface
 | 
			
		||||
* Added GetListRowGap, GetListColumnGap, GetAccentColor, GetTabSize, GetOverflow, IsTimingFunctionValid, and GetTransitions functions
 | 
			
		||||
* Changed GetTransition functions
 | 
			
		||||
* Added the OpenURL function to the Session interface
 | 
			
		||||
* Changed the type of the second argument of all Get functions to "subviewID ...string" (previously "subviewID string")
 | 
			
		||||
 | 
			
		||||
# v0.8.0
 | 
			
		||||
 | 
			
		||||
* Added "loaded-event" and "error-event" events to ImageView
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										475
									
								
								README-ru.md
								
								
								
								
							
							
						
						
									
										475
									
								
								README-ru.md
								
								
								
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
				
			
			@ -14,7 +14,7 @@ type absoluteLayoutData struct {
 | 
			
		|||
// NewAbsoluteLayout create new AbsoluteLayout object and return it
 | 
			
		||||
func NewAbsoluteLayout(session Session, params Params) AbsoluteLayout {
 | 
			
		||||
	view := new(absoluteLayoutData)
 | 
			
		||||
	view.Init(session)
 | 
			
		||||
	view.init(session)
 | 
			
		||||
	setInitParams(view, params)
 | 
			
		||||
	return view
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -24,8 +24,8 @@ func newAbsoluteLayout(session Session) View {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Init initialize fields of ViewsContainer by default values
 | 
			
		||||
func (layout *absoluteLayoutData) Init(session Session) {
 | 
			
		||||
	layout.viewsContainerData.Init(session)
 | 
			
		||||
func (layout *absoluteLayoutData) init(session Session) {
 | 
			
		||||
	layout.viewsContainerData.init(session)
 | 
			
		||||
	layout.tag = "AbsoluteLayout"
 | 
			
		||||
	layout.systemClass = "ruiAbsoluteLayout"
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										177
									
								
								animation.go
								
								
								
								
							
							
						
						
									
										177
									
								
								animation.go
								
								
								
								
							| 
						 | 
				
			
			@ -55,16 +55,19 @@ const (
 | 
			
		|||
	// The animation plays forwards each cycle. In other words, each time the animation cycles,
 | 
			
		||||
	// the animation will reset to the beginning state and start over again. This is the default value.
 | 
			
		||||
	NormalAnimation = 0
 | 
			
		||||
 | 
			
		||||
	// ReverseAnimation is value of the "animation-direction" property.
 | 
			
		||||
	// The animation plays backwards each cycle. In other words, each time the animation cycles,
 | 
			
		||||
	// the animation will reset to the end state and start over again. Animation steps are performed
 | 
			
		||||
	// backwards, and timing functions are also reversed.
 | 
			
		||||
	// For example, an "ease-in" timing function becomes "ease-out".
 | 
			
		||||
	ReverseAnimation = 1
 | 
			
		||||
 | 
			
		||||
	// AlternateAnimation is value of the "animation-direction" property.
 | 
			
		||||
	// The animation reverses direction each cycle, with the first iteration being played forwards.
 | 
			
		||||
	// The count to determine if a cycle is even or odd starts at one.
 | 
			
		||||
	AlternateAnimation = 2
 | 
			
		||||
 | 
			
		||||
	// AlternateReverseAnimation is value of the "animation-direction" property.
 | 
			
		||||
	// The animation reverses direction each cycle, with the first iteration being played backwards.
 | 
			
		||||
	// The count to determine if a cycle is even or odd starts at one.
 | 
			
		||||
| 
						 | 
				
			
			@ -107,11 +110,11 @@ type AnimatedProperty struct {
 | 
			
		|||
	// Tag is the name of the property
 | 
			
		||||
	Tag string
 | 
			
		||||
	// From is the initial value of the property
 | 
			
		||||
	From interface{}
 | 
			
		||||
	From any
 | 
			
		||||
	// To is the final value of the property
 | 
			
		||||
	To interface{}
 | 
			
		||||
	To any
 | 
			
		||||
	// KeyFrames is intermediate property values
 | 
			
		||||
	KeyFrames map[int]interface{}
 | 
			
		||||
	KeyFrames map[int]any
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type animationData struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -184,7 +187,7 @@ func (animation *animationData) normalizeTag(tag string) string {
 | 
			
		|||
	return tag
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (animation *animationData) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (animation *animationData) Set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		animation.Remove(tag)
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			@ -285,7 +288,7 @@ func (animation *animationData) Set(tag string, value interface{}) bool {
 | 
			
		|||
								ErrorLogF(`key-frame "%d" is out of range`, n)
 | 
			
		||||
							} else {
 | 
			
		||||
								if result.KeyFrames == nil {
 | 
			
		||||
									result.KeyFrames = map[int]interface{}{n: node.Text()}
 | 
			
		||||
									result.KeyFrames = map[int]any{n: node.Text()}
 | 
			
		||||
								} else {
 | 
			
		||||
									result.KeyFrames[n] = node.Text()
 | 
			
		||||
								}
 | 
			
		||||
| 
						 | 
				
			
			@ -359,7 +362,7 @@ func (animation *animationData) Remove(tag string) {
 | 
			
		|||
	delete(animation.properties, animation.normalizeTag(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (animation *animationData) Get(tag string) interface{} {
 | 
			
		||||
func (animation *animationData) Get(tag string) any {
 | 
			
		||||
	return animation.getRaw(animation.normalizeTag(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -397,7 +400,7 @@ func (animation *animationData) animationCSS(session Session) string {
 | 
			
		|||
 | 
			
		||||
	buffer.WriteString(animation.keyFramesName)
 | 
			
		||||
 | 
			
		||||
	if duration, _ := floatProperty(animation, Duration, session, 1); duration > 0 {
 | 
			
		||||
	if duration, ok := floatProperty(animation, Duration, session, 1); ok && duration > 0 {
 | 
			
		||||
		buffer.WriteString(fmt.Sprintf(" %gs ", duration))
 | 
			
		||||
	} else {
 | 
			
		||||
		buffer.WriteString(" 1s ")
 | 
			
		||||
| 
						 | 
				
			
			@ -405,7 +408,7 @@ func (animation *animationData) animationCSS(session Session) string {
 | 
			
		|||
 | 
			
		||||
	buffer.WriteString(animation.timingFunctionCSS(session))
 | 
			
		||||
 | 
			
		||||
	if delay, _ := floatProperty(animation, Delay, session, 0); delay > 0 {
 | 
			
		||||
	if delay, ok := floatProperty(animation, Delay, session, 0); ok && delay > 0 {
 | 
			
		||||
		buffer.WriteString(fmt.Sprintf(" %gs", delay))
 | 
			
		||||
	} else {
 | 
			
		||||
		buffer.WriteString(" 0s")
 | 
			
		||||
| 
						 | 
				
			
			@ -435,7 +438,7 @@ func (animation *animationData) animationCSS(session Session) string {
 | 
			
		|||
 | 
			
		||||
func (animation *animationData) transitionCSS(buffer *strings.Builder, session Session) {
 | 
			
		||||
 | 
			
		||||
	if duration, _ := floatProperty(animation, Duration, session, 1); duration > 0 {
 | 
			
		||||
	if duration, ok := floatProperty(animation, Duration, session, 1); ok && duration > 0 {
 | 
			
		||||
		buffer.WriteString(fmt.Sprintf(" %gs ", duration))
 | 
			
		||||
	} else {
 | 
			
		||||
		buffer.WriteString(" 1s ")
 | 
			
		||||
| 
						 | 
				
			
			@ -443,7 +446,7 @@ func (animation *animationData) transitionCSS(buffer *strings.Builder, session S
 | 
			
		|||
 | 
			
		||||
	buffer.WriteString(animation.timingFunctionCSS(session))
 | 
			
		||||
 | 
			
		||||
	if delay, _ := floatProperty(animation, Delay, session, 0); delay > 0 {
 | 
			
		||||
	if delay, ok := floatProperty(animation, Delay, session, 0); ok && delay > 0 {
 | 
			
		||||
		buffer.WriteString(fmt.Sprintf(" %gs", delay))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -481,6 +484,8 @@ func (animation *animationData) writeTransitionString(tag string, buffer *string
 | 
			
		|||
				buffer.WriteRune('"')
 | 
			
		||||
				buffer.WriteString(timingFunction)
 | 
			
		||||
				buffer.WriteRune('"')
 | 
			
		||||
			} else {
 | 
			
		||||
				buffer.WriteString(timingFunction)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -490,14 +495,14 @@ func (animation *animationData) writeTransitionString(tag string, buffer *string
 | 
			
		|||
 | 
			
		||||
func (animation *animationData) timingFunctionCSS(session Session) string {
 | 
			
		||||
	if timingFunction, ok := stringProperty(animation, TimingFunction, session); ok {
 | 
			
		||||
		if timingFunction, ok = session.resolveConstants(timingFunction); ok && validateTimingFunction(timingFunction) {
 | 
			
		||||
		if timingFunction, ok = session.resolveConstants(timingFunction); ok && isTimingFunctionValid(timingFunction) {
 | 
			
		||||
			return timingFunction
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return ("ease")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validateTimingFunction(timingFunction string) bool {
 | 
			
		||||
func isTimingFunctionValid(timingFunction string) bool {
 | 
			
		||||
	switch timingFunction {
 | 
			
		||||
	case "", EaseTiming, EaseInTiming, EaseOutTiming, EaseInOutTiming, LinearTiming:
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			@ -529,6 +534,14 @@ func validateTimingFunction(timingFunction string) bool {
 | 
			
		|||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsTimingFunctionValid returns "true" if the "timingFunction" argument is the valid timing function.
 | 
			
		||||
func IsTimingFunctionValid(timingFunction string, session Session) bool {
 | 
			
		||||
	if timingFunc, ok := session.resolveConstants(strings.Trim(timingFunction, " \t\n")); ok {
 | 
			
		||||
		return isTimingFunctionValid(timingFunc)
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (session *sessionData) registerAnimation(props []AnimatedProperty) string {
 | 
			
		||||
 | 
			
		||||
	session.animationCounter++
 | 
			
		||||
| 
						 | 
				
			
			@ -602,7 +615,7 @@ func (session *sessionData) registerAnimation(props []AnimatedProperty) string {
 | 
			
		|||
	return name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (view *viewData) SetAnimated(tag string, value interface{}, animation Animation) bool {
 | 
			
		||||
func (view *viewData) SetAnimated(tag string, value any, animation Animation) bool {
 | 
			
		||||
	if animation == nil {
 | 
			
		||||
		return view.Set(tag, value)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -659,11 +672,29 @@ func (style *viewStyle) transitionCSS(session Session) string {
 | 
			
		|||
	buffer := allocStringBuilder()
 | 
			
		||||
	defer freeStringBuilder(buffer)
 | 
			
		||||
 | 
			
		||||
	convert := map[string]string{
 | 
			
		||||
		CellHeight:        "grid-template-rows",
 | 
			
		||||
		CellWidth:         "grid-template-columns",
 | 
			
		||||
		Row:               "grid-row",
 | 
			
		||||
		Column:            "grid-column",
 | 
			
		||||
		Clip:              "clip-path",
 | 
			
		||||
		Shadow:            "box-shadow",
 | 
			
		||||
		ColumnSeparator:   "column-rule",
 | 
			
		||||
		FontName:          "font",
 | 
			
		||||
		TextSize:          "font-size",
 | 
			
		||||
		TextLineThickness: "text-decoration-thickness",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for tag, animation := range style.transitions {
 | 
			
		||||
		if buffer.Len() > 0 {
 | 
			
		||||
			buffer.WriteString(", ")
 | 
			
		||||
		}
 | 
			
		||||
		buffer.WriteString(tag)
 | 
			
		||||
 | 
			
		||||
		if cssTag, ok := convert[tag]; ok {
 | 
			
		||||
			buffer.WriteString(cssTag)
 | 
			
		||||
		} else {
 | 
			
		||||
			buffer.WriteString(tag)
 | 
			
		||||
		}
 | 
			
		||||
		animation.transitionCSS(buffer, session)
 | 
			
		||||
	}
 | 
			
		||||
	return buffer.String()
 | 
			
		||||
| 
						 | 
				
			
			@ -673,18 +704,42 @@ func (view *viewData) updateTransitionCSS() {
 | 
			
		|||
	updateCSSProperty(view.htmlID(), "transition", view.transitionCSS(view.Session()), view.Session())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (view *viewData) getTransitions() Params {
 | 
			
		||||
	result := Params{}
 | 
			
		||||
	for tag, animation := range view.transitions {
 | 
			
		||||
func (style *viewStyle) Transition(tag string) Animation {
 | 
			
		||||
	if style.transitions != nil {
 | 
			
		||||
		if anim, ok := style.transitions[tag]; ok {
 | 
			
		||||
			return anim
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (style *viewStyle) Transitions() map[string]Animation {
 | 
			
		||||
	result := map[string]Animation{}
 | 
			
		||||
	for tag, animation := range style.transitions {
 | 
			
		||||
		result[tag] = animation
 | 
			
		||||
	}
 | 
			
		||||
	return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (style *viewStyle) SetTransition(tag string, animation Animation) {
 | 
			
		||||
	if animation == nil {
 | 
			
		||||
		delete(style.transitions, tag)
 | 
			
		||||
	} else {
 | 
			
		||||
		style.transitions[tag] = animation
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (view *viewData) SetTransition(tag string, animation Animation) {
 | 
			
		||||
	view.viewStyle.SetTransition(tag, animation)
 | 
			
		||||
	if view.created {
 | 
			
		||||
		updateCSSProperty(view.htmlID(), "transition", view.transitionCSS(view.Session()), view.Session())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetAnimated sets the property with name "tag" of the "rootView" subview with "viewID" id by value. Result:
 | 
			
		||||
// true - success,
 | 
			
		||||
// false - error (incompatible type or invalid format of a string value, see AppLog).
 | 
			
		||||
func SetAnimated(rootView View, viewID, tag string, value interface{}, animation Animation) bool {
 | 
			
		||||
func SetAnimated(rootView View, viewID, tag string, value any, animation Animation) bool {
 | 
			
		||||
	if view := ViewByID(rootView, viewID); view != nil {
 | 
			
		||||
		return view.SetAnimated(tag, value, animation)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -692,58 +747,60 @@ func SetAnimated(rootView View, viewID, tag string, value interface{}, animation
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// IsAnimationPaused returns "true" if an animation of the subview is paused, "false" otherwise.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func IsAnimationPaused(view View, subviewID string) bool {
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func IsAnimationPaused(view View, subviewID ...string) bool {
 | 
			
		||||
	return boolStyledProperty(view, subviewID, AnimationPaused, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetTransitions returns the subview transitions. The result is always non-nil.
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then transitions of the first argument (view) is returned
 | 
			
		||||
func GetTransitions(view View, subviewID ...string) map[string]Animation {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		return view.Transitions()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return map[string]Animation{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetTransition returns the subview property transition. If there is no transition for the given property then nil is returned.
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then transitions of the first argument (view) is returned
 | 
			
		||||
func GetTransition(view View, subviewID, tag string) Animation {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if result, ok := boolStyledProperty(view, AnimationPaused); ok {
 | 
			
		||||
			return result
 | 
			
		||||
		return view.Transition(tag)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddTransition adds the transition for the subview property.
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then the transition is added to the first argument (view)
 | 
			
		||||
func AddTransition(view View, subviewID, tag string, animation Animation) bool {
 | 
			
		||||
	if tag != "" {
 | 
			
		||||
		if subviewID != "" {
 | 
			
		||||
			view = ViewByID(view, subviewID)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if view != nil {
 | 
			
		||||
			view.SetTransition(tag, animation)
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetTransition returns the subview transitions. The result is always non-nil.
 | 
			
		||||
// If the second argument (subviewID) is "" then transitions of the first argument (view) is returned
 | 
			
		||||
func GetTransition(view View, subviewID string) Params {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		return view.getTransitions()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return Params{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddTransition adds the transition for the subview property.
 | 
			
		||||
// If the second argument (subviewID) is "" then the transition is added to the first argument (view)
 | 
			
		||||
func AddTransition(view View, subviewID, tag string, animation Animation) bool {
 | 
			
		||||
	if tag == "" {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if view == nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	transitions := view.getTransitions()
 | 
			
		||||
	transitions[tag] = animation
 | 
			
		||||
	return view.Set(Transition, transitions)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetAnimation returns the subview animations. The result is always non-nil.
 | 
			
		||||
// If the second argument (subviewID) is "" then transitions of the first argument (view) is returned
 | 
			
		||||
func GetAnimation(view View, subviewID string) []Animation {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then transitions of the first argument (view) is returned
 | 
			
		||||
func GetAnimation(view View, subviewID ...string) []Animation {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if view != nil {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -51,131 +51,6 @@ const (
 | 
			
		|||
	AnimationIterationEvent = "animation-iteration-event"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func valueToAnimationListeners(value interface{}) ([]func(View, string), bool) {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		return nil, true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch value := value.(type) {
 | 
			
		||||
	case func(View, string):
 | 
			
		||||
		return []func(View, string){value}, true
 | 
			
		||||
 | 
			
		||||
	case func(string):
 | 
			
		||||
		fn := func(_ View, event string) {
 | 
			
		||||
			value(event)
 | 
			
		||||
		}
 | 
			
		||||
		return []func(View, string){fn}, true
 | 
			
		||||
 | 
			
		||||
	case func(View):
 | 
			
		||||
		fn := func(view View, _ string) {
 | 
			
		||||
			value(view)
 | 
			
		||||
		}
 | 
			
		||||
		return []func(View, string){fn}, true
 | 
			
		||||
 | 
			
		||||
	case func():
 | 
			
		||||
		fn := func(View, string) {
 | 
			
		||||
			value()
 | 
			
		||||
		}
 | 
			
		||||
		return []func(View, string){fn}, true
 | 
			
		||||
 | 
			
		||||
	case []func(View, string):
 | 
			
		||||
		if len(value) == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		for _, fn := range value {
 | 
			
		||||
			if fn == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return value, true
 | 
			
		||||
 | 
			
		||||
	case []func(string):
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		listeners := make([]func(View, string), count)
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
			listeners[i] = func(_ View, event string) {
 | 
			
		||||
				v(event)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners, true
 | 
			
		||||
 | 
			
		||||
	case []func(View):
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		listeners := make([]func(View, string), count)
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
			listeners[i] = func(view View, _ string) {
 | 
			
		||||
				v(view)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners, true
 | 
			
		||||
 | 
			
		||||
	case []func():
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		listeners := make([]func(View, string), count)
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
			listeners[i] = func(View, string) {
 | 
			
		||||
				v()
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners, true
 | 
			
		||||
 | 
			
		||||
	case []interface{}:
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		listeners := make([]func(View, string), count)
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
			switch v := v.(type) {
 | 
			
		||||
			case func(View, string):
 | 
			
		||||
				listeners[i] = v
 | 
			
		||||
 | 
			
		||||
			case func(string):
 | 
			
		||||
				listeners[i] = func(_ View, event string) {
 | 
			
		||||
					v(event)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			case func(View):
 | 
			
		||||
				listeners[i] = func(view View, _ string) {
 | 
			
		||||
					v(view)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			case func():
 | 
			
		||||
				listeners[i] = func(View, string) {
 | 
			
		||||
					v()
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			default:
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners, true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var transitionEvents = map[string]struct{ jsEvent, jsFunc string }{
 | 
			
		||||
	TransitionRunEvent:    {jsEvent: "ontransitionrun", jsFunc: "transitionRunEvent"},
 | 
			
		||||
	TransitionStartEvent:  {jsEvent: "ontransitionstart", jsFunc: "transitionStartEvent"},
 | 
			
		||||
| 
						 | 
				
			
			@ -183,8 +58,8 @@ var transitionEvents = map[string]struct{ jsEvent, jsFunc string }{
 | 
			
		|||
	TransitionCancelEvent: {jsEvent: "ontransitioncancel", jsFunc: "transitionCancelEvent"},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (view *viewData) setTransitionListener(tag string, value interface{}) bool {
 | 
			
		||||
	listeners, ok := valueToAnimationListeners(value)
 | 
			
		||||
func (view *viewData) setTransitionListener(tag string, value any) bool {
 | 
			
		||||
	listeners, ok := valueToEventListeners[View, string](value)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		notCompatibleType(tag, value)
 | 
			
		||||
		return false
 | 
			
		||||
| 
						 | 
				
			
			@ -212,20 +87,6 @@ func (view *viewData) removeTransitionListener(tag string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getAnimationListeners(view View, subviewID string, tag string) []func(View, string) {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if value := view.Get(tag); value != nil {
 | 
			
		||||
			if result, ok := value.([]func(View, string)); ok {
 | 
			
		||||
				return result
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return []func(View, string){}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func transitionEventsHtml(view View, buffer *strings.Builder) {
 | 
			
		||||
	for tag, js := range transitionEvents {
 | 
			
		||||
		if value := view.getRaw(tag); value != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -250,7 +111,7 @@ func (view *viewData) handleTransitionEvents(tag string, data DataObject) {
 | 
			
		|||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, listener := range getAnimationListeners(view, "", tag) {
 | 
			
		||||
		for _, listener := range getEventListeners[View, string](view, nil, tag) {
 | 
			
		||||
			listener(view, property)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -263,8 +124,8 @@ var animationEvents = map[string]struct{ jsEvent, jsFunc string }{
 | 
			
		|||
	AnimationCancelEvent:    {jsEvent: "onanimationcancel", jsFunc: "animationCancelEvent"},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (view *viewData) setAnimationListener(tag string, value interface{}) bool {
 | 
			
		||||
	listeners, ok := valueToAnimationListeners(value)
 | 
			
		||||
func (view *viewData) setAnimationListener(tag string, value any) bool {
 | 
			
		||||
	listeners, ok := valueToEventListeners[View, string](value)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		notCompatibleType(tag, value)
 | 
			
		||||
		return false
 | 
			
		||||
| 
						 | 
				
			
			@ -303,10 +164,10 @@ func animationEventsHtml(view View, buffer *strings.Builder) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (view *viewData) handleAnimationEvents(tag string, data DataObject) {
 | 
			
		||||
	if listeners := getAnimationListeners(view, "", tag); len(listeners) > 0 {
 | 
			
		||||
	if listeners := getEventListeners[View, string](view, nil, tag); len(listeners) > 0 {
 | 
			
		||||
		id := ""
 | 
			
		||||
		if name, ok := data.PropertyValue("name"); ok {
 | 
			
		||||
			for _, animation := range GetAnimation(view, "") {
 | 
			
		||||
			for _, animation := range GetAnimation(view) {
 | 
			
		||||
				if name == animation.animationName() {
 | 
			
		||||
					id, _ = stringProperty(animation, ID, view.Session())
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			@ -320,56 +181,56 @@ func (view *viewData) handleAnimationEvents(tag string, data DataObject) {
 | 
			
		|||
 | 
			
		||||
// GetTransitionRunListeners returns the "transition-run-event" listener list.
 | 
			
		||||
// If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTransitionRunListeners(view View, subviewID string) []func(View, string) {
 | 
			
		||||
	return getAnimationListeners(view, subviewID, TransitionRunEvent)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTransitionRunListeners(view View, subviewID ...string) []func(View, string) {
 | 
			
		||||
	return getEventListeners[View, string](view, subviewID, TransitionRunEvent)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetTransitionStartListeners returns the "transition-start-event" listener list.
 | 
			
		||||
// If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTransitionStartListeners(view View, subviewID string) []func(View, string) {
 | 
			
		||||
	return getAnimationListeners(view, subviewID, TransitionStartEvent)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTransitionStartListeners(view View, subviewID ...string) []func(View, string) {
 | 
			
		||||
	return getEventListeners[View, string](view, subviewID, TransitionStartEvent)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetTransitionEndListeners returns the "transition-end-event" listener list.
 | 
			
		||||
// If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTransitionEndListeners(view View, subviewID string) []func(View, string) {
 | 
			
		||||
	return getAnimationListeners(view, subviewID, TransitionEndEvent)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTransitionEndListeners(view View, subviewID ...string) []func(View, string) {
 | 
			
		||||
	return getEventListeners[View, string](view, subviewID, TransitionEndEvent)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetTransitionCancelListeners returns the "transition-cancel-event" listener list.
 | 
			
		||||
// If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTransitionCancelListeners(view View, subviewID string) []func(View, string) {
 | 
			
		||||
	return getAnimationListeners(view, subviewID, TransitionCancelEvent)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTransitionCancelListeners(view View, subviewID ...string) []func(View, string) {
 | 
			
		||||
	return getEventListeners[View, string](view, subviewID, TransitionCancelEvent)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetAnimationStartListeners returns the "animation-start-event" listener list.
 | 
			
		||||
// If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetAnimationStartListeners(view View, subviewID string) []func(View, string) {
 | 
			
		||||
	return getAnimationListeners(view, subviewID, AnimationStartEvent)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetAnimationStartListeners(view View, subviewID ...string) []func(View, string) {
 | 
			
		||||
	return getEventListeners[View, string](view, subviewID, AnimationStartEvent)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetAnimationEndListeners returns the "animation-end-event" listener list.
 | 
			
		||||
// If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetAnimationEndListeners(view View, subviewID string) []func(View, string) {
 | 
			
		||||
	return getAnimationListeners(view, subviewID, AnimationEndEvent)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetAnimationEndListeners(view View, subviewID ...string) []func(View, string) {
 | 
			
		||||
	return getEventListeners[View, string](view, subviewID, AnimationEndEvent)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetAnimationCancelListeners returns the "animation-cancel-event" listener list.
 | 
			
		||||
// If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetAnimationCancelListeners(view View, subviewID string) []func(View, string) {
 | 
			
		||||
	return getAnimationListeners(view, subviewID, AnimationCancelEvent)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetAnimationCancelListeners(view View, subviewID ...string) []func(View, string) {
 | 
			
		||||
	return getEventListeners[View, string](view, subviewID, AnimationCancelEvent)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetAnimationIterationListeners returns the "animation-iteration-event" listener list.
 | 
			
		||||
// If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetAnimationIterationListeners(view View, subviewID string) []func(View, string) {
 | 
			
		||||
	return getAnimationListeners(view, subviewID, AnimationIterationEvent)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetAnimationIterationListeners(view View, subviewID ...string) []func(View, string) {
 | 
			
		||||
	return getEventListeners[View, string](view, subviewID, AnimationIterationEvent)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -39,7 +39,7 @@ func DebugLog(text string) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// DebugLogF print the text to the debug log
 | 
			
		||||
func DebugLogF(format string, a ...interface{}) {
 | 
			
		||||
func DebugLogF(format string, a ...any) {
 | 
			
		||||
	if debugLogFunc != nil {
 | 
			
		||||
		debugLogFunc(fmt.Sprintf(format, a...))
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -57,7 +57,7 @@ func ErrorLog(text string) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// ErrorLogF print the text to the error log
 | 
			
		||||
func ErrorLogF(format string, a ...interface{}) {
 | 
			
		||||
func ErrorLogF(format string, a ...any) {
 | 
			
		||||
	lastError = fmt.Sprintf(format, a...)
 | 
			
		||||
	if errorLogFunc != nil {
 | 
			
		||||
		errorLogFunc(lastError)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1793,6 +1793,7 @@ function imageLoaded(element, event) {
 | 
			
		|||
		",natural-height=" + element.naturalHeight +
 | 
			
		||||
		",current-src=\"" + element.currentSrc +  "\"}";
 | 
			
		||||
	sendMessage(message);
 | 
			
		||||
	scanElementsSize()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function imageError(element, event) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -59,7 +59,6 @@ ul:focus {
 | 
			
		|||
  outline: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.ruiRoot {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 0px;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,7 +11,7 @@ type audioPlayerData struct {
 | 
			
		|||
// NewAudioPlayer create new MediaPlayer object and return it
 | 
			
		||||
func NewAudioPlayer(session Session, params Params) AudioPlayer {
 | 
			
		||||
	view := new(audioPlayerData)
 | 
			
		||||
	view.Init(session)
 | 
			
		||||
	view.init(session)
 | 
			
		||||
	view.tag = "AudioPlayer"
 | 
			
		||||
	setInitParams(view, params)
 | 
			
		||||
	return view
 | 
			
		||||
| 
						 | 
				
			
			@ -21,8 +21,8 @@ func newAudioPlayer(session Session) View {
 | 
			
		|||
	return NewAudioPlayer(session, nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (player *audioPlayerData) Init(session Session) {
 | 
			
		||||
	player.mediaPlayerData.Init(session)
 | 
			
		||||
func (player *audioPlayerData) init(session Session) {
 | 
			
		||||
	player.mediaPlayerData.init(session)
 | 
			
		||||
	player.tag = "AudioPlayer"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -81,22 +81,22 @@ func createBackground(obj DataObject) BackgroundElement {
 | 
			
		|||
	switch obj.Tag() {
 | 
			
		||||
	case "image":
 | 
			
		||||
		image := new(backgroundImage)
 | 
			
		||||
		image.properties = map[string]interface{}{}
 | 
			
		||||
		image.properties = map[string]any{}
 | 
			
		||||
		result = image
 | 
			
		||||
 | 
			
		||||
	case "linear-gradient":
 | 
			
		||||
		gradient := new(backgroundLinearGradient)
 | 
			
		||||
		gradient.properties = map[string]interface{}{}
 | 
			
		||||
		gradient.properties = map[string]any{}
 | 
			
		||||
		result = gradient
 | 
			
		||||
 | 
			
		||||
	case "radial-gradient":
 | 
			
		||||
		gradient := new(backgroundRadialGradient)
 | 
			
		||||
		gradient.properties = map[string]interface{}{}
 | 
			
		||||
		gradient.properties = map[string]any{}
 | 
			
		||||
		result = gradient
 | 
			
		||||
 | 
			
		||||
	case "conic-gradient":
 | 
			
		||||
		gradient := new(backgroundConicGradient)
 | 
			
		||||
		gradient.properties = map[string]interface{}{}
 | 
			
		||||
		gradient.properties = map[string]any{}
 | 
			
		||||
		result = gradient
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
| 
						 | 
				
			
			@ -118,7 +118,7 @@ func createBackground(obj DataObject) BackgroundElement {
 | 
			
		|||
// NewBackgroundImage creates the new background image
 | 
			
		||||
func NewBackgroundImage(params Params) BackgroundElement {
 | 
			
		||||
	result := new(backgroundImage)
 | 
			
		||||
	result.properties = map[string]interface{}{}
 | 
			
		||||
	result.properties = map[string]any{}
 | 
			
		||||
	for tag, value := range params {
 | 
			
		||||
		result.Set(tag, value)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -156,7 +156,7 @@ func (image *backgroundImage) normalizeTag(tag string) string {
 | 
			
		|||
	return tag
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (image *backgroundImage) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (image *backgroundImage) Set(tag string, value any) bool {
 | 
			
		||||
	tag = image.normalizeTag(tag)
 | 
			
		||||
	switch tag {
 | 
			
		||||
	case Attachment, Width, Height, Repeat, ImageHorizontalAlign, ImageVerticalAlign,
 | 
			
		||||
| 
						 | 
				
			
			@ -167,7 +167,7 @@ func (image *backgroundImage) Set(tag string, value interface{}) bool {
 | 
			
		|||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (image *backgroundImage) Get(tag string) interface{} {
 | 
			
		||||
func (image *backgroundImage) Get(tag string) any {
 | 
			
		||||
	return image.backgroundElement.Get(image.normalizeTag(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -219,9 +219,9 @@ func (image *backgroundImage) cssStyle(session Session) string {
 | 
			
		|||
 | 
			
		||||
			if width.Type != Auto || height.Type != Auto {
 | 
			
		||||
				buffer.WriteString(` / `)
 | 
			
		||||
				buffer.WriteString(width.cssString("auto"))
 | 
			
		||||
				buffer.WriteString(width.cssString("auto", session))
 | 
			
		||||
				buffer.WriteRune(' ')
 | 
			
		||||
				buffer.WriteString(height.cssString("auto"))
 | 
			
		||||
				buffer.WriteString(height.cssString("auto", session))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,16 +12,16 @@ type backgroundConicGradient struct {
 | 
			
		|||
type BackgroundGradientAngle struct {
 | 
			
		||||
	// Color - the color of the key angle. Must not be nil.
 | 
			
		||||
	// Can take a value of Color type or string (color constant or textual description of the color)
 | 
			
		||||
	Color interface{}
 | 
			
		||||
	Color any
 | 
			
		||||
	// Angle - the key angle. Optional (may be nil).
 | 
			
		||||
	// Can take a value of AngleUnit type or string (angle constant or textual description of the angle)
 | 
			
		||||
	Angle interface{}
 | 
			
		||||
	Angle any
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewBackgroundConicGradient creates the new background conic gradient
 | 
			
		||||
func NewBackgroundConicGradient(params Params) BackgroundElement {
 | 
			
		||||
	result := new(backgroundConicGradient)
 | 
			
		||||
	result.properties = map[string]interface{}{}
 | 
			
		||||
	result.properties = map[string]any{}
 | 
			
		||||
	for tag, value := range params {
 | 
			
		||||
		result.Set(tag, value)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -139,7 +139,7 @@ func (gradient *backgroundConicGradient) normalizeTag(tag string) string {
 | 
			
		|||
	return tag
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gradient *backgroundConicGradient) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (gradient *backgroundConicGradient) Set(tag string, value any) bool {
 | 
			
		||||
	tag = gradient.normalizeTag(tag)
 | 
			
		||||
	switch tag {
 | 
			
		||||
	case CenterX, CenterY, Repeating, From:
 | 
			
		||||
| 
						 | 
				
			
			@ -153,7 +153,7 @@ func (gradient *backgroundConicGradient) Set(tag string, value interface{}) bool
 | 
			
		|||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gradient *backgroundConicGradient) stringToAngle(text string) (interface{}, bool) {
 | 
			
		||||
func (gradient *backgroundConicGradient) stringToAngle(text string) (any, bool) {
 | 
			
		||||
	if text == "" {
 | 
			
		||||
		return nil, false
 | 
			
		||||
	} else if text[0] == '@' {
 | 
			
		||||
| 
						 | 
				
			
			@ -216,7 +216,7 @@ func (gradient *backgroundConicGradient) parseGradientText(value string) []Backg
 | 
			
		|||
	return vector
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gradient *backgroundConicGradient) setGradient(value interface{}) bool {
 | 
			
		||||
func (gradient *backgroundConicGradient) setGradient(value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		delete(gradient.properties, Gradient)
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			@ -262,7 +262,7 @@ func (gradient *backgroundConicGradient) setGradient(value interface{}) bool {
 | 
			
		|||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gradient *backgroundConicGradient) Get(tag string) interface{} {
 | 
			
		||||
func (gradient *backgroundConicGradient) Get(tag string) any {
 | 
			
		||||
	return gradient.backgroundElement.Get(gradient.normalizeTag(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -316,9 +316,9 @@ func (gradient *backgroundConicGradient) cssStyle(session Session) string {
 | 
			
		|||
			buffer.WriteRune(' ')
 | 
			
		||||
		}
 | 
			
		||||
		buffer.WriteString("at ")
 | 
			
		||||
		buffer.WriteString(x.cssString("50%"))
 | 
			
		||||
		buffer.WriteString(x.cssString("50%", session))
 | 
			
		||||
		buffer.WriteString(" ")
 | 
			
		||||
		buffer.WriteString(y.cssString("50%"))
 | 
			
		||||
		buffer.WriteString(y.cssString("50%", session))
 | 
			
		||||
		comma = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,10 +50,10 @@ const (
 | 
			
		|||
type BackgroundGradientPoint struct {
 | 
			
		||||
	// Color - the color of the point. Must not be nil.
 | 
			
		||||
	// Can take a value of Color type or string (color constant or textual description of the color)
 | 
			
		||||
	Color interface{}
 | 
			
		||||
	Color any
 | 
			
		||||
	// Pos - the distance from the start of the gradient straight line. Optional (may be nil).
 | 
			
		||||
	// Can take a value of SizeUnit type or string (angle constant or textual description of the SizeUnit)
 | 
			
		||||
	Pos interface{}
 | 
			
		||||
	Pos any
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type backgroundGradient struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -71,7 +71,7 @@ type backgroundRadialGradient struct {
 | 
			
		|||
// NewBackgroundLinearGradient creates the new background linear gradient
 | 
			
		||||
func NewBackgroundLinearGradient(params Params) BackgroundElement {
 | 
			
		||||
	result := new(backgroundLinearGradient)
 | 
			
		||||
	result.properties = map[string]interface{}{}
 | 
			
		||||
	result.properties = map[string]any{}
 | 
			
		||||
	for tag, value := range params {
 | 
			
		||||
		result.Set(tag, value)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -81,7 +81,7 @@ func NewBackgroundLinearGradient(params Params) BackgroundElement {
 | 
			
		|||
// NewBackgroundRadialGradient creates the new background radial gradient
 | 
			
		||||
func NewBackgroundRadialGradient(params Params) BackgroundElement {
 | 
			
		||||
	result := new(backgroundRadialGradient)
 | 
			
		||||
	result.properties = map[string]interface{}{}
 | 
			
		||||
	result.properties = map[string]any{}
 | 
			
		||||
	for tag, value := range params {
 | 
			
		||||
		result.Set(tag, value)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -106,7 +106,7 @@ func (gradient *backgroundGradient) parseGradientText(value string) []Background
 | 
			
		|||
	return points
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gradient *backgroundGradient) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (gradient *backgroundGradient) Set(tag string, value any) bool {
 | 
			
		||||
 | 
			
		||||
	switch tag = strings.ToLower(tag); tag {
 | 
			
		||||
	case Repeating:
 | 
			
		||||
| 
						 | 
				
			
			@ -261,19 +261,18 @@ func (gradient *backgroundGradient) writeGradient(session Session, buffer *strin
 | 
			
		|||
				switch value := point.Pos.(type) {
 | 
			
		||||
				case string:
 | 
			
		||||
					if value != "" {
 | 
			
		||||
						if value[0] == '@' {
 | 
			
		||||
							value, _ = session.Constant(value[1:])
 | 
			
		||||
						}
 | 
			
		||||
						if pos, ok := StringToSizeUnit(value); ok && pos.Type != Auto {
 | 
			
		||||
							buffer.WriteRune(' ')
 | 
			
		||||
							buffer.WriteString(pos.cssString(""))
 | 
			
		||||
						if value, ok := session.resolveConstants(value); ok {
 | 
			
		||||
							if pos, ok := StringToSizeUnit(value); ok && pos.Type != Auto {
 | 
			
		||||
								buffer.WriteRune(' ')
 | 
			
		||||
								buffer.WriteString(pos.cssString("", session))
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
				case SizeUnit:
 | 
			
		||||
					if value.Type != Auto {
 | 
			
		||||
						buffer.WriteRune(' ')
 | 
			
		||||
						buffer.WriteString(value.cssString(""))
 | 
			
		||||
						buffer.WriteString(value.cssString("", session))
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -296,7 +295,7 @@ func (image *backgroundLinearGradient) Clone() BackgroundElement {
 | 
			
		|||
	return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gradient *backgroundLinearGradient) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (gradient *backgroundLinearGradient) Set(tag string, value any) bool {
 | 
			
		||||
	if strings.ToLower(tag) == Direction {
 | 
			
		||||
		switch value := value.(type) {
 | 
			
		||||
		case AngleUnit:
 | 
			
		||||
| 
						 | 
				
			
			@ -402,7 +401,7 @@ func (gradient *backgroundRadialGradient) normalizeTag(tag string) string {
 | 
			
		|||
	return tag
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gradient *backgroundRadialGradient) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (gradient *backgroundRadialGradient) Set(tag string, value any) bool {
 | 
			
		||||
	tag = gradient.normalizeTag(tag)
 | 
			
		||||
	switch tag {
 | 
			
		||||
	case RadialGradientRadius:
 | 
			
		||||
| 
						 | 
				
			
			@ -426,7 +425,7 @@ func (gradient *backgroundRadialGradient) Set(tag string, value interface{}) boo
 | 
			
		|||
				return true
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		case []interface{}:
 | 
			
		||||
		case []any:
 | 
			
		||||
			switch len(value) {
 | 
			
		||||
			case 0:
 | 
			
		||||
				delete(gradient.properties, RadialGradientRadius)
 | 
			
		||||
| 
						 | 
				
			
			@ -477,7 +476,7 @@ func (gradient *backgroundRadialGradient) Set(tag string, value interface{}) boo
 | 
			
		|||
	return gradient.backgroundGradient.Set(tag, value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gradient *backgroundRadialGradient) Get(tag string) interface{} {
 | 
			
		||||
func (gradient *backgroundRadialGradient) Get(tag string) any {
 | 
			
		||||
	return gradient.backgroundGradient.Get(gradient.normalizeTag(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -512,9 +511,9 @@ func (gradient *backgroundRadialGradient) cssStyle(session Session) string {
 | 
			
		|||
					if r, ok := StringToSizeUnit(text); ok && r.Type != Auto {
 | 
			
		||||
						buffer.WriteString("ellipse ")
 | 
			
		||||
						shapeText = ""
 | 
			
		||||
						buffer.WriteString(r.cssString(""))
 | 
			
		||||
						buffer.WriteString(r.cssString("", session))
 | 
			
		||||
						buffer.WriteString(" ")
 | 
			
		||||
						buffer.WriteString(r.cssString(""))
 | 
			
		||||
						buffer.WriteString(r.cssString("", session))
 | 
			
		||||
						buffer.WriteString(" ")
 | 
			
		||||
					} else {
 | 
			
		||||
						ErrorLog(`Invalid radial gradient radius: ` + text)
 | 
			
		||||
| 
						 | 
				
			
			@ -539,9 +538,9 @@ func (gradient *backgroundRadialGradient) cssStyle(session Session) string {
 | 
			
		|||
			if value.Type != Auto {
 | 
			
		||||
				buffer.WriteString("ellipse ")
 | 
			
		||||
				shapeText = ""
 | 
			
		||||
				buffer.WriteString(value.cssString(""))
 | 
			
		||||
				buffer.WriteString(value.cssString("", session))
 | 
			
		||||
				buffer.WriteString(" ")
 | 
			
		||||
				buffer.WriteString(value.cssString(""))
 | 
			
		||||
				buffer.WriteString(value.cssString("", session))
 | 
			
		||||
				buffer.WriteString(" ")
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -553,11 +552,11 @@ func (gradient *backgroundRadialGradient) cssStyle(session Session) string {
 | 
			
		|||
			buffer.WriteString("ellipse ")
 | 
			
		||||
			shapeText = ""
 | 
			
		||||
			for i := 0; i < count; i++ {
 | 
			
		||||
				buffer.WriteString(value[i].cssString("50%"))
 | 
			
		||||
				buffer.WriteString(value[i].cssString("50%", session))
 | 
			
		||||
				buffer.WriteString(" ")
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		case []interface{}:
 | 
			
		||||
		case []any:
 | 
			
		||||
			count := len(value)
 | 
			
		||||
			if count > 2 {
 | 
			
		||||
				count = 2
 | 
			
		||||
| 
						 | 
				
			
			@ -568,13 +567,13 @@ func (gradient *backgroundRadialGradient) cssStyle(session Session) string {
 | 
			
		|||
				if value[i] != nil {
 | 
			
		||||
					switch value := value[i].(type) {
 | 
			
		||||
					case SizeUnit:
 | 
			
		||||
						buffer.WriteString(value.cssString("50%"))
 | 
			
		||||
						buffer.WriteString(value.cssString("50%", session))
 | 
			
		||||
						buffer.WriteString(" ")
 | 
			
		||||
 | 
			
		||||
					case string:
 | 
			
		||||
						if text, ok := session.resolveConstants(value); ok {
 | 
			
		||||
							if size, err := stringToSizeUnit(text); err == nil {
 | 
			
		||||
								buffer.WriteString(size.cssString("50%"))
 | 
			
		||||
								buffer.WriteString(size.cssString("50%", session))
 | 
			
		||||
								buffer.WriteString(" ")
 | 
			
		||||
							} else {
 | 
			
		||||
								buffer.WriteString("50% ")
 | 
			
		||||
| 
						 | 
				
			
			@ -597,9 +596,9 @@ func (gradient *backgroundRadialGradient) cssStyle(session Session) string {
 | 
			
		|||
			buffer.WriteString(shapeText)
 | 
			
		||||
		}
 | 
			
		||||
		buffer.WriteString("at ")
 | 
			
		||||
		buffer.WriteString(x.cssString("50%"))
 | 
			
		||||
		buffer.WriteString(x.cssString("50%", session))
 | 
			
		||||
		buffer.WriteString(" ")
 | 
			
		||||
		buffer.WriteString(y.cssString("50%"))
 | 
			
		||||
		buffer.WriteString(y.cssString("50%", session))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	buffer.WriteString(", ")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										21
									
								
								border.go
								
								
								
								
							
							
						
						
									
										21
									
								
								border.go
								
								
								
								
							| 
						 | 
				
			
			@ -64,9 +64,9 @@ type borderProperty struct {
 | 
			
		|||
	propertyList
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newBorderProperty(value interface{}) BorderProperty {
 | 
			
		||||
func newBorderProperty(value any) BorderProperty {
 | 
			
		||||
	border := new(borderProperty)
 | 
			
		||||
	border.properties = map[string]interface{}{}
 | 
			
		||||
	border.properties = map[string]any{}
 | 
			
		||||
 | 
			
		||||
	if value != nil {
 | 
			
		||||
		switch value := value.(type) {
 | 
			
		||||
| 
						 | 
				
			
			@ -131,7 +131,7 @@ func newBorderProperty(value interface{}) BorderProperty {
 | 
			
		|||
// NewBorder creates the new BorderProperty
 | 
			
		||||
func NewBorder(params Params) BorderProperty {
 | 
			
		||||
	border := new(borderProperty)
 | 
			
		||||
	border.properties = map[string]interface{}{}
 | 
			
		||||
	border.properties = map[string]any{}
 | 
			
		||||
	if params != nil {
 | 
			
		||||
		for _, tag := range []string{Style, Width, ColorTag, Left, Right, Top, Bottom,
 | 
			
		||||
			LeftStyle, RightStyle, TopStyle, BottomStyle,
 | 
			
		||||
| 
						 | 
				
			
			@ -213,7 +213,7 @@ func (border *borderProperty) writeString(buffer *strings.Builder, indent string
 | 
			
		|||
	buffer.WriteString("_{ ")
 | 
			
		||||
	comma := false
 | 
			
		||||
 | 
			
		||||
	write := func(tag string, value interface{}) {
 | 
			
		||||
	write := func(tag string, value any) {
 | 
			
		||||
		if comma {
 | 
			
		||||
			buffer.WriteString(", ")
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -430,7 +430,7 @@ func (border *borderProperty) Remove(tag string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (border *borderProperty) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (border *borderProperty) Set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		border.Remove(tag)
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			@ -512,7 +512,7 @@ func (border *borderProperty) Set(tag string, value interface{}) bool {
 | 
			
		|||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (border *borderProperty) Get(tag string) interface{} {
 | 
			
		||||
func (border *borderProperty) Get(tag string) any {
 | 
			
		||||
	tag = border.normalizeTag(tag)
 | 
			
		||||
 | 
			
		||||
	if result, ok := border.properties[tag]; ok {
 | 
			
		||||
| 
						 | 
				
			
			@ -658,11 +658,14 @@ func (border *borderProperty) cssWidth(builder cssBuilder, session Session) {
 | 
			
		|||
		borders.Top.Width == borders.Left.Width &&
 | 
			
		||||
		borders.Top.Width == borders.Bottom.Width {
 | 
			
		||||
		if borders.Top.Width.Type != Auto {
 | 
			
		||||
			builder.add("border-width", borders.Top.Width.cssString("0"))
 | 
			
		||||
			builder.add("border-width", borders.Top.Width.cssString("0", session))
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		builder.addValues("border-width", " ", borders.Top.Width.cssString("0"),
 | 
			
		||||
			borders.Right.Width.cssString("0"), borders.Bottom.Width.cssString("0"), borders.Left.Width.cssString("0"))
 | 
			
		||||
		builder.addValues("border-width", " ",
 | 
			
		||||
			borders.Top.Width.cssString("0", session),
 | 
			
		||||
			borders.Right.Width.cssString("0", session),
 | 
			
		||||
			borders.Bottom.Width.cssString("0", session),
 | 
			
		||||
			borders.Left.Width.cssString("0", session))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										39
									
								
								bounds.go
								
								
								
								
							
							
						
						
									
										39
									
								
								bounds.go
								
								
								
								
							| 
						 | 
				
			
			@ -20,7 +20,7 @@ type boundsPropertyData struct {
 | 
			
		|||
// NewBoundsProperty creates the new BoundsProperty object
 | 
			
		||||
func NewBoundsProperty(params Params) BoundsProperty {
 | 
			
		||||
	bounds := new(boundsPropertyData)
 | 
			
		||||
	bounds.properties = map[string]interface{}{}
 | 
			
		||||
	bounds.properties = map[string]any{}
 | 
			
		||||
	if params != nil {
 | 
			
		||||
		for _, tag := range []string{Top, Right, Bottom, Left} {
 | 
			
		||||
			if value, ok := params[tag]; ok {
 | 
			
		||||
| 
						 | 
				
			
			@ -79,7 +79,7 @@ func (bounds *boundsPropertyData) Remove(tag string) {
 | 
			
		|||
	bounds.propertyList.Remove(bounds.normalizeTag(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bounds *boundsPropertyData) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (bounds *boundsPropertyData) Set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		bounds.Remove(tag)
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			@ -98,7 +98,7 @@ func (bounds *boundsPropertyData) Set(tag string, value interface{}) bool {
 | 
			
		|||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bounds *boundsPropertyData) Get(tag string) interface{} {
 | 
			
		||||
func (bounds *boundsPropertyData) Get(tag string) any {
 | 
			
		||||
	tag = bounds.normalizeTag(tag)
 | 
			
		||||
	if value, ok := bounds.properties[tag]; ok {
 | 
			
		||||
		return value
 | 
			
		||||
| 
						 | 
				
			
			@ -213,22 +213,25 @@ func (bounds *Bounds) String() string {
 | 
			
		|||
		bounds.Bottom.String() + "," + bounds.Left.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bounds *Bounds) cssValue(tag string, builder cssBuilder) {
 | 
			
		||||
func (bounds *Bounds) cssValue(tag string, builder cssBuilder, session Session) {
 | 
			
		||||
	if bounds.allFieldsEqual() {
 | 
			
		||||
		builder.add(tag, bounds.Top.cssString("0"))
 | 
			
		||||
		builder.add(tag, bounds.Top.cssString("0", session))
 | 
			
		||||
	} else {
 | 
			
		||||
		builder.addValues(tag, " ", bounds.Top.cssString("0"), bounds.Right.cssString("0"),
 | 
			
		||||
			bounds.Bottom.cssString("0"), bounds.Left.cssString("0"))
 | 
			
		||||
		builder.addValues(tag, " ",
 | 
			
		||||
			bounds.Top.cssString("0", session),
 | 
			
		||||
			bounds.Right.cssString("0", session),
 | 
			
		||||
			bounds.Bottom.cssString("0", session),
 | 
			
		||||
			bounds.Left.cssString("0", session))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bounds *Bounds) cssString() string {
 | 
			
		||||
func (bounds *Bounds) cssString(session Session) string {
 | 
			
		||||
	var builder cssValueBuilder
 | 
			
		||||
	bounds.cssValue("", &builder)
 | 
			
		||||
	bounds.cssValue("", &builder, session)
 | 
			
		||||
	return builder.finish()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (properties *propertyList) setBounds(tag string, value interface{}) bool {
 | 
			
		||||
func (properties *propertyList) setBounds(tag string, value any) bool {
 | 
			
		||||
	if !properties.setSimpleProperty(tag, value) {
 | 
			
		||||
		switch value := value.(type) {
 | 
			
		||||
		case string:
 | 
			
		||||
| 
						 | 
				
			
			@ -260,6 +263,12 @@ func (properties *propertyList) setBounds(tag string, value interface{}) bool {
 | 
			
		|||
		case SizeUnit:
 | 
			
		||||
			properties.properties[tag] = value
 | 
			
		||||
 | 
			
		||||
		case float32:
 | 
			
		||||
			properties.properties[tag] = Px(float64(value))
 | 
			
		||||
 | 
			
		||||
		case float64:
 | 
			
		||||
			properties.properties[tag] = Px(value)
 | 
			
		||||
 | 
			
		||||
		case Bounds:
 | 
			
		||||
			bounds := NewBoundsProperty(nil)
 | 
			
		||||
			if value.Top.Type != Auto {
 | 
			
		||||
| 
						 | 
				
			
			@ -292,8 +301,12 @@ func (properties *propertyList) setBounds(tag string, value interface{}) bool {
 | 
			
		|||
			properties.properties[tag] = bounds
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
			notCompatibleType(tag, value)
 | 
			
		||||
			return false
 | 
			
		||||
			if n, ok := isInt(value); ok {
 | 
			
		||||
				properties.properties[tag] = Px(float64(n))
 | 
			
		||||
			} else {
 | 
			
		||||
				notCompatibleType(tag, value)
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -340,7 +353,7 @@ func (properties *propertyList) removeBoundsSide(mainTag, sideTag string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (properties *propertyList) setBoundsSide(mainTag, sideTag string, value interface{}) bool {
 | 
			
		||||
func (properties *propertyList) setBoundsSide(mainTag, sideTag string, value any) bool {
 | 
			
		||||
	bounds := properties.boundsProperty(mainTag)
 | 
			
		||||
	if bounds.Set(sideTag, value) {
 | 
			
		||||
		properties.properties[mainTag] = bounds
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -592,7 +592,7 @@ func (canvas *canvasData) writeFont(name string, script *strings.Builder) {
 | 
			
		|||
 | 
			
		||||
func (canvas *canvasData) SetFont(name string, size SizeUnit) {
 | 
			
		||||
	canvas.script.WriteString("\nctx.font = '")
 | 
			
		||||
	canvas.script.WriteString(size.cssString("1em"))
 | 
			
		||||
	canvas.script.WriteString(size.cssString("1rem", canvas.View().Session()))
 | 
			
		||||
	canvas.writeFont(name, &canvas.script)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -616,7 +616,7 @@ func (canvas *canvasData) setFontWithParams(name string, size SizeUnit, params F
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	script.WriteString(size.cssString("1em"))
 | 
			
		||||
	script.WriteString(size.cssString("1rem", canvas.View().Session()))
 | 
			
		||||
	switch params.LineHeight.Type {
 | 
			
		||||
	case Auto:
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -634,7 +634,7 @@ func (canvas *canvasData) setFontWithParams(name string, size SizeUnit, params F
 | 
			
		|||
 | 
			
		||||
	default:
 | 
			
		||||
		script.WriteString("/")
 | 
			
		||||
		script.WriteString(params.LineHeight.cssString(""))
 | 
			
		||||
		script.WriteString(params.LineHeight.cssString("", canvas.View().Session()))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	canvas.writeFont(name, script)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,7 +21,7 @@ type canvasViewData struct {
 | 
			
		|||
// NewCanvasView creates the new custom draw view
 | 
			
		||||
func NewCanvasView(session Session, params Params) CanvasView {
 | 
			
		||||
	view := new(canvasViewData)
 | 
			
		||||
	view.Init(session)
 | 
			
		||||
	view.init(session)
 | 
			
		||||
	setInitParams(view, params)
 | 
			
		||||
	return view
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -31,8 +31,8 @@ func newCanvasView(session Session) View {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Init initialize fields of ViewsContainer by default values
 | 
			
		||||
func (canvasView *canvasViewData) Init(session Session) {
 | 
			
		||||
	canvasView.viewData.Init(session)
 | 
			
		||||
func (canvasView *canvasViewData) init(session Session) {
 | 
			
		||||
	canvasView.viewData.init(session)
 | 
			
		||||
	canvasView.tag = "CanvasView"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -63,11 +63,11 @@ func (canvasView *canvasViewData) remove(tag string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (canvasView *canvasViewData) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (canvasView *canvasViewData) Set(tag string, value any) bool {
 | 
			
		||||
	return canvasView.set(canvasView.normalizeTag(tag), value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (canvasView *canvasViewData) set(tag string, value interface{}) bool {
 | 
			
		||||
func (canvasView *canvasViewData) set(tag string, value any) bool {
 | 
			
		||||
	if tag == DrawFunction {
 | 
			
		||||
		if value == nil {
 | 
			
		||||
			canvasView.drawer = nil
 | 
			
		||||
| 
						 | 
				
			
			@ -85,11 +85,11 @@ func (canvasView *canvasViewData) set(tag string, value interface{}) bool {
 | 
			
		|||
	return canvasView.viewData.set(tag, value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (canvasView *canvasViewData) Get(tag string) interface{} {
 | 
			
		||||
func (canvasView *canvasViewData) Get(tag string) any {
 | 
			
		||||
	return canvasView.get(canvasView.normalizeTag(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (canvasView *canvasViewData) get(tag string) interface{} {
 | 
			
		||||
func (canvasView *canvasViewData) get(tag string) any {
 | 
			
		||||
	if tag == DrawFunction {
 | 
			
		||||
		return canvasView.drawer
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										122
									
								
								checkbox.go
								
								
								
								
							
							
						
						
									
										122
									
								
								checkbox.go
								
								
								
								
							| 
						 | 
				
			
			@ -23,7 +23,7 @@ type checkboxData struct {
 | 
			
		|||
// NewCheckbox create new Checkbox object and return it
 | 
			
		||||
func NewCheckbox(session Session, params Params) Checkbox {
 | 
			
		||||
	view := new(checkboxData)
 | 
			
		||||
	view.Init(session)
 | 
			
		||||
	view.init(session)
 | 
			
		||||
	setInitParams(view, Params{
 | 
			
		||||
		ClickEvent:   checkboxClickListener,
 | 
			
		||||
		KeyDownEvent: checkboxKeyListener,
 | 
			
		||||
| 
						 | 
				
			
			@ -36,8 +36,8 @@ func newCheckbox(session Session) View {
 | 
			
		|||
	return NewCheckbox(session, nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (button *checkboxData) Init(session Session) {
 | 
			
		||||
	button.viewsContainerData.Init(session)
 | 
			
		||||
func (button *checkboxData) init(session Session) {
 | 
			
		||||
	button.viewsContainerData.init(session)
 | 
			
		||||
	button.tag = "Checkbox"
 | 
			
		||||
	button.systemClass = "ruiGridLayout ruiCheckbox"
 | 
			
		||||
	button.checkedListeners = []func(Checkbox, bool){}
 | 
			
		||||
| 
						 | 
				
			
			@ -51,7 +51,7 @@ func (button *checkboxData) Focusable() bool {
 | 
			
		|||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (button *checkboxData) Get(tag string) interface{} {
 | 
			
		||||
func (button *checkboxData) Get(tag string) any {
 | 
			
		||||
	switch strings.ToLower(tag) {
 | 
			
		||||
	case CheckboxChangedEvent:
 | 
			
		||||
		return button.checkedListeners
 | 
			
		||||
| 
						 | 
				
			
			@ -60,11 +60,11 @@ func (button *checkboxData) Get(tag string) interface{} {
 | 
			
		|||
	return button.viewsContainerData.Get(tag)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (button *checkboxData) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (button *checkboxData) Set(tag string, value any) bool {
 | 
			
		||||
	return button.set(tag, value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (button *checkboxData) set(tag string, value interface{}) bool {
 | 
			
		||||
func (button *checkboxData) set(tag string, value any) bool {
 | 
			
		||||
	switch tag {
 | 
			
		||||
	case CheckboxChangedEvent:
 | 
			
		||||
		if !button.setChangedListener(value) {
 | 
			
		||||
| 
						 | 
				
			
			@ -194,80 +194,32 @@ func (button *checkboxData) changedCheckboxState(state bool) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func checkboxClickListener(view View) {
 | 
			
		||||
	view.Set(Checked, !IsCheckboxChecked(view, ""))
 | 
			
		||||
	view.Set(Checked, !IsCheckboxChecked(view))
 | 
			
		||||
	BlurView(view)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func checkboxKeyListener(view View, event KeyEvent) {
 | 
			
		||||
	switch event.Code {
 | 
			
		||||
	case "Enter", "Space":
 | 
			
		||||
		view.Set(Checked, !IsCheckboxChecked(view, ""))
 | 
			
		||||
		view.Set(Checked, !IsCheckboxChecked(view))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (button *checkboxData) setChangedListener(value interface{}) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		if len(button.checkedListeners) > 0 {
 | 
			
		||||
			button.checkedListeners = []func(Checkbox, bool){}
 | 
			
		||||
		}
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch value := value.(type) {
 | 
			
		||||
	case func(Checkbox, bool):
 | 
			
		||||
		button.checkedListeners = []func(Checkbox, bool){value}
 | 
			
		||||
 | 
			
		||||
	case func(bool):
 | 
			
		||||
		fn := func(_ Checkbox, checked bool) {
 | 
			
		||||
			value(checked)
 | 
			
		||||
		}
 | 
			
		||||
		button.checkedListeners = []func(Checkbox, bool){fn}
 | 
			
		||||
 | 
			
		||||
	case []func(Checkbox, bool):
 | 
			
		||||
		button.checkedListeners = value
 | 
			
		||||
 | 
			
		||||
	case []func(bool):
 | 
			
		||||
		listeners := make([]func(Checkbox, bool), len(value))
 | 
			
		||||
		for i, val := range value {
 | 
			
		||||
			if val == nil {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			listeners[i] = func(_ Checkbox, checked bool) {
 | 
			
		||||
				val(checked)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		button.checkedListeners = listeners
 | 
			
		||||
 | 
			
		||||
	case []interface{}:
 | 
			
		||||
		listeners := make([]func(Checkbox, bool), len(value))
 | 
			
		||||
		for i, val := range value {
 | 
			
		||||
			if val == nil {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			switch val := val.(type) {
 | 
			
		||||
			case func(Checkbox, bool):
 | 
			
		||||
				listeners[i] = val
 | 
			
		||||
 | 
			
		||||
			case func(bool):
 | 
			
		||||
				listeners[i] = func(_ Checkbox, date bool) {
 | 
			
		||||
					val(date)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			default:
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		button.checkedListeners = listeners
 | 
			
		||||
func (button *checkboxData) setChangedListener(value any) bool {
 | 
			
		||||
	listeners, ok := valueToEventListeners[Checkbox, bool](value)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return false
 | 
			
		||||
	} else if listeners == nil {
 | 
			
		||||
		listeners = []func(Checkbox, bool){}
 | 
			
		||||
	}
 | 
			
		||||
	button.checkedListeners = listeners
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (button *checkboxData) cssStyle(self View, builder cssBuilder) {
 | 
			
		||||
	session := button.Session()
 | 
			
		||||
	vAlign, _ := enumStyledProperty(button, CheckboxVerticalAlign, LeftAlign)
 | 
			
		||||
	hAlign, _ := enumStyledProperty(button, CheckboxHorizontalAlign, TopAlign)
 | 
			
		||||
	vAlign := GetCheckboxVerticalAlign(button)
 | 
			
		||||
	hAlign := GetCheckboxHorizontalAlign(button)
 | 
			
		||||
	switch hAlign {
 | 
			
		||||
	case CenterAlign:
 | 
			
		||||
		if vAlign == BottomAlign {
 | 
			
		||||
| 
						 | 
				
			
			@ -284,7 +236,7 @@ func (button *checkboxData) cssStyle(self View, builder cssBuilder) {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	if gap, ok := sizeConstant(session, "ruiCheckboxGap"); ok && gap.Type != Auto && gap.Value > 0 {
 | 
			
		||||
		builder.add("gap", gap.cssString("0"))
 | 
			
		||||
		builder.add("gap", gap.cssString("0", session))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	builder.add("align-items", "stretch")
 | 
			
		||||
| 
						 | 
				
			
			@ -294,8 +246,8 @@ func (button *checkboxData) cssStyle(self View, builder cssBuilder) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (button *checkboxData) htmlCheckbox(buffer *strings.Builder, checked bool) (int, int) {
 | 
			
		||||
	vAlign, _ := enumStyledProperty(button, CheckboxVerticalAlign, LeftAlign)
 | 
			
		||||
	hAlign, _ := enumStyledProperty(button, CheckboxHorizontalAlign, TopAlign)
 | 
			
		||||
	vAlign := GetCheckboxVerticalAlign(button)
 | 
			
		||||
	hAlign := GetCheckboxHorizontalAlign(button)
 | 
			
		||||
 | 
			
		||||
	buffer.WriteString(`<div id="`)
 | 
			
		||||
	buffer.WriteString(button.htmlID())
 | 
			
		||||
| 
						 | 
				
			
			@ -339,7 +291,7 @@ func (button *checkboxData) htmlCheckbox(buffer *strings.Builder, checked bool)
 | 
			
		|||
 | 
			
		||||
func (button *checkboxData) htmlSubviews(self View, buffer *strings.Builder) {
 | 
			
		||||
 | 
			
		||||
	vCheckboxAlign, hCheckboxAlign := button.htmlCheckbox(buffer, IsCheckboxChecked(button, ""))
 | 
			
		||||
	vCheckboxAlign, hCheckboxAlign := button.htmlCheckbox(buffer, IsCheckboxChecked(button))
 | 
			
		||||
 | 
			
		||||
	buffer.WriteString(`<div id="`)
 | 
			
		||||
	buffer.WriteString(button.htmlID())
 | 
			
		||||
| 
						 | 
				
			
			@ -370,7 +322,7 @@ func (button *checkboxData) htmlSubviews(self View, buffer *strings.Builder) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (button *checkboxData) cssHorizontalAlign() string {
 | 
			
		||||
	align, _ := enumStyledProperty(button, HorizontalAlign, TopAlign)
 | 
			
		||||
	align := GetHorizontalAlign(button)
 | 
			
		||||
	values := enumProperties[CellHorizontalAlign].cssValues
 | 
			
		||||
	if align >= 0 && align < len(values) {
 | 
			
		||||
		return values[align]
 | 
			
		||||
| 
						 | 
				
			
			@ -379,7 +331,7 @@ func (button *checkboxData) cssHorizontalAlign() string {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (button *checkboxData) cssVerticalAlign() string {
 | 
			
		||||
	align, _ := enumStyledProperty(button, VerticalAlign, TopAlign)
 | 
			
		||||
	align := GetVerticalAlign(button)
 | 
			
		||||
	values := enumProperties[CellVerticalAlign].cssValues
 | 
			
		||||
	if align >= 0 && align < len(values) {
 | 
			
		||||
		return values[align]
 | 
			
		||||
| 
						 | 
				
			
			@ -388,17 +340,19 @@ func (button *checkboxData) cssVerticalAlign() string {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// IsCheckboxChecked returns true if the Checkbox is checked, false otherwise.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func IsCheckboxChecked(view View, subviewID string) bool {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if checked := view.Get(Checked); checked != nil {
 | 
			
		||||
			if b, ok := checked.(bool); ok {
 | 
			
		||||
				return b
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func IsCheckboxChecked(view View, subviewID ...string) bool {
 | 
			
		||||
	return boolStyledProperty(view, subviewID, Checked, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetCheckboxVerticalAlign return the vertical align of a Checkbox subview: TopAlign (0), BottomAlign (1), CenterAlign (2)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a left position of the first argument (view) is returned
 | 
			
		||||
func GetCheckboxVerticalAlign(view View, subviewID ...string) int {
 | 
			
		||||
	return enumStyledProperty(view, subviewID, CheckboxVerticalAlign, LeftAlign, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetCheckboxHorizontalAlign return the vertical align of a Checkbox subview: LeftAlign (0), RightAlign (1), CenterAlign (2)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a left position of the first argument (view) is returned
 | 
			
		||||
func GetCheckboxHorizontalAlign(view View, subviewID ...string) int {
 | 
			
		||||
	return enumStyledProperty(view, subviewID, CheckboxHorizontalAlign, TopAlign, false)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										113
									
								
								colorPicker.go
								
								
								
								
							
							
						
						
									
										113
									
								
								colorPicker.go
								
								
								
								
							| 
						 | 
				
			
			@ -23,7 +23,7 @@ type colorPickerData struct {
 | 
			
		|||
// NewColorPicker create new ColorPicker object and return it
 | 
			
		||||
func NewColorPicker(session Session, params Params) ColorPicker {
 | 
			
		||||
	view := new(colorPickerData)
 | 
			
		||||
	view.Init(session)
 | 
			
		||||
	view.init(session)
 | 
			
		||||
	setInitParams(view, params)
 | 
			
		||||
	return view
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -32,8 +32,8 @@ func newColorPicker(session Session) View {
 | 
			
		|||
	return NewColorPicker(session, nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (picker *colorPickerData) Init(session Session) {
 | 
			
		||||
	picker.viewData.Init(session)
 | 
			
		||||
func (picker *colorPickerData) init(session Session) {
 | 
			
		||||
	picker.viewData.init(session)
 | 
			
		||||
	picker.tag = "ColorPicker"
 | 
			
		||||
	picker.colorChangedListeners = []func(ColorPicker, Color){}
 | 
			
		||||
	picker.properties[Padding] = Px(0)
 | 
			
		||||
| 
						 | 
				
			
			@ -66,7 +66,7 @@ func (picker *colorPickerData) remove(tag string) {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
	case ColorPickerValue:
 | 
			
		||||
		oldColor := GetColorPickerValue(picker, "")
 | 
			
		||||
		oldColor := GetColorPickerValue(picker)
 | 
			
		||||
		delete(picker.properties, ColorPickerValue)
 | 
			
		||||
		picker.colorChanged(oldColor)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -75,11 +75,11 @@ func (picker *colorPickerData) remove(tag string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (picker *colorPickerData) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (picker *colorPickerData) Set(tag string, value any) bool {
 | 
			
		||||
	return picker.set(picker.normalizeTag(tag), value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (picker *colorPickerData) set(tag string, value interface{}) bool {
 | 
			
		||||
func (picker *colorPickerData) set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		picker.remove(tag)
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			@ -87,62 +87,19 @@ func (picker *colorPickerData) set(tag string, value interface{}) bool {
 | 
			
		|||
 | 
			
		||||
	switch tag {
 | 
			
		||||
	case ColorChangedEvent:
 | 
			
		||||
		switch value := value.(type) {
 | 
			
		||||
		case func(ColorPicker, Color):
 | 
			
		||||
			picker.colorChangedListeners = []func(ColorPicker, Color){value}
 | 
			
		||||
 | 
			
		||||
		case func(Color):
 | 
			
		||||
			fn := func(_ ColorPicker, date Color) {
 | 
			
		||||
				value(date)
 | 
			
		||||
			}
 | 
			
		||||
			picker.colorChangedListeners = []func(ColorPicker, Color){fn}
 | 
			
		||||
 | 
			
		||||
		case []func(ColorPicker, Color):
 | 
			
		||||
			picker.colorChangedListeners = value
 | 
			
		||||
 | 
			
		||||
		case []func(Color):
 | 
			
		||||
			listeners := make([]func(ColorPicker, Color), len(value))
 | 
			
		||||
			for i, val := range value {
 | 
			
		||||
				if val == nil {
 | 
			
		||||
					notCompatibleType(tag, val)
 | 
			
		||||
					return false
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				listeners[i] = func(_ ColorPicker, date Color) {
 | 
			
		||||
					val(date)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			picker.colorChangedListeners = listeners
 | 
			
		||||
 | 
			
		||||
		case []interface{}:
 | 
			
		||||
			listeners := make([]func(ColorPicker, Color), len(value))
 | 
			
		||||
			for i, val := range value {
 | 
			
		||||
				if val == nil {
 | 
			
		||||
					notCompatibleType(tag, val)
 | 
			
		||||
					return false
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				switch val := val.(type) {
 | 
			
		||||
				case func(ColorPicker, Color):
 | 
			
		||||
					listeners[i] = val
 | 
			
		||||
 | 
			
		||||
				case func(Color):
 | 
			
		||||
					listeners[i] = func(_ ColorPicker, date Color) {
 | 
			
		||||
						val(date)
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
				default:
 | 
			
		||||
					notCompatibleType(tag, val)
 | 
			
		||||
					return false
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			picker.colorChangedListeners = listeners
 | 
			
		||||
		listeners, ok := valueToEventListeners[ColorPicker, Color](value)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			notCompatibleType(tag, value)
 | 
			
		||||
			return false
 | 
			
		||||
		} else if listeners == nil {
 | 
			
		||||
			listeners = []func(ColorPicker, Color){}
 | 
			
		||||
		}
 | 
			
		||||
		picker.colorChangedListeners = listeners
 | 
			
		||||
		picker.propertyChangedEvent(tag)
 | 
			
		||||
		return true
 | 
			
		||||
 | 
			
		||||
	case ColorPickerValue:
 | 
			
		||||
		oldColor := GetColorPickerValue(picker, "")
 | 
			
		||||
		oldColor := GetColorPickerValue(picker)
 | 
			
		||||
		if picker.setColorProperty(ColorPickerValue, value) {
 | 
			
		||||
			picker.colorChanged(oldColor)
 | 
			
		||||
			return true
 | 
			
		||||
| 
						 | 
				
			
			@ -155,7 +112,7 @@ func (picker *colorPickerData) set(tag string, value interface{}) bool {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (picker *colorPickerData) colorChanged(oldColor Color) {
 | 
			
		||||
	if newColor := GetColorPickerValue(picker, ""); oldColor != newColor {
 | 
			
		||||
	if newColor := GetColorPickerValue(picker); oldColor != newColor {
 | 
			
		||||
		if picker.created {
 | 
			
		||||
			picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, picker.htmlID(), newColor.rgbString()))
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -166,11 +123,11 @@ func (picker *colorPickerData) colorChanged(oldColor Color) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (picker *colorPickerData) Get(tag string) interface{} {
 | 
			
		||||
func (picker *colorPickerData) Get(tag string) any {
 | 
			
		||||
	return picker.get(picker.normalizeTag(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (picker *colorPickerData) get(tag string) interface{} {
 | 
			
		||||
func (picker *colorPickerData) get(tag string) any {
 | 
			
		||||
	switch tag {
 | 
			
		||||
	case ColorChangedEvent:
 | 
			
		||||
		return picker.colorChangedListeners
 | 
			
		||||
| 
						 | 
				
			
			@ -188,7 +145,7 @@ func (picker *colorPickerData) htmlProperties(self View, buffer *strings.Builder
 | 
			
		|||
	picker.viewData.htmlProperties(self, buffer)
 | 
			
		||||
 | 
			
		||||
	buffer.WriteString(` type="color" value="`)
 | 
			
		||||
	buffer.WriteString(GetColorPickerValue(picker, "").rgbString())
 | 
			
		||||
	buffer.WriteString(GetColorPickerValue(picker).rgbString())
 | 
			
		||||
	buffer.WriteByte('"')
 | 
			
		||||
 | 
			
		||||
	buffer.WriteString(` oninput="editViewInputEvent(this)"`)
 | 
			
		||||
| 
						 | 
				
			
			@ -198,7 +155,7 @@ func (picker *colorPickerData) htmlProperties(self View, buffer *strings.Builder
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (picker *colorPickerData) htmlDisabledProperties(self View, buffer *strings.Builder) {
 | 
			
		||||
	if IsDisabled(self, "") {
 | 
			
		||||
	if IsDisabled(self) {
 | 
			
		||||
		buffer.WriteString(` disabled`)
 | 
			
		||||
	}
 | 
			
		||||
	picker.viewData.htmlDisabledProperties(self, buffer)
 | 
			
		||||
| 
						 | 
				
			
			@ -208,7 +165,7 @@ func (picker *colorPickerData) handleCommand(self View, command string, data Dat
 | 
			
		|||
	switch command {
 | 
			
		||||
	case "textChanged":
 | 
			
		||||
		if text, ok := data.PropertyValue("text"); ok {
 | 
			
		||||
			oldColor := GetColorPickerValue(picker, "")
 | 
			
		||||
			oldColor := GetColorPickerValue(picker)
 | 
			
		||||
			if color, ok := StringToColor(text); ok {
 | 
			
		||||
				picker.properties[ColorPickerValue] = color
 | 
			
		||||
				if color != oldColor {
 | 
			
		||||
| 
						 | 
				
			
			@ -225,16 +182,16 @@ func (picker *colorPickerData) handleCommand(self View, command string, data Dat
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// GetColorPickerValue returns the value of ColorPicker subview.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetColorPickerValue(view View, subviewID string) Color {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetColorPickerValue(view View, subviewID ...string) Color {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if result, ok := colorStyledProperty(view, ColorPickerValue); ok {
 | 
			
		||||
			return result
 | 
			
		||||
		if value, ok := colorProperty(view, ColorPickerValue, view.Session()); ok {
 | 
			
		||||
			return value
 | 
			
		||||
		}
 | 
			
		||||
		for _, tag := range []string{Value, ColorTag} {
 | 
			
		||||
		for _, tag := range []string{ColorPickerValue, Value, ColorTag} {
 | 
			
		||||
			if value := valueFromStyle(view, tag); value != nil {
 | 
			
		||||
				if result, ok := valueToColor(value, view.Session()); ok {
 | 
			
		||||
					return result
 | 
			
		||||
| 
						 | 
				
			
			@ -247,17 +204,7 @@ func GetColorPickerValue(view View, subviewID string) Color {
 | 
			
		|||
 | 
			
		||||
// GetColorChangedListeners returns the ColorChangedListener list of an ColorPicker subview.
 | 
			
		||||
// If there are no listeners then the empty list is returned
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetColorChangedListeners(view View, subviewID string) []func(ColorPicker, Color) {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if value := view.Get(ColorChangedEvent); value != nil {
 | 
			
		||||
			if listeners, ok := value.([]func(ColorPicker, Color)); ok {
 | 
			
		||||
				return listeners
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return []func(ColorPicker, Color){}
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetColorChangedListeners(view View, subviewID ...string) []func(ColorPicker, Color) {
 | 
			
		||||
	return getEventListeners[ColorPicker, Color](view, subviewID, ColorChangedEvent)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -47,7 +47,7 @@ type columnLayoutData struct {
 | 
			
		|||
// NewColumnLayout create new ColumnLayout object and return it
 | 
			
		||||
func NewColumnLayout(session Session, params Params) ColumnLayout {
 | 
			
		||||
	view := new(columnLayoutData)
 | 
			
		||||
	view.Init(session)
 | 
			
		||||
	view.init(session)
 | 
			
		||||
	setInitParams(view, params)
 | 
			
		||||
	return view
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -57,8 +57,8 @@ func newColumnLayout(session Session) View {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Init initialize fields of ColumnLayout by default values
 | 
			
		||||
func (ColumnLayout *columnLayoutData) Init(session Session) {
 | 
			
		||||
	ColumnLayout.viewsContainerData.Init(session)
 | 
			
		||||
func (ColumnLayout *columnLayoutData) init(session Session) {
 | 
			
		||||
	ColumnLayout.viewsContainerData.init(session)
 | 
			
		||||
	ColumnLayout.tag = "ColumnLayout"
 | 
			
		||||
	//ColumnLayout.systemClass = "ruiColumnLayout"
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -76,7 +76,7 @@ func (columnLayout *columnLayoutData) normalizeTag(tag string) string {
 | 
			
		|||
	return tag
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (columnLayout *columnLayoutData) Get(tag string) interface{} {
 | 
			
		||||
func (columnLayout *columnLayoutData) Get(tag string) any {
 | 
			
		||||
	return columnLayout.get(columnLayout.normalizeTag(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -97,11 +97,11 @@ func (columnLayout *columnLayoutData) remove(tag string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (columnLayout *columnLayoutData) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (columnLayout *columnLayoutData) Set(tag string, value any) bool {
 | 
			
		||||
	return columnLayout.set(columnLayout.normalizeTag(tag), value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (columnLayout *columnLayoutData) set(tag string, value interface{}) bool {
 | 
			
		||||
func (columnLayout *columnLayoutData) set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		columnLayout.remove(tag)
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			@ -137,50 +137,26 @@ func (columnLayout *columnLayoutData) set(tag string, value interface{}) bool {
 | 
			
		|||
// GetColumnCount returns int value which specifies number of columns into which the content of
 | 
			
		||||
// ColumnLayout is break. If the return value is 0 then the number of columns is calculated
 | 
			
		||||
// based on the "column-width" property.
 | 
			
		||||
// If the second argument (subviewID) is "" then a top position of the first argument (view) is returned
 | 
			
		||||
func GetColumnCount(view View, subviewID string) int {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view == nil {
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
	result, _ := intStyledProperty(view, ColumnCount, 0)
 | 
			
		||||
	return result
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a top position of the first argument (view) is returned
 | 
			
		||||
func GetColumnCount(view View, subviewID ...string) int {
 | 
			
		||||
	return intStyledProperty(view, subviewID, ColumnCount, 0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetColumnWidth returns SizeUnit value which specifies the width of each column of ColumnLayout.
 | 
			
		||||
// If the second argument (subviewID) is "" then a top position of the first argument (view) is returned
 | 
			
		||||
func GetColumnWidth(view View, subviewID string) SizeUnit {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view == nil {
 | 
			
		||||
		return AutoSize()
 | 
			
		||||
	}
 | 
			
		||||
	result, _ := sizeStyledProperty(view, ColumnWidth)
 | 
			
		||||
	return result
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a top position of the first argument (view) is returned
 | 
			
		||||
func GetColumnWidth(view View, subviewID ...string) SizeUnit {
 | 
			
		||||
	return sizeStyledProperty(view, subviewID, ColumnWidth, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetColumnGap returns SizeUnit property which specifies the size of the gap (gutter) between columns of ColumnLayout.
 | 
			
		||||
// If the second argument (subviewID) is "" then a top position of the first argument (view) is returned
 | 
			
		||||
func GetColumnGap(view View, subviewID string) SizeUnit {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view == nil {
 | 
			
		||||
		return AutoSize()
 | 
			
		||||
	}
 | 
			
		||||
	result, _ := sizeStyledProperty(view, ColumnGap)
 | 
			
		||||
	return result
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a top position of the first argument (view) is returned
 | 
			
		||||
func GetColumnGap(view View, subviewID ...string) SizeUnit {
 | 
			
		||||
	return sizeStyledProperty(view, subviewID, ColumnGap, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetColumnSeparator returns ViewBorder struct which specifies the line drawn between
 | 
			
		||||
// columns in a multi-column ColumnLayout.
 | 
			
		||||
// If the second argument (subviewID) is "" then a top position of the first argument (view) is returned
 | 
			
		||||
func GetColumnSeparator(view View, subviewID string) ViewBorder {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
func getColumnSeparator(view View, subviewID []string) ViewBorder {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if view != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -199,27 +175,34 @@ func GetColumnSeparator(view View, subviewID string) ViewBorder {
 | 
			
		|||
	return ViewBorder{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetColumnSeparator returns ViewBorder struct which specifies the line drawn between
 | 
			
		||||
// columns in a multi-column ColumnLayout.
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a top position of the first argument (view) is returned
 | 
			
		||||
func GetColumnSeparator(view View, subviewID ...string) ViewBorder {
 | 
			
		||||
	return getColumnSeparator(view, subviewID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ColumnSeparatorStyle returns int value which specifies the style of the line drawn between
 | 
			
		||||
// columns in a multi-column layout.
 | 
			
		||||
// Valid values are NoneLine (0), SolidLine (1), DashedLine (2), DottedLine (3), and DoubleLine (4).
 | 
			
		||||
// If the second argument (subviewID) is "" then a top position of the first argument (view) is returned
 | 
			
		||||
func GetColumnSeparatorStyle(view View, subviewID string) int {
 | 
			
		||||
	border := GetColumnSeparator(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a top position of the first argument (view) is returned
 | 
			
		||||
func GetColumnSeparatorStyle(view View, subviewID ...string) int {
 | 
			
		||||
	border := getColumnSeparator(view, subviewID)
 | 
			
		||||
	return border.Style
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ColumnSeparatorWidth returns SizeUnit value which specifies the width of the line drawn between
 | 
			
		||||
// columns in a multi-column layout.
 | 
			
		||||
// If the second argument (subviewID) is "" then a top position of the first argument (view) is returned
 | 
			
		||||
func GetColumnSeparatorWidth(view View, subviewID string) SizeUnit {
 | 
			
		||||
	border := GetColumnSeparator(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a top position of the first argument (view) is returned
 | 
			
		||||
func GetColumnSeparatorWidth(view View, subviewID ...string) SizeUnit {
 | 
			
		||||
	border := getColumnSeparator(view, subviewID)
 | 
			
		||||
	return border.Width
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ColumnSeparatorColor returns Color value which specifies the color of the line drawn between
 | 
			
		||||
// columns in a multi-column layout.
 | 
			
		||||
// If the second argument (subviewID) is "" then a top position of the first argument (view) is returned
 | 
			
		||||
func GetColumnSeparatorColor(view View, subviewID string) Color {
 | 
			
		||||
	border := GetColumnSeparator(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a top position of the first argument (view) is returned
 | 
			
		||||
func GetColumnSeparatorColor(view View, subviewID ...string) Color {
 | 
			
		||||
	border := getColumnSeparator(view, subviewID)
 | 
			
		||||
	return border.Color
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,11 +18,11 @@ type columnSeparatorProperty struct {
 | 
			
		|||
	propertyList
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newColumnSeparatorProperty(value interface{}) ColumnSeparatorProperty {
 | 
			
		||||
func newColumnSeparatorProperty(value any) ColumnSeparatorProperty {
 | 
			
		||||
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		separator := new(columnSeparatorProperty)
 | 
			
		||||
		separator.properties = map[string]interface{}{}
 | 
			
		||||
		separator.properties = map[string]any{}
 | 
			
		||||
		return separator
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -32,7 +32,7 @@ func newColumnSeparatorProperty(value interface{}) ColumnSeparatorProperty {
 | 
			
		|||
 | 
			
		||||
	case DataObject:
 | 
			
		||||
		separator := new(columnSeparatorProperty)
 | 
			
		||||
		separator.properties = map[string]interface{}{}
 | 
			
		||||
		separator.properties = map[string]any{}
 | 
			
		||||
		for _, tag := range []string{Style, Width, ColorTag} {
 | 
			
		||||
			if val, ok := value.PropertyValue(tag); ok && val != "" {
 | 
			
		||||
				separator.set(tag, value)
 | 
			
		||||
| 
						 | 
				
			
			@ -42,7 +42,7 @@ func newColumnSeparatorProperty(value interface{}) ColumnSeparatorProperty {
 | 
			
		|||
 | 
			
		||||
	case ViewBorder:
 | 
			
		||||
		separator := new(columnSeparatorProperty)
 | 
			
		||||
		separator.properties = map[string]interface{}{
 | 
			
		||||
		separator.properties = map[string]any{
 | 
			
		||||
			Style:    value.Style,
 | 
			
		||||
			Width:    value.Width,
 | 
			
		||||
			ColorTag: value.Color,
 | 
			
		||||
| 
						 | 
				
			
			@ -57,7 +57,7 @@ func newColumnSeparatorProperty(value interface{}) ColumnSeparatorProperty {
 | 
			
		|||
// NewColumnSeparator creates the new ColumnSeparatorProperty
 | 
			
		||||
func NewColumnSeparator(params Params) ColumnSeparatorProperty {
 | 
			
		||||
	separator := new(columnSeparatorProperty)
 | 
			
		||||
	separator.properties = map[string]interface{}{}
 | 
			
		||||
	separator.properties = map[string]any{}
 | 
			
		||||
	if params != nil {
 | 
			
		||||
		for _, tag := range []string{Style, Width, ColorTag} {
 | 
			
		||||
			if value, ok := params[tag]; ok && value != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -117,7 +117,7 @@ func (separator *columnSeparatorProperty) Remove(tag string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (separator *columnSeparatorProperty) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (separator *columnSeparatorProperty) Set(tag string, value any) bool {
 | 
			
		||||
	tag = separator.normalizeTag(tag)
 | 
			
		||||
 | 
			
		||||
	if value == nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -140,7 +140,7 @@ func (separator *columnSeparatorProperty) Set(tag string, value interface{}) boo
 | 
			
		|||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (separator *columnSeparatorProperty) Get(tag string) interface{} {
 | 
			
		||||
func (separator *columnSeparatorProperty) Get(tag string) any {
 | 
			
		||||
	tag = separator.normalizeTag(tag)
 | 
			
		||||
 | 
			
		||||
	if result, ok := separator.properties[tag]; ok {
 | 
			
		||||
| 
						 | 
				
			
			@ -167,8 +167,9 @@ func (separator *columnSeparatorProperty) cssValue(session Session) string {
 | 
			
		|||
	buffer := allocStringBuilder()
 | 
			
		||||
	defer freeStringBuilder(buffer)
 | 
			
		||||
 | 
			
		||||
	if value.Width.Type != Auto && value.Width.Type != SizeInFraction && value.Width.Value > 0 {
 | 
			
		||||
		buffer.WriteString(value.Width.cssString(""))
 | 
			
		||||
	if value.Width.Type != Auto && value.Width.Type != SizeInFraction &&
 | 
			
		||||
		(value.Width.Value > 0 || value.Width.Type == SizeFunction) {
 | 
			
		||||
		buffer.WriteString(value.Width.cssString("", session))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	styles := enumProperties[BorderStyle].cssValues
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,26 +45,26 @@ func (customView *CustomViewData) setTag(tag string) {
 | 
			
		|||
 | 
			
		||||
// Get returns a value of the property with name defined by the argument.
 | 
			
		||||
// The type of return value depends on the property. If the property is not set then nil is returned.
 | 
			
		||||
func (customView *CustomViewData) Get(tag string) interface{} {
 | 
			
		||||
func (customView *CustomViewData) Get(tag string) any {
 | 
			
		||||
	return customView.superView.Get(tag)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (customView *CustomViewData) getRaw(tag string) interface{} {
 | 
			
		||||
func (customView *CustomViewData) getRaw(tag string) any {
 | 
			
		||||
	return customView.superView.getRaw(tag)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (customView *CustomViewData) setRaw(tag string, value interface{}) {
 | 
			
		||||
func (customView *CustomViewData) setRaw(tag string, value any) {
 | 
			
		||||
	customView.superView.setRaw(tag, value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Set sets the value (second argument) of the property with name defined by the first argument.
 | 
			
		||||
// Return "true" if the value has been set, in the opposite case "false" are returned and
 | 
			
		||||
// a description of the error is written to the log
 | 
			
		||||
func (customView *CustomViewData) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (customView *CustomViewData) Set(tag string, value any) bool {
 | 
			
		||||
	return customView.superView.Set(tag, value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (customView *CustomViewData) SetAnimated(tag string, value interface{}, animation Animation) bool {
 | 
			
		||||
func (customView *CustomViewData) SetAnimated(tag string, value any, animation Animation) bool {
 | 
			
		||||
	return customView.superView.SetAnimated(tag, value, animation)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -87,10 +87,6 @@ func (customView *CustomViewData) Clear() {
 | 
			
		|||
	customView.superView.Clear()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Init initializes fields of View by default values
 | 
			
		||||
func (customView *CustomViewData) Init(session Session) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (customView *CustomViewData) cssViewStyle(buffer cssBuilder, session Session) {
 | 
			
		||||
	customView.superView.cssViewStyle(buffer, session)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -264,9 +260,22 @@ func (customView *CustomViewData) setScroll(x, y, width, height float64) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (customView *CustomViewData) getTransitions() Params {
 | 
			
		||||
func (customView *CustomViewData) Transition(tag string) Animation {
 | 
			
		||||
	if customView.superView != nil {
 | 
			
		||||
		return customView.superView.getTransitions()
 | 
			
		||||
		return customView.superView.Transition(tag)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (customView *CustomViewData) Transitions() map[string]Animation {
 | 
			
		||||
	if customView.superView != nil {
 | 
			
		||||
		return customView.superView.Transitions()
 | 
			
		||||
	}
 | 
			
		||||
	return map[string]Animation{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (customView *CustomViewData) SetTransition(tag string, animation Animation) {
 | 
			
		||||
	if customView.superView != nil {
 | 
			
		||||
		customView.superView.SetTransition(tag, animation)
 | 
			
		||||
	}
 | 
			
		||||
	return Params{}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										141
									
								
								datePicker.go
								
								
								
								
							
							
						
						
									
										141
									
								
								datePicker.go
								
								
								
								
							| 
						 | 
				
			
			@ -29,7 +29,7 @@ type datePickerData struct {
 | 
			
		|||
// NewDatePicker create new DatePicker object and return it
 | 
			
		||||
func NewDatePicker(session Session, params Params) DatePicker {
 | 
			
		||||
	view := new(datePickerData)
 | 
			
		||||
	view.Init(session)
 | 
			
		||||
	view.init(session)
 | 
			
		||||
	setInitParams(view, params)
 | 
			
		||||
	return view
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -38,8 +38,8 @@ func newDatePicker(session Session) View {
 | 
			
		|||
	return NewDatePicker(session, nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (picker *datePickerData) Init(session Session) {
 | 
			
		||||
	picker.viewData.Init(session)
 | 
			
		||||
func (picker *datePickerData) init(session Session) {
 | 
			
		||||
	picker.viewData.init(session)
 | 
			
		||||
	picker.tag = "DatePicker"
 | 
			
		||||
	picker.dateChangedListeners = []func(DatePicker, time.Time){}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -96,7 +96,7 @@ func (picker *datePickerData) remove(tag string) {
 | 
			
		|||
	case DatePickerValue:
 | 
			
		||||
		if _, ok := picker.properties[DatePickerValue]; ok {
 | 
			
		||||
			delete(picker.properties, DatePickerValue)
 | 
			
		||||
			date := GetDatePickerValue(picker, "")
 | 
			
		||||
			date := GetDatePickerValue(picker)
 | 
			
		||||
			if picker.created {
 | 
			
		||||
				picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, picker.htmlID(), date.Format(dateFormat)))
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -114,11 +114,11 @@ func (picker *datePickerData) remove(tag string) {
 | 
			
		|||
	picker.propertyChangedEvent(tag)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (picker *datePickerData) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (picker *datePickerData) Set(tag string, value any) bool {
 | 
			
		||||
	return picker.set(picker.normalizeTag(tag), value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (picker *datePickerData) set(tag string, value interface{}) bool {
 | 
			
		||||
func (picker *datePickerData) set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		picker.remove(tag)
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			@ -204,9 +204,9 @@ func (picker *datePickerData) set(tag string, value interface{}) bool {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
	case DatePickerStep:
 | 
			
		||||
		oldStep := GetDatePickerStep(picker, "")
 | 
			
		||||
		oldStep := GetDatePickerStep(picker)
 | 
			
		||||
		if picker.setIntProperty(DatePickerStep, value) {
 | 
			
		||||
			if step := GetDatePickerStep(picker, ""); oldStep != step {
 | 
			
		||||
			if step := GetDatePickerStep(picker); oldStep != step {
 | 
			
		||||
				if picker.created {
 | 
			
		||||
					if step > 0 {
 | 
			
		||||
						updateProperty(picker.htmlID(), Step, strconv.Itoa(step), picker.session)
 | 
			
		||||
| 
						 | 
				
			
			@ -220,7 +220,7 @@ func (picker *datePickerData) set(tag string, value interface{}) bool {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
	case DatePickerValue:
 | 
			
		||||
		oldDate := GetDatePickerValue(picker, "")
 | 
			
		||||
		oldDate := GetDatePickerValue(picker)
 | 
			
		||||
		if date, ok := setTimeValue(DatePickerValue); ok {
 | 
			
		||||
			if date != oldDate {
 | 
			
		||||
				if picker.created {
 | 
			
		||||
| 
						 | 
				
			
			@ -235,57 +235,14 @@ func (picker *datePickerData) set(tag string, value interface{}) bool {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
	case DateChangedEvent:
 | 
			
		||||
		switch value := value.(type) {
 | 
			
		||||
		case func(DatePicker, time.Time):
 | 
			
		||||
			picker.dateChangedListeners = []func(DatePicker, time.Time){value}
 | 
			
		||||
 | 
			
		||||
		case func(time.Time):
 | 
			
		||||
			fn := func(_ DatePicker, date time.Time) {
 | 
			
		||||
				value(date)
 | 
			
		||||
			}
 | 
			
		||||
			picker.dateChangedListeners = []func(DatePicker, time.Time){fn}
 | 
			
		||||
 | 
			
		||||
		case []func(DatePicker, time.Time):
 | 
			
		||||
			picker.dateChangedListeners = value
 | 
			
		||||
 | 
			
		||||
		case []func(time.Time):
 | 
			
		||||
			listeners := make([]func(DatePicker, time.Time), len(value))
 | 
			
		||||
			for i, val := range value {
 | 
			
		||||
				if val == nil {
 | 
			
		||||
					notCompatibleType(tag, val)
 | 
			
		||||
					return false
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				listeners[i] = func(_ DatePicker, date time.Time) {
 | 
			
		||||
					val(date)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			picker.dateChangedListeners = listeners
 | 
			
		||||
 | 
			
		||||
		case []interface{}:
 | 
			
		||||
			listeners := make([]func(DatePicker, time.Time), len(value))
 | 
			
		||||
			for i, val := range value {
 | 
			
		||||
				if val == nil {
 | 
			
		||||
					notCompatibleType(tag, val)
 | 
			
		||||
					return false
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				switch val := val.(type) {
 | 
			
		||||
				case func(DatePicker, time.Time):
 | 
			
		||||
					listeners[i] = val
 | 
			
		||||
 | 
			
		||||
				case func(time.Time):
 | 
			
		||||
					listeners[i] = func(_ DatePicker, date time.Time) {
 | 
			
		||||
						val(date)
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
				default:
 | 
			
		||||
					notCompatibleType(tag, val)
 | 
			
		||||
					return false
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			picker.dateChangedListeners = listeners
 | 
			
		||||
		listeners, ok := valueToEventListeners[DatePicker, time.Time](value)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			notCompatibleType(tag, value)
 | 
			
		||||
			return false
 | 
			
		||||
		} else if listeners == nil {
 | 
			
		||||
			listeners = []func(DatePicker, time.Time){}
 | 
			
		||||
		}
 | 
			
		||||
		picker.dateChangedListeners = listeners
 | 
			
		||||
		picker.propertyChangedEvent(tag)
 | 
			
		||||
		return true
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -295,11 +252,11 @@ func (picker *datePickerData) set(tag string, value interface{}) bool {
 | 
			
		|||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (picker *datePickerData) Get(tag string) interface{} {
 | 
			
		||||
func (picker *datePickerData) Get(tag string) any {
 | 
			
		||||
	return picker.get(picker.normalizeTag(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (picker *datePickerData) get(tag string) interface{} {
 | 
			
		||||
func (picker *datePickerData) get(tag string) any {
 | 
			
		||||
	switch tag {
 | 
			
		||||
	case DateChangedEvent:
 | 
			
		||||
		return picker.dateChangedListeners
 | 
			
		||||
| 
						 | 
				
			
			@ -337,7 +294,7 @@ func (picker *datePickerData) htmlProperties(self View, buffer *strings.Builder)
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	buffer.WriteString(` value="`)
 | 
			
		||||
	buffer.WriteString(GetDatePickerValue(picker, "").Format(dateFormat))
 | 
			
		||||
	buffer.WriteString(GetDatePickerValue(picker).Format(dateFormat))
 | 
			
		||||
	buffer.WriteByte('"')
 | 
			
		||||
 | 
			
		||||
	buffer.WriteString(` oninput="editViewInputEvent(this)"`)
 | 
			
		||||
| 
						 | 
				
			
			@ -347,7 +304,7 @@ func (picker *datePickerData) htmlProperties(self View, buffer *strings.Builder)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (picker *datePickerData) htmlDisabledProperties(self View, buffer *strings.Builder) {
 | 
			
		||||
	if IsDisabled(self, "") {
 | 
			
		||||
	if IsDisabled(self) {
 | 
			
		||||
		buffer.WriteString(` disabled`)
 | 
			
		||||
	}
 | 
			
		||||
	picker.viewData.htmlDisabledProperties(self, buffer)
 | 
			
		||||
| 
						 | 
				
			
			@ -358,7 +315,7 @@ func (picker *datePickerData) handleCommand(self View, command string, data Data
 | 
			
		|||
	case "textChanged":
 | 
			
		||||
		if text, ok := data.PropertyValue("text"); ok {
 | 
			
		||||
			if value, err := time.Parse(dateFormat, text); err == nil {
 | 
			
		||||
				oldValue := GetDatePickerValue(picker, "")
 | 
			
		||||
				oldValue := GetDatePickerValue(picker)
 | 
			
		||||
				picker.properties[DatePickerValue] = value
 | 
			
		||||
				if value != oldValue {
 | 
			
		||||
					for _, listener := range picker.dateChangedListeners {
 | 
			
		||||
| 
						 | 
				
			
			@ -374,7 +331,7 @@ func (picker *datePickerData) handleCommand(self View, command string, data Data
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func getDateProperty(view View, mainTag, shortTag string) (time.Time, bool) {
 | 
			
		||||
	valueToTime := func(value interface{}) (time.Time, bool) {
 | 
			
		||||
	valueToTime := func(value any) (time.Time, bool) {
 | 
			
		||||
		if value != nil {
 | 
			
		||||
			switch value := value.(type) {
 | 
			
		||||
			case time.Time:
 | 
			
		||||
| 
						 | 
				
			
			@ -408,10 +365,10 @@ func getDateProperty(view View, mainTag, shortTag string) (time.Time, bool) {
 | 
			
		|||
 | 
			
		||||
// GetDatePickerMin returns the min date of DatePicker subview and "true" as the second value if the min date is set,
 | 
			
		||||
// "false" as the second value otherwise.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetDatePickerMin(view View, subviewID string) (time.Time, bool) {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetDatePickerMin(view View, subviewID ...string) (time.Time, bool) {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		return getDateProperty(view, DatePickerMin, Min)
 | 
			
		||||
| 
						 | 
				
			
			@ -421,10 +378,10 @@ func GetDatePickerMin(view View, subviewID string) (time.Time, bool) {
 | 
			
		|||
 | 
			
		||||
// GetDatePickerMax returns the max date of DatePicker subview and "true" as the second value if the min date is set,
 | 
			
		||||
// "false" as the second value otherwise.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetDatePickerMax(view View, subviewID string) (time.Time, bool) {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetDatePickerMax(view View, subviewID ...string) (time.Time, bool) {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		return getDateProperty(view, DatePickerMax, Max)
 | 
			
		||||
| 
						 | 
				
			
			@ -433,24 +390,16 @@ func GetDatePickerMax(view View, subviewID string) (time.Time, bool) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// GetDatePickerStep returns the date changing step in days of DatePicker subview.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetDatePickerStep(view View, subviewID string) int {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if result, _ := intStyledProperty(view, DatePickerStep, 0); result >= 0 {
 | 
			
		||||
			return result
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetDatePickerStep(view View, subviewID ...string) int {
 | 
			
		||||
	return intStyledProperty(view, subviewID, DatePickerStep, 0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetDatePickerValue returns the date of DatePicker subview.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetDatePickerValue(view View, subviewID string) time.Time {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetDatePickerValue(view View, subviewID ...string) time.Time {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
	if view == nil {
 | 
			
		||||
		return time.Now()
 | 
			
		||||
| 
						 | 
				
			
			@ -461,17 +410,7 @@ func GetDatePickerValue(view View, subviewID string) time.Time {
 | 
			
		|||
 | 
			
		||||
// GetDateChangedListeners returns the DateChangedListener list of an DatePicker subview.
 | 
			
		||||
// If there are no listeners then the empty list is returned
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetDateChangedListeners(view View, subviewID string) []func(DatePicker, time.Time) {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if value := view.Get(DateChangedEvent); value != nil {
 | 
			
		||||
			if listeners, ok := value.([]func(DatePicker, time.Time)); ok {
 | 
			
		||||
				return listeners
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return []func(DatePicker, time.Time){}
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetDateChangedListeners(view View, subviewID ...string) []func(DatePicker, time.Time) {
 | 
			
		||||
	return getEventListeners[DatePicker, time.Time](view, subviewID, DateChangedEvent)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -61,6 +61,8 @@ theme {
 | 
			
		|||
		ruiTabHeight = 32px,
 | 
			
		||||
		ruiTabBarPadding = 2px,
 | 
			
		||||
		ruiTabRadius = 2px,
 | 
			
		||||
		ruiArrowSize = 16px,
 | 
			
		||||
		ruiArrowWidth = 16px,
 | 
			
		||||
	},
 | 
			
		||||
	constants:touch = _{
 | 
			
		||||
		ruiButtonHorizontalPadding = 20px,
 | 
			
		||||
| 
						 | 
				
			
			@ -72,6 +74,7 @@ theme {
 | 
			
		|||
			text-size = 10pt,
 | 
			
		||||
			text-color = @ruiTextColor,
 | 
			
		||||
			background-color = @ruiBackgroundColor,
 | 
			
		||||
			accent-color = @ruiHighlightColor,
 | 
			
		||||
		},
 | 
			
		||||
		ruiButton {
 | 
			
		||||
			align = center,
 | 
			
		||||
| 
						 | 
				
			
			@ -216,7 +219,6 @@ theme {
 | 
			
		|||
			background-color = @ruiPopupBackgroundColor,
 | 
			
		||||
			text-color = @ruiPopupTextColor,
 | 
			
		||||
			radius = 4px,
 | 
			
		||||
			shadow = _{spread-radius=4px, blur=16px, color=@ruiPopupShadow },
 | 
			
		||||
		},
 | 
			
		||||
		ruiPopupTitle {
 | 
			
		||||
			background-color = @ruiPopupTitleColor,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,7 +24,7 @@ type detailsViewData struct {
 | 
			
		|||
// NewDetailsView create new DetailsView object and return it
 | 
			
		||||
func NewDetailsView(session Session, params Params) DetailsView {
 | 
			
		||||
	view := new(detailsViewData)
 | 
			
		||||
	view.Init(session)
 | 
			
		||||
	view.init(session)
 | 
			
		||||
	setInitParams(view, params)
 | 
			
		||||
	return view
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -34,8 +34,8 @@ func newDetailsView(session Session) View {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Init initialize fields of DetailsView by default values
 | 
			
		||||
func (detailsView *detailsViewData) Init(session Session) {
 | 
			
		||||
	detailsView.viewsContainerData.Init(session)
 | 
			
		||||
func (detailsView *detailsViewData) init(session Session) {
 | 
			
		||||
	detailsView.viewsContainerData.init(session)
 | 
			
		||||
	detailsView.tag = "DetailsView"
 | 
			
		||||
	//detailsView.systemClass = "ruiDetailsView"
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -68,11 +68,11 @@ func (detailsView *detailsViewData) remove(tag string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (detailsView *detailsViewData) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (detailsView *detailsViewData) Set(tag string, value any) bool {
 | 
			
		||||
	return detailsView.set(strings.ToLower(tag), value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (detailsView *detailsViewData) set(tag string, value interface{}) bool {
 | 
			
		||||
func (detailsView *detailsViewData) set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		detailsView.remove(tag)
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			@ -110,7 +110,7 @@ func (detailsView *detailsViewData) set(tag string, value interface{}) bool {
 | 
			
		|||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		if detailsView.created {
 | 
			
		||||
			if IsDetailsExpanded(detailsView, "") {
 | 
			
		||||
			if IsDetailsExpanded(detailsView) {
 | 
			
		||||
				updateProperty(detailsView.htmlID(), "open", "", detailsView.Session())
 | 
			
		||||
			} else {
 | 
			
		||||
				removeProperty(detailsView.htmlID(), "open", detailsView.Session())
 | 
			
		||||
| 
						 | 
				
			
			@ -133,11 +133,11 @@ func (detailsView *detailsViewData) set(tag string, value interface{}) bool {
 | 
			
		|||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (detailsView *detailsViewData) Get(tag string) interface{} {
 | 
			
		||||
func (detailsView *detailsViewData) Get(tag string) any {
 | 
			
		||||
	return detailsView.get(strings.ToLower(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (detailsView *detailsViewData) get(tag string) interface{} {
 | 
			
		||||
func (detailsView *detailsViewData) get(tag string) any {
 | 
			
		||||
	return detailsView.viewsContainerData.get(tag)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -148,7 +148,7 @@ func (detailsView *detailsViewData) htmlTag() string {
 | 
			
		|||
func (detailsView *detailsViewData) htmlProperties(self View, buffer *strings.Builder) {
 | 
			
		||||
	detailsView.viewsContainerData.htmlProperties(self, buffer)
 | 
			
		||||
	buffer.WriteString(` ontoggle="detailsEvent(this)"`)
 | 
			
		||||
	if IsDetailsExpanded(detailsView, "") {
 | 
			
		||||
	if IsDetailsExpanded(detailsView) {
 | 
			
		||||
		buffer.WriteString(` open`)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -157,7 +157,7 @@ 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, "") {
 | 
			
		||||
			if !GetNotTranslate(detailsView) {
 | 
			
		||||
				value, _ = detailsView.session.GetString(value)
 | 
			
		||||
			}
 | 
			
		||||
			buffer.WriteString("<summary>")
 | 
			
		||||
| 
						 | 
				
			
			@ -186,10 +186,10 @@ func (detailsView *detailsViewData) handleCommand(self View, command string, dat
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// GetDetailsSummary returns a value of the Summary property of DetailsView.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetDetailsSummary(view View, subviewID string) View {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetDetailsSummary(view View, subviewID ...string) View {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if value := view.Get(Summary); value != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -206,15 +206,7 @@ func GetDetailsSummary(view View, subviewID string) View {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// IsDetailsExpanded returns a value of the Expanded property of DetailsView.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func IsDetailsExpanded(view View, subviewID string) bool {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if result, ok := boolStyledProperty(view, Expanded); ok {
 | 
			
		||||
			return result
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func IsDetailsExpanded(view View, subviewID ...string) bool {
 | 
			
		||||
	return boolStyledProperty(view, subviewID, Expanded, false)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										154
									
								
								dropDownList.go
								
								
								
								
							
							
						
						
									
										154
									
								
								dropDownList.go
								
								
								
								
							| 
						 | 
				
			
			@ -6,6 +6,9 @@ import (
 | 
			
		|||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// DropDownEvent is the constant for "drop-down-event" property tag.
 | 
			
		||||
// The "drop-down-event" event occurs when a list item becomes selected.
 | 
			
		||||
// The main listener format: func(DropDownList, int), where the second argument is the item index.
 | 
			
		||||
const DropDownEvent = "drop-down-event"
 | 
			
		||||
 | 
			
		||||
// DropDownList - the interface of a drop-down list view
 | 
			
		||||
| 
						 | 
				
			
			@ -17,14 +20,14 @@ type DropDownList interface {
 | 
			
		|||
type dropDownListData struct {
 | 
			
		||||
	viewData
 | 
			
		||||
	items            []string
 | 
			
		||||
	disabledItems    []interface{}
 | 
			
		||||
	disabledItems    []any
 | 
			
		||||
	dropDownListener []func(DropDownList, int)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewDropDownList create new DropDownList object and return it
 | 
			
		||||
func NewDropDownList(session Session, params Params) DropDownList {
 | 
			
		||||
	view := new(dropDownListData)
 | 
			
		||||
	view.Init(session)
 | 
			
		||||
	view.init(session)
 | 
			
		||||
	setInitParams(view, params)
 | 
			
		||||
	return view
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -33,11 +36,11 @@ func newDropDownList(session Session) View {
 | 
			
		|||
	return NewDropDownList(session, nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (list *dropDownListData) Init(session Session) {
 | 
			
		||||
	list.viewData.Init(session)
 | 
			
		||||
func (list *dropDownListData) init(session Session) {
 | 
			
		||||
	list.viewData.init(session)
 | 
			
		||||
	list.tag = "DropDownList"
 | 
			
		||||
	list.items = []string{}
 | 
			
		||||
	list.disabledItems = []interface{}{}
 | 
			
		||||
	list.disabledItems = []any{}
 | 
			
		||||
	list.dropDownListener = []func(DropDownList, int){}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -66,7 +69,7 @@ func (list *dropDownListData) remove(tag string) {
 | 
			
		|||
 | 
			
		||||
	case DisabledItems:
 | 
			
		||||
		if len(list.disabledItems) > 0 {
 | 
			
		||||
			list.disabledItems = []interface{}{}
 | 
			
		||||
			list.disabledItems = []any{}
 | 
			
		||||
			if list.created {
 | 
			
		||||
				updateInnerHTML(list.htmlID(), list.session)
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -80,7 +83,7 @@ func (list *dropDownListData) remove(tag string) {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
	case Current:
 | 
			
		||||
		oldCurrent := GetCurrent(list, "")
 | 
			
		||||
		oldCurrent := GetCurrent(list)
 | 
			
		||||
		delete(list.properties, Current)
 | 
			
		||||
		if oldCurrent != 0 {
 | 
			
		||||
			if list.created {
 | 
			
		||||
| 
						 | 
				
			
			@ -95,11 +98,11 @@ func (list *dropDownListData) remove(tag string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (list *dropDownListData) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (list *dropDownListData) Set(tag string, value any) bool {
 | 
			
		||||
	return list.set(strings.ToLower(tag), value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (list *dropDownListData) set(tag string, value interface{}) bool {
 | 
			
		||||
func (list *dropDownListData) set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		list.remove(tag)
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			@ -113,15 +116,24 @@ func (list *dropDownListData) set(tag string, value interface{}) bool {
 | 
			
		|||
		return list.setDisabledItems(value)
 | 
			
		||||
 | 
			
		||||
	case DropDownEvent:
 | 
			
		||||
		return list.setDropDownListener(value)
 | 
			
		||||
		listeners, ok := valueToEventListeners[DropDownList, int](value)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			notCompatibleType(tag, value)
 | 
			
		||||
			return false
 | 
			
		||||
		} else if listeners == nil {
 | 
			
		||||
			listeners = []func(DropDownList, int){}
 | 
			
		||||
		}
 | 
			
		||||
		list.dropDownListener = listeners
 | 
			
		||||
		list.propertyChangedEvent(tag)
 | 
			
		||||
		return true
 | 
			
		||||
 | 
			
		||||
	case Current:
 | 
			
		||||
		oldCurrent := GetCurrent(list, "")
 | 
			
		||||
		oldCurrent := GetCurrent(list)
 | 
			
		||||
		if !list.setIntProperty(Current, value) {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if current := GetCurrent(list, ""); oldCurrent != current {
 | 
			
		||||
		if current := GetCurrent(list); oldCurrent != current {
 | 
			
		||||
			if list.created {
 | 
			
		||||
				list.session.runScript(fmt.Sprintf(`selectDropDownListItem('%s', %d)`, list.htmlID(), current))
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -133,7 +145,7 @@ func (list *dropDownListData) set(tag string, value interface{}) bool {
 | 
			
		|||
	return list.viewData.set(tag, value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (list *dropDownListData) setItems(value interface{}) bool {
 | 
			
		||||
func (list *dropDownListData) setItems(value any) bool {
 | 
			
		||||
	switch value := value.(type) {
 | 
			
		||||
	case string:
 | 
			
		||||
		list.items = []string{value}
 | 
			
		||||
| 
						 | 
				
			
			@ -155,7 +167,7 @@ func (list *dropDownListData) setItems(value interface{}) bool {
 | 
			
		|||
			list.items[i] = str.String()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case []interface{}:
 | 
			
		||||
	case []any:
 | 
			
		||||
		items := make([]string, 0, len(value))
 | 
			
		||||
		for _, v := range value {
 | 
			
		||||
			switch val := v.(type) {
 | 
			
		||||
| 
						 | 
				
			
			@ -206,16 +218,16 @@ func (list *dropDownListData) setItems(value interface{}) bool {
 | 
			
		|||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (list *dropDownListData) setDisabledItems(value interface{}) bool {
 | 
			
		||||
func (list *dropDownListData) setDisabledItems(value any) bool {
 | 
			
		||||
	switch value := value.(type) {
 | 
			
		||||
	case []int:
 | 
			
		||||
		list.disabledItems = make([]interface{}, len(value))
 | 
			
		||||
		list.disabledItems = make([]any, len(value))
 | 
			
		||||
		for i, n := range value {
 | 
			
		||||
			list.disabledItems[i] = n
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case []interface{}:
 | 
			
		||||
		disabledItems := make([]interface{}, len(value))
 | 
			
		||||
	case []any:
 | 
			
		||||
		disabledItems := make([]any, len(value))
 | 
			
		||||
		for i, val := range value {
 | 
			
		||||
			if val == nil {
 | 
			
		||||
				notCompatibleType(DisabledItems, value)
 | 
			
		||||
| 
						 | 
				
			
			@ -248,7 +260,7 @@ func (list *dropDownListData) setDisabledItems(value interface{}) bool {
 | 
			
		|||
 | 
			
		||||
	case string:
 | 
			
		||||
		values := strings.Split(value, ",")
 | 
			
		||||
		disabledItems := make([]interface{}, len(values))
 | 
			
		||||
		disabledItems := make([]any, len(values))
 | 
			
		||||
		for i, str := range values {
 | 
			
		||||
			str = strings.Trim(str, " ")
 | 
			
		||||
			if str == "" {
 | 
			
		||||
| 
						 | 
				
			
			@ -291,69 +303,11 @@ func (list *dropDownListData) setDisabledItems(value interface{}) bool {
 | 
			
		|||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (list *dropDownListData) setDropDownListener(value interface{}) bool {
 | 
			
		||||
	switch value := value.(type) {
 | 
			
		||||
	case func(DropDownList, int):
 | 
			
		||||
		list.dropDownListener = []func(DropDownList, int){value}
 | 
			
		||||
 | 
			
		||||
	case func(int):
 | 
			
		||||
		list.dropDownListener = []func(DropDownList, int){func(_ DropDownList, index int) {
 | 
			
		||||
			value(index)
 | 
			
		||||
		}}
 | 
			
		||||
 | 
			
		||||
	case []func(DropDownList, int):
 | 
			
		||||
		list.dropDownListener = value
 | 
			
		||||
 | 
			
		||||
	case []func(int):
 | 
			
		||||
		listeners := make([]func(DropDownList, int), len(value))
 | 
			
		||||
		for i, val := range value {
 | 
			
		||||
			if val == nil {
 | 
			
		||||
				notCompatibleType(DropDownEvent, value)
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
			listeners[i] = func(_ DropDownList, index int) {
 | 
			
		||||
				val(index)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		list.dropDownListener = listeners
 | 
			
		||||
 | 
			
		||||
	case []interface{}:
 | 
			
		||||
		listeners := make([]func(DropDownList, int), len(value))
 | 
			
		||||
		for i, val := range value {
 | 
			
		||||
			if val == nil {
 | 
			
		||||
				notCompatibleType(DropDownEvent, value)
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
			switch val := val.(type) {
 | 
			
		||||
			case func(DropDownList, int):
 | 
			
		||||
				listeners[i] = val
 | 
			
		||||
 | 
			
		||||
			case func(int):
 | 
			
		||||
				listeners[i] = func(_ DropDownList, index int) {
 | 
			
		||||
					val(index)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			default:
 | 
			
		||||
				notCompatibleType(DropDownEvent, value)
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		list.dropDownListener = listeners
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		notCompatibleType(DropDownEvent, value)
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	list.propertyChangedEvent(DropDownEvent)
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (list *dropDownListData) Get(tag string) interface{} {
 | 
			
		||||
func (list *dropDownListData) Get(tag string) any {
 | 
			
		||||
	return list.get(strings.ToLower(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (list *dropDownListData) get(tag string) interface{} {
 | 
			
		||||
func (list *dropDownListData) get(tag string) any {
 | 
			
		||||
	switch tag {
 | 
			
		||||
	case Items:
 | 
			
		||||
		return list.items
 | 
			
		||||
| 
						 | 
				
			
			@ -382,9 +336,9 @@ func (list *dropDownListData) htmlTag() string {
 | 
			
		|||
 | 
			
		||||
func (list *dropDownListData) htmlSubviews(self View, buffer *strings.Builder) {
 | 
			
		||||
	if list.items != nil {
 | 
			
		||||
		current := GetCurrent(list, "")
 | 
			
		||||
		notTranslate := GetNotTranslate(list, "")
 | 
			
		||||
		disabledItems := GetDropDownDisabledItems(list, "")
 | 
			
		||||
		current := GetCurrent(list)
 | 
			
		||||
		notTranslate := GetNotTranslate(list)
 | 
			
		||||
		disabledItems := GetDropDownDisabledItems(list)
 | 
			
		||||
		for i, item := range list.items {
 | 
			
		||||
			disabled := false
 | 
			
		||||
			for _, index := range disabledItems {
 | 
			
		||||
| 
						 | 
				
			
			@ -418,7 +372,7 @@ func (list *dropDownListData) htmlProperties(self View, buffer *strings.Builder)
 | 
			
		|||
 | 
			
		||||
func (list *dropDownListData) htmlDisabledProperties(self View, buffer *strings.Builder) {
 | 
			
		||||
	list.viewData.htmlDisabledProperties(self, buffer)
 | 
			
		||||
	if IsDisabled(list, "") {
 | 
			
		||||
	if IsDisabled(list) {
 | 
			
		||||
		buffer.WriteString(`disabled`)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -435,7 +389,7 @@ func (list *dropDownListData) handleCommand(self View, command string, data Data
 | 
			
		|||
	case "itemSelected":
 | 
			
		||||
		if text, ok := data.PropertyValue("number"); ok {
 | 
			
		||||
			if number, err := strconv.Atoi(text); err == nil {
 | 
			
		||||
				if GetCurrent(list, "") != number && number >= 0 && number < len(list.items) {
 | 
			
		||||
				if GetCurrent(list) != number && number >= 0 && number < len(list.items) {
 | 
			
		||||
					list.properties[Current] = number
 | 
			
		||||
					list.onSelectedItemChanged(number)
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			@ -450,19 +404,17 @@ func (list *dropDownListData) handleCommand(self View, command string, data Data
 | 
			
		|||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetDropDownListeners(view View) []func(DropDownList, int) {
 | 
			
		||||
	if value := view.Get(DropDownEvent); value != nil {
 | 
			
		||||
		if listeners, ok := value.([]func(DropDownList, int)); ok {
 | 
			
		||||
			return listeners
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return []func(DropDownList, int){}
 | 
			
		||||
// GetDropDownListeners returns the "drop-down-event" listener list. If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetDropDownListeners(view View, subviewID ...string) []func(DropDownList, int) {
 | 
			
		||||
	return getEventListeners[DropDownList, int](view, subviewID, DropDownEvent)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// func GetDropDownItems return the view items list
 | 
			
		||||
func GetDropDownItems(view View, subviewID string) []string {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// GetDropDownItems return the DropDownList items list.
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetDropDownItems(view View, subviewID ...string) []string {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if list, ok := view.(DropDownList); ok {
 | 
			
		||||
| 
						 | 
				
			
			@ -472,14 +424,16 @@ func GetDropDownItems(view View, subviewID string) []string {
 | 
			
		|||
	return []string{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// func GetDropDownDisabledItems return the list of disabled item indexes
 | 
			
		||||
func GetDropDownDisabledItems(view View, subviewID string) []int {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// GetDropDownDisabledItems return the list of DropDownList disabled item indexes.
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetDropDownDisabledItems(view View, subviewID ...string) []int {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if value := view.Get(DisabledItems); value != nil {
 | 
			
		||||
			if values, ok := value.([]interface{}); ok {
 | 
			
		||||
			if values, ok := value.([]any); ok {
 | 
			
		||||
				count := len(values)
 | 
			
		||||
				if count > 0 {
 | 
			
		||||
					result := make([]int, 0, count)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										279
									
								
								editView.go
								
								
								
								
							
							
						
						
									
										279
									
								
								editView.go
								
								
								
								
							| 
						 | 
				
			
			@ -48,7 +48,7 @@ type editViewData struct {
 | 
			
		|||
// NewEditView create new EditView object and return it
 | 
			
		||||
func NewEditView(session Session, params Params) EditView {
 | 
			
		||||
	view := new(editViewData)
 | 
			
		||||
	view.Init(session)
 | 
			
		||||
	view.init(session)
 | 
			
		||||
	setInitParams(view, params)
 | 
			
		||||
	return view
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -57,8 +57,8 @@ func newEditView(session Session) View {
 | 
			
		|||
	return NewEditView(session, nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (edit *editViewData) Init(session Session) {
 | 
			
		||||
	edit.viewData.Init(session)
 | 
			
		||||
func (edit *editViewData) init(session Session) {
 | 
			
		||||
	edit.viewData.init(session)
 | 
			
		||||
	edit.textChangeListeners = []func(EditView, string){}
 | 
			
		||||
	edit.tag = "EditView"
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -132,7 +132,7 @@ func (edit *editViewData) remove(tag string) {
 | 
			
		|||
 | 
			
		||||
	case Text:
 | 
			
		||||
		if exists {
 | 
			
		||||
			oldText := GetText(edit, "")
 | 
			
		||||
			oldText := GetText(edit)
 | 
			
		||||
			delete(edit.properties, tag)
 | 
			
		||||
			if oldText != "" {
 | 
			
		||||
				edit.textChanged("")
 | 
			
		||||
| 
						 | 
				
			
			@ -144,7 +144,7 @@ func (edit *editViewData) remove(tag string) {
 | 
			
		|||
 | 
			
		||||
	case EditViewPattern:
 | 
			
		||||
		if exists {
 | 
			
		||||
			oldText := GetEditViewPattern(edit, "")
 | 
			
		||||
			oldText := GetEditViewPattern(edit)
 | 
			
		||||
			delete(edit.properties, tag)
 | 
			
		||||
			if oldText != "" {
 | 
			
		||||
				if edit.created {
 | 
			
		||||
| 
						 | 
				
			
			@ -156,7 +156,7 @@ func (edit *editViewData) remove(tag string) {
 | 
			
		|||
 | 
			
		||||
	case EditViewType:
 | 
			
		||||
		if exists {
 | 
			
		||||
			oldType := GetEditViewType(edit, "")
 | 
			
		||||
			oldType := GetEditViewType(edit)
 | 
			
		||||
			delete(edit.properties, tag)
 | 
			
		||||
			if oldType != 0 {
 | 
			
		||||
				if edit.created {
 | 
			
		||||
| 
						 | 
				
			
			@ -168,10 +168,10 @@ func (edit *editViewData) remove(tag string) {
 | 
			
		|||
 | 
			
		||||
	case EditWrap:
 | 
			
		||||
		if exists {
 | 
			
		||||
			oldWrap := IsEditViewWrap(edit, "")
 | 
			
		||||
			oldWrap := IsEditViewWrap(edit)
 | 
			
		||||
			delete(edit.properties, tag)
 | 
			
		||||
			if GetEditViewType(edit, "") == MultiLineText {
 | 
			
		||||
				if wrap := IsEditViewWrap(edit, ""); wrap != oldWrap {
 | 
			
		||||
			if GetEditViewType(edit) == MultiLineText {
 | 
			
		||||
				if wrap := IsEditViewWrap(edit); wrap != oldWrap {
 | 
			
		||||
					if edit.created {
 | 
			
		||||
						if wrap {
 | 
			
		||||
							updateProperty(edit.htmlID(), "wrap", "soft", edit.session)
 | 
			
		||||
| 
						 | 
				
			
			@ -190,11 +190,11 @@ func (edit *editViewData) remove(tag string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (edit *editViewData) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (edit *editViewData) Set(tag string, value any) bool {
 | 
			
		||||
	return edit.set(edit.normalizeTag(tag), value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (edit *editViewData) set(tag string, value interface{}) bool {
 | 
			
		||||
func (edit *editViewData) set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		edit.remove(tag)
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			@ -202,13 +202,13 @@ func (edit *editViewData) set(tag string, value interface{}) bool {
 | 
			
		|||
 | 
			
		||||
	switch tag {
 | 
			
		||||
	case Text:
 | 
			
		||||
		oldText := GetText(edit, "")
 | 
			
		||||
		oldText := GetText(edit)
 | 
			
		||||
		if text, ok := value.(string); ok {
 | 
			
		||||
			edit.properties[Text] = text
 | 
			
		||||
			if text = GetText(edit, ""); oldText != text {
 | 
			
		||||
			if text = GetText(edit); oldText != text {
 | 
			
		||||
				edit.textChanged(text)
 | 
			
		||||
				if edit.created {
 | 
			
		||||
					if GetEditViewType(edit, "") == MultiLineText {
 | 
			
		||||
					if GetEditViewType(edit) == MultiLineText {
 | 
			
		||||
						updateInnerHTML(edit.htmlID(), edit.Session())
 | 
			
		||||
					} else {
 | 
			
		||||
						text = strings.ReplaceAll(text, `"`, `\"`)
 | 
			
		||||
| 
						 | 
				
			
			@ -224,10 +224,10 @@ func (edit *editViewData) set(tag string, value interface{}) bool {
 | 
			
		|||
		return false
 | 
			
		||||
 | 
			
		||||
	case Hint:
 | 
			
		||||
		oldText := GetHint(edit, "")
 | 
			
		||||
		oldText := GetHint(edit)
 | 
			
		||||
		if text, ok := value.(string); ok {
 | 
			
		||||
			edit.properties[Hint] = text
 | 
			
		||||
			if text = GetHint(edit, ""); oldText != text {
 | 
			
		||||
			if text = GetHint(edit); oldText != text {
 | 
			
		||||
				if edit.created {
 | 
			
		||||
					if text != "" {
 | 
			
		||||
						updateProperty(edit.htmlID(), "placeholder", text, edit.session)
 | 
			
		||||
| 
						 | 
				
			
			@ -242,9 +242,9 @@ func (edit *editViewData) set(tag string, value interface{}) bool {
 | 
			
		|||
		return false
 | 
			
		||||
 | 
			
		||||
	case MaxLength:
 | 
			
		||||
		oldMaxLength := GetMaxLength(edit, "")
 | 
			
		||||
		oldMaxLength := GetMaxLength(edit)
 | 
			
		||||
		if edit.setIntProperty(MaxLength, value) {
 | 
			
		||||
			if maxLength := GetMaxLength(edit, ""); maxLength != oldMaxLength {
 | 
			
		||||
			if maxLength := GetMaxLength(edit); maxLength != oldMaxLength {
 | 
			
		||||
				if edit.created {
 | 
			
		||||
					if maxLength > 0 {
 | 
			
		||||
						updateProperty(edit.htmlID(), "maxlength", strconv.Itoa(maxLength), edit.session)
 | 
			
		||||
| 
						 | 
				
			
			@ -261,7 +261,7 @@ func (edit *editViewData) set(tag string, value interface{}) bool {
 | 
			
		|||
	case ReadOnly:
 | 
			
		||||
		if edit.setBoolProperty(ReadOnly, value) {
 | 
			
		||||
			if edit.created {
 | 
			
		||||
				if IsReadOnly(edit, "") {
 | 
			
		||||
				if IsReadOnly(edit) {
 | 
			
		||||
					updateProperty(edit.htmlID(), ReadOnly, "", edit.session)
 | 
			
		||||
				} else {
 | 
			
		||||
					removeProperty(edit.htmlID(), ReadOnly, edit.session)
 | 
			
		||||
| 
						 | 
				
			
			@ -275,7 +275,7 @@ func (edit *editViewData) set(tag string, value interface{}) bool {
 | 
			
		|||
	case Spellcheck:
 | 
			
		||||
		if edit.setBoolProperty(Spellcheck, value) {
 | 
			
		||||
			if edit.created {
 | 
			
		||||
				updateBoolProperty(edit.htmlID(), Spellcheck, IsSpellcheck(edit, ""), edit.session)
 | 
			
		||||
				updateBoolProperty(edit.htmlID(), Spellcheck, IsSpellcheck(edit), edit.session)
 | 
			
		||||
			}
 | 
			
		||||
			edit.propertyChangedEvent(tag)
 | 
			
		||||
			return true
 | 
			
		||||
| 
						 | 
				
			
			@ -283,10 +283,10 @@ func (edit *editViewData) set(tag string, value interface{}) bool {
 | 
			
		|||
		return false
 | 
			
		||||
 | 
			
		||||
	case EditViewPattern:
 | 
			
		||||
		oldText := GetEditViewPattern(edit, "")
 | 
			
		||||
		oldText := GetEditViewPattern(edit)
 | 
			
		||||
		if text, ok := value.(string); ok {
 | 
			
		||||
			edit.properties[EditViewPattern] = text
 | 
			
		||||
			if text = GetEditViewPattern(edit, ""); oldText != text {
 | 
			
		||||
			if text = GetEditViewPattern(edit); oldText != text {
 | 
			
		||||
				if edit.created {
 | 
			
		||||
					if text != "" {
 | 
			
		||||
						updateProperty(edit.htmlID(), Pattern, text, edit.session)
 | 
			
		||||
| 
						 | 
				
			
			@ -301,9 +301,9 @@ func (edit *editViewData) set(tag string, value interface{}) bool {
 | 
			
		|||
		return false
 | 
			
		||||
 | 
			
		||||
	case EditViewType:
 | 
			
		||||
		oldType := GetEditViewType(edit, "")
 | 
			
		||||
		oldType := GetEditViewType(edit)
 | 
			
		||||
		if edit.setEnumProperty(EditViewType, value, enumProperties[EditViewType].values) {
 | 
			
		||||
			if GetEditViewType(edit, "") != oldType {
 | 
			
		||||
			if GetEditViewType(edit) != oldType {
 | 
			
		||||
				if edit.created {
 | 
			
		||||
					updateInnerHTML(edit.parentHTMLID(), edit.session)
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			@ -314,10 +314,10 @@ func (edit *editViewData) set(tag string, value interface{}) bool {
 | 
			
		|||
		return false
 | 
			
		||||
 | 
			
		||||
	case EditWrap:
 | 
			
		||||
		oldWrap := IsEditViewWrap(edit, "")
 | 
			
		||||
		oldWrap := IsEditViewWrap(edit)
 | 
			
		||||
		if edit.setBoolProperty(EditWrap, value) {
 | 
			
		||||
			if GetEditViewType(edit, "") == MultiLineText {
 | 
			
		||||
				if wrap := IsEditViewWrap(edit, ""); wrap != oldWrap {
 | 
			
		||||
			if GetEditViewType(edit) == MultiLineText {
 | 
			
		||||
				if wrap := IsEditViewWrap(edit); wrap != oldWrap {
 | 
			
		||||
					if edit.created {
 | 
			
		||||
						if wrap {
 | 
			
		||||
							updateProperty(edit.htmlID(), "wrap", "soft", edit.session)
 | 
			
		||||
| 
						 | 
				
			
			@ -333,80 +333,34 @@ func (edit *editViewData) set(tag string, value interface{}) bool {
 | 
			
		|||
		return false
 | 
			
		||||
 | 
			
		||||
	case EditTextChangedEvent:
 | 
			
		||||
		ok := edit.setChangeListeners(value)
 | 
			
		||||
		listeners, ok := valueToEventListeners[EditView, string](value)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			notCompatibleType(tag, value)
 | 
			
		||||
			return false
 | 
			
		||||
		} else if listeners == nil {
 | 
			
		||||
			listeners = []func(EditView, string){}
 | 
			
		||||
		}
 | 
			
		||||
		edit.textChangeListeners = listeners
 | 
			
		||||
		edit.propertyChangedEvent(tag)
 | 
			
		||||
		return ok
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return edit.viewData.set(tag, value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (edit *editViewData) setChangeListeners(value interface{}) bool {
 | 
			
		||||
	switch value := value.(type) {
 | 
			
		||||
	case func(EditView, string):
 | 
			
		||||
		edit.textChangeListeners = []func(EditView, string){value}
 | 
			
		||||
 | 
			
		||||
	case func(string):
 | 
			
		||||
		fn := func(_ EditView, text string) {
 | 
			
		||||
			value(text)
 | 
			
		||||
		}
 | 
			
		||||
		edit.textChangeListeners = []func(EditView, string){fn}
 | 
			
		||||
 | 
			
		||||
	case []func(EditView, string):
 | 
			
		||||
		edit.textChangeListeners = value
 | 
			
		||||
 | 
			
		||||
	case []func(string):
 | 
			
		||||
		listeners := make([]func(EditView, string), len(value))
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
			listeners[i] = func(_ EditView, text string) {
 | 
			
		||||
				v(text)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		edit.textChangeListeners = listeners
 | 
			
		||||
 | 
			
		||||
	case []interface{}:
 | 
			
		||||
		listeners := make([]func(EditView, string), len(value))
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
			switch v := v.(type) {
 | 
			
		||||
			case func(EditView, string):
 | 
			
		||||
				listeners[i] = v
 | 
			
		||||
 | 
			
		||||
			case func(string):
 | 
			
		||||
				listeners[i] = func(_ EditView, text string) {
 | 
			
		||||
					v(text)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			default:
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		edit.textChangeListeners = listeners
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (edit *editViewData) Get(tag string) interface{} {
 | 
			
		||||
func (edit *editViewData) Get(tag string) any {
 | 
			
		||||
	return edit.get(edit.normalizeTag(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (edit *editViewData) get(tag string) interface{} {
 | 
			
		||||
func (edit *editViewData) get(tag string) any {
 | 
			
		||||
	if tag == EditTextChangedEvent {
 | 
			
		||||
		return edit.textChangeListeners
 | 
			
		||||
	}
 | 
			
		||||
	return edit.viewData.get(tag)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (edit *editViewData) AppendText(text string) {
 | 
			
		||||
	if GetEditViewType(edit, "") == MultiLineText {
 | 
			
		||||
	if GetEditViewType(edit) == MultiLineText {
 | 
			
		||||
		if value := edit.getRaw(Text); value != nil {
 | 
			
		||||
			if textValue, ok := value.(string); ok {
 | 
			
		||||
				textValue += text
 | 
			
		||||
| 
						 | 
				
			
			@ -425,7 +379,7 @@ func (edit *editViewData) AppendText(text string) {
 | 
			
		|||
		}
 | 
			
		||||
		edit.set(Text, text)
 | 
			
		||||
	} else {
 | 
			
		||||
		edit.set(Text, GetText(edit, "")+text)
 | 
			
		||||
		edit.set(Text, GetText(edit)+text)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -437,7 +391,7 @@ func (edit *editViewData) textChanged(newText string) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (edit *editViewData) htmlTag() string {
 | 
			
		||||
	if GetEditViewType(edit, "") == MultiLineText {
 | 
			
		||||
	if GetEditViewType(edit) == MultiLineText {
 | 
			
		||||
		return "textarea"
 | 
			
		||||
	}
 | 
			
		||||
	return "input"
 | 
			
		||||
| 
						 | 
				
			
			@ -447,14 +401,14 @@ func (edit *editViewData) htmlProperties(self View, buffer *strings.Builder) {
 | 
			
		|||
	edit.viewData.htmlProperties(self, buffer)
 | 
			
		||||
 | 
			
		||||
	writeSpellcheck := func() {
 | 
			
		||||
		if spellcheck := IsSpellcheck(edit, ""); spellcheck {
 | 
			
		||||
		if spellcheck := IsSpellcheck(edit); spellcheck {
 | 
			
		||||
			buffer.WriteString(` spellcheck="true"`)
 | 
			
		||||
		} else {
 | 
			
		||||
			buffer.WriteString(` spellcheck="false"`)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	editType := GetEditViewType(edit, "")
 | 
			
		||||
	editType := GetEditViewType(edit)
 | 
			
		||||
	switch editType {
 | 
			
		||||
	case SingleLineText:
 | 
			
		||||
		buffer.WriteString(` type="text" inputmode="text"`)
 | 
			
		||||
| 
						 | 
				
			
			@ -476,7 +430,7 @@ func (edit *editViewData) htmlProperties(self View, buffer *strings.Builder) {
 | 
			
		|||
		buffer.WriteString(` type="tel" inputmode="tel"`)
 | 
			
		||||
 | 
			
		||||
	case MultiLineText:
 | 
			
		||||
		if IsEditViewWrap(edit, "") {
 | 
			
		||||
		if IsEditViewWrap(edit) {
 | 
			
		||||
			buffer.WriteString(` wrap="soft"`)
 | 
			
		||||
		} else {
 | 
			
		||||
			buffer.WriteString(` wrap="off"`)
 | 
			
		||||
| 
						 | 
				
			
			@ -484,11 +438,11 @@ func (edit *editViewData) htmlProperties(self View, buffer *strings.Builder) {
 | 
			
		|||
		writeSpellcheck()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if IsReadOnly(edit, "") {
 | 
			
		||||
	if IsReadOnly(edit) {
 | 
			
		||||
		buffer.WriteString(` readonly`)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if maxLength := GetMaxLength(edit, ""); maxLength > 0 {
 | 
			
		||||
	if maxLength := GetMaxLength(edit); maxLength > 0 {
 | 
			
		||||
		buffer.WriteString(` maxlength="`)
 | 
			
		||||
		buffer.WriteString(strconv.Itoa(maxLength))
 | 
			
		||||
		buffer.WriteByte('"')
 | 
			
		||||
| 
						 | 
				
			
			@ -501,21 +455,21 @@ func (edit *editViewData) htmlProperties(self View, buffer *strings.Builder) {
 | 
			
		|||
		return textToJS(text)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if hint := GetHint(edit, ""); hint != "" {
 | 
			
		||||
	if hint := GetHint(edit); hint != "" {
 | 
			
		||||
		buffer.WriteString(` placeholder="`)
 | 
			
		||||
		buffer.WriteString(convertText(hint))
 | 
			
		||||
		buffer.WriteByte('"')
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	buffer.WriteString(` oninput="editViewInputEvent(this)"`)
 | 
			
		||||
	if pattern := GetEditViewPattern(edit, ""); pattern != "" {
 | 
			
		||||
	if pattern := GetEditViewPattern(edit); pattern != "" {
 | 
			
		||||
		buffer.WriteString(` pattern="`)
 | 
			
		||||
		buffer.WriteString(convertText(pattern))
 | 
			
		||||
		buffer.WriteByte('"')
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if editType != MultiLineText {
 | 
			
		||||
		if text := GetText(edit, ""); text != "" {
 | 
			
		||||
		if text := GetText(edit); text != "" {
 | 
			
		||||
			buffer.WriteString(` value="`)
 | 
			
		||||
			buffer.WriteString(convertText(text))
 | 
			
		||||
			buffer.WriteByte('"')
 | 
			
		||||
| 
						 | 
				
			
			@ -524,25 +478,25 @@ func (edit *editViewData) htmlProperties(self View, buffer *strings.Builder) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (edit *editViewData) htmlDisabledProperties(self View, buffer *strings.Builder) {
 | 
			
		||||
	if IsDisabled(self, "") {
 | 
			
		||||
	if IsDisabled(self) {
 | 
			
		||||
		buffer.WriteString(` disabled`)
 | 
			
		||||
	}
 | 
			
		||||
	edit.viewData.htmlDisabledProperties(self, buffer)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (edit *editViewData) htmlSubviews(self View, buffer *strings.Builder) {
 | 
			
		||||
	if GetEditViewType(edit, "") == MultiLineText {
 | 
			
		||||
		buffer.WriteString(textToJS(GetText(edit, "")))
 | 
			
		||||
	if GetEditViewType(edit) == MultiLineText {
 | 
			
		||||
		buffer.WriteString(textToJS(GetText(edit)))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (edit *editViewData) handleCommand(self View, command string, data DataObject) bool {
 | 
			
		||||
	switch command {
 | 
			
		||||
	case "textChanged":
 | 
			
		||||
		oldText := GetText(edit, "")
 | 
			
		||||
		oldText := GetText(edit)
 | 
			
		||||
		if text, ok := data.PropertyValue("text"); ok {
 | 
			
		||||
			edit.properties[Text] = text
 | 
			
		||||
			if text := GetText(edit, ""); text != oldText {
 | 
			
		||||
			if text := GetText(edit); text != oldText {
 | 
			
		||||
				edit.textChanged(text)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -553,10 +507,10 @@ func (edit *editViewData) handleCommand(self View, command string, data DataObje
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// GetText returns a text of the EditView subview.
 | 
			
		||||
// If the second argument (subviewID) is "" then a text of the first argument (view) is returned.
 | 
			
		||||
func GetText(view View, subviewID string) string {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a text of the first argument (view) is returned.
 | 
			
		||||
func GetText(view View, subviewID ...string) string {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if value := view.getRaw(Text); value != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -569,10 +523,10 @@ func GetText(view View, subviewID string) string {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// GetHint returns a hint text of the subview.
 | 
			
		||||
// If the second argument (subviewID) is "" then a text of the first argument (view) is returned.
 | 
			
		||||
func GetHint(view View, subviewID string) string {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a text of the first argument (view) is returned.
 | 
			
		||||
func GetHint(view View, subviewID ...string) string {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if text, ok := stringProperty(view, Hint, view.Session()); ok {
 | 
			
		||||
| 
						 | 
				
			
			@ -590,82 +544,41 @@ func GetHint(view View, subviewID string) string {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// GetMaxLength returns a maximal lenght of EditView. If a maximal lenght is not limited  then 0 is returned
 | 
			
		||||
// If the second argument (subviewID) is "" then a value of the first argument (view) is returned.
 | 
			
		||||
func GetMaxLength(view View, subviewID string) int {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if result, ok := intStyledProperty(view, MaxLength, 0); ok {
 | 
			
		||||
			return result
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value of the first argument (view) is returned.
 | 
			
		||||
func GetMaxLength(view View, subviewID ...string) int {
 | 
			
		||||
	return intStyledProperty(view, subviewID, MaxLength, 0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsReadOnly returns the true if a EditView works in read only mode.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value of the first argument (view) is returned.
 | 
			
		||||
func IsReadOnly(view View, subviewID string) bool {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if result, ok := boolStyledProperty(view, ReadOnly); ok {
 | 
			
		||||
			return result
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value of the first argument (view) is returned.
 | 
			
		||||
func IsReadOnly(view View, subviewID ...string) bool {
 | 
			
		||||
	return boolStyledProperty(view, subviewID, ReadOnly, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsSpellcheck returns a value of the Spellcheck property of EditView.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func IsSpellcheck(view View, subviewID string) bool {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if spellcheck, ok := boolStyledProperty(view, Spellcheck); ok {
 | 
			
		||||
			return spellcheck
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func IsSpellcheck(view View, subviewID ...string) bool {
 | 
			
		||||
	return boolStyledProperty(view, subviewID, Spellcheck, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetTextChangedListeners returns the TextChangedListener list of an EditView or MultiLineEditView subview.
 | 
			
		||||
// If there are no listeners then the empty list is returned
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTextChangedListeners(view View, subviewID string) []func(EditView, string) {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if value := view.Get(EditTextChangedEvent); value != nil {
 | 
			
		||||
			if result, ok := value.([]func(EditView, string)); ok {
 | 
			
		||||
				return result
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return []func(EditView, string){}
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTextChangedListeners(view View, subviewID ...string) []func(EditView, string) {
 | 
			
		||||
	return getEventListeners[EditView, string](view, subviewID, EditTextChangedEvent)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetEditViewType returns a value of the Type property of EditView.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetEditViewType(view View, subviewID string) int {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view == nil {
 | 
			
		||||
		return SingleLineText
 | 
			
		||||
	}
 | 
			
		||||
	t, _ := enumStyledProperty(view, EditViewType, SingleLineText)
 | 
			
		||||
	return t
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetEditViewType(view View, subviewID ...string) int {
 | 
			
		||||
	return enumStyledProperty(view, subviewID, EditViewType, SingleLineText, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetEditViewPattern returns a value of the Pattern property of EditView.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetEditViewPattern(view View, subviewID string) string {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetEditViewPattern(view View, subviewID ...string) string {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if pattern, ok := stringProperty(view, EditViewPattern, view.Session()); ok {
 | 
			
		||||
| 
						 | 
				
			
			@ -683,22 +596,13 @@ func GetEditViewPattern(view View, subviewID string) string {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// IsEditViewWrap returns a value of the EditWrap property of MultiLineEditView.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func IsEditViewWrap(view View, subviewID string) bool {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if wrap, ok := boolStyledProperty(view, EditWrap); ok {
 | 
			
		||||
			return wrap
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func IsEditViewWrap(view View, subviewID ...string) bool {
 | 
			
		||||
	return boolStyledProperty(view, subviewID, EditWrap, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AppendEditText appends the text to the EditView content.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func AppendEditText(view View, subviewID string, text string) {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		if edit := EditViewByID(view, subviewID); edit != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -713,14 +617,7 @@ func AppendEditText(view View, subviewID string, text string) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// GetCaretColor returns the color of the text input carret.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetCaretColor(view View, subviewID string) Color {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view == nil {
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
	t, _ := colorStyledProperty(view, CaretColor)
 | 
			
		||||
	return t
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetCaretColor(view View, subviewID ...string) Color {
 | 
			
		||||
	return colorStyledProperty(view, subviewID, CaretColor, false)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										120
									
								
								filePicker.go
								
								
								
								
							
							
						
						
									
										120
									
								
								filePicker.go
								
								
								
								
							| 
						 | 
				
			
			@ -72,7 +72,7 @@ func (file *FileInfo) initBy(node DataValue) {
 | 
			
		|||
// NewFilePicker create new FilePicker object and return it
 | 
			
		||||
func NewFilePicker(session Session, params Params) FilePicker {
 | 
			
		||||
	view := new(filePickerData)
 | 
			
		||||
	view.Init(session)
 | 
			
		||||
	view.init(session)
 | 
			
		||||
	setInitParams(view, params)
 | 
			
		||||
	return view
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -81,8 +81,8 @@ func newFilePicker(session Session) View {
 | 
			
		|||
	return NewFilePicker(session, nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (picker *filePickerData) Init(session Session) {
 | 
			
		||||
	picker.viewData.Init(session)
 | 
			
		||||
func (picker *filePickerData) init(session Session) {
 | 
			
		||||
	picker.viewData.init(session)
 | 
			
		||||
	picker.tag = "FilePicker"
 | 
			
		||||
	picker.files = []FileInfo{}
 | 
			
		||||
	picker.loader = map[int]func(FileInfo, []byte){}
 | 
			
		||||
| 
						 | 
				
			
			@ -139,11 +139,11 @@ func (picker *filePickerData) remove(tag string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (picker *filePickerData) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (picker *filePickerData) Set(tag string, value any) bool {
 | 
			
		||||
	return picker.set(strings.ToLower(tag), value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (picker *filePickerData) set(tag string, value interface{}) bool {
 | 
			
		||||
func (picker *filePickerData) set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		picker.remove(tag)
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			@ -151,57 +151,14 @@ func (picker *filePickerData) set(tag string, value interface{}) bool {
 | 
			
		|||
 | 
			
		||||
	switch tag {
 | 
			
		||||
	case FileSelectedEvent:
 | 
			
		||||
		switch value := value.(type) {
 | 
			
		||||
		case func(FilePicker, []FileInfo):
 | 
			
		||||
			picker.fileSelectedListeners = []func(FilePicker, []FileInfo){value}
 | 
			
		||||
 | 
			
		||||
		case func([]FileInfo):
 | 
			
		||||
			fn := func(_ FilePicker, files []FileInfo) {
 | 
			
		||||
				value(files)
 | 
			
		||||
			}
 | 
			
		||||
			picker.fileSelectedListeners = []func(FilePicker, []FileInfo){fn}
 | 
			
		||||
 | 
			
		||||
		case []func(FilePicker, []FileInfo):
 | 
			
		||||
			picker.fileSelectedListeners = value
 | 
			
		||||
 | 
			
		||||
		case []func([]FileInfo):
 | 
			
		||||
			listeners := make([]func(FilePicker, []FileInfo), len(value))
 | 
			
		||||
			for i, val := range value {
 | 
			
		||||
				if val == nil {
 | 
			
		||||
					notCompatibleType(tag, val)
 | 
			
		||||
					return false
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				listeners[i] = func(_ FilePicker, files []FileInfo) {
 | 
			
		||||
					val(files)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			picker.fileSelectedListeners = listeners
 | 
			
		||||
 | 
			
		||||
		case []interface{}:
 | 
			
		||||
			listeners := make([]func(FilePicker, []FileInfo), len(value))
 | 
			
		||||
			for i, val := range value {
 | 
			
		||||
				if val == nil {
 | 
			
		||||
					notCompatibleType(tag, val)
 | 
			
		||||
					return false
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				switch val := val.(type) {
 | 
			
		||||
				case func(FilePicker, []FileInfo):
 | 
			
		||||
					listeners[i] = val
 | 
			
		||||
 | 
			
		||||
				case func([]FileInfo):
 | 
			
		||||
					listeners[i] = func(_ FilePicker, files []FileInfo) {
 | 
			
		||||
						val(files)
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
				default:
 | 
			
		||||
					notCompatibleType(tag, val)
 | 
			
		||||
					return false
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			picker.fileSelectedListeners = listeners
 | 
			
		||||
		listeners, ok := valueToEventListeners[FilePicker, []FileInfo](value)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			notCompatibleType(tag, value)
 | 
			
		||||
			return false
 | 
			
		||||
		} else if listeners == nil {
 | 
			
		||||
			listeners = []func(FilePicker, []FileInfo){}
 | 
			
		||||
		}
 | 
			
		||||
		picker.fileSelectedListeners = listeners
 | 
			
		||||
		picker.propertyChangedEvent(tag)
 | 
			
		||||
		return true
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -294,7 +251,7 @@ func (picker *filePickerData) htmlProperties(self View, buffer *strings.Builder)
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	buffer.WriteString(` type="file"`)
 | 
			
		||||
	if multiple, ok := boolStyledProperty(picker, Multiple); ok && multiple {
 | 
			
		||||
	if IsMultipleFilePicker(picker) {
 | 
			
		||||
		buffer.WriteString(` multiple`)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -305,7 +262,7 @@ func (picker *filePickerData) htmlProperties(self View, buffer *strings.Builder)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (picker *filePickerData) htmlDisabledProperties(self View, buffer *strings.Builder) {
 | 
			
		||||
	if IsDisabled(self, "") {
 | 
			
		||||
	if IsDisabled(self) {
 | 
			
		||||
		buffer.WriteString(` disabled`)
 | 
			
		||||
	}
 | 
			
		||||
	picker.viewData.htmlDisabledProperties(self, buffer)
 | 
			
		||||
| 
						 | 
				
			
			@ -377,9 +334,14 @@ func (picker *filePickerData) handleCommand(self View, command string, data Data
 | 
			
		|||
 | 
			
		||||
// GetFilePickerFiles returns the list of FilePicker selected files
 | 
			
		||||
// If there are no files selected then an empty slice is returned (the result is always not nil)
 | 
			
		||||
// If the second argument (subviewID) is "" then selected files of the first argument (view) is returned
 | 
			
		||||
func GetFilePickerFiles(view View, subviewID string) []FileInfo {
 | 
			
		||||
	if picker := FilePickerByID(view, subviewID); picker != nil {
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then selected files of the first argument (view) is returned
 | 
			
		||||
func GetFilePickerFiles(view View, subviewID ...string) []FileInfo {
 | 
			
		||||
	subview := ""
 | 
			
		||||
	if len(subviewID) > 0 {
 | 
			
		||||
		subview = subviewID[0]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if picker := FilePickerByID(view, subview); picker != nil {
 | 
			
		||||
		return picker.Files()
 | 
			
		||||
	}
 | 
			
		||||
	return []FileInfo{}
 | 
			
		||||
| 
						 | 
				
			
			@ -395,24 +357,16 @@ func LoadFilePickerFile(view View, subviewID string, file FileInfo, result func(
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// IsMultipleFilePicker returns "true" if multiple files can be selected in the FilePicker, "false" otherwise.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func IsMultipleFilePicker(view View, subviewID string) bool {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if result, ok := boolStyledProperty(view, Multiple); ok {
 | 
			
		||||
			return result
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func IsMultipleFilePicker(view View, subviewID ...string) bool {
 | 
			
		||||
	return boolStyledProperty(view, subviewID, Multiple, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetFilePickerAccept returns sets the list of allowed file extensions or MIME types.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetFilePickerAccept(view View, subviewID string) []string {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetFilePickerAccept(view View, subviewID ...string) []string {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		accept, ok := stringProperty(view, Accept, view.Session())
 | 
			
		||||
| 
						 | 
				
			
			@ -434,17 +388,7 @@ func GetFilePickerAccept(view View, subviewID string) []string {
 | 
			
		|||
 | 
			
		||||
// GetFileSelectedListeners returns the "file-selected-event" listener list.
 | 
			
		||||
// If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetFileSelectedListeners(view View, subviewID string) []func(FilePicker, []FileInfo) {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if value := view.Get(FileSelectedEvent); value != nil {
 | 
			
		||||
			if result, ok := value.([]func(FilePicker, []FileInfo)); ok {
 | 
			
		||||
				return result
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return []func(FilePicker, []FileInfo){}
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetFileSelectedListeners(view View, subviewID ...string) []func(FilePicker, []FileInfo) {
 | 
			
		||||
	return getEventListeners[FilePicker, []FileInfo](view, subviewID, FileSelectedEvent)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,22 +20,22 @@ const (
 | 
			
		|||
	LostFocusEvent = "lost-focus-event"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func valueToFocusListeners(value interface{}) ([]func(View), bool) {
 | 
			
		||||
func valueToNoParamListeners[V any](value any) ([]func(V), bool) {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		return nil, true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch value := value.(type) {
 | 
			
		||||
	case func(View):
 | 
			
		||||
		return []func(View){value}, true
 | 
			
		||||
	case func(V):
 | 
			
		||||
		return []func(V){value}, true
 | 
			
		||||
 | 
			
		||||
	case func():
 | 
			
		||||
		fn := func(View) {
 | 
			
		||||
		fn := func(V) {
 | 
			
		||||
			value()
 | 
			
		||||
		}
 | 
			
		||||
		return []func(View){fn}, true
 | 
			
		||||
		return []func(V){fn}, true
 | 
			
		||||
 | 
			
		||||
	case []func(View):
 | 
			
		||||
	case []func(V):
 | 
			
		||||
		if len(value) == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -51,33 +51,33 @@ func valueToFocusListeners(value interface{}) ([]func(View), bool) {
 | 
			
		|||
		if count == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		listeners := make([]func(View), count)
 | 
			
		||||
		listeners := make([]func(V), count)
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
			listeners[i] = func(View) {
 | 
			
		||||
			listeners[i] = func(V) {
 | 
			
		||||
				v()
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners, true
 | 
			
		||||
 | 
			
		||||
	case []interface{}:
 | 
			
		||||
	case []any:
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		listeners := make([]func(View), count)
 | 
			
		||||
		listeners := make([]func(V), count)
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
			switch v := v.(type) {
 | 
			
		||||
			case func(View):
 | 
			
		||||
			case func(V):
 | 
			
		||||
				listeners[i] = v
 | 
			
		||||
 | 
			
		||||
			case func():
 | 
			
		||||
				listeners[i] = func(View) {
 | 
			
		||||
				listeners[i] = func(V) {
 | 
			
		||||
					v()
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -96,8 +96,8 @@ var focusEvents = map[string]struct{ jsEvent, jsFunc string }{
 | 
			
		|||
	LostFocusEvent: {jsEvent: "onblur", jsFunc: "blurEvent"},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (view *viewData) setFocusListener(tag string, value interface{}) bool {
 | 
			
		||||
	listeners, ok := valueToFocusListeners(value)
 | 
			
		||||
func (view *viewData) setFocusListener(tag string, value any) bool {
 | 
			
		||||
	listeners, ok := valueToNoParamListeners[View](value)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		notCompatibleType(tag, value)
 | 
			
		||||
		return false
 | 
			
		||||
| 
						 | 
				
			
			@ -125,10 +125,11 @@ func (view *viewData) removeFocusListener(tag string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getFocusListeners(view View, subviewID string, tag string) []func(View) {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
func getFocusListeners(view View, subviewID []string, tag string) []func(View) {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if value := view.Get(tag); value != nil {
 | 
			
		||||
			if result, ok := value.([]func(View)); ok {
 | 
			
		||||
| 
						 | 
				
			
			@ -148,13 +149,13 @@ func focusEventsHtml(view View, buffer *strings.Builder) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// GetFocusListeners returns a FocusListener list. If there are no listeners then the empty list is returned
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetFocusListeners(view View, subviewID string) []func(View) {
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetFocusListeners(view View, subviewID ...string) []func(View) {
 | 
			
		||||
	return getFocusListeners(view, subviewID, FocusEvent)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetLostFocusListeners returns a LostFocusListener list. If there are no listeners then the empty list is returned
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetLostFocusListeners(view View, subviewID string) []func(View) {
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetLostFocusListeners(view View, subviewID ...string) []func(View) {
 | 
			
		||||
	return getFocusListeners(view, subviewID, LostFocusEvent)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										2
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										2
									
								
								go.mod
								
								
								
								
							| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
module github.com/anoshenko/rui
 | 
			
		||||
 | 
			
		||||
go 1.17
 | 
			
		||||
go 1.18
 | 
			
		||||
 | 
			
		||||
require github.com/gorilla/websocket v1.5.0
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										128
									
								
								gridLayout.go
								
								
								
								
							
							
						
						
									
										128
									
								
								gridLayout.go
								
								
								
								
							| 
						 | 
				
			
			@ -17,7 +17,7 @@ type gridLayoutData struct {
 | 
			
		|||
// NewGridLayout create new GridLayout object and return it
 | 
			
		||||
func NewGridLayout(session Session, params Params) GridLayout {
 | 
			
		||||
	view := new(gridLayoutData)
 | 
			
		||||
	view.Init(session)
 | 
			
		||||
	view.init(session)
 | 
			
		||||
	setInitParams(view, params)
 | 
			
		||||
	return view
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -27,8 +27,8 @@ func newGridLayout(session Session) View {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Init initialize fields of GridLayout by default values
 | 
			
		||||
func (gridLayout *gridLayoutData) Init(session Session) {
 | 
			
		||||
	gridLayout.viewsContainerData.Init(session)
 | 
			
		||||
func (gridLayout *gridLayoutData) init(session Session) {
 | 
			
		||||
	gridLayout.viewsContainerData.init(session)
 | 
			
		||||
	gridLayout.tag = "GridLayout"
 | 
			
		||||
	gridLayout.systemClass = "ruiGridLayout"
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -37,15 +37,17 @@ func (gridLayout *gridLayoutData) String() string {
 | 
			
		|||
	return getViewString(gridLayout)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (style *viewStyle) setGridCellSize(tag string, value interface{}) bool {
 | 
			
		||||
func (style *viewStyle) setGridCellSize(tag string, value any) bool {
 | 
			
		||||
	setValues := func(values []string) bool {
 | 
			
		||||
		count := len(values)
 | 
			
		||||
		if count > 1 {
 | 
			
		||||
			sizes := make([]interface{}, count)
 | 
			
		||||
			sizes := make([]any, count)
 | 
			
		||||
			for i, val := range values {
 | 
			
		||||
				val = strings.Trim(val, " \t\n\r")
 | 
			
		||||
				if isConstantName(val) {
 | 
			
		||||
					sizes[i] = val
 | 
			
		||||
				} else if fn := parseSizeFunc(val); fn != nil {
 | 
			
		||||
					sizes[i] = SizeUnit{Type: SizeFunction, Function: fn}
 | 
			
		||||
				} else if size, err := stringToSizeUnit(val); err == nil {
 | 
			
		||||
					sizes[i] = size
 | 
			
		||||
				} else {
 | 
			
		||||
| 
						 | 
				
			
			@ -99,13 +101,13 @@ func (style *viewStyle) setGridCellSize(tag string, value interface{}) bool {
 | 
			
		|||
				return false
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		case []interface{}:
 | 
			
		||||
		case []any:
 | 
			
		||||
			count := len(value)
 | 
			
		||||
			if count == 0 {
 | 
			
		||||
				invalidPropertyValue(tag, value)
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
			sizes := make([]interface{}, count)
 | 
			
		||||
			sizes := make([]any, count)
 | 
			
		||||
			for i, val := range value {
 | 
			
		||||
				switch val := val.(type) {
 | 
			
		||||
				case SizeUnit:
 | 
			
		||||
| 
						 | 
				
			
			@ -145,7 +147,7 @@ func (style *viewStyle) gridCellSizesCSS(tag string, session Session) string {
 | 
			
		|||
 | 
			
		||||
	case 1:
 | 
			
		||||
		if cellSize[0].Type != Auto {
 | 
			
		||||
			return `repeat(auto-fill, ` + cellSize[0].cssString(`auto`) + `)`
 | 
			
		||||
			return `repeat(auto-fill, ` + cellSize[0].cssString(`auto`, session) + `)`
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
| 
						 | 
				
			
			@ -161,14 +163,14 @@ func (style *viewStyle) gridCellSizesCSS(tag string, session Session) string {
 | 
			
		|||
		}
 | 
			
		||||
		if !allAuto {
 | 
			
		||||
			if allEqual {
 | 
			
		||||
				return fmt.Sprintf(`repeat(%d, %s)`, len(cellSize), cellSize[0].cssString(`auto`))
 | 
			
		||||
				return fmt.Sprintf(`repeat(%d, %s)`, len(cellSize), cellSize[0].cssString(`auto`, session))
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			buffer := allocStringBuilder()
 | 
			
		||||
			defer freeStringBuilder(buffer)
 | 
			
		||||
			for _, size := range cellSize {
 | 
			
		||||
				buffer.WriteRune(' ')
 | 
			
		||||
				buffer.WriteString(size.cssString(`auto`))
 | 
			
		||||
				buffer.WriteString(size.cssString(`auto`, session))
 | 
			
		||||
			}
 | 
			
		||||
			return buffer.String()
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -195,14 +197,14 @@ func (gridLayout *gridLayoutData) normalizeTag(tag string) string {
 | 
			
		|||
	return tag
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gridLayout *gridLayoutData) Get(tag string) interface{} {
 | 
			
		||||
func (gridLayout *gridLayoutData) Get(tag string) any {
 | 
			
		||||
	return gridLayout.get(gridLayout.normalizeTag(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gridLayout *gridLayoutData) get(tag string) interface{} {
 | 
			
		||||
func (gridLayout *gridLayoutData) get(tag string) any {
 | 
			
		||||
	if tag == Gap {
 | 
			
		||||
		rowGap := GetGridRowGap(gridLayout, "")
 | 
			
		||||
		columnGap := GetGridColumnGap(gridLayout, "")
 | 
			
		||||
		rowGap := GetGridRowGap(gridLayout)
 | 
			
		||||
		columnGap := GetGridColumnGap(gridLayout)
 | 
			
		||||
		if rowGap.Equal(columnGap) {
 | 
			
		||||
			return rowGap
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -239,22 +241,18 @@ func (gridLayout *gridLayoutData) remove(tag string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gridLayout *gridLayoutData) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (gridLayout *gridLayoutData) Set(tag string, value any) bool {
 | 
			
		||||
	return gridLayout.set(gridLayout.normalizeTag(tag), value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gridLayout *gridLayoutData) set(tag string, value interface{}) bool {
 | 
			
		||||
func (gridLayout *gridLayoutData) set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		gridLayout.remove(tag)
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if tag == Gap {
 | 
			
		||||
		if gridLayout.set(GridRowGap, value) && gridLayout.set(GridColumnGap, value) {
 | 
			
		||||
			gridLayout.propertyChangedEvent(Gap)
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
		return false
 | 
			
		||||
		return gridLayout.set(GridRowGap, value) && gridLayout.set(GridColumnGap, value)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if gridLayout.viewsContainerData.set(tag, value) {
 | 
			
		||||
| 
						 | 
				
			
			@ -285,7 +283,7 @@ func gridCellSizes(properties Properties, tag string, session Session) []SizeUni
 | 
			
		|||
		case SizeUnit:
 | 
			
		||||
			return []SizeUnit{value}
 | 
			
		||||
 | 
			
		||||
		case []interface{}:
 | 
			
		||||
		case []any:
 | 
			
		||||
			result := make([]SizeUnit, len(value))
 | 
			
		||||
			for i, val := range value {
 | 
			
		||||
				result[i] = AutoSize()
 | 
			
		||||
| 
						 | 
				
			
			@ -323,53 +321,29 @@ func (gridLayout *gridLayoutData) cssStyle(self View, builder cssBuilder) {
 | 
			
		|||
*/
 | 
			
		||||
 | 
			
		||||
// GetCellVerticalAlign returns the vertical align of a GridLayout cell content: TopAlign (0), BottomAlign (1), CenterAlign (2), StretchAlign (3)
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetCellVerticalAlign(view View, subviewID string) int {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if align, ok := enumStyledProperty(view, CellVerticalAlign, StretchAlign); ok {
 | 
			
		||||
			return align
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return StretchAlign
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetCellVerticalAlign(view View, subviewID ...string) int {
 | 
			
		||||
	return enumStyledProperty(view, subviewID, CellVerticalAlign, StretchAlign, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetCellHorizontalAlign returns the vertical align of a GridLayout cell content: LeftAlign (0), RightAlign (1), CenterAlign (2), StretchAlign (3)
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetCellHorizontalAlign(view View, subviewID string) int {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if align, ok := enumStyledProperty(view, CellHorizontalAlign, StretchAlign); ok {
 | 
			
		||||
			return align
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return StretchAlign
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetCellHorizontalAlign(view View, subviewID ...string) int {
 | 
			
		||||
	return enumStyledProperty(view, subviewID, CellHorizontalAlign, StretchAlign, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetGridAutoFlow returns the value of the  "grid-auto-flow" property
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetGridAutoFlow(view View, subviewID string) int {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if align, ok := enumStyledProperty(view, GridAutoFlow, 0); ok {
 | 
			
		||||
			return align
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetGridAutoFlow(view View, subviewID ...string) int {
 | 
			
		||||
	return enumStyledProperty(view, subviewID, GridAutoFlow, 0, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetCellWidth returns the width of a GridLayout cell. If the result is an empty array, then the width is not set.
 | 
			
		||||
// If the result is a single value array, then the width of all cell is equal.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetCellWidth(view View, subviewID string) []SizeUnit {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetCellWidth(view View, subviewID ...string) []SizeUnit {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		return gridCellSizes(view, CellWidth, view.Session())
 | 
			
		||||
| 
						 | 
				
			
			@ -379,10 +353,10 @@ func GetCellWidth(view View, subviewID string) []SizeUnit {
 | 
			
		|||
 | 
			
		||||
// GetCellHeight returns the height of a GridLayout cell. If the result is an empty array, then the height is not set.
 | 
			
		||||
// If the result is a single value array, then the height of all cell is equal.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetCellHeight(view View, subviewID string) []SizeUnit {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetCellHeight(view View, subviewID ...string) []SizeUnit {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		return gridCellSizes(view, CellHeight, view.Session())
 | 
			
		||||
| 
						 | 
				
			
			@ -391,29 +365,13 @@ func GetCellHeight(view View, subviewID string) []SizeUnit {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// GetGridRowGap returns the gap between GridLayout rows.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetGridRowGap(view View, subviewID string) SizeUnit {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if result, ok := sizeStyledProperty(view, GridRowGap); ok {
 | 
			
		||||
			return result
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return AutoSize()
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetGridRowGap(view View, subviewID ...string) SizeUnit {
 | 
			
		||||
	return sizeStyledProperty(view, subviewID, GridRowGap, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetGridColumnGap returns the gap between GridLayout columns.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetGridColumnGap(view View, subviewID string) SizeUnit {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if result, ok := sizeStyledProperty(view, GridColumnGap); ok {
 | 
			
		||||
			return result
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return AutoSize()
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetGridColumnGap(view View, subviewID ...string) SizeUnit {
 | 
			
		||||
	return sizeStyledProperty(view, subviewID, GridColumnGap, false)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										152
									
								
								imageView.go
								
								
								
								
							
							
						
						
									
										152
									
								
								imageView.go
								
								
								
								
							| 
						 | 
				
			
			@ -54,7 +54,7 @@ type imageViewData struct {
 | 
			
		|||
// NewImageView create new ImageView object and return it
 | 
			
		||||
func NewImageView(session Session, params Params) ImageView {
 | 
			
		||||
	view := new(imageViewData)
 | 
			
		||||
	view.Init(session)
 | 
			
		||||
	view.init(session)
 | 
			
		||||
	setInitParams(view, params)
 | 
			
		||||
	return view
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -64,8 +64,8 @@ func newImageView(session Session) View {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Init initialize fields of imageView by default values
 | 
			
		||||
func (imageView *imageViewData) Init(session Session) {
 | 
			
		||||
	imageView.viewData.Init(session)
 | 
			
		||||
func (imageView *imageViewData) init(session Session) {
 | 
			
		||||
	imageView.viewData.init(session)
 | 
			
		||||
	imageView.tag = "ImageView"
 | 
			
		||||
	//imageView.systemClass = "ruiImageView"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -114,82 +114,11 @@ func (imageView *imageViewData) remove(tag string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (imageView *imageViewData) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (imageView *imageViewData) Set(tag string, value any) bool {
 | 
			
		||||
	return imageView.set(imageView.normalizeTag(tag), value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func valueToImageListeners(value interface{}) ([]func(ImageView), bool) {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		return nil, true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch value := value.(type) {
 | 
			
		||||
	case func(ImageView):
 | 
			
		||||
		return []func(ImageView){value}, true
 | 
			
		||||
 | 
			
		||||
	case func():
 | 
			
		||||
		fn := func(ImageView) {
 | 
			
		||||
			value()
 | 
			
		||||
		}
 | 
			
		||||
		return []func(ImageView){fn}, true
 | 
			
		||||
 | 
			
		||||
	case []func(ImageView):
 | 
			
		||||
		if len(value) == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		for _, fn := range value {
 | 
			
		||||
			if fn == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return value, true
 | 
			
		||||
 | 
			
		||||
	case []func():
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		listeners := make([]func(ImageView), count)
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
			listeners[i] = func(ImageView) {
 | 
			
		||||
				v()
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners, true
 | 
			
		||||
 | 
			
		||||
	case []interface{}:
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		listeners := make([]func(ImageView), count)
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
			switch v := v.(type) {
 | 
			
		||||
			case func(ImageView):
 | 
			
		||||
				listeners[i] = v
 | 
			
		||||
 | 
			
		||||
			case func():
 | 
			
		||||
				listeners[i] = func(ImageView) {
 | 
			
		||||
					v()
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			default:
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners, true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (imageView *imageViewData) set(tag string, value interface{}) bool {
 | 
			
		||||
func (imageView *imageViewData) set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		imageView.remove(tag)
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			@ -228,8 +157,12 @@ func (imageView *imageViewData) set(tag string, value interface{}) bool {
 | 
			
		|||
		notCompatibleType(tag, value)
 | 
			
		||||
 | 
			
		||||
	case LoadedEvent, ErrorEvent:
 | 
			
		||||
		if listeners, ok := valueToImageListeners(value); ok {
 | 
			
		||||
			imageView.properties[tag] = listeners
 | 
			
		||||
		if listeners, ok := valueToNoParamListeners[ImageView](value); ok {
 | 
			
		||||
			if listeners == nil {
 | 
			
		||||
				delete(imageView.properties, tag)
 | 
			
		||||
			} else {
 | 
			
		||||
				imageView.properties[tag] = listeners
 | 
			
		||||
			}
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -248,7 +181,7 @@ func (imageView *imageViewData) set(tag string, value interface{}) bool {
 | 
			
		|||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (imageView *imageViewData) Get(tag string) interface{} {
 | 
			
		||||
func (imageView *imageViewData) Get(tag string) any {
 | 
			
		||||
	return imageView.viewData.get(imageView.normalizeTag(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -311,7 +244,7 @@ func (imageView *imageViewData) htmlProperties(self View, buffer *strings.Builde
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if text := GetImageViewAltText(imageView, ""); text != "" {
 | 
			
		||||
	if text := GetImageViewAltText(imageView); text != "" {
 | 
			
		||||
		buffer.WriteString(` alt="`)
 | 
			
		||||
		buffer.WriteString(textToJS(text))
 | 
			
		||||
		buffer.WriteString(`"`)
 | 
			
		||||
| 
						 | 
				
			
			@ -333,8 +266,8 @@ func (imageView *imageViewData) cssStyle(self View, builder cssBuilder) {
 | 
			
		|||
		builder.add("object-fit", "none")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vAlign := GetImageViewVerticalAlign(imageView, "")
 | 
			
		||||
	hAlign := GetImageViewHorizontalAlign(imageView, "")
 | 
			
		||||
	vAlign := GetImageViewVerticalAlign(imageView)
 | 
			
		||||
	hAlign := GetImageViewHorizontalAlign(imageView)
 | 
			
		||||
	if vAlign != CenterAlign || hAlign != CenterAlign {
 | 
			
		||||
		var position string
 | 
			
		||||
		switch hAlign {
 | 
			
		||||
| 
						 | 
				
			
			@ -390,10 +323,10 @@ func (imageView *imageViewData) CurrentSource() string {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// GetImageViewSource returns the image URL of an ImageView subview.
 | 
			
		||||
// If the second argument (subviewID) is "" then a left position of the first argument (view) is returned
 | 
			
		||||
func GetImageViewSource(view View, subviewID string) string {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a left position of the first argument (view) is returned
 | 
			
		||||
func GetImageViewSource(view View, subviewID ...string) string {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if view != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -406,10 +339,10 @@ func GetImageViewSource(view View, subviewID string) string {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// GetImageViewAltText returns an alternative text description of an ImageView subview.
 | 
			
		||||
// If the second argument (subviewID) is "" then a left position of the first argument (view) is returned
 | 
			
		||||
func GetImageViewAltText(view View, subviewID string) string {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a left position of the first argument (view) is returned
 | 
			
		||||
func GetImageViewAltText(view View, subviewID ...string) string {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if view != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -425,40 +358,19 @@ func GetImageViewAltText(view View, subviewID string) string {
 | 
			
		|||
 | 
			
		||||
// GetImageViewFit returns how the content of a replaced ImageView subview:
 | 
			
		||||
// NoneFit (0), ContainFit (1), CoverFit (2), FillFit (3), or ScaleDownFit (4).
 | 
			
		||||
// If the second argument (subviewID) is "" then a left position of the first argument (view) is returned
 | 
			
		||||
func GetImageViewFit(view View, subviewID string) int {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if value, ok := enumStyledProperty(view, Fit, NoneFit); ok {
 | 
			
		||||
		return value
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a left position of the first argument (view) is returned
 | 
			
		||||
func GetImageViewFit(view View, subviewID ...string) int {
 | 
			
		||||
	return enumStyledProperty(view, subviewID, Fit, NoneFit, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetImageViewVerticalAlign return the vertical align of an ImageView subview: TopAlign (0), BottomAlign (1), CenterAlign (2)
 | 
			
		||||
// If the second argument (subviewID) is "" then a left position of the first argument (view) is returned
 | 
			
		||||
func GetImageViewVerticalAlign(view View, subviewID string) int {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if align, ok := enumStyledProperty(view, ImageVerticalAlign, LeftAlign); ok {
 | 
			
		||||
		return align
 | 
			
		||||
	}
 | 
			
		||||
	return CenterAlign
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a left position of the first argument (view) is returned
 | 
			
		||||
func GetImageViewVerticalAlign(view View, subviewID ...string) int {
 | 
			
		||||
	return enumStyledProperty(view, subviewID, ImageVerticalAlign, LeftAlign, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetImageViewHorizontalAlign return the vertical align of an ImageView subview: LeftAlign (0), RightAlign (1), CenterAlign (2)
 | 
			
		||||
// If the second argument (subviewID) is "" then a left position of the first argument (view) is returned
 | 
			
		||||
func GetImageViewHorizontalAlign(view View, subviewID string) int {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if align, ok := enumStyledProperty(view, ImageHorizontalAlign, LeftAlign); ok {
 | 
			
		||||
		return align
 | 
			
		||||
	}
 | 
			
		||||
	return CenterAlign
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a left position of the first argument (view) is returned
 | 
			
		||||
func GetImageViewHorizontalAlign(view View, subviewID ...string) int {
 | 
			
		||||
	return enumStyledProperty(view, subviewID, ImageHorizontalAlign, LeftAlign, false)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										133
									
								
								keyEvents.go
								
								
								
								
							
							
						
						
									
										133
									
								
								keyEvents.go
								
								
								
								
							| 
						 | 
				
			
			@ -50,34 +50,52 @@ type KeyEvent struct {
 | 
			
		|||
	MetaKey bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func valueToKeyListeners(value interface{}) ([]func(View, KeyEvent), bool) {
 | 
			
		||||
func (event *KeyEvent) init(data DataObject) {
 | 
			
		||||
	getBool := func(tag string) bool {
 | 
			
		||||
		if value, ok := data.PropertyValue(tag); ok && value == "1" {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	event.Key, _ = data.PropertyValue("key")
 | 
			
		||||
	event.Code, _ = data.PropertyValue("code")
 | 
			
		||||
	event.TimeStamp = getTimeStamp(data)
 | 
			
		||||
	event.Repeat = getBool("repeat")
 | 
			
		||||
	event.CtrlKey = getBool("ctrlKey")
 | 
			
		||||
	event.ShiftKey = getBool("shiftKey")
 | 
			
		||||
	event.AltKey = getBool("altKey")
 | 
			
		||||
	event.MetaKey = getBool("metaKey")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func valueToEventListeners[V View, E any](value any) ([]func(V, E), bool) {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		return nil, true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch value := value.(type) {
 | 
			
		||||
	case func(View, KeyEvent):
 | 
			
		||||
		return []func(View, KeyEvent){value}, true
 | 
			
		||||
	case func(V, E):
 | 
			
		||||
		return []func(V, E){value}, true
 | 
			
		||||
 | 
			
		||||
	case func(KeyEvent):
 | 
			
		||||
		fn := func(_ View, event KeyEvent) {
 | 
			
		||||
	case func(E):
 | 
			
		||||
		fn := func(_ V, event E) {
 | 
			
		||||
			value(event)
 | 
			
		||||
		}
 | 
			
		||||
		return []func(View, KeyEvent){fn}, true
 | 
			
		||||
		return []func(V, E){fn}, true
 | 
			
		||||
 | 
			
		||||
	case func(View):
 | 
			
		||||
		fn := func(view View, _ KeyEvent) {
 | 
			
		||||
	case func(V):
 | 
			
		||||
		fn := func(view V, _ E) {
 | 
			
		||||
			value(view)
 | 
			
		||||
		}
 | 
			
		||||
		return []func(View, KeyEvent){fn}, true
 | 
			
		||||
		return []func(V, E){fn}, true
 | 
			
		||||
 | 
			
		||||
	case func():
 | 
			
		||||
		fn := func(View, KeyEvent) {
 | 
			
		||||
		fn := func(V, E) {
 | 
			
		||||
			value()
 | 
			
		||||
		}
 | 
			
		||||
		return []func(View, KeyEvent){fn}, true
 | 
			
		||||
		return []func(V, E){fn}, true
 | 
			
		||||
 | 
			
		||||
	case []func(View, KeyEvent):
 | 
			
		||||
	case []func(V, E):
 | 
			
		||||
		if len(value) == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -88,33 +106,33 @@ func valueToKeyListeners(value interface{}) ([]func(View, KeyEvent), bool) {
 | 
			
		|||
		}
 | 
			
		||||
		return value, true
 | 
			
		||||
 | 
			
		||||
	case []func(KeyEvent):
 | 
			
		||||
	case []func(E):
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		listeners := make([]func(View, KeyEvent), count)
 | 
			
		||||
		listeners := make([]func(V, E), count)
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
			listeners[i] = func(_ View, event KeyEvent) {
 | 
			
		||||
			listeners[i] = func(_ V, event E) {
 | 
			
		||||
				v(event)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners, true
 | 
			
		||||
 | 
			
		||||
	case []func(View):
 | 
			
		||||
	case []func(V):
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		listeners := make([]func(View, KeyEvent), count)
 | 
			
		||||
		listeners := make([]func(V, E), count)
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
			listeners[i] = func(view View, _ KeyEvent) {
 | 
			
		||||
			listeners[i] = func(view V, _ E) {
 | 
			
		||||
				v(view)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -125,43 +143,43 @@ func valueToKeyListeners(value interface{}) ([]func(View, KeyEvent), bool) {
 | 
			
		|||
		if count == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		listeners := make([]func(View, KeyEvent), count)
 | 
			
		||||
		listeners := make([]func(V, E), count)
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
			listeners[i] = func(View, KeyEvent) {
 | 
			
		||||
			listeners[i] = func(V, E) {
 | 
			
		||||
				v()
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners, true
 | 
			
		||||
 | 
			
		||||
	case []interface{}:
 | 
			
		||||
	case []any:
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		listeners := make([]func(View, KeyEvent), count)
 | 
			
		||||
		listeners := make([]func(V, E), count)
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
			switch v := v.(type) {
 | 
			
		||||
			case func(View, KeyEvent):
 | 
			
		||||
			case func(V, E):
 | 
			
		||||
				listeners[i] = v
 | 
			
		||||
 | 
			
		||||
			case func(KeyEvent):
 | 
			
		||||
				listeners[i] = func(_ View, event KeyEvent) {
 | 
			
		||||
			case func(E):
 | 
			
		||||
				listeners[i] = func(_ V, event E) {
 | 
			
		||||
					v(event)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			case func(View):
 | 
			
		||||
				listeners[i] = func(view View, _ KeyEvent) {
 | 
			
		||||
			case func(V):
 | 
			
		||||
				listeners[i] = func(view V, _ E) {
 | 
			
		||||
					v(view)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			case func():
 | 
			
		||||
				listeners[i] = func(View, KeyEvent) {
 | 
			
		||||
				listeners[i] = func(V, E) {
 | 
			
		||||
					v()
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -180,8 +198,8 @@ var keyEvents = map[string]struct{ jsEvent, jsFunc string }{
 | 
			
		|||
	KeyUpEvent:   {jsEvent: "onkeyup", jsFunc: "keyUpEvent"},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (view *viewData) setKeyListener(tag string, value interface{}) bool {
 | 
			
		||||
	listeners, ok := valueToKeyListeners(value)
 | 
			
		||||
func (view *viewData) setKeyListener(tag string, value any) bool {
 | 
			
		||||
	listeners, ok := valueToEventListeners[View, KeyEvent](value)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		notCompatibleType(tag, value)
 | 
			
		||||
		return false
 | 
			
		||||
| 
						 | 
				
			
			@ -209,67 +227,48 @@ func (view *viewData) removeKeyListener(tag string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getKeyListeners(view View, subviewID string, tag string) []func(View, KeyEvent) {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
func getEventListeners[V View, E any](view View, subviewID []string, tag string) []func(V, E) {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if value := view.Get(tag); value != nil {
 | 
			
		||||
			if result, ok := value.([]func(View, KeyEvent)); ok {
 | 
			
		||||
			if result, ok := value.([]func(V, E)); ok {
 | 
			
		||||
				return result
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return []func(View, KeyEvent){}
 | 
			
		||||
	return []func(V, E){}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func keyEventsHtml(view View, buffer *strings.Builder) {
 | 
			
		||||
	for tag, js := range keyEvents {
 | 
			
		||||
		if listeners := getKeyListeners(view, "", tag); len(listeners) > 0 {
 | 
			
		||||
		if listeners := getEventListeners[View, KeyEvent](view, nil, tag); len(listeners) > 0 {
 | 
			
		||||
			buffer.WriteString(js.jsEvent + `="` + js.jsFunc + `(this, event)" `)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func handleKeyEvents(view View, tag string, data DataObject) {
 | 
			
		||||
	listeners := getKeyListeners(view, "", tag)
 | 
			
		||||
	if len(listeners) == 0 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	listeners := getEventListeners[View, KeyEvent](view, nil, tag)
 | 
			
		||||
	if len(listeners) > 0 {
 | 
			
		||||
		var event KeyEvent
 | 
			
		||||
		event.init(data)
 | 
			
		||||
 | 
			
		||||
	getBool := func(tag string) bool {
 | 
			
		||||
		if value, ok := data.PropertyValue(tag); ok && value == "1" {
 | 
			
		||||
			return true
 | 
			
		||||
		for _, listener := range listeners {
 | 
			
		||||
			listener(view, event)
 | 
			
		||||
		}
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	key, _ := data.PropertyValue("key")
 | 
			
		||||
	code, _ := data.PropertyValue("code")
 | 
			
		||||
	event := KeyEvent{
 | 
			
		||||
		TimeStamp: getTimeStamp(data),
 | 
			
		||||
		Key:       key,
 | 
			
		||||
		Code:      code,
 | 
			
		||||
		Repeat:    getBool("repeat"),
 | 
			
		||||
		CtrlKey:   getBool("ctrlKey"),
 | 
			
		||||
		ShiftKey:  getBool("shiftKey"),
 | 
			
		||||
		AltKey:    getBool("altKey"),
 | 
			
		||||
		MetaKey:   getBool("metaKey"),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, listener := range listeners {
 | 
			
		||||
		listener(view, event)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetKeyDownListeners returns the "key-down-event" listener list. If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetKeyDownListeners(view View, subviewID string) []func(View, KeyEvent) {
 | 
			
		||||
	return getKeyListeners(view, subviewID, KeyDownEvent)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetKeyDownListeners(view View, subviewID ...string) []func(View, KeyEvent) {
 | 
			
		||||
	return getEventListeners[View, KeyEvent](view, subviewID, KeyDownEvent)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetKeyUpListeners returns the "key-up-event" listener list. If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetKeyUpListeners(view View, subviewID string) []func(View, KeyEvent) {
 | 
			
		||||
	return getKeyListeners(view, subviewID, KeyUpEvent)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetKeyUpListeners(view View, subviewID ...string) []func(View, KeyEvent) {
 | 
			
		||||
	return getEventListeners[View, KeyEvent](view, subviewID, KeyUpEvent)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -77,7 +77,7 @@ func (adapter *viewListAdapter) ListItem(index int, session Session) View {
 | 
			
		|||
 | 
			
		||||
func (adapter *viewListAdapter) IsListItemEnabled(index int) bool {
 | 
			
		||||
	if index >= 0 && index < len(adapter.items) {
 | 
			
		||||
		return !IsDisabled(adapter.items[index], "")
 | 
			
		||||
		return !IsDisabled(adapter.items[index])
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										100
									
								
								listLayout.go
								
								
								
								
							
							
						
						
									
										100
									
								
								listLayout.go
								
								
								
								
							| 
						 | 
				
			
			@ -33,7 +33,7 @@ type listLayoutData struct {
 | 
			
		|||
// NewListLayout create new ListLayout object and return it
 | 
			
		||||
func NewListLayout(session Session, params Params) ListLayout {
 | 
			
		||||
	view := new(listLayoutData)
 | 
			
		||||
	view.Init(session)
 | 
			
		||||
	view.init(session)
 | 
			
		||||
	setInitParams(view, params)
 | 
			
		||||
	return view
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -43,8 +43,8 @@ func newListLayout(session Session) View {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Init initialize fields of ViewsAlignContainer by default values
 | 
			
		||||
func (listLayout *listLayoutData) Init(session Session) {
 | 
			
		||||
	listLayout.viewsContainerData.Init(session)
 | 
			
		||||
func (listLayout *listLayoutData) init(session Session) {
 | 
			
		||||
	listLayout.viewsContainerData.init(session)
 | 
			
		||||
	listLayout.tag = "ListLayout"
 | 
			
		||||
	listLayout.systemClass = "ruiListLayout"
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -58,19 +58,41 @@ func (listLayout *listLayoutData) normalizeTag(tag string) string {
 | 
			
		|||
	switch tag {
 | 
			
		||||
	case "wrap":
 | 
			
		||||
		tag = ListWrap
 | 
			
		||||
 | 
			
		||||
	case "row-gap":
 | 
			
		||||
		return ListRowGap
 | 
			
		||||
 | 
			
		||||
	case ColumnGap:
 | 
			
		||||
		return ListColumnGap
 | 
			
		||||
	}
 | 
			
		||||
	return tag
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (listLayout *listLayoutData) Get(tag string) interface{} {
 | 
			
		||||
func (listLayout *listLayoutData) Get(tag string) any {
 | 
			
		||||
	return listLayout.get(listLayout.normalizeTag(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (listLayout *listLayoutData) get(tag string) any {
 | 
			
		||||
	if tag == Gap {
 | 
			
		||||
		if rowGap := GetListRowGap(listLayout); rowGap.Equal(GetListColumnGap(listLayout)) {
 | 
			
		||||
			return rowGap
 | 
			
		||||
		}
 | 
			
		||||
		return AutoSize()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return listLayout.viewsContainerData.get(tag)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (listLayout *listLayoutData) Remove(tag string) {
 | 
			
		||||
	listLayout.remove(listLayout.normalizeTag(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (listLayout *listLayoutData) remove(tag string) {
 | 
			
		||||
	if tag == Gap {
 | 
			
		||||
		listLayout.remove(ListRowGap)
 | 
			
		||||
		listLayout.remove(ListColumnGap)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	listLayout.viewsContainerData.remove(tag)
 | 
			
		||||
	if listLayout.created {
 | 
			
		||||
		switch tag {
 | 
			
		||||
| 
						 | 
				
			
			@ -80,16 +102,20 @@ func (listLayout *listLayoutData) remove(tag string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (listLayout *listLayoutData) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (listLayout *listLayoutData) Set(tag string, value any) bool {
 | 
			
		||||
	return listLayout.set(listLayout.normalizeTag(tag), value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (listLayout *listLayoutData) set(tag string, value interface{}) bool {
 | 
			
		||||
func (listLayout *listLayoutData) set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		listLayout.remove(tag)
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if tag == Gap {
 | 
			
		||||
		return listLayout.set(ListRowGap, value) && listLayout.set(ListColumnGap, value)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if listLayout.viewsContainerData.set(tag, value) {
 | 
			
		||||
		if listLayout.created {
 | 
			
		||||
			switch tag {
 | 
			
		||||
| 
						 | 
				
			
			@ -113,38 +139,24 @@ func (listLayout *listLayoutData) htmlSubviews(self View, buffer *strings.Builde
 | 
			
		|||
 | 
			
		||||
// GetListVerticalAlign returns the vertical align of a ListLayout or ListView sibview:
 | 
			
		||||
// TopAlign (0), BottomAlign (1), CenterAlign (2), or StretchAlign (3)
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListVerticalAlign(view View, subviewID string) int {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view == nil {
 | 
			
		||||
		return LeftAlign
 | 
			
		||||
	}
 | 
			
		||||
	result, _ := enumProperty(view, VerticalAlign, view.Session(), 0)
 | 
			
		||||
	return result
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListVerticalAlign(view View, subviewID ...string) int {
 | 
			
		||||
	return enumStyledProperty(view, subviewID, VerticalAlign, TopAlign, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetListHorizontalAlign returns the vertical align of a ListLayout or ListView subview:
 | 
			
		||||
// LeftAlign (0), RightAlign (1), CenterAlign (2), or StretchAlign (3)
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListHorizontalAlign(view View, subviewID string) int {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view == nil {
 | 
			
		||||
		return TopAlign
 | 
			
		||||
	}
 | 
			
		||||
	result, _ := enumProperty(view, HorizontalAlign, view.Session(), 0)
 | 
			
		||||
	return result
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListHorizontalAlign(view View, subviewID ...string) int {
 | 
			
		||||
	return enumStyledProperty(view, subviewID, HorizontalAlign, LeftAlign, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetListOrientation returns the orientation of a ListLayout or ListView subview:
 | 
			
		||||
// TopDownOrientation (0), StartToEndOrientation (1), BottomUpOrientation (2), or EndToStartOrientation (3)
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListOrientation(view View, subviewID string) int {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListOrientation(view View, subviewID ...string) int {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if view != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -163,16 +175,20 @@ func GetListOrientation(view View, subviewID string) int {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// GetListWrap returns the wrap type of a ListLayout or ListView subview:
 | 
			
		||||
// WrapOff (0), WrapOn (1), or WrapReverse (2)
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListWrap(view View, subviewID string) int {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if result, ok := enumStyledProperty(view, ListWrap, 0); ok {
 | 
			
		||||
			return result
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return ListWrapOff
 | 
			
		||||
// ListWrapOff (0), ListWrapOn (1), or ListWrapReverse (2)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListWrap(view View, subviewID ...string) int {
 | 
			
		||||
	return enumStyledProperty(view, subviewID, ListWrap, ListWrapOff, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetListRowGap returns the gap between ListLayout or ListView rows.
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListRowGap(view View, subviewID ...string) SizeUnit {
 | 
			
		||||
	return sizeStyledProperty(view, subviewID, ListRowGap, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetListColumnGap returns the gap between ListLayout or ListView columns.
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListColumnGap(view View, subviewID ...string) SizeUnit {
 | 
			
		||||
	return sizeStyledProperty(view, subviewID, ListColumnGap, false)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										513
									
								
								listView.go
								
								
								
								
							
							
						
						
									
										513
									
								
								listView.go
								
								
								
								
							| 
						 | 
				
			
			@ -69,7 +69,7 @@ type listViewData struct {
 | 
			
		|||
// NewListView creates the new list view
 | 
			
		||||
func NewListView(session Session, params Params) ListView {
 | 
			
		||||
	view := new(listViewData)
 | 
			
		||||
	view.Init(session)
 | 
			
		||||
	view.init(session)
 | 
			
		||||
	setInitParams(view, params)
 | 
			
		||||
	return view
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -79,8 +79,8 @@ func newListView(session Session) View {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Init initialize fields of ViewsContainer by default values
 | 
			
		||||
func (listView *listViewData) Init(session Session) {
 | 
			
		||||
	listView.viewData.Init(session)
 | 
			
		||||
func (listView *listViewData) init(session Session) {
 | 
			
		||||
	listView.viewData.init(session)
 | 
			
		||||
	listView.tag = "ListView"
 | 
			
		||||
	listView.systemClass = "ruiListView"
 | 
			
		||||
	listView.items = []View{}
 | 
			
		||||
| 
						 | 
				
			
			@ -110,6 +110,12 @@ func (listView *listViewData) normalizeTag(tag string) string {
 | 
			
		|||
 | 
			
		||||
	case "wrap":
 | 
			
		||||
		tag = ListWrap
 | 
			
		||||
 | 
			
		||||
	case "row-gap":
 | 
			
		||||
		return ListRowGap
 | 
			
		||||
 | 
			
		||||
	case ColumnGap:
 | 
			
		||||
		return ListColumnGap
 | 
			
		||||
	}
 | 
			
		||||
	return tag
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -120,6 +126,10 @@ func (listView *listViewData) Remove(tag string) {
 | 
			
		|||
 | 
			
		||||
func (listView *listViewData) remove(tag string) {
 | 
			
		||||
	switch tag {
 | 
			
		||||
	case Gap:
 | 
			
		||||
		listView.remove(ListRowGap)
 | 
			
		||||
		listView.remove(ListColumnGap)
 | 
			
		||||
 | 
			
		||||
	case Checked:
 | 
			
		||||
		if len(listView.checkedItem) > 0 {
 | 
			
		||||
			listView.checkedItem = []int{}
 | 
			
		||||
| 
						 | 
				
			
			@ -148,7 +158,7 @@ func (listView *listViewData) remove(tag string) {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
	case Current:
 | 
			
		||||
		current := GetCurrent(listView, "")
 | 
			
		||||
		current := GetCurrent(listView)
 | 
			
		||||
		delete(listView.properties, tag)
 | 
			
		||||
		if listView.created {
 | 
			
		||||
			updateInnerHTML(listView.htmlID(), listView.session)
 | 
			
		||||
| 
						 | 
				
			
			@ -193,41 +203,53 @@ func (listView *listViewData) remove(tag string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (listView *listViewData) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (listView *listViewData) Set(tag string, value any) bool {
 | 
			
		||||
	return listView.set(listView.normalizeTag(tag), value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (listView *listViewData) set(tag string, value interface{}) bool {
 | 
			
		||||
func (listView *listViewData) set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		listView.remove(tag)
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch tag {
 | 
			
		||||
	case Gap:
 | 
			
		||||
		return listView.set(ListRowGap, value) && listView.set(ListColumnGap, value)
 | 
			
		||||
 | 
			
		||||
	case ListItemClickedEvent:
 | 
			
		||||
		listeners := listView.valueToItemListeners(value)
 | 
			
		||||
		if listeners == nil {
 | 
			
		||||
		listeners, ok := valueToEventListeners[ListView, int](value)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			notCompatibleType(tag, value)
 | 
			
		||||
			return false
 | 
			
		||||
		} else if listeners == nil {
 | 
			
		||||
			listeners = []func(ListView, int){}
 | 
			
		||||
		}
 | 
			
		||||
		listView.clickedListeners = listeners
 | 
			
		||||
		listView.propertyChangedEvent(tag)
 | 
			
		||||
		return true
 | 
			
		||||
 | 
			
		||||
	case ListItemSelectedEvent:
 | 
			
		||||
		listeners := listView.valueToItemListeners(value)
 | 
			
		||||
		if listeners == nil {
 | 
			
		||||
		listeners, ok := valueToEventListeners[ListView, int](value)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			notCompatibleType(tag, value)
 | 
			
		||||
			return false
 | 
			
		||||
		} else if listeners == nil {
 | 
			
		||||
			listeners = []func(ListView, int){}
 | 
			
		||||
		}
 | 
			
		||||
		listView.selectedListeners = listeners
 | 
			
		||||
		listView.propertyChangedEvent(tag)
 | 
			
		||||
		return true
 | 
			
		||||
 | 
			
		||||
	case ListItemCheckedEvent:
 | 
			
		||||
		if !listView.setItemCheckedEvent(value) {
 | 
			
		||||
		listeners, ok := valueToEventListeners[ListView, []int](value)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			notCompatibleType(tag, value)
 | 
			
		||||
			return false
 | 
			
		||||
		} else if listeners == nil {
 | 
			
		||||
			listeners = []func(ListView, []int){}
 | 
			
		||||
		}
 | 
			
		||||
		listView.checkedListeners = listeners
 | 
			
		||||
		listView.propertyChangedEvent(tag)
 | 
			
		||||
		return true
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -242,11 +264,11 @@ func (listView *listViewData) set(tag string, value interface{}) bool {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
	case Current:
 | 
			
		||||
		oldCurrent := GetCurrent(listView, "")
 | 
			
		||||
		oldCurrent := GetCurrent(listView)
 | 
			
		||||
		if !listView.setIntProperty(Current, value) {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		current := GetCurrent(listView, "")
 | 
			
		||||
		current := GetCurrent(listView)
 | 
			
		||||
		if oldCurrent == current {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -255,7 +277,7 @@ func (listView *listViewData) set(tag string, value interface{}) bool {
 | 
			
		|||
			listener(listView, current)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case Orientation, ListWrap, VerticalAlign, HorizontalAlign, Style, StyleDisabled, ItemWidth, ItemHeight:
 | 
			
		||||
	case Orientation, ListWrap, ListRowGap, ListColumnGap, VerticalAlign, HorizontalAlign, Style, StyleDisabled, ItemWidth, ItemHeight:
 | 
			
		||||
		result := listView.viewData.set(tag, value)
 | 
			
		||||
		if result && listView.created {
 | 
			
		||||
			updateInnerHTML(listView.htmlID(), listView.session)
 | 
			
		||||
| 
						 | 
				
			
			@ -288,67 +310,18 @@ func (listView *listViewData) set(tag string, value interface{}) bool {
 | 
			
		|||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (listView *listViewData) setItemCheckedEvent(value interface{}) bool {
 | 
			
		||||
	switch value := value.(type) {
 | 
			
		||||
	case func(ListView, []int):
 | 
			
		||||
		listView.checkedListeners = []func(ListView, []int){value}
 | 
			
		||||
 | 
			
		||||
	case func([]int):
 | 
			
		||||
		fn := func(_ ListView, date []int) {
 | 
			
		||||
			value(date)
 | 
			
		||||
		}
 | 
			
		||||
		listView.checkedListeners = []func(ListView, []int){fn}
 | 
			
		||||
 | 
			
		||||
	case []func(ListView, []int):
 | 
			
		||||
		listView.checkedListeners = value
 | 
			
		||||
 | 
			
		||||
	case []func([]int):
 | 
			
		||||
		listeners := make([]func(ListView, []int), len(value))
 | 
			
		||||
		for i, val := range value {
 | 
			
		||||
			if val == nil {
 | 
			
		||||
				notCompatibleType(ListItemCheckedEvent, val)
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			listeners[i] = func(_ ListView, date []int) {
 | 
			
		||||
				val(date)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		listView.checkedListeners = listeners
 | 
			
		||||
 | 
			
		||||
	case []interface{}:
 | 
			
		||||
		listeners := make([]func(ListView, []int), len(value))
 | 
			
		||||
		for i, val := range value {
 | 
			
		||||
			if val == nil {
 | 
			
		||||
				notCompatibleType(ListItemCheckedEvent, val)
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			switch val := val.(type) {
 | 
			
		||||
			case func(ListView, []int):
 | 
			
		||||
				listeners[i] = val
 | 
			
		||||
 | 
			
		||||
			case func([]int):
 | 
			
		||||
				listeners[i] = func(_ ListView, checked []int) {
 | 
			
		||||
					val(checked)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			default:
 | 
			
		||||
				notCompatibleType(ListItemCheckedEvent, val)
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		listView.checkedListeners = listeners
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (listView *listViewData) Get(tag string) interface{} {
 | 
			
		||||
func (listView *listViewData) Get(tag string) any {
 | 
			
		||||
	return listView.get(listView.normalizeTag(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (listView *listViewData) get(tag string) interface{} {
 | 
			
		||||
func (listView *listViewData) get(tag string) any {
 | 
			
		||||
	switch tag {
 | 
			
		||||
	case Gap:
 | 
			
		||||
		if rowGap := GetListRowGap(listView); rowGap.Equal(GetListColumnGap(listView)) {
 | 
			
		||||
			return rowGap
 | 
			
		||||
		}
 | 
			
		||||
		return AutoSize()
 | 
			
		||||
 | 
			
		||||
	case ListItemClickedEvent:
 | 
			
		||||
		return listView.clickedListeners
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -376,7 +349,7 @@ func (listView *listViewData) get(tag string) interface{} {
 | 
			
		|||
	return listView.viewData.get(tag)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (listView *listViewData) setItems(value interface{}) bool {
 | 
			
		||||
func (listView *listViewData) setItems(value any) bool {
 | 
			
		||||
	switch value := value.(type) {
 | 
			
		||||
	case []string:
 | 
			
		||||
		listView.adapter = NewTextListAdapter(value, nil)
 | 
			
		||||
| 
						 | 
				
			
			@ -412,7 +385,7 @@ func (listView *listViewData) setItems(value interface{}) bool {
 | 
			
		|||
			listView.adapter = NewTextListAdapter(items, nil)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case []interface{}:
 | 
			
		||||
	case []any:
 | 
			
		||||
		items := make([]View, len(value))
 | 
			
		||||
		for i, val := range value {
 | 
			
		||||
			switch value := val.(type) {
 | 
			
		||||
| 
						 | 
				
			
			@ -460,62 +433,7 @@ func (listView *listViewData) setItems(value interface{}) bool {
 | 
			
		|||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (listView *listViewData) valueToItemListeners(value interface{}) []func(ListView, int) {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		return []func(ListView, int){}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch value := value.(type) {
 | 
			
		||||
	case func(ListView, int):
 | 
			
		||||
		return []func(ListView, int){value}
 | 
			
		||||
 | 
			
		||||
	case func(int):
 | 
			
		||||
		fn := func(_ ListView, index int) {
 | 
			
		||||
			value(index)
 | 
			
		||||
		}
 | 
			
		||||
		return []func(ListView, int){fn}
 | 
			
		||||
 | 
			
		||||
	case []func(ListView, int):
 | 
			
		||||
		return value
 | 
			
		||||
 | 
			
		||||
	case []func(int):
 | 
			
		||||
		listeners := make([]func(ListView, int), len(value))
 | 
			
		||||
		for i, val := range value {
 | 
			
		||||
			if val == nil {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			listeners[i] = func(_ ListView, index int) {
 | 
			
		||||
				val(index)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners
 | 
			
		||||
 | 
			
		||||
	case []interface{}:
 | 
			
		||||
		listeners := make([]func(ListView, int), len(value))
 | 
			
		||||
		for i, val := range value {
 | 
			
		||||
			if val == nil {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			switch val := val.(type) {
 | 
			
		||||
			case func(ListView, int):
 | 
			
		||||
				listeners[i] = val
 | 
			
		||||
 | 
			
		||||
			case func(int):
 | 
			
		||||
				listeners[i] = func(_ ListView, index int) {
 | 
			
		||||
					val(index)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			default:
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (listView *listViewData) setChecked(value interface{}) bool {
 | 
			
		||||
func (listView *listViewData) setChecked(value any) bool {
 | 
			
		||||
	var checked []int
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		checked = []int{}
 | 
			
		||||
| 
						 | 
				
			
			@ -544,7 +462,7 @@ func (listView *listViewData) setChecked(value interface{}) bool {
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch GetListViewCheckbox(listView, "") {
 | 
			
		||||
	switch GetListViewCheckbox(listView) {
 | 
			
		||||
	case SingleCheckbox:
 | 
			
		||||
		count := len(checked)
 | 
			
		||||
		if count > 1 {
 | 
			
		||||
| 
						 | 
				
			
			@ -628,14 +546,14 @@ func (listView *listViewData) getItemFrames() []Frame {
 | 
			
		|||
 | 
			
		||||
func (listView *listViewData) itemAlign(self View, buffer *strings.Builder) {
 | 
			
		||||
	values := enumProperties[ItemHorizontalAlign].cssValues
 | 
			
		||||
	if hAlign := GetListItemHorizontalAlign(listView, ""); hAlign >= 0 && hAlign < len(values) {
 | 
			
		||||
	if hAlign := GetListItemHorizontalAlign(listView); hAlign >= 0 && hAlign < len(values) {
 | 
			
		||||
		buffer.WriteString(" justify-items: ")
 | 
			
		||||
		buffer.WriteString(values[hAlign])
 | 
			
		||||
		buffer.WriteRune(';')
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	values = enumProperties[ItemVerticalAlign].cssValues
 | 
			
		||||
	if vAlign := GetListItemVerticalAlign(listView, ""); vAlign >= 0 && vAlign < len(values) {
 | 
			
		||||
	if vAlign := GetListItemVerticalAlign(listView); vAlign >= 0 && vAlign < len(values) {
 | 
			
		||||
		buffer.WriteString(" align-items: ")
 | 
			
		||||
		buffer.WriteString(values[vAlign])
 | 
			
		||||
		buffer.WriteRune(';')
 | 
			
		||||
| 
						 | 
				
			
			@ -643,15 +561,15 @@ func (listView *listViewData) itemAlign(self View, buffer *strings.Builder) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (listView *listViewData) itemSize(self View, buffer *strings.Builder) {
 | 
			
		||||
	if itemWidth := GetListItemWidth(listView, ""); itemWidth.Type != Auto {
 | 
			
		||||
	if itemWidth := GetListItemWidth(listView); itemWidth.Type != Auto {
 | 
			
		||||
		buffer.WriteString(` min-width: `)
 | 
			
		||||
		buffer.WriteString(itemWidth.cssString(""))
 | 
			
		||||
		buffer.WriteString(itemWidth.cssString("", listView.Session()))
 | 
			
		||||
		buffer.WriteRune(';')
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if itemHeight := GetListItemHeight(listView, ""); itemHeight.Type != Auto {
 | 
			
		||||
	if itemHeight := GetListItemHeight(listView); itemHeight.Type != Auto {
 | 
			
		||||
		buffer.WriteString(` min-height: `)
 | 
			
		||||
		buffer.WriteString(itemHeight.cssString(""))
 | 
			
		||||
		buffer.WriteString(itemHeight.cssString("", listView.Session()))
 | 
			
		||||
		buffer.WriteRune(';')
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -741,7 +659,7 @@ func (listView *listViewData) checkboxItemDiv(self View, checkbox, hCheckboxAlig
 | 
			
		|||
 | 
			
		||||
	if gap, ok := sizeConstant(listView.session, "ruiCheckboxGap"); ok && gap.Type != Auto {
 | 
			
		||||
		itemStyleBuilder.WriteString(` grid-gap: `)
 | 
			
		||||
		itemStyleBuilder.WriteString(gap.cssString("auto"))
 | 
			
		||||
		itemStyleBuilder.WriteString(gap.cssString("auto", listView.Session()))
 | 
			
		||||
		itemStyleBuilder.WriteRune(';')
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -801,14 +719,14 @@ func (listView *listViewData) checkboxSubviews(self View, buffer *strings.Builde
 | 
			
		|||
	count := listView.adapter.ListSize()
 | 
			
		||||
	listViewID := listView.htmlID()
 | 
			
		||||
 | 
			
		||||
	hCheckboxAlign := GetListViewCheckboxHorizontalAlign(listView, "")
 | 
			
		||||
	vCheckboxAlign := GetListViewCheckboxVerticalAlign(listView, "")
 | 
			
		||||
	hCheckboxAlign := GetListViewCheckboxHorizontalAlign(listView)
 | 
			
		||||
	vCheckboxAlign := GetListViewCheckboxVerticalAlign(listView)
 | 
			
		||||
 | 
			
		||||
	itemDiv := listView.checkboxItemDiv(self, checkbox, hCheckboxAlign, vCheckboxAlign)
 | 
			
		||||
	onDiv, offDiv, contentDiv := listView.getDivs(self, checkbox, hCheckboxAlign, vCheckboxAlign)
 | 
			
		||||
 | 
			
		||||
	current := GetCurrent(listView, "")
 | 
			
		||||
	checkedItems := GetListViewCheckedItems(listView, "")
 | 
			
		||||
	current := GetCurrent(listView)
 | 
			
		||||
	checkedItems := GetListViewCheckedItems(listView)
 | 
			
		||||
	for i := 0; i < count; i++ {
 | 
			
		||||
		buffer.WriteString(`<div id="`)
 | 
			
		||||
		buffer.WriteString(listViewID)
 | 
			
		||||
| 
						 | 
				
			
			@ -864,7 +782,7 @@ func (listView *listViewData) noneCheckboxSubviews(self View, buffer *strings.Bu
 | 
			
		|||
	itemStyleBuilder.WriteString(`" onclick="listItemClickEvent(this, event)"`)
 | 
			
		||||
	itemStyle := itemStyleBuilder.String()
 | 
			
		||||
 | 
			
		||||
	current := GetCurrent(listView, "")
 | 
			
		||||
	current := GetCurrent(listView)
 | 
			
		||||
	for i := 0; i < count; i++ {
 | 
			
		||||
		buffer.WriteString(`<div id="`)
 | 
			
		||||
		buffer.WriteString(listViewID)
 | 
			
		||||
| 
						 | 
				
			
			@ -893,9 +811,9 @@ func (listView *listViewData) noneCheckboxSubviews(self View, buffer *strings.Bu
 | 
			
		|||
 | 
			
		||||
func (listView *listViewData) updateCheckboxItem(index int, checked bool) {
 | 
			
		||||
 | 
			
		||||
	checkbox := GetListViewCheckbox(listView, "")
 | 
			
		||||
	hCheckboxAlign := GetListViewCheckboxHorizontalAlign(listView, "")
 | 
			
		||||
	vCheckboxAlign := GetListViewCheckboxVerticalAlign(listView, "")
 | 
			
		||||
	checkbox := GetListViewCheckbox(listView)
 | 
			
		||||
	hCheckboxAlign := GetListViewCheckboxHorizontalAlign(listView)
 | 
			
		||||
	vCheckboxAlign := GetListViewCheckboxVerticalAlign(listView)
 | 
			
		||||
	onDiv, offDiv, contentDiv := listView.getDivs(listView, checkbox, hCheckboxAlign, vCheckboxAlign)
 | 
			
		||||
 | 
			
		||||
	buffer := allocStringBuilder()
 | 
			
		||||
| 
						 | 
				
			
			@ -937,7 +855,7 @@ func (listView *listViewData) htmlProperties(self View, buffer *strings.Builder)
 | 
			
		|||
	buffer.WriteString(`" data-bluritemstyle="`)
 | 
			
		||||
	buffer.WriteString(listView.currentInactiveStyle())
 | 
			
		||||
	buffer.WriteString(`"`)
 | 
			
		||||
	current := GetCurrent(listView, "")
 | 
			
		||||
	current := GetCurrent(listView)
 | 
			
		||||
	if listView.adapter != nil && current >= 0 && current < listView.adapter.ListSize() {
 | 
			
		||||
		buffer.WriteString(` data-current="`)
 | 
			
		||||
		buffer.WriteString(listView.htmlID())
 | 
			
		||||
| 
						 | 
				
			
			@ -953,8 +871,8 @@ func (listView *listViewData) htmlProperties(self View, buffer *strings.Builder)
 | 
			
		|||
func (listView *listViewData) cssStyle(self View, builder cssBuilder) {
 | 
			
		||||
	listView.viewData.cssStyle(self, builder)
 | 
			
		||||
 | 
			
		||||
	if GetListWrap(listView, "") != WrapOff {
 | 
			
		||||
		switch GetListOrientation(listView, "") {
 | 
			
		||||
	if GetListWrap(listView) != WrapOff {
 | 
			
		||||
		switch GetListOrientation(listView) {
 | 
			
		||||
		case TopDownOrientation, BottomUpOrientation:
 | 
			
		||||
			builder.add(`max-height`, `100%`)
 | 
			
		||||
		default:
 | 
			
		||||
| 
						 | 
				
			
			@ -976,8 +894,20 @@ func (listView *listViewData) htmlSubviews(self View, buffer *strings.Builder) {
 | 
			
		|||
 | 
			
		||||
	buffer.WriteString(`<div style="display: flex; align-content: stretch;`)
 | 
			
		||||
 | 
			
		||||
	wrap := GetListWrap(listView, "")
 | 
			
		||||
	orientation := GetListOrientation(listView, "")
 | 
			
		||||
	if gap := GetListRowGap(listView); gap.Type != Auto {
 | 
			
		||||
		buffer.WriteString(` row-gap: `)
 | 
			
		||||
		buffer.WriteString(gap.cssString("0", listView.Session()))
 | 
			
		||||
		buffer.WriteRune(';')
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if gap := GetListColumnGap(listView); gap.Type != Auto {
 | 
			
		||||
		buffer.WriteString(` column-gap: `)
 | 
			
		||||
		buffer.WriteString(gap.cssString("0", listView.Session()))
 | 
			
		||||
		buffer.WriteRune(';')
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	wrap := GetListWrap(listView)
 | 
			
		||||
	orientation := GetListOrientation(listView)
 | 
			
		||||
	rows := (orientation == StartToEndOrientation || orientation == EndToStartOrientation)
 | 
			
		||||
 | 
			
		||||
	if rows {
 | 
			
		||||
| 
						 | 
				
			
			@ -1018,29 +948,27 @@ func (listView *listViewData) htmlSubviews(self View, buffer *strings.Builder) {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	value := ""
 | 
			
		||||
	if align, ok := enumStyledProperty(listView, HorizontalAlign, LeftAlign); ok {
 | 
			
		||||
		switch align {
 | 
			
		||||
		case LeftAlign:
 | 
			
		||||
			if (!rows && wrap == ListWrapReverse) || orientation == EndToStartOrientation {
 | 
			
		||||
				value = `flex-end`
 | 
			
		||||
			} else {
 | 
			
		||||
				value = `flex-start`
 | 
			
		||||
			}
 | 
			
		||||
		case RightAlign:
 | 
			
		||||
			if (!rows && wrap == ListWrapReverse) || orientation == EndToStartOrientation {
 | 
			
		||||
				value = `flex-start`
 | 
			
		||||
			} else {
 | 
			
		||||
				value = `flex-end`
 | 
			
		||||
			}
 | 
			
		||||
		case CenterAlign:
 | 
			
		||||
			value = `center`
 | 
			
		||||
	switch GetListHorizontalAlign(listView) {
 | 
			
		||||
	case LeftAlign:
 | 
			
		||||
		if (!rows && wrap == ListWrapReverse) || orientation == EndToStartOrientation {
 | 
			
		||||
			value = `flex-end`
 | 
			
		||||
		} else {
 | 
			
		||||
			value = `flex-start`
 | 
			
		||||
		}
 | 
			
		||||
	case RightAlign:
 | 
			
		||||
		if (!rows && wrap == ListWrapReverse) || orientation == EndToStartOrientation {
 | 
			
		||||
			value = `flex-start`
 | 
			
		||||
		} else {
 | 
			
		||||
			value = `flex-end`
 | 
			
		||||
		}
 | 
			
		||||
	case CenterAlign:
 | 
			
		||||
		value = `center`
 | 
			
		||||
 | 
			
		||||
		case StretchAlign:
 | 
			
		||||
			if rows {
 | 
			
		||||
				value = `space-between`
 | 
			
		||||
			} else {
 | 
			
		||||
				value = `stretch`
 | 
			
		||||
			}
 | 
			
		||||
	case StretchAlign:
 | 
			
		||||
		if rows {
 | 
			
		||||
			value = `space-between`
 | 
			
		||||
		} else {
 | 
			
		||||
			value = `stretch`
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1053,29 +981,27 @@ func (listView *listViewData) htmlSubviews(self View, buffer *strings.Builder) {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	value = ""
 | 
			
		||||
	if align, ok := enumStyledProperty(listView, VerticalAlign, TopAlign); ok {
 | 
			
		||||
		switch align {
 | 
			
		||||
		case TopAlign:
 | 
			
		||||
			if (rows && wrap == ListWrapReverse) || orientation == BottomUpOrientation {
 | 
			
		||||
				value = `flex-end`
 | 
			
		||||
			} else {
 | 
			
		||||
				value = `flex-start`
 | 
			
		||||
			}
 | 
			
		||||
		case BottomAlign:
 | 
			
		||||
			if (rows && wrap == ListWrapReverse) || orientation == BottomUpOrientation {
 | 
			
		||||
				value = `flex-start`
 | 
			
		||||
			} else {
 | 
			
		||||
				value = `flex-end`
 | 
			
		||||
			}
 | 
			
		||||
		case CenterAlign:
 | 
			
		||||
			value = `center`
 | 
			
		||||
	switch GetListVerticalAlign(listView) {
 | 
			
		||||
	case TopAlign:
 | 
			
		||||
		if (rows && wrap == ListWrapReverse) || orientation == BottomUpOrientation {
 | 
			
		||||
			value = `flex-end`
 | 
			
		||||
		} else {
 | 
			
		||||
			value = `flex-start`
 | 
			
		||||
		}
 | 
			
		||||
	case BottomAlign:
 | 
			
		||||
		if (rows && wrap == ListWrapReverse) || orientation == BottomUpOrientation {
 | 
			
		||||
			value = `flex-start`
 | 
			
		||||
		} else {
 | 
			
		||||
			value = `flex-end`
 | 
			
		||||
		}
 | 
			
		||||
	case CenterAlign:
 | 
			
		||||
		value = `center`
 | 
			
		||||
 | 
			
		||||
		case StretchAlign:
 | 
			
		||||
			if rows {
 | 
			
		||||
				value = `stretch`
 | 
			
		||||
			} else {
 | 
			
		||||
				value = `space-between`
 | 
			
		||||
			}
 | 
			
		||||
	case StretchAlign:
 | 
			
		||||
		if rows {
 | 
			
		||||
			value = `stretch`
 | 
			
		||||
		} else {
 | 
			
		||||
			value = `space-between`
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1089,7 +1015,7 @@ func (listView *listViewData) htmlSubviews(self View, buffer *strings.Builder) {
 | 
			
		|||
 | 
			
		||||
	buffer.WriteString(`">`)
 | 
			
		||||
 | 
			
		||||
	checkbox := GetListViewCheckbox(listView, "")
 | 
			
		||||
	checkbox := GetListViewCheckbox(listView)
 | 
			
		||||
	if checkbox == NoneCheckbox {
 | 
			
		||||
		listView.noneCheckboxSubviews(self, buffer)
 | 
			
		||||
	} else {
 | 
			
		||||
| 
						 | 
				
			
			@ -1130,9 +1056,9 @@ func (listView *listViewData) handleCommand(self View, command string, data Data
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (listView *listViewData) onItemClick() {
 | 
			
		||||
	current := GetCurrent(listView, "")
 | 
			
		||||
	if current >= 0 && !IsDisabled(listView, "") {
 | 
			
		||||
		checkbox := GetListViewCheckbox(listView, "")
 | 
			
		||||
	current := GetCurrent(listView)
 | 
			
		||||
	if current >= 0 && !IsDisabled(listView) {
 | 
			
		||||
		checkbox := GetListViewCheckbox(listView)
 | 
			
		||||
	m:
 | 
			
		||||
		switch checkbox {
 | 
			
		||||
		case SingleCheckbox:
 | 
			
		||||
| 
						 | 
				
			
			@ -1191,121 +1117,67 @@ func (listView *listViewData) onItemResize(self View, index string, x, y, width,
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// GetVerticalAlign return the vertical align of a list: TopAlign (0), BottomAlign (1), CenterAlign (2), StretchAlign (3)
 | 
			
		||||
func GetVerticalAlign(view View) int {
 | 
			
		||||
	if align, ok := enumProperty(view, VerticalAlign, view.Session(), TopAlign); ok {
 | 
			
		||||
		return align
 | 
			
		||||
	}
 | 
			
		||||
	return TopAlign
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetVerticalAlign(view View, subviewID ...string) int {
 | 
			
		||||
	return enumStyledProperty(view, subviewID, VerticalAlign, TopAlign, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetHorizontalAlign return the vertical align of a list: LeftAlign (0), RightAlign (1), CenterAlign (2), StretchAlign (3)
 | 
			
		||||
func GetHorizontalAlign(view View) int {
 | 
			
		||||
	if align, ok := enumProperty(view, HorizontalAlign, view.Session(), LeftAlign); ok {
 | 
			
		||||
		return align
 | 
			
		||||
	}
 | 
			
		||||
	return LeftAlign
 | 
			
		||||
// GetHorizontalAlign return the vertical align of a list/checkbox: LeftAlign (0), RightAlign (1), CenterAlign (2), StretchAlign (3)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetHorizontalAlign(view View, subviewID ...string) int {
 | 
			
		||||
	return enumStyledProperty(view, subviewID, HorizontalAlign, LeftAlign, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetListItemClickedListeners returns a ListItemClickedListener of the ListView.
 | 
			
		||||
// If there are no listeners then the empty list is returned
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListItemClickedListeners(view View, subviewID string) []func(ListView, int) {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if value := view.Get(ListItemClickedEvent); value != nil {
 | 
			
		||||
			if result, ok := value.([]func(ListView, int)); ok {
 | 
			
		||||
				return result
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return []func(ListView, int){}
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListItemClickedListeners(view View, subviewID ...string) []func(ListView, int) {
 | 
			
		||||
	return getEventListeners[ListView, int](view, subviewID, ListItemClickedEvent)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetListItemSelectedListeners returns a ListItemSelectedListener of the ListView.
 | 
			
		||||
// If there are no listeners then the empty list is returned
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListItemSelectedListeners(view View, subviewID string) []func(ListView, int) {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if value := view.Get(ListItemSelectedEvent); value != nil {
 | 
			
		||||
			if result, ok := value.([]func(ListView, int)); ok {
 | 
			
		||||
				return result
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return []func(ListView, int){}
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListItemSelectedListeners(view View, subviewID ...string) []func(ListView, int) {
 | 
			
		||||
	return getEventListeners[ListView, int](view, subviewID, ListItemSelectedEvent)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetListItemCheckedListeners returns a ListItemCheckedListener of the ListView.
 | 
			
		||||
// If there are no listeners then the empty list is returned
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListItemCheckedListeners(view View, subviewID string) []func(ListView, []int) {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if value := view.Get(ListItemCheckedEvent); value != nil {
 | 
			
		||||
			if result, ok := value.([]func(ListView, []int)); ok {
 | 
			
		||||
				return result
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return []func(ListView, []int){}
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListItemCheckedListeners(view View, subviewID ...string) []func(ListView, []int) {
 | 
			
		||||
	return getEventListeners[ListView, []int](view, subviewID, ListItemCheckedEvent)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetListItemWidth returns the width of a ListView item.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListItemWidth(view View, subviewID string) SizeUnit {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		result, _ := sizeProperty(view, ItemWidth, view.Session())
 | 
			
		||||
		return result
 | 
			
		||||
	}
 | 
			
		||||
	return AutoSize()
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListItemWidth(view View, subviewID ...string) SizeUnit {
 | 
			
		||||
	return sizeStyledProperty(view, subviewID, ItemWidth, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetListItemHeight returns the height of a ListView item.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListItemHeight(view View, subviewID string) SizeUnit {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		result, _ := sizeProperty(view, ItemHeight, view.Session())
 | 
			
		||||
		return result
 | 
			
		||||
	}
 | 
			
		||||
	return AutoSize()
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListItemHeight(view View, subviewID ...string) SizeUnit {
 | 
			
		||||
	return sizeStyledProperty(view, subviewID, ItemHeight, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetListViewCheckbox returns the ListView checkbox type: NoneCheckbox (0), SingleCheckbox (1), or MultipleCheckbox (2).
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListViewCheckbox(view View, subviewID string) int {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		result, _ := enumProperty(view, ItemCheckbox, view.Session(), 0)
 | 
			
		||||
		return result
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListViewCheckbox(view View, subviewID ...string) int {
 | 
			
		||||
	return enumStyledProperty(view, subviewID, ItemCheckbox, 0, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetListViewCheckedItems returns the array of ListView checked items.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListViewCheckedItems(view View, subviewID string) []int {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListViewCheckedItems(view View, subviewID ...string) []int {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if listView, ok := view.(ListView); ok {
 | 
			
		||||
			checkedItems := listView.getCheckedItems()
 | 
			
		||||
			switch GetListViewCheckbox(view, "") {
 | 
			
		||||
			switch GetListViewCheckbox(view) {
 | 
			
		||||
			case NoneCheckbox:
 | 
			
		||||
				return []int{}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1334,66 +1206,34 @@ func IsListViewCheckedItem(view View, subviewID string, index int) bool {
 | 
			
		|||
 | 
			
		||||
// GetListViewCheckboxVerticalAlign returns the vertical align of the ListView checkbox:
 | 
			
		||||
// TopAlign (0), BottomAlign (1), CenterAlign (2)
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListViewCheckboxVerticalAlign(view View, subviewID string) int {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if align, ok := enumProperty(view, CheckboxVerticalAlign, view.Session(), TopAlign); ok {
 | 
			
		||||
			return align
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return TopAlign
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListViewCheckboxVerticalAlign(view View, subviewID ...string) int {
 | 
			
		||||
	return enumStyledProperty(view, subviewID, CheckboxVerticalAlign, TopAlign, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetListViewCheckboxHorizontalAlign returns the horizontal align of the ListView checkbox:
 | 
			
		||||
// LeftAlign (0), RightAlign (1), CenterAlign (2)
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListViewCheckboxHorizontalAlign(view View, subviewID string) int {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if align, ok := enumProperty(view, CheckboxHorizontalAlign, view.Session(), LeftAlign); ok {
 | 
			
		||||
			return align
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return LeftAlign
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListViewCheckboxHorizontalAlign(view View, subviewID ...string) int {
 | 
			
		||||
	return enumStyledProperty(view, subviewID, CheckboxHorizontalAlign, LeftAlign, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetListItemVerticalAlign returns the vertical align of the ListView item content:
 | 
			
		||||
// TopAlign (0), BottomAlign (1), CenterAlign (2)
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListItemVerticalAlign(view View, subviewID string) int {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if align, ok := enumProperty(view, ItemVerticalAlign, view.Session(), TopAlign); ok {
 | 
			
		||||
			return align
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return TopAlign
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListItemVerticalAlign(view View, subviewID ...string) int {
 | 
			
		||||
	return enumStyledProperty(view, subviewID, ItemVerticalAlign, TopAlign, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ItemHorizontalAlign returns the horizontal align of the ListView item content:
 | 
			
		||||
// LeftAlign (0), RightAlign (1), CenterAlign (2), StretchAlign (3)
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListItemHorizontalAlign(view View, subviewID string) int {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if align, ok := enumProperty(view, ItemHorizontalAlign, view.Session(), LeftAlign); ok {
 | 
			
		||||
			return align
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return LeftAlign
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListItemHorizontalAlign(view View, subviewID ...string) int {
 | 
			
		||||
	return enumStyledProperty(view, subviewID, ItemHorizontalAlign, LeftAlign, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetListItemFrame - returns the location and size of the ListView item in pixels.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListItemFrame(view View, subviewID string, index int) Frame {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
| 
						 | 
				
			
			@ -1410,10 +1250,10 @@ func GetListItemFrame(view View, subviewID string, index int) Frame {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// GetListViewAdapter - returns the ListView adapter.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListViewAdapter(view View, subviewID string) ListAdapter {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetListViewAdapter(view View, subviewID ...string) ListAdapter {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if value := view.Get(Items); value != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -1426,11 +1266,12 @@ func GetListViewAdapter(view View, subviewID string) ListAdapter {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// ReloadListViewData updates ListView content
 | 
			
		||||
// If the second argument (subviewID) is "" then content the first argument (view) is updated.
 | 
			
		||||
func ReloadListViewData(view View, subviewID string) {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then content the first argument (view) is updated.
 | 
			
		||||
func ReloadListViewData(view View, subviewID ...string) {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if listView, ok := view.(ListView); ok {
 | 
			
		||||
			listView.ReloadListViewData()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										214
									
								
								mediaPlayer.go
								
								
								
								
							
							
						
						
									
										214
									
								
								mediaPlayer.go
								
								
								
								
							| 
						 | 
				
			
			@ -163,8 +163,8 @@ type MediaSource struct {
 | 
			
		|||
	MimeType string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (player *mediaPlayerData) Init(session Session) {
 | 
			
		||||
	player.viewData.Init(session)
 | 
			
		||||
func (player *mediaPlayerData) init(session Session) {
 | 
			
		||||
	player.viewData.init(session)
 | 
			
		||||
	player.tag = "MediaPlayer"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -185,11 +185,11 @@ func (player *mediaPlayerData) remove(tag string) {
 | 
			
		|||
	player.propertyChanged(tag)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (player *mediaPlayerData) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (player *mediaPlayerData) Set(tag string, value any) bool {
 | 
			
		||||
	return player.set(strings.ToLower(tag), value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (player *mediaPlayerData) set(tag string, value interface{}) bool {
 | 
			
		||||
func (player *mediaPlayerData) set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		player.remove(tag)
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			@ -205,7 +205,7 @@ func (player *mediaPlayerData) set(tag string, value interface{}) bool {
 | 
			
		|||
	case AbortEvent, CanPlayEvent, CanPlayThroughEvent, CompleteEvent, EmptiedEvent, LoadStartEvent,
 | 
			
		||||
		EndedEvent, LoadedDataEvent, LoadedMetadataEvent, PauseEvent, PlayEvent, PlayingEvent,
 | 
			
		||||
		ProgressEvent, SeekedEvent, SeekingEvent, StalledEvent, SuspendEvent, WaitingEvent:
 | 
			
		||||
		if listeners, ok := valueToPlayerListeners(value); ok {
 | 
			
		||||
		if listeners, ok := valueToNoParamListeners[MediaPlayer](value); ok {
 | 
			
		||||
			if listeners == nil {
 | 
			
		||||
				delete(player.properties, tag)
 | 
			
		||||
			} else {
 | 
			
		||||
| 
						 | 
				
			
			@ -218,7 +218,7 @@ func (player *mediaPlayerData) set(tag string, value interface{}) bool {
 | 
			
		|||
		notCompatibleType(tag, value)
 | 
			
		||||
 | 
			
		||||
	case DurationChangedEvent, RateChangedEvent, TimeUpdateEvent, VolumeChangedEvent:
 | 
			
		||||
		if listeners, ok := valueToPlayerTimeListeners(value); ok {
 | 
			
		||||
		if listeners, ok := valueToEventListeners[MediaPlayer, float64](value); ok {
 | 
			
		||||
			if listeners == nil {
 | 
			
		||||
				delete(player.properties, tag)
 | 
			
		||||
			} else {
 | 
			
		||||
| 
						 | 
				
			
			@ -257,7 +257,7 @@ func (player *mediaPlayerData) set(tag string, value interface{}) bool {
 | 
			
		|||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (player *mediaPlayerData) setSource(value interface{}) bool {
 | 
			
		||||
func (player *mediaPlayerData) setSource(value any) bool {
 | 
			
		||||
	switch value := value.(type) {
 | 
			
		||||
	case string:
 | 
			
		||||
		src := MediaSource{Url: value, MimeType: ""}
 | 
			
		||||
| 
						 | 
				
			
			@ -311,203 +311,7 @@ func (player *mediaPlayerData) setSource(value interface{}) bool {
 | 
			
		|||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func valueToPlayerListeners(value interface{}) ([]func(MediaPlayer), bool) {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		return nil, true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch value := value.(type) {
 | 
			
		||||
	case func(MediaPlayer):
 | 
			
		||||
		return []func(MediaPlayer){value}, true
 | 
			
		||||
 | 
			
		||||
	case func():
 | 
			
		||||
		fn := func(MediaPlayer) {
 | 
			
		||||
			value()
 | 
			
		||||
		}
 | 
			
		||||
		return []func(MediaPlayer){fn}, true
 | 
			
		||||
 | 
			
		||||
	case []func(MediaPlayer):
 | 
			
		||||
		if len(value) == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		for _, fn := range value {
 | 
			
		||||
			if fn == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return value, true
 | 
			
		||||
 | 
			
		||||
	case []func():
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		listeners := make([]func(MediaPlayer), count)
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
			listeners[i] = func(MediaPlayer) {
 | 
			
		||||
				v()
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners, true
 | 
			
		||||
 | 
			
		||||
	case []interface{}:
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		listeners := make([]func(MediaPlayer), count)
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
			switch v := v.(type) {
 | 
			
		||||
			case func(MediaPlayer):
 | 
			
		||||
				listeners[i] = v
 | 
			
		||||
 | 
			
		||||
			case func():
 | 
			
		||||
				listeners[i] = func(MediaPlayer) {
 | 
			
		||||
					v()
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			default:
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners, true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func valueToPlayerTimeListeners(value interface{}) ([]func(MediaPlayer, float64), bool) {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		return nil, true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch value := value.(type) {
 | 
			
		||||
	case func(MediaPlayer, float64):
 | 
			
		||||
		return []func(MediaPlayer, float64){value}, true
 | 
			
		||||
 | 
			
		||||
	case func(float64):
 | 
			
		||||
		fn := func(_ MediaPlayer, time float64) {
 | 
			
		||||
			value(time)
 | 
			
		||||
		}
 | 
			
		||||
		return []func(MediaPlayer, float64){fn}, true
 | 
			
		||||
 | 
			
		||||
	case func(MediaPlayer):
 | 
			
		||||
		fn := func(player MediaPlayer, _ float64) {
 | 
			
		||||
			value(player)
 | 
			
		||||
		}
 | 
			
		||||
		return []func(MediaPlayer, float64){fn}, true
 | 
			
		||||
 | 
			
		||||
	case func():
 | 
			
		||||
		fn := func(MediaPlayer, float64) {
 | 
			
		||||
			value()
 | 
			
		||||
		}
 | 
			
		||||
		return []func(MediaPlayer, float64){fn}, true
 | 
			
		||||
 | 
			
		||||
	case []func(MediaPlayer, float64):
 | 
			
		||||
		if len(value) == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		for _, fn := range value {
 | 
			
		||||
			if fn == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return value, true
 | 
			
		||||
 | 
			
		||||
	case []func(float64):
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		listeners := make([]func(MediaPlayer, float64), count)
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
			listeners[i] = func(_ MediaPlayer, time float64) {
 | 
			
		||||
				v(time)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners, true
 | 
			
		||||
 | 
			
		||||
	case []func(MediaPlayer):
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		listeners := make([]func(MediaPlayer, float64), count)
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
			listeners[i] = func(player MediaPlayer, _ float64) {
 | 
			
		||||
				v(player)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners, true
 | 
			
		||||
 | 
			
		||||
	case []func():
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		listeners := make([]func(MediaPlayer, float64), count)
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
			listeners[i] = func(MediaPlayer, float64) {
 | 
			
		||||
				v()
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners, true
 | 
			
		||||
 | 
			
		||||
	case []interface{}:
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		listeners := make([]func(MediaPlayer, float64), count)
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
			switch v := v.(type) {
 | 
			
		||||
			case func(MediaPlayer, float64):
 | 
			
		||||
				listeners[i] = v
 | 
			
		||||
 | 
			
		||||
			case func(float64):
 | 
			
		||||
				listeners[i] = func(_ MediaPlayer, time float64) {
 | 
			
		||||
					v(time)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			case func(MediaPlayer):
 | 
			
		||||
				listeners[i] = func(player MediaPlayer, _ float64) {
 | 
			
		||||
					v(player)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			case func():
 | 
			
		||||
				listeners[i] = func(MediaPlayer, float64) {
 | 
			
		||||
					v()
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			default:
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners, true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func valueToPlayerErrorListeners(value interface{}) ([]func(MediaPlayer, int, string), bool) {
 | 
			
		||||
func valueToPlayerErrorListeners(value any) ([]func(MediaPlayer, int, string), bool) {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		return nil, true
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -593,7 +397,7 @@ func valueToPlayerErrorListeners(value interface{}) ([]func(MediaPlayer, int, st
 | 
			
		|||
		}
 | 
			
		||||
		return listeners, true
 | 
			
		||||
 | 
			
		||||
	case []interface{}:
 | 
			
		||||
	case []any:
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										207
									
								
								mouseEvents.go
								
								
								
								
							
							
						
						
									
										207
									
								
								mouseEvents.go
								
								
								
								
							| 
						 | 
				
			
			@ -144,131 +144,6 @@ type MouseEvent struct {
 | 
			
		|||
	MetaKey bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func valueToMouseListeners(value interface{}) ([]func(View, MouseEvent), bool) {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		return nil, true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch value := value.(type) {
 | 
			
		||||
	case func(View, MouseEvent):
 | 
			
		||||
		return []func(View, MouseEvent){value}, true
 | 
			
		||||
 | 
			
		||||
	case func(MouseEvent):
 | 
			
		||||
		fn := func(_ View, event MouseEvent) {
 | 
			
		||||
			value(event)
 | 
			
		||||
		}
 | 
			
		||||
		return []func(View, MouseEvent){fn}, true
 | 
			
		||||
 | 
			
		||||
	case func(View):
 | 
			
		||||
		fn := func(view View, _ MouseEvent) {
 | 
			
		||||
			value(view)
 | 
			
		||||
		}
 | 
			
		||||
		return []func(View, MouseEvent){fn}, true
 | 
			
		||||
 | 
			
		||||
	case func():
 | 
			
		||||
		fn := func(View, MouseEvent) {
 | 
			
		||||
			value()
 | 
			
		||||
		}
 | 
			
		||||
		return []func(View, MouseEvent){fn}, true
 | 
			
		||||
 | 
			
		||||
	case []func(View, MouseEvent):
 | 
			
		||||
		if len(value) == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		for _, fn := range value {
 | 
			
		||||
			if fn == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return value, true
 | 
			
		||||
 | 
			
		||||
	case []func(MouseEvent):
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		listeners := make([]func(View, MouseEvent), count)
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
			listeners[i] = func(_ View, event MouseEvent) {
 | 
			
		||||
				v(event)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners, true
 | 
			
		||||
 | 
			
		||||
	case []func(View):
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		listeners := make([]func(View, MouseEvent), count)
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
			listeners[i] = func(view View, _ MouseEvent) {
 | 
			
		||||
				v(view)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners, true
 | 
			
		||||
 | 
			
		||||
	case []func():
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		listeners := make([]func(View, MouseEvent), count)
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
			listeners[i] = func(View, MouseEvent) {
 | 
			
		||||
				v()
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners, true
 | 
			
		||||
 | 
			
		||||
	case []interface{}:
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		listeners := make([]func(View, MouseEvent), count)
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
			switch v := v.(type) {
 | 
			
		||||
			case func(View, MouseEvent):
 | 
			
		||||
				listeners[i] = v
 | 
			
		||||
 | 
			
		||||
			case func(MouseEvent):
 | 
			
		||||
				listeners[i] = func(_ View, event MouseEvent) {
 | 
			
		||||
					v(event)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			case func(View):
 | 
			
		||||
				listeners[i] = func(view View, _ MouseEvent) {
 | 
			
		||||
					v(view)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			case func():
 | 
			
		||||
				listeners[i] = func(View, MouseEvent) {
 | 
			
		||||
					v()
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			default:
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners, true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var mouseEvents = map[string]struct{ jsEvent, jsFunc string }{
 | 
			
		||||
	ClickEvent:       {jsEvent: "onclick", jsFunc: "clickEvent"},
 | 
			
		||||
	DoubleClickEvent: {jsEvent: "ondblclick", jsFunc: "doubleClickEvent"},
 | 
			
		||||
| 
						 | 
				
			
			@ -280,8 +155,8 @@ var mouseEvents = map[string]struct{ jsEvent, jsFunc string }{
 | 
			
		|||
	ContextMenuEvent: {jsEvent: "oncontextmenu", jsFunc: "contextMenuEvent"},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (view *viewData) setMouseListener(tag string, value interface{}) bool {
 | 
			
		||||
	listeners, ok := valueToMouseListeners(value)
 | 
			
		||||
func (view *viewData) setMouseListener(tag string, value any) bool {
 | 
			
		||||
	listeners, ok := valueToEventListeners[View, MouseEvent](value)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		notCompatibleType(tag, value)
 | 
			
		||||
		return false
 | 
			
		||||
| 
						 | 
				
			
			@ -309,20 +184,6 @@ func (view *viewData) removeMouseListener(tag string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getMouseListeners(view View, subviewID string, tag string) []func(View, MouseEvent) {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if value := view.Get(tag); value != nil {
 | 
			
		||||
			if result, ok := value.([]func(View, MouseEvent)); ok {
 | 
			
		||||
				return result
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return []func(View, MouseEvent){}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func mouseEventsHtml(view View, buffer *strings.Builder) {
 | 
			
		||||
	for tag, js := range mouseEvents {
 | 
			
		||||
		if value := view.getRaw(tag); value != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -363,64 +224,62 @@ func (event *MouseEvent) init(data DataObject) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func handleMouseEvents(view View, tag string, data DataObject) {
 | 
			
		||||
	listeners := getMouseListeners(view, "", tag)
 | 
			
		||||
	if len(listeners) == 0 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	listeners := getEventListeners[View, MouseEvent](view, nil, tag)
 | 
			
		||||
	if len(listeners) > 0 {
 | 
			
		||||
		var event MouseEvent
 | 
			
		||||
		event.init(data)
 | 
			
		||||
 | 
			
		||||
	var event MouseEvent
 | 
			
		||||
	event.init(data)
 | 
			
		||||
 | 
			
		||||
	for _, listener := range listeners {
 | 
			
		||||
		listener(view, event)
 | 
			
		||||
		for _, listener := range listeners {
 | 
			
		||||
			listener(view, event)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetClickListeners returns the "click-event" listener list. If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetClickListeners(view View, subviewID string) []func(View, MouseEvent) {
 | 
			
		||||
	return getMouseListeners(view, subviewID, ClickEvent)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetClickListeners(view View, subviewID ...string) []func(View, MouseEvent) {
 | 
			
		||||
	return getEventListeners[View, MouseEvent](view, subviewID, ClickEvent)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetDoubleClickListeners returns the "double-click-event" listener list. If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetDoubleClickListeners(view View, subviewID string) []func(View, MouseEvent) {
 | 
			
		||||
	return getMouseListeners(view, subviewID, DoubleClickEvent)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetDoubleClickListeners(view View, subviewID ...string) []func(View, MouseEvent) {
 | 
			
		||||
	return getEventListeners[View, MouseEvent](view, subviewID, DoubleClickEvent)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetContextMenuListeners returns the "context-menu" listener list.
 | 
			
		||||
// If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetContextMenuListeners(view View, subviewID string) []func(View, MouseEvent) {
 | 
			
		||||
	return getMouseListeners(view, subviewID, ContextMenuEvent)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetContextMenuListeners(view View, subviewID ...string) []func(View, MouseEvent) {
 | 
			
		||||
	return getEventListeners[View, MouseEvent](view, subviewID, ContextMenuEvent)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetMouseDownListeners returns the "mouse-down" listener list. If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetMouseDownListeners(view View, subviewID string) []func(View, MouseEvent) {
 | 
			
		||||
	return getMouseListeners(view, subviewID, MouseDown)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetMouseDownListeners(view View, subviewID ...string) []func(View, MouseEvent) {
 | 
			
		||||
	return getEventListeners[View, MouseEvent](view, subviewID, MouseDown)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetMouseUpListeners returns the "mouse-up" listener list. If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetMouseUpListeners(view View, subviewID string) []func(View, MouseEvent) {
 | 
			
		||||
	return getMouseListeners(view, subviewID, MouseUp)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetMouseUpListeners(view View, subviewID ...string) []func(View, MouseEvent) {
 | 
			
		||||
	return getEventListeners[View, MouseEvent](view, subviewID, MouseUp)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetMouseMoveListeners returns the "mouse-move" listener list. If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetMouseMoveListeners(view View, subviewID string) []func(View, MouseEvent) {
 | 
			
		||||
	return getMouseListeners(view, subviewID, MouseMove)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetMouseMoveListeners(view View, subviewID ...string) []func(View, MouseEvent) {
 | 
			
		||||
	return getEventListeners[View, MouseEvent](view, subviewID, MouseMove)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetMouseOverListeners returns the "mouse-over" listener list. If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetMouseOverListeners(view View, subviewID string) []func(View, MouseEvent) {
 | 
			
		||||
	return getMouseListeners(view, subviewID, MouseOver)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetMouseOverListeners(view View, subviewID ...string) []func(View, MouseEvent) {
 | 
			
		||||
	return getEventListeners[View, MouseEvent](view, subviewID, MouseOver)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetMouseOutListeners returns the "mouse-out" listener list. If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetMouseOutListeners(view View, subviewID string) []func(View, MouseEvent) {
 | 
			
		||||
	return getMouseListeners(view, subviewID, MouseOut)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetMouseOutListeners(view View, subviewID ...string) []func(View, MouseEvent) {
 | 
			
		||||
	return getEventListeners[View, MouseEvent](view, subviewID, MouseOut)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										223
									
								
								numberPicker.go
								
								
								
								
							
							
						
						
									
										223
									
								
								numberPicker.go
								
								
								
								
							| 
						 | 
				
			
			@ -36,7 +36,7 @@ type numberPickerData struct {
 | 
			
		|||
// NewNumberPicker create new NumberPicker object and return it
 | 
			
		||||
func NewNumberPicker(session Session, params Params) NumberPicker {
 | 
			
		||||
	view := new(numberPickerData)
 | 
			
		||||
	view.Init(session)
 | 
			
		||||
	view.init(session)
 | 
			
		||||
	setInitParams(view, params)
 | 
			
		||||
	return view
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -45,8 +45,8 @@ func newNumberPicker(session Session) View {
 | 
			
		|||
	return NewNumberPicker(session, nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (picker *numberPickerData) Init(session Session) {
 | 
			
		||||
	picker.viewData.Init(session)
 | 
			
		||||
func (picker *numberPickerData) init(session Session) {
 | 
			
		||||
	picker.viewData.init(session)
 | 
			
		||||
	picker.tag = "NumberPicker"
 | 
			
		||||
	picker.numberChangedListeners = []func(NumberPicker, float64){}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -87,11 +87,11 @@ func (picker *numberPickerData) remove(tag string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (picker *numberPickerData) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (picker *numberPickerData) Set(tag string, value any) bool {
 | 
			
		||||
	return picker.set(picker.normalizeTag(tag), value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (picker *numberPickerData) set(tag string, value interface{}) bool {
 | 
			
		||||
func (picker *numberPickerData) set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		picker.remove(tag)
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			@ -99,65 +99,28 @@ func (picker *numberPickerData) set(tag string, value interface{}) bool {
 | 
			
		|||
 | 
			
		||||
	switch tag {
 | 
			
		||||
	case NumberChangedEvent:
 | 
			
		||||
		switch value := value.(type) {
 | 
			
		||||
		case func(NumberPicker, float64):
 | 
			
		||||
			picker.numberChangedListeners = []func(NumberPicker, float64){value}
 | 
			
		||||
 | 
			
		||||
		case func(float64):
 | 
			
		||||
			fn := func(_ NumberPicker, newValue float64) {
 | 
			
		||||
				value(newValue)
 | 
			
		||||
			}
 | 
			
		||||
			picker.numberChangedListeners = []func(NumberPicker, float64){fn}
 | 
			
		||||
 | 
			
		||||
		case []func(NumberPicker, float64):
 | 
			
		||||
			picker.numberChangedListeners = value
 | 
			
		||||
 | 
			
		||||
		case []func(float64):
 | 
			
		||||
			listeners := make([]func(NumberPicker, float64), len(value))
 | 
			
		||||
			for i, val := range value {
 | 
			
		||||
				if val == nil {
 | 
			
		||||
					notCompatibleType(tag, val)
 | 
			
		||||
					return false
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				listeners[i] = func(_ NumberPicker, newValue float64) {
 | 
			
		||||
					val(newValue)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			picker.numberChangedListeners = listeners
 | 
			
		||||
 | 
			
		||||
		case []interface{}:
 | 
			
		||||
			listeners := make([]func(NumberPicker, float64), len(value))
 | 
			
		||||
			for i, val := range value {
 | 
			
		||||
				if val == nil {
 | 
			
		||||
					notCompatibleType(tag, val)
 | 
			
		||||
					return false
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				switch val := val.(type) {
 | 
			
		||||
				case func(NumberPicker, float64):
 | 
			
		||||
					listeners[i] = val
 | 
			
		||||
 | 
			
		||||
				default:
 | 
			
		||||
					notCompatibleType(tag, val)
 | 
			
		||||
					return false
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			picker.numberChangedListeners = listeners
 | 
			
		||||
		listeners, ok := valueToEventListeners[NumberPicker, float64](value)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			notCompatibleType(tag, value)
 | 
			
		||||
			return false
 | 
			
		||||
		} else if listeners == nil {
 | 
			
		||||
			listeners = []func(NumberPicker, float64){}
 | 
			
		||||
		}
 | 
			
		||||
		picker.numberChangedListeners = listeners
 | 
			
		||||
		picker.propertyChangedEvent(tag)
 | 
			
		||||
		return true
 | 
			
		||||
 | 
			
		||||
	case NumberPickerValue:
 | 
			
		||||
		oldValue := GetNumberPickerValue(picker, "")
 | 
			
		||||
		min, max := GetNumberPickerMinMax(picker, "")
 | 
			
		||||
		oldValue := GetNumberPickerValue(picker)
 | 
			
		||||
		min, max := GetNumberPickerMinMax(picker)
 | 
			
		||||
		if picker.setFloatProperty(NumberPickerValue, value, min, max) {
 | 
			
		||||
			if newValue := GetNumberPickerValue(picker, ""); oldValue != newValue {
 | 
			
		||||
			if f, ok := floatProperty(picker, NumberPickerValue, picker.Session(), min); ok && f != oldValue {
 | 
			
		||||
				newValue, _ := floatTextProperty(picker, NumberPickerValue, picker.Session(), min)
 | 
			
		||||
				if picker.created {
 | 
			
		||||
					picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%f')`, picker.htmlID(), newValue))
 | 
			
		||||
					picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, picker.htmlID(), newValue))
 | 
			
		||||
				}
 | 
			
		||||
				for _, listener := range picker.numberChangedListeners {
 | 
			
		||||
					listener(picker, newValue)
 | 
			
		||||
					listener(picker, f)
 | 
			
		||||
				}
 | 
			
		||||
				picker.propertyChangedEvent(tag)
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -177,29 +140,29 @@ func (picker *numberPickerData) propertyChanged(tag string) {
 | 
			
		|||
	if picker.created {
 | 
			
		||||
		switch tag {
 | 
			
		||||
		case NumberPickerType:
 | 
			
		||||
			if GetNumberPickerType(picker, "") == NumberSlider {
 | 
			
		||||
			if GetNumberPickerType(picker) == NumberSlider {
 | 
			
		||||
				updateProperty(picker.htmlID(), "type", "range", picker.session)
 | 
			
		||||
			} else {
 | 
			
		||||
				updateProperty(picker.htmlID(), "type", "number", picker.session)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		case NumberPickerMin:
 | 
			
		||||
			min, _ := GetNumberPickerMinMax(picker, "")
 | 
			
		||||
			min, _ := GetNumberPickerMinMax(picker)
 | 
			
		||||
			updateProperty(picker.htmlID(), Min, strconv.FormatFloat(min, 'f', -1, 32), picker.session)
 | 
			
		||||
 | 
			
		||||
		case NumberPickerMax:
 | 
			
		||||
			_, max := GetNumberPickerMinMax(picker, "")
 | 
			
		||||
			_, max := GetNumberPickerMinMax(picker)
 | 
			
		||||
			updateProperty(picker.htmlID(), Max, strconv.FormatFloat(max, 'f', -1, 32), picker.session)
 | 
			
		||||
 | 
			
		||||
		case NumberPickerStep:
 | 
			
		||||
			if step := GetNumberPickerStep(picker, ""); step > 0 {
 | 
			
		||||
			if step := GetNumberPickerStep(picker); step > 0 {
 | 
			
		||||
				updateProperty(picker.htmlID(), Step, strconv.FormatFloat(step, 'f', -1, 32), picker.session)
 | 
			
		||||
			} else {
 | 
			
		||||
				updateProperty(picker.htmlID(), Step, "any", picker.session)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		case NumberPickerValue:
 | 
			
		||||
			value := GetNumberPickerValue(picker, "")
 | 
			
		||||
			value := GetNumberPickerValue(picker)
 | 
			
		||||
			picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%f')`, picker.htmlID(), value))
 | 
			
		||||
			for _, listener := range picker.numberChangedListeners {
 | 
			
		||||
				listener(picker, value)
 | 
			
		||||
| 
						 | 
				
			
			@ -208,11 +171,11 @@ func (picker *numberPickerData) propertyChanged(tag string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (picker *numberPickerData) Get(tag string) interface{} {
 | 
			
		||||
func (picker *numberPickerData) Get(tag string) any {
 | 
			
		||||
	return picker.get(picker.normalizeTag(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (picker *numberPickerData) get(tag string) interface{} {
 | 
			
		||||
func (picker *numberPickerData) get(tag string) any {
 | 
			
		||||
	switch tag {
 | 
			
		||||
	case NumberChangedEvent:
 | 
			
		||||
		return picker.numberChangedListeners
 | 
			
		||||
| 
						 | 
				
			
			@ -229,13 +192,13 @@ func (picker *numberPickerData) htmlTag() string {
 | 
			
		|||
func (picker *numberPickerData) htmlProperties(self View, buffer *strings.Builder) {
 | 
			
		||||
	picker.viewData.htmlProperties(self, buffer)
 | 
			
		||||
 | 
			
		||||
	if GetNumberPickerType(picker, "") == NumberSlider {
 | 
			
		||||
	if GetNumberPickerType(picker) == NumberSlider {
 | 
			
		||||
		buffer.WriteString(` type="range"`)
 | 
			
		||||
	} else {
 | 
			
		||||
		buffer.WriteString(` type="number"`)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	min, max := GetNumberPickerMinMax(picker, "")
 | 
			
		||||
	min, max := GetNumberPickerMinMax(picker)
 | 
			
		||||
	if min != math.Inf(-1) {
 | 
			
		||||
		buffer.WriteString(` min="`)
 | 
			
		||||
		buffer.WriteString(strconv.FormatFloat(min, 'f', -1, 64))
 | 
			
		||||
| 
						 | 
				
			
			@ -248,7 +211,7 @@ func (picker *numberPickerData) htmlProperties(self View, buffer *strings.Builde
 | 
			
		|||
		buffer.WriteByte('"')
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	step := GetNumberPickerStep(picker, "")
 | 
			
		||||
	step := GetNumberPickerStep(picker)
 | 
			
		||||
	if step != 0 {
 | 
			
		||||
		buffer.WriteString(` step="`)
 | 
			
		||||
		buffer.WriteString(strconv.FormatFloat(step, 'f', -1, 64))
 | 
			
		||||
| 
						 | 
				
			
			@ -258,14 +221,14 @@ func (picker *numberPickerData) htmlProperties(self View, buffer *strings.Builde
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	buffer.WriteString(` value="`)
 | 
			
		||||
	buffer.WriteString(strconv.FormatFloat(GetNumberPickerValue(picker, ""), 'f', -1, 64))
 | 
			
		||||
	buffer.WriteString(strconv.FormatFloat(GetNumberPickerValue(picker), 'f', -1, 64))
 | 
			
		||||
	buffer.WriteByte('"')
 | 
			
		||||
 | 
			
		||||
	buffer.WriteString(` oninput="editViewInputEvent(this)"`)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (picker *numberPickerData) htmlDisabledProperties(self View, buffer *strings.Builder) {
 | 
			
		||||
	if IsDisabled(self, "") {
 | 
			
		||||
	if IsDisabled(self) {
 | 
			
		||||
		buffer.WriteString(` disabled`)
 | 
			
		||||
	}
 | 
			
		||||
	picker.viewData.htmlDisabledProperties(self, buffer)
 | 
			
		||||
| 
						 | 
				
			
			@ -276,8 +239,8 @@ func (picker *numberPickerData) handleCommand(self View, command string, data Da
 | 
			
		|||
	case "textChanged":
 | 
			
		||||
		if text, ok := data.PropertyValue("text"); ok {
 | 
			
		||||
			if value, err := strconv.ParseFloat(text, 32); err == nil {
 | 
			
		||||
				oldValue := GetNumberPickerValue(picker, "")
 | 
			
		||||
				picker.properties[NumberPickerValue] = value
 | 
			
		||||
				oldValue := GetNumberPickerValue(picker)
 | 
			
		||||
				picker.properties[NumberPickerValue] = text
 | 
			
		||||
				if value != oldValue {
 | 
			
		||||
					for _, listener := range picker.numberChangedListeners {
 | 
			
		||||
						listener(picker, value)
 | 
			
		||||
| 
						 | 
				
			
			@ -294,70 +257,50 @@ func (picker *numberPickerData) handleCommand(self View, command string, data Da
 | 
			
		|||
// GetNumberPickerType returns the type of NumberPicker subview. Valid values:
 | 
			
		||||
// NumberEditor (0) - NumberPicker is presented by editor (default type);
 | 
			
		||||
// NumberSlider (1) - NumberPicker is presented by slider.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetNumberPickerType(view View, subviewID string) int {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view == nil {
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	t, _ := enumStyledProperty(view, NumberPickerType, NumberEditor)
 | 
			
		||||
	return t
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetNumberPickerType(view View, subviewID ...string) int {
 | 
			
		||||
	return enumStyledProperty(view, subviewID, NumberPickerType, NumberEditor, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetNumberPickerMinMax returns the min and max value of NumberPicker subview.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetNumberPickerMinMax(view View, subviewID string) (float64, float64) {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetNumberPickerMinMax(view View, subviewID ...string) (float64, float64) {
 | 
			
		||||
	var pickerType int
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		pickerType = GetNumberPickerType(view, subviewID[0])
 | 
			
		||||
	} else {
 | 
			
		||||
		pickerType = GetNumberPickerType(view)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		t, _ := enumStyledProperty(view, NumberPickerType, NumberEditor)
 | 
			
		||||
		var defMin, defMax float64
 | 
			
		||||
 | 
			
		||||
		if t == NumberSlider {
 | 
			
		||||
			defMin = 0
 | 
			
		||||
			defMax = 1
 | 
			
		||||
		} else {
 | 
			
		||||
			defMin = math.Inf(-1)
 | 
			
		||||
			defMax = math.Inf(1)
 | 
			
		||||
		}
 | 
			
		||||
		min, ok := floatStyledProperty(view, NumberPickerMin, defMin)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			min, _ = floatStyledProperty(view, Min, defMin)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		max, ok := floatStyledProperty(view, NumberPickerMax, defMax)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			max, _ = floatStyledProperty(view, Max, defMax)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if min > max {
 | 
			
		||||
			return max, min
 | 
			
		||||
		}
 | 
			
		||||
		return min, max
 | 
			
		||||
	var defMin, defMax float64
 | 
			
		||||
	if pickerType == NumberSlider {
 | 
			
		||||
		defMin = 0
 | 
			
		||||
		defMax = 1
 | 
			
		||||
	} else {
 | 
			
		||||
		defMin = math.Inf(-1)
 | 
			
		||||
		defMax = math.Inf(1)
 | 
			
		||||
	}
 | 
			
		||||
	return 0, 1
 | 
			
		||||
 | 
			
		||||
	min := floatStyledProperty(view, subviewID, NumberPickerMin, defMin)
 | 
			
		||||
	max := floatStyledProperty(view, subviewID, NumberPickerMax, defMax)
 | 
			
		||||
 | 
			
		||||
	if min > max {
 | 
			
		||||
		return max, min
 | 
			
		||||
	}
 | 
			
		||||
	return min, max
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetNumberPickerStep returns the value changing step of NumberPicker subview.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetNumberPickerStep(view View, subviewID string) float64 {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view == nil {
 | 
			
		||||
		return 0
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetNumberPickerStep(view View, subviewID ...string) float64 {
 | 
			
		||||
	var max float64
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		_, max = GetNumberPickerMinMax(view, subviewID[0])
 | 
			
		||||
	} else {
 | 
			
		||||
		_, max = GetNumberPickerMinMax(view)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result, ok := floatStyledProperty(view, NumberPickerStep, 0)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		result, _ = floatStyledProperty(view, Step, 0)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, max := GetNumberPickerMinMax(view, "")
 | 
			
		||||
	result := floatStyledProperty(view, subviewID, NumberPickerStep, 0)
 | 
			
		||||
	if result > max {
 | 
			
		||||
		return max
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -365,36 +308,22 @@ func GetNumberPickerStep(view View, subviewID string) float64 {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// GetNumberPickerValue returns the value of NumberPicker subview.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetNumberPickerValue(view View, subviewID string) float64 {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view == nil {
 | 
			
		||||
		return 0
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetNumberPickerValue(view View, subviewID ...string) float64 {
 | 
			
		||||
	var min float64
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		min, _ = GetNumberPickerMinMax(view, subviewID[0])
 | 
			
		||||
	} else {
 | 
			
		||||
		min, _ = GetNumberPickerMinMax(view)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	min, _ := GetNumberPickerMinMax(view, "")
 | 
			
		||||
	result, ok := floatStyledProperty(view, NumberPickerValue, min)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		result, _ = floatStyledProperty(view, Value, min)
 | 
			
		||||
	}
 | 
			
		||||
	result := floatStyledProperty(view, subviewID, NumberPickerValue, min)
 | 
			
		||||
	return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetNumberChangedListeners returns the NumberChangedListener list of an NumberPicker subview.
 | 
			
		||||
// If there are no listeners then the empty list is returned
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetNumberChangedListeners(view View, subviewID string) []func(NumberPicker, float64) {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if value := view.Get(NumberChangedEvent); value != nil {
 | 
			
		||||
			if listeners, ok := value.([]func(NumberPicker, float64)); ok {
 | 
			
		||||
				return listeners
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return []func(NumberPicker, float64){}
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetNumberChangedListeners(view View, subviewID ...string) []func(NumberPicker, float64) {
 | 
			
		||||
	return getEventListeners[NumberPicker, float64](view, subviewID, NumberChangedEvent)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										16
									
								
								outline.go
								
								
								
								
							
							
						
						
									
										16
									
								
								outline.go
								
								
								
								
							| 
						 | 
				
			
			@ -18,7 +18,7 @@ type outlinePropertyData struct {
 | 
			
		|||
 | 
			
		||||
func NewOutlineProperty(params Params) OutlineProperty {
 | 
			
		||||
	outline := new(outlinePropertyData)
 | 
			
		||||
	outline.properties = map[string]interface{}{}
 | 
			
		||||
	outline.properties = map[string]any{}
 | 
			
		||||
	for tag, value := range params {
 | 
			
		||||
		outline.Set(tag, value)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -55,7 +55,7 @@ func (outline *outlinePropertyData) Remove(tag string) {
 | 
			
		|||
	delete(outline.properties, outline.normalizeTag(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (outline *outlinePropertyData) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (outline *outlinePropertyData) Set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		outline.Remove(tag)
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			@ -85,7 +85,7 @@ func (outline *outlinePropertyData) Set(tag string, value interface{}) bool {
 | 
			
		|||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (outline *outlinePropertyData) Get(tag string) interface{} {
 | 
			
		||||
func (outline *outlinePropertyData) Get(tag string) any {
 | 
			
		||||
	return outline.propertyList.Get(outline.normalizeTag(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -103,18 +103,18 @@ type ViewOutline struct {
 | 
			
		|||
	Width SizeUnit
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (outline ViewOutline) cssValue(builder cssBuilder) {
 | 
			
		||||
func (outline ViewOutline) cssValue(builder cssBuilder, session Session) {
 | 
			
		||||
	values := enumProperties[BorderStyle].cssValues
 | 
			
		||||
	if outline.Style > 0 && outline.Style < len(values) && outline.Color.Alpha() > 0 &&
 | 
			
		||||
		outline.Width.Type != Auto && outline.Width.Type != SizeInFraction &&
 | 
			
		||||
		outline.Width.Type != SizeInPercent && outline.Width.Value > 0 {
 | 
			
		||||
		builder.addValues("outline", " ", outline.Width.cssString("0"), values[outline.Style], outline.Color.cssString())
 | 
			
		||||
		builder.addValues("outline", " ", outline.Width.cssString("0", session), values[outline.Style], outline.Color.cssString())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (outline ViewOutline) cssString() string {
 | 
			
		||||
func (outline ViewOutline) cssString(session Session) string {
 | 
			
		||||
	var builder cssValueBuilder
 | 
			
		||||
	outline.cssValue(&builder)
 | 
			
		||||
	outline.cssValue(&builder, session)
 | 
			
		||||
	return builder.finish()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -128,7 +128,7 @@ func getOutline(properties Properties) OutlineProperty {
 | 
			
		|||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (style *viewStyle) setOutline(value interface{}) bool {
 | 
			
		||||
func (style *viewStyle) setOutline(value any) bool {
 | 
			
		||||
	switch value := value.(type) {
 | 
			
		||||
	case OutlineProperty:
 | 
			
		||||
		style.properties[Outline] = value
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										10
									
								
								params.go
								
								
								
								
							
							
						
						
									
										10
									
								
								params.go
								
								
								
								
							| 
						 | 
				
			
			@ -3,25 +3,25 @@ package rui
 | 
			
		|||
import "sort"
 | 
			
		||||
 | 
			
		||||
// Params defines a type of a parameters list
 | 
			
		||||
type Params map[string]interface{}
 | 
			
		||||
type Params map[string]any
 | 
			
		||||
 | 
			
		||||
func (params Params) Get(tag string) interface{} {
 | 
			
		||||
func (params Params) Get(tag string) any {
 | 
			
		||||
	return params.getRaw(tag)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (params Params) getRaw(tag string) interface{} {
 | 
			
		||||
func (params Params) getRaw(tag string) any {
 | 
			
		||||
	if value, ok := params[tag]; ok {
 | 
			
		||||
		return value
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (params Params) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (params Params) Set(tag string, value any) bool {
 | 
			
		||||
	params.setRaw(tag, value)
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (params Params) setRaw(tag string, value interface{}) {
 | 
			
		||||
func (params Params) setRaw(tag string, value any) {
 | 
			
		||||
	if value != nil {
 | 
			
		||||
		params[tag] = value
 | 
			
		||||
	} else {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										181
									
								
								pointerEvents.go
								
								
								
								
							
							
						
						
									
										181
									
								
								pointerEvents.go
								
								
								
								
							| 
						 | 
				
			
			@ -87,131 +87,6 @@ type PointerEvent struct {
 | 
			
		|||
	IsPrimary bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func valueToPointerListeners(value interface{}) ([]func(View, PointerEvent), bool) {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		return nil, true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch value := value.(type) {
 | 
			
		||||
	case func(View, PointerEvent):
 | 
			
		||||
		return []func(View, PointerEvent){value}, true
 | 
			
		||||
 | 
			
		||||
	case func(PointerEvent):
 | 
			
		||||
		fn := func(_ View, event PointerEvent) {
 | 
			
		||||
			value(event)
 | 
			
		||||
		}
 | 
			
		||||
		return []func(View, PointerEvent){fn}, true
 | 
			
		||||
 | 
			
		||||
	case func(View):
 | 
			
		||||
		fn := func(view View, _ PointerEvent) {
 | 
			
		||||
			value(view)
 | 
			
		||||
		}
 | 
			
		||||
		return []func(View, PointerEvent){fn}, true
 | 
			
		||||
 | 
			
		||||
	case func():
 | 
			
		||||
		fn := func(View, PointerEvent) {
 | 
			
		||||
			value()
 | 
			
		||||
		}
 | 
			
		||||
		return []func(View, PointerEvent){fn}, true
 | 
			
		||||
 | 
			
		||||
	case []func(View, PointerEvent):
 | 
			
		||||
		if len(value) == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		for _, fn := range value {
 | 
			
		||||
			if fn == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return value, true
 | 
			
		||||
 | 
			
		||||
	case []func(PointerEvent):
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		listeners := make([]func(View, PointerEvent), count)
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
			listeners[i] = func(_ View, event PointerEvent) {
 | 
			
		||||
				v(event)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners, true
 | 
			
		||||
 | 
			
		||||
	case []func(View):
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		listeners := make([]func(View, PointerEvent), count)
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
			listeners[i] = func(view View, _ PointerEvent) {
 | 
			
		||||
				v(view)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners, true
 | 
			
		||||
 | 
			
		||||
	case []func():
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		listeners := make([]func(View, PointerEvent), count)
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
			listeners[i] = func(View, PointerEvent) {
 | 
			
		||||
				v()
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners, true
 | 
			
		||||
 | 
			
		||||
	case []interface{}:
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		listeners := make([]func(View, PointerEvent), count)
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
			switch v := v.(type) {
 | 
			
		||||
			case func(View, PointerEvent):
 | 
			
		||||
				listeners[i] = v
 | 
			
		||||
 | 
			
		||||
			case func(PointerEvent):
 | 
			
		||||
				listeners[i] = func(_ View, event PointerEvent) {
 | 
			
		||||
					v(event)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			case func(View):
 | 
			
		||||
				listeners[i] = func(view View, _ PointerEvent) {
 | 
			
		||||
					v(view)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			case func():
 | 
			
		||||
				listeners[i] = func(View, PointerEvent) {
 | 
			
		||||
					v()
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			default:
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners, true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var pointerEvents = map[string]struct{ jsEvent, jsFunc string }{
 | 
			
		||||
	PointerDown:   {jsEvent: "onpointerdown", jsFunc: "pointerDownEvent"},
 | 
			
		||||
	PointerUp:     {jsEvent: "onpointerup", jsFunc: "pointerUpEvent"},
 | 
			
		||||
| 
						 | 
				
			
			@ -221,8 +96,8 @@ var pointerEvents = map[string]struct{ jsEvent, jsFunc string }{
 | 
			
		|||
	PointerOver:   {jsEvent: "onpointerover", jsFunc: "pointerOverEvent"},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (view *viewData) setPointerListener(tag string, value interface{}) bool {
 | 
			
		||||
	listeners, ok := valueToPointerListeners(value)
 | 
			
		||||
func (view *viewData) setPointerListener(tag string, value any) bool {
 | 
			
		||||
	listeners, ok := valueToEventListeners[View, PointerEvent](value)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		notCompatibleType(tag, value)
 | 
			
		||||
		return false
 | 
			
		||||
| 
						 | 
				
			
			@ -250,20 +125,6 @@ func (view *viewData) removePointerListener(tag string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getPointerListeners(view View, subviewID string, tag string) []func(View, PointerEvent) {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if value := view.Get(tag); value != nil {
 | 
			
		||||
			if result, ok := value.([]func(View, PointerEvent)); ok {
 | 
			
		||||
				return result
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return []func(View, PointerEvent){}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func pointerEventsHtml(view View, buffer *strings.Builder) {
 | 
			
		||||
	for tag, js := range pointerEvents {
 | 
			
		||||
		if value := view.getRaw(tag); value != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -291,7 +152,7 @@ func (event *PointerEvent) init(data DataObject) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func handlePointerEvents(view View, tag string, data DataObject) {
 | 
			
		||||
	listeners := getPointerListeners(view, "", tag)
 | 
			
		||||
	listeners := getEventListeners[View, PointerEvent](view, nil, tag)
 | 
			
		||||
	if len(listeners) == 0 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -305,37 +166,37 @@ func handlePointerEvents(view View, tag string, data DataObject) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// GetPointerDownListeners returns the "pointer-down" listener list. If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetPointerDownListeners(view View, subviewID string) []func(View, PointerEvent) {
 | 
			
		||||
	return getPointerListeners(view, subviewID, PointerDown)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetPointerDownListeners(view View, subviewID ...string) []func(View, PointerEvent) {
 | 
			
		||||
	return getEventListeners[View, PointerEvent](view, subviewID, PointerDown)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetPointerUpListeners returns the "pointer-up" listener list. If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetPointerUpListeners(view View, subviewID string) []func(View, PointerEvent) {
 | 
			
		||||
	return getPointerListeners(view, subviewID, PointerUp)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetPointerUpListeners(view View, subviewID ...string) []func(View, PointerEvent) {
 | 
			
		||||
	return getEventListeners[View, PointerEvent](view, subviewID, PointerUp)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetPointerMoveListeners returns the "pointer-move" listener list. If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetPointerMoveListeners(view View, subviewID string) []func(View, PointerEvent) {
 | 
			
		||||
	return getPointerListeners(view, subviewID, PointerMove)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetPointerMoveListeners(view View, subviewID ...string) []func(View, PointerEvent) {
 | 
			
		||||
	return getEventListeners[View, PointerEvent](view, subviewID, PointerMove)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetPointerCancelListeners returns the "pointer-cancel" listener list. If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetPointerCancelListeners(view View, subviewID string) []func(View, PointerEvent) {
 | 
			
		||||
	return getPointerListeners(view, subviewID, PointerCancel)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetPointerCancelListeners(view View, subviewID ...string) []func(View, PointerEvent) {
 | 
			
		||||
	return getEventListeners[View, PointerEvent](view, subviewID, PointerCancel)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetPointerOverListeners returns the "pointer-over" listener list. If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetPointerOverListeners(view View, subviewID string) []func(View, PointerEvent) {
 | 
			
		||||
	return getPointerListeners(view, subviewID, PointerOver)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetPointerOverListeners(view View, subviewID ...string) []func(View, PointerEvent) {
 | 
			
		||||
	return getEventListeners[View, PointerEvent](view, subviewID, PointerOver)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetPointerOutListeners returns the "pointer-out" listener list. If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetPointerOutListeners(view View, subviewID string) []func(View, PointerEvent) {
 | 
			
		||||
	return getPointerListeners(view, subviewID, PointerOut)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetPointerOutListeners(view View, subviewID ...string) []func(View, PointerEvent) {
 | 
			
		||||
	return getEventListeners[View, PointerEvent](view, subviewID, PointerOut)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										520
									
								
								popup.go
								
								
								
								
							
							
						
						
									
										520
									
								
								popup.go
								
								
								
								
							| 
						 | 
				
			
			@ -1,6 +1,8 @@
 | 
			
		|||
package rui
 | 
			
		||||
 | 
			
		||||
import "strings"
 | 
			
		||||
import (
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// Title is the constant for the "title" property tag.
 | 
			
		||||
| 
						 | 
				
			
			@ -36,6 +38,46 @@ const (
 | 
			
		|||
	// It occurs after the Popup disappears from the screen.
 | 
			
		||||
	// The main listener for this event has the following format: func(Popup)
 | 
			
		||||
	DismissEvent = "dismiss-event"
 | 
			
		||||
 | 
			
		||||
	// Arrow is the constant for the "arrow" property tag.
 | 
			
		||||
	// Using the "popup-arrow" int property you can add ...
 | 
			
		||||
	Arrow = "arrow"
 | 
			
		||||
 | 
			
		||||
	// ArrowAlign is the constant for the "arrow-align" property tag.
 | 
			
		||||
	// The "arrow-align" int property is used for set the horizontal alignment of the Popup arrow.
 | 
			
		||||
	// Valid values: LeftAlign (0), RightAlign (1), TopAlign (0), BottomAlign (1), CenterAlign (2)
 | 
			
		||||
	ArrowAlign = "arrow-align"
 | 
			
		||||
 | 
			
		||||
	// ArrowSize is the constant for the "arrow-size" property tag.
 | 
			
		||||
	// The "arrow-size" SizeUnit property is used for set the size (length) of the Popup arrow.
 | 
			
		||||
	ArrowSize = "arrow-size"
 | 
			
		||||
 | 
			
		||||
	// ArrowWidth is the constant for the "arrow-width" property tag.
 | 
			
		||||
	// The "arrow-width" SizeUnit property is used for set the width of the Popup arrow.
 | 
			
		||||
	ArrowWidth = "arrow-width"
 | 
			
		||||
 | 
			
		||||
	// ArrowOffset is the constant for the "arrow-offset" property tag.
 | 
			
		||||
	// The "arrow-offset" SizeUnit property is used for set the offset of the Popup arrow.
 | 
			
		||||
	ArrowOffset = "arrow-offset"
 | 
			
		||||
 | 
			
		||||
	// NoneArrow is value of the popup "arrow" property: no arrow
 | 
			
		||||
	NoneArrow = 0
 | 
			
		||||
 | 
			
		||||
	// TopArrow is value of the popup "arrow" property:
 | 
			
		||||
	// Arrow at the top side of the pop-up window
 | 
			
		||||
	TopArrow = 1
 | 
			
		||||
 | 
			
		||||
	// RightArrow is value of the popup "arrow" property:
 | 
			
		||||
	// Arrow on the right side of the pop-up window
 | 
			
		||||
	RightArrow = 2
 | 
			
		||||
 | 
			
		||||
	// BottomArrow is value of the popup "arrow" property:
 | 
			
		||||
	// Arrow at the bottom of the pop-up window
 | 
			
		||||
	BottomArrow = 3
 | 
			
		||||
 | 
			
		||||
	// LeftArrow is value of the popup "arrow" property:
 | 
			
		||||
	// Arrow on the left side of the pop-up window
 | 
			
		||||
	LeftArrow = 4
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// PopupButton describes a button that will be placed at the bottom of the window.
 | 
			
		||||
| 
						 | 
				
			
			@ -65,152 +107,388 @@ type popupManager struct {
 | 
			
		|||
	popups []Popup
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (popup *popupData) init(view View, params Params) {
 | 
			
		||||
type popupArrow struct {
 | 
			
		||||
	column, row      int
 | 
			
		||||
	location, align  int
 | 
			
		||||
	size, width, off SizeUnit
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (arrow *popupArrow) fixOff(popupView View) {
 | 
			
		||||
	if arrow.align == CenterAlign && arrow.off.Type == Auto {
 | 
			
		||||
		r := GetRadius(popupView)
 | 
			
		||||
		switch arrow.location {
 | 
			
		||||
		case TopArrow:
 | 
			
		||||
			switch arrow.align {
 | 
			
		||||
			case LeftAlign:
 | 
			
		||||
				arrow.off = r.TopLeftX
 | 
			
		||||
 | 
			
		||||
			case RightAlign:
 | 
			
		||||
				arrow.off = r.TopRightX
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		case BottomArrow:
 | 
			
		||||
			switch arrow.align {
 | 
			
		||||
			case LeftAlign:
 | 
			
		||||
				arrow.off = r.BottomLeftX
 | 
			
		||||
 | 
			
		||||
			case RightAlign:
 | 
			
		||||
				arrow.off = r.BottomRightX
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		case RightArrow:
 | 
			
		||||
			switch arrow.align {
 | 
			
		||||
			case TopAlign:
 | 
			
		||||
				arrow.off = r.TopRightY
 | 
			
		||||
 | 
			
		||||
			case BottomAlign:
 | 
			
		||||
				arrow.off = r.BottomRightY
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		case LeftArrow:
 | 
			
		||||
			switch arrow.align {
 | 
			
		||||
			case TopAlign:
 | 
			
		||||
				arrow.off = r.TopLeftY
 | 
			
		||||
 | 
			
		||||
			case BottomAlign:
 | 
			
		||||
				arrow.off = r.BottomLeftY
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (arrow *popupArrow) createView(popupView View) View {
 | 
			
		||||
	session := popupView.Session()
 | 
			
		||||
 | 
			
		||||
	defaultSize := func(constTag string, defValue SizeUnit) SizeUnit {
 | 
			
		||||
		if value, ok := session.Constant(constTag); ok {
 | 
			
		||||
			if size, ok := StringToSizeUnit(value); ok && size.Type != Auto && size.Value != 0 {
 | 
			
		||||
				return size
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return defValue
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if arrow.size.Type == Auto || arrow.size.Value == 0 {
 | 
			
		||||
		arrow.size = defaultSize("ruiArrowSize", Px(16))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if arrow.width.Type == Auto || arrow.width.Value == 0 {
 | 
			
		||||
		arrow.width = defaultSize("ruiArrowWidth", Px(16))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	params := Params{BackgroundColor: GetBackgroundColor(popupView)}
 | 
			
		||||
 | 
			
		||||
	if shadow := GetViewShadows(popupView); shadow != nil {
 | 
			
		||||
		params[Shadow] = shadow
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if filter := GetBackdropFilter(popupView); filter != nil {
 | 
			
		||||
		params[BackdropFilter] = filter
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch arrow.location {
 | 
			
		||||
	case TopArrow:
 | 
			
		||||
		params[Row] = 0
 | 
			
		||||
		params[Column] = 1
 | 
			
		||||
		params[Clip] = PolygonClip([]any{"0%", "100%", "50%", "0%", "100%", "100%"})
 | 
			
		||||
		params[Width] = arrow.width
 | 
			
		||||
		params[Height] = arrow.size
 | 
			
		||||
 | 
			
		||||
	case RightArrow:
 | 
			
		||||
		params[Row] = 1
 | 
			
		||||
		params[Column] = 0
 | 
			
		||||
		params[Clip] = PolygonClip([]any{"0%", "0%", "100%", "50%", "0%", "100%"})
 | 
			
		||||
		params[Width] = arrow.size
 | 
			
		||||
		params[Height] = arrow.width
 | 
			
		||||
 | 
			
		||||
	case BottomArrow:
 | 
			
		||||
		params[Row] = 0
 | 
			
		||||
		params[Column] = 1
 | 
			
		||||
		params[Clip] = PolygonClip([]any{"0%", "0%", "50%", "100%", "100%", "0%"})
 | 
			
		||||
		params[Width] = arrow.width
 | 
			
		||||
		params[Height] = arrow.size
 | 
			
		||||
 | 
			
		||||
	case LeftArrow:
 | 
			
		||||
		params[Row] = 1
 | 
			
		||||
		params[Column] = 0
 | 
			
		||||
		params[Clip] = PolygonClip([]any{"100%", "0%", "0%", "50%", "100%", "100%"})
 | 
			
		||||
		params[Width] = arrow.size
 | 
			
		||||
		params[Height] = arrow.width
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	arrowView := NewView(session, params)
 | 
			
		||||
 | 
			
		||||
	params = Params{
 | 
			
		||||
		Row:     arrow.row,
 | 
			
		||||
		Column:  arrow.column,
 | 
			
		||||
		Content: arrowView,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	arrow.fixOff(popupView)
 | 
			
		||||
 | 
			
		||||
	switch arrow.location {
 | 
			
		||||
	case TopArrow, BottomArrow:
 | 
			
		||||
		cellWidth := make([]SizeUnit, 3)
 | 
			
		||||
		switch arrow.align {
 | 
			
		||||
		case LeftAlign:
 | 
			
		||||
			cellWidth[0] = arrow.off
 | 
			
		||||
			cellWidth[2] = Fr(1)
 | 
			
		||||
 | 
			
		||||
		case RightAlign:
 | 
			
		||||
			cellWidth[0] = Fr(1)
 | 
			
		||||
			cellWidth[2] = arrow.off
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
			cellWidth[0] = Fr(1)
 | 
			
		||||
			cellWidth[2] = Fr(1)
 | 
			
		||||
			if arrow.off.Type != Auto && arrow.off.Value != 0 {
 | 
			
		||||
				arrowView.Set(MarginLeft, arrow.off)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		params[CellWidth] = cellWidth
 | 
			
		||||
 | 
			
		||||
	case RightArrow, LeftArrow:
 | 
			
		||||
		cellHeight := make([]SizeUnit, 3)
 | 
			
		||||
		switch arrow.align {
 | 
			
		||||
		case TopAlign:
 | 
			
		||||
			cellHeight[0] = arrow.off
 | 
			
		||||
			cellHeight[2] = Fr(1)
 | 
			
		||||
 | 
			
		||||
		case BottomAlign:
 | 
			
		||||
			cellHeight[0] = Fr(1)
 | 
			
		||||
			cellHeight[2] = arrow.off
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
			cellHeight[0] = Fr(1)
 | 
			
		||||
			cellHeight[2] = Fr(1)
 | 
			
		||||
			if arrow.off.Type != Auto && arrow.off.Value != 0 {
 | 
			
		||||
				arrowView.Set(MarginTop, arrow.off)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		params[CellHeight] = cellHeight
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NewGridLayout(session, params)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (popup *popupData) init(view View, popupParams Params) {
 | 
			
		||||
	popup.view = view
 | 
			
		||||
	session := view.Session()
 | 
			
		||||
 | 
			
		||||
	if params == nil {
 | 
			
		||||
		params = Params{}
 | 
			
		||||
	columnCount := 3
 | 
			
		||||
	rowCount := 3
 | 
			
		||||
	popupRow := 1
 | 
			
		||||
	popupColumn := 1
 | 
			
		||||
	arrow := popupArrow{
 | 
			
		||||
		row:    1,
 | 
			
		||||
		column: 1,
 | 
			
		||||
		align:  CenterAlign,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	popup.dismissListener = []func(Popup){}
 | 
			
		||||
	if value, ok := params[DismissEvent]; ok && value != nil {
 | 
			
		||||
		switch value := value.(type) {
 | 
			
		||||
		case func(Popup):
 | 
			
		||||
			popup.dismissListener = []func(Popup){value}
 | 
			
		||||
	switch arrow.location, _ = enumProperty(popupParams, Arrow, session, NoneArrow); arrow.location {
 | 
			
		||||
	case TopArrow:
 | 
			
		||||
		rowCount = 4
 | 
			
		||||
		popupRow = 2
 | 
			
		||||
 | 
			
		||||
		case func():
 | 
			
		||||
			popup.dismissListener = []func(Popup){
 | 
			
		||||
				func(_ Popup) {
 | 
			
		||||
					value()
 | 
			
		||||
				},
 | 
			
		||||
			}
 | 
			
		||||
	case BottomArrow:
 | 
			
		||||
		rowCount = 4
 | 
			
		||||
		arrow.row = 2
 | 
			
		||||
 | 
			
		||||
		case []func(Popup):
 | 
			
		||||
			for _, fn := range value {
 | 
			
		||||
				if fn != nil {
 | 
			
		||||
					popup.dismissListener = append(popup.dismissListener, fn)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
	case LeftArrow:
 | 
			
		||||
		columnCount = 4
 | 
			
		||||
		popupColumn = 2
 | 
			
		||||
 | 
			
		||||
		case []func():
 | 
			
		||||
			for _, fn := range value {
 | 
			
		||||
				if fn != nil {
 | 
			
		||||
					popup.dismissListener = append(popup.dismissListener, func(_ Popup) {
 | 
			
		||||
						fn()
 | 
			
		||||
					})
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
	case RightArrow:
 | 
			
		||||
		columnCount = 4
 | 
			
		||||
		arrow.column = 2
 | 
			
		||||
 | 
			
		||||
		case []interface{}:
 | 
			
		||||
			for _, val := range value {
 | 
			
		||||
				if val != nil {
 | 
			
		||||
					switch fn := val.(type) {
 | 
			
		||||
					case func(Popup):
 | 
			
		||||
						popup.dismissListener = append(popup.dismissListener, fn)
 | 
			
		||||
 | 
			
		||||
					case func():
 | 
			
		||||
						popup.dismissListener = append(popup.dismissListener, func(_ Popup) {
 | 
			
		||||
							fn()
 | 
			
		||||
						})
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var title View = nil
 | 
			
		||||
	titleStyle := "ruiPopupTitle"
 | 
			
		||||
	closeButton, _ := boolProperty(params, CloseButton, session)
 | 
			
		||||
	outsideClose, _ := boolProperty(params, OutsideClose, session)
 | 
			
		||||
	vAlign, _ := enumProperty(params, VerticalAlign, session, CenterAlign)
 | 
			
		||||
	hAlign, _ := enumProperty(params, HorizontalAlign, session, CenterAlign)
 | 
			
		||||
	cellWidth := make([]SizeUnit, columnCount)
 | 
			
		||||
	switch hAlign, _ := enumProperty(popupParams, HorizontalAlign, session, CenterAlign); hAlign {
 | 
			
		||||
	case LeftAlign:
 | 
			
		||||
		cellWidth[columnCount-1] = Fr(1)
 | 
			
		||||
 | 
			
		||||
	buttons := []PopupButton{}
 | 
			
		||||
	if value, ok := params[Buttons]; ok && value != nil {
 | 
			
		||||
		switch value := value.(type) {
 | 
			
		||||
		case PopupButton:
 | 
			
		||||
			buttons = []PopupButton{value}
 | 
			
		||||
	case RightAlign:
 | 
			
		||||
		cellWidth[0] = Fr(1)
 | 
			
		||||
 | 
			
		||||
		case []PopupButton:
 | 
			
		||||
			buttons = value
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		cellWidth[0] = Fr(1)
 | 
			
		||||
		cellWidth[columnCount-1] = Fr(1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	popupView := NewGridLayout(view.Session(), Params{
 | 
			
		||||
	cellHeight := make([]SizeUnit, rowCount)
 | 
			
		||||
	switch vAlign, _ := enumProperty(popupParams, VerticalAlign, session, CenterAlign); vAlign {
 | 
			
		||||
	case LeftAlign:
 | 
			
		||||
		cellHeight[rowCount-1] = Fr(1)
 | 
			
		||||
 | 
			
		||||
	case RightAlign:
 | 
			
		||||
		cellHeight[0] = Fr(1)
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		cellHeight[0] = Fr(1)
 | 
			
		||||
		cellHeight[rowCount-1] = Fr(1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	layerParams := Params{
 | 
			
		||||
		Style:      "ruiPopupLayer",
 | 
			
		||||
		MaxWidth:   Percent(100),
 | 
			
		||||
		MaxHeight:  Percent(100),
 | 
			
		||||
		CellWidth:  cellWidth,
 | 
			
		||||
		CellHeight: cellHeight,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	params := Params{
 | 
			
		||||
		Style:               "ruiPopup",
 | 
			
		||||
		Row:                 popupRow,
 | 
			
		||||
		Column:              popupColumn,
 | 
			
		||||
		MaxWidth:            Percent(100),
 | 
			
		||||
		MaxHeight:           Percent(100),
 | 
			
		||||
		CellVerticalAlign:   StretchAlign,
 | 
			
		||||
		CellHorizontalAlign: StretchAlign,
 | 
			
		||||
		ClickEvent:          func(View) {},
 | 
			
		||||
	})
 | 
			
		||||
		Shadow: NewShadowWithParams(Params{
 | 
			
		||||
			SpreadRadius: Px(4),
 | 
			
		||||
			Blur:         Px(16),
 | 
			
		||||
			ColorTag:     "@ruiPopupShadow",
 | 
			
		||||
		}),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for tag, value := range params {
 | 
			
		||||
		switch tag {
 | 
			
		||||
		case Title:
 | 
			
		||||
			switch value := value.(type) {
 | 
			
		||||
			case string:
 | 
			
		||||
				title = NewTextView(view.Session(), Params{Text: value})
 | 
			
		||||
	var closeButton View = nil
 | 
			
		||||
	outsideClose := false
 | 
			
		||||
	buttons := []PopupButton{}
 | 
			
		||||
	titleStyle := "ruiPopupTitle"
 | 
			
		||||
	var title View = nil
 | 
			
		||||
 | 
			
		||||
			case View:
 | 
			
		||||
				title = value
 | 
			
		||||
	for tag, value := range popupParams {
 | 
			
		||||
		if value != nil {
 | 
			
		||||
			switch tag {
 | 
			
		||||
			case VerticalAlign, HorizontalAlign, Arrow, Row, Column:
 | 
			
		||||
				// Do nothing
 | 
			
		||||
 | 
			
		||||
			case Margin:
 | 
			
		||||
				layerParams[Padding] = value
 | 
			
		||||
 | 
			
		||||
			case MarginLeft:
 | 
			
		||||
				layerParams[PaddingLeft] = value
 | 
			
		||||
 | 
			
		||||
			case MarginRight:
 | 
			
		||||
				layerParams[PaddingRight] = value
 | 
			
		||||
 | 
			
		||||
			case MarginTop:
 | 
			
		||||
				layerParams[PaddingTop] = value
 | 
			
		||||
 | 
			
		||||
			case MarginBottom:
 | 
			
		||||
				layerParams[PaddingBottom] = value
 | 
			
		||||
 | 
			
		||||
			case CloseButton:
 | 
			
		||||
				closeButton = NewGridLayout(session, Params{
 | 
			
		||||
					Column:              1,
 | 
			
		||||
					Height:              "@ruiPopupTitleHeight",
 | 
			
		||||
					Width:               "@ruiPopupTitleHeight",
 | 
			
		||||
					CellHorizontalAlign: CenterAlign,
 | 
			
		||||
					CellVerticalAlign:   CenterAlign,
 | 
			
		||||
					TextSize:            Px(20),
 | 
			
		||||
					Content:             "✕",
 | 
			
		||||
					NotTranslate:        true,
 | 
			
		||||
					ClickEvent: func(View) {
 | 
			
		||||
						popup.Dismiss()
 | 
			
		||||
					},
 | 
			
		||||
				})
 | 
			
		||||
 | 
			
		||||
			case OutsideClose:
 | 
			
		||||
				outsideClose, _ = boolProperty(popupParams, OutsideClose, session)
 | 
			
		||||
 | 
			
		||||
			case Buttons:
 | 
			
		||||
				switch value := value.(type) {
 | 
			
		||||
				case PopupButton:
 | 
			
		||||
					buttons = []PopupButton{value}
 | 
			
		||||
 | 
			
		||||
				case []PopupButton:
 | 
			
		||||
					buttons = value
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			case Title:
 | 
			
		||||
				switch value := value.(type) {
 | 
			
		||||
				case string:
 | 
			
		||||
					title = NewTextView(view.Session(), Params{Text: value})
 | 
			
		||||
 | 
			
		||||
				case View:
 | 
			
		||||
					title = value
 | 
			
		||||
 | 
			
		||||
				default:
 | 
			
		||||
					notCompatibleType(Title, value)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			case TitleStyle:
 | 
			
		||||
				switch value := value.(type) {
 | 
			
		||||
				case string:
 | 
			
		||||
					titleStyle = value
 | 
			
		||||
 | 
			
		||||
				default:
 | 
			
		||||
					notCompatibleType(TitleStyle, value)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			case DismissEvent:
 | 
			
		||||
				if listeners, ok := valueToNoParamListeners[Popup](value); ok {
 | 
			
		||||
					if listeners != nil {
 | 
			
		||||
						popup.dismissListener = listeners
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					notCompatibleType(tag, value)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			case ArrowAlign:
 | 
			
		||||
				switch text := value.(type) {
 | 
			
		||||
				case string:
 | 
			
		||||
					switch text {
 | 
			
		||||
					case "top":
 | 
			
		||||
						value = "left"
 | 
			
		||||
 | 
			
		||||
					case "bottom":
 | 
			
		||||
						value = "right"
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				arrow.align, _ = enumProperty(popupParams, ArrowAlign, session, CenterAlign)
 | 
			
		||||
 | 
			
		||||
			case ArrowSize:
 | 
			
		||||
				arrow.size, _ = sizeProperty(popupParams, ArrowSize, session)
 | 
			
		||||
 | 
			
		||||
			case ArrowOffset:
 | 
			
		||||
				arrow.off, _ = sizeProperty(popupParams, ArrowOffset, session)
 | 
			
		||||
 | 
			
		||||
			default:
 | 
			
		||||
				notCompatibleType(Title, value)
 | 
			
		||||
				params[tag] = value
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		case TitleStyle:
 | 
			
		||||
			switch value := value.(type) {
 | 
			
		||||
			case string:
 | 
			
		||||
				titleStyle = value
 | 
			
		||||
 | 
			
		||||
			default:
 | 
			
		||||
				notCompatibleType(TitleStyle, value)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		case CloseButton, OutsideClose, VerticalAlign, HorizontalAlign, Buttons:
 | 
			
		||||
			// do nothing
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
			popupView.Set(tag, value)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var cellHeight []SizeUnit
 | 
			
		||||
	popupView := NewGridLayout(view.Session(), params)
 | 
			
		||||
 | 
			
		||||
	var popupCellHeight []SizeUnit
 | 
			
		||||
	viewRow := 0
 | 
			
		||||
	if title != nil || closeButton {
 | 
			
		||||
		viewRow = 1
 | 
			
		||||
		titleHeight, _ := sizeConstant(popup.Session(), "ruiPopupTitleHeight")
 | 
			
		||||
		titleView := NewGridLayout(session, Params{
 | 
			
		||||
	if title != nil || closeButton != nil {
 | 
			
		||||
		titleContent := []View{}
 | 
			
		||||
		if title != nil {
 | 
			
		||||
			titleContent = append(titleContent, title)
 | 
			
		||||
		}
 | 
			
		||||
		if closeButton != nil {
 | 
			
		||||
			titleContent = append(titleContent, closeButton)
 | 
			
		||||
		}
 | 
			
		||||
		popupView.Append(NewGridLayout(session, Params{
 | 
			
		||||
			Row:               0,
 | 
			
		||||
			Style:             titleStyle,
 | 
			
		||||
			CellWidth:         []SizeUnit{Fr(1), titleHeight},
 | 
			
		||||
			CellWidth:         []any{Fr(1), AutoSize()},
 | 
			
		||||
			CellVerticalAlign: CenterAlign,
 | 
			
		||||
			PaddingLeft:       Px(12),
 | 
			
		||||
		})
 | 
			
		||||
		if title != nil {
 | 
			
		||||
			titleView.Append(title)
 | 
			
		||||
		}
 | 
			
		||||
		if closeButton {
 | 
			
		||||
			titleView.Append(NewGridLayout(session, Params{
 | 
			
		||||
				Column:              1,
 | 
			
		||||
				Height:              titleHeight,
 | 
			
		||||
				Width:               titleHeight,
 | 
			
		||||
				CellHorizontalAlign: CenterAlign,
 | 
			
		||||
				CellVerticalAlign:   CenterAlign,
 | 
			
		||||
				TextSize:            Px(20),
 | 
			
		||||
				Content:             "✕",
 | 
			
		||||
				ClickEvent: func(View) {
 | 
			
		||||
					popup.Dismiss()
 | 
			
		||||
				},
 | 
			
		||||
			}))
 | 
			
		||||
		}
 | 
			
		||||
			Content:           titleContent,
 | 
			
		||||
		}))
 | 
			
		||||
 | 
			
		||||
		popupView.Append(titleView)
 | 
			
		||||
		cellHeight = []SizeUnit{AutoSize(), Fr(1)}
 | 
			
		||||
		viewRow = 1
 | 
			
		||||
		popupCellHeight = []SizeUnit{AutoSize(), Fr(1)}
 | 
			
		||||
	} else {
 | 
			
		||||
		cellHeight = []SizeUnit{Fr(1)}
 | 
			
		||||
		popupCellHeight = []SizeUnit{Fr(1)}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	view.Set(Row, viewRow)
 | 
			
		||||
| 
						 | 
				
			
			@ -218,7 +496,7 @@ func (popup *popupData) init(view View, params Params) {
 | 
			
		|||
 | 
			
		||||
	if buttonCount := len(buttons); buttonCount > 0 {
 | 
			
		||||
		buttonsAlign, _ := enumProperty(params, ButtonsAlign, session, RightAlign)
 | 
			
		||||
		cellHeight = append(cellHeight, AutoSize())
 | 
			
		||||
		popupCellHeight = append(popupCellHeight, AutoSize())
 | 
			
		||||
		gap, _ := sizeConstant(session, "ruiPopupButtonGap")
 | 
			
		||||
		cellWidth := []SizeUnit{}
 | 
			
		||||
		for i := 0; i < buttonCount; i++ {
 | 
			
		||||
| 
						 | 
				
			
			@ -256,16 +534,16 @@ func (popup *popupData) init(view View, params Params) {
 | 
			
		|||
			Content:             buttonsPanel,
 | 
			
		||||
		}))
 | 
			
		||||
	}
 | 
			
		||||
	popupView.Set(CellHeight, cellHeight)
 | 
			
		||||
 | 
			
		||||
	popup.layerView = NewGridLayout(session, Params{
 | 
			
		||||
		Style:               "ruiPopupLayer",
 | 
			
		||||
		CellVerticalAlign:   vAlign,
 | 
			
		||||
		CellHorizontalAlign: hAlign,
 | 
			
		||||
		Content:             NewColumnLayout(session, Params{Content: popupView}),
 | 
			
		||||
		MaxWidth:            Percent(100),
 | 
			
		||||
		MaxHeight:           Percent(100),
 | 
			
		||||
	})
 | 
			
		||||
	popupView.Set(CellHeight, popupCellHeight)
 | 
			
		||||
 | 
			
		||||
	if arrow.location != NoneArrow {
 | 
			
		||||
		layerParams[Content] = []View{popupView, arrow.createView(popupView)}
 | 
			
		||||
	} else {
 | 
			
		||||
		layerParams[Content] = []View{popupView}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	popup.layerView = NewGridLayout(session, layerParams)
 | 
			
		||||
 | 
			
		||||
	if outsideClose {
 | 
			
		||||
		popup.layerView.Set(ClickEvent, func(View) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -103,19 +103,22 @@ func ShowCancellableQuestion(title, text string, session Session, onYes func(),
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
type popupMenuData struct {
 | 
			
		||||
	items   []string
 | 
			
		||||
	session Session
 | 
			
		||||
	popup   Popup
 | 
			
		||||
	result  func(int)
 | 
			
		||||
	items    []string
 | 
			
		||||
	disabled []int
 | 
			
		||||
	session  Session
 | 
			
		||||
	popup    Popup
 | 
			
		||||
	result   func(int)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (popup *popupMenuData) itemClick(list ListView, n int) {
 | 
			
		||||
	if popup.popup != nil {
 | 
			
		||||
		popup.popup.Dismiss()
 | 
			
		||||
		popup.popup = nil
 | 
			
		||||
	}
 | 
			
		||||
	if popup.result != nil {
 | 
			
		||||
		popup.result(n)
 | 
			
		||||
	if popup.IsListItemEnabled(n) {
 | 
			
		||||
		if popup.popup != nil {
 | 
			
		||||
			popup.popup.Dismiss()
 | 
			
		||||
			popup.popup = nil
 | 
			
		||||
		}
 | 
			
		||||
		if popup.result != nil {
 | 
			
		||||
			popup.result(n)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -131,6 +134,13 @@ func (popup *popupMenuData) ListItem(index int, session Session) View {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (popup *popupMenuData) IsListItemEnabled(index int) bool {
 | 
			
		||||
	if popup.disabled != nil {
 | 
			
		||||
		for _, n := range popup.disabled {
 | 
			
		||||
			if index == n {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -165,13 +175,18 @@ func ShowMenu(session Session, params Params) Popup {
 | 
			
		|||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	value, ok = params[PopupMenuResult]
 | 
			
		||||
	if ok && value != nil {
 | 
			
		||||
	if value, ok := params[PopupMenuResult]; ok && value != nil {
 | 
			
		||||
		if result, ok := value.(func(int)); ok {
 | 
			
		||||
			data.result = result
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if value, ok := params[DisabledItems]; ok && value != nil {
 | 
			
		||||
		if value, ok := value.([]int); ok {
 | 
			
		||||
			data.disabled = value
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	listView := NewListView(session, Params{
 | 
			
		||||
		Items:                adapter,
 | 
			
		||||
		Orientation:          TopDownOrientation,
 | 
			
		||||
| 
						 | 
				
			
			@ -181,7 +196,7 @@ func ShowMenu(session Session, params Params) Popup {
 | 
			
		|||
	popupParams := Params{}
 | 
			
		||||
	for tag, value := range params {
 | 
			
		||||
		switch tag {
 | 
			
		||||
		case Items, PopupMenuResult:
 | 
			
		||||
		case Items, PopupMenuResult, DisabledItems:
 | 
			
		||||
			// do nothing
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,7 +22,7 @@ type progressBarData struct {
 | 
			
		|||
// NewProgressBar create new ProgressBar object and return it
 | 
			
		||||
func NewProgressBar(session Session, params Params) ProgressBar {
 | 
			
		||||
	view := new(progressBarData)
 | 
			
		||||
	view.Init(session)
 | 
			
		||||
	view.init(session)
 | 
			
		||||
	setInitParams(view, params)
 | 
			
		||||
	return view
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -31,8 +31,8 @@ func newProgressBar(session Session) View {
 | 
			
		|||
	return NewProgressBar(session, nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (progress *progressBarData) Init(session Session) {
 | 
			
		||||
	progress.viewData.Init(session)
 | 
			
		||||
func (progress *progressBarData) init(session Session) {
 | 
			
		||||
	progress.viewData.init(session)
 | 
			
		||||
	progress.tag = "ProgressBar"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -65,19 +65,19 @@ func (progress *progressBarData) propertyChanged(tag string) {
 | 
			
		|||
	if progress.created {
 | 
			
		||||
		switch tag {
 | 
			
		||||
		case ProgressBarMax:
 | 
			
		||||
			updateProperty(progress.htmlID(), Max, strconv.FormatFloat(GetProgressBarMax(progress, ""), 'f', -1, 32), progress.session)
 | 
			
		||||
			updateProperty(progress.htmlID(), Max, strconv.FormatFloat(GetProgressBarMax(progress), 'f', -1, 32), progress.session)
 | 
			
		||||
 | 
			
		||||
		case ProgressBarValue:
 | 
			
		||||
			updateProperty(progress.htmlID(), Value, strconv.FormatFloat(GetProgressBarValue(progress, ""), 'f', -1, 32), progress.session)
 | 
			
		||||
			updateProperty(progress.htmlID(), Value, strconv.FormatFloat(GetProgressBarValue(progress), 'f', -1, 32), progress.session)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (progress *progressBarData) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (progress *progressBarData) Set(tag string, value any) bool {
 | 
			
		||||
	return progress.set(progress.normalizeTag(tag), value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (progress *progressBarData) set(tag string, value interface{}) bool {
 | 
			
		||||
func (progress *progressBarData) set(tag string, value any) bool {
 | 
			
		||||
	if progress.viewData.set(tag, value) {
 | 
			
		||||
		progress.propertyChanged(tag)
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			@ -85,7 +85,7 @@ func (progress *progressBarData) set(tag string, value interface{}) bool {
 | 
			
		|||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (progress *progressBarData) Get(tag string) interface{} {
 | 
			
		||||
func (progress *progressBarData) Get(tag string) any {
 | 
			
		||||
	return progress.get(progress.normalizeTag(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -97,44 +97,22 @@ func (progress *progressBarData) htmlProperties(self View, buffer *strings.Build
 | 
			
		|||
	progress.viewData.htmlProperties(self, buffer)
 | 
			
		||||
 | 
			
		||||
	buffer.WriteString(` max="`)
 | 
			
		||||
	buffer.WriteString(strconv.FormatFloat(GetProgressBarMax(progress, ""), 'f', -1, 64))
 | 
			
		||||
	buffer.WriteString(strconv.FormatFloat(GetProgressBarMax(progress), 'f', -1, 64))
 | 
			
		||||
	buffer.WriteByte('"')
 | 
			
		||||
 | 
			
		||||
	buffer.WriteString(` value="`)
 | 
			
		||||
	buffer.WriteString(strconv.FormatFloat(GetProgressBarValue(progress, ""), 'f', -1, 64))
 | 
			
		||||
	buffer.WriteString(strconv.FormatFloat(GetProgressBarValue(progress), 'f', -1, 64))
 | 
			
		||||
	buffer.WriteByte('"')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetProgressBarMax returns the max value of ProgressBar subview.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetProgressBarMax(view View, subviewID string) float64 {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view == nil {
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result, ok := floatStyledProperty(view, ProgressBarMax, 1)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		result, _ = floatStyledProperty(view, Max, 1)
 | 
			
		||||
	}
 | 
			
		||||
	return result
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetProgressBarMax(view View, subviewID ...string) float64 {
 | 
			
		||||
	return floatStyledProperty(view, subviewID, ProgressBarMax, 1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetProgressBarValue returns the value of ProgressBar subview.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetProgressBarValue(view View, subviewID string) float64 {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view == nil {
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result, ok := floatStyledProperty(view, ProgressBarValue, 0)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		result, _ = floatStyledProperty(view, Value, 0)
 | 
			
		||||
	}
 | 
			
		||||
	return result
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetProgressBarValue(view View, subviewID ...string) float64 {
 | 
			
		||||
	return floatStyledProperty(view, subviewID, ProgressBarValue, 0)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,13 +9,13 @@ import (
 | 
			
		|||
type Properties interface {
 | 
			
		||||
	// Get returns a value of the property with name defined by the argument.
 | 
			
		||||
	// The type of return value depends on the property. If the property is not set then nil is returned.
 | 
			
		||||
	Get(tag string) interface{}
 | 
			
		||||
	getRaw(tag string) interface{}
 | 
			
		||||
	Get(tag string) any
 | 
			
		||||
	getRaw(tag string) any
 | 
			
		||||
	// Set sets the value (second argument) of the property with name defined by the first argument.
 | 
			
		||||
	// Return "true" if the value has been set, in the opposite case "false" are returned and
 | 
			
		||||
	// a description of the error is written to the log
 | 
			
		||||
	Set(tag string, value interface{}) bool
 | 
			
		||||
	setRaw(tag string, value interface{})
 | 
			
		||||
	Set(tag string, value any) bool
 | 
			
		||||
	setRaw(tag string, value any)
 | 
			
		||||
	// Remove removes the property with name defined by the argument
 | 
			
		||||
	Remove(tag string)
 | 
			
		||||
	// Clear removes all properties
 | 
			
		||||
| 
						 | 
				
			
			@ -25,25 +25,25 @@ type Properties interface {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
type propertyList struct {
 | 
			
		||||
	properties map[string]interface{}
 | 
			
		||||
	properties map[string]any
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (properties *propertyList) init() {
 | 
			
		||||
	properties.properties = map[string]interface{}{}
 | 
			
		||||
	properties.properties = map[string]any{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (properties *propertyList) Get(tag string) interface{} {
 | 
			
		||||
func (properties *propertyList) Get(tag string) any {
 | 
			
		||||
	return properties.getRaw(strings.ToLower(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (properties *propertyList) getRaw(tag string) interface{} {
 | 
			
		||||
func (properties *propertyList) getRaw(tag string) any {
 | 
			
		||||
	if value, ok := properties.properties[tag]; ok {
 | 
			
		||||
		return value
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (properties *propertyList) setRaw(tag string, value interface{}) {
 | 
			
		||||
func (properties *propertyList) setRaw(tag string, value any) {
 | 
			
		||||
	properties.properties[tag] = value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -56,7 +56,7 @@ func (properties *propertyList) remove(tag string) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (properties *propertyList) Clear() {
 | 
			
		||||
	properties.properties = map[string]interface{}{}
 | 
			
		||||
	properties.properties = map[string]any{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (properties *propertyList) AllTags() []string {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,49 +32,49 @@ func TestProperties(t *testing.T) {
 | 
			
		|||
		t.Error(`list.Get("name") is not string`)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sizeValues := []interface{}{"@small", "auto", "10px", Pt(20), AutoSize()}
 | 
			
		||||
	sizeValues := []any{"@small", "auto", "10px", Pt(20), AutoSize()}
 | 
			
		||||
	for _, value := range sizeValues {
 | 
			
		||||
		if !list.setSizeProperty("size", value) {
 | 
			
		||||
			t.Errorf(`setSizeProperty("size", %v) fail`, value)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	failSizeValues := []interface{}{"@small,big", "abc", "10", Color(20), 100}
 | 
			
		||||
	failSizeValues := []any{"@small,big", "abc", "10", Color(20), 100}
 | 
			
		||||
	for _, value := range failSizeValues {
 | 
			
		||||
		if list.setSizeProperty("size", value) {
 | 
			
		||||
			t.Errorf(`setSizeProperty("size", %v) success`, value)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	angleValues := []interface{}{"@angle", "2pi", "π", "3deg", "60°", Rad(1.5), Deg(45), 1, 1.5}
 | 
			
		||||
	angleValues := []any{"@angle", "2pi", "π", "3deg", "60°", Rad(1.5), Deg(45), 1, 1.5}
 | 
			
		||||
	for _, value := range angleValues {
 | 
			
		||||
		if !list.setAngleProperty("angle", value) {
 | 
			
		||||
			t.Errorf(`setAngleProperty("angle", %v) fail`, value)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	failAngleValues := []interface{}{"@angle,2", "pi32", "deg", "60°x", Color(0xFFFFFFFF)}
 | 
			
		||||
	failAngleValues := []any{"@angle,2", "pi32", "deg", "60°x", Color(0xFFFFFFFF)}
 | 
			
		||||
	for _, value := range failAngleValues {
 | 
			
		||||
		if list.setAngleProperty("angle", value) {
 | 
			
		||||
			t.Errorf(`setAngleProperty("angle", %v) success`, value)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	colorValues := []interface{}{"@color", "#FF234567", "#234567", "rgba(30%, 128, 0.5, .25)", "rgb(30%, 128, 0.5)", Color(0xFFFFFFFF), 0xFFFFFFFF, White}
 | 
			
		||||
	colorValues := []any{"@color", "#FF234567", "#234567", "rgba(30%, 128, 0.5, .25)", "rgb(30%, 128, 0.5)", Color(0xFFFFFFFF), 0xFFFFFFFF, White}
 | 
			
		||||
	for _, color := range colorValues {
 | 
			
		||||
		if !list.setColorProperty("color", color) {
 | 
			
		||||
			t.Errorf(`list.setColorProperty("color", %v) fail`, color)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	failColorValues := []interface{}{"@color|2", "#FF234567FF", "#TT234567", "rgba(500%, 128, 10.5, .25)", 10.6}
 | 
			
		||||
	failColorValues := []any{"@color|2", "#FF234567FF", "#TT234567", "rgba(500%, 128, 10.5, .25)", 10.6}
 | 
			
		||||
	for _, color := range failColorValues {
 | 
			
		||||
		if list.setColorProperty("color", color) {
 | 
			
		||||
			t.Errorf(`list.setColorProperty("color", %v) success`, color)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	enumValues := []interface{}{"@enum", "inherit", "on", Inherit, 2}
 | 
			
		||||
	enumValues := []any{"@enum", "inherit", "on", Inherit, 2}
 | 
			
		||||
	inheritOffOn := inheritOffOnValues()
 | 
			
		||||
	for _, value := range enumValues {
 | 
			
		||||
		if !list.setEnumProperty("enum", value, inheritOffOn) {
 | 
			
		||||
| 
						 | 
				
			
			@ -82,56 +82,56 @@ func TestProperties(t *testing.T) {
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	failEnumValues := []interface{}{"@enum 13", "inherit2", "onn", -1, 10}
 | 
			
		||||
	failEnumValues := []any{"@enum 13", "inherit2", "onn", -1, 10}
 | 
			
		||||
	for _, value := range failEnumValues {
 | 
			
		||||
		if list.setEnumProperty("enum", value, inheritOffOn) {
 | 
			
		||||
			t.Errorf(`list.setEnumProperty("enum", %v, %v) success`, value, inheritOffOn)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	boolValues := []interface{}{"@bool", "true", "yes ", "on", " 1", "false", "no", "off", "0", 0, 1, false, true}
 | 
			
		||||
	boolValues := []any{"@bool", "true", "yes ", "on", " 1", "false", "no", "off", "0", 0, 1, false, true}
 | 
			
		||||
	for _, value := range boolValues {
 | 
			
		||||
		if !list.setBoolProperty("bool", value) {
 | 
			
		||||
			t.Errorf(`list.setBoolProperty("bool", %v) fail`, value)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	failBoolValues := []interface{}{"@bool,2", "tr", "ys", "10", -1, 10, 0.8}
 | 
			
		||||
	failBoolValues := []any{"@bool,2", "tr", "ys", "10", -1, 10, 0.8}
 | 
			
		||||
	for _, value := range failBoolValues {
 | 
			
		||||
		if list.setBoolProperty("bool", value) {
 | 
			
		||||
			t.Errorf(`list.setBoolProperty("bool", %v) success`, value)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	intValues := []interface{}{"@int", " 100", "-10 ", 0, 250}
 | 
			
		||||
	intValues := []any{"@int", " 100", "-10 ", 0, 250}
 | 
			
		||||
	for _, value := range intValues {
 | 
			
		||||
		if !list.setIntProperty("int", value) {
 | 
			
		||||
			t.Errorf(`list.setIntProperty("int", %v) fail`, value)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	failIntValues := []interface{}{"@int|10", "100i", "-1.0 ", 0.0}
 | 
			
		||||
	failIntValues := []any{"@int|10", "100i", "-1.0 ", 0.0}
 | 
			
		||||
	for _, value := range failIntValues {
 | 
			
		||||
		if list.setIntProperty("int", value) {
 | 
			
		||||
			t.Errorf(`list.setIntProperty("int", %v) success`, value)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	floatValues := []interface{}{"@float", " 100.25", "-1.5e12 ", uint(0), 250, float32(10.2), float64(0)}
 | 
			
		||||
	floatValues := []any{"@float", " 100.25", "-1.5e12 ", uint(0), 250, float32(10.2), float64(0)}
 | 
			
		||||
	for _, value := range floatValues {
 | 
			
		||||
		if !list.setFloatProperty("float", value) {
 | 
			
		||||
			t.Errorf(`list.setFloatProperty("float", %v) fail`, value)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	failFloatValues := []interface{}{"@float|2", " 100.25i", "-1.5ee12 ", "abc"}
 | 
			
		||||
	failFloatValues := []any{"@float|2", " 100.25i", "-1.5ee12 ", "abc"}
 | 
			
		||||
	for _, value := range failFloatValues {
 | 
			
		||||
		if list.setFloatProperty("float", value) {
 | 
			
		||||
			t.Errorf(`list.setFloatProperty("float", %v) success`, value)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	boundsValues := []interface{}{"@bounds", "10px,20pt,@bottom,0", Em(2), []interface{}{"@top", Px(10), AutoSize(), "14pt"}}
 | 
			
		||||
	boundsValues := []any{"@bounds", "10px,20pt,@bottom,0", Em(2), []any{"@top", Px(10), AutoSize(), "14pt"}}
 | 
			
		||||
	for _, value := range boundsValues {
 | 
			
		||||
		if !list.setBoundsProperty("margin", value) {
 | 
			
		||||
			t.Errorf(`list.setBoundsProperty("margin", %v) fail`, value)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
package rui
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -31,16 +32,29 @@ func imageProperty(properties Properties, tag string, session Session) (string,
 | 
			
		|||
	return "", false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func valueToSizeUnit(value interface{}, session Session) (SizeUnit, bool) {
 | 
			
		||||
func valueToSizeUnit(value any, session Session) (SizeUnit, bool) {
 | 
			
		||||
	if value != nil {
 | 
			
		||||
		switch value := value.(type) {
 | 
			
		||||
		case SizeUnit:
 | 
			
		||||
			return value, true
 | 
			
		||||
 | 
			
		||||
		case SizeFunc:
 | 
			
		||||
			return SizeUnit{Type: SizeFunction, Function: value}, true
 | 
			
		||||
 | 
			
		||||
		case string:
 | 
			
		||||
			if text, ok := session.resolveConstants(value); ok {
 | 
			
		||||
				return StringToSizeUnit(text)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		case float64:
 | 
			
		||||
			return Px(value), true
 | 
			
		||||
 | 
			
		||||
		case float32:
 | 
			
		||||
			return Px(float64(value)), true
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if n, ok := isInt(value); ok {
 | 
			
		||||
			return Px(float64(n)), true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -67,7 +81,7 @@ func angleProperty(properties Properties, tag string, session Session) (AngleUni
 | 
			
		|||
	return AngleUnit{Type: 0, Value: 0}, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func valueToColor(value interface{}, session Session) (Color, bool) {
 | 
			
		||||
func valueToColor(value any, session Session) (Color, bool) {
 | 
			
		||||
	if value != nil {
 | 
			
		||||
		switch value := value.(type) {
 | 
			
		||||
		case Color:
 | 
			
		||||
| 
						 | 
				
			
			@ -88,7 +102,7 @@ func colorProperty(properties Properties, tag string, session Session) (Color, b
 | 
			
		|||
	return valueToColor(properties.getRaw(tag), session)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func valueToEnum(value interface{}, tag string, session Session, defaultValue int) (int, bool) {
 | 
			
		||||
func valueToEnum(value any, tag string, session Session, defaultValue int) (int, bool) {
 | 
			
		||||
	if value != nil {
 | 
			
		||||
		values := enumProperties[tag].values
 | 
			
		||||
		switch value := value.(type) {
 | 
			
		||||
| 
						 | 
				
			
			@ -155,7 +169,7 @@ func enumProperty(properties Properties, tag string, session Session, defaultVal
 | 
			
		|||
	return valueToEnum(properties.getRaw(tag), tag, session, defaultValue)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func valueToBool(value interface{}, session Session) (bool, bool) {
 | 
			
		||||
func valueToBool(value any, session Session) (bool, bool) {
 | 
			
		||||
	if value != nil {
 | 
			
		||||
		switch value := value.(type) {
 | 
			
		||||
		case bool:
 | 
			
		||||
| 
						 | 
				
			
			@ -184,7 +198,7 @@ func boolProperty(properties Properties, tag string, session Session) (bool, boo
 | 
			
		|||
	return valueToBool(properties.getRaw(tag), session)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func valueToInt(value interface{}, session Session, defaultValue int) (int, bool) {
 | 
			
		||||
func valueToInt(value any, session Session, defaultValue int) (int, bool) {
 | 
			
		||||
	if value != nil {
 | 
			
		||||
		switch value := value.(type) {
 | 
			
		||||
		case string:
 | 
			
		||||
| 
						 | 
				
			
			@ -214,7 +228,7 @@ func intProperty(properties Properties, tag string, session Session, defaultValu
 | 
			
		|||
	return valueToInt(properties.getRaw(tag), session, defaultValue)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func valueToFloat(value interface{}, session Session, defaultValue float64) (float64, bool) {
 | 
			
		||||
func valueToFloat(value any, session Session, defaultValue float64) (float64, bool) {
 | 
			
		||||
	if value != nil {
 | 
			
		||||
		switch value := value.(type) {
 | 
			
		||||
		case float64:
 | 
			
		||||
| 
						 | 
				
			
			@ -238,7 +252,31 @@ func floatProperty(properties Properties, tag string, session Session, defaultVa
 | 
			
		|||
	return valueToFloat(properties.getRaw(tag), session, defaultValue)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func valueToRange(value interface{}, session Session) (Range, bool) {
 | 
			
		||||
func valueToFloatText(value any, session Session, defaultValue float64) (string, bool) {
 | 
			
		||||
	if value != nil {
 | 
			
		||||
		switch value := value.(type) {
 | 
			
		||||
		case float64:
 | 
			
		||||
			return fmt.Sprintf("%g", value), true
 | 
			
		||||
 | 
			
		||||
		case string:
 | 
			
		||||
			if text, ok := session.resolveConstants(value); ok {
 | 
			
		||||
				if _, err := strconv.ParseFloat(text, 64); err != nil {
 | 
			
		||||
					ErrorLog(err.Error())
 | 
			
		||||
					return fmt.Sprintf("%g", defaultValue), false
 | 
			
		||||
				}
 | 
			
		||||
				return text, true
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return fmt.Sprintf("%g", defaultValue), false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func floatTextProperty(properties Properties, tag string, session Session, defaultValue float64) (string, bool) {
 | 
			
		||||
	return valueToFloatText(properties.getRaw(tag), session, defaultValue)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func valueToRange(value any, session Session) (Range, bool) {
 | 
			
		||||
	if value != nil {
 | 
			
		||||
		switch value := value.(type) {
 | 
			
		||||
		case Range:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,6 +45,12 @@ const (
 | 
			
		|||
	// Opacity is the degree to which content behind an element is hidden, and is the opposite of transparency.
 | 
			
		||||
	Opacity = "opacity"
 | 
			
		||||
 | 
			
		||||
	// Overflow is the constant for the "overflow" property tag.
 | 
			
		||||
	// The "overflow" int property sets the desired behavior for an element's overflow — i.e.
 | 
			
		||||
	// when an element's content is too big to fit in its block formatting context — in both directions.
 | 
			
		||||
	// Valid values: OverflowHidden (0), OverflowVisible (1), OverflowScroll (2), OverflowAuto (3)
 | 
			
		||||
	Overflow = "overflow"
 | 
			
		||||
 | 
			
		||||
	// Row is the constant for the "row" property tag.
 | 
			
		||||
	Row = "row"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -141,6 +147,10 @@ const (
 | 
			
		|||
	// The "padding-bottom" SizeUnit property sets the height of the padding area to the bottom of a view.
 | 
			
		||||
	PaddingBottom = "padding-bottom"
 | 
			
		||||
 | 
			
		||||
	// AccentColor is the constant for the "accent-color" property tag.
 | 
			
		||||
	// The "accent-color" property sets sets the accent color for UI controls generated by some elements.
 | 
			
		||||
	AccentColor = "accent-color"
 | 
			
		||||
 | 
			
		||||
	// BackgroundColor is the constant for the "background-color" property tag.
 | 
			
		||||
	// The "background-color" property sets the background color of a view.
 | 
			
		||||
	BackgroundColor = "background-color"
 | 
			
		||||
| 
						 | 
				
			
			@ -314,20 +324,20 @@ const (
 | 
			
		|||
	// This is an inherited property, i.e. if it is not defined, then the value of the parent view is used.
 | 
			
		||||
	Underline = "underline"
 | 
			
		||||
 | 
			
		||||
	// TextLineThickness is the constant for the "text-decoration-thickness" property tag.
 | 
			
		||||
	// The "text-decoration-thickness" SizeUnit property sets the stroke thickness of the decoration line that
 | 
			
		||||
	// TextLineThickness is the constant for the "text-line-thickness" property tag.
 | 
			
		||||
	// The "text-line-thickness" SizeUnit property sets the stroke thickness of the decoration line that
 | 
			
		||||
	// is used on text in an element, such as a line-through, underline, or overline.
 | 
			
		||||
	// This is an inherited property, i.e. if it is not defined, then the value of the parent view is used.
 | 
			
		||||
	TextLineThickness = "text-line-thickness"
 | 
			
		||||
 | 
			
		||||
	// TextLineStyle is the constant for the "text-decoration-style" property tag.
 | 
			
		||||
	// The "text-decoration-style" int property sets the style of the lines specified by "text-decoration" property.
 | 
			
		||||
	// TextLineStyle is the constant for the "text-line-style" property tag.
 | 
			
		||||
	// The "text-line-style" int property sets the style of the lines specified by "text-decoration" property.
 | 
			
		||||
	// The style applies to all lines that are set with "text-decoration" property.
 | 
			
		||||
	// This is an inherited property, i.e. if it is not defined, then the value of the parent view is used.
 | 
			
		||||
	TextLineStyle = "text-line-style"
 | 
			
		||||
 | 
			
		||||
	// TextLineColor is the constant for the "text-decoration-color" property tag.
 | 
			
		||||
	// The "text-decoration-color" Color property sets the color of the lines specified by "text-decoration" property.
 | 
			
		||||
	// TextLineColor is the constant for the "text-line-color" property tag.
 | 
			
		||||
	// The "text-line-color" Color property sets the color of the lines specified by "text-decoration" property.
 | 
			
		||||
	// The color applies to all lines that are set with "text-decoration" property.
 | 
			
		||||
	// This is an inherited property, i.e. if it is not defined, then the value of the parent view is used.
 | 
			
		||||
	TextLineColor = "text-line-color"
 | 
			
		||||
| 
						 | 
				
			
			@ -349,6 +359,11 @@ const (
 | 
			
		|||
	// This is an inherited property, i.e. if it is not defined, then the value of the parent view is used.
 | 
			
		||||
	TextShadow = "text-shadow"
 | 
			
		||||
 | 
			
		||||
	// TabSize is the constant for the "tab-size" property tag.
 | 
			
		||||
	// The "tab-size" int property sets the width of tab characters (U+0009) in spaces.
 | 
			
		||||
	// This is an inherited property, i.e. if it is not defined, then the value of the parent view is used.
 | 
			
		||||
	TabSize = "tab-size"
 | 
			
		||||
 | 
			
		||||
	// LetterSpacing is the constant for the "letter-spacing" property tag.
 | 
			
		||||
	// The "letter-spacing" SizeUnit property sets the horizontal spacing behavior between text characters.
 | 
			
		||||
	// This value is added to the natural spacing between characters while rendering the text.
 | 
			
		||||
| 
						 | 
				
			
			@ -553,9 +568,19 @@ const (
 | 
			
		|||
	// Orientation is the constant for the "orientation" property tag.
 | 
			
		||||
	Orientation = "orientation"
 | 
			
		||||
 | 
			
		||||
	// Gap is the constant for the "gap" property tag.
 | 
			
		||||
	// Gap is t he constant for the "gap" property tag.
 | 
			
		||||
	Gap = "gap"
 | 
			
		||||
 | 
			
		||||
	// ListRowGap is the constant for the "list-row-gap" property tag.
 | 
			
		||||
	// The "list-row-gap" SizeUnit properties allow to set the distance between the rows of the ListLayout or ListView.
 | 
			
		||||
	// The default is 0px.
 | 
			
		||||
	ListRowGap = "list-row-gap"
 | 
			
		||||
 | 
			
		||||
	// ListColumnGap is the constant for the "list-column-gap" property tag.
 | 
			
		||||
	// The "list-column-gap" SizeUnit properties allow to set the distance between the columns of the GridLayout or ListView.
 | 
			
		||||
	// The default is 0px.
 | 
			
		||||
	ListColumnGap = "list-column-gap"
 | 
			
		||||
 | 
			
		||||
	// Text is the constant for the "text" property tag.
 | 
			
		||||
	Text = "text"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -67,6 +67,7 @@ var boolProperties = []string{
 | 
			
		|||
 | 
			
		||||
var intProperties = []string{
 | 
			
		||||
	ZIndex,
 | 
			
		||||
	TabSize,
 | 
			
		||||
	HeadHeight,
 | 
			
		||||
	FootHeight,
 | 
			
		||||
	RowSpan,
 | 
			
		||||
| 
						 | 
				
			
			@ -109,6 +110,8 @@ var sizeProperties = map[string]string{
 | 
			
		|||
	WordSpacing:        WordSpacing,
 | 
			
		||||
	LineHeight:         LineHeight,
 | 
			
		||||
	TextLineThickness:  "text-decoration-thickness",
 | 
			
		||||
	ListRowGap:         "row-gap",
 | 
			
		||||
	ListColumnGap:      "column-gap",
 | 
			
		||||
	GridRowGap:         GridRowGap,
 | 
			
		||||
	GridColumnGap:      GridColumnGap,
 | 
			
		||||
	ColumnWidth:        ColumnWidth,
 | 
			
		||||
| 
						 | 
				
			
			@ -179,6 +182,11 @@ var enumProperties = map[string]struct {
 | 
			
		|||
		"",
 | 
			
		||||
		[]string{"visible", "invisible", "gone"},
 | 
			
		||||
	},
 | 
			
		||||
	Overflow: {
 | 
			
		||||
		[]string{"hidden", "visible", "scroll", "auto"},
 | 
			
		||||
		Overflow,
 | 
			
		||||
		[]string{"hidden", "visible", "scroll", "auto"},
 | 
			
		||||
	},
 | 
			
		||||
	TextAlign: {
 | 
			
		||||
		[]string{"left", "right", "center", "justify"},
 | 
			
		||||
		TextAlign,
 | 
			
		||||
| 
						 | 
				
			
			@ -304,6 +312,11 @@ var enumProperties = map[string]struct {
 | 
			
		|||
		"",
 | 
			
		||||
		[]string{"left", "right", "center", "stretch"},
 | 
			
		||||
	},
 | 
			
		||||
	ArrowAlign: {
 | 
			
		||||
		[]string{"left", "right", "center"},
 | 
			
		||||
		"",
 | 
			
		||||
		[]string{"left", "right", "center"},
 | 
			
		||||
	},
 | 
			
		||||
	CellVerticalAlign: {
 | 
			
		||||
		[]string{"top", "bottom", "center", "stretch"},
 | 
			
		||||
		"align-items",
 | 
			
		||||
| 
						 | 
				
			
			@ -429,13 +442,18 @@ var enumProperties = map[string]struct {
 | 
			
		|||
		"resize",
 | 
			
		||||
		[]string{"none", "both", "horizontal", "vertical"},
 | 
			
		||||
	},
 | 
			
		||||
	Arrow: {
 | 
			
		||||
		[]string{"none", "top", "right", "bottom", "left"},
 | 
			
		||||
		"",
 | 
			
		||||
		[]string{"none", "top", "right", "bottom", "left"},
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func notCompatibleType(tag string, value interface{}) {
 | 
			
		||||
func notCompatibleType(tag string, value any) {
 | 
			
		||||
	ErrorLogF(`"%T" type not compatible with "%s" property`, value, tag)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func invalidPropertyValue(tag string, value interface{}) {
 | 
			
		||||
func invalidPropertyValue(tag string, value any) {
 | 
			
		||||
	ErrorLogF(`Invalid value "%v" of "%s" property`, value, tag)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -457,7 +475,7 @@ func isConstantName(text string) bool {
 | 
			
		|||
	return !strings.ContainsAny(text, ",;|\"'`+(){}[]<>/\\*&%! \t\n\r")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isInt(value interface{}) (int, bool) {
 | 
			
		||||
func isInt(value any) (int, bool) {
 | 
			
		||||
	var n int
 | 
			
		||||
	switch value := value.(type) {
 | 
			
		||||
	case int:
 | 
			
		||||
| 
						 | 
				
			
			@ -497,7 +515,7 @@ func isInt(value interface{}) (int, bool) {
 | 
			
		|||
	return n, true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (properties *propertyList) setSimpleProperty(tag string, value interface{}) bool {
 | 
			
		||||
func (properties *propertyList) setSimpleProperty(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		delete(properties.properties, tag)
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			@ -515,19 +533,26 @@ func (properties *propertyList) setSimpleProperty(tag string, value interface{})
 | 
			
		|||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (properties *propertyList) setSizeProperty(tag string, value interface{}) bool {
 | 
			
		||||
func (properties *propertyList) setSizeProperty(tag string, value any) bool {
 | 
			
		||||
	if !properties.setSimpleProperty(tag, value) {
 | 
			
		||||
		var size SizeUnit
 | 
			
		||||
		switch value := value.(type) {
 | 
			
		||||
		case string:
 | 
			
		||||
			var ok bool
 | 
			
		||||
			if size, ok = StringToSizeUnit(value); !ok {
 | 
			
		||||
			if fn := parseSizeFunc(value); fn != nil {
 | 
			
		||||
				size.Type = SizeFunction
 | 
			
		||||
				size.Function = fn
 | 
			
		||||
			} else if size, ok = StringToSizeUnit(value); !ok {
 | 
			
		||||
				invalidPropertyValue(tag, value)
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
		case SizeUnit:
 | 
			
		||||
			size = value
 | 
			
		||||
 | 
			
		||||
		case SizeFunc:
 | 
			
		||||
			size.Type = SizeFunction
 | 
			
		||||
			size.Function = value
 | 
			
		||||
 | 
			
		||||
		case float32:
 | 
			
		||||
			size.Type = SizeInPixel
 | 
			
		||||
			size.Value = float64(value)
 | 
			
		||||
| 
						 | 
				
			
			@ -556,7 +581,7 @@ func (properties *propertyList) setSizeProperty(tag string, value interface{}) b
 | 
			
		|||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (properties *propertyList) setAngleProperty(tag string, value interface{}) bool {
 | 
			
		||||
func (properties *propertyList) setAngleProperty(tag string, value any) bool {
 | 
			
		||||
	if !properties.setSimpleProperty(tag, value) {
 | 
			
		||||
		var angle AngleUnit
 | 
			
		||||
		switch value := value.(type) {
 | 
			
		||||
| 
						 | 
				
			
			@ -589,7 +614,7 @@ func (properties *propertyList) setAngleProperty(tag string, value interface{})
 | 
			
		|||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (properties *propertyList) setColorProperty(tag string, value interface{}) bool {
 | 
			
		||||
func (properties *propertyList) setColorProperty(tag string, value any) bool {
 | 
			
		||||
	if !properties.setSimpleProperty(tag, value) {
 | 
			
		||||
		var result Color
 | 
			
		||||
		switch value := value.(type) {
 | 
			
		||||
| 
						 | 
				
			
			@ -621,7 +646,7 @@ func (properties *propertyList) setColorProperty(tag string, value interface{})
 | 
			
		|||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (properties *propertyList) setEnumProperty(tag string, value interface{}, values []string) bool {
 | 
			
		||||
func (properties *propertyList) setEnumProperty(tag string, value any, values []string) bool {
 | 
			
		||||
	if !properties.setSimpleProperty(tag, value) {
 | 
			
		||||
		var n int
 | 
			
		||||
		if text, ok := value.(string); ok {
 | 
			
		||||
| 
						 | 
				
			
			@ -646,7 +671,7 @@ func (properties *propertyList) setEnumProperty(tag string, value interface{}, v
 | 
			
		|||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (properties *propertyList) setBoolProperty(tag string, value interface{}) bool {
 | 
			
		||||
func (properties *propertyList) setBoolProperty(tag string, value any) bool {
 | 
			
		||||
	if !properties.setSimpleProperty(tag, value) {
 | 
			
		||||
		if text, ok := value.(string); ok {
 | 
			
		||||
			switch strings.ToLower(strings.Trim(text, " \t")) {
 | 
			
		||||
| 
						 | 
				
			
			@ -683,7 +708,7 @@ func (properties *propertyList) setBoolProperty(tag string, value interface{}) b
 | 
			
		|||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (properties *propertyList) setIntProperty(tag string, value interface{}) bool {
 | 
			
		||||
func (properties *propertyList) setIntProperty(tag string, value any) bool {
 | 
			
		||||
	if !properties.setSimpleProperty(tag, value) {
 | 
			
		||||
		if text, ok := value.(string); ok {
 | 
			
		||||
			n, err := strconv.Atoi(strings.Trim(text, " \t"))
 | 
			
		||||
| 
						 | 
				
			
			@ -704,7 +729,7 @@ func (properties *propertyList) setIntProperty(tag string, value interface{}) bo
 | 
			
		|||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (properties *propertyList) setFloatProperty(tag string, value interface{}, min, max float64) bool {
 | 
			
		||||
func (properties *propertyList) setFloatProperty(tag string, value any, min, max float64) bool {
 | 
			
		||||
	if !properties.setSimpleProperty(tag, value) {
 | 
			
		||||
		f := float64(0)
 | 
			
		||||
		switch value := value.(type) {
 | 
			
		||||
| 
						 | 
				
			
			@ -715,6 +740,12 @@ func (properties *propertyList) setFloatProperty(tag string, value interface{},
 | 
			
		|||
				ErrorLog(err.Error())
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
			if f < min || f > max {
 | 
			
		||||
				ErrorLogF(`"%T" out of range of "%s" property`, value, tag)
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
			properties.properties[tag] = value
 | 
			
		||||
			return true
 | 
			
		||||
 | 
			
		||||
		case float32:
 | 
			
		||||
			f = float64(value)
 | 
			
		||||
| 
						 | 
				
			
			@ -742,11 +773,11 @@ func (properties *propertyList) setFloatProperty(tag string, value interface{},
 | 
			
		|||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (properties *propertyList) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (properties *propertyList) Set(tag string, value any) bool {
 | 
			
		||||
	return properties.set(strings.ToLower(tag), value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (properties *propertyList) set(tag string, value interface{}) bool {
 | 
			
		||||
func (properties *propertyList) set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		delete(properties.properties, tag)
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,29 +3,57 @@ package rui
 | 
			
		|||
const (
 | 
			
		||||
	// Visible - default value of the view Visibility property: View is visible
 | 
			
		||||
	Visible = 0
 | 
			
		||||
 | 
			
		||||
	// Invisible - value of the view Visibility property: View is invisible but takes place
 | 
			
		||||
	Invisible = 1
 | 
			
		||||
 | 
			
		||||
	// Gone - value of the view Visibility property: View is invisible and does not take place
 | 
			
		||||
	Gone = 2
 | 
			
		||||
 | 
			
		||||
	// OverflowHidden - value of the view "overflow" property:
 | 
			
		||||
	// Content is clipped if necessary to fit the padding box. No scrollbars are provided,
 | 
			
		||||
	// and no support for allowing the user to scroll (such as by dragging or using a scroll wheel) is allowed.
 | 
			
		||||
	// The content can be scrolled programmatically, so the element is still a scroll container.
 | 
			
		||||
	OverflowHidden = 0
 | 
			
		||||
 | 
			
		||||
	// OverflowVisible - value of the view "overflow" property:
 | 
			
		||||
	// Content is not clipped and may be rendered outside the padding box.
 | 
			
		||||
	OverflowVisible = 1
 | 
			
		||||
 | 
			
		||||
	// OverflowScroll - value of the view "overflow" property:
 | 
			
		||||
	// Content is clipped if necessary to fit the padding box. Browsers always display scrollbars whether or
 | 
			
		||||
	// not any content is actually clipped, preventing scrollbars from appearing or disappearing as content changes.
 | 
			
		||||
	OverflowScroll = 2
 | 
			
		||||
 | 
			
		||||
	// OverflowAuto - value of the view "overflow" property:
 | 
			
		||||
	// Depends on the browser user agent. If content fits inside the padding box, it looks the same as OverflowVisible,
 | 
			
		||||
	// but still establishes a new block formatting context. Desktop browsers provide scrollbars if content overflows.
 | 
			
		||||
	OverflowAuto = 3
 | 
			
		||||
 | 
			
		||||
	// NoneTextTransform - not transform text
 | 
			
		||||
	NoneTextTransform = 0
 | 
			
		||||
 | 
			
		||||
	// CapitalizeTextTransform - capitalize text
 | 
			
		||||
	CapitalizeTextTransform = 1
 | 
			
		||||
 | 
			
		||||
	// LowerCaseTextTransform - transform text to lower case
 | 
			
		||||
	LowerCaseTextTransform = 2
 | 
			
		||||
 | 
			
		||||
	// UpperCaseTextTransform - transform text to upper case
 | 
			
		||||
	UpperCaseTextTransform = 3
 | 
			
		||||
 | 
			
		||||
	// HorizontalTopToBottom - content flows horizontally from left to right, vertically from top to bottom.
 | 
			
		||||
	// The next horizontal line is positioned below the previous line.
 | 
			
		||||
	HorizontalTopToBottom = 0
 | 
			
		||||
 | 
			
		||||
	// HorizontalBottomToTop - content flows horizontally from left to right, vertically from bottom to top.
 | 
			
		||||
	// The next horizontal line is positioned above the previous line.
 | 
			
		||||
	HorizontalBottomToTop = 1
 | 
			
		||||
 | 
			
		||||
	// VerticalRightToLeft - content flows vertically from top to bottom, horizontally from right to left.
 | 
			
		||||
	// The next vertical line is positioned to the left of the previous line.
 | 
			
		||||
	VerticalRightToLeft = 2
 | 
			
		||||
 | 
			
		||||
	// VerticalLeftToRight - content flows vertically from top to bottom, horizontally from left to right.
 | 
			
		||||
	// The next vertical line is positioned to the right of the previous line.
 | 
			
		||||
	VerticalLeftToRight = 3
 | 
			
		||||
| 
						 | 
				
			
			@ -33,6 +61,7 @@ const (
 | 
			
		|||
	// MixedTextOrientation - rotates the characters of horizontal scripts 90° clockwise.
 | 
			
		||||
	// Lays out the characters of vertical scripts naturally. Default value.
 | 
			
		||||
	MixedTextOrientation = 0
 | 
			
		||||
 | 
			
		||||
	// UprightTextOrientation - lays out the characters of horizontal scripts naturally (upright),
 | 
			
		||||
	// as well as the glyphs for vertical scripts. Note that this keyword causes all characters
 | 
			
		||||
	// to be considered as left-to-right: the used value of "text-direction" is forced to be "left-to-right".
 | 
			
		||||
| 
						 | 
				
			
			@ -40,62 +69,84 @@ const (
 | 
			
		|||
 | 
			
		||||
	// SystemTextDirection - direction of a text and other elements defined by system. This is the default value.
 | 
			
		||||
	SystemTextDirection = 0
 | 
			
		||||
 | 
			
		||||
	// LeftToRightDirection - text and other elements go from left to right.
 | 
			
		||||
	LeftToRightDirection = 1
 | 
			
		||||
 | 
			
		||||
	//RightToLeftDirection - text and other elements go from right to left.
 | 
			
		||||
	RightToLeftDirection = 2
 | 
			
		||||
 | 
			
		||||
	// ThinFont - the value of "text-weight" property: the thin (hairline) text weight
 | 
			
		||||
	ThinFont = 1
 | 
			
		||||
 | 
			
		||||
	// ExtraLightFont - the value of "text-weight" property: the extra light (ultra light) text weight
 | 
			
		||||
	ExtraLightFont = 2
 | 
			
		||||
 | 
			
		||||
	// LightFont - the value of "text-weight" property: the light text weight
 | 
			
		||||
	LightFont = 3
 | 
			
		||||
 | 
			
		||||
	// NormalFont - the value of "text-weight" property (default value): the normal text weight
 | 
			
		||||
	NormalFont = 4
 | 
			
		||||
 | 
			
		||||
	// MediumFont - the value of "text-weight" property: the medium text weight
 | 
			
		||||
	MediumFont = 5
 | 
			
		||||
 | 
			
		||||
	// SemiBoldFont - the value of "text-weight" property: the semi bold (demi bold) text weight
 | 
			
		||||
	SemiBoldFont = 6
 | 
			
		||||
 | 
			
		||||
	// BoldFont - the value of "text-weight" property: the bold text weight
 | 
			
		||||
	BoldFont = 7
 | 
			
		||||
 | 
			
		||||
	// ExtraBoldFont - the value of "text-weight" property: the extra bold (ultra bold) text weight
 | 
			
		||||
	ExtraBoldFont = 8
 | 
			
		||||
 | 
			
		||||
	// BlackFont - the value of "text-weight" property: the black (heavy) text weight
 | 
			
		||||
	BlackFont = 9
 | 
			
		||||
 | 
			
		||||
	// TopAlign - top vertical-align for the "vertical-align" property
 | 
			
		||||
	TopAlign = 0
 | 
			
		||||
 | 
			
		||||
	// BottomAlign - bottom vertical-align for the "vertical-align" property
 | 
			
		||||
	BottomAlign = 1
 | 
			
		||||
 | 
			
		||||
	// LeftAlign - the left horizontal-align for the "horizontal-align" property
 | 
			
		||||
	LeftAlign = 0
 | 
			
		||||
 | 
			
		||||
	// RightAlign - the right horizontal-align for the "horizontal-align" property
 | 
			
		||||
	RightAlign = 1
 | 
			
		||||
 | 
			
		||||
	// CenterAlign - the center horizontal/vertical-align for the "horizontal-align"/"vertical-align" property
 | 
			
		||||
	CenterAlign = 2
 | 
			
		||||
 | 
			
		||||
	// StretchAlign - the stretch horizontal/vertical-align for the "horizontal-align"/"vertical-align" property
 | 
			
		||||
	StretchAlign = 3
 | 
			
		||||
 | 
			
		||||
	// JustifyAlign - the justify text align for "text-align" property
 | 
			
		||||
	JustifyAlign = 3
 | 
			
		||||
 | 
			
		||||
	// BaselineAlign - the baseline cell-vertical-align for the "cell-vertical-align" property
 | 
			
		||||
	BaselineAlign = 4
 | 
			
		||||
 | 
			
		||||
	// WhiteSpaceNormal - sequences of white space are collapsed. Newline characters in the source
 | 
			
		||||
	// are handled the same as other white space. Lines are broken as necessary to fill line boxes.
 | 
			
		||||
	WhiteSpaceNormal = 0
 | 
			
		||||
 | 
			
		||||
	// WhiteSpaceNowrap - collapses white space as for normal, but suppresses line breaks (text wrapping)
 | 
			
		||||
	// within the source.
 | 
			
		||||
	WhiteSpaceNowrap = 1
 | 
			
		||||
 | 
			
		||||
	// WhiteSpacePre - sequences of white space are preserved. Lines are only broken at newline
 | 
			
		||||
	// characters in the source and at <br> elements.
 | 
			
		||||
	WhiteSpacePre = 2
 | 
			
		||||
 | 
			
		||||
	// WhiteSpacePreWrap - Sequences of white space are preserved. Lines are broken at newline
 | 
			
		||||
	// characters, at <br>, and as necessary to fill line boxes.
 | 
			
		||||
	WhiteSpacePreWrap = 3
 | 
			
		||||
 | 
			
		||||
	// WhiteSpacePreLine - sequences of white space are collapsed. Lines are broken at newline characters,
 | 
			
		||||
	// at <br>, and as necessary to fill line boxes.
 | 
			
		||||
	WhiteSpacePreLine = 4
 | 
			
		||||
 | 
			
		||||
	// WhiteSpaceBreakSpaces - the behavior is identical to that of WhiteSpacePreWrap, except that:
 | 
			
		||||
	// * Any sequence of preserved white space always takes up space, including at the end of the line.
 | 
			
		||||
	// * A line breaking opportunity exists after every preserved white space character, including between white space characters.
 | 
			
		||||
| 
						 | 
				
			
			@ -104,12 +155,15 @@ const (
 | 
			
		|||
 | 
			
		||||
	// WordBreakNormal - use the default line break rule.
 | 
			
		||||
	WordBreakNormal = 0
 | 
			
		||||
 | 
			
		||||
	// WordBreakAll - to prevent overflow, word breaks should be inserted between any two characters
 | 
			
		||||
	// (excluding Chinese/Japanese/Korean text).
 | 
			
		||||
	WordBreakAll = 1
 | 
			
		||||
 | 
			
		||||
	// WordBreakKeepAll - word breaks should not be used for Chinese/Japanese/Korean (CJK) text.
 | 
			
		||||
	// Non-CJK text behavior is the same as for normal.
 | 
			
		||||
	WordBreakKeepAll = 2
 | 
			
		||||
 | 
			
		||||
	// WordBreakWord - when the block boundaries are exceeded, the remaining whole words can be split
 | 
			
		||||
	// in an arbitrary place, unless a more suitable place for the line break is found.
 | 
			
		||||
	WordBreakWord = 3
 | 
			
		||||
| 
						 | 
				
			
			@ -117,6 +171,7 @@ const (
 | 
			
		|||
	// TextOverflowClip - truncate the text at the limit of the content area, therefore the truncation
 | 
			
		||||
	// can happen in the middle of a character.
 | 
			
		||||
	TextOverflowClip = 0
 | 
			
		||||
 | 
			
		||||
	// TextOverflowEllipsis - display an ellipsis ('…', U+2026 HORIZONTAL ELLIPSIS) to represent clipped text.
 | 
			
		||||
	// The ellipsis is displayed inside the content area, decreasing the amount of text displayed.
 | 
			
		||||
	// If there is not enough space to display the ellipsis, it is clipped.
 | 
			
		||||
| 
						 | 
				
			
			@ -124,87 +179,111 @@ const (
 | 
			
		|||
 | 
			
		||||
	// DefaultSemantics - default value of the view Semantic property
 | 
			
		||||
	DefaultSemantics = 0
 | 
			
		||||
 | 
			
		||||
	// ArticleSemantics - value of the view Semantic property: view represents a self-contained
 | 
			
		||||
	// composition in a document, page, application, or site, which is intended to be
 | 
			
		||||
	// independently distributable or reusable (e.g., in syndication)
 | 
			
		||||
	ArticleSemantics = 1
 | 
			
		||||
 | 
			
		||||
	// SectionSemantics - value of the view Semantic property: view represents
 | 
			
		||||
	// a generic standalone section of a document, which doesn't have a more specific
 | 
			
		||||
	// semantic element to represent it.
 | 
			
		||||
	SectionSemantics = 2
 | 
			
		||||
 | 
			
		||||
	// AsideSemantics - value of the view Semantic property: view represents a portion
 | 
			
		||||
	// of a document whose content is only indirectly related to the document's main content.
 | 
			
		||||
	// Asides are frequently presented as sidebars or call-out boxes.
 | 
			
		||||
	AsideSemantics = 3
 | 
			
		||||
 | 
			
		||||
	// HeaderSemantics - value of the view Semantic property: view represents introductory
 | 
			
		||||
	// content, typically a group of introductory or navigational aids. It may contain
 | 
			
		||||
	// some heading elements but also a logo, a search form, an author name, and other elements.
 | 
			
		||||
	HeaderSemantics = 4
 | 
			
		||||
 | 
			
		||||
	// MainSemantics - value of the view Semantic property: view represents the dominant content
 | 
			
		||||
	// of the application. The main content area consists of content that is directly related
 | 
			
		||||
	// to or expands upon the central topic of a document, or the central functionality of an application.
 | 
			
		||||
	MainSemantics = 5
 | 
			
		||||
 | 
			
		||||
	// FooterSemantics - value of the view Semantic property: view represents a footer for its
 | 
			
		||||
	// nearest sectioning content or sectioning root element. A footer view typically contains
 | 
			
		||||
	// information about the author of the section, copyright data or links to related documents.
 | 
			
		||||
	FooterSemantics = 6
 | 
			
		||||
 | 
			
		||||
	// NavigationSemantics - value of the view Semantic property: view represents a section of
 | 
			
		||||
	// a page whose purpose is to provide navigation links, either within the current document
 | 
			
		||||
	// or to other documents. Common examples of navigation sections are menus, tables of contents,
 | 
			
		||||
	// and indexes.
 | 
			
		||||
	NavigationSemantics = 7
 | 
			
		||||
 | 
			
		||||
	// FigureSemantics - value of the view Semantic property: view represents self-contained content,
 | 
			
		||||
	// potentially with an optional caption, which is specified using the FigureCaptionSemantics view.
 | 
			
		||||
	FigureSemantics = 8
 | 
			
		||||
 | 
			
		||||
	// FigureCaptionSemantics - value of the view Semantic property: view represents a caption or
 | 
			
		||||
	// legend describing the rest of the contents of its parent FigureSemantics view.
 | 
			
		||||
	FigureCaptionSemantics = 9
 | 
			
		||||
 | 
			
		||||
	// ButtonSemantics - value of the view Semantic property: view a clickable button
 | 
			
		||||
	ButtonSemantics = 10
 | 
			
		||||
 | 
			
		||||
	// ParagraphSemantics - value of the view Semantic property: view represents a paragraph.
 | 
			
		||||
	// Paragraphs are usually represented in visual media as blocks of text separated
 | 
			
		||||
	// from adjacent blocks by blank lines and/or first-line indentation
 | 
			
		||||
	ParagraphSemantics = 11
 | 
			
		||||
 | 
			
		||||
	// H1Semantics - value of the view Semantic property: view represent of first level section headings.
 | 
			
		||||
	// H1Semantics is the highest section level and H6Semantics is the lowest.
 | 
			
		||||
	H1Semantics = 12
 | 
			
		||||
 | 
			
		||||
	// H2Semantics - value of the view Semantic property: view represent of second level section headings.
 | 
			
		||||
	// H1Semantics is the highest section level and H6Semantics is the lowest.
 | 
			
		||||
	H2Semantics = 13
 | 
			
		||||
 | 
			
		||||
	// H3Semantics - value of the view Semantic property: view represent of third level section headings.
 | 
			
		||||
	// H1Semantics is the highest section level and H6Semantics is the lowest.
 | 
			
		||||
	H3Semantics = 14
 | 
			
		||||
 | 
			
		||||
	// H4Semantics - value of the view Semantic property: view represent of fourth level section headings.
 | 
			
		||||
	// H1Semantics is the highest section level and H6Semantics is the lowest.
 | 
			
		||||
	H4Semantics = 15
 | 
			
		||||
 | 
			
		||||
	// H5Semantics - value of the view Semantic property: view represent of fifth level section headings.
 | 
			
		||||
	// H1Semantics is the highest section level and H6Semantics is the lowest.
 | 
			
		||||
	H5Semantics = 16
 | 
			
		||||
 | 
			
		||||
	// H6Semantics - value of the view Semantic property: view represent of sixth level section headings.
 | 
			
		||||
	// H1Semantics is the highest section level and H6Semantics is the lowest.
 | 
			
		||||
	H6Semantics = 17
 | 
			
		||||
 | 
			
		||||
	// BlockquoteSemantics - value of the view Semantic property: view indicates that
 | 
			
		||||
	// the enclosed text is an extended quotation.
 | 
			
		||||
	BlockquoteSemantics = 18
 | 
			
		||||
 | 
			
		||||
	// CodeSemantics - value of the view Semantic property: view displays its contents styled
 | 
			
		||||
	// in a fashion intended to indicate that the text is a short fragment of computer code
 | 
			
		||||
	CodeSemantics = 19
 | 
			
		||||
 | 
			
		||||
	// NoneFloat - value of the view "float" property: the View must not float.
 | 
			
		||||
	NoneFloat = 0
 | 
			
		||||
 | 
			
		||||
	// LeftFloat - value of the view "float" property: the View must float on the left side of its containing block.
 | 
			
		||||
	LeftFloat = 1
 | 
			
		||||
 | 
			
		||||
	// RightFloat - value of the view "float" property: the View must float on the right side of its containing block.
 | 
			
		||||
	RightFloat = 2
 | 
			
		||||
 | 
			
		||||
	// NoneResize - value of the view "resize" property: the View The offers no user-controllable method for resizing it.
 | 
			
		||||
	NoneResize = 0
 | 
			
		||||
 | 
			
		||||
	// BothResize - value of the view "resize" property: the View displays a mechanism for allowing
 | 
			
		||||
	// the user to resize it, which may be resized both horizontally and vertically.
 | 
			
		||||
	BothResize = 1
 | 
			
		||||
 | 
			
		||||
	// HorizontalResize - value of the view "resize" property: the View displays a mechanism for allowing
 | 
			
		||||
	// the user to resize it in the horizontal direction.
 | 
			
		||||
	HorizontalResize = 2
 | 
			
		||||
 | 
			
		||||
	// VerticalResize - value of the view "resize" property: the View displays a mechanism for allowing
 | 
			
		||||
	// the user to resize it in the vertical direction.
 | 
			
		||||
	VerticalResize = 3
 | 
			
		||||
| 
						 | 
				
			
			@ -212,14 +291,17 @@ const (
 | 
			
		|||
	// RowAutoFlow - value of the "grid-auto-flow" property of the GridLayout:
 | 
			
		||||
	// Views are placed by filling each row in turn, adding new rows as necessary.
 | 
			
		||||
	RowAutoFlow = 0
 | 
			
		||||
 | 
			
		||||
	// ColumnAutoFlow - value of the "grid-auto-flow" property of the GridLayout:
 | 
			
		||||
	// Views are placed by filling each column in turn, adding new columns as necessary.
 | 
			
		||||
	ColumnAutoFlow = 1
 | 
			
		||||
 | 
			
		||||
	// RowDenseAutoFlow - value of the "grid-auto-flow" property of the GridLayout:
 | 
			
		||||
	// Views are placed by filling each row, adding new rows as necessary.
 | 
			
		||||
	// "dense" packing algorithm attempts to fill in holes earlier in the grid, if smaller items come up later.
 | 
			
		||||
	// This may cause views to appear out-of-order, when doing so would fill in holes left by larger views.
 | 
			
		||||
	RowDenseAutoFlow = 2
 | 
			
		||||
 | 
			
		||||
	// ColumnDenseAutoFlow - value of the "grid-auto-flow" property of the GridLayout:
 | 
			
		||||
	// Views are placed by filling each column, adding new columns as necessary.
 | 
			
		||||
	// "dense" packing algorithm attempts to fill in holes earlier in the grid, if smaller items come up later.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										47
									
								
								radius.go
								
								
								
								
							
							
						
						
									
										47
									
								
								radius.go
								
								
								
								
							| 
						 | 
				
			
			@ -109,7 +109,7 @@ type radiusPropertyData struct {
 | 
			
		|||
// NewRadiusProperty creates the new RadiusProperty
 | 
			
		||||
func NewRadiusProperty(params Params) RadiusProperty {
 | 
			
		||||
	result := new(radiusPropertyData)
 | 
			
		||||
	result.properties = map[string]interface{}{}
 | 
			
		||||
	result.properties = map[string]any{}
 | 
			
		||||
	if params != nil {
 | 
			
		||||
		for _, tag := range []string{X, Y, TopLeft, TopRight, BottomLeft, BottomRight, TopLeftX, TopLeftY,
 | 
			
		||||
			TopRightX, TopRightY, BottomLeftX, BottomLeftY, BottomRightX, BottomRightY} {
 | 
			
		||||
| 
						 | 
				
			
			@ -172,7 +172,7 @@ func (radius *radiusPropertyData) deleteUnusedTags() {
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	equalValue := func(value1, value2 interface{}) bool {
 | 
			
		||||
	equalValue := func(value1, value2 any) bool {
 | 
			
		||||
		switch value1 := value1.(type) {
 | 
			
		||||
		case string:
 | 
			
		||||
			switch value2 := value2.(type) {
 | 
			
		||||
| 
						 | 
				
			
			@ -243,7 +243,7 @@ func (radius *radiusPropertyData) Remove(tag string) {
 | 
			
		|||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (radius *radiusPropertyData) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (radius *radiusPropertyData) Set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		radius.Remove(tag)
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			@ -318,7 +318,7 @@ func (radius *radiusPropertyData) Set(tag string, value interface{}) bool {
 | 
			
		|||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (radius *radiusPropertyData) Get(tag string) interface{} {
 | 
			
		||||
func (radius *radiusPropertyData) Get(tag string) any {
 | 
			
		||||
	tag = radius.normalizeTag(tag)
 | 
			
		||||
	if value, ok := radius.properties[tag]; ok {
 | 
			
		||||
		return value
 | 
			
		||||
| 
						 | 
				
			
			@ -455,7 +455,7 @@ func (radius BoxRadius) String() string {
 | 
			
		|||
	return buffer.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (radius BoxRadius) cssValue(builder cssBuilder) {
 | 
			
		||||
func (radius BoxRadius) cssValue(builder cssBuilder, session Session) {
 | 
			
		||||
 | 
			
		||||
	if (radius.TopLeftX.Type == Auto || radius.TopLeftX.Value == 0) &&
 | 
			
		||||
		(radius.TopLeftY.Type == Auto || radius.TopLeftY.Value == 0) &&
 | 
			
		||||
| 
						 | 
				
			
			@ -471,23 +471,23 @@ func (radius BoxRadius) cssValue(builder cssBuilder) {
 | 
			
		|||
	buffer := allocStringBuilder()
 | 
			
		||||
	defer freeStringBuilder(buffer)
 | 
			
		||||
 | 
			
		||||
	buffer.WriteString(radius.TopLeftX.cssString("0"))
 | 
			
		||||
	buffer.WriteString(radius.TopLeftX.cssString("0", session))
 | 
			
		||||
 | 
			
		||||
	if radius.AllAnglesIsEqual() {
 | 
			
		||||
 | 
			
		||||
		if !radius.TopLeftX.Equal(radius.TopLeftY) {
 | 
			
		||||
			buffer.WriteString(" / ")
 | 
			
		||||
			buffer.WriteString(radius.TopLeftY.cssString("0"))
 | 
			
		||||
			buffer.WriteString(radius.TopLeftY.cssString("0", session))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	} else {
 | 
			
		||||
 | 
			
		||||
		buffer.WriteRune(' ')
 | 
			
		||||
		buffer.WriteString(radius.TopRightX.cssString("0"))
 | 
			
		||||
		buffer.WriteString(radius.TopRightX.cssString("0", session))
 | 
			
		||||
		buffer.WriteRune(' ')
 | 
			
		||||
		buffer.WriteString(radius.BottomRightX.cssString("0"))
 | 
			
		||||
		buffer.WriteString(radius.BottomRightX.cssString("0", session))
 | 
			
		||||
		buffer.WriteRune(' ')
 | 
			
		||||
		buffer.WriteString(radius.BottomLeftX.cssString("0"))
 | 
			
		||||
		buffer.WriteString(radius.BottomLeftX.cssString("0", session))
 | 
			
		||||
 | 
			
		||||
		if !radius.TopLeftX.Equal(radius.TopLeftY) ||
 | 
			
		||||
			!radius.TopRightX.Equal(radius.TopRightY) ||
 | 
			
		||||
| 
						 | 
				
			
			@ -495,22 +495,22 @@ func (radius BoxRadius) cssValue(builder cssBuilder) {
 | 
			
		|||
			!radius.BottomRightX.Equal(radius.BottomRightY) {
 | 
			
		||||
 | 
			
		||||
			buffer.WriteString(" / ")
 | 
			
		||||
			buffer.WriteString(radius.TopLeftY.cssString("0"))
 | 
			
		||||
			buffer.WriteString(radius.TopLeftY.cssString("0", session))
 | 
			
		||||
			buffer.WriteRune(' ')
 | 
			
		||||
			buffer.WriteString(radius.TopRightY.cssString("0"))
 | 
			
		||||
			buffer.WriteString(radius.TopRightY.cssString("0", session))
 | 
			
		||||
			buffer.WriteRune(' ')
 | 
			
		||||
			buffer.WriteString(radius.BottomRightY.cssString("0"))
 | 
			
		||||
			buffer.WriteString(radius.BottomRightY.cssString("0", session))
 | 
			
		||||
			buffer.WriteRune(' ')
 | 
			
		||||
			buffer.WriteString(radius.BottomLeftY.cssString("0"))
 | 
			
		||||
			buffer.WriteString(radius.BottomLeftY.cssString("0", session))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	builder.add("border-radius", buffer.String())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (radius BoxRadius) cssString() string {
 | 
			
		||||
func (radius BoxRadius) cssString(session Session) string {
 | 
			
		||||
	var builder cssValueBuilder
 | 
			
		||||
	radius.cssValue(&builder)
 | 
			
		||||
	radius.cssValue(&builder, session)
 | 
			
		||||
	return builder.finish()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -570,7 +570,7 @@ func getRadiusProperty(style Properties) RadiusProperty {
 | 
			
		|||
	return NewRadiusProperty(nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (properties *propertyList) setRadius(value interface{}) bool {
 | 
			
		||||
func (properties *propertyList) setRadius(value any) bool {
 | 
			
		||||
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		delete(properties.properties, Radius)
 | 
			
		||||
| 
						 | 
				
			
			@ -645,7 +645,16 @@ func (properties *propertyList) setRadius(value interface{}) bool {
 | 
			
		|||
		properties.properties[Radius] = radius
 | 
			
		||||
		return true
 | 
			
		||||
 | 
			
		||||
	case float32:
 | 
			
		||||
		return properties.setRadius(Px(float64(value)))
 | 
			
		||||
 | 
			
		||||
	case float64:
 | 
			
		||||
		return properties.setRadius(Px(value))
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		if n, ok := isInt(value); ok {
 | 
			
		||||
			return properties.setRadius(Px(float64(n)))
 | 
			
		||||
		}
 | 
			
		||||
		notCompatibleType(Radius, value)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -664,7 +673,7 @@ func (properties *propertyList) removeRadiusElement(tag string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (properties *propertyList) setRadiusElement(tag string, value interface{}) bool {
 | 
			
		||||
func (properties *propertyList) setRadiusElement(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		properties.removeRadiusElement(tag)
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			@ -679,7 +688,7 @@ func (properties *propertyList) setRadiusElement(tag string, value interface{})
 | 
			
		|||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getRadiusElement(style Properties, tag string) interface{} {
 | 
			
		||||
func getRadiusElement(style Properties, tag string) any {
 | 
			
		||||
	value := style.Get(Radius)
 | 
			
		||||
	if value != nil {
 | 
			
		||||
		switch value := value.(type) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										18
									
								
								resizable.go
								
								
								
								
							
							
						
						
									
										18
									
								
								resizable.go
								
								
								
								
							| 
						 | 
				
			
			@ -45,7 +45,7 @@ type resizableData struct {
 | 
			
		|||
// NewResizable create new Resizable object and return it
 | 
			
		||||
func NewResizable(session Session, params Params) Resizable {
 | 
			
		||||
	view := new(resizableData)
 | 
			
		||||
	view.Init(session)
 | 
			
		||||
	view.init(session)
 | 
			
		||||
	setInitParams(view, params)
 | 
			
		||||
	return view
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -54,8 +54,8 @@ func newResizable(session Session) View {
 | 
			
		|||
	return NewResizable(session, nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (resizable *resizableData) Init(session Session) {
 | 
			
		||||
	resizable.viewData.Init(session)
 | 
			
		||||
func (resizable *resizableData) init(session Session) {
 | 
			
		||||
	resizable.viewData.init(session)
 | 
			
		||||
	resizable.tag = "Resizable"
 | 
			
		||||
	resizable.systemClass = "ruiGridLayout"
 | 
			
		||||
	resizable.content = []View{}
 | 
			
		||||
| 
						 | 
				
			
			@ -108,11 +108,11 @@ func (resizable *resizableData) remove(tag string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (resizable *resizableData) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (resizable *resizableData) Set(tag string, value any) bool {
 | 
			
		||||
	return resizable.set(strings.ToLower(tag), value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (resizable *resizableData) set(tag string, value interface{}) bool {
 | 
			
		||||
func (resizable *resizableData) set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		resizable.remove(tag)
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			@ -183,7 +183,7 @@ func (resizable *resizableData) set(tag string, value interface{}) bool {
 | 
			
		|||
	return resizable.viewData.set(tag, value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (resizable *resizableData) Get(tag string) interface{} {
 | 
			
		||||
func (resizable *resizableData) Get(tag string) any {
 | 
			
		||||
	return resizable.get(strings.ToLower(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -235,7 +235,7 @@ func (resizable *resizableData) getSide() int {
 | 
			
		|||
	return AllSides
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (resizable *resizableData) setSide(value interface{}) bool {
 | 
			
		||||
func (resizable *resizableData) setSide(value any) bool {
 | 
			
		||||
	switch value := value.(type) {
 | 
			
		||||
	case string:
 | 
			
		||||
		if n, err := strconv.Atoi(value); err == nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -340,7 +340,7 @@ func (resizable *resizableData) updateResizeBorderWidth() {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (resizable *resizableData) cellSizeCSS() (string, string) {
 | 
			
		||||
	w := resizable.resizeBorderWidth().cssString("4px")
 | 
			
		||||
	w := resizable.resizeBorderWidth().cssString("4px", resizable.Session())
 | 
			
		||||
	side := resizable.getSide()
 | 
			
		||||
	column := "1fr"
 | 
			
		||||
	row := "1fr"
 | 
			
		||||
| 
						 | 
				
			
			@ -384,7 +384,7 @@ func (resizable *resizableData) htmlSubviews(self View, buffer *strings.Builder)
 | 
			
		|||
	top := 1
 | 
			
		||||
	leftSide := (side & LeftSide) != 0
 | 
			
		||||
	rightSide := (side & RightSide) != 0
 | 
			
		||||
	w := resizable.resizeBorderWidth().cssString("4px")
 | 
			
		||||
	w := resizable.resizeBorderWidth().cssString("4px", resizable.Session())
 | 
			
		||||
 | 
			
		||||
	if leftSide {
 | 
			
		||||
		left = 2
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										179
									
								
								resizeEvent.go
								
								
								
								
							
							
						
						
									
										179
									
								
								resizeEvent.go
								
								
								
								
							| 
						 | 
				
			
			@ -3,9 +3,12 @@ package rui
 | 
			
		|||
// ResizeEvent is the constant for "resize-event" property tag.
 | 
			
		||||
// The "resize-event" is fired when the view changes its size.
 | 
			
		||||
// The main listener format:
 | 
			
		||||
//   func(View, Frame).
 | 
			
		||||
//
 | 
			
		||||
//	func(View, Frame).
 | 
			
		||||
//
 | 
			
		||||
// The additional listener formats:
 | 
			
		||||
//   func(Frame), func(View), and func().
 | 
			
		||||
//
 | 
			
		||||
//	func(Frame), func(View), and func().
 | 
			
		||||
const ResizeEvent = "resize-event"
 | 
			
		||||
 | 
			
		||||
func (view *viewData) onResize(self View, x, y, width, height float64) {
 | 
			
		||||
| 
						 | 
				
			
			@ -13,7 +16,7 @@ func (view *viewData) onResize(self View, x, y, width, height float64) {
 | 
			
		|||
	view.frame.Top = y
 | 
			
		||||
	view.frame.Width = width
 | 
			
		||||
	view.frame.Height = height
 | 
			
		||||
	for _, listener := range GetResizeListeners(view, "") {
 | 
			
		||||
	for _, listener := range GetResizeListeners(view) {
 | 
			
		||||
		listener(self, view.frame)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -21,147 +24,19 @@ func (view *viewData) onResize(self View, x, y, width, height float64) {
 | 
			
		|||
func (view *viewData) onItemResize(self View, index string, x, y, width, height float64) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (view *viewData) setFrameListener(tag string, value interface{}) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		delete(view.properties, tag)
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch value := value.(type) {
 | 
			
		||||
	case func(View, Frame):
 | 
			
		||||
		view.properties[tag] = []func(View, Frame){value}
 | 
			
		||||
 | 
			
		||||
	case []func(View, Frame):
 | 
			
		||||
		if len(value) > 0 {
 | 
			
		||||
			view.properties[tag] = value
 | 
			
		||||
		} else {
 | 
			
		||||
			delete(view.properties, tag)
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case func(Frame):
 | 
			
		||||
		fn := func(_ View, frame Frame) {
 | 
			
		||||
			value(frame)
 | 
			
		||||
		}
 | 
			
		||||
		view.properties[tag] = []func(View, Frame){fn}
 | 
			
		||||
 | 
			
		||||
	case []func(Frame):
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			delete(view.properties, tag)
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		listeners := make([]func(View, Frame), count)
 | 
			
		||||
		for i, val := range value {
 | 
			
		||||
			if val == nil {
 | 
			
		||||
				notCompatibleType(tag, val)
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			listeners[i] = func(_ View, frame Frame) {
 | 
			
		||||
				val(frame)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		view.properties[tag] = listeners
 | 
			
		||||
 | 
			
		||||
	case func(View):
 | 
			
		||||
		fn := func(view View, _ Frame) {
 | 
			
		||||
			value(view)
 | 
			
		||||
		}
 | 
			
		||||
		view.properties[tag] = []func(View, Frame){fn}
 | 
			
		||||
 | 
			
		||||
	case []func(View):
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			delete(view.properties, tag)
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		listeners := make([]func(View, Frame), count)
 | 
			
		||||
		for i, val := range value {
 | 
			
		||||
			if val == nil {
 | 
			
		||||
				notCompatibleType(tag, val)
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			listeners[i] = func(view View, _ Frame) {
 | 
			
		||||
				val(view)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		view.properties[tag] = listeners
 | 
			
		||||
 | 
			
		||||
	case func():
 | 
			
		||||
		fn := func(View, Frame) {
 | 
			
		||||
			value()
 | 
			
		||||
		}
 | 
			
		||||
		view.properties[tag] = []func(View, Frame){fn}
 | 
			
		||||
 | 
			
		||||
	case []func():
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			delete(view.properties, tag)
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		listeners := make([]func(View, Frame), count)
 | 
			
		||||
		for i, val := range value {
 | 
			
		||||
			if val == nil {
 | 
			
		||||
				notCompatibleType(tag, val)
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			listeners[i] = func(View, Frame) {
 | 
			
		||||
				val()
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		view.properties[tag] = listeners
 | 
			
		||||
 | 
			
		||||
	case []interface{}:
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			delete(view.properties, tag)
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		listeners := make([]func(View, Frame), count)
 | 
			
		||||
		for i, val := range value {
 | 
			
		||||
			if val == nil {
 | 
			
		||||
				notCompatibleType(tag, val)
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			switch val := val.(type) {
 | 
			
		||||
			case func(View, Frame):
 | 
			
		||||
				listeners[i] = val
 | 
			
		||||
 | 
			
		||||
			case func(Frame):
 | 
			
		||||
				listeners[i] = func(_ View, frame Frame) {
 | 
			
		||||
					val(frame)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			case func(View):
 | 
			
		||||
				listeners[i] = func(view View, _ Frame) {
 | 
			
		||||
					val(view)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			case func():
 | 
			
		||||
				listeners[i] = func(View, Frame) {
 | 
			
		||||
					val()
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			default:
 | 
			
		||||
				notCompatibleType(tag, val)
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		view.properties[tag] = listeners
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
func (view *viewData) setFrameListener(tag string, value any) bool {
 | 
			
		||||
	listeners, ok := valueToEventListeners[View, Frame](value)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		notCompatibleType(tag, value)
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if listeners == nil {
 | 
			
		||||
		delete(view.properties, tag)
 | 
			
		||||
	} else {
 | 
			
		||||
		view.properties[tag] = listeners
 | 
			
		||||
	}
 | 
			
		||||
	view.propertyChangedEvent(tag)
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -190,10 +65,10 @@ func (view *viewData) Frame() Frame {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// GetViewFrame returns the size and location of view's viewport.
 | 
			
		||||
// If the second argument (subviewID) is "" then the value of the first argument (view) is returned
 | 
			
		||||
func GetViewFrame(view View, subviewID string) Frame {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then the value of the first argument (view) is returned
 | 
			
		||||
func GetViewFrame(view View, subviewID ...string) Frame {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
	if view == nil {
 | 
			
		||||
		return Frame{}
 | 
			
		||||
| 
						 | 
				
			
			@ -202,17 +77,7 @@ func GetViewFrame(view View, subviewID string) Frame {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// GetResizeListeners returns the list of "resize-event" listeners. If there are no listeners then the empty list is returned
 | 
			
		||||
// If the second argument (subviewID) is "" then the listeners list of the first argument (view) is returned
 | 
			
		||||
func GetResizeListeners(view View, subviewID string) []func(View, Frame) {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if value := view.Get(ResizeEvent); value != nil {
 | 
			
		||||
			if result, ok := value.([]func(View, Frame)); ok {
 | 
			
		||||
				return result
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return []func(View, Frame){}
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then the listeners list of the first argument (view) is returned
 | 
			
		||||
func GetResizeListeners(view View, subviewID ...string) []func(View, Frame) {
 | 
			
		||||
	return getEventListeners[View, Frame](view, subviewID, ResizeEvent)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										11
									
								
								resources.go
								
								
								
								
							
							
						
						
									
										11
									
								
								resources.go
								
								
								
								
							| 
						 | 
				
			
			@ -3,7 +3,6 @@ package rui
 | 
			
		|||
import (
 | 
			
		||||
	"embed"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
| 
						 | 
				
			
			@ -171,7 +170,7 @@ func registerImage(fs *embed.FS, path, filename string) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func scanImagesDirectory(path, filePrefix string) {
 | 
			
		||||
	if files, err := ioutil.ReadDir(path); err == nil {
 | 
			
		||||
	if files, err := os.ReadDir(path); err == nil {
 | 
			
		||||
		for _, file := range files {
 | 
			
		||||
			filename := file.Name()
 | 
			
		||||
			if filename[0] != '.' {
 | 
			
		||||
| 
						 | 
				
			
			@ -189,7 +188,7 @@ func scanImagesDirectory(path, filePrefix string) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func scanThemesDir(path string) {
 | 
			
		||||
	if files, err := ioutil.ReadDir(path); err == nil {
 | 
			
		||||
	if files, err := os.ReadDir(path); err == nil {
 | 
			
		||||
		for _, file := range files {
 | 
			
		||||
			filename := file.Name()
 | 
			
		||||
			if filename[0] != '.' {
 | 
			
		||||
| 
						 | 
				
			
			@ -197,7 +196,7 @@ func scanThemesDir(path string) {
 | 
			
		|||
				if file.IsDir() {
 | 
			
		||||
					scanThemesDir(newPath)
 | 
			
		||||
				} else if strings.ToLower(filepath.Ext(newPath)) == ".rui" {
 | 
			
		||||
					if data, err := ioutil.ReadFile(newPath); err == nil {
 | 
			
		||||
					if data, err := os.ReadFile(newPath); err == nil {
 | 
			
		||||
						registerThemeText(string(data))
 | 
			
		||||
					} else {
 | 
			
		||||
						ErrorLog(err.Error())
 | 
			
		||||
| 
						 | 
				
			
			@ -380,7 +379,7 @@ func AllRawResources() []string {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	if resources.path != "" {
 | 
			
		||||
		if files, err := ioutil.ReadDir(resources.path + rawDir); err == nil {
 | 
			
		||||
		if files, err := os.ReadDir(resources.path + rawDir); err == nil {
 | 
			
		||||
			for _, file := range files {
 | 
			
		||||
				result = append(result, file.Name())
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -388,7 +387,7 @@ func AllRawResources() []string {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	if exe, err := os.Executable(); err == nil {
 | 
			
		||||
		if files, err := ioutil.ReadDir(filepath.Dir(exe) + "/resources/" + rawDir); err == nil {
 | 
			
		||||
		if files, err := os.ReadDir(filepath.Dir(exe) + "/resources/" + rawDir); err == nil {
 | 
			
		||||
			for _, file := range files {
 | 
			
		||||
				result = append(result, file.Name())
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,7 +13,7 @@ type ruiWriter interface {
 | 
			
		|||
	endObject()
 | 
			
		||||
	startArrayProperty(tag string)
 | 
			
		||||
	endObArray()
 | 
			
		||||
	writeProperty(tag string, value interface{})
 | 
			
		||||
	writeProperty(tag string, value any)
 | 
			
		||||
	finish() string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -112,7 +112,7 @@ func (writer *ruiWriterData) endObArray() {
 | 
			
		|||
	writer.buffer.WriteString("],\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (writer *ruiWriterData) writeValue(value interface{}) {
 | 
			
		||||
func (writer *ruiWriterData) writeValue(value any) {
 | 
			
		||||
 | 
			
		||||
	switch value := value.(type) {
 | 
			
		||||
	case string:
 | 
			
		||||
| 
						 | 
				
			
			@ -175,7 +175,7 @@ func (writer *ruiWriterData) writeValue(value interface{}) {
 | 
			
		|||
			writer.buffer.WriteRune(']')
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case []interface{}:
 | 
			
		||||
	case []any:
 | 
			
		||||
		switch len(value) {
 | 
			
		||||
		case 0:
 | 
			
		||||
			writer.buffer.WriteString("[]\n")
 | 
			
		||||
| 
						 | 
				
			
			@ -205,7 +205,7 @@ func (writer *ruiWriterData) writeValue(value interface{}) {
 | 
			
		|||
	writer.buffer.WriteString(",\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (writer *ruiWriterData) writeProperty(tag string, value interface{}) {
 | 
			
		||||
func (writer *ruiWriterData) writeProperty(tag string, value any) {
 | 
			
		||||
	writer.writeIndent()
 | 
			
		||||
	writer.writeString(tag)
 | 
			
		||||
	writer.buffer.WriteString(" = ")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,11 +3,14 @@ package rui
 | 
			
		|||
import "fmt"
 | 
			
		||||
 | 
			
		||||
// ScrollEvent is the constant for "scroll-event" property tag.
 | 
			
		||||
// The "resize-event" is fired when the content of the view is scrolled.
 | 
			
		||||
// The "scroll-event" is fired when the content of the view is scrolled.
 | 
			
		||||
// The main listener format:
 | 
			
		||||
//   func(View, Frame).
 | 
			
		||||
//
 | 
			
		||||
//	func(View, Frame).
 | 
			
		||||
//
 | 
			
		||||
// The additional listener formats:
 | 
			
		||||
//   func(Frame), func(View), and func().
 | 
			
		||||
//
 | 
			
		||||
//	func(Frame), func(View), and func().
 | 
			
		||||
const ScrollEvent = "scroll-event"
 | 
			
		||||
 | 
			
		||||
func (view *viewData) onScroll(self View, x, y, width, height float64) {
 | 
			
		||||
| 
						 | 
				
			
			@ -15,7 +18,7 @@ func (view *viewData) onScroll(self View, x, y, width, height float64) {
 | 
			
		|||
	view.scroll.Top = y
 | 
			
		||||
	view.scroll.Width = width
 | 
			
		||||
	view.scroll.Height = height
 | 
			
		||||
	for _, listener := range GetScrollListeners(view, "") {
 | 
			
		||||
	for _, listener := range GetScrollListeners(view) {
 | 
			
		||||
		listener(self, view.scroll)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -32,10 +35,10 @@ func (view *viewData) setScroll(x, y, width, height float64) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// GetViewScroll returns ...
 | 
			
		||||
// If the second argument (subviewID) is "" then a value of the first argument (view) is returned
 | 
			
		||||
func GetViewScroll(view View, subviewID string) Frame {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value of the first argument (view) is returned
 | 
			
		||||
func GetViewScroll(view View, subviewID ...string) Frame {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
	if view == nil {
 | 
			
		||||
		return Frame{}
 | 
			
		||||
| 
						 | 
				
			
			@ -44,19 +47,9 @@ func GetViewScroll(view View, subviewID string) Frame {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// GetScrollListeners returns the list of "scroll-event" listeners. If there are no listeners then the empty list is returned
 | 
			
		||||
// If the second argument (subviewID) is "" then the listeners list of the first argument (view) is returned
 | 
			
		||||
func GetScrollListeners(view View, subviewID string) []func(View, Frame) {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if value := view.Get(ScrollEvent); value != nil {
 | 
			
		||||
			if result, ok := value.([]func(View, Frame)); ok {
 | 
			
		||||
				return result
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return []func(View, Frame){}
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then the listeners list of the first argument (view) is returned
 | 
			
		||||
func GetScrollListeners(view View, subviewID ...string) []func(View, Frame) {
 | 
			
		||||
	return getEventListeners[View, Frame](view, subviewID, ResizeEvent)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ScrollTo scrolls the view's content to the given position.
 | 
			
		||||
| 
						 | 
				
			
			@ -71,10 +64,10 @@ func ScrollViewTo(view View, subviewID string, x, y float64) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// ScrollViewToEnd scrolls the view's content to the start of view.
 | 
			
		||||
// If the second argument (subviewID) is "" then the first argument (view) is used
 | 
			
		||||
func ScrollViewToStart(view View, subviewID string) {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then the first argument (view) is used
 | 
			
		||||
func ScrollViewToStart(view View, subviewID ...string) {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		view.Session().runScript(`scrollToStart("` + view.htmlID() + `")`)
 | 
			
		||||
| 
						 | 
				
			
			@ -82,10 +75,10 @@ func ScrollViewToStart(view View, subviewID string) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// ScrollViewToEnd scrolls the view's content to the end of view.
 | 
			
		||||
// If the second argument (subviewID) is "" then the first argument (view) is used
 | 
			
		||||
func ScrollViewToEnd(view View, subviewID string) {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then the first argument (view) is used
 | 
			
		||||
func ScrollViewToEnd(view View, subviewID ...string) {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		view.Session().runScript(`scrollToEnd("` + view.htmlID() + `")`)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										14
									
								
								session.go
								
								
								
								
							
							
						
						
									
										14
									
								
								session.go
								
								
								
								
							| 
						 | 
				
			
			@ -60,11 +60,11 @@ type Session interface {
 | 
			
		|||
	RootView() View
 | 
			
		||||
	// Get returns a value of the view (with id defined by the first argument) property with name defined by the second argument.
 | 
			
		||||
	// The type of return value depends on the property. If the property is not set then nil is returned.
 | 
			
		||||
	Get(viewID, tag string) interface{}
 | 
			
		||||
	Get(viewID, tag string) any
 | 
			
		||||
	// Set sets the value (third argument) of the property (second argument) of the view with id defined by the first argument.
 | 
			
		||||
	// Return "true" if the value has been set, in the opposite case "false" are returned and
 | 
			
		||||
	// a description of the error is written to the log
 | 
			
		||||
	Set(viewID, tag string, value interface{}) bool
 | 
			
		||||
	Set(viewID, tag string, value any) bool
 | 
			
		||||
 | 
			
		||||
	// DownloadFile downloads (saves) on the client side the file located at the specified path on the server.
 | 
			
		||||
	DownloadFile(path string)
 | 
			
		||||
| 
						 | 
				
			
			@ -83,7 +83,7 @@ type Session interface {
 | 
			
		|||
 | 
			
		||||
	viewByHTMLID(id string) View
 | 
			
		||||
	nextViewID() string
 | 
			
		||||
	styleProperty(styleTag, property string) interface{}
 | 
			
		||||
	styleProperty(styleTag, property string) any
 | 
			
		||||
 | 
			
		||||
	setBrige(events chan DataObject, brige WebBrige)
 | 
			
		||||
	writeInitScript(writer *strings.Builder)
 | 
			
		||||
| 
						 | 
				
			
			@ -222,7 +222,7 @@ func (session *sessionData) close() {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (session *sessionData) styleProperty(styleTag, propertyTag string) interface{} {
 | 
			
		||||
func (session *sessionData) styleProperty(styleTag, propertyTag string) any {
 | 
			
		||||
	if style := session.getCurrentTheme().style(styleTag); style != nil {
 | 
			
		||||
		return style.getRaw(propertyTag)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -312,14 +312,14 @@ func (session *sessionData) setIgnoreViewUpdates(ignore bool) {
 | 
			
		|||
	session.ignoreUpdates = ignore
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (session *sessionData) Get(viewID, tag string) interface{} {
 | 
			
		||||
func (session *sessionData) Get(viewID, tag string) any {
 | 
			
		||||
	if view := ViewByID(session.RootView(), viewID); view != nil {
 | 
			
		||||
		return view.Get(tag)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (session *sessionData) Set(viewID, tag string, value interface{}) bool {
 | 
			
		||||
func (session *sessionData) Set(viewID, tag string, value any) bool {
 | 
			
		||||
	if view := ViewByID(session.RootView(), viewID); view != nil {
 | 
			
		||||
		return view.Set(tag, value)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -434,7 +434,7 @@ func (session *sessionData) handleViewEvent(command string, data DataObject) {
 | 
			
		|||
		if view := session.viewByHTMLID(viewID); view != nil {
 | 
			
		||||
			view.handleCommand(view, command, data)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
	} else if command != "clickOutsidePopup" {
 | 
			
		||||
		ErrorLog(`"id" property not found. Event: ` + command)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,8 +4,8 @@ import (
 | 
			
		|||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var stopTestLogFlag = false
 | 
			
		||||
var testLogDone chan int
 | 
			
		||||
// var stopTestLogFlag = false
 | 
			
		||||
// var testLogDone chan int
 | 
			
		||||
var ignoreTestLog = false
 | 
			
		||||
 | 
			
		||||
func createTestLog(t *testing.T, ignore bool) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										20
									
								
								shadow.go
								
								
								
								
							
							
						
						
									
										20
									
								
								shadow.go
								
								
								
								
							| 
						 | 
				
			
			@ -111,7 +111,7 @@ func (shadow *viewShadowData) Remove(tag string) {
 | 
			
		|||
	delete(shadow.properties, strings.ToLower(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (shadow *viewShadowData) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (shadow *viewShadowData) Set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		shadow.Remove(tag)
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			@ -127,7 +127,7 @@ func (shadow *viewShadowData) Set(tag string, value interface{}) bool {
 | 
			
		|||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (shadow *viewShadowData) Get(tag string) interface{} {
 | 
			
		||||
func (shadow *viewShadowData) Get(tag string) any {
 | 
			
		||||
	return shadow.propertyList.Get(strings.ToLower(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -151,13 +151,13 @@ func (shadow *viewShadowData) cssStyle(buffer *strings.Builder, session Session,
 | 
			
		|||
		buffer.WriteString("inset ")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	buffer.WriteString(offsetX.cssString("0"))
 | 
			
		||||
	buffer.WriteString(offsetX.cssString("0", session))
 | 
			
		||||
	buffer.WriteByte(' ')
 | 
			
		||||
	buffer.WriteString(offsetY.cssString("0"))
 | 
			
		||||
	buffer.WriteString(offsetY.cssString("0", session))
 | 
			
		||||
	buffer.WriteByte(' ')
 | 
			
		||||
	buffer.WriteString(blurRadius.cssString("0"))
 | 
			
		||||
	buffer.WriteString(blurRadius.cssString("0", session))
 | 
			
		||||
	buffer.WriteByte(' ')
 | 
			
		||||
	buffer.WriteString(spreadRadius.cssString("0"))
 | 
			
		||||
	buffer.WriteString(spreadRadius.cssString("0", session))
 | 
			
		||||
	buffer.WriteByte(' ')
 | 
			
		||||
	buffer.WriteString(color.cssString())
 | 
			
		||||
	return true
 | 
			
		||||
| 
						 | 
				
			
			@ -177,11 +177,11 @@ func (shadow *viewShadowData) cssTextStyle(buffer *strings.Builder, session Sess
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	buffer.WriteString(lead)
 | 
			
		||||
	buffer.WriteString(offsetX.cssString("0"))
 | 
			
		||||
	buffer.WriteString(offsetX.cssString("0", session))
 | 
			
		||||
	buffer.WriteByte(' ')
 | 
			
		||||
	buffer.WriteString(offsetY.cssString("0"))
 | 
			
		||||
	buffer.WriteString(offsetY.cssString("0", session))
 | 
			
		||||
	buffer.WriteByte(' ')
 | 
			
		||||
	buffer.WriteString(blurRadius.cssString("0"))
 | 
			
		||||
	buffer.WriteString(blurRadius.cssString("0", session))
 | 
			
		||||
	buffer.WriteByte(' ')
 | 
			
		||||
	buffer.WriteString(color.cssString())
 | 
			
		||||
	return true
 | 
			
		||||
| 
						 | 
				
			
			@ -225,7 +225,7 @@ func (shadow *viewShadowData) writeString(buffer *strings.Builder, indent string
 | 
			
		|||
	buffer.WriteString(" }")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (properties *propertyList) setShadow(tag string, value interface{}) bool {
 | 
			
		||||
func (properties *propertyList) setShadow(tag string, value any) bool {
 | 
			
		||||
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		delete(properties.properties, tag)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,373 @@
 | 
			
		|||
package rui
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SizeFunc describes a function that calculates the SizeUnit size.
 | 
			
		||||
// Used as the value of the SizeUnit properties.
 | 
			
		||||
// "min", "max", "clamp", "sum", "sub", "mul", and "div" functions are available.
 | 
			
		||||
type SizeFunc interface {
 | 
			
		||||
	fmt.Stringer
 | 
			
		||||
	// Name() returns the function name: "min", "max", "clamp", "sum", "sub", "mul", or "div"
 | 
			
		||||
	Name() string
 | 
			
		||||
	// Args() returns a list of function arguments
 | 
			
		||||
	Args() []any
 | 
			
		||||
	cssString(session Session) string
 | 
			
		||||
	writeCSS(topFunc string, buffer *strings.Builder, session Session)
 | 
			
		||||
	writeString(topFunc string, buffer *strings.Builder)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type sizeFuncData struct {
 | 
			
		||||
	tag  string
 | 
			
		||||
	args []any
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseSizeFunc(text string) SizeFunc {
 | 
			
		||||
	text = strings.Trim(text, " ")
 | 
			
		||||
 | 
			
		||||
	for _, tag := range []string{"min", "max", "sum", "sub", "mul", "div", "clamp"} {
 | 
			
		||||
		if strings.HasPrefix(text, tag) {
 | 
			
		||||
			text = strings.Trim(strings.TrimPrefix(text, tag), " ")
 | 
			
		||||
			last := len(text) - 1
 | 
			
		||||
			if text[0] == '(' && text[last] == ')' {
 | 
			
		||||
				text = text[1:last]
 | 
			
		||||
				bracket := 0
 | 
			
		||||
				start := 0
 | 
			
		||||
				args := []any{}
 | 
			
		||||
				for i, ch := range text {
 | 
			
		||||
					switch ch {
 | 
			
		||||
					case ',':
 | 
			
		||||
						if bracket == 0 {
 | 
			
		||||
							args = append(args, text[start:i])
 | 
			
		||||
							start = i + 1
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
					case '(':
 | 
			
		||||
						bracket++
 | 
			
		||||
 | 
			
		||||
					case ')':
 | 
			
		||||
						bracket--
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				if bracket != 0 {
 | 
			
		||||
					ErrorLogF(`Invalid "%s" function`, tag)
 | 
			
		||||
					return nil
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				args = append(args, text[start:])
 | 
			
		||||
				switch tag {
 | 
			
		||||
				case "sub", "mul", "div":
 | 
			
		||||
					if len(args) != 2 {
 | 
			
		||||
						ErrorLogF(`"%s" function needs 2 arguments`, tag)
 | 
			
		||||
						return nil
 | 
			
		||||
					}
 | 
			
		||||
				case "clamp":
 | 
			
		||||
					if len(args) != 3 {
 | 
			
		||||
						ErrorLog(`"clamp" function needs 3 arguments`)
 | 
			
		||||
						return nil
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				data := new(sizeFuncData)
 | 
			
		||||
				data.tag = tag
 | 
			
		||||
				if data.parseArgs(args, tag == "mul" || tag == "div") {
 | 
			
		||||
					return data
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			ErrorLogF(`Invalid "%s" function`, tag)
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (data *sizeFuncData) parseArgs(args []any, allowNumber bool) bool {
 | 
			
		||||
	data.args = []any{}
 | 
			
		||||
 | 
			
		||||
	numberArg := func(index int, value float64) bool {
 | 
			
		||||
		if allowNumber {
 | 
			
		||||
			if index == 1 {
 | 
			
		||||
				if value == 0 && data.tag == "div" {
 | 
			
		||||
					ErrorLog(`Division by 0 in div function`)
 | 
			
		||||
					return false
 | 
			
		||||
				}
 | 
			
		||||
				data.args = append(data.args, value)
 | 
			
		||||
				return true
 | 
			
		||||
			} else {
 | 
			
		||||
				ErrorLogF(`Only the second %s function argument can be a number`, data.tag)
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ErrorLogF(`The %s function argument cann't be a number`, data.tag)
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, arg := range args {
 | 
			
		||||
		switch arg := arg.(type) {
 | 
			
		||||
		case string:
 | 
			
		||||
			if arg = strings.Trim(arg, " \t\n"); arg == "" {
 | 
			
		||||
				ErrorLogF(`Unsupported %s function argument #%d: ""`, data.tag, i)
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if arg[0] == '@' {
 | 
			
		||||
				data.args = append(data.args, arg)
 | 
			
		||||
			} else if val, err := strconv.ParseFloat(arg, 64); err == nil {
 | 
			
		||||
				return numberArg(i, val)
 | 
			
		||||
			} else if fn := parseSizeFunc(arg); fn != nil {
 | 
			
		||||
				data.args = append(data.args, fn)
 | 
			
		||||
			} else if size, err := stringToSizeUnit(arg); err == nil {
 | 
			
		||||
				data.args = append(data.args, size)
 | 
			
		||||
			} else {
 | 
			
		||||
				ErrorLogF(`Unsupported %s function argument #%d: "%s"`, data.tag, i, arg)
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		case SizeFunc:
 | 
			
		||||
			data.args = append(data.args, arg)
 | 
			
		||||
 | 
			
		||||
		case SizeUnit:
 | 
			
		||||
			if arg.Type == Auto {
 | 
			
		||||
				ErrorLogF(`Unsupported %s function argument #%d: "auto"`, data.tag, i)
 | 
			
		||||
			}
 | 
			
		||||
			data.args = append(data.args, arg)
 | 
			
		||||
 | 
			
		||||
		case float64:
 | 
			
		||||
			return numberArg(i, arg)
 | 
			
		||||
 | 
			
		||||
		case float32:
 | 
			
		||||
			return numberArg(i, float64(arg))
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
			if n, ok := isInt(arg); ok {
 | 
			
		||||
				return numberArg(i, float64(n))
 | 
			
		||||
			}
 | 
			
		||||
			ErrorLogF(`Unsupported %s function argument #%d: %v`, data.tag, i, arg)
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (data *sizeFuncData) String() string {
 | 
			
		||||
	buffer := allocStringBuilder()
 | 
			
		||||
	defer freeStringBuilder(buffer)
 | 
			
		||||
 | 
			
		||||
	data.writeString("", buffer)
 | 
			
		||||
	return buffer.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (data *sizeFuncData) Name() string {
 | 
			
		||||
	return data.tag
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (data *sizeFuncData) Args() []any {
 | 
			
		||||
	args := make([]any, len(data.args))
 | 
			
		||||
	copy(args, data.args)
 | 
			
		||||
	return args
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (data *sizeFuncData) writeString(topFunc string, buffer *strings.Builder) {
 | 
			
		||||
	buffer.WriteString(data.tag)
 | 
			
		||||
	buffer.WriteRune('(')
 | 
			
		||||
	for i, arg := range data.args {
 | 
			
		||||
		if i > 0 {
 | 
			
		||||
			buffer.WriteString(", ")
 | 
			
		||||
		}
 | 
			
		||||
		switch arg := arg.(type) {
 | 
			
		||||
		case string:
 | 
			
		||||
			buffer.WriteString(arg)
 | 
			
		||||
 | 
			
		||||
		case SizeFunc:
 | 
			
		||||
			arg.writeString(data.tag, buffer)
 | 
			
		||||
 | 
			
		||||
		case SizeUnit:
 | 
			
		||||
			buffer.WriteString(arg.String())
 | 
			
		||||
 | 
			
		||||
		case fmt.Stringer:
 | 
			
		||||
			buffer.WriteString(arg.String())
 | 
			
		||||
 | 
			
		||||
		case float64:
 | 
			
		||||
			buffer.WriteString(fmt.Sprintf("%g", arg))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
	buffer.WriteRune(')')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (data *sizeFuncData) cssString(session Session) string {
 | 
			
		||||
	buffer := allocStringBuilder()
 | 
			
		||||
	defer freeStringBuilder(buffer)
 | 
			
		||||
 | 
			
		||||
	data.writeCSS("", buffer, session)
 | 
			
		||||
	return buffer.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (data *sizeFuncData) writeCSS(topFunc string, buffer *strings.Builder, session Session) {
 | 
			
		||||
	bracket := true
 | 
			
		||||
	sep := ", "
 | 
			
		||||
 | 
			
		||||
	mathFunc := func(s string) {
 | 
			
		||||
		sep = s
 | 
			
		||||
		switch topFunc {
 | 
			
		||||
		case "":
 | 
			
		||||
			buffer.WriteString("calc(")
 | 
			
		||||
 | 
			
		||||
		case "min", "max", "clamp":
 | 
			
		||||
			bracket = false
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
			buffer.WriteRune('(')
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch data.tag {
 | 
			
		||||
	case "min", "max", "clamp":
 | 
			
		||||
		buffer.WriteString(data.tag)
 | 
			
		||||
		buffer.WriteRune('(')
 | 
			
		||||
 | 
			
		||||
	case "sum":
 | 
			
		||||
		mathFunc(" + ")
 | 
			
		||||
 | 
			
		||||
	case "sub":
 | 
			
		||||
		mathFunc(" - ")
 | 
			
		||||
 | 
			
		||||
	case "mul":
 | 
			
		||||
		mathFunc(" * ")
 | 
			
		||||
 | 
			
		||||
	case "div":
 | 
			
		||||
		mathFunc(" / ")
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, arg := range data.args {
 | 
			
		||||
		if i > 0 {
 | 
			
		||||
			buffer.WriteString(sep)
 | 
			
		||||
		}
 | 
			
		||||
		switch arg := arg.(type) {
 | 
			
		||||
		case string:
 | 
			
		||||
			if arg, ok := session.resolveConstants(arg); ok {
 | 
			
		||||
				if fn := parseSizeFunc(arg); fn != nil {
 | 
			
		||||
					fn.writeCSS(data.tag, buffer, session)
 | 
			
		||||
				} else if size, err := stringToSizeUnit(arg); err == nil {
 | 
			
		||||
					buffer.WriteString(size.cssString("0", session))
 | 
			
		||||
				} else {
 | 
			
		||||
					buffer.WriteString("0")
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				buffer.WriteString("0")
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		case SizeFunc:
 | 
			
		||||
			arg.writeCSS(data.tag, buffer, session)
 | 
			
		||||
 | 
			
		||||
		case SizeUnit:
 | 
			
		||||
			buffer.WriteString(arg.cssString("0", session))
 | 
			
		||||
 | 
			
		||||
		case fmt.Stringer:
 | 
			
		||||
			buffer.WriteString(arg.String())
 | 
			
		||||
 | 
			
		||||
		case float64:
 | 
			
		||||
			buffer.WriteString(fmt.Sprintf("%g", arg))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if bracket {
 | 
			
		||||
		buffer.WriteRune(')')
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MaxSize creates a SizeUnit function that calculates the maximum argument.
 | 
			
		||||
// Valid argument types are SizeUnit, SizeFunc and a string which is a text description of SizeUnit or SizeFunc
 | 
			
		||||
func MaxSize(arg0, arg1 any, args ...any) SizeFunc {
 | 
			
		||||
	data := new(sizeFuncData)
 | 
			
		||||
	data.tag = "max"
 | 
			
		||||
	if !data.parseArgs(append([]any{arg0, arg1}, args...), false) {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return data
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MinSize creates a SizeUnit function that calculates the minimum argument.
 | 
			
		||||
// Valid argument types are SizeUnit, SizeFunc and a string which is a text description of SizeUnit or SizeFunc.
 | 
			
		||||
func MinSize(arg0, arg1 any, args ...any) SizeFunc {
 | 
			
		||||
	data := new(sizeFuncData)
 | 
			
		||||
	data.tag = "min"
 | 
			
		||||
	if !data.parseArgs(append([]any{arg0, arg1}, args...), false) {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return data
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SumSize creates a SizeUnit function that calculates the sum of arguments.
 | 
			
		||||
// Valid argument types are SizeUnit, SizeFunc and a string which is a text description of SizeUnit or SizeFunc.
 | 
			
		||||
func SumSize(arg0, arg1 any, args ...any) SizeFunc {
 | 
			
		||||
	data := new(sizeFuncData)
 | 
			
		||||
	data.tag = "sum"
 | 
			
		||||
	if !data.parseArgs(append([]any{arg0, arg1}, args...), false) {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return data
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SumSize creates a SizeUnit function that calculates the result of subtracting the arguments (arg1 - arg2).
 | 
			
		||||
// Valid argument types are SizeUnit, SizeFunc and a string which is a text description of SizeUnit or SizeFunc.
 | 
			
		||||
func SubSize(arg0, arg1 any) SizeFunc {
 | 
			
		||||
	data := new(sizeFuncData)
 | 
			
		||||
	data.tag = "sub"
 | 
			
		||||
	if !data.parseArgs([]any{arg0, arg1}, false) {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return data
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MulSize creates a SizeUnit function that calculates the result of multiplying the arguments (arg1 * arg2).
 | 
			
		||||
// Valid argument types are SizeUnit, SizeFunc and a string which is a text description of SizeUnit or SizeFunc.
 | 
			
		||||
// The second argument can also be a number (float32, float32, int, int8...int64, uint, uint8...unit64)
 | 
			
		||||
// or a string which is a text representation of a number.
 | 
			
		||||
func MulSize(arg0, arg1 any) SizeFunc {
 | 
			
		||||
	data := new(sizeFuncData)
 | 
			
		||||
	data.tag = "mul"
 | 
			
		||||
	if !data.parseArgs([]any{arg0, arg1}, true) {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return data
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DivSize creates a SizeUnit function that calculates the result of dividing the arguments (arg1 / arg2).
 | 
			
		||||
// Valid argument types are SizeUnit, SizeFunc and a string which is a text description of SizeUnit or SizeFunc.
 | 
			
		||||
// The second argument can also be a number (float32, float32, int, int8...int64, uint, uint8...unit64)
 | 
			
		||||
// or a string which is a text representation of a number.
 | 
			
		||||
func DivSize(arg0, arg1 any) SizeFunc {
 | 
			
		||||
	data := new(sizeFuncData)
 | 
			
		||||
	data.tag = "div"
 | 
			
		||||
	if !data.parseArgs([]any{arg0, arg1}, true) {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return data
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ClampSize creates a SizeUnit function whose the result is calculated as follows:
 | 
			
		||||
//
 | 
			
		||||
//	min ≤ value ≤ max -> value;
 | 
			
		||||
//	value < min -> min;
 | 
			
		||||
//	max < value -> max;
 | 
			
		||||
//
 | 
			
		||||
// Valid argument types are SizeUnit, SizeFunc and a string which is a text description of SizeUnit or SizeFunc.
 | 
			
		||||
func ClampSize(min, value, max any) SizeFunc {
 | 
			
		||||
	data := new(sizeFuncData)
 | 
			
		||||
	data.tag = "clamp"
 | 
			
		||||
	if !data.parseArgs([]any{min, value, max}, false) {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return data
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,50 @@
 | 
			
		|||
package rui
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestSizeFunc(t *testing.T) {
 | 
			
		||||
	session := new(sessionData)
 | 
			
		||||
	session.getCurrentTheme().SetConstant("a1", "120px", "120px")
 | 
			
		||||
 | 
			
		||||
	SetErrorLog(func(text string) {
 | 
			
		||||
		t.Error(text)
 | 
			
		||||
	})
 | 
			
		||||
	SetDebugLog(func(text string) {
 | 
			
		||||
		t.Log(text)
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	testFunc := func(fn SizeFunc, str, css string) {
 | 
			
		||||
		if fn != nil {
 | 
			
		||||
			if text := fn.String(); str != text {
 | 
			
		||||
				t.Error("String() error.\nResult:   \"" + text + "\"\nExpected: \"" + str + `"`)
 | 
			
		||||
			}
 | 
			
		||||
			if text := fn.cssString(session); css != text {
 | 
			
		||||
				t.Error("cssString() error.\nResult:   \"" + text + "\"\nExpected: \"" + css + `"`)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	testFunc(MinSize("100%", Px(10)), `min(100%, 10px)`, `min(100%, 10px)`)
 | 
			
		||||
	testFunc(MaxSize(Percent(100), "@a1"), `max(100%, @a1)`, `max(100%, 120px)`)
 | 
			
		||||
	testFunc(SumSize(Percent(100), "@a1"), `sum(100%, @a1)`, `calc(100% + 120px)`)
 | 
			
		||||
	testFunc(SubSize(Percent(100), "@a1"), `sub(100%, @a1)`, `calc(100% - 120px)`)
 | 
			
		||||
	testFunc(MulSize(Percent(100), "@a1"), `mul(100%, @a1)`, `calc(100% * 120px)`)
 | 
			
		||||
	testFunc(DivSize(Percent(100), "@a1"), `div(100%, @a1)`, `calc(100% / 120px)`)
 | 
			
		||||
	testFunc(ClampSize(Percent(20), "@a1", Percent(40)), `clamp(20%, @a1, 40%)`, `clamp(20%, 120px, 40%)`)
 | 
			
		||||
 | 
			
		||||
	testFunc(MaxSize(SubSize(Percent(100), "@a1"), "@a1"), `max(sub(100%, @a1), @a1)`, `max(100% - 120px, 120px)`)
 | 
			
		||||
 | 
			
		||||
	testParse := func(str, css string) {
 | 
			
		||||
		if fn := parseSizeFunc(str); fn != nil {
 | 
			
		||||
			testFunc(fn, str, css)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	testParse(`min(100%, 10px)`, `min(100%, 10px)`)
 | 
			
		||||
	testParse(`max(100%, @a1)`, `max(100%, 120px)`)
 | 
			
		||||
	testParse(`max(sub(100%, @a1), @a1)`, `max(100% - 120px, 120px)`)
 | 
			
		||||
	testParse(`mul(sub(100%, @a1), @a1)`, `calc((100% - 120px) * 120px)`)
 | 
			
		||||
	testParse(`mul(sub(100%, @a1), div(mul(@a1, 3), 2))`, `calc((100% - 120px) * ((120px * 3) / 2))`)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										74
									
								
								sizeUnit.go
								
								
								
								
							
							
						
						
									
										74
									
								
								sizeUnit.go
								
								
								
								
							| 
						 | 
				
			
			@ -14,89 +14,94 @@ import (
 | 
			
		|||
type SizeUnitType uint8
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// Auto - default value.
 | 
			
		||||
	// Auto is the SizeUnit type: default value.
 | 
			
		||||
	Auto SizeUnitType = 0
 | 
			
		||||
	// SizeInPixel - size in pixels.
 | 
			
		||||
	// SizeInPixel is the SizeUnit type: the Value field specifies the size in pixels.
 | 
			
		||||
	SizeInPixel SizeUnitType = 1
 | 
			
		||||
	// SizeInEM - size in em.
 | 
			
		||||
	// SizeInEM is the SizeUnit type: the Value field specifies the size in em.
 | 
			
		||||
	SizeInEM SizeUnitType = 2
 | 
			
		||||
	// SizeInEX - size in em.
 | 
			
		||||
	// SizeInEX is the SizeUnit type: the Value field specifies the size in em.
 | 
			
		||||
	SizeInEX SizeUnitType = 3
 | 
			
		||||
	// SizeInPercent - size in percents of a parant size.
 | 
			
		||||
	// SizeInPercent is the SizeUnit type: the Value field specifies the size in percents of the parent size.
 | 
			
		||||
	SizeInPercent SizeUnitType = 4
 | 
			
		||||
	// SizeInPt - size in pt (1/72 inch).
 | 
			
		||||
	// SizeInPt is the SizeUnit type: the Value field specifies the size in pt (1/72 inch).
 | 
			
		||||
	SizeInPt SizeUnitType = 5
 | 
			
		||||
	// SizeInPc - size in pc (1pc = 12pt).
 | 
			
		||||
	// SizeInPc is the SizeUnit type: the Value field specifies the size in pc (1pc = 12pt).
 | 
			
		||||
	SizeInPc SizeUnitType = 6
 | 
			
		||||
	// SizeInInch - size in inches.
 | 
			
		||||
	// SizeInInch is the SizeUnit type: the Value field specifies the size in inches.
 | 
			
		||||
	SizeInInch SizeUnitType = 7
 | 
			
		||||
	// SizeInMM - size in millimeters.
 | 
			
		||||
	// SizeInMM is the SizeUnit type: the Value field specifies the size in millimeters.
 | 
			
		||||
	SizeInMM SizeUnitType = 8
 | 
			
		||||
	// SizeInCM - size in centimeters.
 | 
			
		||||
	// SizeInCM is the SizeUnit type: the Value field specifies the size in centimeters.
 | 
			
		||||
	SizeInCM SizeUnitType = 9
 | 
			
		||||
	// SizeInFraction - size in fraction. Used only for "cell-width" and "cell-height" property
 | 
			
		||||
	// SizeInFraction is the SizeUnit type: the Value field specifies the size in fraction.
 | 
			
		||||
	// Used only for "cell-width" and "cell-height" property.
 | 
			
		||||
	SizeInFraction SizeUnitType = 10
 | 
			
		||||
	// SizeFunction is the SizeUnit type: the Function field specifies the size function.
 | 
			
		||||
	// "min", "max", "clamp", "sum", "sub", "mul", and "div" functions are available.
 | 
			
		||||
	SizeFunction = 11
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SizeUnit describe a size (Value field) and size unit (Type field).
 | 
			
		||||
type SizeUnit struct {
 | 
			
		||||
	Type  SizeUnitType
 | 
			
		||||
	Value float64
 | 
			
		||||
	Type     SizeUnitType
 | 
			
		||||
	Value    float64
 | 
			
		||||
	Function SizeFunc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AutoSize creates SizeUnit with Auto type
 | 
			
		||||
func AutoSize() SizeUnit {
 | 
			
		||||
	return SizeUnit{Auto, 0}
 | 
			
		||||
	return SizeUnit{Auto, 0, nil}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Px creates SizeUnit with SizeInPixel type
 | 
			
		||||
func Px(value float64) SizeUnit {
 | 
			
		||||
	return SizeUnit{SizeInPixel, value}
 | 
			
		||||
	return SizeUnit{SizeInPixel, value, nil}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Em creates SizeUnit with SizeInEM type
 | 
			
		||||
func Em(value float64) SizeUnit {
 | 
			
		||||
	return SizeUnit{SizeInEM, value}
 | 
			
		||||
	return SizeUnit{SizeInEM, value, nil}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Ex creates SizeUnit with SizeInEX type
 | 
			
		||||
func Ex(value float64) SizeUnit {
 | 
			
		||||
	return SizeUnit{SizeInEX, value}
 | 
			
		||||
	return SizeUnit{SizeInEX, value, nil}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Percent creates SizeUnit with SizeInDIP type
 | 
			
		||||
func Percent(value float64) SizeUnit {
 | 
			
		||||
	return SizeUnit{SizeInPercent, value}
 | 
			
		||||
	return SizeUnit{SizeInPercent, value, nil}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Pt creates SizeUnit with SizeInPt type
 | 
			
		||||
func Pt(value float64) SizeUnit {
 | 
			
		||||
	return SizeUnit{SizeInPt, value}
 | 
			
		||||
	return SizeUnit{SizeInPt, value, nil}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Pc creates SizeUnit with SizeInPc type
 | 
			
		||||
func Pc(value float64) SizeUnit {
 | 
			
		||||
	return SizeUnit{SizeInPc, value}
 | 
			
		||||
	return SizeUnit{SizeInPc, value, nil}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Mm creates SizeUnit with SizeInMM type
 | 
			
		||||
func Mm(value float64) SizeUnit {
 | 
			
		||||
	return SizeUnit{SizeInMM, value}
 | 
			
		||||
	return SizeUnit{SizeInMM, value, nil}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Cm creates SizeUnit with SizeInCM type
 | 
			
		||||
func Cm(value float64) SizeUnit {
 | 
			
		||||
	return SizeUnit{SizeInCM, value}
 | 
			
		||||
	return SizeUnit{SizeInCM, value, nil}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Inch creates SizeUnit with SizeInInch type
 | 
			
		||||
func Inch(value float64) SizeUnit {
 | 
			
		||||
	return SizeUnit{SizeInInch, value}
 | 
			
		||||
	return SizeUnit{SizeInInch, value, nil}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Fr creates SizeUnit with SizeInFraction type
 | 
			
		||||
func Fr(value float64) SizeUnit {
 | 
			
		||||
	return SizeUnit{SizeInFraction, value}
 | 
			
		||||
	return SizeUnit{SizeInFraction, value, nil}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Equal compare two SizeUnit. Return true if SizeUnit are equal
 | 
			
		||||
| 
						 | 
				
			
			@ -152,13 +157,24 @@ func stringToSizeUnit(value string) (SizeUnit, error) {
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if val, err := strconv.ParseFloat(value, 64); err == nil {
 | 
			
		||||
		return SizeUnit{Type: SizeInPixel, Value: val}, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return SizeUnit{Type: Auto, Value: 0}, errors.New(`Invalid SizeUnit value: "` + value + `"`)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// String - convert SizeUnit to string
 | 
			
		||||
func (size SizeUnit) String() string {
 | 
			
		||||
	if size.Type == Auto {
 | 
			
		||||
	switch size.Type {
 | 
			
		||||
	case Auto:
 | 
			
		||||
		return "auto"
 | 
			
		||||
 | 
			
		||||
	case SizeFunction:
 | 
			
		||||
		if size.Function == nil {
 | 
			
		||||
			return "auto"
 | 
			
		||||
		}
 | 
			
		||||
		return size.Function.String()
 | 
			
		||||
	}
 | 
			
		||||
	if suffix, ok := sizeUnitSuffixes()[size.Type]; ok {
 | 
			
		||||
		return fmt.Sprintf("%g%s", size.Value, suffix)
 | 
			
		||||
| 
						 | 
				
			
			@ -167,13 +183,19 @@ func (size SizeUnit) String() string {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// cssString - convert SizeUnit to string
 | 
			
		||||
func (size SizeUnit) cssString(textForAuto string) string {
 | 
			
		||||
func (size SizeUnit) cssString(textForAuto string, session Session) string {
 | 
			
		||||
	switch size.Type {
 | 
			
		||||
	case Auto:
 | 
			
		||||
		return textForAuto
 | 
			
		||||
 | 
			
		||||
	case SizeInEM:
 | 
			
		||||
		return fmt.Sprintf("%grem", size.Value)
 | 
			
		||||
 | 
			
		||||
	case SizeFunction:
 | 
			
		||||
		if size.Function == nil {
 | 
			
		||||
			return textForAuto
 | 
			
		||||
		}
 | 
			
		||||
		return size.Function.cssString(session)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if size.Value == 0 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,7 +41,7 @@ type stackLayoutData struct {
 | 
			
		|||
// NewStackLayout create new StackLayout object and return it
 | 
			
		||||
func NewStackLayout(session Session, params Params) StackLayout {
 | 
			
		||||
	view := new(stackLayoutData)
 | 
			
		||||
	view.Init(session)
 | 
			
		||||
	view.init(session)
 | 
			
		||||
	setInitParams(view, params)
 | 
			
		||||
	return view
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -51,8 +51,8 @@ func newStackLayout(session Session) View {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Init initialize fields of ViewsContainer by default values
 | 
			
		||||
func (layout *stackLayoutData) Init(session Session) {
 | 
			
		||||
	layout.viewsContainerData.Init(session)
 | 
			
		||||
func (layout *stackLayoutData) init(session Session) {
 | 
			
		||||
	layout.viewsContainerData.init(session)
 | 
			
		||||
	layout.tag = "StackLayout"
 | 
			
		||||
	layout.systemClass = "ruiStackLayout"
 | 
			
		||||
	layout.properties[TransitionEndEvent] = []func(View, string){layout.pushFinished, layout.popFinished}
 | 
			
		||||
| 
						 | 
				
			
			@ -97,11 +97,11 @@ func (layout *stackLayoutData) popFinished(view View, tag string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (layout *stackLayoutData) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (layout *stackLayoutData) Set(tag string, value any) bool {
 | 
			
		||||
	return layout.set(strings.ToLower(tag), value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (layout *stackLayoutData) set(tag string, value interface{}) bool {
 | 
			
		||||
func (layout *stackLayoutData) set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		layout.remove(tag)
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			@ -109,8 +109,8 @@ func (layout *stackLayoutData) set(tag string, value interface{}) bool {
 | 
			
		|||
 | 
			
		||||
	switch tag {
 | 
			
		||||
	case TransitionEndEvent:
 | 
			
		||||
		listeners, ok := valueToAnimationListeners(value)
 | 
			
		||||
		if ok {
 | 
			
		||||
		listeners, ok := valueToEventListeners[View, string](value)
 | 
			
		||||
		if ok && listeners != nil {
 | 
			
		||||
			listeners = append(listeners, layout.pushFinished)
 | 
			
		||||
			listeners = append(listeners, layout.popFinished)
 | 
			
		||||
			layout.properties[TransitionEndEvent] = listeners
 | 
			
		||||
| 
						 | 
				
			
			@ -179,11 +179,11 @@ func (layout *stackLayoutData) remove(tag string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (layout *stackLayoutData) Get(tag string) interface{} {
 | 
			
		||||
func (layout *stackLayoutData) Get(tag string) any {
 | 
			
		||||
	return layout.get(strings.ToLower(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (layout *stackLayoutData) get(tag string) interface{} {
 | 
			
		||||
func (layout *stackLayoutData) get(tag string) any {
 | 
			
		||||
	if tag == Current {
 | 
			
		||||
		return layout.peek
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -351,6 +351,8 @@ func (layout *stackLayoutData) Pop(animation int, onPopFinished func(View)) bool
 | 
			
		|||
	buffer.WriteString(htmlID)
 | 
			
		||||
	buffer.WriteString(`pop" class="ruiStackPageLayout" ontransitionend="stackTransitionEndEvent(\'`)
 | 
			
		||||
	buffer.WriteString(htmlID)
 | 
			
		||||
	buffer.WriteString(`\', \'ruiPop\', event)" ontransitioncancel="stackTransitionEndEvent(\'`)
 | 
			
		||||
	buffer.WriteString(htmlID)
 | 
			
		||||
	buffer.WriteString(`\', \'ruiPop\', event)" style="transition: transform 1s ease;">`)
 | 
			
		||||
	viewHTML(layout.popView, buffer)
 | 
			
		||||
	buffer.WriteString(`</div>`)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,7 @@ package rui
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"embed"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -28,7 +28,7 @@ func scanEmbedStringsDir(fs *embed.FS, dir string) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func scanStringsDir(path string) {
 | 
			
		||||
	if files, err := ioutil.ReadDir(path); err == nil {
 | 
			
		||||
	if files, err := os.ReadDir(path); err == nil {
 | 
			
		||||
		for _, file := range files {
 | 
			
		||||
			filename := file.Name()
 | 
			
		||||
			if filename[0] != '.' {
 | 
			
		||||
| 
						 | 
				
			
			@ -36,7 +36,7 @@ func scanStringsDir(path string) {
 | 
			
		|||
				if file.IsDir() {
 | 
			
		||||
					scanStringsDir(newPath)
 | 
			
		||||
				} else if strings.ToLower(filepath.Ext(newPath)) == ".rui" {
 | 
			
		||||
					if data, err := ioutil.ReadFile(newPath); err == nil {
 | 
			
		||||
					if data, err := os.ReadFile(newPath); err == nil {
 | 
			
		||||
						loadStringResources(string(data))
 | 
			
		||||
					} else {
 | 
			
		||||
						ErrorLog(err.Error())
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,7 +18,7 @@ type TableAdapter interface {
 | 
			
		|||
	// * rui.View
 | 
			
		||||
	// * fmt.Stringer
 | 
			
		||||
	// * rui.VerticalTableJoin, rui.HorizontalTableJoin
 | 
			
		||||
	Cell(row, column int) interface{}
 | 
			
		||||
	Cell(row, column int) any
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TableColumnStyle describes the style of TableView columns.
 | 
			
		||||
| 
						 | 
				
			
			@ -59,15 +59,15 @@ type TableAllowRowSelection interface {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// SimpleTableAdapter is implementation of TableAdapter where the content
 | 
			
		||||
// defines as [][]interface{}.
 | 
			
		||||
// When you assign [][]interface{} value to the "content" property, it is converted to SimpleTableAdapter
 | 
			
		||||
// defines as [][]any.
 | 
			
		||||
// When you assign [][]any value to the "content" property, it is converted to SimpleTableAdapter
 | 
			
		||||
type SimpleTableAdapter interface {
 | 
			
		||||
	TableAdapter
 | 
			
		||||
	TableCellStyle
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type simpleTableAdapter struct {
 | 
			
		||||
	content     [][]interface{}
 | 
			
		||||
	content     [][]any
 | 
			
		||||
	columnCount int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -94,7 +94,7 @@ type HorizontalTableJoin struct {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// NewSimpleTableAdapter creates the new SimpleTableAdapter
 | 
			
		||||
func NewSimpleTableAdapter(content [][]interface{}) SimpleTableAdapter {
 | 
			
		||||
func NewSimpleTableAdapter(content [][]any) SimpleTableAdapter {
 | 
			
		||||
	if content == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -125,7 +125,7 @@ func (adapter *simpleTableAdapter) ColumnCount() int {
 | 
			
		|||
	return adapter.columnCount
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (adapter *simpleTableAdapter) Cell(row, column int) interface{} {
 | 
			
		||||
func (adapter *simpleTableAdapter) Cell(row, column int) any {
 | 
			
		||||
	if adapter.content != nil && row >= 0 && row < len(adapter.content) &&
 | 
			
		||||
		adapter.content[row] != nil && column >= 0 && column < len(adapter.content[row]) {
 | 
			
		||||
		return adapter.content[row][column]
 | 
			
		||||
| 
						 | 
				
			
			@ -220,7 +220,7 @@ func (adapter *textTableAdapter) ColumnCount() int {
 | 
			
		|||
	return adapter.columnCount
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (adapter *textTableAdapter) Cell(row, column int) interface{} {
 | 
			
		||||
func (adapter *textTableAdapter) Cell(row, column int) any {
 | 
			
		||||
	if adapter.content != nil && row >= 0 && row < len(adapter.content) &&
 | 
			
		||||
		adapter.content[row] != nil && column >= 0 && column < len(adapter.content[row]) {
 | 
			
		||||
		return adapter.content[row][column]
 | 
			
		||||
| 
						 | 
				
			
			@ -242,7 +242,7 @@ func (style *simpleTableRowStyle) RowStyle(row int) Params {
 | 
			
		|||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (table *tableViewData) setRowStyle(value interface{}) bool {
 | 
			
		||||
func (table *tableViewData) setRowStyle(value any) bool {
 | 
			
		||||
	newSimpleTableRowStyle := func(params []Params) TableRowStyle {
 | 
			
		||||
		if len(params) == 0 {
 | 
			
		||||
			return nil
 | 
			
		||||
| 
						 | 
				
			
			@ -319,7 +319,7 @@ func (style *simpleTableColumnStyle) ColumnStyle(row int) Params {
 | 
			
		|||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (table *tableViewData) setColumnStyle(value interface{}) bool {
 | 
			
		||||
func (table *tableViewData) setColumnStyle(value any) bool {
 | 
			
		||||
	newSimpleTableColumnStyle := func(params []Params) TableColumnStyle {
 | 
			
		||||
		if len(params) == 0 {
 | 
			
		||||
			return nil
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										145
									
								
								tableView.go
								
								
								
								
							
							
						
						
									
										145
									
								
								tableView.go
								
								
								
								
							| 
						 | 
				
			
			@ -245,7 +245,7 @@ type tableCellView struct {
 | 
			
		|||
// NewTableView create new TableView object and return it
 | 
			
		||||
func NewTableView(session Session, params Params) TableView {
 | 
			
		||||
	view := new(tableViewData)
 | 
			
		||||
	view.Init(session)
 | 
			
		||||
	view.init(session)
 | 
			
		||||
	setInitParams(view, params)
 | 
			
		||||
	return view
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -255,8 +255,8 @@ func newTableView(session Session) View {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Init initialize fields of TableView by default values
 | 
			
		||||
func (table *tableViewData) Init(session Session) {
 | 
			
		||||
	table.viewData.Init(session)
 | 
			
		||||
func (table *tableViewData) init(session Session) {
 | 
			
		||||
	table.viewData.init(session)
 | 
			
		||||
	table.tag = "TableView"
 | 
			
		||||
	table.cellViews = []View{}
 | 
			
		||||
	table.cellFrame = []Frame{}
 | 
			
		||||
| 
						 | 
				
			
			@ -290,10 +290,10 @@ func (table *tableViewData) normalizeTag(tag string) string {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (table *tableViewData) Focusable() bool {
 | 
			
		||||
	return GetTableSelectionMode(table, "") != NoneSelection
 | 
			
		||||
	return GetTableSelectionMode(table) != NoneSelection
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (table *tableViewData) Get(tag string) interface{} {
 | 
			
		||||
func (table *tableViewData) Get(tag string) any {
 | 
			
		||||
	return table.get(table.normalizeTag(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -340,11 +340,11 @@ func (table *tableViewData) remove(tag string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (table *tableViewData) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (table *tableViewData) Set(tag string, value any) bool {
 | 
			
		||||
	return table.set(table.normalizeTag(tag), value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (table *tableViewData) set(tag string, value interface{}) bool {
 | 
			
		||||
func (table *tableViewData) set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		table.remove(tag)
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			@ -356,7 +356,7 @@ func (table *tableViewData) set(tag string, value interface{}) bool {
 | 
			
		|||
		case TableAdapter:
 | 
			
		||||
			table.properties[Content] = value
 | 
			
		||||
 | 
			
		||||
		case [][]interface{}:
 | 
			
		||||
		case [][]any:
 | 
			
		||||
			table.properties[Content] = NewSimpleTableAdapter(val)
 | 
			
		||||
 | 
			
		||||
		case [][]string:
 | 
			
		||||
| 
						 | 
				
			
			@ -384,20 +384,24 @@ func (table *tableViewData) set(tag string, value interface{}) bool {
 | 
			
		|||
		table.cellSelectedListener = listeners
 | 
			
		||||
 | 
			
		||||
	case TableRowClickedEvent:
 | 
			
		||||
		listeners := table.valueToRowListeners(value)
 | 
			
		||||
		if listeners == nil {
 | 
			
		||||
		listeners, ok := valueToEventListeners[TableView, int](value)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			notCompatibleType(tag, value)
 | 
			
		||||
			return false
 | 
			
		||||
		} else if listeners == nil {
 | 
			
		||||
			listeners = []func(TableView, int){}
 | 
			
		||||
		}
 | 
			
		||||
		table.rowClickedListener = listeners
 | 
			
		||||
 | 
			
		||||
	case TableRowSelectedEvent:
 | 
			
		||||
		listeners := table.valueToRowListeners(value)
 | 
			
		||||
		if listeners == nil {
 | 
			
		||||
		listeners, ok := valueToEventListeners[TableView, int](value)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			notCompatibleType(tag, value)
 | 
			
		||||
			return false
 | 
			
		||||
		} else if listeners == nil {
 | 
			
		||||
			listeners = []func(TableView, int){}
 | 
			
		||||
		}
 | 
			
		||||
		table.rowSelectedListener = []func(TableView, int){}
 | 
			
		||||
		table.rowSelectedListener = listeners
 | 
			
		||||
 | 
			
		||||
	case CellStyle:
 | 
			
		||||
		if style, ok := value.(TableCellStyle); ok {
 | 
			
		||||
| 
						 | 
				
			
			@ -450,21 +454,23 @@ func (table *tableViewData) set(tag string, value interface{}) bool {
 | 
			
		|||
				delete(table.properties, tag)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		case DataObject:
 | 
			
		||||
			params := Params{}
 | 
			
		||||
			for k := 0; k < value.PropertyCount(); k++ {
 | 
			
		||||
				if prop := value.Property(k); prop != nil && prop.Type() == TextNode {
 | 
			
		||||
					params[prop.Tag()] = prop.Text()
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if len(params) > 0 {
 | 
			
		||||
				table.properties[tag] = params
 | 
			
		||||
			} else {
 | 
			
		||||
				delete(table.properties, tag)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		case DataNode:
 | 
			
		||||
			switch value.Type() {
 | 
			
		||||
			case ObjectNode:
 | 
			
		||||
				obj := value.Object()
 | 
			
		||||
				params := Params{}
 | 
			
		||||
				for k := 0; k < obj.PropertyCount(); k++ {
 | 
			
		||||
					if prop := obj.Property(k); prop != nil && prop.Type() == TextNode {
 | 
			
		||||
						params[prop.Tag()] = prop.Text()
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				if len(params) > 0 {
 | 
			
		||||
					table.properties[tag] = params
 | 
			
		||||
				} else {
 | 
			
		||||
					delete(table.properties, tag)
 | 
			
		||||
				}
 | 
			
		||||
				return table.set(tag, value.Object())
 | 
			
		||||
 | 
			
		||||
			case TextNode:
 | 
			
		||||
				table.properties[tag] = value.Text()
 | 
			
		||||
| 
						 | 
				
			
			@ -590,7 +596,7 @@ func (table *tableViewData) propertyChanged(tag string) {
 | 
			
		|||
				updateCSSProperty(htmlID, "border-spacing", "0", session)
 | 
			
		||||
				updateCSSProperty(htmlID, "border-collapse", "collapse", session)
 | 
			
		||||
			} else {
 | 
			
		||||
				updateCSSProperty(htmlID, "border-spacing", gap.cssString("0"), session)
 | 
			
		||||
				updateCSSProperty(htmlID, "border-spacing", gap.cssString("0", session), session)
 | 
			
		||||
				updateCSSProperty(htmlID, "border-collapse", "separate", session)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -598,7 +604,7 @@ func (table *tableViewData) propertyChanged(tag string) {
 | 
			
		|||
			htmlID := table.htmlID()
 | 
			
		||||
			session := table.Session()
 | 
			
		||||
 | 
			
		||||
			switch GetTableSelectionMode(table, "") {
 | 
			
		||||
			switch GetTableSelectionMode(table) {
 | 
			
		||||
			case CellSelection:
 | 
			
		||||
				updateProperty(htmlID, "tabindex", "0", session)
 | 
			
		||||
				updateProperty(htmlID, "onfocus", "tableViewFocusEvent(this, event)", session)
 | 
			
		||||
| 
						 | 
				
			
			@ -676,7 +682,7 @@ func (table *tableViewData) currentInactiveStyle() string {
 | 
			
		|||
	return "ruiCurrentTableCell"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (table *tableViewData) valueToCellListeners(value interface{}) []func(TableView, int, int) {
 | 
			
		||||
func (table *tableViewData) valueToCellListeners(value any) []func(TableView, int, int) {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		return []func(TableView, int, int){}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -706,7 +712,7 @@ func (table *tableViewData) valueToCellListeners(value interface{}) []func(Table
 | 
			
		|||
		}
 | 
			
		||||
		return listeners
 | 
			
		||||
 | 
			
		||||
	case []interface{}:
 | 
			
		||||
	case []any:
 | 
			
		||||
		listeners := make([]func(TableView, int, int), len(value))
 | 
			
		||||
		for i, val := range value {
 | 
			
		||||
			if val == nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -731,61 +737,6 @@ func (table *tableViewData) valueToCellListeners(value interface{}) []func(Table
 | 
			
		|||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (table *tableViewData) valueToRowListeners(value interface{}) []func(TableView, int) {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		return []func(TableView, int){}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch value := value.(type) {
 | 
			
		||||
	case func(TableView, int):
 | 
			
		||||
		return []func(TableView, int){value}
 | 
			
		||||
 | 
			
		||||
	case func(int):
 | 
			
		||||
		fn := func(_ TableView, index int) {
 | 
			
		||||
			value(index)
 | 
			
		||||
		}
 | 
			
		||||
		return []func(TableView, int){fn}
 | 
			
		||||
 | 
			
		||||
	case []func(TableView, int):
 | 
			
		||||
		return value
 | 
			
		||||
 | 
			
		||||
	case []func(int):
 | 
			
		||||
		listeners := make([]func(TableView, int), len(value))
 | 
			
		||||
		for i, val := range value {
 | 
			
		||||
			if val == nil {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			listeners[i] = func(_ TableView, index int) {
 | 
			
		||||
				val(index)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners
 | 
			
		||||
 | 
			
		||||
	case []interface{}:
 | 
			
		||||
		listeners := make([]func(TableView, int), len(value))
 | 
			
		||||
		for i, val := range value {
 | 
			
		||||
			if val == nil {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			switch val := val.(type) {
 | 
			
		||||
			case func(TableView, int):
 | 
			
		||||
				listeners[i] = val
 | 
			
		||||
 | 
			
		||||
			case func(int):
 | 
			
		||||
				listeners[i] = func(_ TableView, index int) {
 | 
			
		||||
					val(index)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			default:
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (table *tableViewData) htmlTag() string {
 | 
			
		||||
	return "table"
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -808,7 +759,7 @@ func (table *tableViewData) htmlProperties(self View, buffer *strings.Builder) {
 | 
			
		|||
		buffer.WriteRune('"')
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if selectionMode := GetTableSelectionMode(table, ""); selectionMode != NoneSelection {
 | 
			
		||||
	if selectionMode := GetTableSelectionMode(table); selectionMode != NoneSelection {
 | 
			
		||||
		buffer.WriteString(` onfocus="tableViewFocusEvent(this, event)" onblur="tableViewBlurEvent(this, event)" data-focusitemstyle="`)
 | 
			
		||||
		buffer.WriteString(table.currentStyle())
 | 
			
		||||
		buffer.WriteString(`" data-bluritemstyle="`)
 | 
			
		||||
| 
						 | 
				
			
			@ -879,10 +830,10 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
 | 
			
		|||
	defer freeStringBuilder(cssBuilder.buffer)
 | 
			
		||||
 | 
			
		||||
	var view tableCellView
 | 
			
		||||
	view.Init(session)
 | 
			
		||||
	view.init(session)
 | 
			
		||||
 | 
			
		||||
	ignorCells := []struct{ row, column int }{}
 | 
			
		||||
	selectionMode := GetTableSelectionMode(table, "")
 | 
			
		||||
	selectionMode := GetTableSelectionMode(table)
 | 
			
		||||
 | 
			
		||||
	var allowCellSelection TableAllowCellSelection = nil
 | 
			
		||||
	if allow, ok := adapter.(TableAllowCellSelection); ok {
 | 
			
		||||
| 
						 | 
				
			
			@ -905,7 +856,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	vAlignCss := enumProperties[TableVerticalAlign].cssValues
 | 
			
		||||
	vAlignValue := GetTableVerticalAlign(table, "")
 | 
			
		||||
	vAlignValue := GetTableVerticalAlign(table)
 | 
			
		||||
	if vAlignValue < 0 || vAlignValue >= len(vAlignCss) {
 | 
			
		||||
		vAlignValue = 0
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -1160,8 +1111,8 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
 | 
			
		|||
		buffer.WriteString("</colgroup>")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	headHeight := GetTableHeadHeight(table, "")
 | 
			
		||||
	footHeight := GetTableFootHeight(table, "")
 | 
			
		||||
	headHeight := GetTableHeadHeight(table)
 | 
			
		||||
	footHeight := GetTableFootHeight(table)
 | 
			
		||||
	cellBorder := table.getCellBorder()
 | 
			
		||||
	cellPadding := table.boundsProperty(CellPadding)
 | 
			
		||||
	if cellPadding == nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -1370,24 +1321,26 @@ func (table *tableViewData) getCurrent() CellIndex {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (table *tableViewData) cssStyle(self View, builder cssBuilder) {
 | 
			
		||||
	table.viewData.cssViewStyle(builder, table.Session())
 | 
			
		||||
	session := table.Session()
 | 
			
		||||
	table.viewData.cssViewStyle(builder, session)
 | 
			
		||||
 | 
			
		||||
	gap, ok := sizeProperty(table, Gap, table.Session())
 | 
			
		||||
	gap, ok := sizeProperty(table, Gap, session)
 | 
			
		||||
	if !ok || gap.Type == Auto || gap.Value <= 0 {
 | 
			
		||||
		builder.add("border-spacing", "0")
 | 
			
		||||
		builder.add("border-collapse", "collapse")
 | 
			
		||||
	} else {
 | 
			
		||||
		builder.add("border-spacing", gap.cssString("0"))
 | 
			
		||||
		builder.add("border-spacing", gap.cssString("0", session))
 | 
			
		||||
		builder.add("border-collapse", "separate")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (table *tableViewData) ReloadTableData() {
 | 
			
		||||
	session := table.Session()
 | 
			
		||||
	if content := table.content(); content != nil {
 | 
			
		||||
		updateProperty(table.htmlID(), "data-rows", strconv.Itoa(content.RowCount()), table.Session())
 | 
			
		||||
		updateProperty(table.htmlID(), "data-columns", strconv.Itoa(content.ColumnCount()), table.Session())
 | 
			
		||||
		updateProperty(table.htmlID(), "data-rows", strconv.Itoa(content.RowCount()), session)
 | 
			
		||||
		updateProperty(table.htmlID(), "data-columns", strconv.Itoa(content.ColumnCount()), session)
 | 
			
		||||
	}
 | 
			
		||||
	updateInnerHTML(table.htmlID(), table.Session())
 | 
			
		||||
	updateInnerHTML(table.htmlID(), session)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (table *tableViewData) onItemResize(self View, index string, x, y, width, height float64) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,11 +2,11 @@ package rui
 | 
			
		|||
 | 
			
		||||
import "strings"
 | 
			
		||||
 | 
			
		||||
func (cell *tableCellView) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (cell *tableCellView) Set(tag string, value any) bool {
 | 
			
		||||
	return cell.set(strings.ToLower(tag), value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cell *tableCellView) set(tag string, value interface{}) bool {
 | 
			
		||||
func (cell *tableCellView) set(tag string, value any) bool {
 | 
			
		||||
	switch tag {
 | 
			
		||||
	case VerticalAlign:
 | 
			
		||||
		tag = TableVerticalAlign
 | 
			
		||||
| 
						 | 
				
			
			@ -24,10 +24,10 @@ func (cell *tableCellView) cssStyle(self View, builder cssBuilder) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// GetTableContent returns a TableAdapter which defines the TableView content.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTableContent(view View, subviewID string) TableAdapter {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTableContent(view View, subviewID ...string) TableAdapter {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if view != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -40,10 +40,10 @@ func GetTableContent(view View, subviewID string) TableAdapter {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// GetTableRowStyle returns a TableRowStyle which defines styles of TableView rows.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTableRowStyle(view View, subviewID string) TableRowStyle {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTableRowStyle(view View, subviewID ...string) TableRowStyle {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if view != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -56,10 +56,10 @@ func GetTableRowStyle(view View, subviewID string) TableRowStyle {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// GetTableColumnStyle returns a TableColumnStyle which defines styles of TableView columns.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTableColumnStyle(view View, subviewID string) TableColumnStyle {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTableColumnStyle(view View, subviewID ...string) TableColumnStyle {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if view != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -72,10 +72,10 @@ func GetTableColumnStyle(view View, subviewID string) TableColumnStyle {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// GetTableCellStyle returns a TableCellStyle which defines styles of TableView cells.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTableCellStyle(view View, subviewID string) TableCellStyle {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTableCellStyle(view View, subviewID ...string) TableCellStyle {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if view != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -89,72 +89,42 @@ func GetTableCellStyle(view View, subviewID string) TableCellStyle {
 | 
			
		|||
 | 
			
		||||
// GetTableSelectionMode returns the mode of the TableView elements selection.
 | 
			
		||||
// Valid values are NoneSelection (0), CellSelection (1), and RowSelection (2).
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTableSelectionMode(view View, subviewID string) int {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if result, ok := enumStyledProperty(view, SelectionMode, NoneSelection); ok {
 | 
			
		||||
			return result
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return NoneSelection
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTableSelectionMode(view View, subviewID ...string) int {
 | 
			
		||||
	return enumStyledProperty(view, subviewID, SelectionMode, NoneSelection, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetTableVerticalAlign returns a vertical align in a TavleView cell. Returns one of next values:
 | 
			
		||||
// TopAlign (0), BottomAlign (1), CenterAlign (2), and BaselineAlign (3)
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTableVerticalAlign(view View, subviewID string) int {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if result, ok := enumStyledProperty(view, TableVerticalAlign, TopAlign); ok {
 | 
			
		||||
			return result
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return TopAlign
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTableVerticalAlign(view View, subviewID ...string) int {
 | 
			
		||||
	return enumStyledProperty(view, subviewID, TableVerticalAlign, TopAlign, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetTableHeadHeight returns the number of rows in the table header.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTableHeadHeight(view View, subviewID string) int {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		headHeight, _ := intStyledProperty(view, HeadHeight, 0)
 | 
			
		||||
		return headHeight
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTableHeadHeight(view View, subviewID ...string) int {
 | 
			
		||||
	return intStyledProperty(view, subviewID, HeadHeight, 0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetTableFootHeight returns the number of rows in the table footer.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTableFootHeight(view View, subviewID string) int {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		headHeight, _ := intStyledProperty(view, FootHeight, 0)
 | 
			
		||||
		return headHeight
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTableFootHeight(view View, subviewID ...string) int {
 | 
			
		||||
	return intStyledProperty(view, subviewID, FootHeight, 0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetTableCurrent returns the row and column index of the TableView selected cell/row.
 | 
			
		||||
// If there is no selected cell/row or the selection mode is NoneSelection (0),
 | 
			
		||||
// then a value of the row and column index less than 0 is returned.
 | 
			
		||||
// If the selection mode is RowSelection (2) then the returned column index is less than 0.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTableCurrent(view View, subviewID string) CellIndex {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTableCurrent(view View, subviewID ...string) CellIndex {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if selectionMode := GetTableSelectionMode(view, ""); selectionMode != NoneSelection {
 | 
			
		||||
		if selectionMode := GetTableSelectionMode(view); selectionMode != NoneSelection {
 | 
			
		||||
			if tableView, ok := view.(TableView); ok {
 | 
			
		||||
				return tableView.getCurrent()
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -165,10 +135,10 @@ func GetTableCurrent(view View, subviewID string) CellIndex {
 | 
			
		|||
 | 
			
		||||
// GetTableCellClickedListeners returns listeners of event which occurs when the user clicks on a table cell.
 | 
			
		||||
// If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTableCellClickedListeners(view View, subviewID string) []func(TableView, int, int) {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTableCellClickedListeners(view View, subviewID ...string) []func(TableView, int, int) {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if value := view.Get(TableCellClickedEvent); value != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -182,10 +152,10 @@ func GetTableCellClickedListeners(view View, subviewID string) []func(TableView,
 | 
			
		|||
 | 
			
		||||
// GetTableCellSelectedListeners returns listeners of event which occurs when a table cell becomes selected.
 | 
			
		||||
// If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTableCellSelectedListeners(view View, subviewID string) []func(TableView, int, int) {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTableCellSelectedListeners(view View, subviewID ...string) []func(TableView, int, int) {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if value := view.Get(TableCellSelectedEvent); value != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -199,43 +169,23 @@ func GetTableCellSelectedListeners(view View, subviewID string) []func(TableView
 | 
			
		|||
 | 
			
		||||
// GetTableRowClickedListeners returns listeners of event which occurs when the user clicks on a table row.
 | 
			
		||||
// If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTableRowClickedListeners(view View, subviewID string) []func(TableView, int) {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if value := view.Get(TableRowClickedEvent); value != nil {
 | 
			
		||||
			if result, ok := value.([]func(TableView, int)); ok {
 | 
			
		||||
				return result
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return []func(TableView, int){}
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTableRowClickedListeners(view View, subviewID ...string) []func(TableView, int) {
 | 
			
		||||
	return getEventListeners[TableView, int](view, subviewID, TableRowClickedEvent)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetTableRowSelectedListeners returns listeners of event which occurs when a table row becomes selected.
 | 
			
		||||
// If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTableRowSelectedListeners(view View, subviewID string) []func(TableView, int) {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if value := view.Get(TableRowSelectedEvent); value != nil {
 | 
			
		||||
			if result, ok := value.([]func(TableView, int)); ok {
 | 
			
		||||
				return result
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return []func(TableView, int){}
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTableRowSelectedListeners(view View, subviewID ...string) []func(TableView, int) {
 | 
			
		||||
	return getEventListeners[TableView, int](view, subviewID, TableRowSelectedEvent)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReloadTableViewData updates TableView
 | 
			
		||||
func ReloadTableViewData(view View, subviewID string) bool {
 | 
			
		||||
func ReloadTableViewData(view View, subviewID ...string) bool {
 | 
			
		||||
	var tableView TableView
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		if tableView = TableViewByID(view, subviewID); tableView == nil {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		if tableView = TableViewByID(view, subviewID[0]); tableView == nil {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -81,7 +81,7 @@ type tabsLayoutData struct {
 | 
			
		|||
// NewTabsLayout create new TabsLayout object and return it
 | 
			
		||||
func NewTabsLayout(session Session, params Params) TabsLayout {
 | 
			
		||||
	view := new(tabsLayoutData)
 | 
			
		||||
	view.Init(session)
 | 
			
		||||
	view.init(session)
 | 
			
		||||
	setInitParams(view, params)
 | 
			
		||||
	return view
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -91,8 +91,8 @@ func newTabsLayout(session Session) View {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Init initialize fields of ViewsContainer by default values
 | 
			
		||||
func (tabsLayout *tabsLayoutData) Init(session Session) {
 | 
			
		||||
	tabsLayout.viewsContainerData.Init(session)
 | 
			
		||||
func (tabsLayout *tabsLayoutData) init(session Session) {
 | 
			
		||||
	tabsLayout.viewsContainerData.init(session)
 | 
			
		||||
	tabsLayout.tag = "TabsLayout"
 | 
			
		||||
	tabsLayout.systemClass = "ruiTabsLayout"
 | 
			
		||||
	tabsLayout.tabListener = []func(TabsLayout, int, int){}
 | 
			
		||||
| 
						 | 
				
			
			@ -108,11 +108,11 @@ func (tabsLayout *tabsLayoutData) currentItem(defaultValue int) int {
 | 
			
		|||
	return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (tabsLayout *tabsLayoutData) Get(tag string) interface{} {
 | 
			
		||||
func (tabsLayout *tabsLayoutData) Get(tag string) any {
 | 
			
		||||
	return tabsLayout.get(strings.ToLower(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (tabsLayout *tabsLayoutData) get(tag string) interface{} {
 | 
			
		||||
func (tabsLayout *tabsLayoutData) get(tag string) any {
 | 
			
		||||
	switch tag {
 | 
			
		||||
	case CurrentTabChangedEvent:
 | 
			
		||||
		return tabsLayout.tabListener
 | 
			
		||||
| 
						 | 
				
			
			@ -190,11 +190,11 @@ func (tabsLayout *tabsLayoutData) remove(tag string) {
 | 
			
		|||
	tabsLayout.propertyChangedEvent(tag)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (tabsLayout *tabsLayoutData) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (tabsLayout *tabsLayoutData) Set(tag string, value any) bool {
 | 
			
		||||
	return tabsLayout.set(strings.ToLower(tag), value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (tabsLayout *tabsLayoutData) set(tag string, value interface{}) bool {
 | 
			
		||||
func (tabsLayout *tabsLayoutData) set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		tabsLayout.remove(tag)
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			@ -210,10 +210,12 @@ func (tabsLayout *tabsLayoutData) set(tag string, value interface{}) bool {
 | 
			
		|||
		tabsLayout.tabListener = listeners
 | 
			
		||||
 | 
			
		||||
	case TabCloseEvent:
 | 
			
		||||
		listeners := tabsLayout.valueToCloseListeners(value)
 | 
			
		||||
		if listeners == nil {
 | 
			
		||||
		listeners, ok := valueToEventListeners[TabsLayout, int](value)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			notCompatibleType(tag, value)
 | 
			
		||||
			return false
 | 
			
		||||
		} else if listeners == nil {
 | 
			
		||||
			listeners = []func(TabsLayout, int){}
 | 
			
		||||
		}
 | 
			
		||||
		tabsLayout.tabCloseListener = listeners
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -286,7 +288,7 @@ func (tabsLayout *tabsLayoutData) set(tag string, value interface{}) bool {
 | 
			
		|||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (tabsLayout *tabsLayoutData) valueToTabListeners(value interface{}) []func(TabsLayout, int, int) {
 | 
			
		||||
func (tabsLayout *tabsLayoutData) valueToTabListeners(value any) []func(TabsLayout, int, int) {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		return []func(TabsLayout, int, int){}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -388,7 +390,7 @@ func (tabsLayout *tabsLayoutData) valueToTabListeners(value interface{}) []func(
 | 
			
		|||
		}
 | 
			
		||||
		return listeners
 | 
			
		||||
 | 
			
		||||
	case []interface{}:
 | 
			
		||||
	case []any:
 | 
			
		||||
		listeners := make([]func(TabsLayout, int, int), len(value))
 | 
			
		||||
		for i, val := range value {
 | 
			
		||||
			if val == nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -433,61 +435,6 @@ func (tabsLayout *tabsLayoutData) valueToTabListeners(value interface{}) []func(
 | 
			
		|||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (tabsLayout *tabsLayoutData) valueToCloseListeners(value interface{}) []func(TabsLayout, int) {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		return []func(TabsLayout, int){}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch value := value.(type) {
 | 
			
		||||
	case func(TabsLayout, int):
 | 
			
		||||
		return []func(TabsLayout, int){value}
 | 
			
		||||
 | 
			
		||||
	case func(int):
 | 
			
		||||
		fn := func(_ TabsLayout, index int) {
 | 
			
		||||
			value(index)
 | 
			
		||||
		}
 | 
			
		||||
		return []func(TabsLayout, int){fn}
 | 
			
		||||
 | 
			
		||||
	case []func(TabsLayout, int):
 | 
			
		||||
		return value
 | 
			
		||||
 | 
			
		||||
	case []func(int):
 | 
			
		||||
		listeners := make([]func(TabsLayout, int), len(value))
 | 
			
		||||
		for i, val := range value {
 | 
			
		||||
			if val == nil {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			listeners[i] = func(_ TabsLayout, index int) {
 | 
			
		||||
				val(index)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners
 | 
			
		||||
 | 
			
		||||
	case []interface{}:
 | 
			
		||||
		listeners := make([]func(TabsLayout, int), len(value))
 | 
			
		||||
		for i, val := range value {
 | 
			
		||||
			if val == nil {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			switch val := val.(type) {
 | 
			
		||||
			case func(TabsLayout, int):
 | 
			
		||||
				listeners[i] = val
 | 
			
		||||
 | 
			
		||||
			case func(int):
 | 
			
		||||
				listeners[i] = func(_ TabsLayout, index int) {
 | 
			
		||||
					val(index)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			default:
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (tabsLayout *tabsLayoutData) tabsLocation() int {
 | 
			
		||||
	tabs, _ := enumProperty(tabsLayout, Tabs, tabsLayout.session, 0)
 | 
			
		||||
	return tabs
 | 
			
		||||
| 
						 | 
				
			
			@ -576,7 +523,7 @@ func (tabsLayout *tabsLayoutData) ListItem(index int, session Session) View {
 | 
			
		|||
	if !ok || title == "" {
 | 
			
		||||
		title = "No title"
 | 
			
		||||
	}
 | 
			
		||||
	if !GetNotTranslate(tabsLayout, "") {
 | 
			
		||||
	if !GetNotTranslate(tabsLayout) {
 | 
			
		||||
		title, _ = tabsLayout.Session().GetString(title)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -776,7 +723,7 @@ func (tabsLayout *tabsLayoutData) htmlSubviews(self View, buffer *strings.Builde
 | 
			
		|||
		inactiveStyle := tabsLayout.inactiveTabStyle()
 | 
			
		||||
		activeStyle := tabsLayout.activeTabStyle()
 | 
			
		||||
 | 
			
		||||
		notTranslate := GetNotTranslate(tabsLayout, "")
 | 
			
		||||
		notTranslate := GetNotTranslate(tabsLayout)
 | 
			
		||||
		closeButton, _ := boolProperty(tabsLayout, TabCloseButton, tabsLayout.session)
 | 
			
		||||
 | 
			
		||||
		var tabStyle, titleDiv string
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										27
									
								
								textView.go
								
								
								
								
							
							
						
						
									
										27
									
								
								textView.go
								
								
								
								
							| 
						 | 
				
			
			@ -17,7 +17,7 @@ type textViewData struct {
 | 
			
		|||
// NewTextView create new TextView object and return it
 | 
			
		||||
func NewTextView(session Session, params Params) TextView {
 | 
			
		||||
	view := new(textViewData)
 | 
			
		||||
	view.Init(session)
 | 
			
		||||
	view.init(session)
 | 
			
		||||
	setInitParams(view, params)
 | 
			
		||||
	return view
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -27,8 +27,8 @@ func newTextView(session Session) View {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Init initialize fields of TextView by default values
 | 
			
		||||
func (textView *textViewData) Init(session Session) {
 | 
			
		||||
	textView.viewData.Init(session)
 | 
			
		||||
func (textView *textViewData) init(session Session) {
 | 
			
		||||
	textView.viewData.init(session)
 | 
			
		||||
	textView.tag = "TextView"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -36,7 +36,7 @@ func (textView *textViewData) String() string {
 | 
			
		|||
	return getViewString(textView)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (textView *textViewData) Get(tag string) interface{} {
 | 
			
		||||
func (textView *textViewData) Get(tag string) any {
 | 
			
		||||
	return textView.get(strings.ToLower(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -57,11 +57,11 @@ func (textView *textViewData) remove(tag string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (textView *textViewData) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (textView *textViewData) Set(tag string, value any) bool {
 | 
			
		||||
	return textView.set(strings.ToLower(tag), value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (textView *textViewData) set(tag string, value interface{}) bool {
 | 
			
		||||
func (textView *textViewData) set(tag string, value any) bool {
 | 
			
		||||
	switch tag {
 | 
			
		||||
	case Text:
 | 
			
		||||
		switch value := value.(type) {
 | 
			
		||||
| 
						 | 
				
			
			@ -155,7 +155,7 @@ 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, "") {
 | 
			
		||||
			if !GetNotTranslate(textView) {
 | 
			
		||||
				text, _ = textView.session.GetString(text)
 | 
			
		||||
			}
 | 
			
		||||
			buffer.WriteString(textToJS(text))
 | 
			
		||||
| 
						 | 
				
			
			@ -165,14 +165,7 @@ func (textView *textViewData) htmlSubviews(self View, buffer *strings.Builder) {
 | 
			
		|||
 | 
			
		||||
// GetTextOverflow returns a value of the "text-overflow" property:
 | 
			
		||||
// TextOverflowClip (0) or TextOverflowEllipsis (1).
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTextOverflow(view View, subviewID string) int {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view == nil {
 | 
			
		||||
		return SingleLineText
 | 
			
		||||
	}
 | 
			
		||||
	t, _ := enumStyledProperty(view, TextOverflow, SingleLineText)
 | 
			
		||||
	return t
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTextOverflow(view View, subviewID ...string) int {
 | 
			
		||||
	return enumStyledProperty(view, subviewID, TextOverflow, SingleLineText, false)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										148
									
								
								timePicker.go
								
								
								
								
							
							
						
						
									
										148
									
								
								timePicker.go
								
								
								
								
							| 
						 | 
				
			
			@ -29,7 +29,7 @@ type timePickerData struct {
 | 
			
		|||
// NewTimePicker create new TimePicker object and return it
 | 
			
		||||
func NewTimePicker(session Session, params Params) TimePicker {
 | 
			
		||||
	view := new(timePickerData)
 | 
			
		||||
	view.Init(session)
 | 
			
		||||
	view.init(session)
 | 
			
		||||
	setInitParams(view, params)
 | 
			
		||||
	return view
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -38,8 +38,8 @@ func newTimePicker(session Session) View {
 | 
			
		|||
	return NewTimePicker(session, nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (picker *timePickerData) Init(session Session) {
 | 
			
		||||
	picker.viewData.Init(session)
 | 
			
		||||
func (picker *timePickerData) init(session Session) {
 | 
			
		||||
	picker.viewData.init(session)
 | 
			
		||||
	picker.tag = "TimePicker"
 | 
			
		||||
	picker.timeChangedListeners = []func(TimePicker, time.Time){}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -96,7 +96,7 @@ func (picker *timePickerData) remove(tag string) {
 | 
			
		|||
	case TimePickerValue:
 | 
			
		||||
		if _, ok := picker.properties[TimePickerValue]; ok {
 | 
			
		||||
			delete(picker.properties, TimePickerValue)
 | 
			
		||||
			time := GetTimePickerValue(picker, "")
 | 
			
		||||
			time := GetTimePickerValue(picker)
 | 
			
		||||
			if picker.created {
 | 
			
		||||
				picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, picker.htmlID(), time.Format(timeFormat)))
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -114,11 +114,11 @@ func (picker *timePickerData) remove(tag string) {
 | 
			
		|||
	picker.propertyChangedEvent(tag)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (picker *timePickerData) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (picker *timePickerData) Set(tag string, value any) bool {
 | 
			
		||||
	return picker.set(picker.normalizeTag(tag), value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (picker *timePickerData) set(tag string, value interface{}) bool {
 | 
			
		||||
func (picker *timePickerData) set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		picker.remove(tag)
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			@ -192,9 +192,9 @@ func (picker *timePickerData) set(tag string, value interface{}) bool {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
	case TimePickerStep:
 | 
			
		||||
		oldStep := GetTimePickerStep(picker, "")
 | 
			
		||||
		oldStep := GetTimePickerStep(picker)
 | 
			
		||||
		if picker.setIntProperty(TimePickerStep, value) {
 | 
			
		||||
			if step := GetTimePickerStep(picker, ""); oldStep != step {
 | 
			
		||||
			if step := GetTimePickerStep(picker); oldStep != step {
 | 
			
		||||
				if picker.created {
 | 
			
		||||
					if step > 0 {
 | 
			
		||||
						updateProperty(picker.htmlID(), Step, strconv.Itoa(step), picker.session)
 | 
			
		||||
| 
						 | 
				
			
			@ -208,7 +208,7 @@ func (picker *timePickerData) set(tag string, value interface{}) bool {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
	case TimePickerValue:
 | 
			
		||||
		oldTime := GetTimePickerValue(picker, "")
 | 
			
		||||
		oldTime := GetTimePickerValue(picker)
 | 
			
		||||
		if time, ok := setTimeValue(TimePickerValue); ok {
 | 
			
		||||
			if time != oldTime {
 | 
			
		||||
				if picker.created {
 | 
			
		||||
| 
						 | 
				
			
			@ -223,57 +223,14 @@ func (picker *timePickerData) set(tag string, value interface{}) bool {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
	case TimeChangedEvent:
 | 
			
		||||
		switch value := value.(type) {
 | 
			
		||||
		case func(TimePicker, time.Time):
 | 
			
		||||
			picker.timeChangedListeners = []func(TimePicker, time.Time){value}
 | 
			
		||||
 | 
			
		||||
		case func(time.Time):
 | 
			
		||||
			fn := func(_ TimePicker, time time.Time) {
 | 
			
		||||
				value(time)
 | 
			
		||||
			}
 | 
			
		||||
			picker.timeChangedListeners = []func(TimePicker, time.Time){fn}
 | 
			
		||||
 | 
			
		||||
		case []func(TimePicker, time.Time):
 | 
			
		||||
			picker.timeChangedListeners = value
 | 
			
		||||
 | 
			
		||||
		case []func(time.Time):
 | 
			
		||||
			listeners := make([]func(TimePicker, time.Time), len(value))
 | 
			
		||||
			for i, val := range value {
 | 
			
		||||
				if val == nil {
 | 
			
		||||
					notCompatibleType(tag, val)
 | 
			
		||||
					return false
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				listeners[i] = func(_ TimePicker, time time.Time) {
 | 
			
		||||
					val(time)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			picker.timeChangedListeners = listeners
 | 
			
		||||
 | 
			
		||||
		case []interface{}:
 | 
			
		||||
			listeners := make([]func(TimePicker, time.Time), len(value))
 | 
			
		||||
			for i, val := range value {
 | 
			
		||||
				if val == nil {
 | 
			
		||||
					notCompatibleType(tag, val)
 | 
			
		||||
					return false
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				switch val := val.(type) {
 | 
			
		||||
				case func(TimePicker, time.Time):
 | 
			
		||||
					listeners[i] = val
 | 
			
		||||
 | 
			
		||||
				case func(time.Time):
 | 
			
		||||
					listeners[i] = func(_ TimePicker, time time.Time) {
 | 
			
		||||
						val(time)
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
				default:
 | 
			
		||||
					notCompatibleType(tag, val)
 | 
			
		||||
					return false
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			picker.timeChangedListeners = listeners
 | 
			
		||||
		listeners, ok := valueToEventListeners[TimePicker, time.Time](value)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			notCompatibleType(tag, value)
 | 
			
		||||
			return false
 | 
			
		||||
		} else if listeners == nil {
 | 
			
		||||
			listeners = []func(TimePicker, time.Time){}
 | 
			
		||||
		}
 | 
			
		||||
		picker.timeChangedListeners = listeners
 | 
			
		||||
		picker.propertyChangedEvent(tag)
 | 
			
		||||
		return true
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -283,11 +240,11 @@ func (picker *timePickerData) set(tag string, value interface{}) bool {
 | 
			
		|||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (picker *timePickerData) Get(tag string) interface{} {
 | 
			
		||||
func (picker *timePickerData) Get(tag string) any {
 | 
			
		||||
	return picker.get(picker.normalizeTag(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (picker *timePickerData) get(tag string) interface{} {
 | 
			
		||||
func (picker *timePickerData) get(tag string) any {
 | 
			
		||||
	switch tag {
 | 
			
		||||
	case TimeChangedEvent:
 | 
			
		||||
		return picker.timeChangedListeners
 | 
			
		||||
| 
						 | 
				
			
			@ -325,7 +282,7 @@ func (picker *timePickerData) htmlProperties(self View, buffer *strings.Builder)
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	buffer.WriteString(` value="`)
 | 
			
		||||
	buffer.WriteString(GetTimePickerValue(picker, "").Format(timeFormat))
 | 
			
		||||
	buffer.WriteString(GetTimePickerValue(picker).Format(timeFormat))
 | 
			
		||||
	buffer.WriteByte('"')
 | 
			
		||||
 | 
			
		||||
	buffer.WriteString(` oninput="editViewInputEvent(this)"`)
 | 
			
		||||
| 
						 | 
				
			
			@ -335,7 +292,7 @@ func (picker *timePickerData) htmlProperties(self View, buffer *strings.Builder)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (picker *timePickerData) htmlDisabledProperties(self View, buffer *strings.Builder) {
 | 
			
		||||
	if IsDisabled(self, "") {
 | 
			
		||||
	if IsDisabled(self) {
 | 
			
		||||
		buffer.WriteString(` disabled`)
 | 
			
		||||
	}
 | 
			
		||||
	picker.viewData.htmlDisabledProperties(self, buffer)
 | 
			
		||||
| 
						 | 
				
			
			@ -346,7 +303,7 @@ func (picker *timePickerData) handleCommand(self View, command string, data Data
 | 
			
		|||
	case "textChanged":
 | 
			
		||||
		if text, ok := data.PropertyValue("text"); ok {
 | 
			
		||||
			if value, err := time.Parse(timeFormat, text); err == nil {
 | 
			
		||||
				oldValue := GetTimePickerValue(picker, "")
 | 
			
		||||
				oldValue := GetTimePickerValue(picker)
 | 
			
		||||
				picker.properties[TimePickerValue] = value
 | 
			
		||||
				if value != oldValue {
 | 
			
		||||
					for _, listener := range picker.timeChangedListeners {
 | 
			
		||||
| 
						 | 
				
			
			@ -362,7 +319,7 @@ func (picker *timePickerData) handleCommand(self View, command string, data Data
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func getTimeProperty(view View, mainTag, shortTag string) (time.Time, bool) {
 | 
			
		||||
	valueToTime := func(value interface{}) (time.Time, bool) {
 | 
			
		||||
	valueToTime := func(value any) (time.Time, bool) {
 | 
			
		||||
		if value != nil {
 | 
			
		||||
			switch value := value.(type) {
 | 
			
		||||
			case time.Time:
 | 
			
		||||
| 
						 | 
				
			
			@ -396,10 +353,10 @@ func getTimeProperty(view View, mainTag, shortTag string) (time.Time, bool) {
 | 
			
		|||
 | 
			
		||||
// GetTimePickerMin returns the min time of TimePicker subview and "true" as the second value if the min time is set,
 | 
			
		||||
// "false" as the second value otherwise.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTimePickerMin(view View, subviewID string) (time.Time, bool) {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTimePickerMin(view View, subviewID ...string) (time.Time, bool) {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		return getTimeProperty(view, TimePickerMin, Min)
 | 
			
		||||
| 
						 | 
				
			
			@ -409,10 +366,10 @@ func GetTimePickerMin(view View, subviewID string) (time.Time, bool) {
 | 
			
		|||
 | 
			
		||||
// GetTimePickerMax returns the max time of TimePicker subview and "true" as the second value if the min time is set,
 | 
			
		||||
// "false" as the second value otherwise.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTimePickerMax(view View, subviewID string) (time.Time, bool) {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTimePickerMax(view View, subviewID ...string) (time.Time, bool) {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		return getTimeProperty(view, TimePickerMax, Max)
 | 
			
		||||
| 
						 | 
				
			
			@ -421,31 +378,16 @@ func GetTimePickerMax(view View, subviewID string) (time.Time, bool) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// GetTimePickerStep returns the time changing step in seconds of TimePicker subview.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTimePickerStep(view View, subviewID string) int {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view == nil {
 | 
			
		||||
		return 60
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result, ok := intStyledProperty(view, TimePickerStep, 60)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		result, _ = intStyledProperty(view, Step, 60)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if result < 0 {
 | 
			
		||||
		return 60
 | 
			
		||||
	}
 | 
			
		||||
	return result
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTimePickerStep(view View, subviewID ...string) int {
 | 
			
		||||
	return intStyledProperty(view, subviewID, TimePickerStep, 60)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetTimePickerValue returns the time of TimePicker subview.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTimePickerValue(view View, subviewID string) time.Time {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTimePickerValue(view View, subviewID ...string) time.Time {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
	if view == nil {
 | 
			
		||||
		return time.Now()
 | 
			
		||||
| 
						 | 
				
			
			@ -456,17 +398,7 @@ func GetTimePickerValue(view View, subviewID string) time.Time {
 | 
			
		|||
 | 
			
		||||
// GetTimeChangedListeners returns the TimeChangedListener list of an TimePicker subview.
 | 
			
		||||
// If there are no listeners then the empty list is returned
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTimeChangedListeners(view View, subviewID string) []func(TimePicker, time.Time) {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if value := view.Get(TimeChangedEvent); value != nil {
 | 
			
		||||
			if listeners, ok := value.([]func(TimePicker, time.Time)); ok {
 | 
			
		||||
				return listeners
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return []func(TimePicker, time.Time){}
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTimeChangedListeners(view View, subviewID ...string) []func(TimePicker, time.Time) {
 | 
			
		||||
	return getEventListeners[TimePicker, time.Time](view, subviewID, TimeChangedEvent)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										169
									
								
								touchEvents.go
								
								
								
								
							
							
						
						
									
										169
									
								
								touchEvents.go
								
								
								
								
							| 
						 | 
				
			
			@ -90,131 +90,6 @@ type TouchEvent struct {
 | 
			
		|||
	MetaKey bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func valueToTouchListeners(value interface{}) ([]func(View, TouchEvent), bool) {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		return nil, true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch value := value.(type) {
 | 
			
		||||
	case func(View, TouchEvent):
 | 
			
		||||
		return []func(View, TouchEvent){value}, true
 | 
			
		||||
 | 
			
		||||
	case func(TouchEvent):
 | 
			
		||||
		fn := func(_ View, event TouchEvent) {
 | 
			
		||||
			value(event)
 | 
			
		||||
		}
 | 
			
		||||
		return []func(View, TouchEvent){fn}, true
 | 
			
		||||
 | 
			
		||||
	case func(View):
 | 
			
		||||
		fn := func(view View, _ TouchEvent) {
 | 
			
		||||
			value(view)
 | 
			
		||||
		}
 | 
			
		||||
		return []func(View, TouchEvent){fn}, true
 | 
			
		||||
 | 
			
		||||
	case func():
 | 
			
		||||
		fn := func(View, TouchEvent) {
 | 
			
		||||
			value()
 | 
			
		||||
		}
 | 
			
		||||
		return []func(View, TouchEvent){fn}, true
 | 
			
		||||
 | 
			
		||||
	case []func(View, TouchEvent):
 | 
			
		||||
		if len(value) == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		for _, fn := range value {
 | 
			
		||||
			if fn == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return value, true
 | 
			
		||||
 | 
			
		||||
	case []func(TouchEvent):
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		listeners := make([]func(View, TouchEvent), count)
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
			listeners[i] = func(_ View, event TouchEvent) {
 | 
			
		||||
				v(event)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners, true
 | 
			
		||||
 | 
			
		||||
	case []func(View):
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		listeners := make([]func(View, TouchEvent), count)
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
			listeners[i] = func(view View, _ TouchEvent) {
 | 
			
		||||
				v(view)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners, true
 | 
			
		||||
 | 
			
		||||
	case []func():
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		listeners := make([]func(View, TouchEvent), count)
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
			listeners[i] = func(View, TouchEvent) {
 | 
			
		||||
				v()
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners, true
 | 
			
		||||
 | 
			
		||||
	case []interface{}:
 | 
			
		||||
		count := len(value)
 | 
			
		||||
		if count == 0 {
 | 
			
		||||
			return nil, true
 | 
			
		||||
		}
 | 
			
		||||
		listeners := make([]func(View, TouchEvent), count)
 | 
			
		||||
		for i, v := range value {
 | 
			
		||||
			if v == nil {
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
			switch v := v.(type) {
 | 
			
		||||
			case func(View, TouchEvent):
 | 
			
		||||
				listeners[i] = v
 | 
			
		||||
 | 
			
		||||
			case func(TouchEvent):
 | 
			
		||||
				listeners[i] = func(_ View, event TouchEvent) {
 | 
			
		||||
					v(event)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			case func(View):
 | 
			
		||||
				listeners[i] = func(view View, _ TouchEvent) {
 | 
			
		||||
					v(view)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			case func():
 | 
			
		||||
				listeners[i] = func(View, TouchEvent) {
 | 
			
		||||
					v()
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			default:
 | 
			
		||||
				return nil, false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return listeners, true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var touchEvents = map[string]struct{ jsEvent, jsFunc string }{
 | 
			
		||||
	TouchStart:  {jsEvent: "ontouchstart", jsFunc: "touchStartEvent"},
 | 
			
		||||
	TouchEnd:    {jsEvent: "ontouchend", jsFunc: "touchEndEvent"},
 | 
			
		||||
| 
						 | 
				
			
			@ -222,8 +97,8 @@ var touchEvents = map[string]struct{ jsEvent, jsFunc string }{
 | 
			
		|||
	TouchCancel: {jsEvent: "ontouchcancel", jsFunc: "touchCancelEvent"},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (view *viewData) setTouchListener(tag string, value interface{}) bool {
 | 
			
		||||
	listeners, ok := valueToTouchListeners(value)
 | 
			
		||||
func (view *viewData) setTouchListener(tag string, value any) bool {
 | 
			
		||||
	listeners, ok := valueToEventListeners[View, TouchEvent](value)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		notCompatibleType(tag, value)
 | 
			
		||||
		return false
 | 
			
		||||
| 
						 | 
				
			
			@ -251,20 +126,6 @@ func (view *viewData) removeTouchListener(tag string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getTouchListeners(view View, subviewID string, tag string) []func(View, TouchEvent) {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if value := view.Get(tag); value != nil {
 | 
			
		||||
			if result, ok := value.([]func(View, TouchEvent)); ok {
 | 
			
		||||
				return result
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return []func(View, TouchEvent){}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func touchEventsHtml(view View, buffer *strings.Builder) {
 | 
			
		||||
	for tag, js := range touchEvents {
 | 
			
		||||
		if value := view.getRaw(tag); value != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -309,7 +170,7 @@ func (event *TouchEvent) init(data DataObject) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func handleTouchEvents(view View, tag string, data DataObject) {
 | 
			
		||||
	listeners := getTouchListeners(view, "", tag)
 | 
			
		||||
	listeners := getEventListeners[View, TouchEvent](view, nil, tag)
 | 
			
		||||
	if len(listeners) == 0 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -323,25 +184,25 @@ func handleTouchEvents(view View, tag string, data DataObject) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// GetTouchStartListeners returns the "touch-start" listener list. If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTouchStartListeners(view View, subviewID string) []func(View, TouchEvent) {
 | 
			
		||||
	return getTouchListeners(view, subviewID, TouchStart)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTouchStartListeners(view View, subviewID ...string) []func(View, TouchEvent) {
 | 
			
		||||
	return getEventListeners[View, TouchEvent](view, subviewID, TouchStart)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetTouchEndListeners returns the "touch-end" listener list. If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTouchEndListeners(view View, subviewID string) []func(View, TouchEvent) {
 | 
			
		||||
	return getTouchListeners(view, subviewID, TouchEnd)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTouchEndListeners(view View, subviewID ...string) []func(View, TouchEvent) {
 | 
			
		||||
	return getEventListeners[View, TouchEvent](view, subviewID, TouchEnd)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetTouchMoveListeners returns the "touch-move" listener list. If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTouchMoveListeners(view View, subviewID string) []func(View, TouchEvent) {
 | 
			
		||||
	return getTouchListeners(view, subviewID, TouchMove)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTouchMoveListeners(view View, subviewID ...string) []func(View, TouchEvent) {
 | 
			
		||||
	return getEventListeners[View, TouchEvent](view, subviewID, TouchMove)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetTouchCancelListeners returns the "touch-cancel" listener list. If there are no listeners then the empty list is returned.
 | 
			
		||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTouchCancelListeners(view View, subviewID string) []func(View, TouchEvent) {
 | 
			
		||||
	return getTouchListeners(view, subviewID, TouchCancel)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
 | 
			
		||||
func GetTouchCancelListeners(view View, subviewID ...string) []func(View, TouchEvent) {
 | 
			
		||||
	return getEventListeners[View, TouchEvent](view, subviewID, TouchCancel)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,6 @@
 | 
			
		|||
package rui
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -30,7 +29,7 @@ type videoPlayerData struct {
 | 
			
		|||
// NewVideoPlayer create new MediaPlayer object and return it
 | 
			
		||||
func NewVideoPlayer(session Session, params Params) VideoPlayer {
 | 
			
		||||
	view := new(videoPlayerData)
 | 
			
		||||
	view.Init(session)
 | 
			
		||||
	view.init(session)
 | 
			
		||||
	view.tag = "VideoPlayer"
 | 
			
		||||
	setInitParams(view, params)
 | 
			
		||||
	return view
 | 
			
		||||
| 
						 | 
				
			
			@ -40,8 +39,8 @@ func newVideoPlayer(session Session) View {
 | 
			
		|||
	return NewVideoPlayer(session, nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (player *videoPlayerData) Init(session Session) {
 | 
			
		||||
	player.mediaPlayerData.Init(session)
 | 
			
		||||
func (player *videoPlayerData) init(session Session) {
 | 
			
		||||
	player.mediaPlayerData.init(session)
 | 
			
		||||
	player.tag = "VideoPlayer"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -77,11 +76,11 @@ func (player *videoPlayerData) remove(tag string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (player *videoPlayerData) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (player *videoPlayerData) Set(tag string, value any) bool {
 | 
			
		||||
	return player.set(strings.ToLower(tag), value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (player *videoPlayerData) set(tag string, value interface{}) bool {
 | 
			
		||||
func (player *videoPlayerData) set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		player.remove(tag)
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			@ -90,9 +89,9 @@ func (player *videoPlayerData) set(tag string, value interface{}) bool {
 | 
			
		|||
	if player.mediaPlayerData.set(tag, value) {
 | 
			
		||||
		session := player.Session()
 | 
			
		||||
		updateSize := func(cssTag string) {
 | 
			
		||||
			if size, ok := floatProperty(player, tag, session, 0); ok {
 | 
			
		||||
				if size > 0 {
 | 
			
		||||
					updateProperty(player.htmlID(), cssTag, fmt.Sprintf("%g", size), session)
 | 
			
		||||
			if size, ok := floatTextProperty(player, tag, session, 0); ok {
 | 
			
		||||
				if size != "0" {
 | 
			
		||||
					updateProperty(player.htmlID(), cssTag, size, session)
 | 
			
		||||
				} else {
 | 
			
		||||
					removeProperty(player.htmlID(), cssTag, session)
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			@ -122,12 +121,16 @@ func (player *videoPlayerData) htmlProperties(self View, buffer *strings.Builder
 | 
			
		|||
 | 
			
		||||
	session := player.Session()
 | 
			
		||||
 | 
			
		||||
	if size, ok := floatProperty(player, VideoWidth, session, 0); ok && size > 0 {
 | 
			
		||||
		buffer.WriteString(fmt.Sprintf(` width="%g"`, size))
 | 
			
		||||
	if size, ok := floatTextProperty(player, VideoWidth, session, 0); ok && size != "0" {
 | 
			
		||||
		buffer.WriteString(` width="`)
 | 
			
		||||
		buffer.WriteString(size)
 | 
			
		||||
		buffer.WriteString(`"`)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if size, ok := floatProperty(player, VideoHeight, session, 0); ok && size > 0 {
 | 
			
		||||
		buffer.WriteString(fmt.Sprintf(` height="%g"`, size))
 | 
			
		||||
	if size, ok := floatTextProperty(player, VideoHeight, session, 0); ok && size != "0" {
 | 
			
		||||
		buffer.WriteString(` height="`)
 | 
			
		||||
		buffer.WriteString(size)
 | 
			
		||||
		buffer.WriteString(`"`)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if url, ok := stringProperty(player, Poster, session); ok && url != "" {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										71
									
								
								view.go
								
								
								
								
							
							
						
						
									
										71
									
								
								view.go
								
								
								
								
							| 
						 | 
				
			
			@ -33,14 +33,10 @@ type View interface {
 | 
			
		|||
	ViewStyle
 | 
			
		||||
	fmt.Stringer
 | 
			
		||||
 | 
			
		||||
	// Init initializes fields of View by default values
 | 
			
		||||
	Init(session Session)
 | 
			
		||||
	// Session returns the current Session interface
 | 
			
		||||
	Session() Session
 | 
			
		||||
	// Parent returns the parent view
 | 
			
		||||
	Parent() View
 | 
			
		||||
	parentHTMLID() string
 | 
			
		||||
	setParentID(parentID string)
 | 
			
		||||
	// Tag returns the tag of View interface
 | 
			
		||||
	Tag() string
 | 
			
		||||
	// ID returns the id of the view
 | 
			
		||||
| 
						 | 
				
			
			@ -54,7 +50,7 @@ type View interface {
 | 
			
		|||
	// SetAnimated sets the value (second argument) of the property with name defined by the first argument.
 | 
			
		||||
	// Return "true" if the value has been set, in the opposite case "false" are returned and
 | 
			
		||||
	// a description of the error is written to the log
 | 
			
		||||
	SetAnimated(tag string, value interface{}, animation Animation) bool
 | 
			
		||||
	SetAnimated(tag string, value any, animation Animation) bool
 | 
			
		||||
	// SetChangeListener set the function to track the change of the View property
 | 
			
		||||
	SetChangeListener(tag string, listener func(View, string))
 | 
			
		||||
	// HasFocus returns 'true' if the view has focus
 | 
			
		||||
| 
						 | 
				
			
			@ -65,14 +61,14 @@ type View interface {
 | 
			
		|||
	htmlTag() string
 | 
			
		||||
	closeHTMLTag() bool
 | 
			
		||||
	htmlID() string
 | 
			
		||||
	parentHTMLID() string
 | 
			
		||||
	setParentID(parentID string)
 | 
			
		||||
	htmlSubviews(self View, buffer *strings.Builder)
 | 
			
		||||
	htmlProperties(self View, buffer *strings.Builder)
 | 
			
		||||
	htmlDisabledProperties(self View, buffer *strings.Builder)
 | 
			
		||||
	cssStyle(self View, builder cssBuilder)
 | 
			
		||||
	addToCSSStyle(addCSS map[string]string)
 | 
			
		||||
 | 
			
		||||
	getTransitions() Params
 | 
			
		||||
 | 
			
		||||
	onResize(self View, x, y, width, height float64)
 | 
			
		||||
	onItemResize(self View, index string, x, y, width, height float64)
 | 
			
		||||
	setNoResizeEvent()
 | 
			
		||||
| 
						 | 
				
			
			@ -102,7 +98,7 @@ type viewData struct {
 | 
			
		|||
 | 
			
		||||
func newView(session Session) View {
 | 
			
		||||
	view := new(viewData)
 | 
			
		||||
	view.Init(session)
 | 
			
		||||
	view.init(session)
 | 
			
		||||
	return view
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -124,12 +120,12 @@ func setInitParams(view View, params Params) {
 | 
			
		|||
// NewView create new View object and return it
 | 
			
		||||
func NewView(session Session, params Params) View {
 | 
			
		||||
	view := new(viewData)
 | 
			
		||||
	view.Init(session)
 | 
			
		||||
	view.init(session)
 | 
			
		||||
	setInitParams(view, params)
 | 
			
		||||
	return view
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (view *viewData) Init(session Session) {
 | 
			
		||||
func (view *viewData) init(session Session) {
 | 
			
		||||
	view.viewStyle.init()
 | 
			
		||||
	view.tag = "View"
 | 
			
		||||
	view.session = session
 | 
			
		||||
| 
						 | 
				
			
			@ -197,7 +193,7 @@ func (view *viewData) remove(tag string) {
 | 
			
		|||
	case Style, StyleDisabled:
 | 
			
		||||
		if _, ok := view.properties[tag]; ok {
 | 
			
		||||
			delete(view.properties, tag)
 | 
			
		||||
			updateProperty(view.htmlID(), "class", view.htmlClass(IsDisabled(view, "")), view.session)
 | 
			
		||||
			updateProperty(view.htmlID(), "class", view.htmlClass(IsDisabled(view)), view.session)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case FocusEvent, LostFocusEvent:
 | 
			
		||||
| 
						 | 
				
			
			@ -290,11 +286,11 @@ func (view *viewData) propertyChangedEvent(tag string) {
 | 
			
		|||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (view *viewData) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (view *viewData) Set(tag string, value any) bool {
 | 
			
		||||
	return view.set(strings.ToLower(tag), value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (view *viewData) set(tag string, value interface{}) bool {
 | 
			
		||||
func (view *viewData) set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		view.remove(tag)
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			@ -327,7 +323,7 @@ func (view *viewData) set(tag string, value interface{}) bool {
 | 
			
		|||
		}
 | 
			
		||||
		view.properties[tag] = text
 | 
			
		||||
		if view.created {
 | 
			
		||||
			updateProperty(view.htmlID(), "class", view.htmlClass(IsDisabled(view, "")), view.session)
 | 
			
		||||
			updateProperty(view.htmlID(), "class", view.htmlClass(IsDisabled(view)), view.session)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case FocusEvent, LostFocusEvent:
 | 
			
		||||
| 
						 | 
				
			
			@ -381,7 +377,7 @@ func viewPropertyChanged(view *viewData, tag string) {
 | 
			
		|||
		return
 | 
			
		||||
 | 
			
		||||
	case Visibility:
 | 
			
		||||
		switch GetVisibility(view, "") {
 | 
			
		||||
		switch GetVisibility(view) {
 | 
			
		||||
		case Invisible:
 | 
			
		||||
			updateCSSProperty(htmlID, Visibility, "hidden", session)
 | 
			
		||||
			updateCSSProperty(htmlID, "display", "", session)
 | 
			
		||||
| 
						 | 
				
			
			@ -450,7 +446,7 @@ func viewPropertyChanged(view *viewData, tag string) {
 | 
			
		|||
		return
 | 
			
		||||
 | 
			
		||||
	case Outline, OutlineColor, OutlineStyle, OutlineWidth:
 | 
			
		||||
		updateCSSProperty(htmlID, Outline, GetOutline(view, "").cssString(), session)
 | 
			
		||||
		updateCSSProperty(htmlID, Outline, GetOutline(view).cssString(session), session)
 | 
			
		||||
		return
 | 
			
		||||
 | 
			
		||||
	case Shadow:
 | 
			
		||||
| 
						 | 
				
			
			@ -465,20 +461,20 @@ func viewPropertyChanged(view *viewData, tag string) {
 | 
			
		|||
		RadiusTopRight, RadiusTopRightX, RadiusTopRightY,
 | 
			
		||||
		RadiusBottomLeft, RadiusBottomLeftX, RadiusBottomLeftY,
 | 
			
		||||
		RadiusBottomRight, RadiusBottomRightX, RadiusBottomRightY:
 | 
			
		||||
		radius := GetRadius(view, "")
 | 
			
		||||
		updateCSSProperty(htmlID, "border-radius", radius.cssString(), session)
 | 
			
		||||
		radius := GetRadius(view)
 | 
			
		||||
		updateCSSProperty(htmlID, "border-radius", radius.cssString(session), session)
 | 
			
		||||
		return
 | 
			
		||||
 | 
			
		||||
	case Margin, MarginTop, MarginRight, MarginBottom, MarginLeft,
 | 
			
		||||
		"top-margin", "right-margin", "bottom-margin", "left-margin":
 | 
			
		||||
		margin := GetMargin(view, "")
 | 
			
		||||
		updateCSSProperty(htmlID, Margin, margin.cssString(), session)
 | 
			
		||||
		margin := GetMargin(view)
 | 
			
		||||
		updateCSSProperty(htmlID, Margin, margin.cssString(session), session)
 | 
			
		||||
		return
 | 
			
		||||
 | 
			
		||||
	case Padding, PaddingTop, PaddingRight, PaddingBottom, PaddingLeft,
 | 
			
		||||
		"top-padding", "right-padding", "bottom-padding", "left-padding":
 | 
			
		||||
		padding := GetPadding(view, "")
 | 
			
		||||
		updateCSSProperty(htmlID, Padding, padding.cssString(), session)
 | 
			
		||||
		padding := GetPadding(view)
 | 
			
		||||
		updateCSSProperty(htmlID, Padding, padding.cssString(session), session)
 | 
			
		||||
		return
 | 
			
		||||
 | 
			
		||||
	case AvoidBreak:
 | 
			
		||||
| 
						 | 
				
			
			@ -593,9 +589,9 @@ func viewPropertyChanged(view *viewData, tag string) {
 | 
			
		|||
		}
 | 
			
		||||
		return
 | 
			
		||||
 | 
			
		||||
	case ZIndex:
 | 
			
		||||
		if i, ok := intProperty(view, ZIndex, session, 0); ok {
 | 
			
		||||
			updateCSSProperty(htmlID, ZIndex, strconv.Itoa(i), session)
 | 
			
		||||
	case ZIndex, TabSize:
 | 
			
		||||
		if i, ok := intProperty(view, tag, session, 0); ok {
 | 
			
		||||
			updateCSSProperty(htmlID, tag, strconv.Itoa(i), session)
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -630,7 +626,7 @@ func viewPropertyChanged(view *viewData, tag string) {
 | 
			
		|||
 | 
			
		||||
	if cssTag, ok := sizeProperties[tag]; ok {
 | 
			
		||||
		size, _ := sizeProperty(view, tag, session)
 | 
			
		||||
		updateCSSProperty(htmlID, cssTag, size.cssString(""), session)
 | 
			
		||||
		updateCSSProperty(htmlID, cssTag, size.cssString("", session), session)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -639,6 +635,7 @@ func viewPropertyChanged(view *viewData, tag string) {
 | 
			
		|||
		TextColor:       "color",
 | 
			
		||||
		TextLineColor:   "text-decoration-color",
 | 
			
		||||
		CaretColor:      CaretColor,
 | 
			
		||||
		AccentColor:     AccentColor,
 | 
			
		||||
	}
 | 
			
		||||
	if cssTag, ok := colorTags[tag]; ok {
 | 
			
		||||
		if color, ok := colorProperty(view, tag, session); ok {
 | 
			
		||||
| 
						 | 
				
			
			@ -657,19 +654,19 @@ func viewPropertyChanged(view *viewData, tag string) {
 | 
			
		|||
 | 
			
		||||
	for _, floatTag := range []string{Opacity, ScaleX, ScaleY, ScaleZ, RotateX, RotateY, RotateZ} {
 | 
			
		||||
		if tag == floatTag {
 | 
			
		||||
			if f, ok := floatProperty(view, floatTag, session, 0); ok {
 | 
			
		||||
				updateCSSProperty(htmlID, floatTag, strconv.FormatFloat(f, 'g', -1, 64), session)
 | 
			
		||||
			if f, ok := floatTextProperty(view, floatTag, session, 0); ok {
 | 
			
		||||
				updateCSSProperty(htmlID, floatTag, f, session)
 | 
			
		||||
			}
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (view *viewData) Get(tag string) interface{} {
 | 
			
		||||
func (view *viewData) Get(tag string) any {
 | 
			
		||||
	return view.get(strings.ToLower(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (view *viewData) get(tag string) interface{} {
 | 
			
		||||
func (view *viewData) get(tag string) any {
 | 
			
		||||
	if tag == ID {
 | 
			
		||||
		if view.viewID != "" {
 | 
			
		||||
			return view.viewID
 | 
			
		||||
| 
						 | 
				
			
			@ -681,7 +678,7 @@ func (view *viewData) get(tag string) interface{} {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (view *viewData) htmlTag() string {
 | 
			
		||||
	if semantics := GetSemantics(view, ""); semantics > DefaultSemantics {
 | 
			
		||||
	if semantics := GetSemantics(view); semantics > DefaultSemantics {
 | 
			
		||||
		values := enumProperties[Semantics].cssValues
 | 
			
		||||
		if semantics < len(values) {
 | 
			
		||||
			return values[semantics]
 | 
			
		||||
| 
						 | 
				
			
			@ -710,7 +707,7 @@ func (view *viewData) addToCSSStyle(addCSS map[string]string) {
 | 
			
		|||
 | 
			
		||||
func (view *viewData) cssStyle(self View, builder cssBuilder) {
 | 
			
		||||
	view.viewStyle.cssViewStyle(builder, view.session)
 | 
			
		||||
	switch GetVisibility(view, "") {
 | 
			
		||||
	switch GetVisibility(view) {
 | 
			
		||||
	case Invisible:
 | 
			
		||||
		builder.add(`visibility`, `hidden`)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -734,7 +731,7 @@ func (view *viewData) htmlProperties(self View, buffer *strings.Builder) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (view *viewData) htmlDisabledProperties(self View, buffer *strings.Builder) {
 | 
			
		||||
	if IsDisabled(self, "") {
 | 
			
		||||
	if IsDisabled(self) {
 | 
			
		||||
		buffer.WriteString(` data-disabled="1"`)
 | 
			
		||||
	} else {
 | 
			
		||||
		buffer.WriteString(` data-disabled="0"`)
 | 
			
		||||
| 
						 | 
				
			
			@ -749,7 +746,7 @@ func viewHTML(view View, buffer *strings.Builder) {
 | 
			
		|||
	buffer.WriteString(view.htmlID())
 | 
			
		||||
	buffer.WriteRune('"')
 | 
			
		||||
 | 
			
		||||
	disabled := IsDisabled(view, "")
 | 
			
		||||
	disabled := IsDisabled(view)
 | 
			
		||||
 | 
			
		||||
	if cls := view.htmlClass(disabled); cls != "" {
 | 
			
		||||
		buffer.WriteString(` class="`)
 | 
			
		||||
| 
						 | 
				
			
			@ -826,7 +823,7 @@ func (view *viewData) handleCommand(self View, command string, data DataObject)
 | 
			
		|||
	switch command {
 | 
			
		||||
 | 
			
		||||
	case KeyDownEvent, KeyUpEvent:
 | 
			
		||||
		if !IsDisabled(self, "") {
 | 
			
		||||
		if !IsDisabled(self) {
 | 
			
		||||
			handleKeyEvents(self, command, data)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -841,13 +838,13 @@ func (view *viewData) handleCommand(self View, command string, data DataObject)
 | 
			
		|||
 | 
			
		||||
	case FocusEvent:
 | 
			
		||||
		view.hasFocus = true
 | 
			
		||||
		for _, listener := range getFocusListeners(view, "", command) {
 | 
			
		||||
		for _, listener := range getFocusListeners(view, nil, command) {
 | 
			
		||||
			listener(self)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case LostFocusEvent:
 | 
			
		||||
		view.hasFocus = false
 | 
			
		||||
		for _, listener := range getFocusListeners(view, "", command) {
 | 
			
		||||
		for _, listener := range getFocusListeners(view, nil, command) {
 | 
			
		||||
			listener(self)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										76
									
								
								viewClip.go
								
								
								
								
							
							
						
						
									
										76
									
								
								viewClip.go
								
								
								
								
							| 
						 | 
				
			
			@ -27,7 +27,7 @@ type circleClip struct {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
type polygonClip struct {
 | 
			
		||||
	points []interface{}
 | 
			
		||||
	points []any
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InsetClip creates a rectangle View clipping area.
 | 
			
		||||
| 
						 | 
				
			
			@ -73,9 +73,9 @@ func EllipseClip(x, y, rx, ry SizeUnit) ClipShape {
 | 
			
		|||
// PolygonClip creates a polygon View clipping area.
 | 
			
		||||
// The elements of the function argument can be or text constants,
 | 
			
		||||
// or the text representation of SizeUnit, or elements of SizeUnit type.
 | 
			
		||||
func PolygonClip(points []interface{}) ClipShape {
 | 
			
		||||
func PolygonClip(points []any) ClipShape {
 | 
			
		||||
	clip := new(polygonClip)
 | 
			
		||||
	clip.points = []interface{}{}
 | 
			
		||||
	clip.points = []any{}
 | 
			
		||||
	if clip.Set(Points, points) {
 | 
			
		||||
		return clip
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -85,14 +85,14 @@ func PolygonClip(points []interface{}) ClipShape {
 | 
			
		|||
// PolygonPointsClip creates a polygon View clipping area.
 | 
			
		||||
func PolygonPointsClip(points []SizeUnit) ClipShape {
 | 
			
		||||
	clip := new(polygonClip)
 | 
			
		||||
	clip.points = []interface{}{}
 | 
			
		||||
	clip.points = []any{}
 | 
			
		||||
	if clip.Set(Points, points) {
 | 
			
		||||
		return clip
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (clip *insetClip) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (clip *insetClip) Set(tag string, value any) bool {
 | 
			
		||||
	switch strings.ToLower(tag) {
 | 
			
		||||
	case Top, Right, Bottom, Left:
 | 
			
		||||
		if value == nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -146,13 +146,13 @@ func (clip *insetClip) cssStyle(session Session) string {
 | 
			
		|||
	for _, tag := range []string{Top, Right, Bottom, Left} {
 | 
			
		||||
		value, _ := sizeProperty(clip, tag, session)
 | 
			
		||||
		buffer.WriteString(leadText)
 | 
			
		||||
		buffer.WriteString(value.cssString("0px"))
 | 
			
		||||
		buffer.WriteString(value.cssString("0px", session))
 | 
			
		||||
		leadText = " "
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if radius := getRadiusProperty(clip); radius != nil {
 | 
			
		||||
		buffer.WriteString(" round ")
 | 
			
		||||
		buffer.WriteString(radius.BoxRadius(session).cssString())
 | 
			
		||||
		buffer.WriteString(radius.BoxRadius(session).cssString(session))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	buffer.WriteRune(')')
 | 
			
		||||
| 
						 | 
				
			
			@ -168,7 +168,7 @@ func (clip *insetClip) valid(session Session) bool {
 | 
			
		|||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (clip *circleClip) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (clip *circleClip) Set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		clip.Remove(tag)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -211,15 +211,15 @@ func (clip *circleClip) cssStyle(session Session) string {
 | 
			
		|||
 | 
			
		||||
	buffer.WriteString("circle(")
 | 
			
		||||
	r, _ := sizeProperty(clip, Radius, session)
 | 
			
		||||
	buffer.WriteString(r.cssString("50%"))
 | 
			
		||||
	buffer.WriteString(r.cssString("50%", session))
 | 
			
		||||
 | 
			
		||||
	buffer.WriteString(" at ")
 | 
			
		||||
	x, _ := sizeProperty(clip, X, session)
 | 
			
		||||
	buffer.WriteString(x.cssString("50%"))
 | 
			
		||||
	buffer.WriteString(x.cssString("50%", session))
 | 
			
		||||
	buffer.WriteRune(' ')
 | 
			
		||||
 | 
			
		||||
	y, _ := sizeProperty(clip, Y, session)
 | 
			
		||||
	buffer.WriteString(y.cssString("50%"))
 | 
			
		||||
	buffer.WriteString(y.cssString("50%", session))
 | 
			
		||||
	buffer.WriteRune(')')
 | 
			
		||||
 | 
			
		||||
	return buffer.String()
 | 
			
		||||
| 
						 | 
				
			
			@ -232,7 +232,7 @@ func (clip *circleClip) valid(session Session) bool {
 | 
			
		|||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (clip *ellipseClip) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (clip *ellipseClip) Set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		clip.Remove(tag)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -280,17 +280,17 @@ func (clip *ellipseClip) cssStyle(session Session) string {
 | 
			
		|||
	rx, _ := sizeProperty(clip, RadiusX, session)
 | 
			
		||||
	ry, _ := sizeProperty(clip, RadiusX, session)
 | 
			
		||||
	buffer.WriteString("ellipse(")
 | 
			
		||||
	buffer.WriteString(rx.cssString("50%"))
 | 
			
		||||
	buffer.WriteString(rx.cssString("50%", session))
 | 
			
		||||
	buffer.WriteRune(' ')
 | 
			
		||||
	buffer.WriteString(ry.cssString("50%"))
 | 
			
		||||
	buffer.WriteString(ry.cssString("50%", session))
 | 
			
		||||
 | 
			
		||||
	buffer.WriteString(" at ")
 | 
			
		||||
	x, _ := sizeProperty(clip, X, session)
 | 
			
		||||
	buffer.WriteString(x.cssString("50%"))
 | 
			
		||||
	buffer.WriteString(x.cssString("50%", session))
 | 
			
		||||
	buffer.WriteRune(' ')
 | 
			
		||||
 | 
			
		||||
	y, _ := sizeProperty(clip, Y, session)
 | 
			
		||||
	buffer.WriteString(y.cssString("50%"))
 | 
			
		||||
	buffer.WriteString(y.cssString("50%", session))
 | 
			
		||||
	buffer.WriteRune(')')
 | 
			
		||||
 | 
			
		||||
	return buffer.String()
 | 
			
		||||
| 
						 | 
				
			
			@ -302,23 +302,23 @@ func (clip *ellipseClip) valid(session Session) bool {
 | 
			
		|||
	return rx.Value != 0 && ry.Value != 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (clip *polygonClip) Get(tag string) interface{} {
 | 
			
		||||
func (clip *polygonClip) Get(tag string) any {
 | 
			
		||||
	if Points == strings.ToLower(tag) {
 | 
			
		||||
		return clip.points
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (clip *polygonClip) getRaw(tag string) interface{} {
 | 
			
		||||
func (clip *polygonClip) getRaw(tag string) any {
 | 
			
		||||
	return clip.Get(tag)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (clip *polygonClip) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (clip *polygonClip) Set(tag string, value any) bool {
 | 
			
		||||
	if Points == strings.ToLower(tag) {
 | 
			
		||||
		switch value := value.(type) {
 | 
			
		||||
		case []interface{}:
 | 
			
		||||
		case []any:
 | 
			
		||||
			result := true
 | 
			
		||||
			clip.points = make([]interface{}, len(value))
 | 
			
		||||
			clip.points = make([]any, len(value))
 | 
			
		||||
			for i, val := range value {
 | 
			
		||||
				switch val := val.(type) {
 | 
			
		||||
				case string:
 | 
			
		||||
| 
						 | 
				
			
			@ -343,7 +343,7 @@ func (clip *polygonClip) Set(tag string, value interface{}) bool {
 | 
			
		|||
			return result
 | 
			
		||||
 | 
			
		||||
		case []SizeUnit:
 | 
			
		||||
			clip.points = make([]interface{}, len(value))
 | 
			
		||||
			clip.points = make([]any, len(value))
 | 
			
		||||
			for i, point := range value {
 | 
			
		||||
				clip.points[i] = point
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -352,7 +352,7 @@ func (clip *polygonClip) Set(tag string, value interface{}) bool {
 | 
			
		|||
		case string:
 | 
			
		||||
			result := true
 | 
			
		||||
			values := strings.Split(value, ",")
 | 
			
		||||
			clip.points = make([]interface{}, len(values))
 | 
			
		||||
			clip.points = make([]any, len(values))
 | 
			
		||||
			for i, val := range values {
 | 
			
		||||
				val = strings.Trim(val, " \t\n\r")
 | 
			
		||||
				if isConstantName(val) {
 | 
			
		||||
| 
						 | 
				
			
			@ -370,18 +370,18 @@ func (clip *polygonClip) Set(tag string, value interface{}) bool {
 | 
			
		|||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (clip *polygonClip) setRaw(tag string, value interface{}) {
 | 
			
		||||
func (clip *polygonClip) setRaw(tag string, value any) {
 | 
			
		||||
	clip.Set(tag, value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (clip *polygonClip) Remove(tag string) {
 | 
			
		||||
	if Points == strings.ToLower(tag) {
 | 
			
		||||
		clip.points = []interface{}{}
 | 
			
		||||
		clip.points = []any{}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (clip *polygonClip) Clear() {
 | 
			
		||||
	clip.points = []interface{}{}
 | 
			
		||||
	clip.points = []any{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (clip *polygonClip) AllTags() []string {
 | 
			
		||||
| 
						 | 
				
			
			@ -422,18 +422,18 @@ func (clip *polygonClip) cssStyle(session Session) string {
 | 
			
		|||
	buffer := allocStringBuilder()
 | 
			
		||||
	defer freeStringBuilder(buffer)
 | 
			
		||||
 | 
			
		||||
	writePoint := func(value interface{}) {
 | 
			
		||||
	writePoint := func(value any) {
 | 
			
		||||
		switch value := value.(type) {
 | 
			
		||||
		case string:
 | 
			
		||||
			if val, ok := session.resolveConstants(value); ok {
 | 
			
		||||
				if size, ok := StringToSizeUnit(val); ok {
 | 
			
		||||
					buffer.WriteString(size.cssString("0px"))
 | 
			
		||||
					buffer.WriteString(size.cssString("0px", session))
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		case SizeUnit:
 | 
			
		||||
			buffer.WriteString(value.cssString("0px"))
 | 
			
		||||
			buffer.WriteString(value.cssString("0px", session))
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -501,7 +501,7 @@ func parseClipShape(obj DataObject) ClipShape {
 | 
			
		|||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (style *viewStyle) setClipShape(tag string, value interface{}) bool {
 | 
			
		||||
func (style *viewStyle) setClipShape(tag string, value any) bool {
 | 
			
		||||
	switch value := value.(type) {
 | 
			
		||||
	case ClipShape:
 | 
			
		||||
		style.properties[tag] = value
 | 
			
		||||
| 
						 | 
				
			
			@ -558,10 +558,10 @@ func getClipShape(prop Properties, tag string, session Session) ClipShape {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// GetClip returns a View clipping area.
 | 
			
		||||
// If the second argument (subviewID) is "" then a top position of the first argument (view) is returned
 | 
			
		||||
func GetClip(view View, subviewID string) ClipShape {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a top position of the first argument (view) is returned
 | 
			
		||||
func GetClip(view View, subviewID ...string) ClipShape {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		return getClipShape(view, Clip, view.Session())
 | 
			
		||||
| 
						 | 
				
			
			@ -571,10 +571,10 @@ func GetClip(view View, subviewID string) ClipShape {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// GetShapeOutside returns a shape around which adjacent inline content.
 | 
			
		||||
// If the second argument (subviewID) is "" then a top position of the first argument (view) is returned
 | 
			
		||||
func GetShapeOutside(view View, subviewID string) ClipShape {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a top position of the first argument (view) is returned
 | 
			
		||||
func GetShapeOutside(view View, subviewID ...string) ClipShape {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		return getClipShape(view, ShapeOutside, view.Session())
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -130,7 +130,7 @@ func newViewFilter(obj DataObject) ViewFilter {
 | 
			
		|||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (filter *viewFilter) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (filter *viewFilter) Set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		filter.Remove(tag)
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			@ -180,20 +180,22 @@ func (filter *viewFilter) cssStyle(session Session) string {
 | 
			
		|||
	buffer := allocStringBuilder()
 | 
			
		||||
	defer freeStringBuilder(buffer)
 | 
			
		||||
 | 
			
		||||
	if value, ok := floatProperty(filter, Blur, session, 0); ok {
 | 
			
		||||
		size := SizeUnit{Type: SizeInPixel, Value: value}
 | 
			
		||||
	if value, ok := floatTextProperty(filter, Blur, session, 0); ok {
 | 
			
		||||
		buffer.WriteString(Blur)
 | 
			
		||||
		buffer.WriteRune('(')
 | 
			
		||||
		buffer.WriteString(size.cssString("0px"))
 | 
			
		||||
		buffer.WriteRune(')')
 | 
			
		||||
		buffer.WriteString(value)
 | 
			
		||||
		buffer.WriteString("px)")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tag := range []string{Brightness, Contrast, Saturate, Grayscale, Invert, Opacity, Sepia} {
 | 
			
		||||
		if value, ok := floatProperty(filter, tag, session, 0); ok {
 | 
			
		||||
		if value, ok := floatTextProperty(filter, tag, session, 0); ok {
 | 
			
		||||
			if buffer.Len() > 0 {
 | 
			
		||||
				buffer.WriteRune(' ')
 | 
			
		||||
			}
 | 
			
		||||
			buffer.WriteString(fmt.Sprintf("%s(%g%%)", tag, value))
 | 
			
		||||
			buffer.WriteString(tag)
 | 
			
		||||
			buffer.WriteRune('(')
 | 
			
		||||
			buffer.WriteString(value)
 | 
			
		||||
			buffer.WriteString("%)")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -224,7 +226,7 @@ func (filter *viewFilter) cssStyle(session Session) string {
 | 
			
		|||
	return buffer.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (style *viewStyle) setFilter(tag string, value interface{}) bool {
 | 
			
		||||
func (style *viewStyle) setFilter(tag string, value any) bool {
 | 
			
		||||
	switch value := value.(type) {
 | 
			
		||||
	case ViewFilter:
 | 
			
		||||
		style.properties[tag] = value
 | 
			
		||||
| 
						 | 
				
			
			@ -257,34 +259,46 @@ func (style *viewStyle) setFilter(tag string, value interface{}) bool {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// GetFilter returns a View graphical effects like blur or color shift.
 | 
			
		||||
// If the second argument (subviewID) is "" then a top position of the first argument (view) is returned
 | 
			
		||||
func GetFilter(view View, subviewID string) ViewFilter {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a top position of the first argument (view) is returned
 | 
			
		||||
func GetFilter(view View, subviewID ...string) ViewFilter {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if value := view.getRaw(Filter); value != nil {
 | 
			
		||||
			if filter, ok := value.(ViewFilter); ok {
 | 
			
		||||
				return filter
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if value := valueFromStyle(view, Filter); value != nil {
 | 
			
		||||
			if filter, ok := value.(ViewFilter); ok {
 | 
			
		||||
				return filter
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetBackdropFilter returns the area behind a View graphical effects like blur or color shift.
 | 
			
		||||
// If the second argument (subviewID) is "" then a top position of the first argument (view) is returned
 | 
			
		||||
func GetBackdropFilter(view View, subviewID string) ViewFilter {
 | 
			
		||||
	if subviewID != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID)
 | 
			
		||||
// If the second argument (subviewID) is not specified or it is "" then a top position of the first argument (view) is returned
 | 
			
		||||
func GetBackdropFilter(view View, subviewID ...string) ViewFilter {
 | 
			
		||||
	if len(subviewID) > 0 && subviewID[0] != "" {
 | 
			
		||||
		view = ViewByID(view, subviewID[0])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if view != nil {
 | 
			
		||||
		if value := view.getRaw(BackdropFilter); value != nil {
 | 
			
		||||
			if filter, ok := value.(ViewFilter); ok {
 | 
			
		||||
				return filter
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if value := valueFromStyle(view, BackdropFilter); value != nil {
 | 
			
		||||
			if filter, ok := value.(ViewFilter); ok {
 | 
			
		||||
				return filter
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										60
									
								
								viewStyle.go
								
								
								
								
							
							
						
						
									
										60
									
								
								viewStyle.go
								
								
								
								
							| 
						 | 
				
			
			@ -10,6 +10,16 @@ import (
 | 
			
		|||
// ViewStyle interface of the style of view
 | 
			
		||||
type ViewStyle interface {
 | 
			
		||||
	Properties
 | 
			
		||||
 | 
			
		||||
	// Transition returns the transition animation of the property. Returns nil is there is no transition animation.
 | 
			
		||||
	Transition(tag string) Animation
 | 
			
		||||
	// Transitions returns the map of transition animations. The result is always non-nil.
 | 
			
		||||
	Transitions() map[string]Animation
 | 
			
		||||
	// SetTransition sets the transition animation for the property if "animation" argument is not nil, and
 | 
			
		||||
	// removes the transition animation of the property if "animation" argument  is nil.
 | 
			
		||||
	// The "tag" argument is the property name.
 | 
			
		||||
	SetTransition(tag string, animation Animation)
 | 
			
		||||
 | 
			
		||||
	cssViewStyle(buffer cssBuilder, session Session)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -163,11 +173,11 @@ func (style *viewStyle) backgroundCSS(session Session) string {
 | 
			
		|||
func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) {
 | 
			
		||||
 | 
			
		||||
	if margin, ok := boundsProperty(style, Margin, session); ok {
 | 
			
		||||
		margin.cssValue(Margin, builder)
 | 
			
		||||
		margin.cssValue(Margin, builder, session)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if padding, ok := boundsProperty(style, Padding, session); ok {
 | 
			
		||||
		padding.cssValue(Padding, builder)
 | 
			
		||||
		padding.cssValue(Padding, builder, session)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if border := getBorder(style, Border); border != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -177,10 +187,10 @@ func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	radius := getRadius(style, session)
 | 
			
		||||
	radius.cssValue(builder)
 | 
			
		||||
	radius.cssValue(builder, session)
 | 
			
		||||
 | 
			
		||||
	if outline := getOutline(style); outline != nil {
 | 
			
		||||
		outline.ViewOutline(session).cssValue(builder)
 | 
			
		||||
		outline.ViewOutline(session).cssValue(builder, session)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if z, ok := intProperty(style, ZIndex, session, 0); ok {
 | 
			
		||||
| 
						 | 
				
			
			@ -198,14 +208,14 @@ func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) {
 | 
			
		|||
	for _, tag := range []string{
 | 
			
		||||
		Width, Height, MinWidth, MinHeight, MaxWidth, MaxHeight, Left, Right, Top, Bottom,
 | 
			
		||||
		TextSize, TextIndent, LetterSpacing, WordSpacing, LineHeight, TextLineThickness,
 | 
			
		||||
		GridRowGap, GridColumnGap, ColumnGap, ColumnWidth} {
 | 
			
		||||
		ListRowGap, ListColumnGap, GridRowGap, GridColumnGap, ColumnGap, ColumnWidth} {
 | 
			
		||||
 | 
			
		||||
		if size, ok := sizeProperty(style, tag, session); ok && size.Type != Auto {
 | 
			
		||||
			cssTag, ok := sizeProperties[tag]
 | 
			
		||||
			if !ok {
 | 
			
		||||
				cssTag = tag
 | 
			
		||||
			}
 | 
			
		||||
			builder.add(cssTag, size.cssString(""))
 | 
			
		||||
			builder.add(cssTag, size.cssString("", session))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -214,6 +224,7 @@ func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) {
 | 
			
		|||
		{TextColor, "color"},
 | 
			
		||||
		{TextLineColor, "text-decoration-color"},
 | 
			
		||||
		{CaretColor, CaretColor},
 | 
			
		||||
		{AccentColor, AccentColor},
 | 
			
		||||
	}
 | 
			
		||||
	for _, p := range colorProperties {
 | 
			
		||||
		if color, ok := colorProperty(style, p.property, session); ok && color != 0 {
 | 
			
		||||
| 
						 | 
				
			
			@ -235,7 +246,7 @@ func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) {
 | 
			
		|||
 | 
			
		||||
	writingMode := 0
 | 
			
		||||
	for _, tag := range []string{
 | 
			
		||||
		TextAlign, TextTransform, TextWeight, TextLineStyle, WritingMode, TextDirection,
 | 
			
		||||
		Overflow, TextAlign, TextTransform, TextWeight, TextLineStyle, WritingMode, TextDirection,
 | 
			
		||||
		VerticalTextOrientation, CellVerticalAlign, CellHorizontalAlign, GridAutoFlow, Cursor,
 | 
			
		||||
		WhiteSpace, WordBreak, TextOverflow, Float, TableVerticalAlign, Resize} {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -268,6 +279,10 @@ func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) {
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if tabSize, ok := intProperty(style, TabSize, session, 8); ok && tabSize > 0 {
 | 
			
		||||
		builder.add(TabSize, strconv.Itoa(tabSize))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if text := style.cssTextDecoration(session); text != "" {
 | 
			
		||||
		builder.add("text-decoration", text)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -384,12 +399,10 @@ func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	if r, ok := rangeProperty(style, Row, session); ok {
 | 
			
		||||
		builder.add("grid-row-start", strconv.Itoa(r.First+1))
 | 
			
		||||
		builder.add("grid-row-end", strconv.Itoa(r.Last+2))
 | 
			
		||||
		builder.add("grid-row", fmt.Sprintf("%d / %d", r.First+1, r.Last+2))
 | 
			
		||||
	}
 | 
			
		||||
	if r, ok := rangeProperty(style, Column, session); ok {
 | 
			
		||||
		builder.add("grid-column-start", strconv.Itoa(r.First+1))
 | 
			
		||||
		builder.add("grid-column-end", strconv.Itoa(r.Last+2))
 | 
			
		||||
		builder.add("grid-column", fmt.Sprintf("%d / %d", r.First+1, r.Last+2))
 | 
			
		||||
	}
 | 
			
		||||
	if text := style.gridCellSizesCSS(CellWidth, session); text != "" {
 | 
			
		||||
		builder.add(`grid-template-columns`, text)
 | 
			
		||||
| 
						 | 
				
			
			@ -442,7 +455,7 @@ func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func valueToOrientation(value interface{}, session Session) (int, bool) {
 | 
			
		||||
func valueToOrientation(value any, session Session) (int, bool) {
 | 
			
		||||
	if value != nil {
 | 
			
		||||
		switch value := value.(type) {
 | 
			
		||||
		case int:
 | 
			
		||||
| 
						 | 
				
			
			@ -471,11 +484,11 @@ func valueToOrientation(value interface{}, session Session) (int, bool) {
 | 
			
		|||
	return 0, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (style *viewStyle) Get(tag string) interface{} {
 | 
			
		||||
func (style *viewStyle) Get(tag string) any {
 | 
			
		||||
	return style.get(strings.ToLower(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (style *viewStyle) get(tag string) interface{} {
 | 
			
		||||
func (style *viewStyle) get(tag string) any {
 | 
			
		||||
	switch tag {
 | 
			
		||||
	case Border, CellBorder:
 | 
			
		||||
		return getBorder(&style.propertyList, tag)
 | 
			
		||||
| 
						 | 
				
			
			@ -539,7 +552,7 @@ func (style *viewStyle) AllTags() []string {
 | 
			
		|||
	return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func supportedPropertyValue(value interface{}) bool {
 | 
			
		||||
func supportedPropertyValue(value any) bool {
 | 
			
		||||
	switch value.(type) {
 | 
			
		||||
	case string:
 | 
			
		||||
	case []string:
 | 
			
		||||
| 
						 | 
				
			
			@ -551,7 +564,7 @@ func supportedPropertyValue(value interface{}) bool {
 | 
			
		|||
	case fmt.Stringer:
 | 
			
		||||
	case []ViewShadow:
 | 
			
		||||
	case []View:
 | 
			
		||||
	case []interface{}:
 | 
			
		||||
	case []any:
 | 
			
		||||
	case map[string]Animation:
 | 
			
		||||
	default:
 | 
			
		||||
		return false
 | 
			
		||||
| 
						 | 
				
			
			@ -559,7 +572,7 @@ func supportedPropertyValue(value interface{}) bool {
 | 
			
		|||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func writePropertyValue(buffer *strings.Builder, tag string, value interface{}, indent string) {
 | 
			
		||||
func writePropertyValue(buffer *strings.Builder, tag string, value any, indent string) {
 | 
			
		||||
 | 
			
		||||
	writeString := func(text string) {
 | 
			
		||||
		simple := (tag != Text && tag != Title && tag != Summary)
 | 
			
		||||
| 
						 | 
				
			
			@ -569,7 +582,8 @@ func writePropertyValue(buffer *strings.Builder, tag string, value interface{},
 | 
			
		|||
			} else {
 | 
			
		||||
				for _, ch := range text {
 | 
			
		||||
					if (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') ||
 | 
			
		||||
						ch == '+' || ch == '-' || ch == '@' || ch == '/' || ch == '_' || ch == ':' {
 | 
			
		||||
						ch == '+' || ch == '-' || ch == '@' || ch == '/' || ch == '_' || ch == ':' ||
 | 
			
		||||
						ch == '#' || ch == '%' || ch == 'π' || ch == '°' {
 | 
			
		||||
					} else {
 | 
			
		||||
						simple = false
 | 
			
		||||
						break
 | 
			
		||||
| 
						 | 
				
			
			@ -654,7 +668,7 @@ func writePropertyValue(buffer *strings.Builder, tag string, value interface{},
 | 
			
		|||
		value.writeString(buffer, indent+"\t")
 | 
			
		||||
 | 
			
		||||
	case fmt.Stringer:
 | 
			
		||||
		buffer.WriteString(value.String())
 | 
			
		||||
		writeString(value.String())
 | 
			
		||||
 | 
			
		||||
	case []ViewShadow:
 | 
			
		||||
		switch len(value) {
 | 
			
		||||
| 
						 | 
				
			
			@ -697,7 +711,7 @@ func writePropertyValue(buffer *strings.Builder, tag string, value interface{},
 | 
			
		|||
			buffer.WriteRune(']')
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case []interface{}:
 | 
			
		||||
	case []any:
 | 
			
		||||
		switch count := len(value); count {
 | 
			
		||||
		case 0:
 | 
			
		||||
			buffer.WriteString("[]")
 | 
			
		||||
| 
						 | 
				
			
			@ -741,7 +755,7 @@ func writePropertyValue(buffer *strings.Builder, tag string, value interface{},
 | 
			
		|||
				if animation := value[tag]; animation != nil {
 | 
			
		||||
					buffer.WriteString(indent2)
 | 
			
		||||
					animation.writeTransitionString(tag, buffer)
 | 
			
		||||
					buffer.WriteString("\n")
 | 
			
		||||
					buffer.WriteString(",\n")
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			buffer.WriteString(indent)
 | 
			
		||||
| 
						 | 
				
			
			@ -755,7 +769,7 @@ func writeViewStyle(name string, view ViewStyle, buffer *strings.Builder, indent
 | 
			
		|||
	buffer.WriteString(" {\n")
 | 
			
		||||
	indent += "\t"
 | 
			
		||||
 | 
			
		||||
	writeProperty := func(tag string, value interface{}) {
 | 
			
		||||
	writeProperty := func(tag string, value any) {
 | 
			
		||||
		if supportedPropertyValue(value) {
 | 
			
		||||
			buffer.WriteString(indent)
 | 
			
		||||
			buffer.WriteString(tag)
 | 
			
		||||
| 
						 | 
				
			
			@ -786,7 +800,7 @@ func writeViewStyle(name string, view ViewStyle, buffer *strings.Builder, indent
 | 
			
		|||
		Opacity, ZIndex, Width, Height, MinWidth, MinHeight, MaxWidth, MaxHeight,
 | 
			
		||||
		Margin, Padding, BackgroundClip, BackgroundColor, Background, Border, Radius, Outline, Shadow,
 | 
			
		||||
		Orientation, ListWrap, VerticalAlign, HorizontalAlign, CellWidth, CellHeight,
 | 
			
		||||
		CellVerticalAlign, CellHorizontalAlign, GridRowGap, GridColumnGap,
 | 
			
		||||
		CellVerticalAlign, CellHorizontalAlign, ListRowGap, ListColumnGap, GridRowGap, GridColumnGap,
 | 
			
		||||
		ColumnCount, ColumnWidth, ColumnSeparator, ColumnGap, AvoidBreak,
 | 
			
		||||
		Current, Expanded, Side, ResizeBorderWidth, EditViewType, MaxLength, Hint, Text, EditWrap,
 | 
			
		||||
		TextOverflow, FontName, TextSize, TextColor, TextWeight, Italic, SmallCaps,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@ import (
 | 
			
		|||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (style *viewStyle) setRange(tag string, value interface{}) bool {
 | 
			
		||||
func (style *viewStyle) setRange(tag string, value any) bool {
 | 
			
		||||
	switch value := value.(type) {
 | 
			
		||||
	case string:
 | 
			
		||||
		if strings.Contains(value, "@") {
 | 
			
		||||
| 
						 | 
				
			
			@ -31,7 +31,7 @@ func (style *viewStyle) setRange(tag string, value interface{}) bool {
 | 
			
		|||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (style *viewStyle) setBackground(value interface{}) bool {
 | 
			
		||||
func (style *viewStyle) setBackground(value any) bool {
 | 
			
		||||
	switch value := value.(type) {
 | 
			
		||||
	case BackgroundElement:
 | 
			
		||||
		style.properties[Background] = []BackgroundElement{value}
 | 
			
		||||
| 
						 | 
				
			
			@ -122,11 +122,11 @@ func (style *viewStyle) remove(tag string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (style *viewStyle) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (style *viewStyle) Set(tag string, value any) bool {
 | 
			
		||||
	return style.set(strings.ToLower(tag), value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (style *viewStyle) set(tag string, value interface{}) bool {
 | 
			
		||||
func (style *viewStyle) set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		style.remove(tag)
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,9 +1,5 @@
 | 
			
		|||
package rui
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// Perspective is the name of the SizeUnit property that determines the distance between the z = 0 plane
 | 
			
		||||
	// and the user in order to give a 3D-positioned element some perspective. Each 3D element
 | 
			
		||||
| 
						 | 
				
			
			@ -104,20 +100,6 @@ func getTranslate(style Properties, session Session) (SizeUnit, SizeUnit, SizeUn
 | 
			
		|||
	return x, y, z
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getScale(style Properties, session Session) (float64, float64, float64, bool) {
 | 
			
		||||
	scaleX, okX := floatProperty(style, ScaleX, session, 1)
 | 
			
		||||
	scaleY, okY := floatProperty(style, ScaleY, session, 1)
 | 
			
		||||
	scaleZ, okZ := floatProperty(style, ScaleZ, session, 1)
 | 
			
		||||
	return scaleX, scaleY, scaleZ, okX || okY || okZ
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getRotateVector(style Properties, session Session) (float64, float64, float64) {
 | 
			
		||||
	rotateX, _ := floatProperty(style, RotateX, session, 1)
 | 
			
		||||
	rotateY, _ := floatProperty(style, RotateY, session, 1)
 | 
			
		||||
	rotateZ, _ := floatProperty(style, RotateZ, session, 1)
 | 
			
		||||
	return rotateX, rotateY, rotateZ
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (style *viewStyle) transform(session Session) string {
 | 
			
		||||
 | 
			
		||||
	buffer := allocStringBuilder()
 | 
			
		||||
| 
						 | 
				
			
			@ -133,45 +115,52 @@ func (style *viewStyle) transform(session Session) string {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	x, y, z := getTranslate(style, session)
 | 
			
		||||
	scaleX, scaleY, scaleZ, scaleOK := getScale(style, session)
 | 
			
		||||
 | 
			
		||||
	scaleX, okScaleX := floatTextProperty(style, ScaleX, session, 1)
 | 
			
		||||
	scaleY, okScaleY := floatTextProperty(style, ScaleY, session, 1)
 | 
			
		||||
 | 
			
		||||
	if getTransform3D(style, session) {
 | 
			
		||||
		if x.Type != Auto || y.Type != Auto || z.Type != Auto {
 | 
			
		||||
			if buffer.Len() > 0 {
 | 
			
		||||
				buffer.WriteRune(' ')
 | 
			
		||||
			}
 | 
			
		||||
			buffer.WriteString(`translate3d(`)
 | 
			
		||||
			buffer.WriteString(x.cssString("0"))
 | 
			
		||||
			buffer.WriteString(x.cssString("0", session))
 | 
			
		||||
			buffer.WriteRune(',')
 | 
			
		||||
			buffer.WriteString(y.cssString("0"))
 | 
			
		||||
			buffer.WriteString(y.cssString("0", session))
 | 
			
		||||
			buffer.WriteRune(',')
 | 
			
		||||
			buffer.WriteString(z.cssString("0"))
 | 
			
		||||
			buffer.WriteString(z.cssString("0", session))
 | 
			
		||||
			buffer.WriteRune(')')
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if scaleOK {
 | 
			
		||||
		scaleZ, okScaleZ := floatTextProperty(style, ScaleZ, session, 1)
 | 
			
		||||
		if okScaleX || okScaleY || okScaleZ {
 | 
			
		||||
			if buffer.Len() > 0 {
 | 
			
		||||
				buffer.WriteRune(' ')
 | 
			
		||||
			}
 | 
			
		||||
			buffer.WriteString(`scale3d(`)
 | 
			
		||||
			buffer.WriteString(strconv.FormatFloat(scaleX, 'g', -1, 64))
 | 
			
		||||
			buffer.WriteString(scaleX)
 | 
			
		||||
			buffer.WriteRune(',')
 | 
			
		||||
			buffer.WriteString(strconv.FormatFloat(scaleY, 'g', -1, 64))
 | 
			
		||||
			buffer.WriteString(scaleY)
 | 
			
		||||
			buffer.WriteRune(',')
 | 
			
		||||
			buffer.WriteString(strconv.FormatFloat(scaleZ, 'g', -1, 64))
 | 
			
		||||
			buffer.WriteString(scaleZ)
 | 
			
		||||
			buffer.WriteRune(')')
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if angle, ok := angleProperty(style, Rotate, session); ok {
 | 
			
		||||
			rotateX, rotateY, rotateZ := getRotateVector(style, session)
 | 
			
		||||
			rotateX, _ := floatTextProperty(style, RotateX, session, 1)
 | 
			
		||||
			rotateY, _ := floatTextProperty(style, RotateY, session, 1)
 | 
			
		||||
			rotateZ, _ := floatTextProperty(style, RotateZ, session, 1)
 | 
			
		||||
 | 
			
		||||
			if buffer.Len() > 0 {
 | 
			
		||||
				buffer.WriteRune(' ')
 | 
			
		||||
			}
 | 
			
		||||
			buffer.WriteString(`rotate3d(`)
 | 
			
		||||
			buffer.WriteString(strconv.FormatFloat(rotateX, 'g', -1, 64))
 | 
			
		||||
			buffer.WriteString(rotateX)
 | 
			
		||||
			buffer.WriteRune(',')
 | 
			
		||||
			buffer.WriteString(strconv.FormatFloat(rotateY, 'g', -1, 64))
 | 
			
		||||
			buffer.WriteString(rotateY)
 | 
			
		||||
			buffer.WriteRune(',')
 | 
			
		||||
			buffer.WriteString(strconv.FormatFloat(rotateZ, 'g', -1, 64))
 | 
			
		||||
			buffer.WriteString(rotateZ)
 | 
			
		||||
			buffer.WriteRune(',')
 | 
			
		||||
			buffer.WriteString(angle.cssString())
 | 
			
		||||
			buffer.WriteRune(')')
 | 
			
		||||
| 
						 | 
				
			
			@ -183,20 +172,20 @@ func (style *viewStyle) transform(session Session) string {
 | 
			
		|||
				buffer.WriteRune(' ')
 | 
			
		||||
			}
 | 
			
		||||
			buffer.WriteString(`translate(`)
 | 
			
		||||
			buffer.WriteString(x.cssString("0"))
 | 
			
		||||
			buffer.WriteString(x.cssString("0", session))
 | 
			
		||||
			buffer.WriteRune(',')
 | 
			
		||||
			buffer.WriteString(y.cssString("0"))
 | 
			
		||||
			buffer.WriteString(y.cssString("0", session))
 | 
			
		||||
			buffer.WriteRune(')')
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if scaleOK {
 | 
			
		||||
		if okScaleX || okScaleY {
 | 
			
		||||
			if buffer.Len() > 0 {
 | 
			
		||||
				buffer.WriteRune(' ')
 | 
			
		||||
			}
 | 
			
		||||
			buffer.WriteString(`scale(`)
 | 
			
		||||
			buffer.WriteString(strconv.FormatFloat(scaleX, 'g', -1, 64))
 | 
			
		||||
			buffer.WriteString(scaleX)
 | 
			
		||||
			buffer.WriteRune(',')
 | 
			
		||||
			buffer.WriteString(strconv.FormatFloat(scaleY, 'g', -1, 64))
 | 
			
		||||
			buffer.WriteString(scaleY)
 | 
			
		||||
			buffer.WriteRune(')')
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -216,12 +205,12 @@ func (style *viewStyle) transform(session Session) string {
 | 
			
		|||
func (style *viewStyle) writeViewTransformCSS(builder cssBuilder, session Session) {
 | 
			
		||||
	if getTransform3D(style, session) {
 | 
			
		||||
		if perspective, ok := sizeProperty(style, Perspective, session); ok && perspective.Type != Auto && perspective.Value != 0 {
 | 
			
		||||
			builder.add(`perspective`, perspective.cssString("0"))
 | 
			
		||||
			builder.add(`perspective`, perspective.cssString("0", session))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		x, y := getPerspectiveOrigin(style, session)
 | 
			
		||||
		if x.Type != Auto || y.Type != Auto {
 | 
			
		||||
			builder.addValues(`perspective-origin`, ` `, x.cssString("50%"), y.cssString("50%"))
 | 
			
		||||
			builder.addValues(`perspective-origin`, ` `, x.cssString("50%", session), y.cssString("50%", session))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if backfaceVisible, ok := boolProperty(style, BackfaceVisible, session); ok {
 | 
			
		||||
| 
						 | 
				
			
			@ -234,12 +223,12 @@ func (style *viewStyle) writeViewTransformCSS(builder cssBuilder, session Sessio
 | 
			
		|||
 | 
			
		||||
		x, y, z := getOrigin(style, session)
 | 
			
		||||
		if x.Type != Auto || y.Type != Auto || z.Type != Auto {
 | 
			
		||||
			builder.addValues(`transform-origin`, ` `, x.cssString("50%"), y.cssString("50%"), z.cssString("0"))
 | 
			
		||||
			builder.addValues(`transform-origin`, ` `, x.cssString("50%", session), y.cssString("50%", session), z.cssString("0", session))
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		x, y, _ := getOrigin(style, session)
 | 
			
		||||
		if x.Type != Auto || y.Type != Auto {
 | 
			
		||||
			builder.addValues(`transform-origin`, ` `, x.cssString("50%"), y.cssString("50%"))
 | 
			
		||||
			builder.addValues(`transform-origin`, ` `, x.cssString("50%", session), y.cssString("50%", session))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -256,17 +245,17 @@ func (view *viewData) updateTransformProperty(tag string) bool {
 | 
			
		|||
 | 
			
		||||
	case PerspectiveOriginX, PerspectiveOriginY:
 | 
			
		||||
		if getTransform3D(view, session) {
 | 
			
		||||
			x, y := GetPerspectiveOrigin(view, "")
 | 
			
		||||
			x, y := GetPerspectiveOrigin(view)
 | 
			
		||||
			value := ""
 | 
			
		||||
			if x.Type != Auto || y.Type != Auto {
 | 
			
		||||
				value = x.cssString("50%") + " " + y.cssString("50%")
 | 
			
		||||
				value = x.cssString("50%", session) + " " + y.cssString("50%", session)
 | 
			
		||||
			}
 | 
			
		||||
			updateCSSProperty(htmlID, "perspective-origin", value, session)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case BackfaceVisible:
 | 
			
		||||
		if getTransform3D(view, session) {
 | 
			
		||||
			if GetBackfaceVisible(view, "") {
 | 
			
		||||
			if GetBackfaceVisible(view) {
 | 
			
		||||
				updateCSSProperty(htmlID, BackfaceVisible, "visible", session)
 | 
			
		||||
			} else {
 | 
			
		||||
				updateCSSProperty(htmlID, BackfaceVisible, "hidden", session)
 | 
			
		||||
| 
						 | 
				
			
			@ -278,11 +267,11 @@ func (view *viewData) updateTransformProperty(tag string) bool {
 | 
			
		|||
		value := ""
 | 
			
		||||
		if getTransform3D(view, session) {
 | 
			
		||||
			if x.Type != Auto || y.Type != Auto || z.Type != Auto {
 | 
			
		||||
				value = x.cssString("50%") + " " + y.cssString("50%") + " " + z.cssString("50%")
 | 
			
		||||
				value = x.cssString("50%", session) + " " + y.cssString("50%", session) + " " + z.cssString("50%", session)
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			if x.Type != Auto || y.Type != Auto {
 | 
			
		||||
				value = x.cssString("50%") + " " + y.cssString("50%")
 | 
			
		||||
				value = x.cssString("50%", session) + " " + y.cssString("50%", session)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		updateCSSProperty(htmlID, "transform-origin", value, session)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										1014
									
								
								viewUtils.go
								
								
								
								
							
							
						
						
									
										1014
									
								
								viewUtils.go
								
								
								
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
				
			
			@ -25,8 +25,8 @@ type viewsContainerData struct {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Init initialize fields of ViewsContainer by default values
 | 
			
		||||
func (container *viewsContainerData) Init(session Session) {
 | 
			
		||||
	container.viewData.Init(session)
 | 
			
		||||
func (container *viewsContainerData) init(session Session) {
 | 
			
		||||
	container.viewData.init(session)
 | 
			
		||||
	container.tag = "ViewsContainer"
 | 
			
		||||
	container.views = []View{}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -47,8 +47,7 @@ func (container *viewsContainerData) setParentID(parentID string) {
 | 
			
		|||
func (container *viewsContainerData) Views() []View {
 | 
			
		||||
	if container.views == nil {
 | 
			
		||||
		container.views = []View{}
 | 
			
		||||
	}
 | 
			
		||||
	if count := len(container.views); count > 0 {
 | 
			
		||||
	} else if count := len(container.views); count > 0 {
 | 
			
		||||
		views := make([]View, count)
 | 
			
		||||
		copy(views, container.views)
 | 
			
		||||
		return views
 | 
			
		||||
| 
						 | 
				
			
			@ -172,11 +171,11 @@ func (container *viewsContainerData) remove(tag string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (container *viewsContainerData) Set(tag string, value interface{}) bool {
 | 
			
		||||
func (container *viewsContainerData) Set(tag string, value any) bool {
 | 
			
		||||
	return container.set(strings.ToLower(tag), value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (container *viewsContainerData) set(tag string, value interface{}) bool {
 | 
			
		||||
func (container *viewsContainerData) set(tag string, value any) bool {
 | 
			
		||||
	if value == nil {
 | 
			
		||||
		container.remove(tag)
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			@ -187,9 +186,9 @@ func (container *viewsContainerData) set(tag string, value interface{}) bool {
 | 
			
		|||
		// do nothing
 | 
			
		||||
 | 
			
		||||
	case Disabled:
 | 
			
		||||
		oldDisabled := IsDisabled(container, "")
 | 
			
		||||
		oldDisabled := IsDisabled(container)
 | 
			
		||||
		if container.viewData.Set(Disabled, value) {
 | 
			
		||||
			disabled := IsDisabled(container, "")
 | 
			
		||||
			disabled := IsDisabled(container)
 | 
			
		||||
			if oldDisabled != disabled {
 | 
			
		||||
				if container.views != nil {
 | 
			
		||||
					for _, view := range container.views {
 | 
			
		||||
| 
						 | 
				
			
			@ -224,7 +223,7 @@ func (container *viewsContainerData) set(tag string, value interface{}) bool {
 | 
			
		|||
		}
 | 
			
		||||
		container.views = views
 | 
			
		||||
 | 
			
		||||
	case []interface{}:
 | 
			
		||||
	case []any:
 | 
			
		||||
		views := []View{}
 | 
			
		||||
		for _, v := range value {
 | 
			
		||||
			switch v := v.(type) {
 | 
			
		||||
| 
						 | 
				
			
			@ -279,11 +278,11 @@ func (container *viewsContainerData) set(tag string, value interface{}) bool {
 | 
			
		|||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (container *viewsContainerData) Get(tag string) interface{} {
 | 
			
		||||
func (container *viewsContainerData) Get(tag string) any {
 | 
			
		||||
	return container.get(strings.ToLower(tag))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (container *viewsContainerData) get(tag string) interface{} {
 | 
			
		||||
func (container *viewsContainerData) get(tag string) any {
 | 
			
		||||
	switch tag {
 | 
			
		||||
	case Content:
 | 
			
		||||
		return container.views
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue