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
* 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
func NewAbsoluteLayout(session Session, params Params) AbsoluteLayout {
view := new(absoluteLayoutData)
view.Init(session)
view.init(session)
setInitParams(view, params)
return view
}
@ -24,8 +24,8 @@ func newAbsoluteLayout(session Session) View {
}
// Init initialize fields of ViewsContainer by default values
func (layout *absoluteLayoutData) Init(session Session) {
layout.viewsContainerData.Init(session)
func (layout *absoluteLayoutData) init(session Session) {
layout.viewsContainerData.init(session)
layout.tag = "AbsoluteLayout"
layout.systemClass = "ruiAbsoluteLayout"
}

View File

@ -55,16 +55,19 @@ const (
// The animation plays forwards each cycle. In other words, each time the animation cycles,
// the animation will reset to the beginning state and start over again. This is the default value.
NormalAnimation = 0
// ReverseAnimation is value of the "animation-direction" property.
// The animation plays backwards each cycle. In other words, each time the animation cycles,
// the animation will reset to the end state and start over again. Animation steps are performed
// backwards, and timing functions are also reversed.
// For example, an "ease-in" timing function becomes "ease-out".
ReverseAnimation = 1
// AlternateAnimation is value of the "animation-direction" property.
// The animation reverses direction each cycle, with the first iteration being played forwards.
// The count to determine if a cycle is even or odd starts at one.
AlternateAnimation = 2
// AlternateReverseAnimation is value of the "animation-direction" property.
// The animation reverses direction each cycle, with the first iteration being played backwards.
// The count to determine if a cycle is even or odd starts at one.
@ -107,11 +110,11 @@ type AnimatedProperty struct {
// Tag is the name of the property
Tag string
// From is the initial value of the property
From interface{}
From any
// To is the final value of the property
To interface{}
To any
// KeyFrames is intermediate property values
KeyFrames map[int]interface{}
KeyFrames map[int]any
}
type animationData struct {
@ -184,7 +187,7 @@ func (animation *animationData) normalizeTag(tag string) string {
return tag
}
func (animation *animationData) Set(tag string, value interface{}) bool {
func (animation *animationData) Set(tag string, value any) bool {
if value == nil {
animation.Remove(tag)
return true
@ -285,7 +288,7 @@ func (animation *animationData) Set(tag string, value interface{}) bool {
ErrorLogF(`key-frame "%d" is out of range`, n)
} else {
if result.KeyFrames == nil {
result.KeyFrames = map[int]interface{}{n: node.Text()}
result.KeyFrames = map[int]any{n: node.Text()}
} else {
result.KeyFrames[n] = node.Text()
}
@ -359,7 +362,7 @@ func (animation *animationData) Remove(tag string) {
delete(animation.properties, animation.normalizeTag(tag))
}
func (animation *animationData) Get(tag string) interface{} {
func (animation *animationData) Get(tag string) any {
return animation.getRaw(animation.normalizeTag(tag))
}
@ -397,7 +400,7 @@ func (animation *animationData) animationCSS(session Session) string {
buffer.WriteString(animation.keyFramesName)
if duration, _ := floatProperty(animation, Duration, session, 1); duration > 0 {
if duration, ok := floatProperty(animation, Duration, session, 1); ok && duration > 0 {
buffer.WriteString(fmt.Sprintf(" %gs ", duration))
} else {
buffer.WriteString(" 1s ")
@ -405,7 +408,7 @@ func (animation *animationData) animationCSS(session Session) string {
buffer.WriteString(animation.timingFunctionCSS(session))
if delay, _ := floatProperty(animation, Delay, session, 0); delay > 0 {
if delay, ok := floatProperty(animation, Delay, session, 0); ok && delay > 0 {
buffer.WriteString(fmt.Sprintf(" %gs", delay))
} else {
buffer.WriteString(" 0s")
@ -435,7 +438,7 @@ func (animation *animationData) animationCSS(session Session) string {
func (animation *animationData) transitionCSS(buffer *strings.Builder, session Session) {
if duration, _ := floatProperty(animation, Duration, session, 1); duration > 0 {
if duration, ok := floatProperty(animation, Duration, session, 1); ok && duration > 0 {
buffer.WriteString(fmt.Sprintf(" %gs ", duration))
} else {
buffer.WriteString(" 1s ")
@ -443,7 +446,7 @@ func (animation *animationData) transitionCSS(buffer *strings.Builder, session S
buffer.WriteString(animation.timingFunctionCSS(session))
if delay, _ := floatProperty(animation, Delay, session, 0); delay > 0 {
if delay, ok := floatProperty(animation, Delay, session, 0); ok && delay > 0 {
buffer.WriteString(fmt.Sprintf(" %gs", delay))
}
}
@ -481,6 +484,8 @@ func (animation *animationData) writeTransitionString(tag string, buffer *string
buffer.WriteRune('"')
buffer.WriteString(timingFunction)
buffer.WriteRune('"')
} else {
buffer.WriteString(timingFunction)
}
}
}
@ -490,14 +495,14 @@ func (animation *animationData) writeTransitionString(tag string, buffer *string
func (animation *animationData) timingFunctionCSS(session Session) string {
if timingFunction, ok := stringProperty(animation, TimingFunction, session); ok {
if timingFunction, ok = session.resolveConstants(timingFunction); ok && validateTimingFunction(timingFunction) {
if timingFunction, ok = session.resolveConstants(timingFunction); ok && isTimingFunctionValid(timingFunction) {
return timingFunction
}
}
return ("ease")
}
func validateTimingFunction(timingFunction string) bool {
func isTimingFunctionValid(timingFunction string) bool {
switch timingFunction {
case "", EaseTiming, EaseInTiming, EaseOutTiming, EaseInOutTiming, LinearTiming:
return true
@ -529,6 +534,14 @@ func validateTimingFunction(timingFunction string) bool {
return false
}
// IsTimingFunctionValid returns "true" if the "timingFunction" argument is the valid timing function.
func IsTimingFunctionValid(timingFunction string, session Session) bool {
if timingFunc, ok := session.resolveConstants(strings.Trim(timingFunction, " \t\n")); ok {
return isTimingFunctionValid(timingFunc)
}
return false
}
func (session *sessionData) registerAnimation(props []AnimatedProperty) string {
session.animationCounter++
@ -602,7 +615,7 @@ func (session *sessionData) registerAnimation(props []AnimatedProperty) string {
return name
}
func (view *viewData) SetAnimated(tag string, value interface{}, animation Animation) bool {
func (view *viewData) SetAnimated(tag string, value any, animation Animation) bool {
if animation == nil {
return view.Set(tag, value)
}
@ -659,11 +672,29 @@ func (style *viewStyle) transitionCSS(session Session) string {
buffer := allocStringBuilder()
defer freeStringBuilder(buffer)
convert := map[string]string{
CellHeight: "grid-template-rows",
CellWidth: "grid-template-columns",
Row: "grid-row",
Column: "grid-column",
Clip: "clip-path",
Shadow: "box-shadow",
ColumnSeparator: "column-rule",
FontName: "font",
TextSize: "font-size",
TextLineThickness: "text-decoration-thickness",
}
for tag, animation := range style.transitions {
if buffer.Len() > 0 {
buffer.WriteString(", ")
}
buffer.WriteString(tag)
if cssTag, ok := convert[tag]; ok {
buffer.WriteString(cssTag)
} else {
buffer.WriteString(tag)
}
animation.transitionCSS(buffer, session)
}
return buffer.String()
@ -673,18 +704,42 @@ func (view *viewData) updateTransitionCSS() {
updateCSSProperty(view.htmlID(), "transition", view.transitionCSS(view.Session()), view.Session())
}
func (view *viewData) getTransitions() Params {
result := Params{}
for tag, animation := range view.transitions {
func (style *viewStyle) Transition(tag string) Animation {
if style.transitions != nil {
if anim, ok := style.transitions[tag]; ok {
return anim
}
}
return nil
}
func (style *viewStyle) Transitions() map[string]Animation {
result := map[string]Animation{}
for tag, animation := range style.transitions {
result[tag] = animation
}
return result
}
func (style *viewStyle) SetTransition(tag string, animation Animation) {
if animation == nil {
delete(style.transitions, tag)
} else {
style.transitions[tag] = animation
}
}
func (view *viewData) SetTransition(tag string, animation Animation) {
view.viewStyle.SetTransition(tag, animation)
if view.created {
updateCSSProperty(view.htmlID(), "transition", view.transitionCSS(view.Session()), view.Session())
}
}
// SetAnimated sets the property with name "tag" of the "rootView" subview with "viewID" id by value. Result:
// true - success,
// false - error (incompatible type or invalid format of a string value, see AppLog).
func SetAnimated(rootView View, viewID, tag string, value interface{}, animation Animation) bool {
func SetAnimated(rootView View, viewID, tag string, value any, animation Animation) bool {
if view := ViewByID(rootView, viewID); view != nil {
return view.SetAnimated(tag, value, animation)
}
@ -692,58 +747,60 @@ func SetAnimated(rootView View, viewID, tag string, value interface{}, animation
}
// IsAnimationPaused returns "true" if an animation of the subview is paused, "false" otherwise.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func IsAnimationPaused(view View, subviewID string) bool {
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func IsAnimationPaused(view View, subviewID ...string) bool {
return boolStyledProperty(view, subviewID, AnimationPaused, false)
}
// GetTransitions returns the subview transitions. The result is always non-nil.
// If the second argument (subviewID) is not specified or it is "" then transitions of the first argument (view) is returned
func GetTransitions(view View, subviewID ...string) map[string]Animation {
if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0])
}
if view != nil {
return view.Transitions()
}
return map[string]Animation{}
}
// GetTransition returns the subview property transition. If there is no transition for the given property then nil is returned.
// If the second argument (subviewID) is not specified or it is "" then transitions of the first argument (view) is returned
func GetTransition(view View, subviewID, tag string) Animation {
if subviewID != "" {
view = ViewByID(view, subviewID)
}
if view != nil {
if result, ok := boolStyledProperty(view, AnimationPaused); ok {
return result
return view.Transition(tag)
}
return nil
}
// AddTransition adds the transition for the subview property.
// If the second argument (subviewID) is not specified or it is "" then the transition is added to the first argument (view)
func AddTransition(view View, subviewID, tag string, animation Animation) bool {
if tag != "" {
if subviewID != "" {
view = ViewByID(view, subviewID)
}
if view != nil {
view.SetTransition(tag, animation)
return true
}
}
return false
}
// GetTransition returns the subview transitions. The result is always non-nil.
// If the second argument (subviewID) is "" then transitions of the first argument (view) is returned
func GetTransition(view View, subviewID string) Params {
if subviewID != "" {
view = ViewByID(view, subviewID)
}
if view != nil {
return view.getTransitions()
}
return Params{}
}
// AddTransition adds the transition for the subview property.
// If the second argument (subviewID) is "" then the transition is added to the first argument (view)
func AddTransition(view View, subviewID, tag string, animation Animation) bool {
if tag == "" {
return false
}
if subviewID != "" {
view = ViewByID(view, subviewID)
}
if view == nil {
return false
}
transitions := view.getTransitions()
transitions[tag] = animation
return view.Set(Transition, transitions)
}
// GetAnimation returns the subview animations. The result is always non-nil.
// If the second argument (subviewID) is "" then transitions of the first argument (view) is returned
func GetAnimation(view View, subviewID string) []Animation {
if subviewID != "" {
view = ViewByID(view, subviewID)
// If the second argument (subviewID) is not specified or it is "" then transitions of the first argument (view) is returned
func GetAnimation(view View, subviewID ...string) []Animation {
if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0])
}
if view != nil {

View File

@ -51,131 +51,6 @@ const (
AnimationIterationEvent = "animation-iteration-event"
)
func valueToAnimationListeners(value interface{}) ([]func(View, string), bool) {
if value == nil {
return nil, true
}
switch value := value.(type) {
case func(View, string):
return []func(View, string){value}, true
case func(string):
fn := func(_ View, event string) {
value(event)
}
return []func(View, string){fn}, true
case func(View):
fn := func(view View, _ string) {
value(view)
}
return []func(View, string){fn}, true
case func():
fn := func(View, string) {
value()
}
return []func(View, string){fn}, true
case []func(View, string):
if len(value) == 0 {
return nil, true
}
for _, fn := range value {
if fn == nil {
return nil, false
}
}
return value, true
case []func(string):
count := len(value)
if count == 0 {
return nil, true
}
listeners := make([]func(View, string), count)
for i, v := range value {
if v == nil {
return nil, false
}
listeners[i] = func(_ View, event string) {
v(event)
}
}
return listeners, true
case []func(View):
count := len(value)
if count == 0 {
return nil, true
}
listeners := make([]func(View, string), count)
for i, v := range value {
if v == nil {
return nil, false
}
listeners[i] = func(view View, _ string) {
v(view)
}
}
return listeners, true
case []func():
count := len(value)
if count == 0 {
return nil, true
}
listeners := make([]func(View, string), count)
for i, v := range value {
if v == nil {
return nil, false
}
listeners[i] = func(View, string) {
v()
}
}
return listeners, true
case []interface{}:
count := len(value)
if count == 0 {
return nil, true
}
listeners := make([]func(View, string), count)
for i, v := range value {
if v == nil {
return nil, false
}
switch v := v.(type) {
case func(View, string):
listeners[i] = v
case func(string):
listeners[i] = func(_ View, event string) {
v(event)
}
case func(View):
listeners[i] = func(view View, _ string) {
v(view)
}
case func():
listeners[i] = func(View, string) {
v()
}
default:
return nil, false
}
}
return listeners, true
}
return nil, false
}
var transitionEvents = map[string]struct{ jsEvent, jsFunc string }{
TransitionRunEvent: {jsEvent: "ontransitionrun", jsFunc: "transitionRunEvent"},
TransitionStartEvent: {jsEvent: "ontransitionstart", jsFunc: "transitionStartEvent"},
@ -183,8 +58,8 @@ var transitionEvents = map[string]struct{ jsEvent, jsFunc string }{
TransitionCancelEvent: {jsEvent: "ontransitioncancel", jsFunc: "transitionCancelEvent"},
}
func (view *viewData) setTransitionListener(tag string, value interface{}) bool {
listeners, ok := valueToAnimationListeners(value)
func (view *viewData) setTransitionListener(tag string, value any) bool {
listeners, ok := valueToEventListeners[View, string](value)
if !ok {
notCompatibleType(tag, value)
return false
@ -212,20 +87,6 @@ func (view *viewData) removeTransitionListener(tag string) {
}
}
func getAnimationListeners(view View, subviewID string, tag string) []func(View, string) {
if subviewID != "" {
view = ViewByID(view, subviewID)
}
if view != nil {
if value := view.Get(tag); value != nil {
if result, ok := value.([]func(View, string)); ok {
return result
}
}
}
return []func(View, string){}
}
func transitionEventsHtml(view View, buffer *strings.Builder) {
for tag, js := range transitionEvents {
if value := view.getRaw(tag); value != nil {
@ -250,7 +111,7 @@ func (view *viewData) handleTransitionEvents(tag string, data DataObject) {
}
}
for _, listener := range getAnimationListeners(view, "", tag) {
for _, listener := range getEventListeners[View, string](view, nil, tag) {
listener(view, property)
}
}
@ -263,8 +124,8 @@ var animationEvents = map[string]struct{ jsEvent, jsFunc string }{
AnimationCancelEvent: {jsEvent: "onanimationcancel", jsFunc: "animationCancelEvent"},
}
func (view *viewData) setAnimationListener(tag string, value interface{}) bool {
listeners, ok := valueToAnimationListeners(value)
func (view *viewData) setAnimationListener(tag string, value any) bool {
listeners, ok := valueToEventListeners[View, string](value)
if !ok {
notCompatibleType(tag, value)
return false
@ -303,10 +164,10 @@ func animationEventsHtml(view View, buffer *strings.Builder) {
}
func (view *viewData) handleAnimationEvents(tag string, data DataObject) {
if listeners := getAnimationListeners(view, "", tag); len(listeners) > 0 {
if listeners := getEventListeners[View, string](view, nil, tag); len(listeners) > 0 {
id := ""
if name, ok := data.PropertyValue("name"); ok {
for _, animation := range GetAnimation(view, "") {
for _, animation := range GetAnimation(view) {
if name == animation.animationName() {
id, _ = stringProperty(animation, ID, view.Session())
}
@ -320,56 +181,56 @@ func (view *viewData) handleAnimationEvents(tag string, data DataObject) {
// GetTransitionRunListeners returns the "transition-run-event" listener list.
// If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetTransitionRunListeners(view View, subviewID string) []func(View, string) {
return getAnimationListeners(view, subviewID, TransitionRunEvent)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTransitionRunListeners(view View, subviewID ...string) []func(View, string) {
return getEventListeners[View, string](view, subviewID, TransitionRunEvent)
}
// GetTransitionStartListeners returns the "transition-start-event" listener list.
// If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetTransitionStartListeners(view View, subviewID string) []func(View, string) {
return getAnimationListeners(view, subviewID, TransitionStartEvent)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTransitionStartListeners(view View, subviewID ...string) []func(View, string) {
return getEventListeners[View, string](view, subviewID, TransitionStartEvent)
}
// GetTransitionEndListeners returns the "transition-end-event" listener list.
// If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetTransitionEndListeners(view View, subviewID string) []func(View, string) {
return getAnimationListeners(view, subviewID, TransitionEndEvent)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTransitionEndListeners(view View, subviewID ...string) []func(View, string) {
return getEventListeners[View, string](view, subviewID, TransitionEndEvent)
}
// GetTransitionCancelListeners returns the "transition-cancel-event" listener list.
// If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetTransitionCancelListeners(view View, subviewID string) []func(View, string) {
return getAnimationListeners(view, subviewID, TransitionCancelEvent)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTransitionCancelListeners(view View, subviewID ...string) []func(View, string) {
return getEventListeners[View, string](view, subviewID, TransitionCancelEvent)
}
// GetAnimationStartListeners returns the "animation-start-event" listener list.
// If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetAnimationStartListeners(view View, subviewID string) []func(View, string) {
return getAnimationListeners(view, subviewID, AnimationStartEvent)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetAnimationStartListeners(view View, subviewID ...string) []func(View, string) {
return getEventListeners[View, string](view, subviewID, AnimationStartEvent)
}
// GetAnimationEndListeners returns the "animation-end-event" listener list.
// If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetAnimationEndListeners(view View, subviewID string) []func(View, string) {
return getAnimationListeners(view, subviewID, AnimationEndEvent)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetAnimationEndListeners(view View, subviewID ...string) []func(View, string) {
return getEventListeners[View, string](view, subviewID, AnimationEndEvent)
}
// GetAnimationCancelListeners returns the "animation-cancel-event" listener list.
// If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetAnimationCancelListeners(view View, subviewID string) []func(View, string) {
return getAnimationListeners(view, subviewID, AnimationCancelEvent)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetAnimationCancelListeners(view View, subviewID ...string) []func(View, string) {
return getEventListeners[View, string](view, subviewID, AnimationCancelEvent)
}
// GetAnimationIterationListeners returns the "animation-iteration-event" listener list.
// If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetAnimationIterationListeners(view View, subviewID string) []func(View, string) {
return getAnimationListeners(view, subviewID, AnimationIterationEvent)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetAnimationIterationListeners(view View, subviewID ...string) []func(View, string) {
return getEventListeners[View, string](view, subviewID, AnimationIterationEvent)
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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.
// The type of return value depends on the property. If the property is not set then nil is returned.
func (customView *CustomViewData) Get(tag string) interface{} {
func (customView *CustomViewData) Get(tag string) any {
return customView.superView.Get(tag)
}
func (customView *CustomViewData) getRaw(tag string) interface{} {
func (customView *CustomViewData) getRaw(tag string) any {
return customView.superView.getRaw(tag)
}
func (customView *CustomViewData) setRaw(tag string, value interface{}) {
func (customView *CustomViewData) setRaw(tag string, value any) {
customView.superView.setRaw(tag, value)
}
// Set sets the value (second argument) of the property with name defined by the first argument.
// Return "true" if the value has been set, in the opposite case "false" are returned and
// a description of the error is written to the log
func (customView *CustomViewData) Set(tag string, value interface{}) bool {
func (customView *CustomViewData) Set(tag string, value any) bool {
return customView.superView.Set(tag, value)
}
func (customView *CustomViewData) SetAnimated(tag string, value interface{}, animation Animation) bool {
func (customView *CustomViewData) SetAnimated(tag string, value any, animation Animation) bool {
return customView.superView.SetAnimated(tag, value, animation)
}
@ -87,10 +87,6 @@ func (customView *CustomViewData) Clear() {
customView.superView.Clear()
}
// Init initializes fields of View by default values
func (customView *CustomViewData) Init(session Session) {
}
func (customView *CustomViewData) cssViewStyle(buffer cssBuilder, session Session) {
customView.superView.cssViewStyle(buffer, session)
}
@ -264,9 +260,22 @@ func (customView *CustomViewData) setScroll(x, y, width, height float64) {
}
}
func (customView *CustomViewData) getTransitions() Params {
func (customView *CustomViewData) Transition(tag string) Animation {
if customView.superView != nil {
return customView.superView.getTransitions()
return customView.superView.Transition(tag)
}
return nil
}
func (customView *CustomViewData) Transitions() map[string]Animation {
if customView.superView != nil {
return customView.superView.Transitions()
}
return map[string]Animation{}
}
func (customView *CustomViewData) SetTransition(tag string, animation Animation) {
if customView.superView != nil {
customView.superView.SetTransition(tag, animation)
}
return Params{}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -72,7 +72,7 @@ func (file *FileInfo) initBy(node DataValue) {
// NewFilePicker create new FilePicker object and return it
func NewFilePicker(session Session, params Params) FilePicker {
view := new(filePickerData)
view.Init(session)
view.init(session)
setInitParams(view, params)
return view
}
@ -81,8 +81,8 @@ func newFilePicker(session Session) View {
return NewFilePicker(session, nil)
}
func (picker *filePickerData) Init(session Session) {
picker.viewData.Init(session)
func (picker *filePickerData) init(session Session) {
picker.viewData.init(session)
picker.tag = "FilePicker"
picker.files = []FileInfo{}
picker.loader = map[int]func(FileInfo, []byte){}
@ -139,11 +139,11 @@ func (picker *filePickerData) remove(tag string) {
}
}
func (picker *filePickerData) Set(tag string, value interface{}) bool {
func (picker *filePickerData) Set(tag string, value any) bool {
return picker.set(strings.ToLower(tag), value)
}
func (picker *filePickerData) set(tag string, value interface{}) bool {
func (picker *filePickerData) set(tag string, value any) bool {
if value == nil {
picker.remove(tag)
return true
@ -151,57 +151,14 @@ func (picker *filePickerData) set(tag string, value interface{}) bool {
switch tag {
case FileSelectedEvent:
switch value := value.(type) {
case func(FilePicker, []FileInfo):
picker.fileSelectedListeners = []func(FilePicker, []FileInfo){value}
case func([]FileInfo):
fn := func(_ FilePicker, files []FileInfo) {
value(files)
}
picker.fileSelectedListeners = []func(FilePicker, []FileInfo){fn}
case []func(FilePicker, []FileInfo):
picker.fileSelectedListeners = value
case []func([]FileInfo):
listeners := make([]func(FilePicker, []FileInfo), len(value))
for i, val := range value {
if val == nil {
notCompatibleType(tag, val)
return false
}
listeners[i] = func(_ FilePicker, files []FileInfo) {
val(files)
}
}
picker.fileSelectedListeners = listeners
case []interface{}:
listeners := make([]func(FilePicker, []FileInfo), len(value))
for i, val := range value {
if val == nil {
notCompatibleType(tag, val)
return false
}
switch val := val.(type) {
case func(FilePicker, []FileInfo):
listeners[i] = val
case func([]FileInfo):
listeners[i] = func(_ FilePicker, files []FileInfo) {
val(files)
}
default:
notCompatibleType(tag, val)
return false
}
}
picker.fileSelectedListeners = listeners
listeners, ok := valueToEventListeners[FilePicker, []FileInfo](value)
if !ok {
notCompatibleType(tag, value)
return false
} else if listeners == nil {
listeners = []func(FilePicker, []FileInfo){}
}
picker.fileSelectedListeners = listeners
picker.propertyChangedEvent(tag)
return true
@ -294,7 +251,7 @@ func (picker *filePickerData) htmlProperties(self View, buffer *strings.Builder)
}
buffer.WriteString(` type="file"`)
if multiple, ok := boolStyledProperty(picker, Multiple); ok && multiple {
if IsMultipleFilePicker(picker) {
buffer.WriteString(` multiple`)
}
@ -305,7 +262,7 @@ func (picker *filePickerData) htmlProperties(self View, buffer *strings.Builder)
}
func (picker *filePickerData) htmlDisabledProperties(self View, buffer *strings.Builder) {
if IsDisabled(self, "") {
if IsDisabled(self) {
buffer.WriteString(` disabled`)
}
picker.viewData.htmlDisabledProperties(self, buffer)
@ -377,9 +334,14 @@ func (picker *filePickerData) handleCommand(self View, command string, data Data
// GetFilePickerFiles returns the list of FilePicker selected files
// If there are no files selected then an empty slice is returned (the result is always not nil)
// If the second argument (subviewID) is "" then selected files of the first argument (view) is returned
func GetFilePickerFiles(view View, subviewID string) []FileInfo {
if picker := FilePickerByID(view, subviewID); picker != nil {
// If the second argument (subviewID) is not specified or it is "" then selected files of the first argument (view) is returned
func GetFilePickerFiles(view View, subviewID ...string) []FileInfo {
subview := ""
if len(subviewID) > 0 {
subview = subviewID[0]
}
if picker := FilePickerByID(view, subview); picker != nil {
return picker.Files()
}
return []FileInfo{}
@ -395,24 +357,16 @@ func LoadFilePickerFile(view View, subviewID string, file FileInfo, result func(
}
// IsMultipleFilePicker returns "true" if multiple files can be selected in the FilePicker, "false" otherwise.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func IsMultipleFilePicker(view View, subviewID string) bool {
if subviewID != "" {
view = ViewByID(view, subviewID)
}
if view != nil {
if result, ok := boolStyledProperty(view, Multiple); ok {
return result
}
}
return false
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func IsMultipleFilePicker(view View, subviewID ...string) bool {
return boolStyledProperty(view, subviewID, Multiple, false)
}
// GetFilePickerAccept returns sets the list of allowed file extensions or MIME types.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetFilePickerAccept(view View, subviewID string) []string {
if subviewID != "" {
view = ViewByID(view, subviewID)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetFilePickerAccept(view View, subviewID ...string) []string {
if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0])
}
if view != nil {
accept, ok := stringProperty(view, Accept, view.Session())
@ -434,17 +388,7 @@ func GetFilePickerAccept(view View, subviewID string) []string {
// GetFileSelectedListeners returns the "file-selected-event" listener list.
// If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetFileSelectedListeners(view View, subviewID string) []func(FilePicker, []FileInfo) {
if subviewID != "" {
view = ViewByID(view, subviewID)
}
if view != nil {
if value := view.Get(FileSelectedEvent); value != nil {
if result, ok := value.([]func(FilePicker, []FileInfo)); ok {
return result
}
}
}
return []func(FilePicker, []FileInfo){}
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetFileSelectedListeners(view View, subviewID ...string) []func(FilePicker, []FileInfo) {
return getEventListeners[FilePicker, []FileInfo](view, subviewID, FileSelectedEvent)
}

View File

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

2
go.mod
View File

@ -1,5 +1,5 @@
module github.com/anoshenko/rui
go 1.17
go 1.18
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
func NewGridLayout(session Session, params Params) GridLayout {
view := new(gridLayoutData)
view.Init(session)
view.init(session)
setInitParams(view, params)
return view
}
@ -27,8 +27,8 @@ func newGridLayout(session Session) View {
}
// Init initialize fields of GridLayout by default values
func (gridLayout *gridLayoutData) Init(session Session) {
gridLayout.viewsContainerData.Init(session)
func (gridLayout *gridLayoutData) init(session Session) {
gridLayout.viewsContainerData.init(session)
gridLayout.tag = "GridLayout"
gridLayout.systemClass = "ruiGridLayout"
}
@ -37,15 +37,17 @@ func (gridLayout *gridLayoutData) String() string {
return getViewString(gridLayout)
}
func (style *viewStyle) setGridCellSize(tag string, value interface{}) bool {
func (style *viewStyle) setGridCellSize(tag string, value any) bool {
setValues := func(values []string) bool {
count := len(values)
if count > 1 {
sizes := make([]interface{}, count)
sizes := make([]any, count)
for i, val := range values {
val = strings.Trim(val, " \t\n\r")
if isConstantName(val) {
sizes[i] = val
} else if fn := parseSizeFunc(val); fn != nil {
sizes[i] = SizeUnit{Type: SizeFunction, Function: fn}
} else if size, err := stringToSizeUnit(val); err == nil {
sizes[i] = size
} else {
@ -99,13 +101,13 @@ func (style *viewStyle) setGridCellSize(tag string, value interface{}) bool {
return false
}
case []interface{}:
case []any:
count := len(value)
if count == 0 {
invalidPropertyValue(tag, value)
return false
}
sizes := make([]interface{}, count)
sizes := make([]any, count)
for i, val := range value {
switch val := val.(type) {
case SizeUnit:
@ -145,7 +147,7 @@ func (style *viewStyle) gridCellSizesCSS(tag string, session Session) string {
case 1:
if cellSize[0].Type != Auto {
return `repeat(auto-fill, ` + cellSize[0].cssString(`auto`) + `)`
return `repeat(auto-fill, ` + cellSize[0].cssString(`auto`, session) + `)`
}
default:
@ -161,14 +163,14 @@ func (style *viewStyle) gridCellSizesCSS(tag string, session Session) string {
}
if !allAuto {
if allEqual {
return fmt.Sprintf(`repeat(%d, %s)`, len(cellSize), cellSize[0].cssString(`auto`))
return fmt.Sprintf(`repeat(%d, %s)`, len(cellSize), cellSize[0].cssString(`auto`, session))
}
buffer := allocStringBuilder()
defer freeStringBuilder(buffer)
for _, size := range cellSize {
buffer.WriteRune(' ')
buffer.WriteString(size.cssString(`auto`))
buffer.WriteString(size.cssString(`auto`, session))
}
return buffer.String()
}
@ -195,14 +197,14 @@ func (gridLayout *gridLayoutData) normalizeTag(tag string) string {
return tag
}
func (gridLayout *gridLayoutData) Get(tag string) interface{} {
func (gridLayout *gridLayoutData) Get(tag string) any {
return gridLayout.get(gridLayout.normalizeTag(tag))
}
func (gridLayout *gridLayoutData) get(tag string) interface{} {
func (gridLayout *gridLayoutData) get(tag string) any {
if tag == Gap {
rowGap := GetGridRowGap(gridLayout, "")
columnGap := GetGridColumnGap(gridLayout, "")
rowGap := GetGridRowGap(gridLayout)
columnGap := GetGridColumnGap(gridLayout)
if rowGap.Equal(columnGap) {
return rowGap
}
@ -239,22 +241,18 @@ func (gridLayout *gridLayoutData) remove(tag string) {
}
}
func (gridLayout *gridLayoutData) Set(tag string, value interface{}) bool {
func (gridLayout *gridLayoutData) Set(tag string, value any) bool {
return gridLayout.set(gridLayout.normalizeTag(tag), value)
}
func (gridLayout *gridLayoutData) set(tag string, value interface{}) bool {
func (gridLayout *gridLayoutData) set(tag string, value any) bool {
if value == nil {
gridLayout.remove(tag)
return true
}
if tag == Gap {
if gridLayout.set(GridRowGap, value) && gridLayout.set(GridColumnGap, value) {
gridLayout.propertyChangedEvent(Gap)
return true
}
return false
return gridLayout.set(GridRowGap, value) && gridLayout.set(GridColumnGap, value)
}
if gridLayout.viewsContainerData.set(tag, value) {
@ -285,7 +283,7 @@ func gridCellSizes(properties Properties, tag string, session Session) []SizeUni
case SizeUnit:
return []SizeUnit{value}
case []interface{}:
case []any:
result := make([]SizeUnit, len(value))
for i, val := range value {
result[i] = AutoSize()
@ -323,53 +321,29 @@ func (gridLayout *gridLayoutData) cssStyle(self View, builder cssBuilder) {
*/
// GetCellVerticalAlign returns the vertical align of a GridLayout cell content: TopAlign (0), BottomAlign (1), CenterAlign (2), StretchAlign (3)
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetCellVerticalAlign(view View, subviewID string) int {
if subviewID != "" {
view = ViewByID(view, subviewID)
}
if view != nil {
if align, ok := enumStyledProperty(view, CellVerticalAlign, StretchAlign); ok {
return align
}
}
return StretchAlign
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetCellVerticalAlign(view View, subviewID ...string) int {
return enumStyledProperty(view, subviewID, CellVerticalAlign, StretchAlign, false)
}
// GetCellHorizontalAlign returns the vertical align of a GridLayout cell content: LeftAlign (0), RightAlign (1), CenterAlign (2), StretchAlign (3)
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetCellHorizontalAlign(view View, subviewID string) int {
if subviewID != "" {
view = ViewByID(view, subviewID)
}
if view != nil {
if align, ok := enumStyledProperty(view, CellHorizontalAlign, StretchAlign); ok {
return align
}
}
return StretchAlign
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetCellHorizontalAlign(view View, subviewID ...string) int {
return enumStyledProperty(view, subviewID, CellHorizontalAlign, StretchAlign, false)
}
// GetGridAutoFlow returns the value of the "grid-auto-flow" property
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetGridAutoFlow(view View, subviewID string) int {
if subviewID != "" {
view = ViewByID(view, subviewID)
}
if view != nil {
if align, ok := enumStyledProperty(view, GridAutoFlow, 0); ok {
return align
}
}
return 0
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetGridAutoFlow(view View, subviewID ...string) int {
return enumStyledProperty(view, subviewID, GridAutoFlow, 0, false)
}
// GetCellWidth returns the width of a GridLayout cell. If the result is an empty array, then the width is not set.
// If the result is a single value array, then the width of all cell is equal.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetCellWidth(view View, subviewID string) []SizeUnit {
if subviewID != "" {
view = ViewByID(view, subviewID)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetCellWidth(view View, subviewID ...string) []SizeUnit {
if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0])
}
if view != nil {
return gridCellSizes(view, CellWidth, view.Session())
@ -379,10 +353,10 @@ func GetCellWidth(view View, subviewID string) []SizeUnit {
// GetCellHeight returns the height of a GridLayout cell. If the result is an empty array, then the height is not set.
// If the result is a single value array, then the height of all cell is equal.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetCellHeight(view View, subviewID string) []SizeUnit {
if subviewID != "" {
view = ViewByID(view, subviewID)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetCellHeight(view View, subviewID ...string) []SizeUnit {
if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0])
}
if view != nil {
return gridCellSizes(view, CellHeight, view.Session())
@ -391,29 +365,13 @@ func GetCellHeight(view View, subviewID string) []SizeUnit {
}
// GetGridRowGap returns the gap between GridLayout rows.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetGridRowGap(view View, subviewID string) SizeUnit {
if subviewID != "" {
view = ViewByID(view, subviewID)
}
if view != nil {
if result, ok := sizeStyledProperty(view, GridRowGap); ok {
return result
}
}
return AutoSize()
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetGridRowGap(view View, subviewID ...string) SizeUnit {
return sizeStyledProperty(view, subviewID, GridRowGap, false)
}
// GetGridColumnGap returns the gap between GridLayout columns.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetGridColumnGap(view View, subviewID string) SizeUnit {
if subviewID != "" {
view = ViewByID(view, subviewID)
}
if view != nil {
if result, ok := sizeStyledProperty(view, GridColumnGap); ok {
return result
}
}
return AutoSize()
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetGridColumnGap(view View, subviewID ...string) SizeUnit {
return sizeStyledProperty(view, subviewID, GridColumnGap, false)
}

View File

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

View File

@ -50,34 +50,52 @@ type KeyEvent struct {
MetaKey bool
}
func valueToKeyListeners(value interface{}) ([]func(View, KeyEvent), bool) {
func (event *KeyEvent) init(data DataObject) {
getBool := func(tag string) bool {
if value, ok := data.PropertyValue(tag); ok && value == "1" {
return true
}
return false
}
event.Key, _ = data.PropertyValue("key")
event.Code, _ = data.PropertyValue("code")
event.TimeStamp = getTimeStamp(data)
event.Repeat = getBool("repeat")
event.CtrlKey = getBool("ctrlKey")
event.ShiftKey = getBool("shiftKey")
event.AltKey = getBool("altKey")
event.MetaKey = getBool("metaKey")
}
func valueToEventListeners[V View, E any](value any) ([]func(V, E), bool) {
if value == nil {
return nil, true
}
switch value := value.(type) {
case func(View, KeyEvent):
return []func(View, KeyEvent){value}, true
case func(V, E):
return []func(V, E){value}, true
case func(KeyEvent):
fn := func(_ View, event KeyEvent) {
case func(E):
fn := func(_ V, event E) {
value(event)
}
return []func(View, KeyEvent){fn}, true
return []func(V, E){fn}, true
case func(View):
fn := func(view View, _ KeyEvent) {
case func(V):
fn := func(view V, _ E) {
value(view)
}
return []func(View, KeyEvent){fn}, true
return []func(V, E){fn}, true
case func():
fn := func(View, KeyEvent) {
fn := func(V, E) {
value()
}
return []func(View, KeyEvent){fn}, true
return []func(V, E){fn}, true
case []func(View, KeyEvent):
case []func(V, E):
if len(value) == 0 {
return nil, true
}
@ -88,33 +106,33 @@ func valueToKeyListeners(value interface{}) ([]func(View, KeyEvent), bool) {
}
return value, true
case []func(KeyEvent):
case []func(E):
count := len(value)
if count == 0 {
return nil, true
}
listeners := make([]func(View, KeyEvent), count)
listeners := make([]func(V, E), count)
for i, v := range value {
if v == nil {
return nil, false
}
listeners[i] = func(_ View, event KeyEvent) {
listeners[i] = func(_ V, event E) {
v(event)
}
}
return listeners, true
case []func(View):
case []func(V):
count := len(value)
if count == 0 {
return nil, true
}
listeners := make([]func(View, KeyEvent), count)
listeners := make([]func(V, E), count)
for i, v := range value {
if v == nil {
return nil, false
}
listeners[i] = func(view View, _ KeyEvent) {
listeners[i] = func(view V, _ E) {
v(view)
}
}
@ -125,43 +143,43 @@ func valueToKeyListeners(value interface{}) ([]func(View, KeyEvent), bool) {
if count == 0 {
return nil, true
}
listeners := make([]func(View, KeyEvent), count)
listeners := make([]func(V, E), count)
for i, v := range value {
if v == nil {
return nil, false
}
listeners[i] = func(View, KeyEvent) {
listeners[i] = func(V, E) {
v()
}
}
return listeners, true
case []interface{}:
case []any:
count := len(value)
if count == 0 {
return nil, true
}
listeners := make([]func(View, KeyEvent), count)
listeners := make([]func(V, E), count)
for i, v := range value {
if v == nil {
return nil, false
}
switch v := v.(type) {
case func(View, KeyEvent):
case func(V, E):
listeners[i] = v
case func(KeyEvent):
listeners[i] = func(_ View, event KeyEvent) {
case func(E):
listeners[i] = func(_ V, event E) {
v(event)
}
case func(View):
listeners[i] = func(view View, _ KeyEvent) {
case func(V):
listeners[i] = func(view V, _ E) {
v(view)
}
case func():
listeners[i] = func(View, KeyEvent) {
listeners[i] = func(V, E) {
v()
}
@ -180,8 +198,8 @@ var keyEvents = map[string]struct{ jsEvent, jsFunc string }{
KeyUpEvent: {jsEvent: "onkeyup", jsFunc: "keyUpEvent"},
}
func (view *viewData) setKeyListener(tag string, value interface{}) bool {
listeners, ok := valueToKeyListeners(value)
func (view *viewData) setKeyListener(tag string, value any) bool {
listeners, ok := valueToEventListeners[View, KeyEvent](value)
if !ok {
notCompatibleType(tag, value)
return false
@ -209,67 +227,48 @@ func (view *viewData) removeKeyListener(tag string) {
}
}
func getKeyListeners(view View, subviewID string, tag string) []func(View, KeyEvent) {
if subviewID != "" {
view = ViewByID(view, subviewID)
func getEventListeners[V View, E any](view View, subviewID []string, tag string) []func(V, E) {
if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0])
}
if view != nil {
if value := view.Get(tag); value != nil {
if result, ok := value.([]func(View, KeyEvent)); ok {
if result, ok := value.([]func(V, E)); ok {
return result
}
}
}
return []func(View, KeyEvent){}
return []func(V, E){}
}
func keyEventsHtml(view View, buffer *strings.Builder) {
for tag, js := range keyEvents {
if listeners := getKeyListeners(view, "", tag); len(listeners) > 0 {
if listeners := getEventListeners[View, KeyEvent](view, nil, tag); len(listeners) > 0 {
buffer.WriteString(js.jsEvent + `="` + js.jsFunc + `(this, event)" `)
}
}
}
func handleKeyEvents(view View, tag string, data DataObject) {
listeners := getKeyListeners(view, "", tag)
if len(listeners) == 0 {
return
}
listeners := getEventListeners[View, KeyEvent](view, nil, tag)
if len(listeners) > 0 {
var event KeyEvent
event.init(data)
getBool := func(tag string) bool {
if value, ok := data.PropertyValue(tag); ok && value == "1" {
return true
for _, listener := range listeners {
listener(view, event)
}
return false
}
key, _ := data.PropertyValue("key")
code, _ := data.PropertyValue("code")
event := KeyEvent{
TimeStamp: getTimeStamp(data),
Key: key,
Code: code,
Repeat: getBool("repeat"),
CtrlKey: getBool("ctrlKey"),
ShiftKey: getBool("shiftKey"),
AltKey: getBool("altKey"),
MetaKey: getBool("metaKey"),
}
for _, listener := range listeners {
listener(view, event)
}
}
// GetKeyDownListeners returns the "key-down-event" listener list. If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetKeyDownListeners(view View, subviewID string) []func(View, KeyEvent) {
return getKeyListeners(view, subviewID, KeyDownEvent)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetKeyDownListeners(view View, subviewID ...string) []func(View, KeyEvent) {
return getEventListeners[View, KeyEvent](view, subviewID, KeyDownEvent)
}
// GetKeyUpListeners returns the "key-up-event" listener list. If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetKeyUpListeners(view View, subviewID string) []func(View, KeyEvent) {
return getKeyListeners(view, subviewID, KeyUpEvent)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetKeyUpListeners(view View, subviewID ...string) []func(View, KeyEvent) {
return getEventListeners[View, KeyEvent](view, subviewID, KeyUpEvent)
}

View File

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

View File

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

View File

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

View File

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

View File

@ -144,131 +144,6 @@ type MouseEvent struct {
MetaKey bool
}
func valueToMouseListeners(value interface{}) ([]func(View, MouseEvent), bool) {
if value == nil {
return nil, true
}
switch value := value.(type) {
case func(View, MouseEvent):
return []func(View, MouseEvent){value}, true
case func(MouseEvent):
fn := func(_ View, event MouseEvent) {
value(event)
}
return []func(View, MouseEvent){fn}, true
case func(View):
fn := func(view View, _ MouseEvent) {
value(view)
}
return []func(View, MouseEvent){fn}, true
case func():
fn := func(View, MouseEvent) {
value()
}
return []func(View, MouseEvent){fn}, true
case []func(View, MouseEvent):
if len(value) == 0 {
return nil, true
}
for _, fn := range value {
if fn == nil {
return nil, false
}
}
return value, true
case []func(MouseEvent):
count := len(value)
if count == 0 {
return nil, true
}
listeners := make([]func(View, MouseEvent), count)
for i, v := range value {
if v == nil {
return nil, false
}
listeners[i] = func(_ View, event MouseEvent) {
v(event)
}
}
return listeners, true
case []func(View):
count := len(value)
if count == 0 {
return nil, true
}
listeners := make([]func(View, MouseEvent), count)
for i, v := range value {
if v == nil {
return nil, false
}
listeners[i] = func(view View, _ MouseEvent) {
v(view)
}
}
return listeners, true
case []func():
count := len(value)
if count == 0 {
return nil, true
}
listeners := make([]func(View, MouseEvent), count)
for i, v := range value {
if v == nil {
return nil, false
}
listeners[i] = func(View, MouseEvent) {
v()
}
}
return listeners, true
case []interface{}:
count := len(value)
if count == 0 {
return nil, true
}
listeners := make([]func(View, MouseEvent), count)
for i, v := range value {
if v == nil {
return nil, false
}
switch v := v.(type) {
case func(View, MouseEvent):
listeners[i] = v
case func(MouseEvent):
listeners[i] = func(_ View, event MouseEvent) {
v(event)
}
case func(View):
listeners[i] = func(view View, _ MouseEvent) {
v(view)
}
case func():
listeners[i] = func(View, MouseEvent) {
v()
}
default:
return nil, false
}
}
return listeners, true
}
return nil, false
}
var mouseEvents = map[string]struct{ jsEvent, jsFunc string }{
ClickEvent: {jsEvent: "onclick", jsFunc: "clickEvent"},
DoubleClickEvent: {jsEvent: "ondblclick", jsFunc: "doubleClickEvent"},
@ -280,8 +155,8 @@ var mouseEvents = map[string]struct{ jsEvent, jsFunc string }{
ContextMenuEvent: {jsEvent: "oncontextmenu", jsFunc: "contextMenuEvent"},
}
func (view *viewData) setMouseListener(tag string, value interface{}) bool {
listeners, ok := valueToMouseListeners(value)
func (view *viewData) setMouseListener(tag string, value any) bool {
listeners, ok := valueToEventListeners[View, MouseEvent](value)
if !ok {
notCompatibleType(tag, value)
return false
@ -309,20 +184,6 @@ func (view *viewData) removeMouseListener(tag string) {
}
}
func getMouseListeners(view View, subviewID string, tag string) []func(View, MouseEvent) {
if subviewID != "" {
view = ViewByID(view, subviewID)
}
if view != nil {
if value := view.Get(tag); value != nil {
if result, ok := value.([]func(View, MouseEvent)); ok {
return result
}
}
}
return []func(View, MouseEvent){}
}
func mouseEventsHtml(view View, buffer *strings.Builder) {
for tag, js := range mouseEvents {
if value := view.getRaw(tag); value != nil {
@ -363,64 +224,62 @@ func (event *MouseEvent) init(data DataObject) {
}
func handleMouseEvents(view View, tag string, data DataObject) {
listeners := getMouseListeners(view, "", tag)
if len(listeners) == 0 {
return
}
listeners := getEventListeners[View, MouseEvent](view, nil, tag)
if len(listeners) > 0 {
var event MouseEvent
event.init(data)
var event MouseEvent
event.init(data)
for _, listener := range listeners {
listener(view, event)
for _, listener := range listeners {
listener(view, event)
}
}
}
// GetClickListeners returns the "click-event" listener list. If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetClickListeners(view View, subviewID string) []func(View, MouseEvent) {
return getMouseListeners(view, subviewID, ClickEvent)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetClickListeners(view View, subviewID ...string) []func(View, MouseEvent) {
return getEventListeners[View, MouseEvent](view, subviewID, ClickEvent)
}
// GetDoubleClickListeners returns the "double-click-event" listener list. If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetDoubleClickListeners(view View, subviewID string) []func(View, MouseEvent) {
return getMouseListeners(view, subviewID, DoubleClickEvent)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetDoubleClickListeners(view View, subviewID ...string) []func(View, MouseEvent) {
return getEventListeners[View, MouseEvent](view, subviewID, DoubleClickEvent)
}
// GetContextMenuListeners returns the "context-menu" listener list.
// If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetContextMenuListeners(view View, subviewID string) []func(View, MouseEvent) {
return getMouseListeners(view, subviewID, ContextMenuEvent)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetContextMenuListeners(view View, subviewID ...string) []func(View, MouseEvent) {
return getEventListeners[View, MouseEvent](view, subviewID, ContextMenuEvent)
}
// GetMouseDownListeners returns the "mouse-down" listener list. If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetMouseDownListeners(view View, subviewID string) []func(View, MouseEvent) {
return getMouseListeners(view, subviewID, MouseDown)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetMouseDownListeners(view View, subviewID ...string) []func(View, MouseEvent) {
return getEventListeners[View, MouseEvent](view, subviewID, MouseDown)
}
// GetMouseUpListeners returns the "mouse-up" listener list. If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetMouseUpListeners(view View, subviewID string) []func(View, MouseEvent) {
return getMouseListeners(view, subviewID, MouseUp)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetMouseUpListeners(view View, subviewID ...string) []func(View, MouseEvent) {
return getEventListeners[View, MouseEvent](view, subviewID, MouseUp)
}
// GetMouseMoveListeners returns the "mouse-move" listener list. If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetMouseMoveListeners(view View, subviewID string) []func(View, MouseEvent) {
return getMouseListeners(view, subviewID, MouseMove)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetMouseMoveListeners(view View, subviewID ...string) []func(View, MouseEvent) {
return getEventListeners[View, MouseEvent](view, subviewID, MouseMove)
}
// GetMouseOverListeners returns the "mouse-over" listener list. If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetMouseOverListeners(view View, subviewID string) []func(View, MouseEvent) {
return getMouseListeners(view, subviewID, MouseOver)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetMouseOverListeners(view View, subviewID ...string) []func(View, MouseEvent) {
return getEventListeners[View, MouseEvent](view, subviewID, MouseOver)
}
// GetMouseOutListeners returns the "mouse-out" listener list. If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetMouseOutListeners(view View, subviewID string) []func(View, MouseEvent) {
return getMouseListeners(view, subviewID, MouseOut)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetMouseOutListeners(view View, subviewID ...string) []func(View, MouseEvent) {
return getEventListeners[View, MouseEvent](view, subviewID, MouseOut)
}

View File

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

View File

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

View File

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

View File

@ -87,131 +87,6 @@ type PointerEvent struct {
IsPrimary bool
}
func valueToPointerListeners(value interface{}) ([]func(View, PointerEvent), bool) {
if value == nil {
return nil, true
}
switch value := value.(type) {
case func(View, PointerEvent):
return []func(View, PointerEvent){value}, true
case func(PointerEvent):
fn := func(_ View, event PointerEvent) {
value(event)
}
return []func(View, PointerEvent){fn}, true
case func(View):
fn := func(view View, _ PointerEvent) {
value(view)
}
return []func(View, PointerEvent){fn}, true
case func():
fn := func(View, PointerEvent) {
value()
}
return []func(View, PointerEvent){fn}, true
case []func(View, PointerEvent):
if len(value) == 0 {
return nil, true
}
for _, fn := range value {
if fn == nil {
return nil, false
}
}
return value, true
case []func(PointerEvent):
count := len(value)
if count == 0 {
return nil, true
}
listeners := make([]func(View, PointerEvent), count)
for i, v := range value {
if v == nil {
return nil, false
}
listeners[i] = func(_ View, event PointerEvent) {
v(event)
}
}
return listeners, true
case []func(View):
count := len(value)
if count == 0 {
return nil, true
}
listeners := make([]func(View, PointerEvent), count)
for i, v := range value {
if v == nil {
return nil, false
}
listeners[i] = func(view View, _ PointerEvent) {
v(view)
}
}
return listeners, true
case []func():
count := len(value)
if count == 0 {
return nil, true
}
listeners := make([]func(View, PointerEvent), count)
for i, v := range value {
if v == nil {
return nil, false
}
listeners[i] = func(View, PointerEvent) {
v()
}
}
return listeners, true
case []interface{}:
count := len(value)
if count == 0 {
return nil, true
}
listeners := make([]func(View, PointerEvent), count)
for i, v := range value {
if v == nil {
return nil, false
}
switch v := v.(type) {
case func(View, PointerEvent):
listeners[i] = v
case func(PointerEvent):
listeners[i] = func(_ View, event PointerEvent) {
v(event)
}
case func(View):
listeners[i] = func(view View, _ PointerEvent) {
v(view)
}
case func():
listeners[i] = func(View, PointerEvent) {
v()
}
default:
return nil, false
}
}
return listeners, true
}
return nil, false
}
var pointerEvents = map[string]struct{ jsEvent, jsFunc string }{
PointerDown: {jsEvent: "onpointerdown", jsFunc: "pointerDownEvent"},
PointerUp: {jsEvent: "onpointerup", jsFunc: "pointerUpEvent"},
@ -221,8 +96,8 @@ var pointerEvents = map[string]struct{ jsEvent, jsFunc string }{
PointerOver: {jsEvent: "onpointerover", jsFunc: "pointerOverEvent"},
}
func (view *viewData) setPointerListener(tag string, value interface{}) bool {
listeners, ok := valueToPointerListeners(value)
func (view *viewData) setPointerListener(tag string, value any) bool {
listeners, ok := valueToEventListeners[View, PointerEvent](value)
if !ok {
notCompatibleType(tag, value)
return false
@ -250,20 +125,6 @@ func (view *viewData) removePointerListener(tag string) {
}
}
func getPointerListeners(view View, subviewID string, tag string) []func(View, PointerEvent) {
if subviewID != "" {
view = ViewByID(view, subviewID)
}
if view != nil {
if value := view.Get(tag); value != nil {
if result, ok := value.([]func(View, PointerEvent)); ok {
return result
}
}
}
return []func(View, PointerEvent){}
}
func pointerEventsHtml(view View, buffer *strings.Builder) {
for tag, js := range pointerEvents {
if value := view.getRaw(tag); value != nil {
@ -291,7 +152,7 @@ func (event *PointerEvent) init(data DataObject) {
}
func handlePointerEvents(view View, tag string, data DataObject) {
listeners := getPointerListeners(view, "", tag)
listeners := getEventListeners[View, PointerEvent](view, nil, tag)
if len(listeners) == 0 {
return
}
@ -305,37 +166,37 @@ func handlePointerEvents(view View, tag string, data DataObject) {
}
// GetPointerDownListeners returns the "pointer-down" listener list. If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetPointerDownListeners(view View, subviewID string) []func(View, PointerEvent) {
return getPointerListeners(view, subviewID, PointerDown)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetPointerDownListeners(view View, subviewID ...string) []func(View, PointerEvent) {
return getEventListeners[View, PointerEvent](view, subviewID, PointerDown)
}
// GetPointerUpListeners returns the "pointer-up" listener list. If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetPointerUpListeners(view View, subviewID string) []func(View, PointerEvent) {
return getPointerListeners(view, subviewID, PointerUp)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetPointerUpListeners(view View, subviewID ...string) []func(View, PointerEvent) {
return getEventListeners[View, PointerEvent](view, subviewID, PointerUp)
}
// GetPointerMoveListeners returns the "pointer-move" listener list. If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetPointerMoveListeners(view View, subviewID string) []func(View, PointerEvent) {
return getPointerListeners(view, subviewID, PointerMove)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetPointerMoveListeners(view View, subviewID ...string) []func(View, PointerEvent) {
return getEventListeners[View, PointerEvent](view, subviewID, PointerMove)
}
// GetPointerCancelListeners returns the "pointer-cancel" listener list. If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetPointerCancelListeners(view View, subviewID string) []func(View, PointerEvent) {
return getPointerListeners(view, subviewID, PointerCancel)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetPointerCancelListeners(view View, subviewID ...string) []func(View, PointerEvent) {
return getEventListeners[View, PointerEvent](view, subviewID, PointerCancel)
}
// GetPointerOverListeners returns the "pointer-over" listener list. If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetPointerOverListeners(view View, subviewID string) []func(View, PointerEvent) {
return getPointerListeners(view, subviewID, PointerOver)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetPointerOverListeners(view View, subviewID ...string) []func(View, PointerEvent) {
return getEventListeners[View, PointerEvent](view, subviewID, PointerOver)
}
// GetPointerOutListeners returns the "pointer-out" listener list. If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetPointerOutListeners(view View, subviewID string) []func(View, PointerEvent) {
return getPointerListeners(view, subviewID, PointerOut)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetPointerOutListeners(view View, subviewID ...string) []func(View, PointerEvent) {
return getEventListeners[View, PointerEvent](view, subviewID, PointerOut)
}

520
popup.go
View File

@ -1,6 +1,8 @@
package rui
import "strings"
import (
"strings"
)
const (
// Title is the constant for the "title" property tag.
@ -36,6 +38,46 @@ const (
// It occurs after the Popup disappears from the screen.
// The main listener for this event has the following format: func(Popup)
DismissEvent = "dismiss-event"
// Arrow is the constant for the "arrow" property tag.
// Using the "popup-arrow" int property you can add ...
Arrow = "arrow"
// ArrowAlign is the constant for the "arrow-align" property tag.
// The "arrow-align" int property is used for set the horizontal alignment of the Popup arrow.
// Valid values: LeftAlign (0), RightAlign (1), TopAlign (0), BottomAlign (1), CenterAlign (2)
ArrowAlign = "arrow-align"
// ArrowSize is the constant for the "arrow-size" property tag.
// The "arrow-size" SizeUnit property is used for set the size (length) of the Popup arrow.
ArrowSize = "arrow-size"
// ArrowWidth is the constant for the "arrow-width" property tag.
// The "arrow-width" SizeUnit property is used for set the width of the Popup arrow.
ArrowWidth = "arrow-width"
// ArrowOffset is the constant for the "arrow-offset" property tag.
// The "arrow-offset" SizeUnit property is used for set the offset of the Popup arrow.
ArrowOffset = "arrow-offset"
// NoneArrow is value of the popup "arrow" property: no arrow
NoneArrow = 0
// TopArrow is value of the popup "arrow" property:
// Arrow at the top side of the pop-up window
TopArrow = 1
// RightArrow is value of the popup "arrow" property:
// Arrow on the right side of the pop-up window
RightArrow = 2
// BottomArrow is value of the popup "arrow" property:
// Arrow at the bottom of the pop-up window
BottomArrow = 3
// LeftArrow is value of the popup "arrow" property:
// Arrow on the left side of the pop-up window
LeftArrow = 4
)
// PopupButton describes a button that will be placed at the bottom of the window.
@ -65,152 +107,388 @@ type popupManager struct {
popups []Popup
}
func (popup *popupData) init(view View, params Params) {
type popupArrow struct {
column, row int
location, align int
size, width, off SizeUnit
}
func (arrow *popupArrow) fixOff(popupView View) {
if arrow.align == CenterAlign && arrow.off.Type == Auto {
r := GetRadius(popupView)
switch arrow.location {
case TopArrow:
switch arrow.align {
case LeftAlign:
arrow.off = r.TopLeftX
case RightAlign:
arrow.off = r.TopRightX
}
case BottomArrow:
switch arrow.align {
case LeftAlign:
arrow.off = r.BottomLeftX
case RightAlign:
arrow.off = r.BottomRightX
}
case RightArrow:
switch arrow.align {
case TopAlign:
arrow.off = r.TopRightY
case BottomAlign:
arrow.off = r.BottomRightY
}
case LeftArrow:
switch arrow.align {
case TopAlign:
arrow.off = r.TopLeftY
case BottomAlign:
arrow.off = r.BottomLeftY
}
}
}
}
func (arrow *popupArrow) createView(popupView View) View {
session := popupView.Session()
defaultSize := func(constTag string, defValue SizeUnit) SizeUnit {
if value, ok := session.Constant(constTag); ok {
if size, ok := StringToSizeUnit(value); ok && size.Type != Auto && size.Value != 0 {
return size
}
}
return defValue
}
if arrow.size.Type == Auto || arrow.size.Value == 0 {
arrow.size = defaultSize("ruiArrowSize", Px(16))
}
if arrow.width.Type == Auto || arrow.width.Value == 0 {
arrow.width = defaultSize("ruiArrowWidth", Px(16))
}
params := Params{BackgroundColor: GetBackgroundColor(popupView)}
if shadow := GetViewShadows(popupView); shadow != nil {
params[Shadow] = shadow
}
if filter := GetBackdropFilter(popupView); filter != nil {
params[BackdropFilter] = filter
}
switch arrow.location {
case TopArrow:
params[Row] = 0
params[Column] = 1
params[Clip] = PolygonClip([]any{"0%", "100%", "50%", "0%", "100%", "100%"})
params[Width] = arrow.width
params[Height] = arrow.size
case RightArrow:
params[Row] = 1
params[Column] = 0
params[Clip] = PolygonClip([]any{"0%", "0%", "100%", "50%", "0%", "100%"})
params[Width] = arrow.size
params[Height] = arrow.width
case BottomArrow:
params[Row] = 0
params[Column] = 1
params[Clip] = PolygonClip([]any{"0%", "0%", "50%", "100%", "100%", "0%"})
params[Width] = arrow.width
params[Height] = arrow.size
case LeftArrow:
params[Row] = 1
params[Column] = 0
params[Clip] = PolygonClip([]any{"100%", "0%", "0%", "50%", "100%", "100%"})
params[Width] = arrow.size
params[Height] = arrow.width
}
arrowView := NewView(session, params)
params = Params{
Row: arrow.row,
Column: arrow.column,
Content: arrowView,
}
arrow.fixOff(popupView)
switch arrow.location {
case TopArrow, BottomArrow:
cellWidth := make([]SizeUnit, 3)
switch arrow.align {
case LeftAlign:
cellWidth[0] = arrow.off
cellWidth[2] = Fr(1)
case RightAlign:
cellWidth[0] = Fr(1)
cellWidth[2] = arrow.off
default:
cellWidth[0] = Fr(1)
cellWidth[2] = Fr(1)
if arrow.off.Type != Auto && arrow.off.Value != 0 {
arrowView.Set(MarginLeft, arrow.off)
}
}
params[CellWidth] = cellWidth
case RightArrow, LeftArrow:
cellHeight := make([]SizeUnit, 3)
switch arrow.align {
case TopAlign:
cellHeight[0] = arrow.off
cellHeight[2] = Fr(1)
case BottomAlign:
cellHeight[0] = Fr(1)
cellHeight[2] = arrow.off
default:
cellHeight[0] = Fr(1)
cellHeight[2] = Fr(1)
if arrow.off.Type != Auto && arrow.off.Value != 0 {
arrowView.Set(MarginTop, arrow.off)
}
}
params[CellHeight] = cellHeight
}
return NewGridLayout(session, params)
}
func (popup *popupData) init(view View, popupParams Params) {
popup.view = view
session := view.Session()
if params == nil {
params = Params{}
columnCount := 3
rowCount := 3
popupRow := 1
popupColumn := 1
arrow := popupArrow{
row: 1,
column: 1,
align: CenterAlign,
}
popup.dismissListener = []func(Popup){}
if value, ok := params[DismissEvent]; ok && value != nil {
switch value := value.(type) {
case func(Popup):
popup.dismissListener = []func(Popup){value}
switch arrow.location, _ = enumProperty(popupParams, Arrow, session, NoneArrow); arrow.location {
case TopArrow:
rowCount = 4
popupRow = 2
case func():
popup.dismissListener = []func(Popup){
func(_ Popup) {
value()
},
}
case BottomArrow:
rowCount = 4
arrow.row = 2
case []func(Popup):
for _, fn := range value {
if fn != nil {
popup.dismissListener = append(popup.dismissListener, fn)
}
}
case LeftArrow:
columnCount = 4
popupColumn = 2
case []func():
for _, fn := range value {
if fn != nil {
popup.dismissListener = append(popup.dismissListener, func(_ Popup) {
fn()
})
}
}
case RightArrow:
columnCount = 4
arrow.column = 2
case []interface{}:
for _, val := range value {
if val != nil {
switch fn := val.(type) {
case func(Popup):
popup.dismissListener = append(popup.dismissListener, fn)
case func():
popup.dismissListener = append(popup.dismissListener, func(_ Popup) {
fn()
})
}
}
}
}
default:
}
var title View = nil
titleStyle := "ruiPopupTitle"
closeButton, _ := boolProperty(params, CloseButton, session)
outsideClose, _ := boolProperty(params, OutsideClose, session)
vAlign, _ := enumProperty(params, VerticalAlign, session, CenterAlign)
hAlign, _ := enumProperty(params, HorizontalAlign, session, CenterAlign)
cellWidth := make([]SizeUnit, columnCount)
switch hAlign, _ := enumProperty(popupParams, HorizontalAlign, session, CenterAlign); hAlign {
case LeftAlign:
cellWidth[columnCount-1] = Fr(1)
buttons := []PopupButton{}
if value, ok := params[Buttons]; ok && value != nil {
switch value := value.(type) {
case PopupButton:
buttons = []PopupButton{value}
case RightAlign:
cellWidth[0] = Fr(1)
case []PopupButton:
buttons = value
}
default:
cellWidth[0] = Fr(1)
cellWidth[columnCount-1] = Fr(1)
}
popupView := NewGridLayout(view.Session(), Params{
cellHeight := make([]SizeUnit, rowCount)
switch vAlign, _ := enumProperty(popupParams, VerticalAlign, session, CenterAlign); vAlign {
case LeftAlign:
cellHeight[rowCount-1] = Fr(1)
case RightAlign:
cellHeight[0] = Fr(1)
default:
cellHeight[0] = Fr(1)
cellHeight[rowCount-1] = Fr(1)
}
layerParams := Params{
Style: "ruiPopupLayer",
MaxWidth: Percent(100),
MaxHeight: Percent(100),
CellWidth: cellWidth,
CellHeight: cellHeight,
}
params := Params{
Style: "ruiPopup",
Row: popupRow,
Column: popupColumn,
MaxWidth: Percent(100),
MaxHeight: Percent(100),
CellVerticalAlign: StretchAlign,
CellHorizontalAlign: StretchAlign,
ClickEvent: func(View) {},
})
Shadow: NewShadowWithParams(Params{
SpreadRadius: Px(4),
Blur: Px(16),
ColorTag: "@ruiPopupShadow",
}),
}
for tag, value := range params {
switch tag {
case Title:
switch value := value.(type) {
case string:
title = NewTextView(view.Session(), Params{Text: value})
var closeButton View = nil
outsideClose := false
buttons := []PopupButton{}
titleStyle := "ruiPopupTitle"
var title View = nil
case View:
title = value
for tag, value := range popupParams {
if value != nil {
switch tag {
case VerticalAlign, HorizontalAlign, Arrow, Row, Column:
// Do nothing
case Margin:
layerParams[Padding] = value
case MarginLeft:
layerParams[PaddingLeft] = value
case MarginRight:
layerParams[PaddingRight] = value
case MarginTop:
layerParams[PaddingTop] = value
case MarginBottom:
layerParams[PaddingBottom] = value
case CloseButton:
closeButton = NewGridLayout(session, Params{
Column: 1,
Height: "@ruiPopupTitleHeight",
Width: "@ruiPopupTitleHeight",
CellHorizontalAlign: CenterAlign,
CellVerticalAlign: CenterAlign,
TextSize: Px(20),
Content: "✕",
NotTranslate: true,
ClickEvent: func(View) {
popup.Dismiss()
},
})
case OutsideClose:
outsideClose, _ = boolProperty(popupParams, OutsideClose, session)
case Buttons:
switch value := value.(type) {
case PopupButton:
buttons = []PopupButton{value}
case []PopupButton:
buttons = value
}
case Title:
switch value := value.(type) {
case string:
title = NewTextView(view.Session(), Params{Text: value})
case View:
title = value
default:
notCompatibleType(Title, value)
}
case TitleStyle:
switch value := value.(type) {
case string:
titleStyle = value
default:
notCompatibleType(TitleStyle, value)
}
case DismissEvent:
if listeners, ok := valueToNoParamListeners[Popup](value); ok {
if listeners != nil {
popup.dismissListener = listeners
}
} else {
notCompatibleType(tag, value)
}
case ArrowAlign:
switch text := value.(type) {
case string:
switch text {
case "top":
value = "left"
case "bottom":
value = "right"
}
}
arrow.align, _ = enumProperty(popupParams, ArrowAlign, session, CenterAlign)
case ArrowSize:
arrow.size, _ = sizeProperty(popupParams, ArrowSize, session)
case ArrowOffset:
arrow.off, _ = sizeProperty(popupParams, ArrowOffset, session)
default:
notCompatibleType(Title, value)
params[tag] = value
}
case TitleStyle:
switch value := value.(type) {
case string:
titleStyle = value
default:
notCompatibleType(TitleStyle, value)
}
case CloseButton, OutsideClose, VerticalAlign, HorizontalAlign, Buttons:
// do nothing
default:
popupView.Set(tag, value)
}
}
var cellHeight []SizeUnit
popupView := NewGridLayout(view.Session(), params)
var popupCellHeight []SizeUnit
viewRow := 0
if title != nil || closeButton {
viewRow = 1
titleHeight, _ := sizeConstant(popup.Session(), "ruiPopupTitleHeight")
titleView := NewGridLayout(session, Params{
if title != nil || closeButton != nil {
titleContent := []View{}
if title != nil {
titleContent = append(titleContent, title)
}
if closeButton != nil {
titleContent = append(titleContent, closeButton)
}
popupView.Append(NewGridLayout(session, Params{
Row: 0,
Style: titleStyle,
CellWidth: []SizeUnit{Fr(1), titleHeight},
CellWidth: []any{Fr(1), AutoSize()},
CellVerticalAlign: CenterAlign,
PaddingLeft: Px(12),
})
if title != nil {
titleView.Append(title)
}
if closeButton {
titleView.Append(NewGridLayout(session, Params{
Column: 1,
Height: titleHeight,
Width: titleHeight,
CellHorizontalAlign: CenterAlign,
CellVerticalAlign: CenterAlign,
TextSize: Px(20),
Content: "✕",
ClickEvent: func(View) {
popup.Dismiss()
},
}))
}
Content: titleContent,
}))
popupView.Append(titleView)
cellHeight = []SizeUnit{AutoSize(), Fr(1)}
viewRow = 1
popupCellHeight = []SizeUnit{AutoSize(), Fr(1)}
} else {
cellHeight = []SizeUnit{Fr(1)}
popupCellHeight = []SizeUnit{Fr(1)}
}
view.Set(Row, viewRow)
@ -218,7 +496,7 @@ func (popup *popupData) init(view View, params Params) {
if buttonCount := len(buttons); buttonCount > 0 {
buttonsAlign, _ := enumProperty(params, ButtonsAlign, session, RightAlign)
cellHeight = append(cellHeight, AutoSize())
popupCellHeight = append(popupCellHeight, AutoSize())
gap, _ := sizeConstant(session, "ruiPopupButtonGap")
cellWidth := []SizeUnit{}
for i := 0; i < buttonCount; i++ {
@ -256,16 +534,16 @@ func (popup *popupData) init(view View, params Params) {
Content: buttonsPanel,
}))
}
popupView.Set(CellHeight, cellHeight)
popup.layerView = NewGridLayout(session, Params{
Style: "ruiPopupLayer",
CellVerticalAlign: vAlign,
CellHorizontalAlign: hAlign,
Content: NewColumnLayout(session, Params{Content: popupView}),
MaxWidth: Percent(100),
MaxHeight: Percent(100),
})
popupView.Set(CellHeight, popupCellHeight)
if arrow.location != NoneArrow {
layerParams[Content] = []View{popupView, arrow.createView(popupView)}
} else {
layerParams[Content] = []View{popupView}
}
popup.layerView = NewGridLayout(session, layerParams)
if outsideClose {
popup.layerView.Set(ClickEvent, func(View) {

View File

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

View File

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

View File

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

View File

@ -32,49 +32,49 @@ func TestProperties(t *testing.T) {
t.Error(`list.Get("name") is not string`)
}
sizeValues := []interface{}{"@small", "auto", "10px", Pt(20), AutoSize()}
sizeValues := []any{"@small", "auto", "10px", Pt(20), AutoSize()}
for _, value := range sizeValues {
if !list.setSizeProperty("size", value) {
t.Errorf(`setSizeProperty("size", %v) fail`, value)
}
}
failSizeValues := []interface{}{"@small,big", "abc", "10", Color(20), 100}
failSizeValues := []any{"@small,big", "abc", "10", Color(20), 100}
for _, value := range failSizeValues {
if list.setSizeProperty("size", value) {
t.Errorf(`setSizeProperty("size", %v) success`, value)
}
}
angleValues := []interface{}{"@angle", "2pi", "π", "3deg", "60°", Rad(1.5), Deg(45), 1, 1.5}
angleValues := []any{"@angle", "2pi", "π", "3deg", "60°", Rad(1.5), Deg(45), 1, 1.5}
for _, value := range angleValues {
if !list.setAngleProperty("angle", value) {
t.Errorf(`setAngleProperty("angle", %v) fail`, value)
}
}
failAngleValues := []interface{}{"@angle,2", "pi32", "deg", "60°x", Color(0xFFFFFFFF)}
failAngleValues := []any{"@angle,2", "pi32", "deg", "60°x", Color(0xFFFFFFFF)}
for _, value := range failAngleValues {
if list.setAngleProperty("angle", value) {
t.Errorf(`setAngleProperty("angle", %v) success`, value)
}
}
colorValues := []interface{}{"@color", "#FF234567", "#234567", "rgba(30%, 128, 0.5, .25)", "rgb(30%, 128, 0.5)", Color(0xFFFFFFFF), 0xFFFFFFFF, White}
colorValues := []any{"@color", "#FF234567", "#234567", "rgba(30%, 128, 0.5, .25)", "rgb(30%, 128, 0.5)", Color(0xFFFFFFFF), 0xFFFFFFFF, White}
for _, color := range colorValues {
if !list.setColorProperty("color", color) {
t.Errorf(`list.setColorProperty("color", %v) fail`, color)
}
}
failColorValues := []interface{}{"@color|2", "#FF234567FF", "#TT234567", "rgba(500%, 128, 10.5, .25)", 10.6}
failColorValues := []any{"@color|2", "#FF234567FF", "#TT234567", "rgba(500%, 128, 10.5, .25)", 10.6}
for _, color := range failColorValues {
if list.setColorProperty("color", color) {
t.Errorf(`list.setColorProperty("color", %v) success`, color)
}
}
enumValues := []interface{}{"@enum", "inherit", "on", Inherit, 2}
enumValues := []any{"@enum", "inherit", "on", Inherit, 2}
inheritOffOn := inheritOffOnValues()
for _, value := range enumValues {
if !list.setEnumProperty("enum", value, inheritOffOn) {
@ -82,56 +82,56 @@ func TestProperties(t *testing.T) {
}
}
failEnumValues := []interface{}{"@enum 13", "inherit2", "onn", -1, 10}
failEnumValues := []any{"@enum 13", "inherit2", "onn", -1, 10}
for _, value := range failEnumValues {
if list.setEnumProperty("enum", value, inheritOffOn) {
t.Errorf(`list.setEnumProperty("enum", %v, %v) success`, value, inheritOffOn)
}
}
boolValues := []interface{}{"@bool", "true", "yes ", "on", " 1", "false", "no", "off", "0", 0, 1, false, true}
boolValues := []any{"@bool", "true", "yes ", "on", " 1", "false", "no", "off", "0", 0, 1, false, true}
for _, value := range boolValues {
if !list.setBoolProperty("bool", value) {
t.Errorf(`list.setBoolProperty("bool", %v) fail`, value)
}
}
failBoolValues := []interface{}{"@bool,2", "tr", "ys", "10", -1, 10, 0.8}
failBoolValues := []any{"@bool,2", "tr", "ys", "10", -1, 10, 0.8}
for _, value := range failBoolValues {
if list.setBoolProperty("bool", value) {
t.Errorf(`list.setBoolProperty("bool", %v) success`, value)
}
}
intValues := []interface{}{"@int", " 100", "-10 ", 0, 250}
intValues := []any{"@int", " 100", "-10 ", 0, 250}
for _, value := range intValues {
if !list.setIntProperty("int", value) {
t.Errorf(`list.setIntProperty("int", %v) fail`, value)
}
}
failIntValues := []interface{}{"@int|10", "100i", "-1.0 ", 0.0}
failIntValues := []any{"@int|10", "100i", "-1.0 ", 0.0}
for _, value := range failIntValues {
if list.setIntProperty("int", value) {
t.Errorf(`list.setIntProperty("int", %v) success`, value)
}
}
floatValues := []interface{}{"@float", " 100.25", "-1.5e12 ", uint(0), 250, float32(10.2), float64(0)}
floatValues := []any{"@float", " 100.25", "-1.5e12 ", uint(0), 250, float32(10.2), float64(0)}
for _, value := range floatValues {
if !list.setFloatProperty("float", value) {
t.Errorf(`list.setFloatProperty("float", %v) fail`, value)
}
}
failFloatValues := []interface{}{"@float|2", " 100.25i", "-1.5ee12 ", "abc"}
failFloatValues := []any{"@float|2", " 100.25i", "-1.5ee12 ", "abc"}
for _, value := range failFloatValues {
if list.setFloatProperty("float", value) {
t.Errorf(`list.setFloatProperty("float", %v) success`, value)
}
}
boundsValues := []interface{}{"@bounds", "10px,20pt,@bottom,0", Em(2), []interface{}{"@top", Px(10), AutoSize(), "14pt"}}
boundsValues := []any{"@bounds", "10px,20pt,@bottom,0", Em(2), []any{"@top", Px(10), AutoSize(), "14pt"}}
for _, value := range boundsValues {
if !list.setBoundsProperty("margin", value) {
t.Errorf(`list.setBoundsProperty("margin", %v) fail`, value)

View File

@ -1,6 +1,7 @@
package rui
import (
"fmt"
"strconv"
"strings"
)
@ -31,16 +32,29 @@ func imageProperty(properties Properties, tag string, session Session) (string,
return "", false
}
func valueToSizeUnit(value interface{}, session Session) (SizeUnit, bool) {
func valueToSizeUnit(value any, session Session) (SizeUnit, bool) {
if value != nil {
switch value := value.(type) {
case SizeUnit:
return value, true
case SizeFunc:
return SizeUnit{Type: SizeFunction, Function: value}, true
case string:
if text, ok := session.resolveConstants(value); ok {
return StringToSizeUnit(text)
}
case float64:
return Px(value), true
case float32:
return Px(float64(value)), true
}
if n, ok := isInt(value); ok {
return Px(float64(n)), true
}
}
@ -67,7 +81,7 @@ func angleProperty(properties Properties, tag string, session Session) (AngleUni
return AngleUnit{Type: 0, Value: 0}, false
}
func valueToColor(value interface{}, session Session) (Color, bool) {
func valueToColor(value any, session Session) (Color, bool) {
if value != nil {
switch value := value.(type) {
case Color:
@ -88,7 +102,7 @@ func colorProperty(properties Properties, tag string, session Session) (Color, b
return valueToColor(properties.getRaw(tag), session)
}
func valueToEnum(value interface{}, tag string, session Session, defaultValue int) (int, bool) {
func valueToEnum(value any, tag string, session Session, defaultValue int) (int, bool) {
if value != nil {
values := enumProperties[tag].values
switch value := value.(type) {
@ -155,7 +169,7 @@ func enumProperty(properties Properties, tag string, session Session, defaultVal
return valueToEnum(properties.getRaw(tag), tag, session, defaultValue)
}
func valueToBool(value interface{}, session Session) (bool, bool) {
func valueToBool(value any, session Session) (bool, bool) {
if value != nil {
switch value := value.(type) {
case bool:
@ -184,7 +198,7 @@ func boolProperty(properties Properties, tag string, session Session) (bool, boo
return valueToBool(properties.getRaw(tag), session)
}
func valueToInt(value interface{}, session Session, defaultValue int) (int, bool) {
func valueToInt(value any, session Session, defaultValue int) (int, bool) {
if value != nil {
switch value := value.(type) {
case string:
@ -214,7 +228,7 @@ func intProperty(properties Properties, tag string, session Session, defaultValu
return valueToInt(properties.getRaw(tag), session, defaultValue)
}
func valueToFloat(value interface{}, session Session, defaultValue float64) (float64, bool) {
func valueToFloat(value any, session Session, defaultValue float64) (float64, bool) {
if value != nil {
switch value := value.(type) {
case float64:
@ -238,7 +252,31 @@ func floatProperty(properties Properties, tag string, session Session, defaultVa
return valueToFloat(properties.getRaw(tag), session, defaultValue)
}
func valueToRange(value interface{}, session Session) (Range, bool) {
func valueToFloatText(value any, session Session, defaultValue float64) (string, bool) {
if value != nil {
switch value := value.(type) {
case float64:
return fmt.Sprintf("%g", value), true
case string:
if text, ok := session.resolveConstants(value); ok {
if _, err := strconv.ParseFloat(text, 64); err != nil {
ErrorLog(err.Error())
return fmt.Sprintf("%g", defaultValue), false
}
return text, true
}
}
}
return fmt.Sprintf("%g", defaultValue), false
}
func floatTextProperty(properties Properties, tag string, session Session, defaultValue float64) (string, bool) {
return valueToFloatText(properties.getRaw(tag), session, defaultValue)
}
func valueToRange(value any, session Session) (Range, bool) {
if value != nil {
switch value := value.(type) {
case Range:

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 = "opacity"
// Overflow is the constant for the "overflow" property tag.
// The "overflow" int property sets the desired behavior for an element's overflow — i.e.
// when an element's content is too big to fit in its block formatting context — in both directions.
// Valid values: OverflowHidden (0), OverflowVisible (1), OverflowScroll (2), OverflowAuto (3)
Overflow = "overflow"
// Row is the constant for the "row" property tag.
Row = "row"
@ -141,6 +147,10 @@ const (
// The "padding-bottom" SizeUnit property sets the height of the padding area to the bottom of a view.
PaddingBottom = "padding-bottom"
// AccentColor is the constant for the "accent-color" property tag.
// The "accent-color" property sets sets the accent color for UI controls generated by some elements.
AccentColor = "accent-color"
// BackgroundColor is the constant for the "background-color" property tag.
// The "background-color" property sets the background color of a view.
BackgroundColor = "background-color"
@ -314,20 +324,20 @@ const (
// This is an inherited property, i.e. if it is not defined, then the value of the parent view is used.
Underline = "underline"
// TextLineThickness is the constant for the "text-decoration-thickness" property tag.
// The "text-decoration-thickness" SizeUnit property sets the stroke thickness of the decoration line that
// TextLineThickness is the constant for the "text-line-thickness" property tag.
// The "text-line-thickness" SizeUnit property sets the stroke thickness of the decoration line that
// is used on text in an element, such as a line-through, underline, or overline.
// This is an inherited property, i.e. if it is not defined, then the value of the parent view is used.
TextLineThickness = "text-line-thickness"
// TextLineStyle is the constant for the "text-decoration-style" property tag.
// The "text-decoration-style" int property sets the style of the lines specified by "text-decoration" property.
// TextLineStyle is the constant for the "text-line-style" property tag.
// The "text-line-style" int property sets the style of the lines specified by "text-decoration" property.
// The style applies to all lines that are set with "text-decoration" property.
// This is an inherited property, i.e. if it is not defined, then the value of the parent view is used.
TextLineStyle = "text-line-style"
// TextLineColor is the constant for the "text-decoration-color" property tag.
// The "text-decoration-color" Color property sets the color of the lines specified by "text-decoration" property.
// TextLineColor is the constant for the "text-line-color" property tag.
// The "text-line-color" Color property sets the color of the lines specified by "text-decoration" property.
// The color applies to all lines that are set with "text-decoration" property.
// This is an inherited property, i.e. if it is not defined, then the value of the parent view is used.
TextLineColor = "text-line-color"
@ -349,6 +359,11 @@ const (
// This is an inherited property, i.e. if it is not defined, then the value of the parent view is used.
TextShadow = "text-shadow"
// TabSize is the constant for the "tab-size" property tag.
// The "tab-size" int property sets the width of tab characters (U+0009) in spaces.
// This is an inherited property, i.e. if it is not defined, then the value of the parent view is used.
TabSize = "tab-size"
// LetterSpacing is the constant for the "letter-spacing" property tag.
// The "letter-spacing" SizeUnit property sets the horizontal spacing behavior between text characters.
// This value is added to the natural spacing between characters while rendering the text.
@ -553,9 +568,19 @@ const (
// Orientation is the constant for the "orientation" property tag.
Orientation = "orientation"
// Gap is the constant for the "gap" property tag.
// Gap is t he constant for the "gap" property tag.
Gap = "gap"
// ListRowGap is the constant for the "list-row-gap" property tag.
// The "list-row-gap" SizeUnit properties allow to set the distance between the rows of the ListLayout or ListView.
// The default is 0px.
ListRowGap = "list-row-gap"
// ListColumnGap is the constant for the "list-column-gap" property tag.
// The "list-column-gap" SizeUnit properties allow to set the distance between the columns of the GridLayout or ListView.
// The default is 0px.
ListColumnGap = "list-column-gap"
// Text is the constant for the "text" property tag.
Text = "text"

View File

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

View File

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

View File

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

View File

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

View File

@ -3,9 +3,12 @@ package rui
// ResizeEvent is the constant for "resize-event" property tag.
// The "resize-event" is fired when the view changes its size.
// The main listener format:
// func(View, Frame).
//
// func(View, Frame).
//
// The additional listener formats:
// func(Frame), func(View), and func().
//
// func(Frame), func(View), and func().
const ResizeEvent = "resize-event"
func (view *viewData) onResize(self View, x, y, width, height float64) {
@ -13,7 +16,7 @@ func (view *viewData) onResize(self View, x, y, width, height float64) {
view.frame.Top = y
view.frame.Width = width
view.frame.Height = height
for _, listener := range GetResizeListeners(view, "") {
for _, listener := range GetResizeListeners(view) {
listener(self, view.frame)
}
}
@ -21,147 +24,19 @@ func (view *viewData) onResize(self View, x, y, width, height float64) {
func (view *viewData) onItemResize(self View, index string, x, y, width, height float64) {
}
func (view *viewData) setFrameListener(tag string, value interface{}) bool {
if value == nil {
delete(view.properties, tag)
return true
}
switch value := value.(type) {
case func(View, Frame):
view.properties[tag] = []func(View, Frame){value}
case []func(View, Frame):
if len(value) > 0 {
view.properties[tag] = value
} else {
delete(view.properties, tag)
return true
}
case func(Frame):
fn := func(_ View, frame Frame) {
value(frame)
}
view.properties[tag] = []func(View, Frame){fn}
case []func(Frame):
count := len(value)
if count == 0 {
delete(view.properties, tag)
return true
}
listeners := make([]func(View, Frame), count)
for i, val := range value {
if val == nil {
notCompatibleType(tag, val)
return false
}
listeners[i] = func(_ View, frame Frame) {
val(frame)
}
}
view.properties[tag] = listeners
case func(View):
fn := func(view View, _ Frame) {
value(view)
}
view.properties[tag] = []func(View, Frame){fn}
case []func(View):
count := len(value)
if count == 0 {
delete(view.properties, tag)
return true
}
listeners := make([]func(View, Frame), count)
for i, val := range value {
if val == nil {
notCompatibleType(tag, val)
return false
}
listeners[i] = func(view View, _ Frame) {
val(view)
}
}
view.properties[tag] = listeners
case func():
fn := func(View, Frame) {
value()
}
view.properties[tag] = []func(View, Frame){fn}
case []func():
count := len(value)
if count == 0 {
delete(view.properties, tag)
return true
}
listeners := make([]func(View, Frame), count)
for i, val := range value {
if val == nil {
notCompatibleType(tag, val)
return false
}
listeners[i] = func(View, Frame) {
val()
}
}
view.properties[tag] = listeners
case []interface{}:
count := len(value)
if count == 0 {
delete(view.properties, tag)
return true
}
listeners := make([]func(View, Frame), count)
for i, val := range value {
if val == nil {
notCompatibleType(tag, val)
return false
}
switch val := val.(type) {
case func(View, Frame):
listeners[i] = val
case func(Frame):
listeners[i] = func(_ View, frame Frame) {
val(frame)
}
case func(View):
listeners[i] = func(view View, _ Frame) {
val(view)
}
case func():
listeners[i] = func(View, Frame) {
val()
}
default:
notCompatibleType(tag, val)
return false
}
}
view.properties[tag] = listeners
default:
func (view *viewData) setFrameListener(tag string, value any) bool {
listeners, ok := valueToEventListeners[View, Frame](value)
if !ok {
notCompatibleType(tag, value)
return false
}
if listeners == nil {
delete(view.properties, tag)
} else {
view.properties[tag] = listeners
}
view.propertyChangedEvent(tag)
return true
}
@ -190,10 +65,10 @@ func (view *viewData) Frame() Frame {
}
// GetViewFrame returns the size and location of view's viewport.
// If the second argument (subviewID) is "" then the value of the first argument (view) is returned
func GetViewFrame(view View, subviewID string) Frame {
if subviewID != "" {
view = ViewByID(view, subviewID)
// If the second argument (subviewID) is not specified or it is "" then the value of the first argument (view) is returned
func GetViewFrame(view View, subviewID ...string) Frame {
if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0])
}
if view == nil {
return Frame{}
@ -202,17 +77,7 @@ func GetViewFrame(view View, subviewID string) Frame {
}
// GetResizeListeners returns the list of "resize-event" listeners. If there are no listeners then the empty list is returned
// If the second argument (subviewID) is "" then the listeners list of the first argument (view) is returned
func GetResizeListeners(view View, subviewID string) []func(View, Frame) {
if subviewID != "" {
view = ViewByID(view, subviewID)
}
if view != nil {
if value := view.Get(ResizeEvent); value != nil {
if result, ok := value.([]func(View, Frame)); ok {
return result
}
}
}
return []func(View, Frame){}
// If the second argument (subviewID) is not specified or it is "" then the listeners list of the first argument (view) is returned
func GetResizeListeners(view View, subviewID ...string) []func(View, Frame) {
return getEventListeners[View, Frame](view, subviewID, ResizeEvent)
}

View File

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

View File

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

View File

@ -3,11 +3,14 @@ package rui
import "fmt"
// ScrollEvent is the constant for "scroll-event" property tag.
// The "resize-event" is fired when the content of the view is scrolled.
// The "scroll-event" is fired when the content of the view is scrolled.
// The main listener format:
// func(View, Frame).
//
// func(View, Frame).
//
// The additional listener formats:
// func(Frame), func(View), and func().
//
// func(Frame), func(View), and func().
const ScrollEvent = "scroll-event"
func (view *viewData) onScroll(self View, x, y, width, height float64) {
@ -15,7 +18,7 @@ func (view *viewData) onScroll(self View, x, y, width, height float64) {
view.scroll.Top = y
view.scroll.Width = width
view.scroll.Height = height
for _, listener := range GetScrollListeners(view, "") {
for _, listener := range GetScrollListeners(view) {
listener(self, view.scroll)
}
}
@ -32,10 +35,10 @@ func (view *viewData) setScroll(x, y, width, height float64) {
}
// GetViewScroll returns ...
// If the second argument (subviewID) is "" then a value of the first argument (view) is returned
func GetViewScroll(view View, subviewID string) Frame {
if subviewID != "" {
view = ViewByID(view, subviewID)
// If the second argument (subviewID) is not specified or it is "" then a value of the first argument (view) is returned
func GetViewScroll(view View, subviewID ...string) Frame {
if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0])
}
if view == nil {
return Frame{}
@ -44,19 +47,9 @@ func GetViewScroll(view View, subviewID string) Frame {
}
// GetScrollListeners returns the list of "scroll-event" listeners. If there are no listeners then the empty list is returned
// If the second argument (subviewID) is "" then the listeners list of the first argument (view) is returned
func GetScrollListeners(view View, subviewID string) []func(View, Frame) {
if subviewID != "" {
view = ViewByID(view, subviewID)
}
if view != nil {
if value := view.Get(ScrollEvent); value != nil {
if result, ok := value.([]func(View, Frame)); ok {
return result
}
}
}
return []func(View, Frame){}
// If the second argument (subviewID) is not specified or it is "" then the listeners list of the first argument (view) is returned
func GetScrollListeners(view View, subviewID ...string) []func(View, Frame) {
return getEventListeners[View, Frame](view, subviewID, ResizeEvent)
}
// ScrollTo scrolls the view's content to the given position.
@ -71,10 +64,10 @@ func ScrollViewTo(view View, subviewID string, x, y float64) {
}
// ScrollViewToEnd scrolls the view's content to the start of view.
// If the second argument (subviewID) is "" then the first argument (view) is used
func ScrollViewToStart(view View, subviewID string) {
if subviewID != "" {
view = ViewByID(view, subviewID)
// If the second argument (subviewID) is not specified or it is "" then the first argument (view) is used
func ScrollViewToStart(view View, subviewID ...string) {
if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0])
}
if view != nil {
view.Session().runScript(`scrollToStart("` + view.htmlID() + `")`)
@ -82,10 +75,10 @@ func ScrollViewToStart(view View, subviewID string) {
}
// ScrollViewToEnd scrolls the view's content to the end of view.
// If the second argument (subviewID) is "" then the first argument (view) is used
func ScrollViewToEnd(view View, subviewID string) {
if subviewID != "" {
view = ViewByID(view, subviewID)
// If the second argument (subviewID) is not specified or it is "" then the first argument (view) is used
func ScrollViewToEnd(view View, subviewID ...string) {
if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0])
}
if view != nil {
view.Session().runScript(`scrollToEnd("` + view.htmlID() + `")`)

View File

@ -60,11 +60,11 @@ type Session interface {
RootView() View
// Get returns a value of the view (with id defined by the first argument) property with name defined by the second argument.
// The type of return value depends on the property. If the property is not set then nil is returned.
Get(viewID, tag string) interface{}
Get(viewID, tag string) any
// Set sets the value (third argument) of the property (second argument) of the view with id defined by the first argument.
// Return "true" if the value has been set, in the opposite case "false" are returned and
// a description of the error is written to the log
Set(viewID, tag string, value interface{}) bool
Set(viewID, tag string, value any) bool
// DownloadFile downloads (saves) on the client side the file located at the specified path on the server.
DownloadFile(path string)
@ -83,7 +83,7 @@ type Session interface {
viewByHTMLID(id string) View
nextViewID() string
styleProperty(styleTag, property string) interface{}
styleProperty(styleTag, property string) any
setBrige(events chan DataObject, brige WebBrige)
writeInitScript(writer *strings.Builder)
@ -222,7 +222,7 @@ func (session *sessionData) close() {
}
}
func (session *sessionData) styleProperty(styleTag, propertyTag string) interface{} {
func (session *sessionData) styleProperty(styleTag, propertyTag string) any {
if style := session.getCurrentTheme().style(styleTag); style != nil {
return style.getRaw(propertyTag)
}
@ -312,14 +312,14 @@ func (session *sessionData) setIgnoreViewUpdates(ignore bool) {
session.ignoreUpdates = ignore
}
func (session *sessionData) Get(viewID, tag string) interface{} {
func (session *sessionData) Get(viewID, tag string) any {
if view := ViewByID(session.RootView(), viewID); view != nil {
return view.Get(tag)
}
return nil
}
func (session *sessionData) Set(viewID, tag string, value interface{}) bool {
func (session *sessionData) Set(viewID, tag string, value any) bool {
if view := ViewByID(session.RootView(), viewID); view != nil {
return view.Set(tag, value)
}
@ -434,7 +434,7 @@ func (session *sessionData) handleViewEvent(command string, data DataObject) {
if view := session.viewByHTMLID(viewID); view != nil {
view.handleCommand(view, command, data)
}
} else {
} else if command != "clickOutsidePopup" {
ErrorLog(`"id" property not found. Event: ` + command)
}
}

View File

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

View File

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

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
const (
// Auto - default value.
// Auto is the SizeUnit type: default value.
Auto SizeUnitType = 0
// SizeInPixel - size in pixels.
// SizeInPixel is the SizeUnit type: the Value field specifies the size in pixels.
SizeInPixel SizeUnitType = 1
// SizeInEM - size in em.
// SizeInEM is the SizeUnit type: the Value field specifies the size in em.
SizeInEM SizeUnitType = 2
// SizeInEX - size in em.
// SizeInEX is the SizeUnit type: the Value field specifies the size in em.
SizeInEX SizeUnitType = 3
// SizeInPercent - size in percents of a parant size.
// SizeInPercent is the SizeUnit type: the Value field specifies the size in percents of the parent size.
SizeInPercent SizeUnitType = 4
// SizeInPt - size in pt (1/72 inch).
// SizeInPt is the SizeUnit type: the Value field specifies the size in pt (1/72 inch).
SizeInPt SizeUnitType = 5
// SizeInPc - size in pc (1pc = 12pt).
// SizeInPc is the SizeUnit type: the Value field specifies the size in pc (1pc = 12pt).
SizeInPc SizeUnitType = 6
// SizeInInch - size in inches.
// SizeInInch is the SizeUnit type: the Value field specifies the size in inches.
SizeInInch SizeUnitType = 7
// SizeInMM - size in millimeters.
// SizeInMM is the SizeUnit type: the Value field specifies the size in millimeters.
SizeInMM SizeUnitType = 8
// SizeInCM - size in centimeters.
// SizeInCM is the SizeUnit type: the Value field specifies the size in centimeters.
SizeInCM SizeUnitType = 9
// SizeInFraction - size in fraction. Used only for "cell-width" and "cell-height" property
// SizeInFraction is the SizeUnit type: the Value field specifies the size in fraction.
// Used only for "cell-width" and "cell-height" property.
SizeInFraction SizeUnitType = 10
// SizeFunction is the SizeUnit type: the Function field specifies the size function.
// "min", "max", "clamp", "sum", "sub", "mul", and "div" functions are available.
SizeFunction = 11
)
// SizeUnit describe a size (Value field) and size unit (Type field).
type SizeUnit struct {
Type SizeUnitType
Value float64
Type SizeUnitType
Value float64
Function SizeFunc
}
// AutoSize creates SizeUnit with Auto type
func AutoSize() SizeUnit {
return SizeUnit{Auto, 0}
return SizeUnit{Auto, 0, nil}
}
// Px creates SizeUnit with SizeInPixel type
func Px(value float64) SizeUnit {
return SizeUnit{SizeInPixel, value}
return SizeUnit{SizeInPixel, value, nil}
}
// Em creates SizeUnit with SizeInEM type
func Em(value float64) SizeUnit {
return SizeUnit{SizeInEM, value}
return SizeUnit{SizeInEM, value, nil}
}
// Ex creates SizeUnit with SizeInEX type
func Ex(value float64) SizeUnit {
return SizeUnit{SizeInEX, value}
return SizeUnit{SizeInEX, value, nil}
}
// Percent creates SizeUnit with SizeInDIP type
func Percent(value float64) SizeUnit {
return SizeUnit{SizeInPercent, value}
return SizeUnit{SizeInPercent, value, nil}
}
// Pt creates SizeUnit with SizeInPt type
func Pt(value float64) SizeUnit {
return SizeUnit{SizeInPt, value}
return SizeUnit{SizeInPt, value, nil}
}
// Pc creates SizeUnit with SizeInPc type
func Pc(value float64) SizeUnit {
return SizeUnit{SizeInPc, value}
return SizeUnit{SizeInPc, value, nil}
}
// Mm creates SizeUnit with SizeInMM type
func Mm(value float64) SizeUnit {
return SizeUnit{SizeInMM, value}
return SizeUnit{SizeInMM, value, nil}
}
// Cm creates SizeUnit with SizeInCM type
func Cm(value float64) SizeUnit {
return SizeUnit{SizeInCM, value}
return SizeUnit{SizeInCM, value, nil}
}
// Inch creates SizeUnit with SizeInInch type
func Inch(value float64) SizeUnit {
return SizeUnit{SizeInInch, value}
return SizeUnit{SizeInInch, value, nil}
}
// Fr creates SizeUnit with SizeInFraction type
func Fr(value float64) SizeUnit {
return SizeUnit{SizeInFraction, value}
return SizeUnit{SizeInFraction, value, nil}
}
// Equal compare two SizeUnit. Return true if SizeUnit are equal
@ -152,13 +157,24 @@ func stringToSizeUnit(value string) (SizeUnit, error) {
}
}
if val, err := strconv.ParseFloat(value, 64); err == nil {
return SizeUnit{Type: SizeInPixel, Value: val}, nil
}
return SizeUnit{Type: Auto, Value: 0}, errors.New(`Invalid SizeUnit value: "` + value + `"`)
}
// String - convert SizeUnit to string
func (size SizeUnit) String() string {
if size.Type == Auto {
switch size.Type {
case Auto:
return "auto"
case SizeFunction:
if size.Function == nil {
return "auto"
}
return size.Function.String()
}
if suffix, ok := sizeUnitSuffixes()[size.Type]; ok {
return fmt.Sprintf("%g%s", size.Value, suffix)
@ -167,13 +183,19 @@ func (size SizeUnit) String() string {
}
// cssString - convert SizeUnit to string
func (size SizeUnit) cssString(textForAuto string) string {
func (size SizeUnit) cssString(textForAuto string, session Session) string {
switch size.Type {
case Auto:
return textForAuto
case SizeInEM:
return fmt.Sprintf("%grem", size.Value)
case SizeFunction:
if size.Function == nil {
return textForAuto
}
return size.Function.cssString(session)
}
if size.Value == 0 {

View File

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

View File

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

View File

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

View File

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

View File

@ -2,11 +2,11 @@ package rui
import "strings"
func (cell *tableCellView) Set(tag string, value interface{}) bool {
func (cell *tableCellView) Set(tag string, value any) bool {
return cell.set(strings.ToLower(tag), value)
}
func (cell *tableCellView) set(tag string, value interface{}) bool {
func (cell *tableCellView) set(tag string, value any) bool {
switch tag {
case VerticalAlign:
tag = TableVerticalAlign
@ -24,10 +24,10 @@ func (cell *tableCellView) cssStyle(self View, builder cssBuilder) {
}
// GetTableContent returns a TableAdapter which defines the TableView content.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetTableContent(view View, subviewID string) TableAdapter {
if subviewID != "" {
view = ViewByID(view, subviewID)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTableContent(view View, subviewID ...string) TableAdapter {
if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0])
}
if view != nil {
@ -40,10 +40,10 @@ func GetTableContent(view View, subviewID string) TableAdapter {
}
// GetTableRowStyle returns a TableRowStyle which defines styles of TableView rows.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetTableRowStyle(view View, subviewID string) TableRowStyle {
if subviewID != "" {
view = ViewByID(view, subviewID)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTableRowStyle(view View, subviewID ...string) TableRowStyle {
if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0])
}
if view != nil {
@ -56,10 +56,10 @@ func GetTableRowStyle(view View, subviewID string) TableRowStyle {
}
// GetTableColumnStyle returns a TableColumnStyle which defines styles of TableView columns.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetTableColumnStyle(view View, subviewID string) TableColumnStyle {
if subviewID != "" {
view = ViewByID(view, subviewID)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTableColumnStyle(view View, subviewID ...string) TableColumnStyle {
if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0])
}
if view != nil {
@ -72,10 +72,10 @@ func GetTableColumnStyle(view View, subviewID string) TableColumnStyle {
}
// GetTableCellStyle returns a TableCellStyle which defines styles of TableView cells.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetTableCellStyle(view View, subviewID string) TableCellStyle {
if subviewID != "" {
view = ViewByID(view, subviewID)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTableCellStyle(view View, subviewID ...string) TableCellStyle {
if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0])
}
if view != nil {
@ -89,72 +89,42 @@ func GetTableCellStyle(view View, subviewID string) TableCellStyle {
// GetTableSelectionMode returns the mode of the TableView elements selection.
// Valid values are NoneSelection (0), CellSelection (1), and RowSelection (2).
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetTableSelectionMode(view View, subviewID string) int {
if subviewID != "" {
view = ViewByID(view, subviewID)
}
if view != nil {
if result, ok := enumStyledProperty(view, SelectionMode, NoneSelection); ok {
return result
}
}
return NoneSelection
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTableSelectionMode(view View, subviewID ...string) int {
return enumStyledProperty(view, subviewID, SelectionMode, NoneSelection, false)
}
// GetTableVerticalAlign returns a vertical align in a TavleView cell. Returns one of next values:
// TopAlign (0), BottomAlign (1), CenterAlign (2), and BaselineAlign (3)
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetTableVerticalAlign(view View, subviewID string) int {
if subviewID != "" {
view = ViewByID(view, subviewID)
}
if view != nil {
if result, ok := enumStyledProperty(view, TableVerticalAlign, TopAlign); ok {
return result
}
}
return TopAlign
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTableVerticalAlign(view View, subviewID ...string) int {
return enumStyledProperty(view, subviewID, TableVerticalAlign, TopAlign, false)
}
// GetTableHeadHeight returns the number of rows in the table header.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetTableHeadHeight(view View, subviewID string) int {
if subviewID != "" {
view = ViewByID(view, subviewID)
}
if view != nil {
headHeight, _ := intStyledProperty(view, HeadHeight, 0)
return headHeight
}
return 0
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTableHeadHeight(view View, subviewID ...string) int {
return intStyledProperty(view, subviewID, HeadHeight, 0)
}
// GetTableFootHeight returns the number of rows in the table footer.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetTableFootHeight(view View, subviewID string) int {
if subviewID != "" {
view = ViewByID(view, subviewID)
}
if view != nil {
headHeight, _ := intStyledProperty(view, FootHeight, 0)
return headHeight
}
return 0
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTableFootHeight(view View, subviewID ...string) int {
return intStyledProperty(view, subviewID, FootHeight, 0)
}
// GetTableCurrent returns the row and column index of the TableView selected cell/row.
// If there is no selected cell/row or the selection mode is NoneSelection (0),
// then a value of the row and column index less than 0 is returned.
// If the selection mode is RowSelection (2) then the returned column index is less than 0.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetTableCurrent(view View, subviewID string) CellIndex {
if subviewID != "" {
view = ViewByID(view, subviewID)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTableCurrent(view View, subviewID ...string) CellIndex {
if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0])
}
if view != nil {
if selectionMode := GetTableSelectionMode(view, ""); selectionMode != NoneSelection {
if selectionMode := GetTableSelectionMode(view); selectionMode != NoneSelection {
if tableView, ok := view.(TableView); ok {
return tableView.getCurrent()
}
@ -165,10 +135,10 @@ func GetTableCurrent(view View, subviewID string) CellIndex {
// GetTableCellClickedListeners returns listeners of event which occurs when the user clicks on a table cell.
// If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetTableCellClickedListeners(view View, subviewID string) []func(TableView, int, int) {
if subviewID != "" {
view = ViewByID(view, subviewID)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTableCellClickedListeners(view View, subviewID ...string) []func(TableView, int, int) {
if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0])
}
if view != nil {
if value := view.Get(TableCellClickedEvent); value != nil {
@ -182,10 +152,10 @@ func GetTableCellClickedListeners(view View, subviewID string) []func(TableView,
// GetTableCellSelectedListeners returns listeners of event which occurs when a table cell becomes selected.
// If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetTableCellSelectedListeners(view View, subviewID string) []func(TableView, int, int) {
if subviewID != "" {
view = ViewByID(view, subviewID)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTableCellSelectedListeners(view View, subviewID ...string) []func(TableView, int, int) {
if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0])
}
if view != nil {
if value := view.Get(TableCellSelectedEvent); value != nil {
@ -199,43 +169,23 @@ func GetTableCellSelectedListeners(view View, subviewID string) []func(TableView
// GetTableRowClickedListeners returns listeners of event which occurs when the user clicks on a table row.
// If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetTableRowClickedListeners(view View, subviewID string) []func(TableView, int) {
if subviewID != "" {
view = ViewByID(view, subviewID)
}
if view != nil {
if value := view.Get(TableRowClickedEvent); value != nil {
if result, ok := value.([]func(TableView, int)); ok {
return result
}
}
}
return []func(TableView, int){}
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTableRowClickedListeners(view View, subviewID ...string) []func(TableView, int) {
return getEventListeners[TableView, int](view, subviewID, TableRowClickedEvent)
}
// GetTableRowSelectedListeners returns listeners of event which occurs when a table row becomes selected.
// If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetTableRowSelectedListeners(view View, subviewID string) []func(TableView, int) {
if subviewID != "" {
view = ViewByID(view, subviewID)
}
if view != nil {
if value := view.Get(TableRowSelectedEvent); value != nil {
if result, ok := value.([]func(TableView, int)); ok {
return result
}
}
}
return []func(TableView, int){}
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTableRowSelectedListeners(view View, subviewID ...string) []func(TableView, int) {
return getEventListeners[TableView, int](view, subviewID, TableRowSelectedEvent)
}
// ReloadTableViewData updates TableView
func ReloadTableViewData(view View, subviewID string) bool {
func ReloadTableViewData(view View, subviewID ...string) bool {
var tableView TableView
if subviewID != "" {
if tableView = TableViewByID(view, subviewID); tableView == nil {
if len(subviewID) > 0 && subviewID[0] != "" {
if tableView = TableViewByID(view, subviewID[0]); tableView == nil {
return false
}
} else {

View File

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

View File

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

View File

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

View File

@ -90,131 +90,6 @@ type TouchEvent struct {
MetaKey bool
}
func valueToTouchListeners(value interface{}) ([]func(View, TouchEvent), bool) {
if value == nil {
return nil, true
}
switch value := value.(type) {
case func(View, TouchEvent):
return []func(View, TouchEvent){value}, true
case func(TouchEvent):
fn := func(_ View, event TouchEvent) {
value(event)
}
return []func(View, TouchEvent){fn}, true
case func(View):
fn := func(view View, _ TouchEvent) {
value(view)
}
return []func(View, TouchEvent){fn}, true
case func():
fn := func(View, TouchEvent) {
value()
}
return []func(View, TouchEvent){fn}, true
case []func(View, TouchEvent):
if len(value) == 0 {
return nil, true
}
for _, fn := range value {
if fn == nil {
return nil, false
}
}
return value, true
case []func(TouchEvent):
count := len(value)
if count == 0 {
return nil, true
}
listeners := make([]func(View, TouchEvent), count)
for i, v := range value {
if v == nil {
return nil, false
}
listeners[i] = func(_ View, event TouchEvent) {
v(event)
}
}
return listeners, true
case []func(View):
count := len(value)
if count == 0 {
return nil, true
}
listeners := make([]func(View, TouchEvent), count)
for i, v := range value {
if v == nil {
return nil, false
}
listeners[i] = func(view View, _ TouchEvent) {
v(view)
}
}
return listeners, true
case []func():
count := len(value)
if count == 0 {
return nil, true
}
listeners := make([]func(View, TouchEvent), count)
for i, v := range value {
if v == nil {
return nil, false
}
listeners[i] = func(View, TouchEvent) {
v()
}
}
return listeners, true
case []interface{}:
count := len(value)
if count == 0 {
return nil, true
}
listeners := make([]func(View, TouchEvent), count)
for i, v := range value {
if v == nil {
return nil, false
}
switch v := v.(type) {
case func(View, TouchEvent):
listeners[i] = v
case func(TouchEvent):
listeners[i] = func(_ View, event TouchEvent) {
v(event)
}
case func(View):
listeners[i] = func(view View, _ TouchEvent) {
v(view)
}
case func():
listeners[i] = func(View, TouchEvent) {
v()
}
default:
return nil, false
}
}
return listeners, true
}
return nil, false
}
var touchEvents = map[string]struct{ jsEvent, jsFunc string }{
TouchStart: {jsEvent: "ontouchstart", jsFunc: "touchStartEvent"},
TouchEnd: {jsEvent: "ontouchend", jsFunc: "touchEndEvent"},
@ -222,8 +97,8 @@ var touchEvents = map[string]struct{ jsEvent, jsFunc string }{
TouchCancel: {jsEvent: "ontouchcancel", jsFunc: "touchCancelEvent"},
}
func (view *viewData) setTouchListener(tag string, value interface{}) bool {
listeners, ok := valueToTouchListeners(value)
func (view *viewData) setTouchListener(tag string, value any) bool {
listeners, ok := valueToEventListeners[View, TouchEvent](value)
if !ok {
notCompatibleType(tag, value)
return false
@ -251,20 +126,6 @@ func (view *viewData) removeTouchListener(tag string) {
}
}
func getTouchListeners(view View, subviewID string, tag string) []func(View, TouchEvent) {
if subviewID != "" {
view = ViewByID(view, subviewID)
}
if view != nil {
if value := view.Get(tag); value != nil {
if result, ok := value.([]func(View, TouchEvent)); ok {
return result
}
}
}
return []func(View, TouchEvent){}
}
func touchEventsHtml(view View, buffer *strings.Builder) {
for tag, js := range touchEvents {
if value := view.getRaw(tag); value != nil {
@ -309,7 +170,7 @@ func (event *TouchEvent) init(data DataObject) {
}
func handleTouchEvents(view View, tag string, data DataObject) {
listeners := getTouchListeners(view, "", tag)
listeners := getEventListeners[View, TouchEvent](view, nil, tag)
if len(listeners) == 0 {
return
}
@ -323,25 +184,25 @@ func handleTouchEvents(view View, tag string, data DataObject) {
}
// GetTouchStartListeners returns the "touch-start" listener list. If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetTouchStartListeners(view View, subviewID string) []func(View, TouchEvent) {
return getTouchListeners(view, subviewID, TouchStart)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTouchStartListeners(view View, subviewID ...string) []func(View, TouchEvent) {
return getEventListeners[View, TouchEvent](view, subviewID, TouchStart)
}
// GetTouchEndListeners returns the "touch-end" listener list. If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetTouchEndListeners(view View, subviewID string) []func(View, TouchEvent) {
return getTouchListeners(view, subviewID, TouchEnd)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTouchEndListeners(view View, subviewID ...string) []func(View, TouchEvent) {
return getEventListeners[View, TouchEvent](view, subviewID, TouchEnd)
}
// GetTouchMoveListeners returns the "touch-move" listener list. If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetTouchMoveListeners(view View, subviewID string) []func(View, TouchEvent) {
return getTouchListeners(view, subviewID, TouchMove)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTouchMoveListeners(view View, subviewID ...string) []func(View, TouchEvent) {
return getEventListeners[View, TouchEvent](view, subviewID, TouchMove)
}
// GetTouchCancelListeners returns the "touch-cancel" listener list. If there are no listeners then the empty list is returned.
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
func GetTouchCancelListeners(view View, subviewID string) []func(View, TouchEvent) {
return getTouchListeners(view, subviewID, TouchCancel)
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTouchCancelListeners(view View, subviewID ...string) []func(View, TouchEvent) {
return getEventListeners[View, TouchEvent](view, subviewID, TouchCancel)
}

View File

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

71
view.go
View File

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

View File

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

View File

@ -130,7 +130,7 @@ func newViewFilter(obj DataObject) ViewFilter {
return nil
}
func (filter *viewFilter) Set(tag string, value interface{}) bool {
func (filter *viewFilter) Set(tag string, value any) bool {
if value == nil {
filter.Remove(tag)
return true
@ -180,20 +180,22 @@ func (filter *viewFilter) cssStyle(session Session) string {
buffer := allocStringBuilder()
defer freeStringBuilder(buffer)
if value, ok := floatProperty(filter, Blur, session, 0); ok {
size := SizeUnit{Type: SizeInPixel, Value: value}
if value, ok := floatTextProperty(filter, Blur, session, 0); ok {
buffer.WriteString(Blur)
buffer.WriteRune('(')
buffer.WriteString(size.cssString("0px"))
buffer.WriteRune(')')
buffer.WriteString(value)
buffer.WriteString("px)")
}
for _, tag := range []string{Brightness, Contrast, Saturate, Grayscale, Invert, Opacity, Sepia} {
if value, ok := floatProperty(filter, tag, session, 0); ok {
if value, ok := floatTextProperty(filter, tag, session, 0); ok {
if buffer.Len() > 0 {
buffer.WriteRune(' ')
}
buffer.WriteString(fmt.Sprintf("%s(%g%%)", tag, value))
buffer.WriteString(tag)
buffer.WriteRune('(')
buffer.WriteString(value)
buffer.WriteString("%)")
}
}
@ -224,7 +226,7 @@ func (filter *viewFilter) cssStyle(session Session) string {
return buffer.String()
}
func (style *viewStyle) setFilter(tag string, value interface{}) bool {
func (style *viewStyle) setFilter(tag string, value any) bool {
switch value := value.(type) {
case ViewFilter:
style.properties[tag] = value
@ -257,34 +259,46 @@ func (style *viewStyle) setFilter(tag string, value interface{}) bool {
}
// GetFilter returns a View graphical effects like blur or color shift.
// If the second argument (subviewID) is "" then a top position of the first argument (view) is returned
func GetFilter(view View, subviewID string) ViewFilter {
if subviewID != "" {
view = ViewByID(view, subviewID)
// If the second argument (subviewID) is not specified or it is "" then a top position of the first argument (view) is returned
func GetFilter(view View, subviewID ...string) ViewFilter {
if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0])
}
if view != nil {
if value := view.getRaw(Filter); value != nil {
if filter, ok := value.(ViewFilter); ok {
return filter
}
}
if value := valueFromStyle(view, Filter); value != nil {
if filter, ok := value.(ViewFilter); ok {
return filter
}
}
}
return nil
}
// GetBackdropFilter returns the area behind a View graphical effects like blur or color shift.
// If the second argument (subviewID) is "" then a top position of the first argument (view) is returned
func GetBackdropFilter(view View, subviewID string) ViewFilter {
if subviewID != "" {
view = ViewByID(view, subviewID)
// If the second argument (subviewID) is not specified or it is "" then a top position of the first argument (view) is returned
func GetBackdropFilter(view View, subviewID ...string) ViewFilter {
if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0])
}
if view != nil {
if value := view.getRaw(BackdropFilter); value != nil {
if filter, ok := value.(ViewFilter); ok {
return filter
}
}
if value := valueFromStyle(view, BackdropFilter); value != nil {
if filter, ok := value.(ViewFilter); ok {
return filter
}
}
}
return nil

View File

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

View File

@ -4,7 +4,7 @@ import (
"strings"
)
func (style *viewStyle) setRange(tag string, value interface{}) bool {
func (style *viewStyle) setRange(tag string, value any) bool {
switch value := value.(type) {
case string:
if strings.Contains(value, "@") {
@ -31,7 +31,7 @@ func (style *viewStyle) setRange(tag string, value interface{}) bool {
return true
}
func (style *viewStyle) setBackground(value interface{}) bool {
func (style *viewStyle) setBackground(value any) bool {
switch value := value.(type) {
case BackgroundElement:
style.properties[Background] = []BackgroundElement{value}
@ -122,11 +122,11 @@ func (style *viewStyle) remove(tag string) {
}
}
func (style *viewStyle) Set(tag string, value interface{}) bool {
func (style *viewStyle) Set(tag string, value any) bool {
return style.set(strings.ToLower(tag), value)
}
func (style *viewStyle) set(tag string, value interface{}) bool {
func (style *viewStyle) set(tag string, value any) bool {
if value == nil {
style.remove(tag)
return true

View File

@ -1,9 +1,5 @@
package rui
import (
"strconv"
)
const (
// Perspective is the name of the SizeUnit property that determines the distance between the z = 0 plane
// and the user in order to give a 3D-positioned element some perspective. Each 3D element
@ -104,20 +100,6 @@ func getTranslate(style Properties, session Session) (SizeUnit, SizeUnit, SizeUn
return x, y, z
}
func getScale(style Properties, session Session) (float64, float64, float64, bool) {
scaleX, okX := floatProperty(style, ScaleX, session, 1)
scaleY, okY := floatProperty(style, ScaleY, session, 1)
scaleZ, okZ := floatProperty(style, ScaleZ, session, 1)
return scaleX, scaleY, scaleZ, okX || okY || okZ
}
func getRotateVector(style Properties, session Session) (float64, float64, float64) {
rotateX, _ := floatProperty(style, RotateX, session, 1)
rotateY, _ := floatProperty(style, RotateY, session, 1)
rotateZ, _ := floatProperty(style, RotateZ, session, 1)
return rotateX, rotateY, rotateZ
}
func (style *viewStyle) transform(session Session) string {
buffer := allocStringBuilder()
@ -133,45 +115,52 @@ func (style *viewStyle) transform(session Session) string {
}
x, y, z := getTranslate(style, session)
scaleX, scaleY, scaleZ, scaleOK := getScale(style, session)
scaleX, okScaleX := floatTextProperty(style, ScaleX, session, 1)
scaleY, okScaleY := floatTextProperty(style, ScaleY, session, 1)
if getTransform3D(style, session) {
if x.Type != Auto || y.Type != Auto || z.Type != Auto {
if buffer.Len() > 0 {
buffer.WriteRune(' ')
}
buffer.WriteString(`translate3d(`)
buffer.WriteString(x.cssString("0"))
buffer.WriteString(x.cssString("0", session))
buffer.WriteRune(',')
buffer.WriteString(y.cssString("0"))
buffer.WriteString(y.cssString("0", session))
buffer.WriteRune(',')
buffer.WriteString(z.cssString("0"))
buffer.WriteString(z.cssString("0", session))
buffer.WriteRune(')')
}
if scaleOK {
scaleZ, okScaleZ := floatTextProperty(style, ScaleZ, session, 1)
if okScaleX || okScaleY || okScaleZ {
if buffer.Len() > 0 {
buffer.WriteRune(' ')
}
buffer.WriteString(`scale3d(`)
buffer.WriteString(strconv.FormatFloat(scaleX, 'g', -1, 64))
buffer.WriteString(scaleX)
buffer.WriteRune(',')
buffer.WriteString(strconv.FormatFloat(scaleY, 'g', -1, 64))
buffer.WriteString(scaleY)
buffer.WriteRune(',')
buffer.WriteString(strconv.FormatFloat(scaleZ, 'g', -1, 64))
buffer.WriteString(scaleZ)
buffer.WriteRune(')')
}
if angle, ok := angleProperty(style, Rotate, session); ok {
rotateX, rotateY, rotateZ := getRotateVector(style, session)
rotateX, _ := floatTextProperty(style, RotateX, session, 1)
rotateY, _ := floatTextProperty(style, RotateY, session, 1)
rotateZ, _ := floatTextProperty(style, RotateZ, session, 1)
if buffer.Len() > 0 {
buffer.WriteRune(' ')
}
buffer.WriteString(`rotate3d(`)
buffer.WriteString(strconv.FormatFloat(rotateX, 'g', -1, 64))
buffer.WriteString(rotateX)
buffer.WriteRune(',')
buffer.WriteString(strconv.FormatFloat(rotateY, 'g', -1, 64))
buffer.WriteString(rotateY)
buffer.WriteRune(',')
buffer.WriteString(strconv.FormatFloat(rotateZ, 'g', -1, 64))
buffer.WriteString(rotateZ)
buffer.WriteRune(',')
buffer.WriteString(angle.cssString())
buffer.WriteRune(')')
@ -183,20 +172,20 @@ func (style *viewStyle) transform(session Session) string {
buffer.WriteRune(' ')
}
buffer.WriteString(`translate(`)
buffer.WriteString(x.cssString("0"))
buffer.WriteString(x.cssString("0", session))
buffer.WriteRune(',')
buffer.WriteString(y.cssString("0"))
buffer.WriteString(y.cssString("0", session))
buffer.WriteRune(')')
}
if scaleOK {
if okScaleX || okScaleY {
if buffer.Len() > 0 {
buffer.WriteRune(' ')
}
buffer.WriteString(`scale(`)
buffer.WriteString(strconv.FormatFloat(scaleX, 'g', -1, 64))
buffer.WriteString(scaleX)
buffer.WriteRune(',')
buffer.WriteString(strconv.FormatFloat(scaleY, 'g', -1, 64))
buffer.WriteString(scaleY)
buffer.WriteRune(')')
}
@ -216,12 +205,12 @@ func (style *viewStyle) transform(session Session) string {
func (style *viewStyle) writeViewTransformCSS(builder cssBuilder, session Session) {
if getTransform3D(style, session) {
if perspective, ok := sizeProperty(style, Perspective, session); ok && perspective.Type != Auto && perspective.Value != 0 {
builder.add(`perspective`, perspective.cssString("0"))
builder.add(`perspective`, perspective.cssString("0", session))
}
x, y := getPerspectiveOrigin(style, session)
if x.Type != Auto || y.Type != Auto {
builder.addValues(`perspective-origin`, ` `, x.cssString("50%"), y.cssString("50%"))
builder.addValues(`perspective-origin`, ` `, x.cssString("50%", session), y.cssString("50%", session))
}
if backfaceVisible, ok := boolProperty(style, BackfaceVisible, session); ok {
@ -234,12 +223,12 @@ func (style *viewStyle) writeViewTransformCSS(builder cssBuilder, session Sessio
x, y, z := getOrigin(style, session)
if x.Type != Auto || y.Type != Auto || z.Type != Auto {
builder.addValues(`transform-origin`, ` `, x.cssString("50%"), y.cssString("50%"), z.cssString("0"))
builder.addValues(`transform-origin`, ` `, x.cssString("50%", session), y.cssString("50%", session), z.cssString("0", session))
}
} else {
x, y, _ := getOrigin(style, session)
if x.Type != Auto || y.Type != Auto {
builder.addValues(`transform-origin`, ` `, x.cssString("50%"), y.cssString("50%"))
builder.addValues(`transform-origin`, ` `, x.cssString("50%", session), y.cssString("50%", session))
}
}
@ -256,17 +245,17 @@ func (view *viewData) updateTransformProperty(tag string) bool {
case PerspectiveOriginX, PerspectiveOriginY:
if getTransform3D(view, session) {
x, y := GetPerspectiveOrigin(view, "")
x, y := GetPerspectiveOrigin(view)
value := ""
if x.Type != Auto || y.Type != Auto {
value = x.cssString("50%") + " " + y.cssString("50%")
value = x.cssString("50%", session) + " " + y.cssString("50%", session)
}
updateCSSProperty(htmlID, "perspective-origin", value, session)
}
case BackfaceVisible:
if getTransform3D(view, session) {
if GetBackfaceVisible(view, "") {
if GetBackfaceVisible(view) {
updateCSSProperty(htmlID, BackfaceVisible, "visible", session)
} else {
updateCSSProperty(htmlID, BackfaceVisible, "hidden", session)
@ -278,11 +267,11 @@ func (view *viewData) updateTransformProperty(tag string) bool {
value := ""
if getTransform3D(view, session) {
if x.Type != Auto || y.Type != Auto || z.Type != Auto {
value = x.cssString("50%") + " " + y.cssString("50%") + " " + z.cssString("50%")
value = x.cssString("50%", session) + " " + y.cssString("50%", session) + " " + z.cssString("50%", session)
}
} else {
if x.Type != Auto || y.Type != Auto {
value = x.cssString("50%") + " " + y.cssString("50%")
value = x.cssString("50%", session) + " " + y.cssString("50%", session)
}
}
updateCSSProperty(htmlID, "transform-origin", value, session)

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