forked from mbk-lab/rui_orig
2
0
Fork 0

Merge branch '0.9'

This commit is contained in:
Alexei Anoshenko 2022-10-06 11:56:34 +03:00
commit 3c4315ed78
81 changed files with 3794 additions and 4685 deletions

View File

@ -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 # v0.8.0
* Added "loaded-event" and "error-event" events to ImageView * Added "loaded-event" and "error-event" events to ImageView

File diff suppressed because it is too large Load Diff

472
README.md

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,7 @@ type absoluteLayoutData struct {
// NewAbsoluteLayout create new AbsoluteLayout object and return it // NewAbsoluteLayout create new AbsoluteLayout object and return it
func NewAbsoluteLayout(session Session, params Params) AbsoluteLayout { func NewAbsoluteLayout(session Session, params Params) AbsoluteLayout {
view := new(absoluteLayoutData) view := new(absoluteLayoutData)
view.Init(session) view.init(session)
setInitParams(view, params) setInitParams(view, params)
return view return view
} }
@ -24,8 +24,8 @@ func newAbsoluteLayout(session Session) View {
} }
// Init initialize fields of ViewsContainer by default values // Init initialize fields of ViewsContainer by default values
func (layout *absoluteLayoutData) Init(session Session) { func (layout *absoluteLayoutData) init(session Session) {
layout.viewsContainerData.Init(session) layout.viewsContainerData.init(session)
layout.tag = "AbsoluteLayout" layout.tag = "AbsoluteLayout"
layout.systemClass = "ruiAbsoluteLayout" layout.systemClass = "ruiAbsoluteLayout"
} }

View File

@ -55,16 +55,19 @@ const (
// The animation plays forwards each cycle. In other words, each time the animation cycles, // 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. // the animation will reset to the beginning state and start over again. This is the default value.
NormalAnimation = 0 NormalAnimation = 0
// ReverseAnimation is value of the "animation-direction" property. // ReverseAnimation is value of the "animation-direction" property.
// The animation plays backwards each cycle. In other words, each time the animation cycles, // 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 // the animation will reset to the end state and start over again. Animation steps are performed
// backwards, and timing functions are also reversed. // backwards, and timing functions are also reversed.
// For example, an "ease-in" timing function becomes "ease-out". // For example, an "ease-in" timing function becomes "ease-out".
ReverseAnimation = 1 ReverseAnimation = 1
// AlternateAnimation is value of the "animation-direction" property. // AlternateAnimation is value of the "animation-direction" property.
// The animation reverses direction each cycle, with the first iteration being played forwards. // 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. // The count to determine if a cycle is even or odd starts at one.
AlternateAnimation = 2 AlternateAnimation = 2
// AlternateReverseAnimation is value of the "animation-direction" property. // AlternateReverseAnimation is value of the "animation-direction" property.
// The animation reverses direction each cycle, with the first iteration being played backwards. // 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. // 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 is the name of the property
Tag string Tag string
// From is the initial value of the property // From is the initial value of the property
From interface{} From any
// To is the final value of the property // To is the final value of the property
To interface{} To any
// KeyFrames is intermediate property values // KeyFrames is intermediate property values
KeyFrames map[int]interface{} KeyFrames map[int]any
} }
type animationData struct { type animationData struct {
@ -184,7 +187,7 @@ func (animation *animationData) normalizeTag(tag string) string {
return tag return tag
} }
func (animation *animationData) Set(tag string, value interface{}) bool { func (animation *animationData) Set(tag string, value any) bool {
if value == nil { if value == nil {
animation.Remove(tag) animation.Remove(tag)
return true return true
@ -285,7 +288,7 @@ func (animation *animationData) Set(tag string, value interface{}) bool {
ErrorLogF(`key-frame "%d" is out of range`, n) ErrorLogF(`key-frame "%d" is out of range`, n)
} else { } else {
if result.KeyFrames == nil { if result.KeyFrames == nil {
result.KeyFrames = map[int]interface{}{n: node.Text()} result.KeyFrames = map[int]any{n: node.Text()}
} else { } else {
result.KeyFrames[n] = node.Text() result.KeyFrames[n] = node.Text()
} }
@ -359,7 +362,7 @@ func (animation *animationData) Remove(tag string) {
delete(animation.properties, animation.normalizeTag(tag)) 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)) return animation.getRaw(animation.normalizeTag(tag))
} }
@ -397,7 +400,7 @@ func (animation *animationData) animationCSS(session Session) string {
buffer.WriteString(animation.keyFramesName) 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)) buffer.WriteString(fmt.Sprintf(" %gs ", duration))
} else { } else {
buffer.WriteString(" 1s ") buffer.WriteString(" 1s ")
@ -405,7 +408,7 @@ func (animation *animationData) animationCSS(session Session) string {
buffer.WriteString(animation.timingFunctionCSS(session)) 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)) buffer.WriteString(fmt.Sprintf(" %gs", delay))
} else { } else {
buffer.WriteString(" 0s") buffer.WriteString(" 0s")
@ -435,7 +438,7 @@ func (animation *animationData) animationCSS(session Session) string {
func (animation *animationData) transitionCSS(buffer *strings.Builder, session Session) { 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)) buffer.WriteString(fmt.Sprintf(" %gs ", duration))
} else { } else {
buffer.WriteString(" 1s ") buffer.WriteString(" 1s ")
@ -443,7 +446,7 @@ func (animation *animationData) transitionCSS(buffer *strings.Builder, session S
buffer.WriteString(animation.timingFunctionCSS(session)) 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)) buffer.WriteString(fmt.Sprintf(" %gs", delay))
} }
} }
@ -481,6 +484,8 @@ func (animation *animationData) writeTransitionString(tag string, buffer *string
buffer.WriteRune('"') buffer.WriteRune('"')
buffer.WriteString(timingFunction) buffer.WriteString(timingFunction)
buffer.WriteRune('"') 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 { func (animation *animationData) timingFunctionCSS(session Session) string {
if timingFunction, ok := stringProperty(animation, TimingFunction, session); ok { 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 timingFunction
} }
} }
return ("ease") return ("ease")
} }
func validateTimingFunction(timingFunction string) bool { func isTimingFunctionValid(timingFunction string) bool {
switch timingFunction { switch timingFunction {
case "", EaseTiming, EaseInTiming, EaseOutTiming, EaseInOutTiming, LinearTiming: case "", EaseTiming, EaseInTiming, EaseOutTiming, EaseInOutTiming, LinearTiming:
return true return true
@ -529,6 +534,14 @@ func validateTimingFunction(timingFunction string) bool {
return false 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 { func (session *sessionData) registerAnimation(props []AnimatedProperty) string {
session.animationCounter++ session.animationCounter++
@ -602,7 +615,7 @@ func (session *sessionData) registerAnimation(props []AnimatedProperty) string {
return name 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 { if animation == nil {
return view.Set(tag, value) return view.Set(tag, value)
} }
@ -659,11 +672,29 @@ func (style *viewStyle) transitionCSS(session Session) string {
buffer := allocStringBuilder() buffer := allocStringBuilder()
defer freeStringBuilder(buffer) 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 { for tag, animation := range style.transitions {
if buffer.Len() > 0 { if buffer.Len() > 0 {
buffer.WriteString(", ") buffer.WriteString(", ")
} }
buffer.WriteString(tag)
if cssTag, ok := convert[tag]; ok {
buffer.WriteString(cssTag)
} else {
buffer.WriteString(tag)
}
animation.transitionCSS(buffer, session) animation.transitionCSS(buffer, session)
} }
return buffer.String() return buffer.String()
@ -673,18 +704,42 @@ func (view *viewData) updateTransitionCSS() {
updateCSSProperty(view.htmlID(), "transition", view.transitionCSS(view.Session()), view.Session()) updateCSSProperty(view.htmlID(), "transition", view.transitionCSS(view.Session()), view.Session())
} }
func (view *viewData) getTransitions() Params { func (style *viewStyle) Transition(tag string) Animation {
result := Params{} if style.transitions != nil {
for tag, animation := range view.transitions { 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 result[tag] = animation
} }
return result 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: // SetAnimated sets the property with name "tag" of the "rootView" subview with "viewID" id by value. Result:
// true - success, // true - success,
// false - error (incompatible type or invalid format of a string value, see AppLog). // 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 { if view := ViewByID(rootView, viewID); view != nil {
return view.SetAnimated(tag, value, animation) 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. // 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. // 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 { 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 != "" { if subviewID != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID)
} }
if view != nil { if view != nil {
if result, ok := boolStyledProperty(view, AnimationPaused); ok { return view.Transition(tag)
return result }
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 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. // 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 // 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 { func GetAnimation(view View, subviewID ...string) []Animation {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {

View File

@ -51,131 +51,6 @@ const (
AnimationIterationEvent = "animation-iteration-event" 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 }{ var transitionEvents = map[string]struct{ jsEvent, jsFunc string }{
TransitionRunEvent: {jsEvent: "ontransitionrun", jsFunc: "transitionRunEvent"}, TransitionRunEvent: {jsEvent: "ontransitionrun", jsFunc: "transitionRunEvent"},
TransitionStartEvent: {jsEvent: "ontransitionstart", jsFunc: "transitionStartEvent"}, TransitionStartEvent: {jsEvent: "ontransitionstart", jsFunc: "transitionStartEvent"},
@ -183,8 +58,8 @@ var transitionEvents = map[string]struct{ jsEvent, jsFunc string }{
TransitionCancelEvent: {jsEvent: "ontransitioncancel", jsFunc: "transitionCancelEvent"}, TransitionCancelEvent: {jsEvent: "ontransitioncancel", jsFunc: "transitionCancelEvent"},
} }
func (view *viewData) setTransitionListener(tag string, value interface{}) bool { func (view *viewData) setTransitionListener(tag string, value any) bool {
listeners, ok := valueToAnimationListeners(value) listeners, ok := valueToEventListeners[View, string](value)
if !ok { if !ok {
notCompatibleType(tag, value) notCompatibleType(tag, value)
return false 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) { func transitionEventsHtml(view View, buffer *strings.Builder) {
for tag, js := range transitionEvents { for tag, js := range transitionEvents {
if value := view.getRaw(tag); value != nil { 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) listener(view, property)
} }
} }
@ -263,8 +124,8 @@ var animationEvents = map[string]struct{ jsEvent, jsFunc string }{
AnimationCancelEvent: {jsEvent: "onanimationcancel", jsFunc: "animationCancelEvent"}, AnimationCancelEvent: {jsEvent: "onanimationcancel", jsFunc: "animationCancelEvent"},
} }
func (view *viewData) setAnimationListener(tag string, value interface{}) bool { func (view *viewData) setAnimationListener(tag string, value any) bool {
listeners, ok := valueToAnimationListeners(value) listeners, ok := valueToEventListeners[View, string](value)
if !ok { if !ok {
notCompatibleType(tag, value) notCompatibleType(tag, value)
return false return false
@ -303,10 +164,10 @@ func animationEventsHtml(view View, buffer *strings.Builder) {
} }
func (view *viewData) handleAnimationEvents(tag string, data DataObject) { 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 := "" id := ""
if name, ok := data.PropertyValue("name"); ok { if name, ok := data.PropertyValue("name"); ok {
for _, animation := range GetAnimation(view, "") { for _, animation := range GetAnimation(view) {
if name == animation.animationName() { if name == animation.animationName() {
id, _ = stringProperty(animation, ID, view.Session()) 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. // GetTransitionRunListeners returns the "transition-run-event" listener list.
// If there are no listeners then the empty list is returned. // 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. // 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) { func GetTransitionRunListeners(view View, subviewID ...string) []func(View, string) {
return getAnimationListeners(view, subviewID, TransitionRunEvent) return getEventListeners[View, string](view, subviewID, TransitionRunEvent)
} }
// GetTransitionStartListeners returns the "transition-start-event" listener list. // GetTransitionStartListeners returns the "transition-start-event" listener list.
// If there are no listeners then the empty list is returned. // 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. // 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) { func GetTransitionStartListeners(view View, subviewID ...string) []func(View, string) {
return getAnimationListeners(view, subviewID, TransitionStartEvent) return getEventListeners[View, string](view, subviewID, TransitionStartEvent)
} }
// GetTransitionEndListeners returns the "transition-end-event" listener list. // GetTransitionEndListeners returns the "transition-end-event" listener list.
// If there are no listeners then the empty list is returned. // 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. // 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) { func GetTransitionEndListeners(view View, subviewID ...string) []func(View, string) {
return getAnimationListeners(view, subviewID, TransitionEndEvent) return getEventListeners[View, string](view, subviewID, TransitionEndEvent)
} }
// GetTransitionCancelListeners returns the "transition-cancel-event" listener list. // GetTransitionCancelListeners returns the "transition-cancel-event" listener list.
// If there are no listeners then the empty list is returned. // 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. // 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) { func GetTransitionCancelListeners(view View, subviewID ...string) []func(View, string) {
return getAnimationListeners(view, subviewID, TransitionCancelEvent) return getEventListeners[View, string](view, subviewID, TransitionCancelEvent)
} }
// GetAnimationStartListeners returns the "animation-start-event" listener list. // GetAnimationStartListeners returns the "animation-start-event" listener list.
// If there are no listeners then the empty list is returned. // 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. // 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) { func GetAnimationStartListeners(view View, subviewID ...string) []func(View, string) {
return getAnimationListeners(view, subviewID, AnimationStartEvent) return getEventListeners[View, string](view, subviewID, AnimationStartEvent)
} }
// GetAnimationEndListeners returns the "animation-end-event" listener list. // GetAnimationEndListeners returns the "animation-end-event" listener list.
// If there are no listeners then the empty list is returned. // 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. // 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) { func GetAnimationEndListeners(view View, subviewID ...string) []func(View, string) {
return getAnimationListeners(view, subviewID, AnimationEndEvent) return getEventListeners[View, string](view, subviewID, AnimationEndEvent)
} }
// GetAnimationCancelListeners returns the "animation-cancel-event" listener list. // GetAnimationCancelListeners returns the "animation-cancel-event" listener list.
// If there are no listeners then the empty list is returned. // 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. // 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) { func GetAnimationCancelListeners(view View, subviewID ...string) []func(View, string) {
return getAnimationListeners(view, subviewID, AnimationCancelEvent) return getEventListeners[View, string](view, subviewID, AnimationCancelEvent)
} }
// GetAnimationIterationListeners returns the "animation-iteration-event" listener list. // GetAnimationIterationListeners returns the "animation-iteration-event" listener list.
// If there are no listeners then the empty list is returned. // 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. // 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) { func GetAnimationIterationListeners(view View, subviewID ...string) []func(View, string) {
return getAnimationListeners(view, subviewID, AnimationIterationEvent) return getEventListeners[View, string](view, subviewID, AnimationIterationEvent)
} }

View File

@ -39,7 +39,7 @@ func DebugLog(text string) {
} }
// DebugLogF print the text to the debug log // DebugLogF print the text to the debug log
func DebugLogF(format string, a ...interface{}) { func DebugLogF(format string, a ...any) {
if debugLogFunc != nil { if debugLogFunc != nil {
debugLogFunc(fmt.Sprintf(format, a...)) debugLogFunc(fmt.Sprintf(format, a...))
} }
@ -57,7 +57,7 @@ func ErrorLog(text string) {
} }
// ErrorLogF print the text to the error log // 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...) lastError = fmt.Sprintf(format, a...)
if errorLogFunc != nil { if errorLogFunc != nil {
errorLogFunc(lastError) errorLogFunc(lastError)

View File

@ -1793,6 +1793,7 @@ function imageLoaded(element, event) {
",natural-height=" + element.naturalHeight + ",natural-height=" + element.naturalHeight +
",current-src=\"" + element.currentSrc + "\"}"; ",current-src=\"" + element.currentSrc + "\"}";
sendMessage(message); sendMessage(message);
scanElementsSize()
} }
function imageError(element, event) { function imageError(element, event) {

View File

@ -59,7 +59,6 @@ ul:focus {
outline: none; outline: none;
} }
.ruiRoot { .ruiRoot {
position: absolute; position: absolute;
top: 0px; top: 0px;

View File

@ -11,7 +11,7 @@ type audioPlayerData struct {
// NewAudioPlayer create new MediaPlayer object and return it // NewAudioPlayer create new MediaPlayer object and return it
func NewAudioPlayer(session Session, params Params) AudioPlayer { func NewAudioPlayer(session Session, params Params) AudioPlayer {
view := new(audioPlayerData) view := new(audioPlayerData)
view.Init(session) view.init(session)
view.tag = "AudioPlayer" view.tag = "AudioPlayer"
setInitParams(view, params) setInitParams(view, params)
return view return view
@ -21,8 +21,8 @@ func newAudioPlayer(session Session) View {
return NewAudioPlayer(session, nil) return NewAudioPlayer(session, nil)
} }
func (player *audioPlayerData) Init(session Session) { func (player *audioPlayerData) init(session Session) {
player.mediaPlayerData.Init(session) player.mediaPlayerData.init(session)
player.tag = "AudioPlayer" player.tag = "AudioPlayer"
} }

View File

@ -81,22 +81,22 @@ func createBackground(obj DataObject) BackgroundElement {
switch obj.Tag() { switch obj.Tag() {
case "image": case "image":
image := new(backgroundImage) image := new(backgroundImage)
image.properties = map[string]interface{}{} image.properties = map[string]any{}
result = image result = image
case "linear-gradient": case "linear-gradient":
gradient := new(backgroundLinearGradient) gradient := new(backgroundLinearGradient)
gradient.properties = map[string]interface{}{} gradient.properties = map[string]any{}
result = gradient result = gradient
case "radial-gradient": case "radial-gradient":
gradient := new(backgroundRadialGradient) gradient := new(backgroundRadialGradient)
gradient.properties = map[string]interface{}{} gradient.properties = map[string]any{}
result = gradient result = gradient
case "conic-gradient": case "conic-gradient":
gradient := new(backgroundConicGradient) gradient := new(backgroundConicGradient)
gradient.properties = map[string]interface{}{} gradient.properties = map[string]any{}
result = gradient result = gradient
default: default:
@ -118,7 +118,7 @@ func createBackground(obj DataObject) BackgroundElement {
// NewBackgroundImage creates the new background image // NewBackgroundImage creates the new background image
func NewBackgroundImage(params Params) BackgroundElement { func NewBackgroundImage(params Params) BackgroundElement {
result := new(backgroundImage) result := new(backgroundImage)
result.properties = map[string]interface{}{} result.properties = map[string]any{}
for tag, value := range params { for tag, value := range params {
result.Set(tag, value) result.Set(tag, value)
} }
@ -156,7 +156,7 @@ func (image *backgroundImage) normalizeTag(tag string) string {
return tag return tag
} }
func (image *backgroundImage) Set(tag string, value interface{}) bool { func (image *backgroundImage) Set(tag string, value any) bool {
tag = image.normalizeTag(tag) tag = image.normalizeTag(tag)
switch tag { switch tag {
case Attachment, Width, Height, Repeat, ImageHorizontalAlign, ImageVerticalAlign, case Attachment, Width, Height, Repeat, ImageHorizontalAlign, ImageVerticalAlign,
@ -167,7 +167,7 @@ func (image *backgroundImage) Set(tag string, value interface{}) bool {
return false return false
} }
func (image *backgroundImage) Get(tag string) interface{} { func (image *backgroundImage) Get(tag string) any {
return image.backgroundElement.Get(image.normalizeTag(tag)) 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 { if width.Type != Auto || height.Type != Auto {
buffer.WriteString(` / `) buffer.WriteString(` / `)
buffer.WriteString(width.cssString("auto")) buffer.WriteString(width.cssString("auto", session))
buffer.WriteRune(' ') buffer.WriteRune(' ')
buffer.WriteString(height.cssString("auto")) buffer.WriteString(height.cssString("auto", session))
} }
} }

View File

@ -12,16 +12,16 @@ type backgroundConicGradient struct {
type BackgroundGradientAngle struct { type BackgroundGradientAngle struct {
// Color - the color of the key angle. Must not be nil. // 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) // 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). // 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) // 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 // NewBackgroundConicGradient creates the new background conic gradient
func NewBackgroundConicGradient(params Params) BackgroundElement { func NewBackgroundConicGradient(params Params) BackgroundElement {
result := new(backgroundConicGradient) result := new(backgroundConicGradient)
result.properties = map[string]interface{}{} result.properties = map[string]any{}
for tag, value := range params { for tag, value := range params {
result.Set(tag, value) result.Set(tag, value)
} }
@ -139,7 +139,7 @@ func (gradient *backgroundConicGradient) normalizeTag(tag string) string {
return tag return tag
} }
func (gradient *backgroundConicGradient) Set(tag string, value interface{}) bool { func (gradient *backgroundConicGradient) Set(tag string, value any) bool {
tag = gradient.normalizeTag(tag) tag = gradient.normalizeTag(tag)
switch tag { switch tag {
case CenterX, CenterY, Repeating, From: case CenterX, CenterY, Repeating, From:
@ -153,7 +153,7 @@ func (gradient *backgroundConicGradient) Set(tag string, value interface{}) bool
return false return false
} }
func (gradient *backgroundConicGradient) stringToAngle(text string) (interface{}, bool) { func (gradient *backgroundConicGradient) stringToAngle(text string) (any, bool) {
if text == "" { if text == "" {
return nil, false return nil, false
} else if text[0] == '@' { } else if text[0] == '@' {
@ -216,7 +216,7 @@ func (gradient *backgroundConicGradient) parseGradientText(value string) []Backg
return vector return vector
} }
func (gradient *backgroundConicGradient) setGradient(value interface{}) bool { func (gradient *backgroundConicGradient) setGradient(value any) bool {
if value == nil { if value == nil {
delete(gradient.properties, Gradient) delete(gradient.properties, Gradient)
return true return true
@ -262,7 +262,7 @@ func (gradient *backgroundConicGradient) setGradient(value interface{}) bool {
return false return false
} }
func (gradient *backgroundConicGradient) Get(tag string) interface{} { func (gradient *backgroundConicGradient) Get(tag string) any {
return gradient.backgroundElement.Get(gradient.normalizeTag(tag)) return gradient.backgroundElement.Get(gradient.normalizeTag(tag))
} }
@ -316,9 +316,9 @@ func (gradient *backgroundConicGradient) cssStyle(session Session) string {
buffer.WriteRune(' ') buffer.WriteRune(' ')
} }
buffer.WriteString("at ") buffer.WriteString("at ")
buffer.WriteString(x.cssString("50%")) buffer.WriteString(x.cssString("50%", session))
buffer.WriteString(" ") buffer.WriteString(" ")
buffer.WriteString(y.cssString("50%")) buffer.WriteString(y.cssString("50%", session))
comma = true comma = true
} }

View File

@ -50,10 +50,10 @@ const (
type BackgroundGradientPoint struct { type BackgroundGradientPoint struct {
// Color - the color of the point. Must not be nil. // 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) // 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). // 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) // Can take a value of SizeUnit type or string (angle constant or textual description of the SizeUnit)
Pos interface{} Pos any
} }
type backgroundGradient struct { type backgroundGradient struct {
@ -71,7 +71,7 @@ type backgroundRadialGradient struct {
// NewBackgroundLinearGradient creates the new background linear gradient // NewBackgroundLinearGradient creates the new background linear gradient
func NewBackgroundLinearGradient(params Params) BackgroundElement { func NewBackgroundLinearGradient(params Params) BackgroundElement {
result := new(backgroundLinearGradient) result := new(backgroundLinearGradient)
result.properties = map[string]interface{}{} result.properties = map[string]any{}
for tag, value := range params { for tag, value := range params {
result.Set(tag, value) result.Set(tag, value)
} }
@ -81,7 +81,7 @@ func NewBackgroundLinearGradient(params Params) BackgroundElement {
// NewBackgroundRadialGradient creates the new background radial gradient // NewBackgroundRadialGradient creates the new background radial gradient
func NewBackgroundRadialGradient(params Params) BackgroundElement { func NewBackgroundRadialGradient(params Params) BackgroundElement {
result := new(backgroundRadialGradient) result := new(backgroundRadialGradient)
result.properties = map[string]interface{}{} result.properties = map[string]any{}
for tag, value := range params { for tag, value := range params {
result.Set(tag, value) result.Set(tag, value)
} }
@ -106,7 +106,7 @@ func (gradient *backgroundGradient) parseGradientText(value string) []Background
return points 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 { switch tag = strings.ToLower(tag); tag {
case Repeating: case Repeating:
@ -261,19 +261,18 @@ func (gradient *backgroundGradient) writeGradient(session Session, buffer *strin
switch value := point.Pos.(type) { switch value := point.Pos.(type) {
case string: case string:
if value != "" { if value != "" {
if value[0] == '@' { if value, ok := session.resolveConstants(value); ok {
value, _ = session.Constant(value[1:]) if pos, ok := StringToSizeUnit(value); ok && pos.Type != Auto {
} buffer.WriteRune(' ')
if pos, ok := StringToSizeUnit(value); ok && pos.Type != Auto { buffer.WriteString(pos.cssString("", session))
buffer.WriteRune(' ') }
buffer.WriteString(pos.cssString(""))
} }
} }
case SizeUnit: case SizeUnit:
if value.Type != Auto { if value.Type != Auto {
buffer.WriteRune(' ') buffer.WriteRune(' ')
buffer.WriteString(value.cssString("")) buffer.WriteString(value.cssString("", session))
} }
} }
} }
@ -296,7 +295,7 @@ func (image *backgroundLinearGradient) Clone() BackgroundElement {
return result 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 { if strings.ToLower(tag) == Direction {
switch value := value.(type) { switch value := value.(type) {
case AngleUnit: case AngleUnit:
@ -402,7 +401,7 @@ func (gradient *backgroundRadialGradient) normalizeTag(tag string) string {
return tag return tag
} }
func (gradient *backgroundRadialGradient) Set(tag string, value interface{}) bool { func (gradient *backgroundRadialGradient) Set(tag string, value any) bool {
tag = gradient.normalizeTag(tag) tag = gradient.normalizeTag(tag)
switch tag { switch tag {
case RadialGradientRadius: case RadialGradientRadius:
@ -426,7 +425,7 @@ func (gradient *backgroundRadialGradient) Set(tag string, value interface{}) boo
return true return true
} }
case []interface{}: case []any:
switch len(value) { switch len(value) {
case 0: case 0:
delete(gradient.properties, RadialGradientRadius) delete(gradient.properties, RadialGradientRadius)
@ -477,7 +476,7 @@ func (gradient *backgroundRadialGradient) Set(tag string, value interface{}) boo
return gradient.backgroundGradient.Set(tag, value) 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)) 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 { if r, ok := StringToSizeUnit(text); ok && r.Type != Auto {
buffer.WriteString("ellipse ") buffer.WriteString("ellipse ")
shapeText = "" shapeText = ""
buffer.WriteString(r.cssString("")) buffer.WriteString(r.cssString("", session))
buffer.WriteString(" ") buffer.WriteString(" ")
buffer.WriteString(r.cssString("")) buffer.WriteString(r.cssString("", session))
buffer.WriteString(" ") buffer.WriteString(" ")
} else { } else {
ErrorLog(`Invalid radial gradient radius: ` + text) ErrorLog(`Invalid radial gradient radius: ` + text)
@ -539,9 +538,9 @@ func (gradient *backgroundRadialGradient) cssStyle(session Session) string {
if value.Type != Auto { if value.Type != Auto {
buffer.WriteString("ellipse ") buffer.WriteString("ellipse ")
shapeText = "" shapeText = ""
buffer.WriteString(value.cssString("")) buffer.WriteString(value.cssString("", session))
buffer.WriteString(" ") buffer.WriteString(" ")
buffer.WriteString(value.cssString("")) buffer.WriteString(value.cssString("", session))
buffer.WriteString(" ") buffer.WriteString(" ")
} }
@ -553,11 +552,11 @@ func (gradient *backgroundRadialGradient) cssStyle(session Session) string {
buffer.WriteString("ellipse ") buffer.WriteString("ellipse ")
shapeText = "" shapeText = ""
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
buffer.WriteString(value[i].cssString("50%")) buffer.WriteString(value[i].cssString("50%", session))
buffer.WriteString(" ") buffer.WriteString(" ")
} }
case []interface{}: case []any:
count := len(value) count := len(value)
if count > 2 { if count > 2 {
count = 2 count = 2
@ -568,13 +567,13 @@ func (gradient *backgroundRadialGradient) cssStyle(session Session) string {
if value[i] != nil { if value[i] != nil {
switch value := value[i].(type) { switch value := value[i].(type) {
case SizeUnit: case SizeUnit:
buffer.WriteString(value.cssString("50%")) buffer.WriteString(value.cssString("50%", session))
buffer.WriteString(" ") buffer.WriteString(" ")
case string: case string:
if text, ok := session.resolveConstants(value); ok { if text, ok := session.resolveConstants(value); ok {
if size, err := stringToSizeUnit(text); err == nil { if size, err := stringToSizeUnit(text); err == nil {
buffer.WriteString(size.cssString("50%")) buffer.WriteString(size.cssString("50%", session))
buffer.WriteString(" ") buffer.WriteString(" ")
} else { } else {
buffer.WriteString("50% ") buffer.WriteString("50% ")
@ -597,9 +596,9 @@ func (gradient *backgroundRadialGradient) cssStyle(session Session) string {
buffer.WriteString(shapeText) buffer.WriteString(shapeText)
} }
buffer.WriteString("at ") buffer.WriteString("at ")
buffer.WriteString(x.cssString("50%")) buffer.WriteString(x.cssString("50%", session))
buffer.WriteString(" ") buffer.WriteString(" ")
buffer.WriteString(y.cssString("50%")) buffer.WriteString(y.cssString("50%", session))
} }
buffer.WriteString(", ") buffer.WriteString(", ")

View File

@ -64,9 +64,9 @@ type borderProperty struct {
propertyList propertyList
} }
func newBorderProperty(value interface{}) BorderProperty { func newBorderProperty(value any) BorderProperty {
border := new(borderProperty) border := new(borderProperty)
border.properties = map[string]interface{}{} border.properties = map[string]any{}
if value != nil { if value != nil {
switch value := value.(type) { switch value := value.(type) {
@ -131,7 +131,7 @@ func newBorderProperty(value interface{}) BorderProperty {
// NewBorder creates the new BorderProperty // NewBorder creates the new BorderProperty
func NewBorder(params Params) BorderProperty { func NewBorder(params Params) BorderProperty {
border := new(borderProperty) border := new(borderProperty)
border.properties = map[string]interface{}{} border.properties = map[string]any{}
if params != nil { if params != nil {
for _, tag := range []string{Style, Width, ColorTag, Left, Right, Top, Bottom, for _, tag := range []string{Style, Width, ColorTag, Left, Right, Top, Bottom,
LeftStyle, RightStyle, TopStyle, BottomStyle, LeftStyle, RightStyle, TopStyle, BottomStyle,
@ -213,7 +213,7 @@ func (border *borderProperty) writeString(buffer *strings.Builder, indent string
buffer.WriteString("_{ ") buffer.WriteString("_{ ")
comma := false comma := false
write := func(tag string, value interface{}) { write := func(tag string, value any) {
if comma { if comma {
buffer.WriteString(", ") 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 { if value == nil {
border.Remove(tag) border.Remove(tag)
return true return true
@ -512,7 +512,7 @@ func (border *borderProperty) Set(tag string, value interface{}) bool {
return false return false
} }
func (border *borderProperty) Get(tag string) interface{} { func (border *borderProperty) Get(tag string) any {
tag = border.normalizeTag(tag) tag = border.normalizeTag(tag)
if result, ok := border.properties[tag]; ok { 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.Left.Width &&
borders.Top.Width == borders.Bottom.Width { borders.Top.Width == borders.Bottom.Width {
if borders.Top.Width.Type != Auto { 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 { } else {
builder.addValues("border-width", " ", borders.Top.Width.cssString("0"), builder.addValues("border-width", " ",
borders.Right.Width.cssString("0"), borders.Bottom.Width.cssString("0"), borders.Left.Width.cssString("0")) borders.Top.Width.cssString("0", session),
borders.Right.Width.cssString("0", session),
borders.Bottom.Width.cssString("0", session),
borders.Left.Width.cssString("0", session))
} }
} }

View File

@ -20,7 +20,7 @@ type boundsPropertyData struct {
// NewBoundsProperty creates the new BoundsProperty object // NewBoundsProperty creates the new BoundsProperty object
func NewBoundsProperty(params Params) BoundsProperty { func NewBoundsProperty(params Params) BoundsProperty {
bounds := new(boundsPropertyData) bounds := new(boundsPropertyData)
bounds.properties = map[string]interface{}{} bounds.properties = map[string]any{}
if params != nil { if params != nil {
for _, tag := range []string{Top, Right, Bottom, Left} { for _, tag := range []string{Top, Right, Bottom, Left} {
if value, ok := params[tag]; ok { if value, ok := params[tag]; ok {
@ -79,7 +79,7 @@ func (bounds *boundsPropertyData) Remove(tag string) {
bounds.propertyList.Remove(bounds.normalizeTag(tag)) 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 { if value == nil {
bounds.Remove(tag) bounds.Remove(tag)
return true return true
@ -98,7 +98,7 @@ func (bounds *boundsPropertyData) Set(tag string, value interface{}) bool {
return false return false
} }
func (bounds *boundsPropertyData) Get(tag string) interface{} { func (bounds *boundsPropertyData) Get(tag string) any {
tag = bounds.normalizeTag(tag) tag = bounds.normalizeTag(tag)
if value, ok := bounds.properties[tag]; ok { if value, ok := bounds.properties[tag]; ok {
return value return value
@ -213,22 +213,25 @@ func (bounds *Bounds) String() string {
bounds.Bottom.String() + "," + bounds.Left.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() { if bounds.allFieldsEqual() {
builder.add(tag, bounds.Top.cssString("0")) builder.add(tag, bounds.Top.cssString("0", session))
} else { } else {
builder.addValues(tag, " ", bounds.Top.cssString("0"), bounds.Right.cssString("0"), builder.addValues(tag, " ",
bounds.Bottom.cssString("0"), bounds.Left.cssString("0")) 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 var builder cssValueBuilder
bounds.cssValue("", &builder) bounds.cssValue("", &builder, session)
return builder.finish() 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) { if !properties.setSimpleProperty(tag, value) {
switch value := value.(type) { switch value := value.(type) {
case string: case string:
@ -260,6 +263,12 @@ func (properties *propertyList) setBounds(tag string, value interface{}) bool {
case SizeUnit: case SizeUnit:
properties.properties[tag] = value properties.properties[tag] = value
case float32:
properties.properties[tag] = Px(float64(value))
case float64:
properties.properties[tag] = Px(value)
case Bounds: case Bounds:
bounds := NewBoundsProperty(nil) bounds := NewBoundsProperty(nil)
if value.Top.Type != Auto { if value.Top.Type != Auto {
@ -292,8 +301,12 @@ func (properties *propertyList) setBounds(tag string, value interface{}) bool {
properties.properties[tag] = bounds properties.properties[tag] = bounds
default: default:
notCompatibleType(tag, value) if n, ok := isInt(value); ok {
return false 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) bounds := properties.boundsProperty(mainTag)
if bounds.Set(sideTag, value) { if bounds.Set(sideTag, value) {
properties.properties[mainTag] = bounds properties.properties[mainTag] = bounds

View File

@ -592,7 +592,7 @@ func (canvas *canvasData) writeFont(name string, script *strings.Builder) {
func (canvas *canvasData) SetFont(name string, size SizeUnit) { func (canvas *canvasData) SetFont(name string, size SizeUnit) {
canvas.script.WriteString("\nctx.font = '") 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) 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 { switch params.LineHeight.Type {
case Auto: case Auto:
@ -634,7 +634,7 @@ func (canvas *canvasData) setFontWithParams(name string, size SizeUnit, params F
default: default:
script.WriteString("/") script.WriteString("/")
script.WriteString(params.LineHeight.cssString("")) script.WriteString(params.LineHeight.cssString("", canvas.View().Session()))
} }
canvas.writeFont(name, script) canvas.writeFont(name, script)

View File

@ -21,7 +21,7 @@ type canvasViewData struct {
// NewCanvasView creates the new custom draw view // NewCanvasView creates the new custom draw view
func NewCanvasView(session Session, params Params) CanvasView { func NewCanvasView(session Session, params Params) CanvasView {
view := new(canvasViewData) view := new(canvasViewData)
view.Init(session) view.init(session)
setInitParams(view, params) setInitParams(view, params)
return view return view
} }
@ -31,8 +31,8 @@ func newCanvasView(session Session) View {
} }
// Init initialize fields of ViewsContainer by default values // Init initialize fields of ViewsContainer by default values
func (canvasView *canvasViewData) Init(session Session) { func (canvasView *canvasViewData) init(session Session) {
canvasView.viewData.Init(session) canvasView.viewData.init(session)
canvasView.tag = "CanvasView" 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) 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 tag == DrawFunction {
if value == nil { if value == nil {
canvasView.drawer = nil canvasView.drawer = nil
@ -85,11 +85,11 @@ func (canvasView *canvasViewData) set(tag string, value interface{}) bool {
return canvasView.viewData.set(tag, value) 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)) return canvasView.get(canvasView.normalizeTag(tag))
} }
func (canvasView *canvasViewData) get(tag string) interface{} { func (canvasView *canvasViewData) get(tag string) any {
if tag == DrawFunction { if tag == DrawFunction {
return canvasView.drawer return canvasView.drawer
} }

View File

@ -23,7 +23,7 @@ type checkboxData struct {
// NewCheckbox create new Checkbox object and return it // NewCheckbox create new Checkbox object and return it
func NewCheckbox(session Session, params Params) Checkbox { func NewCheckbox(session Session, params Params) Checkbox {
view := new(checkboxData) view := new(checkboxData)
view.Init(session) view.init(session)
setInitParams(view, Params{ setInitParams(view, Params{
ClickEvent: checkboxClickListener, ClickEvent: checkboxClickListener,
KeyDownEvent: checkboxKeyListener, KeyDownEvent: checkboxKeyListener,
@ -36,8 +36,8 @@ func newCheckbox(session Session) View {
return NewCheckbox(session, nil) return NewCheckbox(session, nil)
} }
func (button *checkboxData) Init(session Session) { func (button *checkboxData) init(session Session) {
button.viewsContainerData.Init(session) button.viewsContainerData.init(session)
button.tag = "Checkbox" button.tag = "Checkbox"
button.systemClass = "ruiGridLayout ruiCheckbox" button.systemClass = "ruiGridLayout ruiCheckbox"
button.checkedListeners = []func(Checkbox, bool){} button.checkedListeners = []func(Checkbox, bool){}
@ -51,7 +51,7 @@ func (button *checkboxData) Focusable() bool {
return true return true
} }
func (button *checkboxData) Get(tag string) interface{} { func (button *checkboxData) Get(tag string) any {
switch strings.ToLower(tag) { switch strings.ToLower(tag) {
case CheckboxChangedEvent: case CheckboxChangedEvent:
return button.checkedListeners return button.checkedListeners
@ -60,11 +60,11 @@ func (button *checkboxData) Get(tag string) interface{} {
return button.viewsContainerData.Get(tag) 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) 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 { switch tag {
case CheckboxChangedEvent: case CheckboxChangedEvent:
if !button.setChangedListener(value) { if !button.setChangedListener(value) {
@ -194,80 +194,32 @@ func (button *checkboxData) changedCheckboxState(state bool) {
} }
func checkboxClickListener(view View) { func checkboxClickListener(view View) {
view.Set(Checked, !IsCheckboxChecked(view, "")) view.Set(Checked, !IsCheckboxChecked(view))
BlurView(view) BlurView(view)
} }
func checkboxKeyListener(view View, event KeyEvent) { func checkboxKeyListener(view View, event KeyEvent) {
switch event.Code { switch event.Code {
case "Enter", "Space": case "Enter", "Space":
view.Set(Checked, !IsCheckboxChecked(view, "")) view.Set(Checked, !IsCheckboxChecked(view))
} }
} }
func (button *checkboxData) setChangedListener(value interface{}) bool { func (button *checkboxData) setChangedListener(value any) bool {
if value == nil { listeners, ok := valueToEventListeners[Checkbox, bool](value)
if len(button.checkedListeners) > 0 { if !ok {
button.checkedListeners = []func(Checkbox, bool){} return false
} } else if listeners == nil {
return true listeners = []func(Checkbox, bool){}
}
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
} }
button.checkedListeners = listeners
return true return true
} }
func (button *checkboxData) cssStyle(self View, builder cssBuilder) { func (button *checkboxData) cssStyle(self View, builder cssBuilder) {
session := button.Session() session := button.Session()
vAlign, _ := enumStyledProperty(button, CheckboxVerticalAlign, LeftAlign) vAlign := GetCheckboxVerticalAlign(button)
hAlign, _ := enumStyledProperty(button, CheckboxHorizontalAlign, TopAlign) hAlign := GetCheckboxHorizontalAlign(button)
switch hAlign { switch hAlign {
case CenterAlign: case CenterAlign:
if vAlign == BottomAlign { 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 { 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") 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) { func (button *checkboxData) htmlCheckbox(buffer *strings.Builder, checked bool) (int, int) {
vAlign, _ := enumStyledProperty(button, CheckboxVerticalAlign, LeftAlign) vAlign := GetCheckboxVerticalAlign(button)
hAlign, _ := enumStyledProperty(button, CheckboxHorizontalAlign, TopAlign) hAlign := GetCheckboxHorizontalAlign(button)
buffer.WriteString(`<div id="`) buffer.WriteString(`<div id="`)
buffer.WriteString(button.htmlID()) 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) { 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(`<div id="`)
buffer.WriteString(button.htmlID()) buffer.WriteString(button.htmlID())
@ -370,7 +322,7 @@ func (button *checkboxData) htmlSubviews(self View, buffer *strings.Builder) {
} }
func (button *checkboxData) cssHorizontalAlign() string { func (button *checkboxData) cssHorizontalAlign() string {
align, _ := enumStyledProperty(button, HorizontalAlign, TopAlign) align := GetHorizontalAlign(button)
values := enumProperties[CellHorizontalAlign].cssValues values := enumProperties[CellHorizontalAlign].cssValues
if align >= 0 && align < len(values) { if align >= 0 && align < len(values) {
return values[align] return values[align]
@ -379,7 +331,7 @@ func (button *checkboxData) cssHorizontalAlign() string {
} }
func (button *checkboxData) cssVerticalAlign() string { func (button *checkboxData) cssVerticalAlign() string {
align, _ := enumStyledProperty(button, VerticalAlign, TopAlign) align := GetVerticalAlign(button)
values := enumProperties[CellVerticalAlign].cssValues values := enumProperties[CellVerticalAlign].cssValues
if align >= 0 && align < len(values) { if align >= 0 && align < len(values) {
return values[align] return values[align]
@ -388,17 +340,19 @@ func (button *checkboxData) cssVerticalAlign() string {
} }
// IsCheckboxChecked returns true if the Checkbox is checked, false otherwise. // 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. // 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 { func IsCheckboxChecked(view View, subviewID ...string) bool {
if subviewID != "" { return boolStyledProperty(view, subviewID, Checked, false)
view = ViewByID(view, subviewID) }
}
if view != nil { // GetCheckboxVerticalAlign return the vertical align of a Checkbox subview: TopAlign (0), BottomAlign (1), CenterAlign (2)
if checked := view.Get(Checked); checked != nil { // If the second argument (subviewID) is not specified or it is "" then a left position of the first argument (view) is returned
if b, ok := checked.(bool); ok { func GetCheckboxVerticalAlign(view View, subviewID ...string) int {
return b return enumStyledProperty(view, subviewID, CheckboxVerticalAlign, LeftAlign, false)
} }
}
} // GetCheckboxHorizontalAlign return the vertical align of a Checkbox subview: LeftAlign (0), RightAlign (1), CenterAlign (2)
return false // 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)
} }

View File

@ -23,7 +23,7 @@ type colorPickerData struct {
// NewColorPicker create new ColorPicker object and return it // NewColorPicker create new ColorPicker object and return it
func NewColorPicker(session Session, params Params) ColorPicker { func NewColorPicker(session Session, params Params) ColorPicker {
view := new(colorPickerData) view := new(colorPickerData)
view.Init(session) view.init(session)
setInitParams(view, params) setInitParams(view, params)
return view return view
} }
@ -32,8 +32,8 @@ func newColorPicker(session Session) View {
return NewColorPicker(session, nil) return NewColorPicker(session, nil)
} }
func (picker *colorPickerData) Init(session Session) { func (picker *colorPickerData) init(session Session) {
picker.viewData.Init(session) picker.viewData.init(session)
picker.tag = "ColorPicker" picker.tag = "ColorPicker"
picker.colorChangedListeners = []func(ColorPicker, Color){} picker.colorChangedListeners = []func(ColorPicker, Color){}
picker.properties[Padding] = Px(0) picker.properties[Padding] = Px(0)
@ -66,7 +66,7 @@ func (picker *colorPickerData) remove(tag string) {
} }
case ColorPickerValue: case ColorPickerValue:
oldColor := GetColorPickerValue(picker, "") oldColor := GetColorPickerValue(picker)
delete(picker.properties, ColorPickerValue) delete(picker.properties, ColorPickerValue)
picker.colorChanged(oldColor) 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) 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 { if value == nil {
picker.remove(tag) picker.remove(tag)
return true return true
@ -87,62 +87,19 @@ func (picker *colorPickerData) set(tag string, value interface{}) bool {
switch tag { switch tag {
case ColorChangedEvent: case ColorChangedEvent:
switch value := value.(type) { listeners, ok := valueToEventListeners[ColorPicker, Color](value)
case func(ColorPicker, Color): if !ok {
picker.colorChangedListeners = []func(ColorPicker, Color){value} notCompatibleType(tag, value)
return false
case func(Color): } else if listeners == nil {
fn := func(_ ColorPicker, date Color) { listeners = []func(ColorPicker, 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
} }
picker.colorChangedListeners = listeners
picker.propertyChangedEvent(tag) picker.propertyChangedEvent(tag)
return true return true
case ColorPickerValue: case ColorPickerValue:
oldColor := GetColorPickerValue(picker, "") oldColor := GetColorPickerValue(picker)
if picker.setColorProperty(ColorPickerValue, value) { if picker.setColorProperty(ColorPickerValue, value) {
picker.colorChanged(oldColor) picker.colorChanged(oldColor)
return true return true
@ -155,7 +112,7 @@ func (picker *colorPickerData) set(tag string, value interface{}) bool {
} }
func (picker *colorPickerData) colorChanged(oldColor Color) { func (picker *colorPickerData) colorChanged(oldColor Color) {
if newColor := GetColorPickerValue(picker, ""); oldColor != newColor { if newColor := GetColorPickerValue(picker); oldColor != newColor {
if picker.created { if picker.created {
picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, picker.htmlID(), newColor.rgbString())) 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)) return picker.get(picker.normalizeTag(tag))
} }
func (picker *colorPickerData) get(tag string) interface{} { func (picker *colorPickerData) get(tag string) any {
switch tag { switch tag {
case ColorChangedEvent: case ColorChangedEvent:
return picker.colorChangedListeners return picker.colorChangedListeners
@ -188,7 +145,7 @@ func (picker *colorPickerData) htmlProperties(self View, buffer *strings.Builder
picker.viewData.htmlProperties(self, buffer) picker.viewData.htmlProperties(self, buffer)
buffer.WriteString(` type="color" value="`) buffer.WriteString(` type="color" value="`)
buffer.WriteString(GetColorPickerValue(picker, "").rgbString()) buffer.WriteString(GetColorPickerValue(picker).rgbString())
buffer.WriteByte('"') buffer.WriteByte('"')
buffer.WriteString(` oninput="editViewInputEvent(this)"`) 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) { func (picker *colorPickerData) htmlDisabledProperties(self View, buffer *strings.Builder) {
if IsDisabled(self, "") { if IsDisabled(self) {
buffer.WriteString(` disabled`) buffer.WriteString(` disabled`)
} }
picker.viewData.htmlDisabledProperties(self, buffer) picker.viewData.htmlDisabledProperties(self, buffer)
@ -208,7 +165,7 @@ func (picker *colorPickerData) handleCommand(self View, command string, data Dat
switch command { switch command {
case "textChanged": case "textChanged":
if text, ok := data.PropertyValue("text"); ok { if text, ok := data.PropertyValue("text"); ok {
oldColor := GetColorPickerValue(picker, "") oldColor := GetColorPickerValue(picker)
if color, ok := StringToColor(text); ok { if color, ok := StringToColor(text); ok {
picker.properties[ColorPickerValue] = color picker.properties[ColorPickerValue] = color
if color != oldColor { if color != oldColor {
@ -225,16 +182,16 @@ func (picker *colorPickerData) handleCommand(self View, command string, data Dat
} }
// GetColorPickerValue returns the value of ColorPicker subview. // GetColorPickerValue returns the value of ColorPicker subview.
// 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 GetColorPickerValue(view View, subviewID string) Color { func GetColorPickerValue(view View, subviewID ...string) Color {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
if result, ok := colorStyledProperty(view, ColorPickerValue); ok { if value, ok := colorProperty(view, ColorPickerValue, view.Session()); ok {
return result return value
} }
for _, tag := range []string{Value, ColorTag} { for _, tag := range []string{ColorPickerValue, Value, ColorTag} {
if value := valueFromStyle(view, tag); value != nil { if value := valueFromStyle(view, tag); value != nil {
if result, ok := valueToColor(value, view.Session()); ok { if result, ok := valueToColor(value, view.Session()); ok {
return result return result
@ -247,17 +204,7 @@ func GetColorPickerValue(view View, subviewID string) Color {
// GetColorChangedListeners returns the ColorChangedListener list of an ColorPicker subview. // GetColorChangedListeners returns the ColorChangedListener list of an ColorPicker subview.
// If there are no listeners then the empty list is returned // 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. // 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) { func GetColorChangedListeners(view View, subviewID ...string) []func(ColorPicker, Color) {
if subviewID != "" { return getEventListeners[ColorPicker, Color](view, subviewID, ColorChangedEvent)
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){}
} }

View File

@ -47,7 +47,7 @@ type columnLayoutData struct {
// NewColumnLayout create new ColumnLayout object and return it // NewColumnLayout create new ColumnLayout object and return it
func NewColumnLayout(session Session, params Params) ColumnLayout { func NewColumnLayout(session Session, params Params) ColumnLayout {
view := new(columnLayoutData) view := new(columnLayoutData)
view.Init(session) view.init(session)
setInitParams(view, params) setInitParams(view, params)
return view return view
} }
@ -57,8 +57,8 @@ func newColumnLayout(session Session) View {
} }
// Init initialize fields of ColumnLayout by default values // Init initialize fields of ColumnLayout by default values
func (ColumnLayout *columnLayoutData) Init(session Session) { func (ColumnLayout *columnLayoutData) init(session Session) {
ColumnLayout.viewsContainerData.Init(session) ColumnLayout.viewsContainerData.init(session)
ColumnLayout.tag = "ColumnLayout" ColumnLayout.tag = "ColumnLayout"
//ColumnLayout.systemClass = "ruiColumnLayout" //ColumnLayout.systemClass = "ruiColumnLayout"
} }
@ -76,7 +76,7 @@ func (columnLayout *columnLayoutData) normalizeTag(tag string) string {
return tag return tag
} }
func (columnLayout *columnLayoutData) Get(tag string) interface{} { func (columnLayout *columnLayoutData) Get(tag string) any {
return columnLayout.get(columnLayout.normalizeTag(tag)) 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) 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 { if value == nil {
columnLayout.remove(tag) columnLayout.remove(tag)
return true 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 // 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 // ColumnLayout is break. If the return value is 0 then the number of columns is calculated
// based on the "column-width" property. // based on the "column-width" property.
// If the second argument (subviewID) is "" then a top position of the first argument (view) is returned // 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 { func GetColumnCount(view View, subviewID ...string) int {
if subviewID != "" { return intStyledProperty(view, subviewID, ColumnCount, 0)
view = ViewByID(view, subviewID)
}
if view == nil {
return 0
}
result, _ := intStyledProperty(view, ColumnCount, 0)
return result
} }
// GetColumnWidth returns SizeUnit value which specifies the width of each column of ColumnLayout. // 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 // 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 { func GetColumnWidth(view View, subviewID ...string) SizeUnit {
if subviewID != "" { return sizeStyledProperty(view, subviewID, ColumnWidth, false)
view = ViewByID(view, subviewID)
}
if view == nil {
return AutoSize()
}
result, _ := sizeStyledProperty(view, ColumnWidth)
return result
} }
// GetColumnGap returns SizeUnit property which specifies the size of the gap (gutter) between columns of ColumnLayout. // 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 // 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 { func GetColumnGap(view View, subviewID ...string) SizeUnit {
if subviewID != "" { return sizeStyledProperty(view, subviewID, ColumnGap, false)
view = ViewByID(view, subviewID)
}
if view == nil {
return AutoSize()
}
result, _ := sizeStyledProperty(view, ColumnGap)
return result
} }
// GetColumnSeparator returns ViewBorder struct which specifies the line drawn between func getColumnSeparator(view View, subviewID []string) ViewBorder {
// columns in a multi-column ColumnLayout. if len(subviewID) > 0 && subviewID[0] != "" {
// If the second argument (subviewID) is "" then a top position of the first argument (view) is returned view = ViewByID(view, subviewID[0])
func GetColumnSeparator(view View, subviewID string) ViewBorder {
if subviewID != "" {
view = ViewByID(view, subviewID)
} }
if view != nil { if view != nil {
@ -199,27 +175,34 @@ func GetColumnSeparator(view View, subviewID string) ViewBorder {
return 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 // ColumnSeparatorStyle returns int value which specifies the style of the line drawn between
// columns in a multi-column layout. // columns in a multi-column layout.
// Valid values are NoneLine (0), SolidLine (1), DashedLine (2), DottedLine (3), and DoubleLine (4). // 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 // 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 { func GetColumnSeparatorStyle(view View, subviewID ...string) int {
border := GetColumnSeparator(view, subviewID) border := getColumnSeparator(view, subviewID)
return border.Style return border.Style
} }
// ColumnSeparatorWidth returns SizeUnit value which specifies the width of the line drawn between // ColumnSeparatorWidth returns SizeUnit value which specifies the width of the line drawn between
// columns in a multi-column layout. // columns in a multi-column layout.
// If the second argument (subviewID) is "" then a top position of the first argument (view) is returned // 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 { func GetColumnSeparatorWidth(view View, subviewID ...string) SizeUnit {
border := GetColumnSeparator(view, subviewID) border := getColumnSeparator(view, subviewID)
return border.Width return border.Width
} }
// ColumnSeparatorColor returns Color value which specifies the color of the line drawn between // ColumnSeparatorColor returns Color value which specifies the color of the line drawn between
// columns in a multi-column layout. // columns in a multi-column layout.
// If the second argument (subviewID) is "" then a top position of the first argument (view) is returned // 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 { func GetColumnSeparatorColor(view View, subviewID ...string) Color {
border := GetColumnSeparator(view, subviewID) border := getColumnSeparator(view, subviewID)
return border.Color return border.Color
} }

View File

@ -18,11 +18,11 @@ type columnSeparatorProperty struct {
propertyList propertyList
} }
func newColumnSeparatorProperty(value interface{}) ColumnSeparatorProperty { func newColumnSeparatorProperty(value any) ColumnSeparatorProperty {
if value == nil { if value == nil {
separator := new(columnSeparatorProperty) separator := new(columnSeparatorProperty)
separator.properties = map[string]interface{}{} separator.properties = map[string]any{}
return separator return separator
} }
@ -32,7 +32,7 @@ func newColumnSeparatorProperty(value interface{}) ColumnSeparatorProperty {
case DataObject: case DataObject:
separator := new(columnSeparatorProperty) separator := new(columnSeparatorProperty)
separator.properties = map[string]interface{}{} separator.properties = map[string]any{}
for _, tag := range []string{Style, Width, ColorTag} { for _, tag := range []string{Style, Width, ColorTag} {
if val, ok := value.PropertyValue(tag); ok && val != "" { if val, ok := value.PropertyValue(tag); ok && val != "" {
separator.set(tag, value) separator.set(tag, value)
@ -42,7 +42,7 @@ func newColumnSeparatorProperty(value interface{}) ColumnSeparatorProperty {
case ViewBorder: case ViewBorder:
separator := new(columnSeparatorProperty) separator := new(columnSeparatorProperty)
separator.properties = map[string]interface{}{ separator.properties = map[string]any{
Style: value.Style, Style: value.Style,
Width: value.Width, Width: value.Width,
ColorTag: value.Color, ColorTag: value.Color,
@ -57,7 +57,7 @@ func newColumnSeparatorProperty(value interface{}) ColumnSeparatorProperty {
// NewColumnSeparator creates the new ColumnSeparatorProperty // NewColumnSeparator creates the new ColumnSeparatorProperty
func NewColumnSeparator(params Params) ColumnSeparatorProperty { func NewColumnSeparator(params Params) ColumnSeparatorProperty {
separator := new(columnSeparatorProperty) separator := new(columnSeparatorProperty)
separator.properties = map[string]interface{}{} separator.properties = map[string]any{}
if params != nil { if params != nil {
for _, tag := range []string{Style, Width, ColorTag} { for _, tag := range []string{Style, Width, ColorTag} {
if value, ok := params[tag]; ok && value != nil { 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) tag = separator.normalizeTag(tag)
if value == nil { if value == nil {
@ -140,7 +140,7 @@ func (separator *columnSeparatorProperty) Set(tag string, value interface{}) boo
return false return false
} }
func (separator *columnSeparatorProperty) Get(tag string) interface{} { func (separator *columnSeparatorProperty) Get(tag string) any {
tag = separator.normalizeTag(tag) tag = separator.normalizeTag(tag)
if result, ok := separator.properties[tag]; ok { if result, ok := separator.properties[tag]; ok {
@ -167,8 +167,9 @@ func (separator *columnSeparatorProperty) cssValue(session Session) string {
buffer := allocStringBuilder() buffer := allocStringBuilder()
defer freeStringBuilder(buffer) defer freeStringBuilder(buffer)
if value.Width.Type != Auto && value.Width.Type != SizeInFraction && value.Width.Value > 0 { if value.Width.Type != Auto && value.Width.Type != SizeInFraction &&
buffer.WriteString(value.Width.cssString("")) (value.Width.Value > 0 || value.Width.Type == SizeFunction) {
buffer.WriteString(value.Width.cssString("", session))
} }
styles := enumProperties[BorderStyle].cssValues styles := enumProperties[BorderStyle].cssValues

View File

@ -45,26 +45,26 @@ func (customView *CustomViewData) setTag(tag string) {
// Get returns a value of the property with name defined by the argument. // 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. // 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) return customView.superView.Get(tag)
} }
func (customView *CustomViewData) getRaw(tag string) interface{} { func (customView *CustomViewData) getRaw(tag string) any {
return customView.superView.getRaw(tag) 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) customView.superView.setRaw(tag, value)
} }
// Set sets the value (second argument) of the property with name defined by the first argument. // 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 // 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 // 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) 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) return customView.superView.SetAnimated(tag, value, animation)
} }
@ -87,10 +87,6 @@ func (customView *CustomViewData) Clear() {
customView.superView.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) { func (customView *CustomViewData) cssViewStyle(buffer cssBuilder, session Session) {
customView.superView.cssViewStyle(buffer, 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 { 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{}
} }

View File

@ -29,7 +29,7 @@ type datePickerData struct {
// NewDatePicker create new DatePicker object and return it // NewDatePicker create new DatePicker object and return it
func NewDatePicker(session Session, params Params) DatePicker { func NewDatePicker(session Session, params Params) DatePicker {
view := new(datePickerData) view := new(datePickerData)
view.Init(session) view.init(session)
setInitParams(view, params) setInitParams(view, params)
return view return view
} }
@ -38,8 +38,8 @@ func newDatePicker(session Session) View {
return NewDatePicker(session, nil) return NewDatePicker(session, nil)
} }
func (picker *datePickerData) Init(session Session) { func (picker *datePickerData) init(session Session) {
picker.viewData.Init(session) picker.viewData.init(session)
picker.tag = "DatePicker" picker.tag = "DatePicker"
picker.dateChangedListeners = []func(DatePicker, time.Time){} picker.dateChangedListeners = []func(DatePicker, time.Time){}
} }
@ -96,7 +96,7 @@ func (picker *datePickerData) remove(tag string) {
case DatePickerValue: case DatePickerValue:
if _, ok := picker.properties[DatePickerValue]; ok { if _, ok := picker.properties[DatePickerValue]; ok {
delete(picker.properties, DatePickerValue) delete(picker.properties, DatePickerValue)
date := GetDatePickerValue(picker, "") date := GetDatePickerValue(picker)
if picker.created { if picker.created {
picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, picker.htmlID(), date.Format(dateFormat))) 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) 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) 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 { if value == nil {
picker.remove(tag) picker.remove(tag)
return true return true
@ -204,9 +204,9 @@ func (picker *datePickerData) set(tag string, value interface{}) bool {
} }
case DatePickerStep: case DatePickerStep:
oldStep := GetDatePickerStep(picker, "") oldStep := GetDatePickerStep(picker)
if picker.setIntProperty(DatePickerStep, value) { if picker.setIntProperty(DatePickerStep, value) {
if step := GetDatePickerStep(picker, ""); oldStep != step { if step := GetDatePickerStep(picker); oldStep != step {
if picker.created { if picker.created {
if step > 0 { if step > 0 {
updateProperty(picker.htmlID(), Step, strconv.Itoa(step), picker.session) updateProperty(picker.htmlID(), Step, strconv.Itoa(step), picker.session)
@ -220,7 +220,7 @@ func (picker *datePickerData) set(tag string, value interface{}) bool {
} }
case DatePickerValue: case DatePickerValue:
oldDate := GetDatePickerValue(picker, "") oldDate := GetDatePickerValue(picker)
if date, ok := setTimeValue(DatePickerValue); ok { if date, ok := setTimeValue(DatePickerValue); ok {
if date != oldDate { if date != oldDate {
if picker.created { if picker.created {
@ -235,57 +235,14 @@ func (picker *datePickerData) set(tag string, value interface{}) bool {
} }
case DateChangedEvent: case DateChangedEvent:
switch value := value.(type) { listeners, ok := valueToEventListeners[DatePicker, time.Time](value)
case func(DatePicker, time.Time): if !ok {
picker.dateChangedListeners = []func(DatePicker, time.Time){value} notCompatibleType(tag, value)
return false
case func(time.Time): } else if listeners == nil {
fn := func(_ DatePicker, date time.Time) { listeners = []func(DatePicker, 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
} }
picker.dateChangedListeners = listeners
picker.propertyChangedEvent(tag) picker.propertyChangedEvent(tag)
return true return true
@ -295,11 +252,11 @@ func (picker *datePickerData) set(tag string, value interface{}) bool {
return false return false
} }
func (picker *datePickerData) Get(tag string) interface{} { func (picker *datePickerData) Get(tag string) any {
return picker.get(picker.normalizeTag(tag)) return picker.get(picker.normalizeTag(tag))
} }
func (picker *datePickerData) get(tag string) interface{} { func (picker *datePickerData) get(tag string) any {
switch tag { switch tag {
case DateChangedEvent: case DateChangedEvent:
return picker.dateChangedListeners return picker.dateChangedListeners
@ -337,7 +294,7 @@ func (picker *datePickerData) htmlProperties(self View, buffer *strings.Builder)
} }
buffer.WriteString(` value="`) buffer.WriteString(` value="`)
buffer.WriteString(GetDatePickerValue(picker, "").Format(dateFormat)) buffer.WriteString(GetDatePickerValue(picker).Format(dateFormat))
buffer.WriteByte('"') buffer.WriteByte('"')
buffer.WriteString(` oninput="editViewInputEvent(this)"`) 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) { func (picker *datePickerData) htmlDisabledProperties(self View, buffer *strings.Builder) {
if IsDisabled(self, "") { if IsDisabled(self) {
buffer.WriteString(` disabled`) buffer.WriteString(` disabled`)
} }
picker.viewData.htmlDisabledProperties(self, buffer) picker.viewData.htmlDisabledProperties(self, buffer)
@ -358,7 +315,7 @@ func (picker *datePickerData) handleCommand(self View, command string, data Data
case "textChanged": case "textChanged":
if text, ok := data.PropertyValue("text"); ok { if text, ok := data.PropertyValue("text"); ok {
if value, err := time.Parse(dateFormat, text); err == nil { if value, err := time.Parse(dateFormat, text); err == nil {
oldValue := GetDatePickerValue(picker, "") oldValue := GetDatePickerValue(picker)
picker.properties[DatePickerValue] = value picker.properties[DatePickerValue] = value
if value != oldValue { if value != oldValue {
for _, listener := range picker.dateChangedListeners { 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) { 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 { if value != nil {
switch value := value.(type) { switch value := value.(type) {
case time.Time: 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, // 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. // "false" as the second value otherwise.
// 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 GetDatePickerMin(view View, subviewID string) (time.Time, bool) { func GetDatePickerMin(view View, subviewID ...string) (time.Time, bool) {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
return getDateProperty(view, DatePickerMin, Min) 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, // 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. // "false" as the second value otherwise.
// 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 GetDatePickerMax(view View, subviewID string) (time.Time, bool) { func GetDatePickerMax(view View, subviewID ...string) (time.Time, bool) {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
return getDateProperty(view, DatePickerMax, Max) 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. // 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. // 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 { func GetDatePickerStep(view View, subviewID ...string) int {
if subviewID != "" { return intStyledProperty(view, subviewID, DatePickerStep, 0)
view = ViewByID(view, subviewID)
}
if view != nil {
if result, _ := intStyledProperty(view, DatePickerStep, 0); result >= 0 {
return result
}
}
return 0
} }
// GetDatePickerValue returns the date of DatePicker subview. // GetDatePickerValue returns the date of DatePicker subview.
// 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 GetDatePickerValue(view View, subviewID string) time.Time { func GetDatePickerValue(view View, subviewID ...string) time.Time {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view == nil { if view == nil {
return time.Now() return time.Now()
@ -461,17 +410,7 @@ func GetDatePickerValue(view View, subviewID string) time.Time {
// GetDateChangedListeners returns the DateChangedListener list of an DatePicker subview. // GetDateChangedListeners returns the DateChangedListener list of an DatePicker subview.
// If there are no listeners then the empty list is returned // 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. // 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) { func GetDateChangedListeners(view View, subviewID ...string) []func(DatePicker, time.Time) {
if subviewID != "" { return getEventListeners[DatePicker, time.Time](view, subviewID, DateChangedEvent)
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){}
} }

View File

@ -61,6 +61,8 @@ theme {
ruiTabHeight = 32px, ruiTabHeight = 32px,
ruiTabBarPadding = 2px, ruiTabBarPadding = 2px,
ruiTabRadius = 2px, ruiTabRadius = 2px,
ruiArrowSize = 16px,
ruiArrowWidth = 16px,
}, },
constants:touch = _{ constants:touch = _{
ruiButtonHorizontalPadding = 20px, ruiButtonHorizontalPadding = 20px,
@ -72,6 +74,7 @@ theme {
text-size = 10pt, text-size = 10pt,
text-color = @ruiTextColor, text-color = @ruiTextColor,
background-color = @ruiBackgroundColor, background-color = @ruiBackgroundColor,
accent-color = @ruiHighlightColor,
}, },
ruiButton { ruiButton {
align = center, align = center,
@ -216,7 +219,6 @@ theme {
background-color = @ruiPopupBackgroundColor, background-color = @ruiPopupBackgroundColor,
text-color = @ruiPopupTextColor, text-color = @ruiPopupTextColor,
radius = 4px, radius = 4px,
shadow = _{spread-radius=4px, blur=16px, color=@ruiPopupShadow },
}, },
ruiPopupTitle { ruiPopupTitle {
background-color = @ruiPopupTitleColor, background-color = @ruiPopupTitleColor,

View File

@ -24,7 +24,7 @@ type detailsViewData struct {
// NewDetailsView create new DetailsView object and return it // NewDetailsView create new DetailsView object and return it
func NewDetailsView(session Session, params Params) DetailsView { func NewDetailsView(session Session, params Params) DetailsView {
view := new(detailsViewData) view := new(detailsViewData)
view.Init(session) view.init(session)
setInitParams(view, params) setInitParams(view, params)
return view return view
} }
@ -34,8 +34,8 @@ func newDetailsView(session Session) View {
} }
// Init initialize fields of DetailsView by default values // Init initialize fields of DetailsView by default values
func (detailsView *detailsViewData) Init(session Session) { func (detailsView *detailsViewData) init(session Session) {
detailsView.viewsContainerData.Init(session) detailsView.viewsContainerData.init(session)
detailsView.tag = "DetailsView" detailsView.tag = "DetailsView"
//detailsView.systemClass = "ruiDetailsView" //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) 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 { if value == nil {
detailsView.remove(tag) detailsView.remove(tag)
return true return true
@ -110,7 +110,7 @@ func (detailsView *detailsViewData) set(tag string, value interface{}) bool {
return false return false
} }
if detailsView.created { if detailsView.created {
if IsDetailsExpanded(detailsView, "") { if IsDetailsExpanded(detailsView) {
updateProperty(detailsView.htmlID(), "open", "", detailsView.Session()) updateProperty(detailsView.htmlID(), "open", "", detailsView.Session())
} else { } else {
removeProperty(detailsView.htmlID(), "open", detailsView.Session()) removeProperty(detailsView.htmlID(), "open", detailsView.Session())
@ -133,11 +133,11 @@ func (detailsView *detailsViewData) set(tag string, value interface{}) bool {
return true return true
} }
func (detailsView *detailsViewData) Get(tag string) interface{} { func (detailsView *detailsViewData) Get(tag string) any {
return detailsView.get(strings.ToLower(tag)) 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) return detailsView.viewsContainerData.get(tag)
} }
@ -148,7 +148,7 @@ func (detailsView *detailsViewData) htmlTag() string {
func (detailsView *detailsViewData) htmlProperties(self View, buffer *strings.Builder) { func (detailsView *detailsViewData) htmlProperties(self View, buffer *strings.Builder) {
detailsView.viewsContainerData.htmlProperties(self, buffer) detailsView.viewsContainerData.htmlProperties(self, buffer)
buffer.WriteString(` ontoggle="detailsEvent(this)"`) buffer.WriteString(` ontoggle="detailsEvent(this)"`)
if IsDetailsExpanded(detailsView, "") { if IsDetailsExpanded(detailsView) {
buffer.WriteString(` open`) buffer.WriteString(` open`)
} }
} }
@ -157,7 +157,7 @@ func (detailsView *detailsViewData) htmlSubviews(self View, buffer *strings.Buil
if value, ok := detailsView.properties[Summary]; ok { if value, ok := detailsView.properties[Summary]; ok {
switch value := value.(type) { switch value := value.(type) {
case string: case string:
if !GetNotTranslate(detailsView, "") { if !GetNotTranslate(detailsView) {
value, _ = detailsView.session.GetString(value) value, _ = detailsView.session.GetString(value)
} }
buffer.WriteString("<summary>") 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. // 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. // 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 { func GetDetailsSummary(view View, subviewID ...string) View {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
if value := view.Get(Summary); value != 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. // 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. // 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 { func IsDetailsExpanded(view View, subviewID ...string) bool {
if subviewID != "" { return boolStyledProperty(view, subviewID, Expanded, false)
view = ViewByID(view, subviewID)
}
if view != nil {
if result, ok := boolStyledProperty(view, Expanded); ok {
return result
}
}
return false
} }

View File

@ -6,6 +6,9 @@ import (
"strings" "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" const DropDownEvent = "drop-down-event"
// DropDownList - the interface of a drop-down list view // DropDownList - the interface of a drop-down list view
@ -17,14 +20,14 @@ type DropDownList interface {
type dropDownListData struct { type dropDownListData struct {
viewData viewData
items []string items []string
disabledItems []interface{} disabledItems []any
dropDownListener []func(DropDownList, int) dropDownListener []func(DropDownList, int)
} }
// NewDropDownList create new DropDownList object and return it // NewDropDownList create new DropDownList object and return it
func NewDropDownList(session Session, params Params) DropDownList { func NewDropDownList(session Session, params Params) DropDownList {
view := new(dropDownListData) view := new(dropDownListData)
view.Init(session) view.init(session)
setInitParams(view, params) setInitParams(view, params)
return view return view
} }
@ -33,11 +36,11 @@ func newDropDownList(session Session) View {
return NewDropDownList(session, nil) return NewDropDownList(session, nil)
} }
func (list *dropDownListData) Init(session Session) { func (list *dropDownListData) init(session Session) {
list.viewData.Init(session) list.viewData.init(session)
list.tag = "DropDownList" list.tag = "DropDownList"
list.items = []string{} list.items = []string{}
list.disabledItems = []interface{}{} list.disabledItems = []any{}
list.dropDownListener = []func(DropDownList, int){} list.dropDownListener = []func(DropDownList, int){}
} }
@ -66,7 +69,7 @@ func (list *dropDownListData) remove(tag string) {
case DisabledItems: case DisabledItems:
if len(list.disabledItems) > 0 { if len(list.disabledItems) > 0 {
list.disabledItems = []interface{}{} list.disabledItems = []any{}
if list.created { if list.created {
updateInnerHTML(list.htmlID(), list.session) updateInnerHTML(list.htmlID(), list.session)
} }
@ -80,7 +83,7 @@ func (list *dropDownListData) remove(tag string) {
} }
case Current: case Current:
oldCurrent := GetCurrent(list, "") oldCurrent := GetCurrent(list)
delete(list.properties, Current) delete(list.properties, Current)
if oldCurrent != 0 { if oldCurrent != 0 {
if list.created { 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) 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 { if value == nil {
list.remove(tag) list.remove(tag)
return true return true
@ -113,15 +116,24 @@ func (list *dropDownListData) set(tag string, value interface{}) bool {
return list.setDisabledItems(value) return list.setDisabledItems(value)
case DropDownEvent: 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: case Current:
oldCurrent := GetCurrent(list, "") oldCurrent := GetCurrent(list)
if !list.setIntProperty(Current, value) { if !list.setIntProperty(Current, value) {
return false return false
} }
if current := GetCurrent(list, ""); oldCurrent != current { if current := GetCurrent(list); oldCurrent != current {
if list.created { if list.created {
list.session.runScript(fmt.Sprintf(`selectDropDownListItem('%s', %d)`, list.htmlID(), current)) 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) return list.viewData.set(tag, value)
} }
func (list *dropDownListData) setItems(value interface{}) bool { func (list *dropDownListData) setItems(value any) bool {
switch value := value.(type) { switch value := value.(type) {
case string: case string:
list.items = []string{value} list.items = []string{value}
@ -155,7 +167,7 @@ func (list *dropDownListData) setItems(value interface{}) bool {
list.items[i] = str.String() list.items[i] = str.String()
} }
case []interface{}: case []any:
items := make([]string, 0, len(value)) items := make([]string, 0, len(value))
for _, v := range value { for _, v := range value {
switch val := v.(type) { switch val := v.(type) {
@ -206,16 +218,16 @@ func (list *dropDownListData) setItems(value interface{}) bool {
return true return true
} }
func (list *dropDownListData) setDisabledItems(value interface{}) bool { func (list *dropDownListData) setDisabledItems(value any) bool {
switch value := value.(type) { switch value := value.(type) {
case []int: case []int:
list.disabledItems = make([]interface{}, len(value)) list.disabledItems = make([]any, len(value))
for i, n := range value { for i, n := range value {
list.disabledItems[i] = n list.disabledItems[i] = n
} }
case []interface{}: case []any:
disabledItems := make([]interface{}, len(value)) disabledItems := make([]any, len(value))
for i, val := range value { for i, val := range value {
if val == nil { if val == nil {
notCompatibleType(DisabledItems, value) notCompatibleType(DisabledItems, value)
@ -248,7 +260,7 @@ func (list *dropDownListData) setDisabledItems(value interface{}) bool {
case string: case string:
values := strings.Split(value, ",") values := strings.Split(value, ",")
disabledItems := make([]interface{}, len(values)) disabledItems := make([]any, len(values))
for i, str := range values { for i, str := range values {
str = strings.Trim(str, " ") str = strings.Trim(str, " ")
if str == "" { if str == "" {
@ -291,69 +303,11 @@ func (list *dropDownListData) setDisabledItems(value interface{}) bool {
} }
func (list *dropDownListData) setDropDownListener(value interface{}) bool { func (list *dropDownListData) Get(tag string) any {
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{} {
return list.get(strings.ToLower(tag)) return list.get(strings.ToLower(tag))
} }
func (list *dropDownListData) get(tag string) interface{} { func (list *dropDownListData) get(tag string) any {
switch tag { switch tag {
case Items: case Items:
return list.items return list.items
@ -382,9 +336,9 @@ func (list *dropDownListData) htmlTag() string {
func (list *dropDownListData) htmlSubviews(self View, buffer *strings.Builder) { func (list *dropDownListData) htmlSubviews(self View, buffer *strings.Builder) {
if list.items != nil { if list.items != nil {
current := GetCurrent(list, "") current := GetCurrent(list)
notTranslate := GetNotTranslate(list, "") notTranslate := GetNotTranslate(list)
disabledItems := GetDropDownDisabledItems(list, "") disabledItems := GetDropDownDisabledItems(list)
for i, item := range list.items { for i, item := range list.items {
disabled := false disabled := false
for _, index := range disabledItems { 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) { func (list *dropDownListData) htmlDisabledProperties(self View, buffer *strings.Builder) {
list.viewData.htmlDisabledProperties(self, buffer) list.viewData.htmlDisabledProperties(self, buffer)
if IsDisabled(list, "") { if IsDisabled(list) {
buffer.WriteString(`disabled`) buffer.WriteString(`disabled`)
} }
} }
@ -435,7 +389,7 @@ func (list *dropDownListData) handleCommand(self View, command string, data Data
case "itemSelected": case "itemSelected":
if text, ok := data.PropertyValue("number"); ok { if text, ok := data.PropertyValue("number"); ok {
if number, err := strconv.Atoi(text); err == nil { 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.properties[Current] = number
list.onSelectedItemChanged(number) list.onSelectedItemChanged(number)
} }
@ -450,19 +404,17 @@ func (list *dropDownListData) handleCommand(self View, command string, data Data
return true return true
} }
func GetDropDownListeners(view View) []func(DropDownList, int) { // GetDropDownListeners returns the "drop-down-event" listener list. If there are no listeners then the empty list is returned.
if value := view.Get(DropDownEvent); value != nil { // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
if listeners, ok := value.([]func(DropDownList, int)); ok { func GetDropDownListeners(view View, subviewID ...string) []func(DropDownList, int) {
return listeners return getEventListeners[DropDownList, int](view, subviewID, DropDownEvent)
}
}
return []func(DropDownList, int){}
} }
// func GetDropDownItems return the view items list // GetDropDownItems return the DropDownList items list.
func GetDropDownItems(view View, subviewID string) []string { // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
if subviewID != "" { func GetDropDownItems(view View, subviewID ...string) []string {
view = ViewByID(view, subviewID) if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
if list, ok := view.(DropDownList); ok { if list, ok := view.(DropDownList); ok {
@ -472,14 +424,16 @@ func GetDropDownItems(view View, subviewID string) []string {
return []string{} return []string{}
} }
// func GetDropDownDisabledItems return the list of disabled item indexes // GetDropDownDisabledItems return the list of DropDownList disabled item indexes.
func GetDropDownDisabledItems(view View, subviewID string) []int { // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
if subviewID != "" { func GetDropDownDisabledItems(view View, subviewID ...string) []int {
view = ViewByID(view, subviewID) if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
if value := view.Get(DisabledItems); value != nil { if value := view.Get(DisabledItems); value != nil {
if values, ok := value.([]interface{}); ok { if values, ok := value.([]any); ok {
count := len(values) count := len(values)
if count > 0 { if count > 0 {
result := make([]int, 0, count) result := make([]int, 0, count)

View File

@ -48,7 +48,7 @@ type editViewData struct {
// NewEditView create new EditView object and return it // NewEditView create new EditView object and return it
func NewEditView(session Session, params Params) EditView { func NewEditView(session Session, params Params) EditView {
view := new(editViewData) view := new(editViewData)
view.Init(session) view.init(session)
setInitParams(view, params) setInitParams(view, params)
return view return view
} }
@ -57,8 +57,8 @@ func newEditView(session Session) View {
return NewEditView(session, nil) return NewEditView(session, nil)
} }
func (edit *editViewData) Init(session Session) { func (edit *editViewData) init(session Session) {
edit.viewData.Init(session) edit.viewData.init(session)
edit.textChangeListeners = []func(EditView, string){} edit.textChangeListeners = []func(EditView, string){}
edit.tag = "EditView" edit.tag = "EditView"
} }
@ -132,7 +132,7 @@ func (edit *editViewData) remove(tag string) {
case Text: case Text:
if exists { if exists {
oldText := GetText(edit, "") oldText := GetText(edit)
delete(edit.properties, tag) delete(edit.properties, tag)
if oldText != "" { if oldText != "" {
edit.textChanged("") edit.textChanged("")
@ -144,7 +144,7 @@ func (edit *editViewData) remove(tag string) {
case EditViewPattern: case EditViewPattern:
if exists { if exists {
oldText := GetEditViewPattern(edit, "") oldText := GetEditViewPattern(edit)
delete(edit.properties, tag) delete(edit.properties, tag)
if oldText != "" { if oldText != "" {
if edit.created { if edit.created {
@ -156,7 +156,7 @@ func (edit *editViewData) remove(tag string) {
case EditViewType: case EditViewType:
if exists { if exists {
oldType := GetEditViewType(edit, "") oldType := GetEditViewType(edit)
delete(edit.properties, tag) delete(edit.properties, tag)
if oldType != 0 { if oldType != 0 {
if edit.created { if edit.created {
@ -168,10 +168,10 @@ func (edit *editViewData) remove(tag string) {
case EditWrap: case EditWrap:
if exists { if exists {
oldWrap := IsEditViewWrap(edit, "") oldWrap := IsEditViewWrap(edit)
delete(edit.properties, tag) delete(edit.properties, tag)
if GetEditViewType(edit, "") == MultiLineText { if GetEditViewType(edit) == MultiLineText {
if wrap := IsEditViewWrap(edit, ""); wrap != oldWrap { if wrap := IsEditViewWrap(edit); wrap != oldWrap {
if edit.created { if edit.created {
if wrap { if wrap {
updateProperty(edit.htmlID(), "wrap", "soft", edit.session) 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) 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 { if value == nil {
edit.remove(tag) edit.remove(tag)
return true return true
@ -202,13 +202,13 @@ func (edit *editViewData) set(tag string, value interface{}) bool {
switch tag { switch tag {
case Text: case Text:
oldText := GetText(edit, "") oldText := GetText(edit)
if text, ok := value.(string); ok { if text, ok := value.(string); ok {
edit.properties[Text] = text edit.properties[Text] = text
if text = GetText(edit, ""); oldText != text { if text = GetText(edit); oldText != text {
edit.textChanged(text) edit.textChanged(text)
if edit.created { if edit.created {
if GetEditViewType(edit, "") == MultiLineText { if GetEditViewType(edit) == MultiLineText {
updateInnerHTML(edit.htmlID(), edit.Session()) updateInnerHTML(edit.htmlID(), edit.Session())
} else { } else {
text = strings.ReplaceAll(text, `"`, `\"`) text = strings.ReplaceAll(text, `"`, `\"`)
@ -224,10 +224,10 @@ func (edit *editViewData) set(tag string, value interface{}) bool {
return false return false
case Hint: case Hint:
oldText := GetHint(edit, "") oldText := GetHint(edit)
if text, ok := value.(string); ok { if text, ok := value.(string); ok {
edit.properties[Hint] = text edit.properties[Hint] = text
if text = GetHint(edit, ""); oldText != text { if text = GetHint(edit); oldText != text {
if edit.created { if edit.created {
if text != "" { if text != "" {
updateProperty(edit.htmlID(), "placeholder", text, edit.session) updateProperty(edit.htmlID(), "placeholder", text, edit.session)
@ -242,9 +242,9 @@ func (edit *editViewData) set(tag string, value interface{}) bool {
return false return false
case MaxLength: case MaxLength:
oldMaxLength := GetMaxLength(edit, "") oldMaxLength := GetMaxLength(edit)
if edit.setIntProperty(MaxLength, value) { if edit.setIntProperty(MaxLength, value) {
if maxLength := GetMaxLength(edit, ""); maxLength != oldMaxLength { if maxLength := GetMaxLength(edit); maxLength != oldMaxLength {
if edit.created { if edit.created {
if maxLength > 0 { if maxLength > 0 {
updateProperty(edit.htmlID(), "maxlength", strconv.Itoa(maxLength), edit.session) updateProperty(edit.htmlID(), "maxlength", strconv.Itoa(maxLength), edit.session)
@ -261,7 +261,7 @@ func (edit *editViewData) set(tag string, value interface{}) bool {
case ReadOnly: case ReadOnly:
if edit.setBoolProperty(ReadOnly, value) { if edit.setBoolProperty(ReadOnly, value) {
if edit.created { if edit.created {
if IsReadOnly(edit, "") { if IsReadOnly(edit) {
updateProperty(edit.htmlID(), ReadOnly, "", edit.session) updateProperty(edit.htmlID(), ReadOnly, "", edit.session)
} else { } else {
removeProperty(edit.htmlID(), ReadOnly, edit.session) removeProperty(edit.htmlID(), ReadOnly, edit.session)
@ -275,7 +275,7 @@ func (edit *editViewData) set(tag string, value interface{}) bool {
case Spellcheck: case Spellcheck:
if edit.setBoolProperty(Spellcheck, value) { if edit.setBoolProperty(Spellcheck, value) {
if edit.created { if edit.created {
updateBoolProperty(edit.htmlID(), Spellcheck, IsSpellcheck(edit, ""), edit.session) updateBoolProperty(edit.htmlID(), Spellcheck, IsSpellcheck(edit), edit.session)
} }
edit.propertyChangedEvent(tag) edit.propertyChangedEvent(tag)
return true return true
@ -283,10 +283,10 @@ func (edit *editViewData) set(tag string, value interface{}) bool {
return false return false
case EditViewPattern: case EditViewPattern:
oldText := GetEditViewPattern(edit, "") oldText := GetEditViewPattern(edit)
if text, ok := value.(string); ok { if text, ok := value.(string); ok {
edit.properties[EditViewPattern] = text edit.properties[EditViewPattern] = text
if text = GetEditViewPattern(edit, ""); oldText != text { if text = GetEditViewPattern(edit); oldText != text {
if edit.created { if edit.created {
if text != "" { if text != "" {
updateProperty(edit.htmlID(), Pattern, text, edit.session) updateProperty(edit.htmlID(), Pattern, text, edit.session)
@ -301,9 +301,9 @@ func (edit *editViewData) set(tag string, value interface{}) bool {
return false return false
case EditViewType: case EditViewType:
oldType := GetEditViewType(edit, "") oldType := GetEditViewType(edit)
if edit.setEnumProperty(EditViewType, value, enumProperties[EditViewType].values) { if edit.setEnumProperty(EditViewType, value, enumProperties[EditViewType].values) {
if GetEditViewType(edit, "") != oldType { if GetEditViewType(edit) != oldType {
if edit.created { if edit.created {
updateInnerHTML(edit.parentHTMLID(), edit.session) updateInnerHTML(edit.parentHTMLID(), edit.session)
} }
@ -314,10 +314,10 @@ func (edit *editViewData) set(tag string, value interface{}) bool {
return false return false
case EditWrap: case EditWrap:
oldWrap := IsEditViewWrap(edit, "") oldWrap := IsEditViewWrap(edit)
if edit.setBoolProperty(EditWrap, value) { if edit.setBoolProperty(EditWrap, value) {
if GetEditViewType(edit, "") == MultiLineText { if GetEditViewType(edit) == MultiLineText {
if wrap := IsEditViewWrap(edit, ""); wrap != oldWrap { if wrap := IsEditViewWrap(edit); wrap != oldWrap {
if edit.created { if edit.created {
if wrap { if wrap {
updateProperty(edit.htmlID(), "wrap", "soft", edit.session) updateProperty(edit.htmlID(), "wrap", "soft", edit.session)
@ -333,80 +333,34 @@ func (edit *editViewData) set(tag string, value interface{}) bool {
return false return false
case EditTextChangedEvent: case EditTextChangedEvent:
ok := edit.setChangeListeners(value) listeners, ok := valueToEventListeners[EditView, string](value)
if !ok { if !ok {
notCompatibleType(tag, value) notCompatibleType(tag, value)
return false
} else if listeners == nil {
listeners = []func(EditView, string){}
} }
edit.textChangeListeners = listeners
edit.propertyChangedEvent(tag) edit.propertyChangedEvent(tag)
return ok return true
} }
return edit.viewData.set(tag, value) return edit.viewData.set(tag, value)
} }
func (edit *editViewData) setChangeListeners(value interface{}) bool { func (edit *editViewData) Get(tag string) any {
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{} {
return edit.get(edit.normalizeTag(tag)) 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) return edit.viewData.get(tag)
} }
func (edit *editViewData) AppendText(text string) { func (edit *editViewData) AppendText(text string) {
if GetEditViewType(edit, "") == MultiLineText { if GetEditViewType(edit) == MultiLineText {
if value := edit.getRaw(Text); value != nil { if value := edit.getRaw(Text); value != nil {
if textValue, ok := value.(string); ok { if textValue, ok := value.(string); ok {
textValue += text textValue += text
@ -425,7 +379,7 @@ func (edit *editViewData) AppendText(text string) {
} }
edit.set(Text, text) edit.set(Text, text)
} else { } 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 { func (edit *editViewData) htmlTag() string {
if GetEditViewType(edit, "") == MultiLineText { if GetEditViewType(edit) == MultiLineText {
return "textarea" return "textarea"
} }
return "input" return "input"
@ -447,14 +401,14 @@ func (edit *editViewData) htmlProperties(self View, buffer *strings.Builder) {
edit.viewData.htmlProperties(self, buffer) edit.viewData.htmlProperties(self, buffer)
writeSpellcheck := func() { writeSpellcheck := func() {
if spellcheck := IsSpellcheck(edit, ""); spellcheck { if spellcheck := IsSpellcheck(edit); spellcheck {
buffer.WriteString(` spellcheck="true"`) buffer.WriteString(` spellcheck="true"`)
} else { } else {
buffer.WriteString(` spellcheck="false"`) buffer.WriteString(` spellcheck="false"`)
} }
} }
editType := GetEditViewType(edit, "") editType := GetEditViewType(edit)
switch editType { switch editType {
case SingleLineText: case SingleLineText:
buffer.WriteString(` type="text" inputmode="text"`) 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"`) buffer.WriteString(` type="tel" inputmode="tel"`)
case MultiLineText: case MultiLineText:
if IsEditViewWrap(edit, "") { if IsEditViewWrap(edit) {
buffer.WriteString(` wrap="soft"`) buffer.WriteString(` wrap="soft"`)
} else { } else {
buffer.WriteString(` wrap="off"`) buffer.WriteString(` wrap="off"`)
@ -484,11 +438,11 @@ func (edit *editViewData) htmlProperties(self View, buffer *strings.Builder) {
writeSpellcheck() writeSpellcheck()
} }
if IsReadOnly(edit, "") { if IsReadOnly(edit) {
buffer.WriteString(` readonly`) buffer.WriteString(` readonly`)
} }
if maxLength := GetMaxLength(edit, ""); maxLength > 0 { if maxLength := GetMaxLength(edit); maxLength > 0 {
buffer.WriteString(` maxlength="`) buffer.WriteString(` maxlength="`)
buffer.WriteString(strconv.Itoa(maxLength)) buffer.WriteString(strconv.Itoa(maxLength))
buffer.WriteByte('"') buffer.WriteByte('"')
@ -501,21 +455,21 @@ func (edit *editViewData) htmlProperties(self View, buffer *strings.Builder) {
return textToJS(text) return textToJS(text)
} }
if hint := GetHint(edit, ""); hint != "" { if hint := GetHint(edit); hint != "" {
buffer.WriteString(` placeholder="`) buffer.WriteString(` placeholder="`)
buffer.WriteString(convertText(hint)) buffer.WriteString(convertText(hint))
buffer.WriteByte('"') buffer.WriteByte('"')
} }
buffer.WriteString(` oninput="editViewInputEvent(this)"`) buffer.WriteString(` oninput="editViewInputEvent(this)"`)
if pattern := GetEditViewPattern(edit, ""); pattern != "" { if pattern := GetEditViewPattern(edit); pattern != "" {
buffer.WriteString(` pattern="`) buffer.WriteString(` pattern="`)
buffer.WriteString(convertText(pattern)) buffer.WriteString(convertText(pattern))
buffer.WriteByte('"') buffer.WriteByte('"')
} }
if editType != MultiLineText { if editType != MultiLineText {
if text := GetText(edit, ""); text != "" { if text := GetText(edit); text != "" {
buffer.WriteString(` value="`) buffer.WriteString(` value="`)
buffer.WriteString(convertText(text)) buffer.WriteString(convertText(text))
buffer.WriteByte('"') buffer.WriteByte('"')
@ -524,25 +478,25 @@ func (edit *editViewData) htmlProperties(self View, buffer *strings.Builder) {
} }
func (edit *editViewData) htmlDisabledProperties(self View, buffer *strings.Builder) { func (edit *editViewData) htmlDisabledProperties(self View, buffer *strings.Builder) {
if IsDisabled(self, "") { if IsDisabled(self) {
buffer.WriteString(` disabled`) buffer.WriteString(` disabled`)
} }
edit.viewData.htmlDisabledProperties(self, buffer) edit.viewData.htmlDisabledProperties(self, buffer)
} }
func (edit *editViewData) htmlSubviews(self View, buffer *strings.Builder) { func (edit *editViewData) htmlSubviews(self View, buffer *strings.Builder) {
if GetEditViewType(edit, "") == MultiLineText { if GetEditViewType(edit) == MultiLineText {
buffer.WriteString(textToJS(GetText(edit, ""))) buffer.WriteString(textToJS(GetText(edit)))
} }
} }
func (edit *editViewData) handleCommand(self View, command string, data DataObject) bool { func (edit *editViewData) handleCommand(self View, command string, data DataObject) bool {
switch command { switch command {
case "textChanged": case "textChanged":
oldText := GetText(edit, "") oldText := GetText(edit)
if text, ok := data.PropertyValue("text"); ok { if text, ok := data.PropertyValue("text"); ok {
edit.properties[Text] = text edit.properties[Text] = text
if text := GetText(edit, ""); text != oldText { if text := GetText(edit); text != oldText {
edit.textChanged(text) 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. // GetText returns a text of the EditView subview.
// If the second argument (subviewID) is "" then a text of the first argument (view) is returned. // 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 { func GetText(view View, subviewID ...string) string {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
if value := view.getRaw(Text); value != 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. // GetHint returns a hint text of the subview.
// If the second argument (subviewID) is "" then a text of the first argument (view) is returned. // 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 { func GetHint(view View, subviewID ...string) string {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
if text, ok := stringProperty(view, Hint, view.Session()); ok { 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 // 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. // 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 { func GetMaxLength(view View, subviewID ...string) int {
if subviewID != "" { return intStyledProperty(view, subviewID, MaxLength, 0)
view = ViewByID(view, subviewID)
}
if view != nil {
if result, ok := intStyledProperty(view, MaxLength, 0); ok {
return result
}
}
return 0
} }
// IsReadOnly returns the true if a EditView works in read only mode. // 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. // 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 { func IsReadOnly(view View, subviewID ...string) bool {
if subviewID != "" { return boolStyledProperty(view, subviewID, ReadOnly, false)
view = ViewByID(view, subviewID)
}
if view != nil {
if result, ok := boolStyledProperty(view, ReadOnly); ok {
return result
}
}
return false
} }
// IsSpellcheck returns a value of the Spellcheck property of EditView. // 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. // 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 { func IsSpellcheck(view View, subviewID ...string) bool {
if subviewID != "" { return boolStyledProperty(view, subviewID, Spellcheck, false)
view = ViewByID(view, subviewID)
}
if view != nil {
if spellcheck, ok := boolStyledProperty(view, Spellcheck); ok {
return spellcheck
}
}
return false
} }
// GetTextChangedListeners returns the TextChangedListener list of an EditView or MultiLineEditView subview. // GetTextChangedListeners returns the TextChangedListener list of an EditView or MultiLineEditView subview.
// If there are no listeners then the empty list is returned // 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. // 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) { func GetTextChangedListeners(view View, subviewID ...string) []func(EditView, string) {
if subviewID != "" { return getEventListeners[EditView, string](view, subviewID, EditTextChangedEvent)
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){}
} }
// GetEditViewType returns a value of the Type property of EditView. // 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. // 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 { func GetEditViewType(view View, subviewID ...string) int {
if subviewID != "" { return enumStyledProperty(view, subviewID, EditViewType, SingleLineText, false)
view = ViewByID(view, subviewID)
}
if view == nil {
return SingleLineText
}
t, _ := enumStyledProperty(view, EditViewType, SingleLineText)
return t
} }
// GetEditViewPattern returns a value of the Pattern property of EditView. // 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. // 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 { func GetEditViewPattern(view View, subviewID ...string) string {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
if pattern, ok := stringProperty(view, EditViewPattern, view.Session()); ok { 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. // 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. // 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 { func IsEditViewWrap(view View, subviewID ...string) bool {
if subviewID != "" { return boolStyledProperty(view, subviewID, EditWrap, false)
view = ViewByID(view, subviewID)
}
if view != nil {
if wrap, ok := boolStyledProperty(view, EditWrap); ok {
return wrap
}
}
return false
} }
// AppendEditText appends the text to the EditView content. // 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) { func AppendEditText(view View, subviewID string, text string) {
if subviewID != "" { if subviewID != "" {
if edit := EditViewByID(view, subviewID); edit != nil { 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. // 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. // 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 { func GetCaretColor(view View, subviewID ...string) Color {
if subviewID != "" { return colorStyledProperty(view, subviewID, CaretColor, false)
view = ViewByID(view, subviewID)
}
if view == nil {
return 0
}
t, _ := colorStyledProperty(view, CaretColor)
return t
} }

View File

@ -72,7 +72,7 @@ func (file *FileInfo) initBy(node DataValue) {
// NewFilePicker create new FilePicker object and return it // NewFilePicker create new FilePicker object and return it
func NewFilePicker(session Session, params Params) FilePicker { func NewFilePicker(session Session, params Params) FilePicker {
view := new(filePickerData) view := new(filePickerData)
view.Init(session) view.init(session)
setInitParams(view, params) setInitParams(view, params)
return view return view
} }
@ -81,8 +81,8 @@ func newFilePicker(session Session) View {
return NewFilePicker(session, nil) return NewFilePicker(session, nil)
} }
func (picker *filePickerData) Init(session Session) { func (picker *filePickerData) init(session Session) {
picker.viewData.Init(session) picker.viewData.init(session)
picker.tag = "FilePicker" picker.tag = "FilePicker"
picker.files = []FileInfo{} picker.files = []FileInfo{}
picker.loader = map[int]func(FileInfo, []byte){} 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) 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 { if value == nil {
picker.remove(tag) picker.remove(tag)
return true return true
@ -151,57 +151,14 @@ func (picker *filePickerData) set(tag string, value interface{}) bool {
switch tag { switch tag {
case FileSelectedEvent: case FileSelectedEvent:
switch value := value.(type) { listeners, ok := valueToEventListeners[FilePicker, []FileInfo](value)
case func(FilePicker, []FileInfo): if !ok {
picker.fileSelectedListeners = []func(FilePicker, []FileInfo){value} notCompatibleType(tag, value)
return false
case func([]FileInfo): } else if listeners == nil {
fn := func(_ FilePicker, files []FileInfo) { listeners = []func(FilePicker, []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
} }
picker.fileSelectedListeners = listeners
picker.propertyChangedEvent(tag) picker.propertyChangedEvent(tag)
return true return true
@ -294,7 +251,7 @@ func (picker *filePickerData) htmlProperties(self View, buffer *strings.Builder)
} }
buffer.WriteString(` type="file"`) buffer.WriteString(` type="file"`)
if multiple, ok := boolStyledProperty(picker, Multiple); ok && multiple { if IsMultipleFilePicker(picker) {
buffer.WriteString(` multiple`) 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) { func (picker *filePickerData) htmlDisabledProperties(self View, buffer *strings.Builder) {
if IsDisabled(self, "") { if IsDisabled(self) {
buffer.WriteString(` disabled`) buffer.WriteString(` disabled`)
} }
picker.viewData.htmlDisabledProperties(self, buffer) 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 // 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 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 // 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 { func GetFilePickerFiles(view View, subviewID ...string) []FileInfo {
if picker := FilePickerByID(view, subviewID); picker != nil { subview := ""
if len(subviewID) > 0 {
subview = subviewID[0]
}
if picker := FilePickerByID(view, subview); picker != nil {
return picker.Files() return picker.Files()
} }
return []FileInfo{} 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. // 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. // 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 { func IsMultipleFilePicker(view View, subviewID ...string) bool {
if subviewID != "" { return boolStyledProperty(view, subviewID, Multiple, false)
view = ViewByID(view, subviewID)
}
if view != nil {
if result, ok := boolStyledProperty(view, Multiple); ok {
return result
}
}
return false
} }
// GetFilePickerAccept returns sets the list of allowed file extensions or MIME types. // 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. // 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 { func GetFilePickerAccept(view View, subviewID ...string) []string {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
accept, ok := stringProperty(view, Accept, view.Session()) 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. // GetFileSelectedListeners returns the "file-selected-event" listener list.
// If there are no listeners then the empty list is returned. // 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. // 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) { func GetFileSelectedListeners(view View, subviewID ...string) []func(FilePicker, []FileInfo) {
if subviewID != "" { return getEventListeners[FilePicker, []FileInfo](view, subviewID, FileSelectedEvent)
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){}
} }

View File

@ -20,22 +20,22 @@ const (
LostFocusEvent = "lost-focus-event" LostFocusEvent = "lost-focus-event"
) )
func valueToFocusListeners(value interface{}) ([]func(View), bool) { func valueToNoParamListeners[V any](value any) ([]func(V), bool) {
if value == nil { if value == nil {
return nil, true return nil, true
} }
switch value := value.(type) { switch value := value.(type) {
case func(View): case func(V):
return []func(View){value}, true return []func(V){value}, true
case func(): case func():
fn := func(View) { fn := func(V) {
value() value()
} }
return []func(View){fn}, true return []func(V){fn}, true
case []func(View): case []func(V):
if len(value) == 0 { if len(value) == 0 {
return nil, true return nil, true
} }
@ -51,33 +51,33 @@ func valueToFocusListeners(value interface{}) ([]func(View), bool) {
if count == 0 { if count == 0 {
return nil, true return nil, true
} }
listeners := make([]func(View), count) listeners := make([]func(V), count)
for i, v := range value { for i, v := range value {
if v == nil { if v == nil {
return nil, false return nil, false
} }
listeners[i] = func(View) { listeners[i] = func(V) {
v() v()
} }
} }
return listeners, true return listeners, true
case []interface{}: case []any:
count := len(value) count := len(value)
if count == 0 { if count == 0 {
return nil, true return nil, true
} }
listeners := make([]func(View), count) listeners := make([]func(V), count)
for i, v := range value { for i, v := range value {
if v == nil { if v == nil {
return nil, false return nil, false
} }
switch v := v.(type) { switch v := v.(type) {
case func(View): case func(V):
listeners[i] = v listeners[i] = v
case func(): case func():
listeners[i] = func(View) { listeners[i] = func(V) {
v() v()
} }
@ -96,8 +96,8 @@ var focusEvents = map[string]struct{ jsEvent, jsFunc string }{
LostFocusEvent: {jsEvent: "onblur", jsFunc: "blurEvent"}, LostFocusEvent: {jsEvent: "onblur", jsFunc: "blurEvent"},
} }
func (view *viewData) setFocusListener(tag string, value interface{}) bool { func (view *viewData) setFocusListener(tag string, value any) bool {
listeners, ok := valueToFocusListeners(value) listeners, ok := valueToNoParamListeners[View](value)
if !ok { if !ok {
notCompatibleType(tag, value) notCompatibleType(tag, value)
return false return false
@ -125,10 +125,11 @@ func (view *viewData) removeFocusListener(tag string) {
} }
} }
func getFocusListeners(view View, subviewID string, tag string) []func(View) { func getFocusListeners(view View, subviewID []string, tag string) []func(View) {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
if value := view.Get(tag); value != nil { if value := view.Get(tag); value != nil {
if result, ok := value.([]func(View)); ok { 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 // 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. // 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) { func GetFocusListeners(view View, subviewID ...string) []func(View) {
return getFocusListeners(view, subviewID, FocusEvent) return getFocusListeners(view, subviewID, FocusEvent)
} }
// GetLostFocusListeners returns a LostFocusListener list. If there are no listeners then the empty list is returned // 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. // 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) { func GetLostFocusListeners(view View, subviewID ...string) []func(View) {
return getFocusListeners(view, subviewID, LostFocusEvent) return getFocusListeners(view, subviewID, LostFocusEvent)
} }

2
go.mod
View File

@ -1,5 +1,5 @@
module github.com/anoshenko/rui module github.com/anoshenko/rui
go 1.17 go 1.18
require github.com/gorilla/websocket v1.5.0 require github.com/gorilla/websocket v1.5.0

View File

@ -17,7 +17,7 @@ type gridLayoutData struct {
// NewGridLayout create new GridLayout object and return it // NewGridLayout create new GridLayout object and return it
func NewGridLayout(session Session, params Params) GridLayout { func NewGridLayout(session Session, params Params) GridLayout {
view := new(gridLayoutData) view := new(gridLayoutData)
view.Init(session) view.init(session)
setInitParams(view, params) setInitParams(view, params)
return view return view
} }
@ -27,8 +27,8 @@ func newGridLayout(session Session) View {
} }
// Init initialize fields of GridLayout by default values // Init initialize fields of GridLayout by default values
func (gridLayout *gridLayoutData) Init(session Session) { func (gridLayout *gridLayoutData) init(session Session) {
gridLayout.viewsContainerData.Init(session) gridLayout.viewsContainerData.init(session)
gridLayout.tag = "GridLayout" gridLayout.tag = "GridLayout"
gridLayout.systemClass = "ruiGridLayout" gridLayout.systemClass = "ruiGridLayout"
} }
@ -37,15 +37,17 @@ func (gridLayout *gridLayoutData) String() string {
return getViewString(gridLayout) 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 { setValues := func(values []string) bool {
count := len(values) count := len(values)
if count > 1 { if count > 1 {
sizes := make([]interface{}, count) sizes := make([]any, count)
for i, val := range values { for i, val := range values {
val = strings.Trim(val, " \t\n\r") val = strings.Trim(val, " \t\n\r")
if isConstantName(val) { if isConstantName(val) {
sizes[i] = 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 { } else if size, err := stringToSizeUnit(val); err == nil {
sizes[i] = size sizes[i] = size
} else { } else {
@ -99,13 +101,13 @@ func (style *viewStyle) setGridCellSize(tag string, value interface{}) bool {
return false return false
} }
case []interface{}: case []any:
count := len(value) count := len(value)
if count == 0 { if count == 0 {
invalidPropertyValue(tag, value) invalidPropertyValue(tag, value)
return false return false
} }
sizes := make([]interface{}, count) sizes := make([]any, count)
for i, val := range value { for i, val := range value {
switch val := val.(type) { switch val := val.(type) {
case SizeUnit: case SizeUnit:
@ -145,7 +147,7 @@ func (style *viewStyle) gridCellSizesCSS(tag string, session Session) string {
case 1: case 1:
if cellSize[0].Type != Auto { if cellSize[0].Type != Auto {
return `repeat(auto-fill, ` + cellSize[0].cssString(`auto`) + `)` return `repeat(auto-fill, ` + cellSize[0].cssString(`auto`, session) + `)`
} }
default: default:
@ -161,14 +163,14 @@ func (style *viewStyle) gridCellSizesCSS(tag string, session Session) string {
} }
if !allAuto { if !allAuto {
if allEqual { 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() buffer := allocStringBuilder()
defer freeStringBuilder(buffer) defer freeStringBuilder(buffer)
for _, size := range cellSize { for _, size := range cellSize {
buffer.WriteRune(' ') buffer.WriteRune(' ')
buffer.WriteString(size.cssString(`auto`)) buffer.WriteString(size.cssString(`auto`, session))
} }
return buffer.String() return buffer.String()
} }
@ -195,14 +197,14 @@ func (gridLayout *gridLayoutData) normalizeTag(tag string) string {
return tag return tag
} }
func (gridLayout *gridLayoutData) Get(tag string) interface{} { func (gridLayout *gridLayoutData) Get(tag string) any {
return gridLayout.get(gridLayout.normalizeTag(tag)) return gridLayout.get(gridLayout.normalizeTag(tag))
} }
func (gridLayout *gridLayoutData) get(tag string) interface{} { func (gridLayout *gridLayoutData) get(tag string) any {
if tag == Gap { if tag == Gap {
rowGap := GetGridRowGap(gridLayout, "") rowGap := GetGridRowGap(gridLayout)
columnGap := GetGridColumnGap(gridLayout, "") columnGap := GetGridColumnGap(gridLayout)
if rowGap.Equal(columnGap) { if rowGap.Equal(columnGap) {
return rowGap 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) 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 { if value == nil {
gridLayout.remove(tag) gridLayout.remove(tag)
return true return true
} }
if tag == Gap { if tag == Gap {
if gridLayout.set(GridRowGap, value) && gridLayout.set(GridColumnGap, value) { return gridLayout.set(GridRowGap, value) && gridLayout.set(GridColumnGap, value)
gridLayout.propertyChangedEvent(Gap)
return true
}
return false
} }
if gridLayout.viewsContainerData.set(tag, value) { if gridLayout.viewsContainerData.set(tag, value) {
@ -285,7 +283,7 @@ func gridCellSizes(properties Properties, tag string, session Session) []SizeUni
case SizeUnit: case SizeUnit:
return []SizeUnit{value} return []SizeUnit{value}
case []interface{}: case []any:
result := make([]SizeUnit, len(value)) result := make([]SizeUnit, len(value))
for i, val := range value { for i, val := range value {
result[i] = AutoSize() 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) // 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. // 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 { func GetCellVerticalAlign(view View, subviewID ...string) int {
if subviewID != "" { return enumStyledProperty(view, subviewID, CellVerticalAlign, StretchAlign, false)
view = ViewByID(view, subviewID)
}
if view != nil {
if align, ok := enumStyledProperty(view, CellVerticalAlign, StretchAlign); ok {
return align
}
}
return StretchAlign
} }
// GetCellHorizontalAlign returns the vertical align of a GridLayout cell content: LeftAlign (0), RightAlign (1), CenterAlign (2), StretchAlign (3) // 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. // 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 { func GetCellHorizontalAlign(view View, subviewID ...string) int {
if subviewID != "" { return enumStyledProperty(view, subviewID, CellHorizontalAlign, StretchAlign, false)
view = ViewByID(view, subviewID)
}
if view != nil {
if align, ok := enumStyledProperty(view, CellHorizontalAlign, StretchAlign); ok {
return align
}
}
return StretchAlign
} }
// GetGridAutoFlow returns the value of the "grid-auto-flow" property // 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. // 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 { func GetGridAutoFlow(view View, subviewID ...string) int {
if subviewID != "" { return enumStyledProperty(view, subviewID, GridAutoFlow, 0, false)
view = ViewByID(view, subviewID)
}
if view != nil {
if align, ok := enumStyledProperty(view, GridAutoFlow, 0); ok {
return align
}
}
return 0
} }
// GetCellWidth returns the width of a GridLayout cell. If the result is an empty array, then the width is not set. // 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 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. // 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 { func GetCellWidth(view View, subviewID ...string) []SizeUnit {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
return gridCellSizes(view, CellWidth, view.Session()) 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. // 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 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. // 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 { func GetCellHeight(view View, subviewID ...string) []SizeUnit {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
return gridCellSizes(view, CellHeight, view.Session()) return gridCellSizes(view, CellHeight, view.Session())
@ -391,29 +365,13 @@ func GetCellHeight(view View, subviewID string) []SizeUnit {
} }
// GetGridRowGap returns the gap between GridLayout rows. // GetGridRowGap returns the gap between GridLayout rows.
// 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 GetGridRowGap(view View, subviewID string) SizeUnit { func GetGridRowGap(view View, subviewID ...string) SizeUnit {
if subviewID != "" { return sizeStyledProperty(view, subviewID, GridRowGap, false)
view = ViewByID(view, subviewID)
}
if view != nil {
if result, ok := sizeStyledProperty(view, GridRowGap); ok {
return result
}
}
return AutoSize()
} }
// GetGridColumnGap returns the gap between GridLayout columns. // GetGridColumnGap returns the gap between GridLayout columns.
// 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 GetGridColumnGap(view View, subviewID string) SizeUnit { func GetGridColumnGap(view View, subviewID ...string) SizeUnit {
if subviewID != "" { return sizeStyledProperty(view, subviewID, GridColumnGap, false)
view = ViewByID(view, subviewID)
}
if view != nil {
if result, ok := sizeStyledProperty(view, GridColumnGap); ok {
return result
}
}
return AutoSize()
} }

View File

@ -54,7 +54,7 @@ type imageViewData struct {
// NewImageView create new ImageView object and return it // NewImageView create new ImageView object and return it
func NewImageView(session Session, params Params) ImageView { func NewImageView(session Session, params Params) ImageView {
view := new(imageViewData) view := new(imageViewData)
view.Init(session) view.init(session)
setInitParams(view, params) setInitParams(view, params)
return view return view
} }
@ -64,8 +64,8 @@ func newImageView(session Session) View {
} }
// Init initialize fields of imageView by default values // Init initialize fields of imageView by default values
func (imageView *imageViewData) Init(session Session) { func (imageView *imageViewData) init(session Session) {
imageView.viewData.Init(session) imageView.viewData.init(session)
imageView.tag = "ImageView" imageView.tag = "ImageView"
//imageView.systemClass = "ruiImageView" //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) return imageView.set(imageView.normalizeTag(tag), value)
} }
func valueToImageListeners(value interface{}) ([]func(ImageView), bool) { func (imageView *imageViewData) set(tag string, value any) 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 {
if value == nil { if value == nil {
imageView.remove(tag) imageView.remove(tag)
return true return true
@ -228,8 +157,12 @@ func (imageView *imageViewData) set(tag string, value interface{}) bool {
notCompatibleType(tag, value) notCompatibleType(tag, value)
case LoadedEvent, ErrorEvent: case LoadedEvent, ErrorEvent:
if listeners, ok := valueToImageListeners(value); ok { if listeners, ok := valueToNoParamListeners[ImageView](value); ok {
imageView.properties[tag] = listeners if listeners == nil {
delete(imageView.properties, tag)
} else {
imageView.properties[tag] = listeners
}
return true return true
} }
@ -248,7 +181,7 @@ func (imageView *imageViewData) set(tag string, value interface{}) bool {
return false return false
} }
func (imageView *imageViewData) Get(tag string) interface{} { func (imageView *imageViewData) Get(tag string) any {
return imageView.viewData.get(imageView.normalizeTag(tag)) 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(` alt="`)
buffer.WriteString(textToJS(text)) buffer.WriteString(textToJS(text))
buffer.WriteString(`"`) buffer.WriteString(`"`)
@ -333,8 +266,8 @@ func (imageView *imageViewData) cssStyle(self View, builder cssBuilder) {
builder.add("object-fit", "none") builder.add("object-fit", "none")
} }
vAlign := GetImageViewVerticalAlign(imageView, "") vAlign := GetImageViewVerticalAlign(imageView)
hAlign := GetImageViewHorizontalAlign(imageView, "") hAlign := GetImageViewHorizontalAlign(imageView)
if vAlign != CenterAlign || hAlign != CenterAlign { if vAlign != CenterAlign || hAlign != CenterAlign {
var position string var position string
switch hAlign { switch hAlign {
@ -390,10 +323,10 @@ func (imageView *imageViewData) CurrentSource() string {
} }
// GetImageViewSource returns the image URL of an ImageView subview. // 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 // 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 { func GetImageViewSource(view View, subviewID ...string) string {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
@ -406,10 +339,10 @@ func GetImageViewSource(view View, subviewID string) string {
} }
// GetImageViewAltText returns an alternative text description of an ImageView subview. // 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 // 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 { func GetImageViewAltText(view View, subviewID ...string) string {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
@ -425,40 +358,19 @@ func GetImageViewAltText(view View, subviewID string) string {
// GetImageViewFit returns how the content of a replaced ImageView subview: // GetImageViewFit returns how the content of a replaced ImageView subview:
// NoneFit (0), ContainFit (1), CoverFit (2), FillFit (3), or ScaleDownFit (4). // 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 // 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 { func GetImageViewFit(view View, subviewID ...string) int {
if subviewID != "" { return enumStyledProperty(view, subviewID, Fit, NoneFit, false)
view = ViewByID(view, subviewID)
}
if value, ok := enumStyledProperty(view, Fit, NoneFit); ok {
return value
}
return 0
} }
// GetImageViewVerticalAlign return the vertical align of an ImageView subview: TopAlign (0), BottomAlign (1), CenterAlign (2) // 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 // 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 { func GetImageViewVerticalAlign(view View, subviewID ...string) int {
if subviewID != "" { return enumStyledProperty(view, subviewID, ImageVerticalAlign, LeftAlign, false)
view = ViewByID(view, subviewID)
}
if align, ok := enumStyledProperty(view, ImageVerticalAlign, LeftAlign); ok {
return align
}
return CenterAlign
} }
// GetImageViewHorizontalAlign return the vertical align of an ImageView subview: LeftAlign (0), RightAlign (1), CenterAlign (2) // 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 // 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 { func GetImageViewHorizontalAlign(view View, subviewID ...string) int {
if subviewID != "" { return enumStyledProperty(view, subviewID, ImageHorizontalAlign, LeftAlign, false)
view = ViewByID(view, subviewID)
}
if align, ok := enumStyledProperty(view, ImageHorizontalAlign, LeftAlign); ok {
return align
}
return CenterAlign
} }

View File

@ -50,34 +50,52 @@ type KeyEvent struct {
MetaKey bool 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 { if value == nil {
return nil, true return nil, true
} }
switch value := value.(type) { switch value := value.(type) {
case func(View, KeyEvent): case func(V, E):
return []func(View, KeyEvent){value}, true return []func(V, E){value}, true
case func(KeyEvent): case func(E):
fn := func(_ View, event KeyEvent) { fn := func(_ V, event E) {
value(event) value(event)
} }
return []func(View, KeyEvent){fn}, true return []func(V, E){fn}, true
case func(View): case func(V):
fn := func(view View, _ KeyEvent) { fn := func(view V, _ E) {
value(view) value(view)
} }
return []func(View, KeyEvent){fn}, true return []func(V, E){fn}, true
case func(): case func():
fn := func(View, KeyEvent) { fn := func(V, E) {
value() 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 { if len(value) == 0 {
return nil, true return nil, true
} }
@ -88,33 +106,33 @@ func valueToKeyListeners(value interface{}) ([]func(View, KeyEvent), bool) {
} }
return value, true return value, true
case []func(KeyEvent): case []func(E):
count := len(value) count := len(value)
if count == 0 { if count == 0 {
return nil, true return nil, true
} }
listeners := make([]func(View, KeyEvent), count) listeners := make([]func(V, E), count)
for i, v := range value { for i, v := range value {
if v == nil { if v == nil {
return nil, false return nil, false
} }
listeners[i] = func(_ View, event KeyEvent) { listeners[i] = func(_ V, event E) {
v(event) v(event)
} }
} }
return listeners, true return listeners, true
case []func(View): case []func(V):
count := len(value) count := len(value)
if count == 0 { if count == 0 {
return nil, true return nil, true
} }
listeners := make([]func(View, KeyEvent), count) listeners := make([]func(V, E), count)
for i, v := range value { for i, v := range value {
if v == nil { if v == nil {
return nil, false return nil, false
} }
listeners[i] = func(view View, _ KeyEvent) { listeners[i] = func(view V, _ E) {
v(view) v(view)
} }
} }
@ -125,43 +143,43 @@ func valueToKeyListeners(value interface{}) ([]func(View, KeyEvent), bool) {
if count == 0 { if count == 0 {
return nil, true return nil, true
} }
listeners := make([]func(View, KeyEvent), count) listeners := make([]func(V, E), count)
for i, v := range value { for i, v := range value {
if v == nil { if v == nil {
return nil, false return nil, false
} }
listeners[i] = func(View, KeyEvent) { listeners[i] = func(V, E) {
v() v()
} }
} }
return listeners, true return listeners, true
case []interface{}: case []any:
count := len(value) count := len(value)
if count == 0 { if count == 0 {
return nil, true return nil, true
} }
listeners := make([]func(View, KeyEvent), count) listeners := make([]func(V, E), count)
for i, v := range value { for i, v := range value {
if v == nil { if v == nil {
return nil, false return nil, false
} }
switch v := v.(type) { switch v := v.(type) {
case func(View, KeyEvent): case func(V, E):
listeners[i] = v listeners[i] = v
case func(KeyEvent): case func(E):
listeners[i] = func(_ View, event KeyEvent) { listeners[i] = func(_ V, event E) {
v(event) v(event)
} }
case func(View): case func(V):
listeners[i] = func(view View, _ KeyEvent) { listeners[i] = func(view V, _ E) {
v(view) v(view)
} }
case func(): case func():
listeners[i] = func(View, KeyEvent) { listeners[i] = func(V, E) {
v() v()
} }
@ -180,8 +198,8 @@ var keyEvents = map[string]struct{ jsEvent, jsFunc string }{
KeyUpEvent: {jsEvent: "onkeyup", jsFunc: "keyUpEvent"}, KeyUpEvent: {jsEvent: "onkeyup", jsFunc: "keyUpEvent"},
} }
func (view *viewData) setKeyListener(tag string, value interface{}) bool { func (view *viewData) setKeyListener(tag string, value any) bool {
listeners, ok := valueToKeyListeners(value) listeners, ok := valueToEventListeners[View, KeyEvent](value)
if !ok { if !ok {
notCompatibleType(tag, value) notCompatibleType(tag, value)
return false return false
@ -209,67 +227,48 @@ func (view *viewData) removeKeyListener(tag string) {
} }
} }
func getKeyListeners(view View, subviewID string, tag string) []func(View, KeyEvent) { func getEventListeners[V View, E any](view View, subviewID []string, tag string) []func(V, E) {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
if value := view.Get(tag); value != 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 result
} }
} }
} }
return []func(View, KeyEvent){} return []func(V, E){}
} }
func keyEventsHtml(view View, buffer *strings.Builder) { func keyEventsHtml(view View, buffer *strings.Builder) {
for tag, js := range keyEvents { 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)" `) buffer.WriteString(js.jsEvent + `="` + js.jsFunc + `(this, event)" `)
} }
} }
} }
func handleKeyEvents(view View, tag string, data DataObject) { func handleKeyEvents(view View, tag string, data DataObject) {
listeners := getKeyListeners(view, "", tag) listeners := getEventListeners[View, KeyEvent](view, nil, tag)
if len(listeners) == 0 { if len(listeners) > 0 {
return var event KeyEvent
} event.init(data)
getBool := func(tag string) bool { for _, listener := range listeners {
if value, ok := data.PropertyValue(tag); ok && value == "1" { listener(view, event)
return true
} }
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. // 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. // 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) { func GetKeyDownListeners(view View, subviewID ...string) []func(View, KeyEvent) {
return getKeyListeners(view, subviewID, KeyDownEvent) 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. // 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. // 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) { func GetKeyUpListeners(view View, subviewID ...string) []func(View, KeyEvent) {
return getKeyListeners(view, subviewID, KeyUpEvent) return getEventListeners[View, KeyEvent](view, subviewID, KeyUpEvent)
} }

View File

@ -77,7 +77,7 @@ func (adapter *viewListAdapter) ListItem(index int, session Session) View {
func (adapter *viewListAdapter) IsListItemEnabled(index int) bool { func (adapter *viewListAdapter) IsListItemEnabled(index int) bool {
if index >= 0 && index < len(adapter.items) { if index >= 0 && index < len(adapter.items) {
return !IsDisabled(adapter.items[index], "") return !IsDisabled(adapter.items[index])
} }
return true return true
} }

View File

@ -33,7 +33,7 @@ type listLayoutData struct {
// NewListLayout create new ListLayout object and return it // NewListLayout create new ListLayout object and return it
func NewListLayout(session Session, params Params) ListLayout { func NewListLayout(session Session, params Params) ListLayout {
view := new(listLayoutData) view := new(listLayoutData)
view.Init(session) view.init(session)
setInitParams(view, params) setInitParams(view, params)
return view return view
} }
@ -43,8 +43,8 @@ func newListLayout(session Session) View {
} }
// Init initialize fields of ViewsAlignContainer by default values // Init initialize fields of ViewsAlignContainer by default values
func (listLayout *listLayoutData) Init(session Session) { func (listLayout *listLayoutData) init(session Session) {
listLayout.viewsContainerData.Init(session) listLayout.viewsContainerData.init(session)
listLayout.tag = "ListLayout" listLayout.tag = "ListLayout"
listLayout.systemClass = "ruiListLayout" listLayout.systemClass = "ruiListLayout"
} }
@ -58,19 +58,41 @@ func (listLayout *listLayoutData) normalizeTag(tag string) string {
switch tag { switch tag {
case "wrap": case "wrap":
tag = ListWrap tag = ListWrap
case "row-gap":
return ListRowGap
case ColumnGap:
return ListColumnGap
} }
return tag return tag
} }
func (listLayout *listLayoutData) Get(tag string) interface{} { func (listLayout *listLayoutData) Get(tag string) any {
return listLayout.get(listLayout.normalizeTag(tag)) 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) { func (listLayout *listLayoutData) Remove(tag string) {
listLayout.remove(listLayout.normalizeTag(tag)) listLayout.remove(listLayout.normalizeTag(tag))
} }
func (listLayout *listLayoutData) remove(tag string) { func (listLayout *listLayoutData) remove(tag string) {
if tag == Gap {
listLayout.remove(ListRowGap)
listLayout.remove(ListColumnGap)
return
}
listLayout.viewsContainerData.remove(tag) listLayout.viewsContainerData.remove(tag)
if listLayout.created { if listLayout.created {
switch tag { 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) 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 { if value == nil {
listLayout.remove(tag) listLayout.remove(tag)
return true return true
} }
if tag == Gap {
return listLayout.set(ListRowGap, value) && listLayout.set(ListColumnGap, value)
}
if listLayout.viewsContainerData.set(tag, value) { if listLayout.viewsContainerData.set(tag, value) {
if listLayout.created { if listLayout.created {
switch tag { 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: // GetListVerticalAlign returns the vertical align of a ListLayout or ListView sibview:
// TopAlign (0), BottomAlign (1), CenterAlign (2), or StretchAlign (3) // 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. // 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 { func GetListVerticalAlign(view View, subviewID ...string) int {
if subviewID != "" { return enumStyledProperty(view, subviewID, VerticalAlign, TopAlign, false)
view = ViewByID(view, subviewID)
}
if view == nil {
return LeftAlign
}
result, _ := enumProperty(view, VerticalAlign, view.Session(), 0)
return result
} }
// GetListHorizontalAlign returns the vertical align of a ListLayout or ListView subview: // GetListHorizontalAlign returns the vertical align of a ListLayout or ListView subview:
// LeftAlign (0), RightAlign (1), CenterAlign (2), or StretchAlign (3) // 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. // 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 { func GetListHorizontalAlign(view View, subviewID ...string) int {
if subviewID != "" { return enumStyledProperty(view, subviewID, HorizontalAlign, LeftAlign, false)
view = ViewByID(view, subviewID)
}
if view == nil {
return TopAlign
}
result, _ := enumProperty(view, HorizontalAlign, view.Session(), 0)
return result
} }
// GetListOrientation returns the orientation of a ListLayout or ListView subview: // GetListOrientation returns the orientation of a ListLayout or ListView subview:
// TopDownOrientation (0), StartToEndOrientation (1), BottomUpOrientation (2), or EndToStartOrientation (3) // 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. // 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 { func GetListOrientation(view View, subviewID ...string) int {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { 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: // GetListWrap returns the wrap type of a ListLayout or ListView subview:
// WrapOff (0), WrapOn (1), or WrapReverse (2) // ListWrapOff (0), ListWrapOn (1), or ListWrapReverse (2)
// 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 GetListWrap(view View, subviewID string) int { func GetListWrap(view View, subviewID ...string) int {
if subviewID != "" { return enumStyledProperty(view, subviewID, ListWrap, ListWrapOff, false)
view = ViewByID(view, subviewID) }
}
if view != nil { // GetListRowGap returns the gap between ListLayout or ListView rows.
if result, ok := enumStyledProperty(view, ListWrap, 0); ok { // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
return result func GetListRowGap(view View, subviewID ...string) SizeUnit {
} return sizeStyledProperty(view, subviewID, ListRowGap, false)
} }
return ListWrapOff
// 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)
} }

View File

@ -69,7 +69,7 @@ type listViewData struct {
// NewListView creates the new list view // NewListView creates the new list view
func NewListView(session Session, params Params) ListView { func NewListView(session Session, params Params) ListView {
view := new(listViewData) view := new(listViewData)
view.Init(session) view.init(session)
setInitParams(view, params) setInitParams(view, params)
return view return view
} }
@ -79,8 +79,8 @@ func newListView(session Session) View {
} }
// Init initialize fields of ViewsContainer by default values // Init initialize fields of ViewsContainer by default values
func (listView *listViewData) Init(session Session) { func (listView *listViewData) init(session Session) {
listView.viewData.Init(session) listView.viewData.init(session)
listView.tag = "ListView" listView.tag = "ListView"
listView.systemClass = "ruiListView" listView.systemClass = "ruiListView"
listView.items = []View{} listView.items = []View{}
@ -110,6 +110,12 @@ func (listView *listViewData) normalizeTag(tag string) string {
case "wrap": case "wrap":
tag = ListWrap tag = ListWrap
case "row-gap":
return ListRowGap
case ColumnGap:
return ListColumnGap
} }
return tag return tag
} }
@ -120,6 +126,10 @@ func (listView *listViewData) Remove(tag string) {
func (listView *listViewData) remove(tag string) { func (listView *listViewData) remove(tag string) {
switch tag { switch tag {
case Gap:
listView.remove(ListRowGap)
listView.remove(ListColumnGap)
case Checked: case Checked:
if len(listView.checkedItem) > 0 { if len(listView.checkedItem) > 0 {
listView.checkedItem = []int{} listView.checkedItem = []int{}
@ -148,7 +158,7 @@ func (listView *listViewData) remove(tag string) {
} }
case Current: case Current:
current := GetCurrent(listView, "") current := GetCurrent(listView)
delete(listView.properties, tag) delete(listView.properties, tag)
if listView.created { if listView.created {
updateInnerHTML(listView.htmlID(), listView.session) 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) 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 { if value == nil {
listView.remove(tag) listView.remove(tag)
return true return true
} }
switch tag { switch tag {
case Gap:
return listView.set(ListRowGap, value) && listView.set(ListColumnGap, value)
case ListItemClickedEvent: case ListItemClickedEvent:
listeners := listView.valueToItemListeners(value) listeners, ok := valueToEventListeners[ListView, int](value)
if listeners == nil { if !ok {
notCompatibleType(tag, value) notCompatibleType(tag, value)
return false return false
} else if listeners == nil {
listeners = []func(ListView, int){}
} }
listView.clickedListeners = listeners listView.clickedListeners = listeners
listView.propertyChangedEvent(tag) listView.propertyChangedEvent(tag)
return true return true
case ListItemSelectedEvent: case ListItemSelectedEvent:
listeners := listView.valueToItemListeners(value) listeners, ok := valueToEventListeners[ListView, int](value)
if listeners == nil { if !ok {
notCompatibleType(tag, value) notCompatibleType(tag, value)
return false return false
} else if listeners == nil {
listeners = []func(ListView, int){}
} }
listView.selectedListeners = listeners listView.selectedListeners = listeners
listView.propertyChangedEvent(tag) listView.propertyChangedEvent(tag)
return true return true
case ListItemCheckedEvent: case ListItemCheckedEvent:
if !listView.setItemCheckedEvent(value) { listeners, ok := valueToEventListeners[ListView, []int](value)
if !ok {
notCompatibleType(tag, value)
return false return false
} else if listeners == nil {
listeners = []func(ListView, []int){}
} }
listView.checkedListeners = listeners
listView.propertyChangedEvent(tag) listView.propertyChangedEvent(tag)
return true return true
@ -242,11 +264,11 @@ func (listView *listViewData) set(tag string, value interface{}) bool {
} }
case Current: case Current:
oldCurrent := GetCurrent(listView, "") oldCurrent := GetCurrent(listView)
if !listView.setIntProperty(Current, value) { if !listView.setIntProperty(Current, value) {
return false return false
} }
current := GetCurrent(listView, "") current := GetCurrent(listView)
if oldCurrent == current { if oldCurrent == current {
return true return true
} }
@ -255,7 +277,7 @@ func (listView *listViewData) set(tag string, value interface{}) bool {
listener(listView, current) 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) result := listView.viewData.set(tag, value)
if result && listView.created { if result && listView.created {
updateInnerHTML(listView.htmlID(), listView.session) updateInnerHTML(listView.htmlID(), listView.session)
@ -288,67 +310,18 @@ func (listView *listViewData) set(tag string, value interface{}) bool {
return true return true
} }
func (listView *listViewData) setItemCheckedEvent(value interface{}) bool { func (listView *listViewData) Get(tag string) any {
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{} {
return listView.get(listView.normalizeTag(tag)) return listView.get(listView.normalizeTag(tag))
} }
func (listView *listViewData) get(tag string) interface{} { func (listView *listViewData) get(tag string) any {
switch tag { switch tag {
case Gap:
if rowGap := GetListRowGap(listView); rowGap.Equal(GetListColumnGap(listView)) {
return rowGap
}
return AutoSize()
case ListItemClickedEvent: case ListItemClickedEvent:
return listView.clickedListeners return listView.clickedListeners
@ -376,7 +349,7 @@ func (listView *listViewData) get(tag string) interface{} {
return listView.viewData.get(tag) return listView.viewData.get(tag)
} }
func (listView *listViewData) setItems(value interface{}) bool { func (listView *listViewData) setItems(value any) bool {
switch value := value.(type) { switch value := value.(type) {
case []string: case []string:
listView.adapter = NewTextListAdapter(value, nil) listView.adapter = NewTextListAdapter(value, nil)
@ -412,7 +385,7 @@ func (listView *listViewData) setItems(value interface{}) bool {
listView.adapter = NewTextListAdapter(items, nil) listView.adapter = NewTextListAdapter(items, nil)
} }
case []interface{}: case []any:
items := make([]View, len(value)) items := make([]View, len(value))
for i, val := range value { for i, val := range value {
switch value := val.(type) { switch value := val.(type) {
@ -460,62 +433,7 @@ func (listView *listViewData) setItems(value interface{}) bool {
return true return true
} }
func (listView *listViewData) valueToItemListeners(value interface{}) []func(ListView, int) { func (listView *listViewData) setChecked(value any) bool {
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 {
var checked []int var checked []int
if value == nil { if value == nil {
checked = []int{} checked = []int{}
@ -544,7 +462,7 @@ func (listView *listViewData) setChecked(value interface{}) bool {
} }
} }
switch GetListViewCheckbox(listView, "") { switch GetListViewCheckbox(listView) {
case SingleCheckbox: case SingleCheckbox:
count := len(checked) count := len(checked)
if count > 1 { if count > 1 {
@ -628,14 +546,14 @@ func (listView *listViewData) getItemFrames() []Frame {
func (listView *listViewData) itemAlign(self View, buffer *strings.Builder) { func (listView *listViewData) itemAlign(self View, buffer *strings.Builder) {
values := enumProperties[ItemHorizontalAlign].cssValues 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(" justify-items: ")
buffer.WriteString(values[hAlign]) buffer.WriteString(values[hAlign])
buffer.WriteRune(';') buffer.WriteRune(';')
} }
values = enumProperties[ItemVerticalAlign].cssValues 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(" align-items: ")
buffer.WriteString(values[vAlign]) buffer.WriteString(values[vAlign])
buffer.WriteRune(';') buffer.WriteRune(';')
@ -643,15 +561,15 @@ func (listView *listViewData) itemAlign(self View, buffer *strings.Builder) {
} }
func (listView *listViewData) itemSize(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(` min-width: `)
buffer.WriteString(itemWidth.cssString("")) buffer.WriteString(itemWidth.cssString("", listView.Session()))
buffer.WriteRune(';') buffer.WriteRune(';')
} }
if itemHeight := GetListItemHeight(listView, ""); itemHeight.Type != Auto { if itemHeight := GetListItemHeight(listView); itemHeight.Type != Auto {
buffer.WriteString(` min-height: `) buffer.WriteString(` min-height: `)
buffer.WriteString(itemHeight.cssString("")) buffer.WriteString(itemHeight.cssString("", listView.Session()))
buffer.WriteRune(';') 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 { if gap, ok := sizeConstant(listView.session, "ruiCheckboxGap"); ok && gap.Type != Auto {
itemStyleBuilder.WriteString(` grid-gap: `) itemStyleBuilder.WriteString(` grid-gap: `)
itemStyleBuilder.WriteString(gap.cssString("auto")) itemStyleBuilder.WriteString(gap.cssString("auto", listView.Session()))
itemStyleBuilder.WriteRune(';') itemStyleBuilder.WriteRune(';')
} }
@ -801,14 +719,14 @@ func (listView *listViewData) checkboxSubviews(self View, buffer *strings.Builde
count := listView.adapter.ListSize() count := listView.adapter.ListSize()
listViewID := listView.htmlID() listViewID := listView.htmlID()
hCheckboxAlign := GetListViewCheckboxHorizontalAlign(listView, "") hCheckboxAlign := GetListViewCheckboxHorizontalAlign(listView)
vCheckboxAlign := GetListViewCheckboxVerticalAlign(listView, "") vCheckboxAlign := GetListViewCheckboxVerticalAlign(listView)
itemDiv := listView.checkboxItemDiv(self, checkbox, hCheckboxAlign, vCheckboxAlign) itemDiv := listView.checkboxItemDiv(self, checkbox, hCheckboxAlign, vCheckboxAlign)
onDiv, offDiv, contentDiv := listView.getDivs(self, checkbox, hCheckboxAlign, vCheckboxAlign) onDiv, offDiv, contentDiv := listView.getDivs(self, checkbox, hCheckboxAlign, vCheckboxAlign)
current := GetCurrent(listView, "") current := GetCurrent(listView)
checkedItems := GetListViewCheckedItems(listView, "") checkedItems := GetListViewCheckedItems(listView)
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
buffer.WriteString(`<div id="`) buffer.WriteString(`<div id="`)
buffer.WriteString(listViewID) buffer.WriteString(listViewID)
@ -864,7 +782,7 @@ func (listView *listViewData) noneCheckboxSubviews(self View, buffer *strings.Bu
itemStyleBuilder.WriteString(`" onclick="listItemClickEvent(this, event)"`) itemStyleBuilder.WriteString(`" onclick="listItemClickEvent(this, event)"`)
itemStyle := itemStyleBuilder.String() itemStyle := itemStyleBuilder.String()
current := GetCurrent(listView, "") current := GetCurrent(listView)
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
buffer.WriteString(`<div id="`) buffer.WriteString(`<div id="`)
buffer.WriteString(listViewID) buffer.WriteString(listViewID)
@ -893,9 +811,9 @@ func (listView *listViewData) noneCheckboxSubviews(self View, buffer *strings.Bu
func (listView *listViewData) updateCheckboxItem(index int, checked bool) { func (listView *listViewData) updateCheckboxItem(index int, checked bool) {
checkbox := GetListViewCheckbox(listView, "") checkbox := GetListViewCheckbox(listView)
hCheckboxAlign := GetListViewCheckboxHorizontalAlign(listView, "") hCheckboxAlign := GetListViewCheckboxHorizontalAlign(listView)
vCheckboxAlign := GetListViewCheckboxVerticalAlign(listView, "") vCheckboxAlign := GetListViewCheckboxVerticalAlign(listView)
onDiv, offDiv, contentDiv := listView.getDivs(listView, checkbox, hCheckboxAlign, vCheckboxAlign) onDiv, offDiv, contentDiv := listView.getDivs(listView, checkbox, hCheckboxAlign, vCheckboxAlign)
buffer := allocStringBuilder() buffer := allocStringBuilder()
@ -937,7 +855,7 @@ func (listView *listViewData) htmlProperties(self View, buffer *strings.Builder)
buffer.WriteString(`" data-bluritemstyle="`) buffer.WriteString(`" data-bluritemstyle="`)
buffer.WriteString(listView.currentInactiveStyle()) buffer.WriteString(listView.currentInactiveStyle())
buffer.WriteString(`"`) buffer.WriteString(`"`)
current := GetCurrent(listView, "") current := GetCurrent(listView)
if listView.adapter != nil && current >= 0 && current < listView.adapter.ListSize() { if listView.adapter != nil && current >= 0 && current < listView.adapter.ListSize() {
buffer.WriteString(` data-current="`) buffer.WriteString(` data-current="`)
buffer.WriteString(listView.htmlID()) 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) { func (listView *listViewData) cssStyle(self View, builder cssBuilder) {
listView.viewData.cssStyle(self, builder) listView.viewData.cssStyle(self, builder)
if GetListWrap(listView, "") != WrapOff { if GetListWrap(listView) != WrapOff {
switch GetListOrientation(listView, "") { switch GetListOrientation(listView) {
case TopDownOrientation, BottomUpOrientation: case TopDownOrientation, BottomUpOrientation:
builder.add(`max-height`, `100%`) builder.add(`max-height`, `100%`)
default: default:
@ -976,8 +894,20 @@ func (listView *listViewData) htmlSubviews(self View, buffer *strings.Builder) {
buffer.WriteString(`<div style="display: flex; align-content: stretch;`) buffer.WriteString(`<div style="display: flex; align-content: stretch;`)
wrap := GetListWrap(listView, "") if gap := GetListRowGap(listView); gap.Type != Auto {
orientation := GetListOrientation(listView, "") 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) rows := (orientation == StartToEndOrientation || orientation == EndToStartOrientation)
if rows { if rows {
@ -1018,29 +948,27 @@ func (listView *listViewData) htmlSubviews(self View, buffer *strings.Builder) {
} }
value := "" value := ""
if align, ok := enumStyledProperty(listView, HorizontalAlign, LeftAlign); ok { switch GetListHorizontalAlign(listView) {
switch align { case LeftAlign:
case LeftAlign: if (!rows && wrap == ListWrapReverse) || orientation == EndToStartOrientation {
if (!rows && wrap == ListWrapReverse) || orientation == EndToStartOrientation { value = `flex-end`
value = `flex-end` } else {
} else { value = `flex-start`
value = `flex-start` }
} case RightAlign:
case RightAlign: if (!rows && wrap == ListWrapReverse) || orientation == EndToStartOrientation {
if (!rows && wrap == ListWrapReverse) || orientation == EndToStartOrientation { value = `flex-start`
value = `flex-start` } else {
} else { value = `flex-end`
value = `flex-end` }
} case CenterAlign:
case CenterAlign: value = `center`
value = `center`
case StretchAlign: case StretchAlign:
if rows { if rows {
value = `space-between` value = `space-between`
} else { } else {
value = `stretch` value = `stretch`
}
} }
} }
@ -1053,29 +981,27 @@ func (listView *listViewData) htmlSubviews(self View, buffer *strings.Builder) {
} }
value = "" value = ""
if align, ok := enumStyledProperty(listView, VerticalAlign, TopAlign); ok { switch GetListVerticalAlign(listView) {
switch align { case TopAlign:
case TopAlign: if (rows && wrap == ListWrapReverse) || orientation == BottomUpOrientation {
if (rows && wrap == ListWrapReverse) || orientation == BottomUpOrientation { value = `flex-end`
value = `flex-end` } else {
} else { value = `flex-start`
value = `flex-start` }
} case BottomAlign:
case BottomAlign: if (rows && wrap == ListWrapReverse) || orientation == BottomUpOrientation {
if (rows && wrap == ListWrapReverse) || orientation == BottomUpOrientation { value = `flex-start`
value = `flex-start` } else {
} else { value = `flex-end`
value = `flex-end` }
} case CenterAlign:
case CenterAlign: value = `center`
value = `center`
case StretchAlign: case StretchAlign:
if rows { if rows {
value = `stretch` value = `stretch`
} else { } else {
value = `space-between` value = `space-between`
}
} }
} }
@ -1089,7 +1015,7 @@ func (listView *listViewData) htmlSubviews(self View, buffer *strings.Builder) {
buffer.WriteString(`">`) buffer.WriteString(`">`)
checkbox := GetListViewCheckbox(listView, "") checkbox := GetListViewCheckbox(listView)
if checkbox == NoneCheckbox { if checkbox == NoneCheckbox {
listView.noneCheckboxSubviews(self, buffer) listView.noneCheckboxSubviews(self, buffer)
} else { } else {
@ -1130,9 +1056,9 @@ func (listView *listViewData) handleCommand(self View, command string, data Data
} }
func (listView *listViewData) onItemClick() { func (listView *listViewData) onItemClick() {
current := GetCurrent(listView, "") current := GetCurrent(listView)
if current >= 0 && !IsDisabled(listView, "") { if current >= 0 && !IsDisabled(listView) {
checkbox := GetListViewCheckbox(listView, "") checkbox := GetListViewCheckbox(listView)
m: m:
switch checkbox { switch checkbox {
case SingleCheckbox: 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) // GetVerticalAlign return the vertical align of a list: TopAlign (0), BottomAlign (1), CenterAlign (2), StretchAlign (3)
func GetVerticalAlign(view View) int { // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
if align, ok := enumProperty(view, VerticalAlign, view.Session(), TopAlign); ok { func GetVerticalAlign(view View, subviewID ...string) int {
return align return enumStyledProperty(view, subviewID, VerticalAlign, TopAlign, false)
}
return TopAlign
} }
// GetHorizontalAlign return the vertical align of a list: LeftAlign (0), RightAlign (1), CenterAlign (2), StretchAlign (3) // GetHorizontalAlign return the vertical align of a list/checkbox: LeftAlign (0), RightAlign (1), CenterAlign (2), StretchAlign (3)
func GetHorizontalAlign(view View) int { // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
if align, ok := enumProperty(view, HorizontalAlign, view.Session(), LeftAlign); ok { func GetHorizontalAlign(view View, subviewID ...string) int {
return align return enumStyledProperty(view, subviewID, HorizontalAlign, LeftAlign, false)
}
return LeftAlign
} }
// GetListItemClickedListeners returns a ListItemClickedListener of the ListView. // GetListItemClickedListeners returns a ListItemClickedListener of the ListView.
// If there are no listeners then the empty list is returned // 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. // 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) { func GetListItemClickedListeners(view View, subviewID ...string) []func(ListView, int) {
if subviewID != "" { return getEventListeners[ListView, int](view, subviewID, ListItemClickedEvent)
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){}
} }
// GetListItemSelectedListeners returns a ListItemSelectedListener of the ListView. // GetListItemSelectedListeners returns a ListItemSelectedListener of the ListView.
// If there are no listeners then the empty list is returned // 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. // 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) { func GetListItemSelectedListeners(view View, subviewID ...string) []func(ListView, int) {
if subviewID != "" { return getEventListeners[ListView, int](view, subviewID, ListItemSelectedEvent)
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){}
} }
// GetListItemCheckedListeners returns a ListItemCheckedListener of the ListView. // GetListItemCheckedListeners returns a ListItemCheckedListener of the ListView.
// If there are no listeners then the empty list is returned // 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. // 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) { func GetListItemCheckedListeners(view View, subviewID ...string) []func(ListView, []int) {
if subviewID != "" { return getEventListeners[ListView, []int](view, subviewID, ListItemCheckedEvent)
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){}
} }
// GetListItemWidth returns the width of a ListView item. // GetListItemWidth returns the width of a ListView item.
// 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 GetListItemWidth(view View, subviewID string) SizeUnit { func GetListItemWidth(view View, subviewID ...string) SizeUnit {
if subviewID != "" { return sizeStyledProperty(view, subviewID, ItemWidth, false)
view = ViewByID(view, subviewID)
}
if view != nil {
result, _ := sizeProperty(view, ItemWidth, view.Session())
return result
}
return AutoSize()
} }
// GetListItemHeight returns the height of a ListView item. // GetListItemHeight returns the height of a ListView item.
// 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 GetListItemHeight(view View, subviewID string) SizeUnit { func GetListItemHeight(view View, subviewID ...string) SizeUnit {
if subviewID != "" { return sizeStyledProperty(view, subviewID, ItemHeight, false)
view = ViewByID(view, subviewID)
}
if view != nil {
result, _ := sizeProperty(view, ItemHeight, view.Session())
return result
}
return AutoSize()
} }
// GetListViewCheckbox returns the ListView checkbox type: NoneCheckbox (0), SingleCheckbox (1), or MultipleCheckbox (2). // 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. // 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 { func GetListViewCheckbox(view View, subviewID ...string) int {
if subviewID != "" { return enumStyledProperty(view, subviewID, ItemCheckbox, 0, false)
view = ViewByID(view, subviewID)
}
if view != nil {
result, _ := enumProperty(view, ItemCheckbox, view.Session(), 0)
return result
}
return 0
} }
// GetListViewCheckedItems returns the array of ListView checked items. // GetListViewCheckedItems returns the array of ListView checked items.
// 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 GetListViewCheckedItems(view View, subviewID string) []int { func GetListViewCheckedItems(view View, subviewID ...string) []int {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
if listView, ok := view.(ListView); ok { if listView, ok := view.(ListView); ok {
checkedItems := listView.getCheckedItems() checkedItems := listView.getCheckedItems()
switch GetListViewCheckbox(view, "") { switch GetListViewCheckbox(view) {
case NoneCheckbox: case NoneCheckbox:
return []int{} return []int{}
@ -1334,66 +1206,34 @@ func IsListViewCheckedItem(view View, subviewID string, index int) bool {
// GetListViewCheckboxVerticalAlign returns the vertical align of the ListView checkbox: // GetListViewCheckboxVerticalAlign returns the vertical align of the ListView checkbox:
// TopAlign (0), BottomAlign (1), CenterAlign (2) // TopAlign (0), BottomAlign (1), CenterAlign (2)
// 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 GetListViewCheckboxVerticalAlign(view View, subviewID string) int { func GetListViewCheckboxVerticalAlign(view View, subviewID ...string) int {
if subviewID != "" { return enumStyledProperty(view, subviewID, CheckboxVerticalAlign, TopAlign, false)
view = ViewByID(view, subviewID)
}
if view != nil {
if align, ok := enumProperty(view, CheckboxVerticalAlign, view.Session(), TopAlign); ok {
return align
}
}
return TopAlign
} }
// GetListViewCheckboxHorizontalAlign returns the horizontal align of the ListView checkbox: // GetListViewCheckboxHorizontalAlign returns the horizontal align of the ListView checkbox:
// LeftAlign (0), RightAlign (1), CenterAlign (2) // LeftAlign (0), RightAlign (1), CenterAlign (2)
// 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 GetListViewCheckboxHorizontalAlign(view View, subviewID string) int { func GetListViewCheckboxHorizontalAlign(view View, subviewID ...string) int {
if subviewID != "" { return enumStyledProperty(view, subviewID, CheckboxHorizontalAlign, LeftAlign, false)
view = ViewByID(view, subviewID)
}
if view != nil {
if align, ok := enumProperty(view, CheckboxHorizontalAlign, view.Session(), LeftAlign); ok {
return align
}
}
return LeftAlign
} }
// GetListItemVerticalAlign returns the vertical align of the ListView item content: // GetListItemVerticalAlign returns the vertical align of the ListView item content:
// TopAlign (0), BottomAlign (1), CenterAlign (2) // TopAlign (0), BottomAlign (1), CenterAlign (2)
// 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 GetListItemVerticalAlign(view View, subviewID string) int { func GetListItemVerticalAlign(view View, subviewID ...string) int {
if subviewID != "" { return enumStyledProperty(view, subviewID, ItemVerticalAlign, TopAlign, false)
view = ViewByID(view, subviewID)
}
if view != nil {
if align, ok := enumProperty(view, ItemVerticalAlign, view.Session(), TopAlign); ok {
return align
}
}
return TopAlign
} }
// ItemHorizontalAlign returns the horizontal align of the ListView item content: // ItemHorizontalAlign returns the horizontal align of the ListView item content:
// LeftAlign (0), RightAlign (1), CenterAlign (2), StretchAlign (3) // LeftAlign (0), RightAlign (1), CenterAlign (2), StretchAlign (3)
// 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 GetListItemHorizontalAlign(view View, subviewID string) int { func GetListItemHorizontalAlign(view View, subviewID ...string) int {
if subviewID != "" { return enumStyledProperty(view, subviewID, ItemHorizontalAlign, LeftAlign, false)
view = ViewByID(view, subviewID)
}
if view != nil {
if align, ok := enumProperty(view, ItemHorizontalAlign, view.Session(), LeftAlign); ok {
return align
}
}
return LeftAlign
} }
// GetListItemFrame - returns the location and size of the ListView item in pixels. // 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 { func GetListItemFrame(view View, subviewID string, index int) Frame {
if subviewID != "" { if subviewID != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID)
@ -1410,10 +1250,10 @@ func GetListItemFrame(view View, subviewID string, index int) Frame {
} }
// GetListViewAdapter - returns the ListView adapter. // GetListViewAdapter - returns the ListView adapter.
// 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 GetListViewAdapter(view View, subviewID string) ListAdapter { func GetListViewAdapter(view View, subviewID ...string) ListAdapter {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
if value := view.Get(Items); value != nil { if value := view.Get(Items); value != nil {
@ -1426,11 +1266,12 @@ func GetListViewAdapter(view View, subviewID string) ListAdapter {
} }
// ReloadListViewData updates ListView content // ReloadListViewData updates ListView content
// If the second argument (subviewID) is "" then content the first argument (view) is updated. // 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) { func ReloadListViewData(view View, subviewID ...string) {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
if listView, ok := view.(ListView); ok { if listView, ok := view.(ListView); ok {
listView.ReloadListViewData() listView.ReloadListViewData()

View File

@ -163,8 +163,8 @@ type MediaSource struct {
MimeType string MimeType string
} }
func (player *mediaPlayerData) Init(session Session) { func (player *mediaPlayerData) init(session Session) {
player.viewData.Init(session) player.viewData.init(session)
player.tag = "MediaPlayer" player.tag = "MediaPlayer"
} }
@ -185,11 +185,11 @@ func (player *mediaPlayerData) remove(tag string) {
player.propertyChanged(tag) 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) 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 { if value == nil {
player.remove(tag) player.remove(tag)
return true return true
@ -205,7 +205,7 @@ func (player *mediaPlayerData) set(tag string, value interface{}) bool {
case AbortEvent, CanPlayEvent, CanPlayThroughEvent, CompleteEvent, EmptiedEvent, LoadStartEvent, case AbortEvent, CanPlayEvent, CanPlayThroughEvent, CompleteEvent, EmptiedEvent, LoadStartEvent,
EndedEvent, LoadedDataEvent, LoadedMetadataEvent, PauseEvent, PlayEvent, PlayingEvent, EndedEvent, LoadedDataEvent, LoadedMetadataEvent, PauseEvent, PlayEvent, PlayingEvent,
ProgressEvent, SeekedEvent, SeekingEvent, StalledEvent, SuspendEvent, WaitingEvent: ProgressEvent, SeekedEvent, SeekingEvent, StalledEvent, SuspendEvent, WaitingEvent:
if listeners, ok := valueToPlayerListeners(value); ok { if listeners, ok := valueToNoParamListeners[MediaPlayer](value); ok {
if listeners == nil { if listeners == nil {
delete(player.properties, tag) delete(player.properties, tag)
} else { } else {
@ -218,7 +218,7 @@ func (player *mediaPlayerData) set(tag string, value interface{}) bool {
notCompatibleType(tag, value) notCompatibleType(tag, value)
case DurationChangedEvent, RateChangedEvent, TimeUpdateEvent, VolumeChangedEvent: case DurationChangedEvent, RateChangedEvent, TimeUpdateEvent, VolumeChangedEvent:
if listeners, ok := valueToPlayerTimeListeners(value); ok { if listeners, ok := valueToEventListeners[MediaPlayer, float64](value); ok {
if listeners == nil { if listeners == nil {
delete(player.properties, tag) delete(player.properties, tag)
} else { } else {
@ -257,7 +257,7 @@ func (player *mediaPlayerData) set(tag string, value interface{}) bool {
return false return false
} }
func (player *mediaPlayerData) setSource(value interface{}) bool { func (player *mediaPlayerData) setSource(value any) bool {
switch value := value.(type) { switch value := value.(type) {
case string: case string:
src := MediaSource{Url: value, MimeType: ""} src := MediaSource{Url: value, MimeType: ""}
@ -311,203 +311,7 @@ func (player *mediaPlayerData) setSource(value interface{}) bool {
return true return true
} }
func valueToPlayerListeners(value interface{}) ([]func(MediaPlayer), bool) { func valueToPlayerErrorListeners(value any) ([]func(MediaPlayer, int, string), 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) {
if value == nil { if value == nil {
return nil, true return nil, true
} }
@ -593,7 +397,7 @@ func valueToPlayerErrorListeners(value interface{}) ([]func(MediaPlayer, int, st
} }
return listeners, true return listeners, true
case []interface{}: case []any:
count := len(value) count := len(value)
if count == 0 { if count == 0 {
return nil, true return nil, true

View File

@ -144,131 +144,6 @@ type MouseEvent struct {
MetaKey bool 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 }{ var mouseEvents = map[string]struct{ jsEvent, jsFunc string }{
ClickEvent: {jsEvent: "onclick", jsFunc: "clickEvent"}, ClickEvent: {jsEvent: "onclick", jsFunc: "clickEvent"},
DoubleClickEvent: {jsEvent: "ondblclick", jsFunc: "doubleClickEvent"}, DoubleClickEvent: {jsEvent: "ondblclick", jsFunc: "doubleClickEvent"},
@ -280,8 +155,8 @@ var mouseEvents = map[string]struct{ jsEvent, jsFunc string }{
ContextMenuEvent: {jsEvent: "oncontextmenu", jsFunc: "contextMenuEvent"}, ContextMenuEvent: {jsEvent: "oncontextmenu", jsFunc: "contextMenuEvent"},
} }
func (view *viewData) setMouseListener(tag string, value interface{}) bool { func (view *viewData) setMouseListener(tag string, value any) bool {
listeners, ok := valueToMouseListeners(value) listeners, ok := valueToEventListeners[View, MouseEvent](value)
if !ok { if !ok {
notCompatibleType(tag, value) notCompatibleType(tag, value)
return false 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) { func mouseEventsHtml(view View, buffer *strings.Builder) {
for tag, js := range mouseEvents { for tag, js := range mouseEvents {
if value := view.getRaw(tag); value != nil { 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) { func handleMouseEvents(view View, tag string, data DataObject) {
listeners := getMouseListeners(view, "", tag) listeners := getEventListeners[View, MouseEvent](view, nil, tag)
if len(listeners) == 0 { if len(listeners) > 0 {
return var event MouseEvent
} event.init(data)
var event MouseEvent for _, listener := range listeners {
event.init(data) 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. // 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. // 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) { func GetClickListeners(view View, subviewID ...string) []func(View, MouseEvent) {
return getMouseListeners(view, subviewID, ClickEvent) 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. // 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. // 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) { func GetDoubleClickListeners(view View, subviewID ...string) []func(View, MouseEvent) {
return getMouseListeners(view, subviewID, DoubleClickEvent) return getEventListeners[View, MouseEvent](view, subviewID, DoubleClickEvent)
} }
// GetContextMenuListeners returns the "context-menu" listener list. // GetContextMenuListeners returns the "context-menu" listener list.
// If there are no listeners then the empty list is returned. // 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. // 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) { func GetContextMenuListeners(view View, subviewID ...string) []func(View, MouseEvent) {
return getMouseListeners(view, subviewID, ContextMenuEvent) 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. // 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. // 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) { func GetMouseDownListeners(view View, subviewID ...string) []func(View, MouseEvent) {
return getMouseListeners(view, subviewID, MouseDown) 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. // 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. // 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) { func GetMouseUpListeners(view View, subviewID ...string) []func(View, MouseEvent) {
return getMouseListeners(view, subviewID, MouseUp) 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. // 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. // 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) { func GetMouseMoveListeners(view View, subviewID ...string) []func(View, MouseEvent) {
return getMouseListeners(view, subviewID, MouseMove) 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. // 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. // 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) { func GetMouseOverListeners(view View, subviewID ...string) []func(View, MouseEvent) {
return getMouseListeners(view, subviewID, MouseOver) 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. // 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. // 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) { func GetMouseOutListeners(view View, subviewID ...string) []func(View, MouseEvent) {
return getMouseListeners(view, subviewID, MouseOut) return getEventListeners[View, MouseEvent](view, subviewID, MouseOut)
} }

View File

@ -36,7 +36,7 @@ type numberPickerData struct {
// NewNumberPicker create new NumberPicker object and return it // NewNumberPicker create new NumberPicker object and return it
func NewNumberPicker(session Session, params Params) NumberPicker { func NewNumberPicker(session Session, params Params) NumberPicker {
view := new(numberPickerData) view := new(numberPickerData)
view.Init(session) view.init(session)
setInitParams(view, params) setInitParams(view, params)
return view return view
} }
@ -45,8 +45,8 @@ func newNumberPicker(session Session) View {
return NewNumberPicker(session, nil) return NewNumberPicker(session, nil)
} }
func (picker *numberPickerData) Init(session Session) { func (picker *numberPickerData) init(session Session) {
picker.viewData.Init(session) picker.viewData.init(session)
picker.tag = "NumberPicker" picker.tag = "NumberPicker"
picker.numberChangedListeners = []func(NumberPicker, float64){} 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) 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 { if value == nil {
picker.remove(tag) picker.remove(tag)
return true return true
@ -99,65 +99,28 @@ func (picker *numberPickerData) set(tag string, value interface{}) bool {
switch tag { switch tag {
case NumberChangedEvent: case NumberChangedEvent:
switch value := value.(type) { listeners, ok := valueToEventListeners[NumberPicker, float64](value)
case func(NumberPicker, float64): if !ok {
picker.numberChangedListeners = []func(NumberPicker, float64){value} notCompatibleType(tag, value)
return false
case func(float64): } else if listeners == nil {
fn := func(_ NumberPicker, newValue float64) { listeners = []func(NumberPicker, 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
} }
picker.numberChangedListeners = listeners
picker.propertyChangedEvent(tag) picker.propertyChangedEvent(tag)
return true return true
case NumberPickerValue: case NumberPickerValue:
oldValue := GetNumberPickerValue(picker, "") oldValue := GetNumberPickerValue(picker)
min, max := GetNumberPickerMinMax(picker, "") min, max := GetNumberPickerMinMax(picker)
if picker.setFloatProperty(NumberPickerValue, value, min, max) { 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 { 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 { for _, listener := range picker.numberChangedListeners {
listener(picker, newValue) listener(picker, f)
} }
picker.propertyChangedEvent(tag) picker.propertyChangedEvent(tag)
} }
@ -177,29 +140,29 @@ func (picker *numberPickerData) propertyChanged(tag string) {
if picker.created { if picker.created {
switch tag { switch tag {
case NumberPickerType: case NumberPickerType:
if GetNumberPickerType(picker, "") == NumberSlider { if GetNumberPickerType(picker) == NumberSlider {
updateProperty(picker.htmlID(), "type", "range", picker.session) updateProperty(picker.htmlID(), "type", "range", picker.session)
} else { } else {
updateProperty(picker.htmlID(), "type", "number", picker.session) updateProperty(picker.htmlID(), "type", "number", picker.session)
} }
case NumberPickerMin: case NumberPickerMin:
min, _ := GetNumberPickerMinMax(picker, "") min, _ := GetNumberPickerMinMax(picker)
updateProperty(picker.htmlID(), Min, strconv.FormatFloat(min, 'f', -1, 32), picker.session) updateProperty(picker.htmlID(), Min, strconv.FormatFloat(min, 'f', -1, 32), picker.session)
case NumberPickerMax: case NumberPickerMax:
_, max := GetNumberPickerMinMax(picker, "") _, max := GetNumberPickerMinMax(picker)
updateProperty(picker.htmlID(), Max, strconv.FormatFloat(max, 'f', -1, 32), picker.session) updateProperty(picker.htmlID(), Max, strconv.FormatFloat(max, 'f', -1, 32), picker.session)
case NumberPickerStep: 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) updateProperty(picker.htmlID(), Step, strconv.FormatFloat(step, 'f', -1, 32), picker.session)
} else { } else {
updateProperty(picker.htmlID(), Step, "any", picker.session) updateProperty(picker.htmlID(), Step, "any", picker.session)
} }
case NumberPickerValue: case NumberPickerValue:
value := GetNumberPickerValue(picker, "") value := GetNumberPickerValue(picker)
picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%f')`, picker.htmlID(), value)) picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%f')`, picker.htmlID(), value))
for _, listener := range picker.numberChangedListeners { for _, listener := range picker.numberChangedListeners {
listener(picker, value) 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)) return picker.get(picker.normalizeTag(tag))
} }
func (picker *numberPickerData) get(tag string) interface{} { func (picker *numberPickerData) get(tag string) any {
switch tag { switch tag {
case NumberChangedEvent: case NumberChangedEvent:
return picker.numberChangedListeners return picker.numberChangedListeners
@ -229,13 +192,13 @@ func (picker *numberPickerData) htmlTag() string {
func (picker *numberPickerData) htmlProperties(self View, buffer *strings.Builder) { func (picker *numberPickerData) htmlProperties(self View, buffer *strings.Builder) {
picker.viewData.htmlProperties(self, buffer) picker.viewData.htmlProperties(self, buffer)
if GetNumberPickerType(picker, "") == NumberSlider { if GetNumberPickerType(picker) == NumberSlider {
buffer.WriteString(` type="range"`) buffer.WriteString(` type="range"`)
} else { } else {
buffer.WriteString(` type="number"`) buffer.WriteString(` type="number"`)
} }
min, max := GetNumberPickerMinMax(picker, "") min, max := GetNumberPickerMinMax(picker)
if min != math.Inf(-1) { if min != math.Inf(-1) {
buffer.WriteString(` min="`) buffer.WriteString(` min="`)
buffer.WriteString(strconv.FormatFloat(min, 'f', -1, 64)) buffer.WriteString(strconv.FormatFloat(min, 'f', -1, 64))
@ -248,7 +211,7 @@ func (picker *numberPickerData) htmlProperties(self View, buffer *strings.Builde
buffer.WriteByte('"') buffer.WriteByte('"')
} }
step := GetNumberPickerStep(picker, "") step := GetNumberPickerStep(picker)
if step != 0 { if step != 0 {
buffer.WriteString(` step="`) buffer.WriteString(` step="`)
buffer.WriteString(strconv.FormatFloat(step, 'f', -1, 64)) 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(` value="`)
buffer.WriteString(strconv.FormatFloat(GetNumberPickerValue(picker, ""), 'f', -1, 64)) buffer.WriteString(strconv.FormatFloat(GetNumberPickerValue(picker), 'f', -1, 64))
buffer.WriteByte('"') buffer.WriteByte('"')
buffer.WriteString(` oninput="editViewInputEvent(this)"`) buffer.WriteString(` oninput="editViewInputEvent(this)"`)
} }
func (picker *numberPickerData) htmlDisabledProperties(self View, buffer *strings.Builder) { func (picker *numberPickerData) htmlDisabledProperties(self View, buffer *strings.Builder) {
if IsDisabled(self, "") { if IsDisabled(self) {
buffer.WriteString(` disabled`) buffer.WriteString(` disabled`)
} }
picker.viewData.htmlDisabledProperties(self, buffer) picker.viewData.htmlDisabledProperties(self, buffer)
@ -276,8 +239,8 @@ func (picker *numberPickerData) handleCommand(self View, command string, data Da
case "textChanged": case "textChanged":
if text, ok := data.PropertyValue("text"); ok { if text, ok := data.PropertyValue("text"); ok {
if value, err := strconv.ParseFloat(text, 32); err == nil { if value, err := strconv.ParseFloat(text, 32); err == nil {
oldValue := GetNumberPickerValue(picker, "") oldValue := GetNumberPickerValue(picker)
picker.properties[NumberPickerValue] = value picker.properties[NumberPickerValue] = text
if value != oldValue { if value != oldValue {
for _, listener := range picker.numberChangedListeners { for _, listener := range picker.numberChangedListeners {
listener(picker, value) 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: // GetNumberPickerType returns the type of NumberPicker subview. Valid values:
// NumberEditor (0) - NumberPicker is presented by editor (default type); // NumberEditor (0) - NumberPicker is presented by editor (default type);
// NumberSlider (1) - NumberPicker is presented by slider. // NumberSlider (1) - NumberPicker is presented by slider.
// 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 GetNumberPickerType(view View, subviewID string) int { func GetNumberPickerType(view View, subviewID ...string) int {
if subviewID != "" { return enumStyledProperty(view, subviewID, NumberPickerType, NumberEditor, false)
view = ViewByID(view, subviewID)
}
if view == nil {
return 0
}
t, _ := enumStyledProperty(view, NumberPickerType, NumberEditor)
return t
} }
// GetNumberPickerMinMax returns the min and max value of NumberPicker subview. // 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. // 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) { func GetNumberPickerMinMax(view View, subviewID ...string) (float64, float64) {
if subviewID != "" { var pickerType int
view = ViewByID(view, subviewID) 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 { var defMin, defMax float64
defMin = 0 if pickerType == NumberSlider {
defMax = 1 defMin = 0
} else { defMax = 1
defMin = math.Inf(-1) } else {
defMax = math.Inf(1) 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
} }
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. // 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. // 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 { func GetNumberPickerStep(view View, subviewID ...string) float64 {
if subviewID != "" { var max float64
view = ViewByID(view, subviewID) if len(subviewID) > 0 && subviewID[0] != "" {
} _, max = GetNumberPickerMinMax(view, subviewID[0])
if view == nil { } else {
return 0 _, max = GetNumberPickerMinMax(view)
} }
result, ok := floatStyledProperty(view, NumberPickerStep, 0) result := floatStyledProperty(view, subviewID, NumberPickerStep, 0)
if !ok {
result, _ = floatStyledProperty(view, Step, 0)
}
_, max := GetNumberPickerMinMax(view, "")
if result > max { if result > max {
return max return max
} }
@ -365,36 +308,22 @@ func GetNumberPickerStep(view View, subviewID string) float64 {
} }
// GetNumberPickerValue returns the value of NumberPicker subview. // GetNumberPickerValue returns the value of NumberPicker subview.
// 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 GetNumberPickerValue(view View, subviewID string) float64 { func GetNumberPickerValue(view View, subviewID ...string) float64 {
if subviewID != "" { var min float64
view = ViewByID(view, subviewID) if len(subviewID) > 0 && subviewID[0] != "" {
} min, _ = GetNumberPickerMinMax(view, subviewID[0])
if view == nil { } else {
return 0 min, _ = GetNumberPickerMinMax(view)
} }
min, _ := GetNumberPickerMinMax(view, "") result := floatStyledProperty(view, subviewID, NumberPickerValue, min)
result, ok := floatStyledProperty(view, NumberPickerValue, min)
if !ok {
result, _ = floatStyledProperty(view, Value, min)
}
return result return result
} }
// GetNumberChangedListeners returns the NumberChangedListener list of an NumberPicker subview. // GetNumberChangedListeners returns the NumberChangedListener list of an NumberPicker subview.
// If there are no listeners then the empty list is returned // 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. // 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) { func GetNumberChangedListeners(view View, subviewID ...string) []func(NumberPicker, float64) {
if subviewID != "" { return getEventListeners[NumberPicker, float64](view, subviewID, NumberChangedEvent)
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){}
} }

View File

@ -18,7 +18,7 @@ type outlinePropertyData struct {
func NewOutlineProperty(params Params) OutlineProperty { func NewOutlineProperty(params Params) OutlineProperty {
outline := new(outlinePropertyData) outline := new(outlinePropertyData)
outline.properties = map[string]interface{}{} outline.properties = map[string]any{}
for tag, value := range params { for tag, value := range params {
outline.Set(tag, value) outline.Set(tag, value)
} }
@ -55,7 +55,7 @@ func (outline *outlinePropertyData) Remove(tag string) {
delete(outline.properties, outline.normalizeTag(tag)) 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 { if value == nil {
outline.Remove(tag) outline.Remove(tag)
return true return true
@ -85,7 +85,7 @@ func (outline *outlinePropertyData) Set(tag string, value interface{}) bool {
return false return false
} }
func (outline *outlinePropertyData) Get(tag string) interface{} { func (outline *outlinePropertyData) Get(tag string) any {
return outline.propertyList.Get(outline.normalizeTag(tag)) return outline.propertyList.Get(outline.normalizeTag(tag))
} }
@ -103,18 +103,18 @@ type ViewOutline struct {
Width SizeUnit Width SizeUnit
} }
func (outline ViewOutline) cssValue(builder cssBuilder) { func (outline ViewOutline) cssValue(builder cssBuilder, session Session) {
values := enumProperties[BorderStyle].cssValues values := enumProperties[BorderStyle].cssValues
if outline.Style > 0 && outline.Style < len(values) && outline.Color.Alpha() > 0 && if outline.Style > 0 && outline.Style < len(values) && outline.Color.Alpha() > 0 &&
outline.Width.Type != Auto && outline.Width.Type != SizeInFraction && outline.Width.Type != Auto && outline.Width.Type != SizeInFraction &&
outline.Width.Type != SizeInPercent && outline.Width.Value > 0 { 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 var builder cssValueBuilder
outline.cssValue(&builder) outline.cssValue(&builder, session)
return builder.finish() return builder.finish()
} }
@ -128,7 +128,7 @@ func getOutline(properties Properties) OutlineProperty {
return nil return nil
} }
func (style *viewStyle) setOutline(value interface{}) bool { func (style *viewStyle) setOutline(value any) bool {
switch value := value.(type) { switch value := value.(type) {
case OutlineProperty: case OutlineProperty:
style.properties[Outline] = value style.properties[Outline] = value

View File

@ -3,25 +3,25 @@ package rui
import "sort" import "sort"
// Params defines a type of a parameters list // 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) return params.getRaw(tag)
} }
func (params Params) getRaw(tag string) interface{} { func (params Params) getRaw(tag string) any {
if value, ok := params[tag]; ok { if value, ok := params[tag]; ok {
return value return value
} }
return nil return nil
} }
func (params Params) Set(tag string, value interface{}) bool { func (params Params) Set(tag string, value any) bool {
params.setRaw(tag, value) params.setRaw(tag, value)
return true return true
} }
func (params Params) setRaw(tag string, value interface{}) { func (params Params) setRaw(tag string, value any) {
if value != nil { if value != nil {
params[tag] = value params[tag] = value
} else { } else {

View File

@ -87,131 +87,6 @@ type PointerEvent struct {
IsPrimary bool 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 }{ var pointerEvents = map[string]struct{ jsEvent, jsFunc string }{
PointerDown: {jsEvent: "onpointerdown", jsFunc: "pointerDownEvent"}, PointerDown: {jsEvent: "onpointerdown", jsFunc: "pointerDownEvent"},
PointerUp: {jsEvent: "onpointerup", jsFunc: "pointerUpEvent"}, PointerUp: {jsEvent: "onpointerup", jsFunc: "pointerUpEvent"},
@ -221,8 +96,8 @@ var pointerEvents = map[string]struct{ jsEvent, jsFunc string }{
PointerOver: {jsEvent: "onpointerover", jsFunc: "pointerOverEvent"}, PointerOver: {jsEvent: "onpointerover", jsFunc: "pointerOverEvent"},
} }
func (view *viewData) setPointerListener(tag string, value interface{}) bool { func (view *viewData) setPointerListener(tag string, value any) bool {
listeners, ok := valueToPointerListeners(value) listeners, ok := valueToEventListeners[View, PointerEvent](value)
if !ok { if !ok {
notCompatibleType(tag, value) notCompatibleType(tag, value)
return false 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) { func pointerEventsHtml(view View, buffer *strings.Builder) {
for tag, js := range pointerEvents { for tag, js := range pointerEvents {
if value := view.getRaw(tag); value != nil { 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) { func handlePointerEvents(view View, tag string, data DataObject) {
listeners := getPointerListeners(view, "", tag) listeners := getEventListeners[View, PointerEvent](view, nil, tag)
if len(listeners) == 0 { if len(listeners) == 0 {
return 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. // 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. // 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) { func GetPointerDownListeners(view View, subviewID ...string) []func(View, PointerEvent) {
return getPointerListeners(view, subviewID, PointerDown) 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. // 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. // 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) { func GetPointerUpListeners(view View, subviewID ...string) []func(View, PointerEvent) {
return getPointerListeners(view, subviewID, PointerUp) 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. // 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. // 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) { func GetPointerMoveListeners(view View, subviewID ...string) []func(View, PointerEvent) {
return getPointerListeners(view, subviewID, PointerMove) 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. // 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. // 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) { func GetPointerCancelListeners(view View, subviewID ...string) []func(View, PointerEvent) {
return getPointerListeners(view, subviewID, PointerCancel) 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. // 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. // 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) { func GetPointerOverListeners(view View, subviewID ...string) []func(View, PointerEvent) {
return getPointerListeners(view, subviewID, PointerOver) 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. // 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. // 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) { func GetPointerOutListeners(view View, subviewID ...string) []func(View, PointerEvent) {
return getPointerListeners(view, subviewID, PointerOut) return getEventListeners[View, PointerEvent](view, subviewID, PointerOut)
} }

520
popup.go
View File

@ -1,6 +1,8 @@
package rui package rui
import "strings" import (
"strings"
)
const ( const (
// Title is the constant for the "title" property tag. // Title is the constant for the "title" property tag.
@ -36,6 +38,46 @@ const (
// It occurs after the Popup disappears from the screen. // It occurs after the Popup disappears from the screen.
// The main listener for this event has the following format: func(Popup) // The main listener for this event has the following format: func(Popup)
DismissEvent = "dismiss-event" 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. // PopupButton describes a button that will be placed at the bottom of the window.
@ -65,152 +107,388 @@ type popupManager struct {
popups []Popup 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 popup.view = view
session := view.Session() session := view.Session()
if params == nil { columnCount := 3
params = Params{} rowCount := 3
popupRow := 1
popupColumn := 1
arrow := popupArrow{
row: 1,
column: 1,
align: CenterAlign,
} }
popup.dismissListener = []func(Popup){} switch arrow.location, _ = enumProperty(popupParams, Arrow, session, NoneArrow); arrow.location {
if value, ok := params[DismissEvent]; ok && value != nil { case TopArrow:
switch value := value.(type) { rowCount = 4
case func(Popup): popupRow = 2
popup.dismissListener = []func(Popup){value}
case func(): case BottomArrow:
popup.dismissListener = []func(Popup){ rowCount = 4
func(_ Popup) { arrow.row = 2
value()
},
}
case []func(Popup): case LeftArrow:
for _, fn := range value { columnCount = 4
if fn != nil { popupColumn = 2
popup.dismissListener = append(popup.dismissListener, fn)
}
}
case []func(): case RightArrow:
for _, fn := range value { columnCount = 4
if fn != nil { arrow.column = 2
popup.dismissListener = append(popup.dismissListener, func(_ Popup) {
fn()
})
}
}
case []interface{}: default:
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()
})
}
}
}
}
} }
var title View = nil cellWidth := make([]SizeUnit, columnCount)
titleStyle := "ruiPopupTitle" switch hAlign, _ := enumProperty(popupParams, HorizontalAlign, session, CenterAlign); hAlign {
closeButton, _ := boolProperty(params, CloseButton, session) case LeftAlign:
outsideClose, _ := boolProperty(params, OutsideClose, session) cellWidth[columnCount-1] = Fr(1)
vAlign, _ := enumProperty(params, VerticalAlign, session, CenterAlign)
hAlign, _ := enumProperty(params, HorizontalAlign, session, CenterAlign)
buttons := []PopupButton{} case RightAlign:
if value, ok := params[Buttons]; ok && value != nil { cellWidth[0] = Fr(1)
switch value := value.(type) {
case PopupButton:
buttons = []PopupButton{value}
case []PopupButton: default:
buttons = value 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", Style: "ruiPopup",
Row: popupRow,
Column: popupColumn,
MaxWidth: Percent(100), MaxWidth: Percent(100),
MaxHeight: Percent(100), MaxHeight: Percent(100),
CellVerticalAlign: StretchAlign, CellVerticalAlign: StretchAlign,
CellHorizontalAlign: StretchAlign, CellHorizontalAlign: StretchAlign,
ClickEvent: func(View) {}, ClickEvent: func(View) {},
}) Shadow: NewShadowWithParams(Params{
SpreadRadius: Px(4),
Blur: Px(16),
ColorTag: "@ruiPopupShadow",
}),
}
for tag, value := range params { var closeButton View = nil
switch tag { outsideClose := false
case Title: buttons := []PopupButton{}
switch value := value.(type) { titleStyle := "ruiPopupTitle"
case string: var title View = nil
title = NewTextView(view.Session(), Params{Text: value})
case View: for tag, value := range popupParams {
title = value 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: 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 viewRow := 0
if title != nil || closeButton { if title != nil || closeButton != nil {
viewRow = 1 titleContent := []View{}
titleHeight, _ := sizeConstant(popup.Session(), "ruiPopupTitleHeight") if title != nil {
titleView := NewGridLayout(session, Params{ titleContent = append(titleContent, title)
}
if closeButton != nil {
titleContent = append(titleContent, closeButton)
}
popupView.Append(NewGridLayout(session, Params{
Row: 0, Row: 0,
Style: titleStyle, Style: titleStyle,
CellWidth: []SizeUnit{Fr(1), titleHeight}, CellWidth: []any{Fr(1), AutoSize()},
CellVerticalAlign: CenterAlign, CellVerticalAlign: CenterAlign,
PaddingLeft: Px(12), PaddingLeft: Px(12),
}) Content: titleContent,
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()
},
}))
}
popupView.Append(titleView) viewRow = 1
cellHeight = []SizeUnit{AutoSize(), Fr(1)} popupCellHeight = []SizeUnit{AutoSize(), Fr(1)}
} else { } else {
cellHeight = []SizeUnit{Fr(1)} popupCellHeight = []SizeUnit{Fr(1)}
} }
view.Set(Row, viewRow) view.Set(Row, viewRow)
@ -218,7 +496,7 @@ func (popup *popupData) init(view View, params Params) {
if buttonCount := len(buttons); buttonCount > 0 { if buttonCount := len(buttons); buttonCount > 0 {
buttonsAlign, _ := enumProperty(params, ButtonsAlign, session, RightAlign) buttonsAlign, _ := enumProperty(params, ButtonsAlign, session, RightAlign)
cellHeight = append(cellHeight, AutoSize()) popupCellHeight = append(popupCellHeight, AutoSize())
gap, _ := sizeConstant(session, "ruiPopupButtonGap") gap, _ := sizeConstant(session, "ruiPopupButtonGap")
cellWidth := []SizeUnit{} cellWidth := []SizeUnit{}
for i := 0; i < buttonCount; i++ { for i := 0; i < buttonCount; i++ {
@ -256,16 +534,16 @@ func (popup *popupData) init(view View, params Params) {
Content: buttonsPanel, Content: buttonsPanel,
})) }))
} }
popupView.Set(CellHeight, cellHeight)
popup.layerView = NewGridLayout(session, Params{ popupView.Set(CellHeight, popupCellHeight)
Style: "ruiPopupLayer",
CellVerticalAlign: vAlign, if arrow.location != NoneArrow {
CellHorizontalAlign: hAlign, layerParams[Content] = []View{popupView, arrow.createView(popupView)}
Content: NewColumnLayout(session, Params{Content: popupView}), } else {
MaxWidth: Percent(100), layerParams[Content] = []View{popupView}
MaxHeight: Percent(100), }
})
popup.layerView = NewGridLayout(session, layerParams)
if outsideClose { if outsideClose {
popup.layerView.Set(ClickEvent, func(View) { popup.layerView.Set(ClickEvent, func(View) {

View File

@ -103,19 +103,22 @@ func ShowCancellableQuestion(title, text string, session Session, onYes func(),
} }
type popupMenuData struct { type popupMenuData struct {
items []string items []string
session Session disabled []int
popup Popup session Session
result func(int) popup Popup
result func(int)
} }
func (popup *popupMenuData) itemClick(list ListView, n int) { func (popup *popupMenuData) itemClick(list ListView, n int) {
if popup.popup != nil { if popup.IsListItemEnabled(n) {
popup.popup.Dismiss() if popup.popup != nil {
popup.popup = nil popup.popup.Dismiss()
} popup.popup = nil
if popup.result != nil { }
popup.result(n) 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 { func (popup *popupMenuData) IsListItemEnabled(index int) bool {
if popup.disabled != nil {
for _, n := range popup.disabled {
if index == n {
return false
}
}
}
return true return true
} }
@ -165,13 +175,18 @@ func ShowMenu(session Session, params Params) Popup {
return nil return nil
} }
value, ok = params[PopupMenuResult] if value, ok := params[PopupMenuResult]; ok && value != nil {
if ok && value != nil {
if result, ok := value.(func(int)); ok { if result, ok := value.(func(int)); ok {
data.result = result data.result = result
} }
} }
if value, ok := params[DisabledItems]; ok && value != nil {
if value, ok := value.([]int); ok {
data.disabled = value
}
}
listView := NewListView(session, Params{ listView := NewListView(session, Params{
Items: adapter, Items: adapter,
Orientation: TopDownOrientation, Orientation: TopDownOrientation,
@ -181,7 +196,7 @@ func ShowMenu(session Session, params Params) Popup {
popupParams := Params{} popupParams := Params{}
for tag, value := range params { for tag, value := range params {
switch tag { switch tag {
case Items, PopupMenuResult: case Items, PopupMenuResult, DisabledItems:
// do nothing // do nothing
default: default:

View File

@ -22,7 +22,7 @@ type progressBarData struct {
// NewProgressBar create new ProgressBar object and return it // NewProgressBar create new ProgressBar object and return it
func NewProgressBar(session Session, params Params) ProgressBar { func NewProgressBar(session Session, params Params) ProgressBar {
view := new(progressBarData) view := new(progressBarData)
view.Init(session) view.init(session)
setInitParams(view, params) setInitParams(view, params)
return view return view
} }
@ -31,8 +31,8 @@ func newProgressBar(session Session) View {
return NewProgressBar(session, nil) return NewProgressBar(session, nil)
} }
func (progress *progressBarData) Init(session Session) { func (progress *progressBarData) init(session Session) {
progress.viewData.Init(session) progress.viewData.init(session)
progress.tag = "ProgressBar" progress.tag = "ProgressBar"
} }
@ -65,19 +65,19 @@ func (progress *progressBarData) propertyChanged(tag string) {
if progress.created { if progress.created {
switch tag { switch tag {
case ProgressBarMax: 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: 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) 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) { if progress.viewData.set(tag, value) {
progress.propertyChanged(tag) progress.propertyChanged(tag)
return true return true
@ -85,7 +85,7 @@ func (progress *progressBarData) set(tag string, value interface{}) bool {
return false return false
} }
func (progress *progressBarData) Get(tag string) interface{} { func (progress *progressBarData) Get(tag string) any {
return progress.get(progress.normalizeTag(tag)) return progress.get(progress.normalizeTag(tag))
} }
@ -97,44 +97,22 @@ func (progress *progressBarData) htmlProperties(self View, buffer *strings.Build
progress.viewData.htmlProperties(self, buffer) progress.viewData.htmlProperties(self, buffer)
buffer.WriteString(` max="`) buffer.WriteString(` max="`)
buffer.WriteString(strconv.FormatFloat(GetProgressBarMax(progress, ""), 'f', -1, 64)) buffer.WriteString(strconv.FormatFloat(GetProgressBarMax(progress), 'f', -1, 64))
buffer.WriteByte('"') buffer.WriteByte('"')
buffer.WriteString(` value="`) buffer.WriteString(` value="`)
buffer.WriteString(strconv.FormatFloat(GetProgressBarValue(progress, ""), 'f', -1, 64)) buffer.WriteString(strconv.FormatFloat(GetProgressBarValue(progress), 'f', -1, 64))
buffer.WriteByte('"') buffer.WriteByte('"')
} }
// GetProgressBarMax returns the max value of ProgressBar subview. // GetProgressBarMax returns the max value of ProgressBar subview.
// 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 GetProgressBarMax(view View, subviewID string) float64 { func GetProgressBarMax(view View, subviewID ...string) float64 {
if subviewID != "" { return floatStyledProperty(view, subviewID, ProgressBarMax, 1)
view = ViewByID(view, subviewID)
}
if view == nil {
return 0
}
result, ok := floatStyledProperty(view, ProgressBarMax, 1)
if !ok {
result, _ = floatStyledProperty(view, Max, 1)
}
return result
} }
// GetProgressBarValue returns the value of ProgressBar subview. // GetProgressBarValue returns the value of ProgressBar subview.
// 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 GetProgressBarValue(view View, subviewID string) float64 { func GetProgressBarValue(view View, subviewID ...string) float64 {
if subviewID != "" { return floatStyledProperty(view, subviewID, ProgressBarValue, 0)
view = ViewByID(view, subviewID)
}
if view == nil {
return 0
}
result, ok := floatStyledProperty(view, ProgressBarValue, 0)
if !ok {
result, _ = floatStyledProperty(view, Value, 0)
}
return result
} }

View File

@ -9,13 +9,13 @@ import (
type Properties interface { type Properties interface {
// Get returns a value of the property with name defined by the argument. // 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. // The type of return value depends on the property. If the property is not set then nil is returned.
Get(tag string) interface{} Get(tag string) any
getRaw(tag string) interface{} getRaw(tag string) any
// Set sets the value (second argument) of the property with name defined by the first argument. // 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 // 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 // a description of the error is written to the log
Set(tag string, value interface{}) bool Set(tag string, value any) bool
setRaw(tag string, value interface{}) setRaw(tag string, value any)
// Remove removes the property with name defined by the argument // Remove removes the property with name defined by the argument
Remove(tag string) Remove(tag string)
// Clear removes all properties // Clear removes all properties
@ -25,25 +25,25 @@ type Properties interface {
} }
type propertyList struct { type propertyList struct {
properties map[string]interface{} properties map[string]any
} }
func (properties *propertyList) init() { 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)) 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 { if value, ok := properties.properties[tag]; ok {
return value return value
} }
return nil return nil
} }
func (properties *propertyList) setRaw(tag string, value interface{}) { func (properties *propertyList) setRaw(tag string, value any) {
properties.properties[tag] = value properties.properties[tag] = value
} }
@ -56,7 +56,7 @@ func (properties *propertyList) remove(tag string) {
} }
func (properties *propertyList) Clear() { func (properties *propertyList) Clear() {
properties.properties = map[string]interface{}{} properties.properties = map[string]any{}
} }
func (properties *propertyList) AllTags() []string { func (properties *propertyList) AllTags() []string {

View File

@ -32,49 +32,49 @@ func TestProperties(t *testing.T) {
t.Error(`list.Get("name") is not string`) 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 { for _, value := range sizeValues {
if !list.setSizeProperty("size", value) { if !list.setSizeProperty("size", value) {
t.Errorf(`setSizeProperty("size", %v) fail`, 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 { for _, value := range failSizeValues {
if list.setSizeProperty("size", value) { if list.setSizeProperty("size", value) {
t.Errorf(`setSizeProperty("size", %v) success`, 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 { for _, value := range angleValues {
if !list.setAngleProperty("angle", value) { if !list.setAngleProperty("angle", value) {
t.Errorf(`setAngleProperty("angle", %v) fail`, 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 { for _, value := range failAngleValues {
if list.setAngleProperty("angle", value) { if list.setAngleProperty("angle", value) {
t.Errorf(`setAngleProperty("angle", %v) success`, 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 { for _, color := range colorValues {
if !list.setColorProperty("color", color) { if !list.setColorProperty("color", color) {
t.Errorf(`list.setColorProperty("color", %v) fail`, 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 { for _, color := range failColorValues {
if list.setColorProperty("color", color) { if list.setColorProperty("color", color) {
t.Errorf(`list.setColorProperty("color", %v) success`, 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() inheritOffOn := inheritOffOnValues()
for _, value := range enumValues { for _, value := range enumValues {
if !list.setEnumProperty("enum", value, inheritOffOn) { 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 { for _, value := range failEnumValues {
if list.setEnumProperty("enum", value, inheritOffOn) { if list.setEnumProperty("enum", value, inheritOffOn) {
t.Errorf(`list.setEnumProperty("enum", %v, %v) success`, 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 { for _, value := range boolValues {
if !list.setBoolProperty("bool", value) { if !list.setBoolProperty("bool", value) {
t.Errorf(`list.setBoolProperty("bool", %v) fail`, 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 { for _, value := range failBoolValues {
if list.setBoolProperty("bool", value) { if list.setBoolProperty("bool", value) {
t.Errorf(`list.setBoolProperty("bool", %v) success`, 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 { for _, value := range intValues {
if !list.setIntProperty("int", value) { if !list.setIntProperty("int", value) {
t.Errorf(`list.setIntProperty("int", %v) fail`, 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 { for _, value := range failIntValues {
if list.setIntProperty("int", value) { if list.setIntProperty("int", value) {
t.Errorf(`list.setIntProperty("int", %v) success`, 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 { for _, value := range floatValues {
if !list.setFloatProperty("float", value) { if !list.setFloatProperty("float", value) {
t.Errorf(`list.setFloatProperty("float", %v) fail`, 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 { for _, value := range failFloatValues {
if list.setFloatProperty("float", value) { if list.setFloatProperty("float", value) {
t.Errorf(`list.setFloatProperty("float", %v) success`, 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 { for _, value := range boundsValues {
if !list.setBoundsProperty("margin", value) { if !list.setBoundsProperty("margin", value) {
t.Errorf(`list.setBoundsProperty("margin", %v) fail`, value) t.Errorf(`list.setBoundsProperty("margin", %v) fail`, value)

View File

@ -1,6 +1,7 @@
package rui package rui
import ( import (
"fmt"
"strconv" "strconv"
"strings" "strings"
) )
@ -31,16 +32,29 @@ func imageProperty(properties Properties, tag string, session Session) (string,
return "", false return "", false
} }
func valueToSizeUnit(value interface{}, session Session) (SizeUnit, bool) { func valueToSizeUnit(value any, session Session) (SizeUnit, bool) {
if value != nil { if value != nil {
switch value := value.(type) { switch value := value.(type) {
case SizeUnit: case SizeUnit:
return value, true return value, true
case SizeFunc:
return SizeUnit{Type: SizeFunction, Function: value}, true
case string: case string:
if text, ok := session.resolveConstants(value); ok { if text, ok := session.resolveConstants(value); ok {
return StringToSizeUnit(text) 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 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 { if value != nil {
switch value := value.(type) { switch value := value.(type) {
case Color: case Color:
@ -88,7 +102,7 @@ func colorProperty(properties Properties, tag string, session Session) (Color, b
return valueToColor(properties.getRaw(tag), session) 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 { if value != nil {
values := enumProperties[tag].values values := enumProperties[tag].values
switch value := value.(type) { 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) 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 { if value != nil {
switch value := value.(type) { switch value := value.(type) {
case bool: case bool:
@ -184,7 +198,7 @@ func boolProperty(properties Properties, tag string, session Session) (bool, boo
return valueToBool(properties.getRaw(tag), session) 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 { if value != nil {
switch value := value.(type) { switch value := value.(type) {
case string: case string:
@ -214,7 +228,7 @@ func intProperty(properties Properties, tag string, session Session, defaultValu
return valueToInt(properties.getRaw(tag), session, defaultValue) 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 { if value != nil {
switch value := value.(type) { switch value := value.(type) {
case float64: case float64:
@ -238,7 +252,31 @@ func floatProperty(properties Properties, tag string, session Session, defaultVa
return valueToFloat(properties.getRaw(tag), session, defaultValue) 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 { if value != nil {
switch value := value.(type) { switch value := value.(type) {
case Range: case Range:

View File

@ -45,6 +45,12 @@ const (
// Opacity is the degree to which content behind an element is hidden, and is the opposite of transparency. // Opacity is the degree to which content behind an element is hidden, and is the opposite of transparency.
Opacity = "opacity" 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 is the constant for the "row" property tag.
Row = "row" 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. // The "padding-bottom" SizeUnit property sets the height of the padding area to the bottom of a view.
PaddingBottom = "padding-bottom" 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. // BackgroundColor is the constant for the "background-color" property tag.
// The "background-color" property sets the background color of a view. // The "background-color" property sets the background color of a view.
BackgroundColor = "background-color" 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. // This is an inherited property, i.e. if it is not defined, then the value of the parent view is used.
Underline = "underline" Underline = "underline"
// TextLineThickness is the constant for the "text-decoration-thickness" property tag. // TextLineThickness is the constant for the "text-line-thickness" property tag.
// The "text-decoration-thickness" SizeUnit property sets the stroke thickness of the decoration line that // 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. // 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. // 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" TextLineThickness = "text-line-thickness"
// TextLineStyle is the constant for the "text-decoration-style" property tag. // TextLineStyle is the constant for the "text-line-style" property tag.
// The "text-decoration-style" int property sets the style of the lines specified by "text-decoration" property. // 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. // 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. // 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" TextLineStyle = "text-line-style"
// TextLineColor is the constant for the "text-decoration-color" property tag. // TextLineColor is the constant for the "text-line-color" property tag.
// The "text-decoration-color" Color property sets the color of the lines specified by "text-decoration" property. // 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. // 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. // 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" 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. // This is an inherited property, i.e. if it is not defined, then the value of the parent view is used.
TextShadow = "text-shadow" 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. // LetterSpacing is the constant for the "letter-spacing" property tag.
// The "letter-spacing" SizeUnit property sets the horizontal spacing behavior between text characters. // 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. // 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 is the constant for the "orientation" property tag.
Orientation = "orientation" Orientation = "orientation"
// Gap is the constant for the "gap" property tag. // Gap is t he constant for the "gap" property tag.
Gap = "gap" 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 is the constant for the "text" property tag.
Text = "text" Text = "text"

View File

@ -67,6 +67,7 @@ var boolProperties = []string{
var intProperties = []string{ var intProperties = []string{
ZIndex, ZIndex,
TabSize,
HeadHeight, HeadHeight,
FootHeight, FootHeight,
RowSpan, RowSpan,
@ -109,6 +110,8 @@ var sizeProperties = map[string]string{
WordSpacing: WordSpacing, WordSpacing: WordSpacing,
LineHeight: LineHeight, LineHeight: LineHeight,
TextLineThickness: "text-decoration-thickness", TextLineThickness: "text-decoration-thickness",
ListRowGap: "row-gap",
ListColumnGap: "column-gap",
GridRowGap: GridRowGap, GridRowGap: GridRowGap,
GridColumnGap: GridColumnGap, GridColumnGap: GridColumnGap,
ColumnWidth: ColumnWidth, ColumnWidth: ColumnWidth,
@ -179,6 +182,11 @@ var enumProperties = map[string]struct {
"", "",
[]string{"visible", "invisible", "gone"}, []string{"visible", "invisible", "gone"},
}, },
Overflow: {
[]string{"hidden", "visible", "scroll", "auto"},
Overflow,
[]string{"hidden", "visible", "scroll", "auto"},
},
TextAlign: { TextAlign: {
[]string{"left", "right", "center", "justify"}, []string{"left", "right", "center", "justify"},
TextAlign, TextAlign,
@ -304,6 +312,11 @@ var enumProperties = map[string]struct {
"", "",
[]string{"left", "right", "center", "stretch"}, []string{"left", "right", "center", "stretch"},
}, },
ArrowAlign: {
[]string{"left", "right", "center"},
"",
[]string{"left", "right", "center"},
},
CellVerticalAlign: { CellVerticalAlign: {
[]string{"top", "bottom", "center", "stretch"}, []string{"top", "bottom", "center", "stretch"},
"align-items", "align-items",
@ -429,13 +442,18 @@ var enumProperties = map[string]struct {
"resize", "resize",
[]string{"none", "both", "horizontal", "vertical"}, []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) 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) 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") return !strings.ContainsAny(text, ",;|\"'`+(){}[]<>/\\*&%! \t\n\r")
} }
func isInt(value interface{}) (int, bool) { func isInt(value any) (int, bool) {
var n int var n int
switch value := value.(type) { switch value := value.(type) {
case int: case int:
@ -497,7 +515,7 @@ func isInt(value interface{}) (int, bool) {
return n, true return n, true
} }
func (properties *propertyList) setSimpleProperty(tag string, value interface{}) bool { func (properties *propertyList) setSimpleProperty(tag string, value any) bool {
if value == nil { if value == nil {
delete(properties.properties, tag) delete(properties.properties, tag)
return true return true
@ -515,19 +533,26 @@ func (properties *propertyList) setSimpleProperty(tag string, value interface{})
return false 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) { if !properties.setSimpleProperty(tag, value) {
var size SizeUnit var size SizeUnit
switch value := value.(type) { switch value := value.(type) {
case string: case string:
var ok bool 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) invalidPropertyValue(tag, value)
return false return false
} }
case SizeUnit: case SizeUnit:
size = value size = value
case SizeFunc:
size.Type = SizeFunction
size.Function = value
case float32: case float32:
size.Type = SizeInPixel size.Type = SizeInPixel
size.Value = float64(value) size.Value = float64(value)
@ -556,7 +581,7 @@ func (properties *propertyList) setSizeProperty(tag string, value interface{}) b
return true 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) { if !properties.setSimpleProperty(tag, value) {
var angle AngleUnit var angle AngleUnit
switch value := value.(type) { switch value := value.(type) {
@ -589,7 +614,7 @@ func (properties *propertyList) setAngleProperty(tag string, value interface{})
return true 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) { if !properties.setSimpleProperty(tag, value) {
var result Color var result Color
switch value := value.(type) { switch value := value.(type) {
@ -621,7 +646,7 @@ func (properties *propertyList) setColorProperty(tag string, value interface{})
return true 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) { if !properties.setSimpleProperty(tag, value) {
var n int var n int
if text, ok := value.(string); ok { if text, ok := value.(string); ok {
@ -646,7 +671,7 @@ func (properties *propertyList) setEnumProperty(tag string, value interface{}, v
return true 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 !properties.setSimpleProperty(tag, value) {
if text, ok := value.(string); ok { if text, ok := value.(string); ok {
switch strings.ToLower(strings.Trim(text, " \t")) { switch strings.ToLower(strings.Trim(text, " \t")) {
@ -683,7 +708,7 @@ func (properties *propertyList) setBoolProperty(tag string, value interface{}) b
return true 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 !properties.setSimpleProperty(tag, value) {
if text, ok := value.(string); ok { if text, ok := value.(string); ok {
n, err := strconv.Atoi(strings.Trim(text, " \t")) n, err := strconv.Atoi(strings.Trim(text, " \t"))
@ -704,7 +729,7 @@ func (properties *propertyList) setIntProperty(tag string, value interface{}) bo
return true 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) { if !properties.setSimpleProperty(tag, value) {
f := float64(0) f := float64(0)
switch value := value.(type) { switch value := value.(type) {
@ -715,6 +740,12 @@ func (properties *propertyList) setFloatProperty(tag string, value interface{},
ErrorLog(err.Error()) ErrorLog(err.Error())
return false 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: case float32:
f = float64(value) f = float64(value)
@ -742,11 +773,11 @@ func (properties *propertyList) setFloatProperty(tag string, value interface{},
return true 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) 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 { if value == nil {
delete(properties.properties, tag) delete(properties.properties, tag)
return true return true

View File

@ -3,29 +3,57 @@ package rui
const ( const (
// Visible - default value of the view Visibility property: View is visible // Visible - default value of the view Visibility property: View is visible
Visible = 0 Visible = 0
// Invisible - value of the view Visibility property: View is invisible but takes place // Invisible - value of the view Visibility property: View is invisible but takes place
Invisible = 1 Invisible = 1
// Gone - value of the view Visibility property: View is invisible and does not take place // Gone - value of the view Visibility property: View is invisible and does not take place
Gone = 2 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 - not transform text
NoneTextTransform = 0 NoneTextTransform = 0
// CapitalizeTextTransform - capitalize text // CapitalizeTextTransform - capitalize text
CapitalizeTextTransform = 1 CapitalizeTextTransform = 1
// LowerCaseTextTransform - transform text to lower case // LowerCaseTextTransform - transform text to lower case
LowerCaseTextTransform = 2 LowerCaseTextTransform = 2
// UpperCaseTextTransform - transform text to upper case // UpperCaseTextTransform - transform text to upper case
UpperCaseTextTransform = 3 UpperCaseTextTransform = 3
// HorizontalTopToBottom - content flows horizontally from left to right, vertically from top to bottom. // HorizontalTopToBottom - content flows horizontally from left to right, vertically from top to bottom.
// The next horizontal line is positioned below the previous line. // The next horizontal line is positioned below the previous line.
HorizontalTopToBottom = 0 HorizontalTopToBottom = 0
// HorizontalBottomToTop - content flows horizontally from left to right, vertically from bottom to top. // HorizontalBottomToTop - content flows horizontally from left to right, vertically from bottom to top.
// The next horizontal line is positioned above the previous line. // The next horizontal line is positioned above the previous line.
HorizontalBottomToTop = 1 HorizontalBottomToTop = 1
// VerticalRightToLeft - content flows vertically from top to bottom, horizontally from right to left. // 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. // The next vertical line is positioned to the left of the previous line.
VerticalRightToLeft = 2 VerticalRightToLeft = 2
// VerticalLeftToRight - content flows vertically from top to bottom, horizontally from left to right. // 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. // The next vertical line is positioned to the right of the previous line.
VerticalLeftToRight = 3 VerticalLeftToRight = 3
@ -33,6 +61,7 @@ const (
// MixedTextOrientation - rotates the characters of horizontal scripts 90° clockwise. // MixedTextOrientation - rotates the characters of horizontal scripts 90° clockwise.
// Lays out the characters of vertical scripts naturally. Default value. // Lays out the characters of vertical scripts naturally. Default value.
MixedTextOrientation = 0 MixedTextOrientation = 0
// UprightTextOrientation - lays out the characters of horizontal scripts naturally (upright), // 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 // 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". // 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 - direction of a text and other elements defined by system. This is the default value.
SystemTextDirection = 0 SystemTextDirection = 0
// LeftToRightDirection - text and other elements go from left to right. // LeftToRightDirection - text and other elements go from left to right.
LeftToRightDirection = 1 LeftToRightDirection = 1
//RightToLeftDirection - text and other elements go from right to left. //RightToLeftDirection - text and other elements go from right to left.
RightToLeftDirection = 2 RightToLeftDirection = 2
// ThinFont - the value of "text-weight" property: the thin (hairline) text weight // ThinFont - the value of "text-weight" property: the thin (hairline) text weight
ThinFont = 1 ThinFont = 1
// ExtraLightFont - the value of "text-weight" property: the extra light (ultra light) text weight // ExtraLightFont - the value of "text-weight" property: the extra light (ultra light) text weight
ExtraLightFont = 2 ExtraLightFont = 2
// LightFont - the value of "text-weight" property: the light text weight // LightFont - the value of "text-weight" property: the light text weight
LightFont = 3 LightFont = 3
// NormalFont - the value of "text-weight" property (default value): the normal text weight // NormalFont - the value of "text-weight" property (default value): the normal text weight
NormalFont = 4 NormalFont = 4
// MediumFont - the value of "text-weight" property: the medium text weight // MediumFont - the value of "text-weight" property: the medium text weight
MediumFont = 5 MediumFont = 5
// SemiBoldFont - the value of "text-weight" property: the semi bold (demi bold) text weight // SemiBoldFont - the value of "text-weight" property: the semi bold (demi bold) text weight
SemiBoldFont = 6 SemiBoldFont = 6
// BoldFont - the value of "text-weight" property: the bold text weight // BoldFont - the value of "text-weight" property: the bold text weight
BoldFont = 7 BoldFont = 7
// ExtraBoldFont - the value of "text-weight" property: the extra bold (ultra bold) text weight // ExtraBoldFont - the value of "text-weight" property: the extra bold (ultra bold) text weight
ExtraBoldFont = 8 ExtraBoldFont = 8
// BlackFont - the value of "text-weight" property: the black (heavy) text weight // BlackFont - the value of "text-weight" property: the black (heavy) text weight
BlackFont = 9 BlackFont = 9
// TopAlign - top vertical-align for the "vertical-align" property // TopAlign - top vertical-align for the "vertical-align" property
TopAlign = 0 TopAlign = 0
// BottomAlign - bottom vertical-align for the "vertical-align" property // BottomAlign - bottom vertical-align for the "vertical-align" property
BottomAlign = 1 BottomAlign = 1
// LeftAlign - the left horizontal-align for the "horizontal-align" property // LeftAlign - the left horizontal-align for the "horizontal-align" property
LeftAlign = 0 LeftAlign = 0
// RightAlign - the right horizontal-align for the "horizontal-align" property // RightAlign - the right horizontal-align for the "horizontal-align" property
RightAlign = 1 RightAlign = 1
// CenterAlign - the center horizontal/vertical-align for the "horizontal-align"/"vertical-align" property // CenterAlign - the center horizontal/vertical-align for the "horizontal-align"/"vertical-align" property
CenterAlign = 2 CenterAlign = 2
// StretchAlign - the stretch horizontal/vertical-align for the "horizontal-align"/"vertical-align" property // StretchAlign - the stretch horizontal/vertical-align for the "horizontal-align"/"vertical-align" property
StretchAlign = 3 StretchAlign = 3
// JustifyAlign - the justify text align for "text-align" property // JustifyAlign - the justify text align for "text-align" property
JustifyAlign = 3 JustifyAlign = 3
// BaselineAlign - the baseline cell-vertical-align for the "cell-vertical-align" property // BaselineAlign - the baseline cell-vertical-align for the "cell-vertical-align" property
BaselineAlign = 4 BaselineAlign = 4
// WhiteSpaceNormal - sequences of white space are collapsed. Newline characters in the source // 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. // are handled the same as other white space. Lines are broken as necessary to fill line boxes.
WhiteSpaceNormal = 0 WhiteSpaceNormal = 0
// WhiteSpaceNowrap - collapses white space as for normal, but suppresses line breaks (text wrapping) // WhiteSpaceNowrap - collapses white space as for normal, but suppresses line breaks (text wrapping)
// within the source. // within the source.
WhiteSpaceNowrap = 1 WhiteSpaceNowrap = 1
// WhiteSpacePre - sequences of white space are preserved. Lines are only broken at newline // WhiteSpacePre - sequences of white space are preserved. Lines are only broken at newline
// characters in the source and at <br> elements. // characters in the source and at <br> elements.
WhiteSpacePre = 2 WhiteSpacePre = 2
// WhiteSpacePreWrap - Sequences of white space are preserved. Lines are broken at newline // WhiteSpacePreWrap - Sequences of white space are preserved. Lines are broken at newline
// characters, at <br>, and as necessary to fill line boxes. // characters, at <br>, and as necessary to fill line boxes.
WhiteSpacePreWrap = 3 WhiteSpacePreWrap = 3
// WhiteSpacePreLine - sequences of white space are collapsed. Lines are broken at newline characters, // WhiteSpacePreLine - sequences of white space are collapsed. Lines are broken at newline characters,
// at <br>, and as necessary to fill line boxes. // at <br>, and as necessary to fill line boxes.
WhiteSpacePreLine = 4 WhiteSpacePreLine = 4
// WhiteSpaceBreakSpaces - the behavior is identical to that of WhiteSpacePreWrap, except that: // 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. // * 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. // * 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 - use the default line break rule.
WordBreakNormal = 0 WordBreakNormal = 0
// WordBreakAll - to prevent overflow, word breaks should be inserted between any two characters // WordBreakAll - to prevent overflow, word breaks should be inserted between any two characters
// (excluding Chinese/Japanese/Korean text). // (excluding Chinese/Japanese/Korean text).
WordBreakAll = 1 WordBreakAll = 1
// WordBreakKeepAll - word breaks should not be used for Chinese/Japanese/Korean (CJK) text. // WordBreakKeepAll - word breaks should not be used for Chinese/Japanese/Korean (CJK) text.
// Non-CJK text behavior is the same as for normal. // Non-CJK text behavior is the same as for normal.
WordBreakKeepAll = 2 WordBreakKeepAll = 2
// WordBreakWord - when the block boundaries are exceeded, the remaining whole words can be split // 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. // in an arbitrary place, unless a more suitable place for the line break is found.
WordBreakWord = 3 WordBreakWord = 3
@ -117,6 +171,7 @@ const (
// TextOverflowClip - truncate the text at the limit of the content area, therefore the truncation // TextOverflowClip - truncate the text at the limit of the content area, therefore the truncation
// can happen in the middle of a character. // can happen in the middle of a character.
TextOverflowClip = 0 TextOverflowClip = 0
// TextOverflowEllipsis - display an ellipsis ('…', U+2026 HORIZONTAL ELLIPSIS) to represent clipped text. // 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. // 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. // 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 - default value of the view Semantic property
DefaultSemantics = 0 DefaultSemantics = 0
// ArticleSemantics - value of the view Semantic property: view represents a self-contained // 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 // composition in a document, page, application, or site, which is intended to be
// independently distributable or reusable (e.g., in syndication) // independently distributable or reusable (e.g., in syndication)
ArticleSemantics = 1 ArticleSemantics = 1
// SectionSemantics - value of the view Semantic property: view represents // SectionSemantics - value of the view Semantic property: view represents
// a generic standalone section of a document, which doesn't have a more specific // a generic standalone section of a document, which doesn't have a more specific
// semantic element to represent it. // semantic element to represent it.
SectionSemantics = 2 SectionSemantics = 2
// AsideSemantics - value of the view Semantic property: view represents a portion // 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. // 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. // Asides are frequently presented as sidebars or call-out boxes.
AsideSemantics = 3 AsideSemantics = 3
// HeaderSemantics - value of the view Semantic property: view represents introductory // HeaderSemantics - value of the view Semantic property: view represents introductory
// content, typically a group of introductory or navigational aids. It may contain // 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. // some heading elements but also a logo, a search form, an author name, and other elements.
HeaderSemantics = 4 HeaderSemantics = 4
// MainSemantics - value of the view Semantic property: view represents the dominant content // 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 // 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. // to or expands upon the central topic of a document, or the central functionality of an application.
MainSemantics = 5 MainSemantics = 5
// FooterSemantics - value of the view Semantic property: view represents a footer for its // 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 // 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. // information about the author of the section, copyright data or links to related documents.
FooterSemantics = 6 FooterSemantics = 6
// NavigationSemantics - value of the view Semantic property: view represents a section of // 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 // 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, // or to other documents. Common examples of navigation sections are menus, tables of contents,
// and indexes. // and indexes.
NavigationSemantics = 7 NavigationSemantics = 7
// FigureSemantics - value of the view Semantic property: view represents self-contained content, // FigureSemantics - value of the view Semantic property: view represents self-contained content,
// potentially with an optional caption, which is specified using the FigureCaptionSemantics view. // potentially with an optional caption, which is specified using the FigureCaptionSemantics view.
FigureSemantics = 8 FigureSemantics = 8
// FigureCaptionSemantics - value of the view Semantic property: view represents a caption or // FigureCaptionSemantics - value of the view Semantic property: view represents a caption or
// legend describing the rest of the contents of its parent FigureSemantics view. // legend describing the rest of the contents of its parent FigureSemantics view.
FigureCaptionSemantics = 9 FigureCaptionSemantics = 9
// ButtonSemantics - value of the view Semantic property: view a clickable button // ButtonSemantics - value of the view Semantic property: view a clickable button
ButtonSemantics = 10 ButtonSemantics = 10
// ParagraphSemantics - value of the view Semantic property: view represents a paragraph. // ParagraphSemantics - value of the view Semantic property: view represents a paragraph.
// Paragraphs are usually represented in visual media as blocks of text separated // Paragraphs are usually represented in visual media as blocks of text separated
// from adjacent blocks by blank lines and/or first-line indentation // from adjacent blocks by blank lines and/or first-line indentation
ParagraphSemantics = 11 ParagraphSemantics = 11
// H1Semantics - value of the view Semantic property: view represent of first level section headings. // 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 is the highest section level and H6Semantics is the lowest.
H1Semantics = 12 H1Semantics = 12
// H2Semantics - value of the view Semantic property: view represent of second level section headings. // 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. // H1Semantics is the highest section level and H6Semantics is the lowest.
H2Semantics = 13 H2Semantics = 13
// H3Semantics - value of the view Semantic property: view represent of third level section headings. // 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. // H1Semantics is the highest section level and H6Semantics is the lowest.
H3Semantics = 14 H3Semantics = 14
// H4Semantics - value of the view Semantic property: view represent of fourth level section headings. // 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. // H1Semantics is the highest section level and H6Semantics is the lowest.
H4Semantics = 15 H4Semantics = 15
// H5Semantics - value of the view Semantic property: view represent of fifth level section headings. // 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. // H1Semantics is the highest section level and H6Semantics is the lowest.
H5Semantics = 16 H5Semantics = 16
// H6Semantics - value of the view Semantic property: view represent of sixth level section headings. // 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. // H1Semantics is the highest section level and H6Semantics is the lowest.
H6Semantics = 17 H6Semantics = 17
// BlockquoteSemantics - value of the view Semantic property: view indicates that // BlockquoteSemantics - value of the view Semantic property: view indicates that
// the enclosed text is an extended quotation. // the enclosed text is an extended quotation.
BlockquoteSemantics = 18 BlockquoteSemantics = 18
// CodeSemantics - value of the view Semantic property: view displays its contents styled // 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 // in a fashion intended to indicate that the text is a short fragment of computer code
CodeSemantics = 19 CodeSemantics = 19
// NoneFloat - value of the view "float" property: the View must not float. // NoneFloat - value of the view "float" property: the View must not float.
NoneFloat = 0 NoneFloat = 0
// LeftFloat - value of the view "float" property: the View must float on the left side of its containing block. // LeftFloat - value of the view "float" property: the View must float on the left side of its containing block.
LeftFloat = 1 LeftFloat = 1
// RightFloat - value of the view "float" property: the View must float on the right side of its containing block. // RightFloat - value of the view "float" property: the View must float on the right side of its containing block.
RightFloat = 2 RightFloat = 2
// NoneResize - value of the view "resize" property: the View The offers no user-controllable method for resizing it. // NoneResize - value of the view "resize" property: the View The offers no user-controllable method for resizing it.
NoneResize = 0 NoneResize = 0
// BothResize - value of the view "resize" property: the View displays a mechanism for allowing // 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. // the user to resize it, which may be resized both horizontally and vertically.
BothResize = 1 BothResize = 1
// HorizontalResize - value of the view "resize" property: the View displays a mechanism for allowing // HorizontalResize - value of the view "resize" property: the View displays a mechanism for allowing
// the user to resize it in the horizontal direction. // the user to resize it in the horizontal direction.
HorizontalResize = 2 HorizontalResize = 2
// VerticalResize - value of the view "resize" property: the View displays a mechanism for allowing // VerticalResize - value of the view "resize" property: the View displays a mechanism for allowing
// the user to resize it in the vertical direction. // the user to resize it in the vertical direction.
VerticalResize = 3 VerticalResize = 3
@ -212,14 +291,17 @@ const (
// RowAutoFlow - value of the "grid-auto-flow" property of the GridLayout: // 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. // Views are placed by filling each row in turn, adding new rows as necessary.
RowAutoFlow = 0 RowAutoFlow = 0
// ColumnAutoFlow - value of the "grid-auto-flow" property of the GridLayout: // 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. // Views are placed by filling each column in turn, adding new columns as necessary.
ColumnAutoFlow = 1 ColumnAutoFlow = 1
// RowDenseAutoFlow - value of the "grid-auto-flow" property of the GridLayout: // RowDenseAutoFlow - value of the "grid-auto-flow" property of the GridLayout:
// Views are placed by filling each row, adding new rows as necessary. // 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. // "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. // This may cause views to appear out-of-order, when doing so would fill in holes left by larger views.
RowDenseAutoFlow = 2 RowDenseAutoFlow = 2
// ColumnDenseAutoFlow - value of the "grid-auto-flow" property of the GridLayout: // ColumnDenseAutoFlow - value of the "grid-auto-flow" property of the GridLayout:
// Views are placed by filling each column, adding new columns as necessary. // 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. // "dense" packing algorithm attempts to fill in holes earlier in the grid, if smaller items come up later.

View File

@ -109,7 +109,7 @@ type radiusPropertyData struct {
// NewRadiusProperty creates the new RadiusProperty // NewRadiusProperty creates the new RadiusProperty
func NewRadiusProperty(params Params) RadiusProperty { func NewRadiusProperty(params Params) RadiusProperty {
result := new(radiusPropertyData) result := new(radiusPropertyData)
result.properties = map[string]interface{}{} result.properties = map[string]any{}
if params != nil { if params != nil {
for _, tag := range []string{X, Y, TopLeft, TopRight, BottomLeft, BottomRight, TopLeftX, TopLeftY, for _, tag := range []string{X, Y, TopLeft, TopRight, BottomLeft, BottomRight, TopLeftX, TopLeftY,
TopRightX, TopRightY, BottomLeftX, BottomLeftY, BottomRightX, BottomRightY} { 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) { switch value1 := value1.(type) {
case string: case string:
switch value2 := value2.(type) { 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 { if value == nil {
radius.Remove(tag) radius.Remove(tag)
return true return true
@ -318,7 +318,7 @@ func (radius *radiusPropertyData) Set(tag string, value interface{}) bool {
return false return false
} }
func (radius *radiusPropertyData) Get(tag string) interface{} { func (radius *radiusPropertyData) Get(tag string) any {
tag = radius.normalizeTag(tag) tag = radius.normalizeTag(tag)
if value, ok := radius.properties[tag]; ok { if value, ok := radius.properties[tag]; ok {
return value return value
@ -455,7 +455,7 @@ func (radius BoxRadius) String() string {
return buffer.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) && if (radius.TopLeftX.Type == Auto || radius.TopLeftX.Value == 0) &&
(radius.TopLeftY.Type == Auto || radius.TopLeftY.Value == 0) && (radius.TopLeftY.Type == Auto || radius.TopLeftY.Value == 0) &&
@ -471,23 +471,23 @@ func (radius BoxRadius) cssValue(builder cssBuilder) {
buffer := allocStringBuilder() buffer := allocStringBuilder()
defer freeStringBuilder(buffer) defer freeStringBuilder(buffer)
buffer.WriteString(radius.TopLeftX.cssString("0")) buffer.WriteString(radius.TopLeftX.cssString("0", session))
if radius.AllAnglesIsEqual() { if radius.AllAnglesIsEqual() {
if !radius.TopLeftX.Equal(radius.TopLeftY) { if !radius.TopLeftX.Equal(radius.TopLeftY) {
buffer.WriteString(" / ") buffer.WriteString(" / ")
buffer.WriteString(radius.TopLeftY.cssString("0")) buffer.WriteString(radius.TopLeftY.cssString("0", session))
} }
} else { } else {
buffer.WriteRune(' ') buffer.WriteRune(' ')
buffer.WriteString(radius.TopRightX.cssString("0")) buffer.WriteString(radius.TopRightX.cssString("0", session))
buffer.WriteRune(' ') buffer.WriteRune(' ')
buffer.WriteString(radius.BottomRightX.cssString("0")) buffer.WriteString(radius.BottomRightX.cssString("0", session))
buffer.WriteRune(' ') buffer.WriteRune(' ')
buffer.WriteString(radius.BottomLeftX.cssString("0")) buffer.WriteString(radius.BottomLeftX.cssString("0", session))
if !radius.TopLeftX.Equal(radius.TopLeftY) || if !radius.TopLeftX.Equal(radius.TopLeftY) ||
!radius.TopRightX.Equal(radius.TopRightY) || !radius.TopRightX.Equal(radius.TopRightY) ||
@ -495,22 +495,22 @@ func (radius BoxRadius) cssValue(builder cssBuilder) {
!radius.BottomRightX.Equal(radius.BottomRightY) { !radius.BottomRightX.Equal(radius.BottomRightY) {
buffer.WriteString(" / ") buffer.WriteString(" / ")
buffer.WriteString(radius.TopLeftY.cssString("0")) buffer.WriteString(radius.TopLeftY.cssString("0", session))
buffer.WriteRune(' ') buffer.WriteRune(' ')
buffer.WriteString(radius.TopRightY.cssString("0")) buffer.WriteString(radius.TopRightY.cssString("0", session))
buffer.WriteRune(' ') buffer.WriteRune(' ')
buffer.WriteString(radius.BottomRightY.cssString("0")) buffer.WriteString(radius.BottomRightY.cssString("0", session))
buffer.WriteRune(' ') buffer.WriteRune(' ')
buffer.WriteString(radius.BottomLeftY.cssString("0")) buffer.WriteString(radius.BottomLeftY.cssString("0", session))
} }
} }
builder.add("border-radius", buffer.String()) builder.add("border-radius", buffer.String())
} }
func (radius BoxRadius) cssString() string { func (radius BoxRadius) cssString(session Session) string {
var builder cssValueBuilder var builder cssValueBuilder
radius.cssValue(&builder) radius.cssValue(&builder, session)
return builder.finish() return builder.finish()
} }
@ -570,7 +570,7 @@ func getRadiusProperty(style Properties) RadiusProperty {
return NewRadiusProperty(nil) return NewRadiusProperty(nil)
} }
func (properties *propertyList) setRadius(value interface{}) bool { func (properties *propertyList) setRadius(value any) bool {
if value == nil { if value == nil {
delete(properties.properties, Radius) delete(properties.properties, Radius)
@ -645,7 +645,16 @@ func (properties *propertyList) setRadius(value interface{}) bool {
properties.properties[Radius] = radius properties.properties[Radius] = radius
return true return true
case float32:
return properties.setRadius(Px(float64(value)))
case float64:
return properties.setRadius(Px(value))
default: default:
if n, ok := isInt(value); ok {
return properties.setRadius(Px(float64(n)))
}
notCompatibleType(Radius, value) 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 { if value == nil {
properties.removeRadiusElement(tag) properties.removeRadiusElement(tag)
return true return true
@ -679,7 +688,7 @@ func (properties *propertyList) setRadiusElement(tag string, value interface{})
return false return false
} }
func getRadiusElement(style Properties, tag string) interface{} { func getRadiusElement(style Properties, tag string) any {
value := style.Get(Radius) value := style.Get(Radius)
if value != nil { if value != nil {
switch value := value.(type) { switch value := value.(type) {

View File

@ -45,7 +45,7 @@ type resizableData struct {
// NewResizable create new Resizable object and return it // NewResizable create new Resizable object and return it
func NewResizable(session Session, params Params) Resizable { func NewResizable(session Session, params Params) Resizable {
view := new(resizableData) view := new(resizableData)
view.Init(session) view.init(session)
setInitParams(view, params) setInitParams(view, params)
return view return view
} }
@ -54,8 +54,8 @@ func newResizable(session Session) View {
return NewResizable(session, nil) return NewResizable(session, nil)
} }
func (resizable *resizableData) Init(session Session) { func (resizable *resizableData) init(session Session) {
resizable.viewData.Init(session) resizable.viewData.init(session)
resizable.tag = "Resizable" resizable.tag = "Resizable"
resizable.systemClass = "ruiGridLayout" resizable.systemClass = "ruiGridLayout"
resizable.content = []View{} 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) 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 { if value == nil {
resizable.remove(tag) resizable.remove(tag)
return true return true
@ -183,7 +183,7 @@ func (resizable *resizableData) set(tag string, value interface{}) bool {
return resizable.viewData.set(tag, value) 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)) return resizable.get(strings.ToLower(tag))
} }
@ -235,7 +235,7 @@ func (resizable *resizableData) getSide() int {
return AllSides return AllSides
} }
func (resizable *resizableData) setSide(value interface{}) bool { func (resizable *resizableData) setSide(value any) bool {
switch value := value.(type) { switch value := value.(type) {
case string: case string:
if n, err := strconv.Atoi(value); err == nil { if n, err := strconv.Atoi(value); err == nil {
@ -340,7 +340,7 @@ func (resizable *resizableData) updateResizeBorderWidth() {
} }
func (resizable *resizableData) cellSizeCSS() (string, string) { func (resizable *resizableData) cellSizeCSS() (string, string) {
w := resizable.resizeBorderWidth().cssString("4px") w := resizable.resizeBorderWidth().cssString("4px", resizable.Session())
side := resizable.getSide() side := resizable.getSide()
column := "1fr" column := "1fr"
row := "1fr" row := "1fr"
@ -384,7 +384,7 @@ func (resizable *resizableData) htmlSubviews(self View, buffer *strings.Builder)
top := 1 top := 1
leftSide := (side & LeftSide) != 0 leftSide := (side & LeftSide) != 0
rightSide := (side & RightSide) != 0 rightSide := (side & RightSide) != 0
w := resizable.resizeBorderWidth().cssString("4px") w := resizable.resizeBorderWidth().cssString("4px", resizable.Session())
if leftSide { if leftSide {
left = 2 left = 2

View File

@ -3,9 +3,12 @@ package rui
// ResizeEvent is the constant for "resize-event" property tag. // ResizeEvent is the constant for "resize-event" property tag.
// The "resize-event" is fired when the view changes its size. // The "resize-event" is fired when the view changes its size.
// The main listener format: // The main listener format:
// func(View, Frame). //
// func(View, Frame).
//
// The additional listener formats: // The additional listener formats:
// func(Frame), func(View), and func(). //
// func(Frame), func(View), and func().
const ResizeEvent = "resize-event" const ResizeEvent = "resize-event"
func (view *viewData) onResize(self View, x, y, width, height float64) { 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.Top = y
view.frame.Width = width view.frame.Width = width
view.frame.Height = height view.frame.Height = height
for _, listener := range GetResizeListeners(view, "") { for _, listener := range GetResizeListeners(view) {
listener(self, view.frame) 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) onItemResize(self View, index string, x, y, width, height float64) {
} }
func (view *viewData) setFrameListener(tag string, value interface{}) bool { func (view *viewData) setFrameListener(tag string, value any) bool {
if value == nil { listeners, ok := valueToEventListeners[View, Frame](value)
delete(view.properties, tag) if !ok {
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:
notCompatibleType(tag, value) notCompatibleType(tag, value)
return false return false
} }
if listeners == nil {
delete(view.properties, tag)
} else {
view.properties[tag] = listeners
}
view.propertyChangedEvent(tag)
return true return true
} }
@ -190,10 +65,10 @@ func (view *viewData) Frame() Frame {
} }
// GetViewFrame returns the size and location of view's viewport. // 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 // 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 { func GetViewFrame(view View, subviewID ...string) Frame {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view == nil { if view == nil {
return Frame{} 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 // 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 // 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) { func GetResizeListeners(view View, subviewID ...string) []func(View, Frame) {
if subviewID != "" { return getEventListeners[View, Frame](view, subviewID, ResizeEvent)
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){}
} }

View File

@ -3,7 +3,6 @@ package rui
import ( import (
"embed" "embed"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
@ -171,7 +170,7 @@ func registerImage(fs *embed.FS, path, filename string) {
} }
func scanImagesDirectory(path, filePrefix 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 { for _, file := range files {
filename := file.Name() filename := file.Name()
if filename[0] != '.' { if filename[0] != '.' {
@ -189,7 +188,7 @@ func scanImagesDirectory(path, filePrefix string) {
} }
func scanThemesDir(path 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 { for _, file := range files {
filename := file.Name() filename := file.Name()
if filename[0] != '.' { if filename[0] != '.' {
@ -197,7 +196,7 @@ func scanThemesDir(path string) {
if file.IsDir() { if file.IsDir() {
scanThemesDir(newPath) scanThemesDir(newPath)
} else if strings.ToLower(filepath.Ext(newPath)) == ".rui" { } 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)) registerThemeText(string(data))
} else { } else {
ErrorLog(err.Error()) ErrorLog(err.Error())
@ -380,7 +379,7 @@ func AllRawResources() []string {
} }
if resources.path != "" { 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 { for _, file := range files {
result = append(result, file.Name()) result = append(result, file.Name())
} }
@ -388,7 +387,7 @@ func AllRawResources() []string {
} }
if exe, err := os.Executable(); err == nil { 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 { for _, file := range files {
result = append(result, file.Name()) result = append(result, file.Name())
} }

View File

@ -13,7 +13,7 @@ type ruiWriter interface {
endObject() endObject()
startArrayProperty(tag string) startArrayProperty(tag string)
endObArray() endObArray()
writeProperty(tag string, value interface{}) writeProperty(tag string, value any)
finish() string finish() string
} }
@ -112,7 +112,7 @@ func (writer *ruiWriterData) endObArray() {
writer.buffer.WriteString("],\n") writer.buffer.WriteString("],\n")
} }
func (writer *ruiWriterData) writeValue(value interface{}) { func (writer *ruiWriterData) writeValue(value any) {
switch value := value.(type) { switch value := value.(type) {
case string: case string:
@ -175,7 +175,7 @@ func (writer *ruiWriterData) writeValue(value interface{}) {
writer.buffer.WriteRune(']') writer.buffer.WriteRune(']')
} }
case []interface{}: case []any:
switch len(value) { switch len(value) {
case 0: case 0:
writer.buffer.WriteString("[]\n") writer.buffer.WriteString("[]\n")
@ -205,7 +205,7 @@ func (writer *ruiWriterData) writeValue(value interface{}) {
writer.buffer.WriteString(",\n") writer.buffer.WriteString(",\n")
} }
func (writer *ruiWriterData) writeProperty(tag string, value interface{}) { func (writer *ruiWriterData) writeProperty(tag string, value any) {
writer.writeIndent() writer.writeIndent()
writer.writeString(tag) writer.writeString(tag)
writer.buffer.WriteString(" = ") writer.buffer.WriteString(" = ")

View File

@ -3,11 +3,14 @@ package rui
import "fmt" import "fmt"
// ScrollEvent is the constant for "scroll-event" property tag. // 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: // The main listener format:
// func(View, Frame). //
// func(View, Frame).
//
// The additional listener formats: // The additional listener formats:
// func(Frame), func(View), and func(). //
// func(Frame), func(View), and func().
const ScrollEvent = "scroll-event" const ScrollEvent = "scroll-event"
func (view *viewData) onScroll(self View, x, y, width, height float64) { 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.Top = y
view.scroll.Width = width view.scroll.Width = width
view.scroll.Height = height view.scroll.Height = height
for _, listener := range GetScrollListeners(view, "") { for _, listener := range GetScrollListeners(view) {
listener(self, view.scroll) listener(self, view.scroll)
} }
} }
@ -32,10 +35,10 @@ func (view *viewData) setScroll(x, y, width, height float64) {
} }
// GetViewScroll returns ... // GetViewScroll returns ...
// If the second argument (subviewID) is "" then a value of the first argument (view) is returned // 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 { func GetViewScroll(view View, subviewID ...string) Frame {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view == nil { if view == nil {
return Frame{} 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 // 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 // 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) { func GetScrollListeners(view View, subviewID ...string) []func(View, Frame) {
if subviewID != "" { return getEventListeners[View, Frame](view, subviewID, ResizeEvent)
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){}
} }
// ScrollTo scrolls the view's content to the given position. // 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. // ScrollViewToEnd scrolls the view's content to the start of view.
// If the second argument (subviewID) is "" then the first argument (view) is used // If the second argument (subviewID) is not specified or it is "" then the first argument (view) is used
func ScrollViewToStart(view View, subviewID string) { func ScrollViewToStart(view View, subviewID ...string) {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
view.Session().runScript(`scrollToStart("` + view.htmlID() + `")`) 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. // ScrollViewToEnd scrolls the view's content to the end of view.
// If the second argument (subviewID) is "" then the first argument (view) is used // If the second argument (subviewID) is not specified or it is "" then the first argument (view) is used
func ScrollViewToEnd(view View, subviewID string) { func ScrollViewToEnd(view View, subviewID ...string) {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
view.Session().runScript(`scrollToEnd("` + view.htmlID() + `")`) view.Session().runScript(`scrollToEnd("` + view.htmlID() + `")`)

View File

@ -60,11 +60,11 @@ type Session interface {
RootView() View RootView() View
// Get returns a value of the view (with id defined by the first argument) property with name defined by the second argument. // 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. // 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. // 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 // 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 // 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 downloads (saves) on the client side the file located at the specified path on the server.
DownloadFile(path string) DownloadFile(path string)
@ -83,7 +83,7 @@ type Session interface {
viewByHTMLID(id string) View viewByHTMLID(id string) View
nextViewID() string nextViewID() string
styleProperty(styleTag, property string) interface{} styleProperty(styleTag, property string) any
setBrige(events chan DataObject, brige WebBrige) setBrige(events chan DataObject, brige WebBrige)
writeInitScript(writer *strings.Builder) 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 { if style := session.getCurrentTheme().style(styleTag); style != nil {
return style.getRaw(propertyTag) return style.getRaw(propertyTag)
} }
@ -312,14 +312,14 @@ func (session *sessionData) setIgnoreViewUpdates(ignore bool) {
session.ignoreUpdates = ignore 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 { if view := ViewByID(session.RootView(), viewID); view != nil {
return view.Get(tag) return view.Get(tag)
} }
return nil 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 { if view := ViewByID(session.RootView(), viewID); view != nil {
return view.Set(tag, value) return view.Set(tag, value)
} }
@ -434,7 +434,7 @@ func (session *sessionData) handleViewEvent(command string, data DataObject) {
if view := session.viewByHTMLID(viewID); view != nil { if view := session.viewByHTMLID(viewID); view != nil {
view.handleCommand(view, command, data) view.handleCommand(view, command, data)
} }
} else { } else if command != "clickOutsidePopup" {
ErrorLog(`"id" property not found. Event: ` + command) ErrorLog(`"id" property not found. Event: ` + command)
} }
} }

View File

@ -4,8 +4,8 @@ import (
"testing" "testing"
) )
var stopTestLogFlag = false // var stopTestLogFlag = false
var testLogDone chan int // var testLogDone chan int
var ignoreTestLog = false var ignoreTestLog = false
func createTestLog(t *testing.T, ignore bool) { func createTestLog(t *testing.T, ignore bool) {

View File

@ -111,7 +111,7 @@ func (shadow *viewShadowData) Remove(tag string) {
delete(shadow.properties, strings.ToLower(tag)) 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 { if value == nil {
shadow.Remove(tag) shadow.Remove(tag)
return true return true
@ -127,7 +127,7 @@ func (shadow *viewShadowData) Set(tag string, value interface{}) bool {
return false return false
} }
func (shadow *viewShadowData) Get(tag string) interface{} { func (shadow *viewShadowData) Get(tag string) any {
return shadow.propertyList.Get(strings.ToLower(tag)) 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("inset ")
} }
buffer.WriteString(offsetX.cssString("0")) buffer.WriteString(offsetX.cssString("0", session))
buffer.WriteByte(' ') buffer.WriteByte(' ')
buffer.WriteString(offsetY.cssString("0")) buffer.WriteString(offsetY.cssString("0", session))
buffer.WriteByte(' ') buffer.WriteByte(' ')
buffer.WriteString(blurRadius.cssString("0")) buffer.WriteString(blurRadius.cssString("0", session))
buffer.WriteByte(' ') buffer.WriteByte(' ')
buffer.WriteString(spreadRadius.cssString("0")) buffer.WriteString(spreadRadius.cssString("0", session))
buffer.WriteByte(' ') buffer.WriteByte(' ')
buffer.WriteString(color.cssString()) buffer.WriteString(color.cssString())
return true return true
@ -177,11 +177,11 @@ func (shadow *viewShadowData) cssTextStyle(buffer *strings.Builder, session Sess
} }
buffer.WriteString(lead) buffer.WriteString(lead)
buffer.WriteString(offsetX.cssString("0")) buffer.WriteString(offsetX.cssString("0", session))
buffer.WriteByte(' ') buffer.WriteByte(' ')
buffer.WriteString(offsetY.cssString("0")) buffer.WriteString(offsetY.cssString("0", session))
buffer.WriteByte(' ') buffer.WriteByte(' ')
buffer.WriteString(blurRadius.cssString("0")) buffer.WriteString(blurRadius.cssString("0", session))
buffer.WriteByte(' ') buffer.WriteByte(' ')
buffer.WriteString(color.cssString()) buffer.WriteString(color.cssString())
return true return true
@ -225,7 +225,7 @@ func (shadow *viewShadowData) writeString(buffer *strings.Builder, indent string
buffer.WriteString(" }") buffer.WriteString(" }")
} }
func (properties *propertyList) setShadow(tag string, value interface{}) bool { func (properties *propertyList) setShadow(tag string, value any) bool {
if value == nil { if value == nil {
delete(properties.properties, tag) delete(properties.properties, tag)

373
sizeFunc.go Normal file
View File

@ -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
}

50
sizeFunc_test.go Normal file
View File

@ -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))`)
}

View File

@ -14,89 +14,94 @@ import (
type SizeUnitType uint8 type SizeUnitType uint8
const ( const (
// Auto - default value. // Auto is the SizeUnit type: default value.
Auto SizeUnitType = 0 Auto SizeUnitType = 0
// SizeInPixel - size in pixels. // SizeInPixel is the SizeUnit type: the Value field specifies the size in pixels.
SizeInPixel SizeUnitType = 1 SizeInPixel SizeUnitType = 1
// SizeInEM - size in em. // SizeInEM is the SizeUnit type: the Value field specifies the size in em.
SizeInEM SizeUnitType = 2 SizeInEM SizeUnitType = 2
// SizeInEX - size in em. // SizeInEX is the SizeUnit type: the Value field specifies the size in em.
SizeInEX SizeUnitType = 3 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 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 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 SizeInPc SizeUnitType = 6
// SizeInInch - size in inches. // SizeInInch is the SizeUnit type: the Value field specifies the size in inches.
SizeInInch SizeUnitType = 7 SizeInInch SizeUnitType = 7
// SizeInMM - size in millimeters. // SizeInMM is the SizeUnit type: the Value field specifies the size in millimeters.
SizeInMM SizeUnitType = 8 SizeInMM SizeUnitType = 8
// SizeInCM - size in centimeters. // SizeInCM is the SizeUnit type: the Value field specifies the size in centimeters.
SizeInCM SizeUnitType = 9 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 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). // SizeUnit describe a size (Value field) and size unit (Type field).
type SizeUnit struct { type SizeUnit struct {
Type SizeUnitType Type SizeUnitType
Value float64 Value float64
Function SizeFunc
} }
// AutoSize creates SizeUnit with Auto type // AutoSize creates SizeUnit with Auto type
func AutoSize() SizeUnit { func AutoSize() SizeUnit {
return SizeUnit{Auto, 0} return SizeUnit{Auto, 0, nil}
} }
// Px creates SizeUnit with SizeInPixel type // Px creates SizeUnit with SizeInPixel type
func Px(value float64) SizeUnit { func Px(value float64) SizeUnit {
return SizeUnit{SizeInPixel, value} return SizeUnit{SizeInPixel, value, nil}
} }
// Em creates SizeUnit with SizeInEM type // Em creates SizeUnit with SizeInEM type
func Em(value float64) SizeUnit { func Em(value float64) SizeUnit {
return SizeUnit{SizeInEM, value} return SizeUnit{SizeInEM, value, nil}
} }
// Ex creates SizeUnit with SizeInEX type // Ex creates SizeUnit with SizeInEX type
func Ex(value float64) SizeUnit { func Ex(value float64) SizeUnit {
return SizeUnit{SizeInEX, value} return SizeUnit{SizeInEX, value, nil}
} }
// Percent creates SizeUnit with SizeInDIP type // Percent creates SizeUnit with SizeInDIP type
func Percent(value float64) SizeUnit { func Percent(value float64) SizeUnit {
return SizeUnit{SizeInPercent, value} return SizeUnit{SizeInPercent, value, nil}
} }
// Pt creates SizeUnit with SizeInPt type // Pt creates SizeUnit with SizeInPt type
func Pt(value float64) SizeUnit { func Pt(value float64) SizeUnit {
return SizeUnit{SizeInPt, value} return SizeUnit{SizeInPt, value, nil}
} }
// Pc creates SizeUnit with SizeInPc type // Pc creates SizeUnit with SizeInPc type
func Pc(value float64) SizeUnit { func Pc(value float64) SizeUnit {
return SizeUnit{SizeInPc, value} return SizeUnit{SizeInPc, value, nil}
} }
// Mm creates SizeUnit with SizeInMM type // Mm creates SizeUnit with SizeInMM type
func Mm(value float64) SizeUnit { func Mm(value float64) SizeUnit {
return SizeUnit{SizeInMM, value} return SizeUnit{SizeInMM, value, nil}
} }
// Cm creates SizeUnit with SizeInCM type // Cm creates SizeUnit with SizeInCM type
func Cm(value float64) SizeUnit { func Cm(value float64) SizeUnit {
return SizeUnit{SizeInCM, value} return SizeUnit{SizeInCM, value, nil}
} }
// Inch creates SizeUnit with SizeInInch type // Inch creates SizeUnit with SizeInInch type
func Inch(value float64) SizeUnit { func Inch(value float64) SizeUnit {
return SizeUnit{SizeInInch, value} return SizeUnit{SizeInInch, value, nil}
} }
// Fr creates SizeUnit with SizeInFraction type // Fr creates SizeUnit with SizeInFraction type
func Fr(value float64) SizeUnit { func Fr(value float64) SizeUnit {
return SizeUnit{SizeInFraction, value} return SizeUnit{SizeInFraction, value, nil}
} }
// Equal compare two SizeUnit. Return true if SizeUnit are equal // 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 + `"`) return SizeUnit{Type: Auto, Value: 0}, errors.New(`Invalid SizeUnit value: "` + value + `"`)
} }
// String - convert SizeUnit to string // String - convert SizeUnit to string
func (size SizeUnit) String() string { func (size SizeUnit) String() string {
if size.Type == Auto { switch size.Type {
case Auto:
return "auto" return "auto"
case SizeFunction:
if size.Function == nil {
return "auto"
}
return size.Function.String()
} }
if suffix, ok := sizeUnitSuffixes()[size.Type]; ok { if suffix, ok := sizeUnitSuffixes()[size.Type]; ok {
return fmt.Sprintf("%g%s", size.Value, suffix) return fmt.Sprintf("%g%s", size.Value, suffix)
@ -167,13 +183,19 @@ func (size SizeUnit) String() string {
} }
// cssString - convert SizeUnit to 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 { switch size.Type {
case Auto: case Auto:
return textForAuto return textForAuto
case SizeInEM: case SizeInEM:
return fmt.Sprintf("%grem", size.Value) return fmt.Sprintf("%grem", size.Value)
case SizeFunction:
if size.Function == nil {
return textForAuto
}
return size.Function.cssString(session)
} }
if size.Value == 0 { if size.Value == 0 {

View File

@ -41,7 +41,7 @@ type stackLayoutData struct {
// NewStackLayout create new StackLayout object and return it // NewStackLayout create new StackLayout object and return it
func NewStackLayout(session Session, params Params) StackLayout { func NewStackLayout(session Session, params Params) StackLayout {
view := new(stackLayoutData) view := new(stackLayoutData)
view.Init(session) view.init(session)
setInitParams(view, params) setInitParams(view, params)
return view return view
} }
@ -51,8 +51,8 @@ func newStackLayout(session Session) View {
} }
// Init initialize fields of ViewsContainer by default values // Init initialize fields of ViewsContainer by default values
func (layout *stackLayoutData) Init(session Session) { func (layout *stackLayoutData) init(session Session) {
layout.viewsContainerData.Init(session) layout.viewsContainerData.init(session)
layout.tag = "StackLayout" layout.tag = "StackLayout"
layout.systemClass = "ruiStackLayout" layout.systemClass = "ruiStackLayout"
layout.properties[TransitionEndEvent] = []func(View, string){layout.pushFinished, layout.popFinished} 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) 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 { if value == nil {
layout.remove(tag) layout.remove(tag)
return true return true
@ -109,8 +109,8 @@ func (layout *stackLayoutData) set(tag string, value interface{}) bool {
switch tag { switch tag {
case TransitionEndEvent: case TransitionEndEvent:
listeners, ok := valueToAnimationListeners(value) listeners, ok := valueToEventListeners[View, string](value)
if ok { if ok && listeners != nil {
listeners = append(listeners, layout.pushFinished) listeners = append(listeners, layout.pushFinished)
listeners = append(listeners, layout.popFinished) listeners = append(listeners, layout.popFinished)
layout.properties[TransitionEndEvent] = listeners 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)) return layout.get(strings.ToLower(tag))
} }
func (layout *stackLayoutData) get(tag string) interface{} { func (layout *stackLayoutData) get(tag string) any {
if tag == Current { if tag == Current {
return layout.peek return layout.peek
} }
@ -351,6 +351,8 @@ func (layout *stackLayoutData) Pop(animation int, onPopFinished func(View)) bool
buffer.WriteString(htmlID) buffer.WriteString(htmlID)
buffer.WriteString(`pop" class="ruiStackPageLayout" ontransitionend="stackTransitionEndEvent(\'`) buffer.WriteString(`pop" class="ruiStackPageLayout" ontransitionend="stackTransitionEndEvent(\'`)
buffer.WriteString(htmlID) buffer.WriteString(htmlID)
buffer.WriteString(`\', \'ruiPop\', event)" ontransitioncancel="stackTransitionEndEvent(\'`)
buffer.WriteString(htmlID)
buffer.WriteString(`\', \'ruiPop\', event)" style="transition: transform 1s ease;">`) buffer.WriteString(`\', \'ruiPop\', event)" style="transition: transform 1s ease;">`)
viewHTML(layout.popView, buffer) viewHTML(layout.popView, buffer)
buffer.WriteString(`</div>`) buffer.WriteString(`</div>`)

View File

@ -2,7 +2,7 @@ package rui
import ( import (
"embed" "embed"
"io/ioutil" "os"
"path/filepath" "path/filepath"
"strings" "strings"
) )
@ -28,7 +28,7 @@ func scanEmbedStringsDir(fs *embed.FS, dir string) {
} }
func scanStringsDir(path 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 { for _, file := range files {
filename := file.Name() filename := file.Name()
if filename[0] != '.' { if filename[0] != '.' {
@ -36,7 +36,7 @@ func scanStringsDir(path string) {
if file.IsDir() { if file.IsDir() {
scanStringsDir(newPath) scanStringsDir(newPath)
} else if strings.ToLower(filepath.Ext(newPath)) == ".rui" { } 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)) loadStringResources(string(data))
} else { } else {
ErrorLog(err.Error()) ErrorLog(err.Error())

View File

@ -18,7 +18,7 @@ type TableAdapter interface {
// * rui.View // * rui.View
// * fmt.Stringer // * fmt.Stringer
// * rui.VerticalTableJoin, rui.HorizontalTableJoin // * rui.VerticalTableJoin, rui.HorizontalTableJoin
Cell(row, column int) interface{} Cell(row, column int) any
} }
// TableColumnStyle describes the style of TableView columns. // TableColumnStyle describes the style of TableView columns.
@ -59,15 +59,15 @@ type TableAllowRowSelection interface {
} }
// SimpleTableAdapter is implementation of TableAdapter where the content // SimpleTableAdapter is implementation of TableAdapter where the content
// defines as [][]interface{}. // defines as [][]any.
// When you assign [][]interface{} value to the "content" property, it is converted to SimpleTableAdapter // When you assign [][]any value to the "content" property, it is converted to SimpleTableAdapter
type SimpleTableAdapter interface { type SimpleTableAdapter interface {
TableAdapter TableAdapter
TableCellStyle TableCellStyle
} }
type simpleTableAdapter struct { type simpleTableAdapter struct {
content [][]interface{} content [][]any
columnCount int columnCount int
} }
@ -94,7 +94,7 @@ type HorizontalTableJoin struct {
} }
// NewSimpleTableAdapter creates the new SimpleTableAdapter // NewSimpleTableAdapter creates the new SimpleTableAdapter
func NewSimpleTableAdapter(content [][]interface{}) SimpleTableAdapter { func NewSimpleTableAdapter(content [][]any) SimpleTableAdapter {
if content == nil { if content == nil {
return nil return nil
} }
@ -125,7 +125,7 @@ func (adapter *simpleTableAdapter) ColumnCount() int {
return adapter.columnCount 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) && if adapter.content != nil && row >= 0 && row < len(adapter.content) &&
adapter.content[row] != nil && column >= 0 && column < len(adapter.content[row]) { adapter.content[row] != nil && column >= 0 && column < len(adapter.content[row]) {
return adapter.content[row][column] return adapter.content[row][column]
@ -220,7 +220,7 @@ func (adapter *textTableAdapter) ColumnCount() int {
return adapter.columnCount 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) && if adapter.content != nil && row >= 0 && row < len(adapter.content) &&
adapter.content[row] != nil && column >= 0 && column < len(adapter.content[row]) { adapter.content[row] != nil && column >= 0 && column < len(adapter.content[row]) {
return adapter.content[row][column] return adapter.content[row][column]
@ -242,7 +242,7 @@ func (style *simpleTableRowStyle) RowStyle(row int) Params {
return nil return nil
} }
func (table *tableViewData) setRowStyle(value interface{}) bool { func (table *tableViewData) setRowStyle(value any) bool {
newSimpleTableRowStyle := func(params []Params) TableRowStyle { newSimpleTableRowStyle := func(params []Params) TableRowStyle {
if len(params) == 0 { if len(params) == 0 {
return nil return nil
@ -319,7 +319,7 @@ func (style *simpleTableColumnStyle) ColumnStyle(row int) Params {
return nil return nil
} }
func (table *tableViewData) setColumnStyle(value interface{}) bool { func (table *tableViewData) setColumnStyle(value any) bool {
newSimpleTableColumnStyle := func(params []Params) TableColumnStyle { newSimpleTableColumnStyle := func(params []Params) TableColumnStyle {
if len(params) == 0 { if len(params) == 0 {
return nil return nil

View File

@ -245,7 +245,7 @@ type tableCellView struct {
// NewTableView create new TableView object and return it // NewTableView create new TableView object and return it
func NewTableView(session Session, params Params) TableView { func NewTableView(session Session, params Params) TableView {
view := new(tableViewData) view := new(tableViewData)
view.Init(session) view.init(session)
setInitParams(view, params) setInitParams(view, params)
return view return view
} }
@ -255,8 +255,8 @@ func newTableView(session Session) View {
} }
// Init initialize fields of TableView by default values // Init initialize fields of TableView by default values
func (table *tableViewData) Init(session Session) { func (table *tableViewData) init(session Session) {
table.viewData.Init(session) table.viewData.init(session)
table.tag = "TableView" table.tag = "TableView"
table.cellViews = []View{} table.cellViews = []View{}
table.cellFrame = []Frame{} table.cellFrame = []Frame{}
@ -290,10 +290,10 @@ func (table *tableViewData) normalizeTag(tag string) string {
} }
func (table *tableViewData) Focusable() bool { 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)) 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) 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 { if value == nil {
table.remove(tag) table.remove(tag)
return true return true
@ -356,7 +356,7 @@ func (table *tableViewData) set(tag string, value interface{}) bool {
case TableAdapter: case TableAdapter:
table.properties[Content] = value table.properties[Content] = value
case [][]interface{}: case [][]any:
table.properties[Content] = NewSimpleTableAdapter(val) table.properties[Content] = NewSimpleTableAdapter(val)
case [][]string: case [][]string:
@ -384,20 +384,24 @@ func (table *tableViewData) set(tag string, value interface{}) bool {
table.cellSelectedListener = listeners table.cellSelectedListener = listeners
case TableRowClickedEvent: case TableRowClickedEvent:
listeners := table.valueToRowListeners(value) listeners, ok := valueToEventListeners[TableView, int](value)
if listeners == nil { if !ok {
notCompatibleType(tag, value) notCompatibleType(tag, value)
return false return false
} else if listeners == nil {
listeners = []func(TableView, int){}
} }
table.rowClickedListener = listeners table.rowClickedListener = listeners
case TableRowSelectedEvent: case TableRowSelectedEvent:
listeners := table.valueToRowListeners(value) listeners, ok := valueToEventListeners[TableView, int](value)
if listeners == nil { if !ok {
notCompatibleType(tag, value) notCompatibleType(tag, value)
return false return false
} else if listeners == nil {
listeners = []func(TableView, int){}
} }
table.rowSelectedListener = []func(TableView, int){} table.rowSelectedListener = listeners
case CellStyle: case CellStyle:
if style, ok := value.(TableCellStyle); ok { if style, ok := value.(TableCellStyle); ok {
@ -450,21 +454,23 @@ func (table *tableViewData) set(tag string, value interface{}) bool {
delete(table.properties, tag) 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: case DataNode:
switch value.Type() { switch value.Type() {
case ObjectNode: case ObjectNode:
obj := value.Object() return table.set(tag, 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)
}
case TextNode: case TextNode:
table.properties[tag] = value.Text() table.properties[tag] = value.Text()
@ -590,7 +596,7 @@ func (table *tableViewData) propertyChanged(tag string) {
updateCSSProperty(htmlID, "border-spacing", "0", session) updateCSSProperty(htmlID, "border-spacing", "0", session)
updateCSSProperty(htmlID, "border-collapse", "collapse", session) updateCSSProperty(htmlID, "border-collapse", "collapse", session)
} else { } else {
updateCSSProperty(htmlID, "border-spacing", gap.cssString("0"), session) updateCSSProperty(htmlID, "border-spacing", gap.cssString("0", session), session)
updateCSSProperty(htmlID, "border-collapse", "separate", session) updateCSSProperty(htmlID, "border-collapse", "separate", session)
} }
@ -598,7 +604,7 @@ func (table *tableViewData) propertyChanged(tag string) {
htmlID := table.htmlID() htmlID := table.htmlID()
session := table.Session() session := table.Session()
switch GetTableSelectionMode(table, "") { switch GetTableSelectionMode(table) {
case CellSelection: case CellSelection:
updateProperty(htmlID, "tabindex", "0", session) updateProperty(htmlID, "tabindex", "0", session)
updateProperty(htmlID, "onfocus", "tableViewFocusEvent(this, event)", session) updateProperty(htmlID, "onfocus", "tableViewFocusEvent(this, event)", session)
@ -676,7 +682,7 @@ func (table *tableViewData) currentInactiveStyle() string {
return "ruiCurrentTableCell" 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 { if value == nil {
return []func(TableView, int, int){} return []func(TableView, int, int){}
} }
@ -706,7 +712,7 @@ func (table *tableViewData) valueToCellListeners(value interface{}) []func(Table
} }
return listeners return listeners
case []interface{}: case []any:
listeners := make([]func(TableView, int, int), len(value)) listeners := make([]func(TableView, int, int), len(value))
for i, val := range value { for i, val := range value {
if val == nil { if val == nil {
@ -731,61 +737,6 @@ func (table *tableViewData) valueToCellListeners(value interface{}) []func(Table
return nil 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 { func (table *tableViewData) htmlTag() string {
return "table" return "table"
} }
@ -808,7 +759,7 @@ func (table *tableViewData) htmlProperties(self View, buffer *strings.Builder) {
buffer.WriteRune('"') 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(` onfocus="tableViewFocusEvent(this, event)" onblur="tableViewBlurEvent(this, event)" data-focusitemstyle="`)
buffer.WriteString(table.currentStyle()) buffer.WriteString(table.currentStyle())
buffer.WriteString(`" data-bluritemstyle="`) buffer.WriteString(`" data-bluritemstyle="`)
@ -879,10 +830,10 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
defer freeStringBuilder(cssBuilder.buffer) defer freeStringBuilder(cssBuilder.buffer)
var view tableCellView var view tableCellView
view.Init(session) view.init(session)
ignorCells := []struct{ row, column int }{} ignorCells := []struct{ row, column int }{}
selectionMode := GetTableSelectionMode(table, "") selectionMode := GetTableSelectionMode(table)
var allowCellSelection TableAllowCellSelection = nil var allowCellSelection TableAllowCellSelection = nil
if allow, ok := adapter.(TableAllowCellSelection); ok { if allow, ok := adapter.(TableAllowCellSelection); ok {
@ -905,7 +856,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
} }
vAlignCss := enumProperties[TableVerticalAlign].cssValues vAlignCss := enumProperties[TableVerticalAlign].cssValues
vAlignValue := GetTableVerticalAlign(table, "") vAlignValue := GetTableVerticalAlign(table)
if vAlignValue < 0 || vAlignValue >= len(vAlignCss) { if vAlignValue < 0 || vAlignValue >= len(vAlignCss) {
vAlignValue = 0 vAlignValue = 0
} }
@ -1160,8 +1111,8 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
buffer.WriteString("</colgroup>") buffer.WriteString("</colgroup>")
} }
headHeight := GetTableHeadHeight(table, "") headHeight := GetTableHeadHeight(table)
footHeight := GetTableFootHeight(table, "") footHeight := GetTableFootHeight(table)
cellBorder := table.getCellBorder() cellBorder := table.getCellBorder()
cellPadding := table.boundsProperty(CellPadding) cellPadding := table.boundsProperty(CellPadding)
if cellPadding == nil { if cellPadding == nil {
@ -1370,24 +1321,26 @@ func (table *tableViewData) getCurrent() CellIndex {
} }
func (table *tableViewData) cssStyle(self View, builder cssBuilder) { 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 { if !ok || gap.Type == Auto || gap.Value <= 0 {
builder.add("border-spacing", "0") builder.add("border-spacing", "0")
builder.add("border-collapse", "collapse") builder.add("border-collapse", "collapse")
} else { } else {
builder.add("border-spacing", gap.cssString("0")) builder.add("border-spacing", gap.cssString("0", session))
builder.add("border-collapse", "separate") builder.add("border-collapse", "separate")
} }
} }
func (table *tableViewData) ReloadTableData() { func (table *tableViewData) ReloadTableData() {
session := table.Session()
if content := table.content(); content != nil { if content := table.content(); content != nil {
updateProperty(table.htmlID(), "data-rows", strconv.Itoa(content.RowCount()), table.Session()) updateProperty(table.htmlID(), "data-rows", strconv.Itoa(content.RowCount()), session)
updateProperty(table.htmlID(), "data-columns", strconv.Itoa(content.ColumnCount()), table.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) { func (table *tableViewData) onItemResize(self View, index string, x, y, width, height float64) {

View File

@ -2,11 +2,11 @@ package rui
import "strings" 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) 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 { switch tag {
case VerticalAlign: case VerticalAlign:
tag = TableVerticalAlign tag = TableVerticalAlign
@ -24,10 +24,10 @@ func (cell *tableCellView) cssStyle(self View, builder cssBuilder) {
} }
// GetTableContent returns a TableAdapter which defines the TableView content. // 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. // 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 { func GetTableContent(view View, subviewID ...string) TableAdapter {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
@ -40,10 +40,10 @@ func GetTableContent(view View, subviewID string) TableAdapter {
} }
// GetTableRowStyle returns a TableRowStyle which defines styles of TableView rows. // 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. // 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 { func GetTableRowStyle(view View, subviewID ...string) TableRowStyle {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
@ -56,10 +56,10 @@ func GetTableRowStyle(view View, subviewID string) TableRowStyle {
} }
// GetTableColumnStyle returns a TableColumnStyle which defines styles of TableView columns. // 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. // 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 { func GetTableColumnStyle(view View, subviewID ...string) TableColumnStyle {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
@ -72,10 +72,10 @@ func GetTableColumnStyle(view View, subviewID string) TableColumnStyle {
} }
// GetTableCellStyle returns a TableCellStyle which defines styles of TableView cells. // 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. // 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 { func GetTableCellStyle(view View, subviewID ...string) TableCellStyle {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
@ -89,72 +89,42 @@ func GetTableCellStyle(view View, subviewID string) TableCellStyle {
// GetTableSelectionMode returns the mode of the TableView elements selection. // GetTableSelectionMode returns the mode of the TableView elements selection.
// Valid values are NoneSelection (0), CellSelection (1), and RowSelection (2). // 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. // 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 { func GetTableSelectionMode(view View, subviewID ...string) int {
if subviewID != "" { return enumStyledProperty(view, subviewID, SelectionMode, NoneSelection, false)
view = ViewByID(view, subviewID)
}
if view != nil {
if result, ok := enumStyledProperty(view, SelectionMode, NoneSelection); ok {
return result
}
}
return NoneSelection
} }
// GetTableVerticalAlign returns a vertical align in a TavleView cell. Returns one of next values: // GetTableVerticalAlign returns a vertical align in a TavleView cell. Returns one of next values:
// TopAlign (0), BottomAlign (1), CenterAlign (2), and BaselineAlign (3) // 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. // 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 { func GetTableVerticalAlign(view View, subviewID ...string) int {
if subviewID != "" { return enumStyledProperty(view, subviewID, TableVerticalAlign, TopAlign, false)
view = ViewByID(view, subviewID)
}
if view != nil {
if result, ok := enumStyledProperty(view, TableVerticalAlign, TopAlign); ok {
return result
}
}
return TopAlign
} }
// GetTableHeadHeight returns the number of rows in the table header. // 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. // 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 { func GetTableHeadHeight(view View, subviewID ...string) int {
if subviewID != "" { return intStyledProperty(view, subviewID, HeadHeight, 0)
view = ViewByID(view, subviewID)
}
if view != nil {
headHeight, _ := intStyledProperty(view, HeadHeight, 0)
return headHeight
}
return 0
} }
// GetTableFootHeight returns the number of rows in the table footer. // 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. // 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 { func GetTableFootHeight(view View, subviewID ...string) int {
if subviewID != "" { return intStyledProperty(view, subviewID, FootHeight, 0)
view = ViewByID(view, subviewID)
}
if view != nil {
headHeight, _ := intStyledProperty(view, FootHeight, 0)
return headHeight
}
return 0
} }
// GetTableCurrent returns the row and column index of the TableView selected cell/row. // 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), // 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. // 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 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. // 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 { func GetTableCurrent(view View, subviewID ...string) CellIndex {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
if selectionMode := GetTableSelectionMode(view, ""); selectionMode != NoneSelection { if selectionMode := GetTableSelectionMode(view); selectionMode != NoneSelection {
if tableView, ok := view.(TableView); ok { if tableView, ok := view.(TableView); ok {
return tableView.getCurrent() 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. // 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 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. // 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) { func GetTableCellClickedListeners(view View, subviewID ...string) []func(TableView, int, int) {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
if value := view.Get(TableCellClickedEvent); value != 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. // 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 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. // 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) { func GetTableCellSelectedListeners(view View, subviewID ...string) []func(TableView, int, int) {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
if value := view.Get(TableCellSelectedEvent); value != 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. // 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 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. // 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) { func GetTableRowClickedListeners(view View, subviewID ...string) []func(TableView, int) {
if subviewID != "" { return getEventListeners[TableView, int](view, subviewID, TableRowClickedEvent)
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){}
} }
// GetTableRowSelectedListeners returns listeners of event which occurs when a table row becomes selected. // 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 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. // 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) { func GetTableRowSelectedListeners(view View, subviewID ...string) []func(TableView, int) {
if subviewID != "" { return getEventListeners[TableView, int](view, subviewID, TableRowSelectedEvent)
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){}
} }
// ReloadTableViewData updates TableView // ReloadTableViewData updates TableView
func ReloadTableViewData(view View, subviewID string) bool { func ReloadTableViewData(view View, subviewID ...string) bool {
var tableView TableView var tableView TableView
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
if tableView = TableViewByID(view, subviewID); tableView == nil { if tableView = TableViewByID(view, subviewID[0]); tableView == nil {
return false return false
} }
} else { } else {

View File

@ -81,7 +81,7 @@ type tabsLayoutData struct {
// NewTabsLayout create new TabsLayout object and return it // NewTabsLayout create new TabsLayout object and return it
func NewTabsLayout(session Session, params Params) TabsLayout { func NewTabsLayout(session Session, params Params) TabsLayout {
view := new(tabsLayoutData) view := new(tabsLayoutData)
view.Init(session) view.init(session)
setInitParams(view, params) setInitParams(view, params)
return view return view
} }
@ -91,8 +91,8 @@ func newTabsLayout(session Session) View {
} }
// Init initialize fields of ViewsContainer by default values // Init initialize fields of ViewsContainer by default values
func (tabsLayout *tabsLayoutData) Init(session Session) { func (tabsLayout *tabsLayoutData) init(session Session) {
tabsLayout.viewsContainerData.Init(session) tabsLayout.viewsContainerData.init(session)
tabsLayout.tag = "TabsLayout" tabsLayout.tag = "TabsLayout"
tabsLayout.systemClass = "ruiTabsLayout" tabsLayout.systemClass = "ruiTabsLayout"
tabsLayout.tabListener = []func(TabsLayout, int, int){} tabsLayout.tabListener = []func(TabsLayout, int, int){}
@ -108,11 +108,11 @@ func (tabsLayout *tabsLayoutData) currentItem(defaultValue int) int {
return result return result
} }
func (tabsLayout *tabsLayoutData) Get(tag string) interface{} { func (tabsLayout *tabsLayoutData) Get(tag string) any {
return tabsLayout.get(strings.ToLower(tag)) return tabsLayout.get(strings.ToLower(tag))
} }
func (tabsLayout *tabsLayoutData) get(tag string) interface{} { func (tabsLayout *tabsLayoutData) get(tag string) any {
switch tag { switch tag {
case CurrentTabChangedEvent: case CurrentTabChangedEvent:
return tabsLayout.tabListener return tabsLayout.tabListener
@ -190,11 +190,11 @@ func (tabsLayout *tabsLayoutData) remove(tag string) {
tabsLayout.propertyChangedEvent(tag) 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) 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 { if value == nil {
tabsLayout.remove(tag) tabsLayout.remove(tag)
return true return true
@ -210,10 +210,12 @@ func (tabsLayout *tabsLayoutData) set(tag string, value interface{}) bool {
tabsLayout.tabListener = listeners tabsLayout.tabListener = listeners
case TabCloseEvent: case TabCloseEvent:
listeners := tabsLayout.valueToCloseListeners(value) listeners, ok := valueToEventListeners[TabsLayout, int](value)
if listeners == nil { if !ok {
notCompatibleType(tag, value) notCompatibleType(tag, value)
return false return false
} else if listeners == nil {
listeners = []func(TabsLayout, int){}
} }
tabsLayout.tabCloseListener = listeners tabsLayout.tabCloseListener = listeners
@ -286,7 +288,7 @@ func (tabsLayout *tabsLayoutData) set(tag string, value interface{}) bool {
return true 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 { if value == nil {
return []func(TabsLayout, int, int){} return []func(TabsLayout, int, int){}
} }
@ -388,7 +390,7 @@ func (tabsLayout *tabsLayoutData) valueToTabListeners(value interface{}) []func(
} }
return listeners return listeners
case []interface{}: case []any:
listeners := make([]func(TabsLayout, int, int), len(value)) listeners := make([]func(TabsLayout, int, int), len(value))
for i, val := range value { for i, val := range value {
if val == nil { if val == nil {
@ -433,61 +435,6 @@ func (tabsLayout *tabsLayoutData) valueToTabListeners(value interface{}) []func(
return nil 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 { func (tabsLayout *tabsLayoutData) tabsLocation() int {
tabs, _ := enumProperty(tabsLayout, Tabs, tabsLayout.session, 0) tabs, _ := enumProperty(tabsLayout, Tabs, tabsLayout.session, 0)
return tabs return tabs
@ -576,7 +523,7 @@ func (tabsLayout *tabsLayoutData) ListItem(index int, session Session) View {
if !ok || title == "" { if !ok || title == "" {
title = "No title" title = "No title"
} }
if !GetNotTranslate(tabsLayout, "") { if !GetNotTranslate(tabsLayout) {
title, _ = tabsLayout.Session().GetString(title) title, _ = tabsLayout.Session().GetString(title)
} }
@ -776,7 +723,7 @@ func (tabsLayout *tabsLayoutData) htmlSubviews(self View, buffer *strings.Builde
inactiveStyle := tabsLayout.inactiveTabStyle() inactiveStyle := tabsLayout.inactiveTabStyle()
activeStyle := tabsLayout.activeTabStyle() activeStyle := tabsLayout.activeTabStyle()
notTranslate := GetNotTranslate(tabsLayout, "") notTranslate := GetNotTranslate(tabsLayout)
closeButton, _ := boolProperty(tabsLayout, TabCloseButton, tabsLayout.session) closeButton, _ := boolProperty(tabsLayout, TabCloseButton, tabsLayout.session)
var tabStyle, titleDiv string var tabStyle, titleDiv string

View File

@ -17,7 +17,7 @@ type textViewData struct {
// NewTextView create new TextView object and return it // NewTextView create new TextView object and return it
func NewTextView(session Session, params Params) TextView { func NewTextView(session Session, params Params) TextView {
view := new(textViewData) view := new(textViewData)
view.Init(session) view.init(session)
setInitParams(view, params) setInitParams(view, params)
return view return view
} }
@ -27,8 +27,8 @@ func newTextView(session Session) View {
} }
// Init initialize fields of TextView by default values // Init initialize fields of TextView by default values
func (textView *textViewData) Init(session Session) { func (textView *textViewData) init(session Session) {
textView.viewData.Init(session) textView.viewData.init(session)
textView.tag = "TextView" textView.tag = "TextView"
} }
@ -36,7 +36,7 @@ func (textView *textViewData) String() string {
return getViewString(textView) return getViewString(textView)
} }
func (textView *textViewData) Get(tag string) interface{} { func (textView *textViewData) Get(tag string) any {
return textView.get(strings.ToLower(tag)) 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) 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 { switch tag {
case Text: case Text:
switch value := value.(type) { switch value := value.(type) {
@ -155,7 +155,7 @@ func textToJS(text string) string {
func (textView *textViewData) htmlSubviews(self View, buffer *strings.Builder) { func (textView *textViewData) htmlSubviews(self View, buffer *strings.Builder) {
if value := textView.getRaw(Text); value != nil { if value := textView.getRaw(Text); value != nil {
if text, ok := value.(string); ok { if text, ok := value.(string); ok {
if !GetNotTranslate(textView, "") { if !GetNotTranslate(textView) {
text, _ = textView.session.GetString(text) text, _ = textView.session.GetString(text)
} }
buffer.WriteString(textToJS(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: // GetTextOverflow returns a value of the "text-overflow" property:
// TextOverflowClip (0) or TextOverflowEllipsis (1). // TextOverflowClip (0) or TextOverflowEllipsis (1).
// 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 GetTextOverflow(view View, subviewID string) int { func GetTextOverflow(view View, subviewID ...string) int {
if subviewID != "" { return enumStyledProperty(view, subviewID, TextOverflow, SingleLineText, false)
view = ViewByID(view, subviewID)
}
if view == nil {
return SingleLineText
}
t, _ := enumStyledProperty(view, TextOverflow, SingleLineText)
return t
} }

View File

@ -29,7 +29,7 @@ type timePickerData struct {
// NewTimePicker create new TimePicker object and return it // NewTimePicker create new TimePicker object and return it
func NewTimePicker(session Session, params Params) TimePicker { func NewTimePicker(session Session, params Params) TimePicker {
view := new(timePickerData) view := new(timePickerData)
view.Init(session) view.init(session)
setInitParams(view, params) setInitParams(view, params)
return view return view
} }
@ -38,8 +38,8 @@ func newTimePicker(session Session) View {
return NewTimePicker(session, nil) return NewTimePicker(session, nil)
} }
func (picker *timePickerData) Init(session Session) { func (picker *timePickerData) init(session Session) {
picker.viewData.Init(session) picker.viewData.init(session)
picker.tag = "TimePicker" picker.tag = "TimePicker"
picker.timeChangedListeners = []func(TimePicker, time.Time){} picker.timeChangedListeners = []func(TimePicker, time.Time){}
} }
@ -96,7 +96,7 @@ func (picker *timePickerData) remove(tag string) {
case TimePickerValue: case TimePickerValue:
if _, ok := picker.properties[TimePickerValue]; ok { if _, ok := picker.properties[TimePickerValue]; ok {
delete(picker.properties, TimePickerValue) delete(picker.properties, TimePickerValue)
time := GetTimePickerValue(picker, "") time := GetTimePickerValue(picker)
if picker.created { if picker.created {
picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, picker.htmlID(), time.Format(timeFormat))) 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) 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) 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 { if value == nil {
picker.remove(tag) picker.remove(tag)
return true return true
@ -192,9 +192,9 @@ func (picker *timePickerData) set(tag string, value interface{}) bool {
} }
case TimePickerStep: case TimePickerStep:
oldStep := GetTimePickerStep(picker, "") oldStep := GetTimePickerStep(picker)
if picker.setIntProperty(TimePickerStep, value) { if picker.setIntProperty(TimePickerStep, value) {
if step := GetTimePickerStep(picker, ""); oldStep != step { if step := GetTimePickerStep(picker); oldStep != step {
if picker.created { if picker.created {
if step > 0 { if step > 0 {
updateProperty(picker.htmlID(), Step, strconv.Itoa(step), picker.session) updateProperty(picker.htmlID(), Step, strconv.Itoa(step), picker.session)
@ -208,7 +208,7 @@ func (picker *timePickerData) set(tag string, value interface{}) bool {
} }
case TimePickerValue: case TimePickerValue:
oldTime := GetTimePickerValue(picker, "") oldTime := GetTimePickerValue(picker)
if time, ok := setTimeValue(TimePickerValue); ok { if time, ok := setTimeValue(TimePickerValue); ok {
if time != oldTime { if time != oldTime {
if picker.created { if picker.created {
@ -223,57 +223,14 @@ func (picker *timePickerData) set(tag string, value interface{}) bool {
} }
case TimeChangedEvent: case TimeChangedEvent:
switch value := value.(type) { listeners, ok := valueToEventListeners[TimePicker, time.Time](value)
case func(TimePicker, time.Time): if !ok {
picker.timeChangedListeners = []func(TimePicker, time.Time){value} notCompatibleType(tag, value)
return false
case func(time.Time): } else if listeners == nil {
fn := func(_ TimePicker, time time.Time) { listeners = []func(TimePicker, 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
} }
picker.timeChangedListeners = listeners
picker.propertyChangedEvent(tag) picker.propertyChangedEvent(tag)
return true return true
@ -283,11 +240,11 @@ func (picker *timePickerData) set(tag string, value interface{}) bool {
return false return false
} }
func (picker *timePickerData) Get(tag string) interface{} { func (picker *timePickerData) Get(tag string) any {
return picker.get(picker.normalizeTag(tag)) return picker.get(picker.normalizeTag(tag))
} }
func (picker *timePickerData) get(tag string) interface{} { func (picker *timePickerData) get(tag string) any {
switch tag { switch tag {
case TimeChangedEvent: case TimeChangedEvent:
return picker.timeChangedListeners return picker.timeChangedListeners
@ -325,7 +282,7 @@ func (picker *timePickerData) htmlProperties(self View, buffer *strings.Builder)
} }
buffer.WriteString(` value="`) buffer.WriteString(` value="`)
buffer.WriteString(GetTimePickerValue(picker, "").Format(timeFormat)) buffer.WriteString(GetTimePickerValue(picker).Format(timeFormat))
buffer.WriteByte('"') buffer.WriteByte('"')
buffer.WriteString(` oninput="editViewInputEvent(this)"`) 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) { func (picker *timePickerData) htmlDisabledProperties(self View, buffer *strings.Builder) {
if IsDisabled(self, "") { if IsDisabled(self) {
buffer.WriteString(` disabled`) buffer.WriteString(` disabled`)
} }
picker.viewData.htmlDisabledProperties(self, buffer) picker.viewData.htmlDisabledProperties(self, buffer)
@ -346,7 +303,7 @@ func (picker *timePickerData) handleCommand(self View, command string, data Data
case "textChanged": case "textChanged":
if text, ok := data.PropertyValue("text"); ok { if text, ok := data.PropertyValue("text"); ok {
if value, err := time.Parse(timeFormat, text); err == nil { if value, err := time.Parse(timeFormat, text); err == nil {
oldValue := GetTimePickerValue(picker, "") oldValue := GetTimePickerValue(picker)
picker.properties[TimePickerValue] = value picker.properties[TimePickerValue] = value
if value != oldValue { if value != oldValue {
for _, listener := range picker.timeChangedListeners { 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) { 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 { if value != nil {
switch value := value.(type) { switch value := value.(type) {
case time.Time: 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, // 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. // "false" as the second value otherwise.
// 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 GetTimePickerMin(view View, subviewID string) (time.Time, bool) { func GetTimePickerMin(view View, subviewID ...string) (time.Time, bool) {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
return getTimeProperty(view, TimePickerMin, Min) 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, // 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. // "false" as the second value otherwise.
// 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 GetTimePickerMax(view View, subviewID string) (time.Time, bool) { func GetTimePickerMax(view View, subviewID ...string) (time.Time, bool) {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
return getTimeProperty(view, TimePickerMax, Max) 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. // 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. // 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 { func GetTimePickerStep(view View, subviewID ...string) int {
if subviewID != "" { return intStyledProperty(view, subviewID, TimePickerStep, 60)
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
} }
// GetTimePickerValue returns the time of TimePicker subview. // GetTimePickerValue returns the time of TimePicker subview.
// 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 GetTimePickerValue(view View, subviewID string) time.Time { func GetTimePickerValue(view View, subviewID ...string) time.Time {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view == nil { if view == nil {
return time.Now() return time.Now()
@ -456,17 +398,7 @@ func GetTimePickerValue(view View, subviewID string) time.Time {
// GetTimeChangedListeners returns the TimeChangedListener list of an TimePicker subview. // GetTimeChangedListeners returns the TimeChangedListener list of an TimePicker subview.
// If there are no listeners then the empty list is returned // 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. // 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) { func GetTimeChangedListeners(view View, subviewID ...string) []func(TimePicker, time.Time) {
if subviewID != "" { return getEventListeners[TimePicker, time.Time](view, subviewID, TimeChangedEvent)
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){}
} }

View File

@ -90,131 +90,6 @@ type TouchEvent struct {
MetaKey bool 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 }{ var touchEvents = map[string]struct{ jsEvent, jsFunc string }{
TouchStart: {jsEvent: "ontouchstart", jsFunc: "touchStartEvent"}, TouchStart: {jsEvent: "ontouchstart", jsFunc: "touchStartEvent"},
TouchEnd: {jsEvent: "ontouchend", jsFunc: "touchEndEvent"}, TouchEnd: {jsEvent: "ontouchend", jsFunc: "touchEndEvent"},
@ -222,8 +97,8 @@ var touchEvents = map[string]struct{ jsEvent, jsFunc string }{
TouchCancel: {jsEvent: "ontouchcancel", jsFunc: "touchCancelEvent"}, TouchCancel: {jsEvent: "ontouchcancel", jsFunc: "touchCancelEvent"},
} }
func (view *viewData) setTouchListener(tag string, value interface{}) bool { func (view *viewData) setTouchListener(tag string, value any) bool {
listeners, ok := valueToTouchListeners(value) listeners, ok := valueToEventListeners[View, TouchEvent](value)
if !ok { if !ok {
notCompatibleType(tag, value) notCompatibleType(tag, value)
return false 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) { func touchEventsHtml(view View, buffer *strings.Builder) {
for tag, js := range touchEvents { for tag, js := range touchEvents {
if value := view.getRaw(tag); value != nil { 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) { func handleTouchEvents(view View, tag string, data DataObject) {
listeners := getTouchListeners(view, "", tag) listeners := getEventListeners[View, TouchEvent](view, nil, tag)
if len(listeners) == 0 { if len(listeners) == 0 {
return 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. // 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. // 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) { func GetTouchStartListeners(view View, subviewID ...string) []func(View, TouchEvent) {
return getTouchListeners(view, subviewID, TouchStart) 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. // 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. // 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) { func GetTouchEndListeners(view View, subviewID ...string) []func(View, TouchEvent) {
return getTouchListeners(view, subviewID, TouchEnd) 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. // 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. // 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) { func GetTouchMoveListeners(view View, subviewID ...string) []func(View, TouchEvent) {
return getTouchListeners(view, subviewID, TouchMove) 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. // 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. // 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) { func GetTouchCancelListeners(view View, subviewID ...string) []func(View, TouchEvent) {
return getTouchListeners(view, subviewID, TouchCancel) return getEventListeners[View, TouchEvent](view, subviewID, TouchCancel)
} }

View File

@ -1,7 +1,6 @@
package rui package rui
import ( import (
"fmt"
"strings" "strings"
) )
@ -30,7 +29,7 @@ type videoPlayerData struct {
// NewVideoPlayer create new MediaPlayer object and return it // NewVideoPlayer create new MediaPlayer object and return it
func NewVideoPlayer(session Session, params Params) VideoPlayer { func NewVideoPlayer(session Session, params Params) VideoPlayer {
view := new(videoPlayerData) view := new(videoPlayerData)
view.Init(session) view.init(session)
view.tag = "VideoPlayer" view.tag = "VideoPlayer"
setInitParams(view, params) setInitParams(view, params)
return view return view
@ -40,8 +39,8 @@ func newVideoPlayer(session Session) View {
return NewVideoPlayer(session, nil) return NewVideoPlayer(session, nil)
} }
func (player *videoPlayerData) Init(session Session) { func (player *videoPlayerData) init(session Session) {
player.mediaPlayerData.Init(session) player.mediaPlayerData.init(session)
player.tag = "VideoPlayer" 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) 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 { if value == nil {
player.remove(tag) player.remove(tag)
return true return true
@ -90,9 +89,9 @@ func (player *videoPlayerData) set(tag string, value interface{}) bool {
if player.mediaPlayerData.set(tag, value) { if player.mediaPlayerData.set(tag, value) {
session := player.Session() session := player.Session()
updateSize := func(cssTag string) { updateSize := func(cssTag string) {
if size, ok := floatProperty(player, tag, session, 0); ok { if size, ok := floatTextProperty(player, tag, session, 0); ok {
if size > 0 { if size != "0" {
updateProperty(player.htmlID(), cssTag, fmt.Sprintf("%g", size), session) updateProperty(player.htmlID(), cssTag, size, session)
} else { } else {
removeProperty(player.htmlID(), cssTag, session) removeProperty(player.htmlID(), cssTag, session)
} }
@ -122,12 +121,16 @@ func (player *videoPlayerData) htmlProperties(self View, buffer *strings.Builder
session := player.Session() session := player.Session()
if size, ok := floatProperty(player, VideoWidth, session, 0); ok && size > 0 { if size, ok := floatTextProperty(player, VideoWidth, session, 0); ok && size != "0" {
buffer.WriteString(fmt.Sprintf(` width="%g"`, size)) buffer.WriteString(` width="`)
buffer.WriteString(size)
buffer.WriteString(`"`)
} }
if size, ok := floatProperty(player, VideoHeight, session, 0); ok && size > 0 { if size, ok := floatTextProperty(player, VideoHeight, session, 0); ok && size != "0" {
buffer.WriteString(fmt.Sprintf(` height="%g"`, size)) buffer.WriteString(` height="`)
buffer.WriteString(size)
buffer.WriteString(`"`)
} }
if url, ok := stringProperty(player, Poster, session); ok && url != "" { if url, ok := stringProperty(player, Poster, session); ok && url != "" {

71
view.go
View File

@ -33,14 +33,10 @@ type View interface {
ViewStyle ViewStyle
fmt.Stringer fmt.Stringer
// Init initializes fields of View by default values
Init(session Session)
// Session returns the current Session interface // Session returns the current Session interface
Session() Session Session() Session
// Parent returns the parent view // Parent returns the parent view
Parent() View Parent() View
parentHTMLID() string
setParentID(parentID string)
// Tag returns the tag of View interface // Tag returns the tag of View interface
Tag() string Tag() string
// ID returns the id of the view // 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. // 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 // 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 // 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 set the function to track the change of the View property
SetChangeListener(tag string, listener func(View, string)) SetChangeListener(tag string, listener func(View, string))
// HasFocus returns 'true' if the view has focus // HasFocus returns 'true' if the view has focus
@ -65,14 +61,14 @@ type View interface {
htmlTag() string htmlTag() string
closeHTMLTag() bool closeHTMLTag() bool
htmlID() string htmlID() string
parentHTMLID() string
setParentID(parentID string)
htmlSubviews(self View, buffer *strings.Builder) htmlSubviews(self View, buffer *strings.Builder)
htmlProperties(self View, buffer *strings.Builder) htmlProperties(self View, buffer *strings.Builder)
htmlDisabledProperties(self View, buffer *strings.Builder) htmlDisabledProperties(self View, buffer *strings.Builder)
cssStyle(self View, builder cssBuilder) cssStyle(self View, builder cssBuilder)
addToCSSStyle(addCSS map[string]string) addToCSSStyle(addCSS map[string]string)
getTransitions() Params
onResize(self View, x, y, width, height float64) onResize(self View, x, y, width, height float64)
onItemResize(self View, index string, x, y, width, height float64) onItemResize(self View, index string, x, y, width, height float64)
setNoResizeEvent() setNoResizeEvent()
@ -102,7 +98,7 @@ type viewData struct {
func newView(session Session) View { func newView(session Session) View {
view := new(viewData) view := new(viewData)
view.Init(session) view.init(session)
return view return view
} }
@ -124,12 +120,12 @@ func setInitParams(view View, params Params) {
// NewView create new View object and return it // NewView create new View object and return it
func NewView(session Session, params Params) View { func NewView(session Session, params Params) View {
view := new(viewData) view := new(viewData)
view.Init(session) view.init(session)
setInitParams(view, params) setInitParams(view, params)
return view return view
} }
func (view *viewData) Init(session Session) { func (view *viewData) init(session Session) {
view.viewStyle.init() view.viewStyle.init()
view.tag = "View" view.tag = "View"
view.session = session view.session = session
@ -197,7 +193,7 @@ func (view *viewData) remove(tag string) {
case Style, StyleDisabled: case Style, StyleDisabled:
if _, ok := view.properties[tag]; ok { if _, ok := view.properties[tag]; ok {
delete(view.properties, tag) 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: 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) 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 { if value == nil {
view.remove(tag) view.remove(tag)
return true return true
@ -327,7 +323,7 @@ func (view *viewData) set(tag string, value interface{}) bool {
} }
view.properties[tag] = text view.properties[tag] = text
if view.created { 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: case FocusEvent, LostFocusEvent:
@ -381,7 +377,7 @@ func viewPropertyChanged(view *viewData, tag string) {
return return
case Visibility: case Visibility:
switch GetVisibility(view, "") { switch GetVisibility(view) {
case Invisible: case Invisible:
updateCSSProperty(htmlID, Visibility, "hidden", session) updateCSSProperty(htmlID, Visibility, "hidden", session)
updateCSSProperty(htmlID, "display", "", session) updateCSSProperty(htmlID, "display", "", session)
@ -450,7 +446,7 @@ func viewPropertyChanged(view *viewData, tag string) {
return return
case Outline, OutlineColor, OutlineStyle, OutlineWidth: case Outline, OutlineColor, OutlineStyle, OutlineWidth:
updateCSSProperty(htmlID, Outline, GetOutline(view, "").cssString(), session) updateCSSProperty(htmlID, Outline, GetOutline(view).cssString(session), session)
return return
case Shadow: case Shadow:
@ -465,20 +461,20 @@ func viewPropertyChanged(view *viewData, tag string) {
RadiusTopRight, RadiusTopRightX, RadiusTopRightY, RadiusTopRight, RadiusTopRightX, RadiusTopRightY,
RadiusBottomLeft, RadiusBottomLeftX, RadiusBottomLeftY, RadiusBottomLeft, RadiusBottomLeftX, RadiusBottomLeftY,
RadiusBottomRight, RadiusBottomRightX, RadiusBottomRightY: RadiusBottomRight, RadiusBottomRightX, RadiusBottomRightY:
radius := GetRadius(view, "") radius := GetRadius(view)
updateCSSProperty(htmlID, "border-radius", radius.cssString(), session) updateCSSProperty(htmlID, "border-radius", radius.cssString(session), session)
return return
case Margin, MarginTop, MarginRight, MarginBottom, MarginLeft, case Margin, MarginTop, MarginRight, MarginBottom, MarginLeft,
"top-margin", "right-margin", "bottom-margin", "left-margin": "top-margin", "right-margin", "bottom-margin", "left-margin":
margin := GetMargin(view, "") margin := GetMargin(view)
updateCSSProperty(htmlID, Margin, margin.cssString(), session) updateCSSProperty(htmlID, Margin, margin.cssString(session), session)
return return
case Padding, PaddingTop, PaddingRight, PaddingBottom, PaddingLeft, case Padding, PaddingTop, PaddingRight, PaddingBottom, PaddingLeft,
"top-padding", "right-padding", "bottom-padding", "left-padding": "top-padding", "right-padding", "bottom-padding", "left-padding":
padding := GetPadding(view, "") padding := GetPadding(view)
updateCSSProperty(htmlID, Padding, padding.cssString(), session) updateCSSProperty(htmlID, Padding, padding.cssString(session), session)
return return
case AvoidBreak: case AvoidBreak:
@ -593,9 +589,9 @@ func viewPropertyChanged(view *viewData, tag string) {
} }
return return
case ZIndex: case ZIndex, TabSize:
if i, ok := intProperty(view, ZIndex, session, 0); ok { if i, ok := intProperty(view, tag, session, 0); ok {
updateCSSProperty(htmlID, ZIndex, strconv.Itoa(i), session) updateCSSProperty(htmlID, tag, strconv.Itoa(i), session)
} }
return return
@ -630,7 +626,7 @@ func viewPropertyChanged(view *viewData, tag string) {
if cssTag, ok := sizeProperties[tag]; ok { if cssTag, ok := sizeProperties[tag]; ok {
size, _ := sizeProperty(view, tag, session) size, _ := sizeProperty(view, tag, session)
updateCSSProperty(htmlID, cssTag, size.cssString(""), session) updateCSSProperty(htmlID, cssTag, size.cssString("", session), session)
return return
} }
@ -639,6 +635,7 @@ func viewPropertyChanged(view *viewData, tag string) {
TextColor: "color", TextColor: "color",
TextLineColor: "text-decoration-color", TextLineColor: "text-decoration-color",
CaretColor: CaretColor, CaretColor: CaretColor,
AccentColor: AccentColor,
} }
if cssTag, ok := colorTags[tag]; ok { if cssTag, ok := colorTags[tag]; ok {
if color, ok := colorProperty(view, tag, session); 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} { for _, floatTag := range []string{Opacity, ScaleX, ScaleY, ScaleZ, RotateX, RotateY, RotateZ} {
if tag == floatTag { if tag == floatTag {
if f, ok := floatProperty(view, floatTag, session, 0); ok { if f, ok := floatTextProperty(view, floatTag, session, 0); ok {
updateCSSProperty(htmlID, floatTag, strconv.FormatFloat(f, 'g', -1, 64), session) updateCSSProperty(htmlID, floatTag, f, session)
} }
return return
} }
} }
} }
func (view *viewData) Get(tag string) interface{} { func (view *viewData) Get(tag string) any {
return view.get(strings.ToLower(tag)) return view.get(strings.ToLower(tag))
} }
func (view *viewData) get(tag string) interface{} { func (view *viewData) get(tag string) any {
if tag == ID { if tag == ID {
if view.viewID != "" { if view.viewID != "" {
return view.viewID return view.viewID
@ -681,7 +678,7 @@ func (view *viewData) get(tag string) interface{} {
} }
func (view *viewData) htmlTag() string { func (view *viewData) htmlTag() string {
if semantics := GetSemantics(view, ""); semantics > DefaultSemantics { if semantics := GetSemantics(view); semantics > DefaultSemantics {
values := enumProperties[Semantics].cssValues values := enumProperties[Semantics].cssValues
if semantics < len(values) { if semantics < len(values) {
return values[semantics] return values[semantics]
@ -710,7 +707,7 @@ func (view *viewData) addToCSSStyle(addCSS map[string]string) {
func (view *viewData) cssStyle(self View, builder cssBuilder) { func (view *viewData) cssStyle(self View, builder cssBuilder) {
view.viewStyle.cssViewStyle(builder, view.session) view.viewStyle.cssViewStyle(builder, view.session)
switch GetVisibility(view, "") { switch GetVisibility(view) {
case Invisible: case Invisible:
builder.add(`visibility`, `hidden`) 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) { func (view *viewData) htmlDisabledProperties(self View, buffer *strings.Builder) {
if IsDisabled(self, "") { if IsDisabled(self) {
buffer.WriteString(` data-disabled="1"`) buffer.WriteString(` data-disabled="1"`)
} else { } else {
buffer.WriteString(` data-disabled="0"`) buffer.WriteString(` data-disabled="0"`)
@ -749,7 +746,7 @@ func viewHTML(view View, buffer *strings.Builder) {
buffer.WriteString(view.htmlID()) buffer.WriteString(view.htmlID())
buffer.WriteRune('"') buffer.WriteRune('"')
disabled := IsDisabled(view, "") disabled := IsDisabled(view)
if cls := view.htmlClass(disabled); cls != "" { if cls := view.htmlClass(disabled); cls != "" {
buffer.WriteString(` class="`) buffer.WriteString(` class="`)
@ -826,7 +823,7 @@ func (view *viewData) handleCommand(self View, command string, data DataObject)
switch command { switch command {
case KeyDownEvent, KeyUpEvent: case KeyDownEvent, KeyUpEvent:
if !IsDisabled(self, "") { if !IsDisabled(self) {
handleKeyEvents(self, command, data) handleKeyEvents(self, command, data)
} }
@ -841,13 +838,13 @@ func (view *viewData) handleCommand(self View, command string, data DataObject)
case FocusEvent: case FocusEvent:
view.hasFocus = true view.hasFocus = true
for _, listener := range getFocusListeners(view, "", command) { for _, listener := range getFocusListeners(view, nil, command) {
listener(self) listener(self)
} }
case LostFocusEvent: case LostFocusEvent:
view.hasFocus = false view.hasFocus = false
for _, listener := range getFocusListeners(view, "", command) { for _, listener := range getFocusListeners(view, nil, command) {
listener(self) listener(self)
} }

View File

@ -27,7 +27,7 @@ type circleClip struct {
} }
type polygonClip struct { type polygonClip struct {
points []interface{} points []any
} }
// InsetClip creates a rectangle View clipping area. // 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. // PolygonClip creates a polygon View clipping area.
// The elements of the function argument can be or text constants, // The elements of the function argument can be or text constants,
// or the text representation of SizeUnit, or elements of SizeUnit type. // 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 := new(polygonClip)
clip.points = []interface{}{} clip.points = []any{}
if clip.Set(Points, points) { if clip.Set(Points, points) {
return clip return clip
} }
@ -85,14 +85,14 @@ func PolygonClip(points []interface{}) ClipShape {
// PolygonPointsClip creates a polygon View clipping area. // PolygonPointsClip creates a polygon View clipping area.
func PolygonPointsClip(points []SizeUnit) ClipShape { func PolygonPointsClip(points []SizeUnit) ClipShape {
clip := new(polygonClip) clip := new(polygonClip)
clip.points = []interface{}{} clip.points = []any{}
if clip.Set(Points, points) { if clip.Set(Points, points) {
return clip return clip
} }
return nil return nil
} }
func (clip *insetClip) Set(tag string, value interface{}) bool { func (clip *insetClip) Set(tag string, value any) bool {
switch strings.ToLower(tag) { switch strings.ToLower(tag) {
case Top, Right, Bottom, Left: case Top, Right, Bottom, Left:
if value == nil { if value == nil {
@ -146,13 +146,13 @@ func (clip *insetClip) cssStyle(session Session) string {
for _, tag := range []string{Top, Right, Bottom, Left} { for _, tag := range []string{Top, Right, Bottom, Left} {
value, _ := sizeProperty(clip, tag, session) value, _ := sizeProperty(clip, tag, session)
buffer.WriteString(leadText) buffer.WriteString(leadText)
buffer.WriteString(value.cssString("0px")) buffer.WriteString(value.cssString("0px", session))
leadText = " " leadText = " "
} }
if radius := getRadiusProperty(clip); radius != nil { if radius := getRadiusProperty(clip); radius != nil {
buffer.WriteString(" round ") buffer.WriteString(" round ")
buffer.WriteString(radius.BoxRadius(session).cssString()) buffer.WriteString(radius.BoxRadius(session).cssString(session))
} }
buffer.WriteRune(')') buffer.WriteRune(')')
@ -168,7 +168,7 @@ func (clip *insetClip) valid(session Session) bool {
return false return false
} }
func (clip *circleClip) Set(tag string, value interface{}) bool { func (clip *circleClip) Set(tag string, value any) bool {
if value == nil { if value == nil {
clip.Remove(tag) clip.Remove(tag)
} }
@ -211,15 +211,15 @@ func (clip *circleClip) cssStyle(session Session) string {
buffer.WriteString("circle(") buffer.WriteString("circle(")
r, _ := sizeProperty(clip, Radius, session) r, _ := sizeProperty(clip, Radius, session)
buffer.WriteString(r.cssString("50%")) buffer.WriteString(r.cssString("50%", session))
buffer.WriteString(" at ") buffer.WriteString(" at ")
x, _ := sizeProperty(clip, X, session) x, _ := sizeProperty(clip, X, session)
buffer.WriteString(x.cssString("50%")) buffer.WriteString(x.cssString("50%", session))
buffer.WriteRune(' ') buffer.WriteRune(' ')
y, _ := sizeProperty(clip, Y, session) y, _ := sizeProperty(clip, Y, session)
buffer.WriteString(y.cssString("50%")) buffer.WriteString(y.cssString("50%", session))
buffer.WriteRune(')') buffer.WriteRune(')')
return buffer.String() return buffer.String()
@ -232,7 +232,7 @@ func (clip *circleClip) valid(session Session) bool {
return true return true
} }
func (clip *ellipseClip) Set(tag string, value interface{}) bool { func (clip *ellipseClip) Set(tag string, value any) bool {
if value == nil { if value == nil {
clip.Remove(tag) clip.Remove(tag)
} }
@ -280,17 +280,17 @@ func (clip *ellipseClip) cssStyle(session Session) string {
rx, _ := sizeProperty(clip, RadiusX, session) rx, _ := sizeProperty(clip, RadiusX, session)
ry, _ := sizeProperty(clip, RadiusX, session) ry, _ := sizeProperty(clip, RadiusX, session)
buffer.WriteString("ellipse(") buffer.WriteString("ellipse(")
buffer.WriteString(rx.cssString("50%")) buffer.WriteString(rx.cssString("50%", session))
buffer.WriteRune(' ') buffer.WriteRune(' ')
buffer.WriteString(ry.cssString("50%")) buffer.WriteString(ry.cssString("50%", session))
buffer.WriteString(" at ") buffer.WriteString(" at ")
x, _ := sizeProperty(clip, X, session) x, _ := sizeProperty(clip, X, session)
buffer.WriteString(x.cssString("50%")) buffer.WriteString(x.cssString("50%", session))
buffer.WriteRune(' ') buffer.WriteRune(' ')
y, _ := sizeProperty(clip, Y, session) y, _ := sizeProperty(clip, Y, session)
buffer.WriteString(y.cssString("50%")) buffer.WriteString(y.cssString("50%", session))
buffer.WriteRune(')') buffer.WriteRune(')')
return buffer.String() return buffer.String()
@ -302,23 +302,23 @@ func (clip *ellipseClip) valid(session Session) bool {
return rx.Value != 0 && ry.Value != 0 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) { if Points == strings.ToLower(tag) {
return clip.points return clip.points
} }
return nil return nil
} }
func (clip *polygonClip) getRaw(tag string) interface{} { func (clip *polygonClip) getRaw(tag string) any {
return clip.Get(tag) 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) { if Points == strings.ToLower(tag) {
switch value := value.(type) { switch value := value.(type) {
case []interface{}: case []any:
result := true result := true
clip.points = make([]interface{}, len(value)) clip.points = make([]any, len(value))
for i, val := range value { for i, val := range value {
switch val := val.(type) { switch val := val.(type) {
case string: case string:
@ -343,7 +343,7 @@ func (clip *polygonClip) Set(tag string, value interface{}) bool {
return result return result
case []SizeUnit: case []SizeUnit:
clip.points = make([]interface{}, len(value)) clip.points = make([]any, len(value))
for i, point := range value { for i, point := range value {
clip.points[i] = point clip.points[i] = point
} }
@ -352,7 +352,7 @@ func (clip *polygonClip) Set(tag string, value interface{}) bool {
case string: case string:
result := true result := true
values := strings.Split(value, ",") values := strings.Split(value, ",")
clip.points = make([]interface{}, len(values)) clip.points = make([]any, len(values))
for i, val := range values { for i, val := range values {
val = strings.Trim(val, " \t\n\r") val = strings.Trim(val, " \t\n\r")
if isConstantName(val) { if isConstantName(val) {
@ -370,18 +370,18 @@ func (clip *polygonClip) Set(tag string, value interface{}) bool {
return false return false
} }
func (clip *polygonClip) setRaw(tag string, value interface{}) { func (clip *polygonClip) setRaw(tag string, value any) {
clip.Set(tag, value) clip.Set(tag, value)
} }
func (clip *polygonClip) Remove(tag string) { func (clip *polygonClip) Remove(tag string) {
if Points == strings.ToLower(tag) { if Points == strings.ToLower(tag) {
clip.points = []interface{}{} clip.points = []any{}
} }
} }
func (clip *polygonClip) Clear() { func (clip *polygonClip) Clear() {
clip.points = []interface{}{} clip.points = []any{}
} }
func (clip *polygonClip) AllTags() []string { func (clip *polygonClip) AllTags() []string {
@ -422,18 +422,18 @@ func (clip *polygonClip) cssStyle(session Session) string {
buffer := allocStringBuilder() buffer := allocStringBuilder()
defer freeStringBuilder(buffer) defer freeStringBuilder(buffer)
writePoint := func(value interface{}) { writePoint := func(value any) {
switch value := value.(type) { switch value := value.(type) {
case string: case string:
if val, ok := session.resolveConstants(value); ok { if val, ok := session.resolveConstants(value); ok {
if size, ok := StringToSizeUnit(val); ok { if size, ok := StringToSizeUnit(val); ok {
buffer.WriteString(size.cssString("0px")) buffer.WriteString(size.cssString("0px", session))
return return
} }
} }
case SizeUnit: case SizeUnit:
buffer.WriteString(value.cssString("0px")) buffer.WriteString(value.cssString("0px", session))
return return
} }
@ -501,7 +501,7 @@ func parseClipShape(obj DataObject) ClipShape {
return nil return nil
} }
func (style *viewStyle) setClipShape(tag string, value interface{}) bool { func (style *viewStyle) setClipShape(tag string, value any) bool {
switch value := value.(type) { switch value := value.(type) {
case ClipShape: case ClipShape:
style.properties[tag] = value style.properties[tag] = value
@ -558,10 +558,10 @@ func getClipShape(prop Properties, tag string, session Session) ClipShape {
} }
// GetClip returns a View clipping area. // GetClip returns a View clipping area.
// If the second argument (subviewID) is "" then a top position of the first argument (view) is returned // 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 { func GetClip(view View, subviewID ...string) ClipShape {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
return getClipShape(view, Clip, view.Session()) 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. // 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 // 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 { func GetShapeOutside(view View, subviewID ...string) ClipShape {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
return getClipShape(view, ShapeOutside, view.Session()) return getClipShape(view, ShapeOutside, view.Session())

View File

@ -130,7 +130,7 @@ func newViewFilter(obj DataObject) ViewFilter {
return nil return nil
} }
func (filter *viewFilter) Set(tag string, value interface{}) bool { func (filter *viewFilter) Set(tag string, value any) bool {
if value == nil { if value == nil {
filter.Remove(tag) filter.Remove(tag)
return true return true
@ -180,20 +180,22 @@ func (filter *viewFilter) cssStyle(session Session) string {
buffer := allocStringBuilder() buffer := allocStringBuilder()
defer freeStringBuilder(buffer) defer freeStringBuilder(buffer)
if value, ok := floatProperty(filter, Blur, session, 0); ok { if value, ok := floatTextProperty(filter, Blur, session, 0); ok {
size := SizeUnit{Type: SizeInPixel, Value: value}
buffer.WriteString(Blur) buffer.WriteString(Blur)
buffer.WriteRune('(') buffer.WriteRune('(')
buffer.WriteString(size.cssString("0px")) buffer.WriteString(value)
buffer.WriteRune(')') buffer.WriteString("px)")
} }
for _, tag := range []string{Brightness, Contrast, Saturate, Grayscale, Invert, Opacity, Sepia} { 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 { if buffer.Len() > 0 {
buffer.WriteRune(' ') 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() 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) { switch value := value.(type) {
case ViewFilter: case ViewFilter:
style.properties[tag] = value 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. // 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 // 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 { func GetFilter(view View, subviewID ...string) ViewFilter {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
if value := view.getRaw(Filter); value != nil { if value := view.getRaw(Filter); value != nil {
if filter, ok := value.(ViewFilter); ok { if filter, ok := value.(ViewFilter); ok {
return filter return filter
} }
} }
if value := valueFromStyle(view, Filter); value != nil {
if filter, ok := value.(ViewFilter); ok {
return filter
}
}
} }
return nil return nil
} }
// GetBackdropFilter returns the area behind a View graphical effects like blur or color shift. // 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 // 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 { func GetBackdropFilter(view View, subviewID ...string) ViewFilter {
if subviewID != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID) view = ViewByID(view, subviewID[0])
} }
if view != nil { if view != nil {
if value := view.getRaw(BackdropFilter); value != nil { if value := view.getRaw(BackdropFilter); value != nil {
if filter, ok := value.(ViewFilter); ok { if filter, ok := value.(ViewFilter); ok {
return filter return filter
} }
} }
if value := valueFromStyle(view, BackdropFilter); value != nil {
if filter, ok := value.(ViewFilter); ok {
return filter
}
}
} }
return nil return nil

View File

@ -10,6 +10,16 @@ import (
// ViewStyle interface of the style of view // ViewStyle interface of the style of view
type ViewStyle interface { type ViewStyle interface {
Properties 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) cssViewStyle(buffer cssBuilder, session Session)
} }
@ -163,11 +173,11 @@ func (style *viewStyle) backgroundCSS(session Session) string {
func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) { func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) {
if margin, ok := boundsProperty(style, Margin, session); ok { 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 { if padding, ok := boundsProperty(style, Padding, session); ok {
padding.cssValue(Padding, builder) padding.cssValue(Padding, builder, session)
} }
if border := getBorder(style, Border); border != nil { if border := getBorder(style, Border); border != nil {
@ -177,10 +187,10 @@ func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) {
} }
radius := getRadius(style, session) radius := getRadius(style, session)
radius.cssValue(builder) radius.cssValue(builder, session)
if outline := getOutline(style); outline != nil { 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 { 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{ for _, tag := range []string{
Width, Height, MinWidth, MinHeight, MaxWidth, MaxHeight, Left, Right, Top, Bottom, Width, Height, MinWidth, MinHeight, MaxWidth, MaxHeight, Left, Right, Top, Bottom,
TextSize, TextIndent, LetterSpacing, WordSpacing, LineHeight, TextLineThickness, 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 { if size, ok := sizeProperty(style, tag, session); ok && size.Type != Auto {
cssTag, ok := sizeProperties[tag] cssTag, ok := sizeProperties[tag]
if !ok { if !ok {
cssTag = tag 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"}, {TextColor, "color"},
{TextLineColor, "text-decoration-color"}, {TextLineColor, "text-decoration-color"},
{CaretColor, CaretColor}, {CaretColor, CaretColor},
{AccentColor, AccentColor},
} }
for _, p := range colorProperties { for _, p := range colorProperties {
if color, ok := colorProperty(style, p.property, session); ok && color != 0 { 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 writingMode := 0
for _, tag := range []string{ for _, tag := range []string{
TextAlign, TextTransform, TextWeight, TextLineStyle, WritingMode, TextDirection, Overflow, TextAlign, TextTransform, TextWeight, TextLineStyle, WritingMode, TextDirection,
VerticalTextOrientation, CellVerticalAlign, CellHorizontalAlign, GridAutoFlow, Cursor, VerticalTextOrientation, CellVerticalAlign, CellHorizontalAlign, GridAutoFlow, Cursor,
WhiteSpace, WordBreak, TextOverflow, Float, TableVerticalAlign, Resize} { 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 != "" { if text := style.cssTextDecoration(session); text != "" {
builder.add("text-decoration", 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 { if r, ok := rangeProperty(style, Row, session); ok {
builder.add("grid-row-start", strconv.Itoa(r.First+1)) builder.add("grid-row", fmt.Sprintf("%d / %d", r.First+1, r.Last+2))
builder.add("grid-row-end", strconv.Itoa(r.Last+2))
} }
if r, ok := rangeProperty(style, Column, session); ok { if r, ok := rangeProperty(style, Column, session); ok {
builder.add("grid-column-start", strconv.Itoa(r.First+1)) builder.add("grid-column", fmt.Sprintf("%d / %d", r.First+1, r.Last+2))
builder.add("grid-column-end", strconv.Itoa(r.Last+2))
} }
if text := style.gridCellSizesCSS(CellWidth, session); text != "" { if text := style.gridCellSizesCSS(CellWidth, session); text != "" {
builder.add(`grid-template-columns`, 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 { if value != nil {
switch value := value.(type) { switch value := value.(type) {
case int: case int:
@ -471,11 +484,11 @@ func valueToOrientation(value interface{}, session Session) (int, bool) {
return 0, false return 0, false
} }
func (style *viewStyle) Get(tag string) interface{} { func (style *viewStyle) Get(tag string) any {
return style.get(strings.ToLower(tag)) return style.get(strings.ToLower(tag))
} }
func (style *viewStyle) get(tag string) interface{} { func (style *viewStyle) get(tag string) any {
switch tag { switch tag {
case Border, CellBorder: case Border, CellBorder:
return getBorder(&style.propertyList, tag) return getBorder(&style.propertyList, tag)
@ -539,7 +552,7 @@ func (style *viewStyle) AllTags() []string {
return result return result
} }
func supportedPropertyValue(value interface{}) bool { func supportedPropertyValue(value any) bool {
switch value.(type) { switch value.(type) {
case string: case string:
case []string: case []string:
@ -551,7 +564,7 @@ func supportedPropertyValue(value interface{}) bool {
case fmt.Stringer: case fmt.Stringer:
case []ViewShadow: case []ViewShadow:
case []View: case []View:
case []interface{}: case []any:
case map[string]Animation: case map[string]Animation:
default: default:
return false return false
@ -559,7 +572,7 @@ func supportedPropertyValue(value interface{}) bool {
return true 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) { writeString := func(text string) {
simple := (tag != Text && tag != Title && tag != Summary) simple := (tag != Text && tag != Title && tag != Summary)
@ -569,7 +582,8 @@ func writePropertyValue(buffer *strings.Builder, tag string, value interface{},
} else { } else {
for _, ch := range text { for _, ch := range text {
if (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || 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 { } else {
simple = false simple = false
break break
@ -654,7 +668,7 @@ func writePropertyValue(buffer *strings.Builder, tag string, value interface{},
value.writeString(buffer, indent+"\t") value.writeString(buffer, indent+"\t")
case fmt.Stringer: case fmt.Stringer:
buffer.WriteString(value.String()) writeString(value.String())
case []ViewShadow: case []ViewShadow:
switch len(value) { switch len(value) {
@ -697,7 +711,7 @@ func writePropertyValue(buffer *strings.Builder, tag string, value interface{},
buffer.WriteRune(']') buffer.WriteRune(']')
} }
case []interface{}: case []any:
switch count := len(value); count { switch count := len(value); count {
case 0: case 0:
buffer.WriteString("[]") buffer.WriteString("[]")
@ -741,7 +755,7 @@ func writePropertyValue(buffer *strings.Builder, tag string, value interface{},
if animation := value[tag]; animation != nil { if animation := value[tag]; animation != nil {
buffer.WriteString(indent2) buffer.WriteString(indent2)
animation.writeTransitionString(tag, buffer) animation.writeTransitionString(tag, buffer)
buffer.WriteString("\n") buffer.WriteString(",\n")
} }
} }
buffer.WriteString(indent) buffer.WriteString(indent)
@ -755,7 +769,7 @@ func writeViewStyle(name string, view ViewStyle, buffer *strings.Builder, indent
buffer.WriteString(" {\n") buffer.WriteString(" {\n")
indent += "\t" indent += "\t"
writeProperty := func(tag string, value interface{}) { writeProperty := func(tag string, value any) {
if supportedPropertyValue(value) { if supportedPropertyValue(value) {
buffer.WriteString(indent) buffer.WriteString(indent)
buffer.WriteString(tag) 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, Opacity, ZIndex, Width, Height, MinWidth, MinHeight, MaxWidth, MaxHeight,
Margin, Padding, BackgroundClip, BackgroundColor, Background, Border, Radius, Outline, Shadow, Margin, Padding, BackgroundClip, BackgroundColor, Background, Border, Radius, Outline, Shadow,
Orientation, ListWrap, VerticalAlign, HorizontalAlign, CellWidth, CellHeight, Orientation, ListWrap, VerticalAlign, HorizontalAlign, CellWidth, CellHeight,
CellVerticalAlign, CellHorizontalAlign, GridRowGap, GridColumnGap, CellVerticalAlign, CellHorizontalAlign, ListRowGap, ListColumnGap, GridRowGap, GridColumnGap,
ColumnCount, ColumnWidth, ColumnSeparator, ColumnGap, AvoidBreak, ColumnCount, ColumnWidth, ColumnSeparator, ColumnGap, AvoidBreak,
Current, Expanded, Side, ResizeBorderWidth, EditViewType, MaxLength, Hint, Text, EditWrap, Current, Expanded, Side, ResizeBorderWidth, EditViewType, MaxLength, Hint, Text, EditWrap,
TextOverflow, FontName, TextSize, TextColor, TextWeight, Italic, SmallCaps, TextOverflow, FontName, TextSize, TextColor, TextWeight, Italic, SmallCaps,

View File

@ -4,7 +4,7 @@ import (
"strings" "strings"
) )
func (style *viewStyle) setRange(tag string, value interface{}) bool { func (style *viewStyle) setRange(tag string, value any) bool {
switch value := value.(type) { switch value := value.(type) {
case string: case string:
if strings.Contains(value, "@") { if strings.Contains(value, "@") {
@ -31,7 +31,7 @@ func (style *viewStyle) setRange(tag string, value interface{}) bool {
return true return true
} }
func (style *viewStyle) setBackground(value interface{}) bool { func (style *viewStyle) setBackground(value any) bool {
switch value := value.(type) { switch value := value.(type) {
case BackgroundElement: case BackgroundElement:
style.properties[Background] = []BackgroundElement{value} 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) 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 { if value == nil {
style.remove(tag) style.remove(tag)
return true return true

View File

@ -1,9 +1,5 @@
package rui package rui
import (
"strconv"
)
const ( const (
// Perspective is the name of the SizeUnit property that determines the distance between the z = 0 plane // 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 // 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 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 { func (style *viewStyle) transform(session Session) string {
buffer := allocStringBuilder() buffer := allocStringBuilder()
@ -133,45 +115,52 @@ func (style *viewStyle) transform(session Session) string {
} }
x, y, z := getTranslate(style, session) 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 getTransform3D(style, session) {
if x.Type != Auto || y.Type != Auto || z.Type != Auto { if x.Type != Auto || y.Type != Auto || z.Type != Auto {
if buffer.Len() > 0 { if buffer.Len() > 0 {
buffer.WriteRune(' ') buffer.WriteRune(' ')
} }
buffer.WriteString(`translate3d(`) buffer.WriteString(`translate3d(`)
buffer.WriteString(x.cssString("0")) buffer.WriteString(x.cssString("0", session))
buffer.WriteRune(',') buffer.WriteRune(',')
buffer.WriteString(y.cssString("0")) buffer.WriteString(y.cssString("0", session))
buffer.WriteRune(',') buffer.WriteRune(',')
buffer.WriteString(z.cssString("0")) buffer.WriteString(z.cssString("0", session))
buffer.WriteRune(')') buffer.WriteRune(')')
} }
if scaleOK { scaleZ, okScaleZ := floatTextProperty(style, ScaleZ, session, 1)
if okScaleX || okScaleY || okScaleZ {
if buffer.Len() > 0 { if buffer.Len() > 0 {
buffer.WriteRune(' ') buffer.WriteRune(' ')
} }
buffer.WriteString(`scale3d(`) buffer.WriteString(`scale3d(`)
buffer.WriteString(strconv.FormatFloat(scaleX, 'g', -1, 64)) buffer.WriteString(scaleX)
buffer.WriteRune(',') buffer.WriteRune(',')
buffer.WriteString(strconv.FormatFloat(scaleY, 'g', -1, 64)) buffer.WriteString(scaleY)
buffer.WriteRune(',') buffer.WriteRune(',')
buffer.WriteString(strconv.FormatFloat(scaleZ, 'g', -1, 64)) buffer.WriteString(scaleZ)
buffer.WriteRune(')') buffer.WriteRune(')')
} }
if angle, ok := angleProperty(style, Rotate, session); ok { 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 { if buffer.Len() > 0 {
buffer.WriteRune(' ') buffer.WriteRune(' ')
} }
buffer.WriteString(`rotate3d(`) buffer.WriteString(`rotate3d(`)
buffer.WriteString(strconv.FormatFloat(rotateX, 'g', -1, 64)) buffer.WriteString(rotateX)
buffer.WriteRune(',') buffer.WriteRune(',')
buffer.WriteString(strconv.FormatFloat(rotateY, 'g', -1, 64)) buffer.WriteString(rotateY)
buffer.WriteRune(',') buffer.WriteRune(',')
buffer.WriteString(strconv.FormatFloat(rotateZ, 'g', -1, 64)) buffer.WriteString(rotateZ)
buffer.WriteRune(',') buffer.WriteRune(',')
buffer.WriteString(angle.cssString()) buffer.WriteString(angle.cssString())
buffer.WriteRune(')') buffer.WriteRune(')')
@ -183,20 +172,20 @@ func (style *viewStyle) transform(session Session) string {
buffer.WriteRune(' ') buffer.WriteRune(' ')
} }
buffer.WriteString(`translate(`) buffer.WriteString(`translate(`)
buffer.WriteString(x.cssString("0")) buffer.WriteString(x.cssString("0", session))
buffer.WriteRune(',') buffer.WriteRune(',')
buffer.WriteString(y.cssString("0")) buffer.WriteString(y.cssString("0", session))
buffer.WriteRune(')') buffer.WriteRune(')')
} }
if scaleOK { if okScaleX || okScaleY {
if buffer.Len() > 0 { if buffer.Len() > 0 {
buffer.WriteRune(' ') buffer.WriteRune(' ')
} }
buffer.WriteString(`scale(`) buffer.WriteString(`scale(`)
buffer.WriteString(strconv.FormatFloat(scaleX, 'g', -1, 64)) buffer.WriteString(scaleX)
buffer.WriteRune(',') buffer.WriteRune(',')
buffer.WriteString(strconv.FormatFloat(scaleY, 'g', -1, 64)) buffer.WriteString(scaleY)
buffer.WriteRune(')') buffer.WriteRune(')')
} }
@ -216,12 +205,12 @@ func (style *viewStyle) transform(session Session) string {
func (style *viewStyle) writeViewTransformCSS(builder cssBuilder, session Session) { func (style *viewStyle) writeViewTransformCSS(builder cssBuilder, session Session) {
if getTransform3D(style, session) { if getTransform3D(style, session) {
if perspective, ok := sizeProperty(style, Perspective, session); ok && perspective.Type != Auto && perspective.Value != 0 { 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) x, y := getPerspectiveOrigin(style, session)
if x.Type != Auto || y.Type != Auto { 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 { 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) x, y, z := getOrigin(style, session)
if x.Type != Auto || y.Type != Auto || z.Type != Auto { 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 { } else {
x, y, _ := getOrigin(style, session) x, y, _ := getOrigin(style, session)
if x.Type != Auto || y.Type != Auto { 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: case PerspectiveOriginX, PerspectiveOriginY:
if getTransform3D(view, session) { if getTransform3D(view, session) {
x, y := GetPerspectiveOrigin(view, "") x, y := GetPerspectiveOrigin(view)
value := "" value := ""
if x.Type != Auto || y.Type != Auto { 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) updateCSSProperty(htmlID, "perspective-origin", value, session)
} }
case BackfaceVisible: case BackfaceVisible:
if getTransform3D(view, session) { if getTransform3D(view, session) {
if GetBackfaceVisible(view, "") { if GetBackfaceVisible(view) {
updateCSSProperty(htmlID, BackfaceVisible, "visible", session) updateCSSProperty(htmlID, BackfaceVisible, "visible", session)
} else { } else {
updateCSSProperty(htmlID, BackfaceVisible, "hidden", session) updateCSSProperty(htmlID, BackfaceVisible, "hidden", session)
@ -278,11 +267,11 @@ func (view *viewData) updateTransformProperty(tag string) bool {
value := "" value := ""
if getTransform3D(view, session) { if getTransform3D(view, session) {
if x.Type != Auto || y.Type != Auto || z.Type != Auto { 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 { } else {
if x.Type != Auto || y.Type != Auto { 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) updateCSSProperty(htmlID, "transform-origin", value, session)

File diff suppressed because it is too large Load Diff

View File

@ -25,8 +25,8 @@ type viewsContainerData struct {
} }
// Init initialize fields of ViewsContainer by default values // Init initialize fields of ViewsContainer by default values
func (container *viewsContainerData) Init(session Session) { func (container *viewsContainerData) init(session Session) {
container.viewData.Init(session) container.viewData.init(session)
container.tag = "ViewsContainer" container.tag = "ViewsContainer"
container.views = []View{} container.views = []View{}
} }
@ -47,8 +47,7 @@ func (container *viewsContainerData) setParentID(parentID string) {
func (container *viewsContainerData) Views() []View { func (container *viewsContainerData) Views() []View {
if container.views == nil { if container.views == nil {
container.views = []View{} container.views = []View{}
} } else if count := len(container.views); count > 0 {
if count := len(container.views); count > 0 {
views := make([]View, count) views := make([]View, count)
copy(views, container.views) copy(views, container.views)
return 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) 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 { if value == nil {
container.remove(tag) container.remove(tag)
return true return true
@ -187,9 +186,9 @@ func (container *viewsContainerData) set(tag string, value interface{}) bool {
// do nothing // do nothing
case Disabled: case Disabled:
oldDisabled := IsDisabled(container, "") oldDisabled := IsDisabled(container)
if container.viewData.Set(Disabled, value) { if container.viewData.Set(Disabled, value) {
disabled := IsDisabled(container, "") disabled := IsDisabled(container)
if oldDisabled != disabled { if oldDisabled != disabled {
if container.views != nil { if container.views != nil {
for _, view := range container.views { for _, view := range container.views {
@ -224,7 +223,7 @@ func (container *viewsContainerData) set(tag string, value interface{}) bool {
} }
container.views = views container.views = views
case []interface{}: case []any:
views := []View{} views := []View{}
for _, v := range value { for _, v := range value {
switch v := v.(type) { switch v := v.(type) {
@ -279,11 +278,11 @@ func (container *viewsContainerData) set(tag string, value interface{}) bool {
return true return true
} }
func (container *viewsContainerData) Get(tag string) interface{} { func (container *viewsContainerData) Get(tag string) any {
return container.get(strings.ToLower(tag)) return container.get(strings.ToLower(tag))
} }
func (container *viewsContainerData) get(tag string) interface{} { func (container *viewsContainerData) get(tag string) any {
switch tag { switch tag {
case Content: case Content:
return container.views return container.views