diff --git a/animation.go b/animation.go index 68d222e..e8d7ead 100644 --- a/animation.go +++ b/animation.go @@ -245,14 +245,12 @@ func parseAnimation(obj DataObject) AnimationProperty { animation := new(animationData) animation.init() - for i := range obj.PropertyCount() { - if node := obj.Property(i); node != nil { - tag := PropertyName(node.Tag()) - if node.Type() == TextNode { - animation.Set(tag, node.Text()) - } else { - animation.Set(tag, node) - } + for node := range obj.Properties() { + tag := PropertyName(node.Tag()) + if node.Type() == TextNode { + animation.Set(tag, node.Text()) + } else { + animation.Set(tag, node) } } return animation @@ -493,8 +491,8 @@ func animationSet(properties Properties, tag PropertyName, value any) []Property case DataNode: parseObject := func(obj DataObject) (AnimatedProperty, bool) { result := AnimatedProperty{} - for i := range obj.PropertyCount() { - if node := obj.Property(i); node.Type() == TextNode { + for node := range obj.Properties() { + if node.Type() == TextNode { propTag := strings.ToLower(node.Tag()) switch propTag { case "from", "0", "0%": diff --git a/background.go b/background.go index 920064b..7e50655 100644 --- a/background.go +++ b/background.go @@ -66,9 +66,8 @@ func createBackground(obj DataObject) BackgroundElement { return nil } - count := obj.PropertyCount() - for i := range count { - if node := obj.Property(i); node.Type() == TextNode { + for node := range obj.Properties() { + if node.Type() == TextNode { if value := node.Text(); value != "" { result.Set(PropertyName(node.Tag()), value) } diff --git a/border.go b/border.go index 08be325..23f7cd1 100644 --- a/border.go +++ b/border.go @@ -436,24 +436,20 @@ func (border *borderProperty) String() string { func (border *borderProperty) setBorderObject(obj DataObject) bool { result := true - for i := range obj.PropertyCount() { - if node := obj.Property(i); node != nil { - tag := PropertyName(node.Tag()) - switch node.Type() { - case TextNode: - if borderSet(border, tag, node.Text()) == nil { - result = false - } - - case ObjectNode: - if borderSet(border, tag, node.Object()) == nil { - result = false - } - - default: + for node := range obj.Properties() { + tag := PropertyName(node.Tag()) + switch node.Type() { + case TextNode: + if borderSet(border, tag, node.Text()) == nil { result = false } - } else { + + case ObjectNode: + if borderSet(border, tag, node.Object()) == nil { + result = false + } + + default: result = false } } diff --git a/data.go b/data.go index edc6998..ff06a30 100644 --- a/data.go +++ b/data.go @@ -3,6 +3,7 @@ package rui import ( "errors" "fmt" + "iter" "slices" "strings" "unicode" @@ -27,6 +28,9 @@ type DataObject interface { // Tag returns data object tag Tag() string + // Properties() returns an iterator to access the properties + Properties() iter.Seq[DataNode] + // PropertyCount returns properties count PropertyCount() int @@ -142,6 +146,16 @@ func (object *dataObject) Tag() string { return object.tag } +func (object *dataObject) Properties() iter.Seq[DataNode] { + return func(yield func(DataNode) bool) { + for _, node := range object.property { + if !yield(node) { + return + } + } + } +} + func (object *dataObject) PropertyCount() int { if object.property != nil { return len(object.property) @@ -231,10 +245,14 @@ func (object *dataObject) SetPropertyValue(tag, value string) { // SetPropertyObject - set a property with tag by object func (object *dataObject) SetPropertyObject(tag string, obj DataObject) { - node := new(dataNode) - node.tag = tag - node.value = obj - object.setNode(node) + if obj != nil { + node := new(dataNode) + node.tag = tag + node.value = obj + object.setNode(node) + } else { + object.RemovePropertyByTag(tag) + } } func (object *dataObject) ToParams() Params { diff --git a/filter.go b/filter.go index 887f953..89b1568 100644 --- a/filter.go +++ b/filter.go @@ -159,23 +159,21 @@ func NewFilterProperty(params Params) FilterProperty { func newFilterProperty(obj DataObject) FilterProperty { filter := new(filterData) filter.init() - for i := range obj.PropertyCount() { - if node := obj.Property(i); node != nil { - tag := node.Tag() - switch node.Type() { - case TextNode: - filter.Set(PropertyName(tag), node.Text()) + for node := range obj.Properties() { + tag := node.Tag() + switch node.Type() { + case TextNode: + filter.Set(PropertyName(tag), node.Text()) - case ObjectNode: - if tag == string(HueRotate) { - // TODO - } else { - ErrorLog(`Invalid value of "` + tag + `"`) - } - - default: + case ObjectNode: + if tag == string(HueRotate) { + // TODO + } else { ErrorLog(`Invalid value of "` + tag + `"`) } + + default: + ErrorLog(`Invalid value of "` + tag + `"`) } } diff --git a/properties.go b/properties.go index f0814a3..c71dd23 100644 --- a/properties.go +++ b/properties.go @@ -118,19 +118,16 @@ func (properties *propertyList) writeToBuffer(buffer *strings.Builder, } func parseProperties(properties Properties, object DataObject) { - count := object.PropertyCount() - for i := range count { - if node := object.Property(i); node != nil { - switch node.Type() { - case TextNode: - properties.Set(PropertyName(node.Tag()), node.Text()) + for node := range object.Properties() { + switch node.Type() { + case TextNode: + properties.Set(PropertyName(node.Tag()), node.Text()) - case ObjectNode: - properties.Set(PropertyName(node.Tag()), node.Object()) + case ObjectNode: + properties.Set(PropertyName(node.Tag()), node.Object()) - case ArrayNode: - properties.Set(PropertyName(node.Tag()), node.ArrayElements()) - } + case ArrayNode: + properties.Set(PropertyName(node.Tag()), node.ArrayElements()) } } } diff --git a/session.go b/session.go index 5c92551..d828160 100644 --- a/session.go +++ b/session.go @@ -735,8 +735,8 @@ func (session *sessionData) handleSessionInfo(params DataObject) { if node := params.PropertyByTag("storage"); node != nil && node.Type() == ObjectNode { if obj := node.Object(); obj != nil { - for i := range obj.PropertyCount() { - if element := obj.Property(i); element.Type() == TextNode { + for element := range obj.Properties() { + if element.Type() == TextNode { session.clientStorage[element.Tag()] = element.Text() } } diff --git a/strings.go b/strings.go index 090ecd9..fa023cb 100644 --- a/strings.go +++ b/strings.go @@ -62,8 +62,8 @@ func loadStringResources(text string) { table = map[string]string{} } - for i := range obj.PropertyCount() { - if prop := obj.Property(i); prop != nil && prop.Type() == TextNode { + for prop := range obj.Properties() { + if prop.Type() == TextNode { table[prop.Tag()] = prop.Text() } } @@ -73,8 +73,8 @@ func loadStringResources(text string) { tag := data.Tag() if tag == "strings" { - for i := range data.PropertyCount() { - if prop := data.Property(i); prop != nil && prop.Type() == ObjectNode { + for prop := range data.Properties() { + if prop.Type() == ObjectNode { parseStrings(prop.Object(), prop.Tag()) } } diff --git a/tableView.go b/tableView.go index 8f198e1..4e59f0e 100644 --- a/tableView.go +++ b/tableView.go @@ -731,8 +731,8 @@ func (table *tableViewData) setFunc(tag PropertyName, value any) []PropertyName case DataObject: params := Params{} - for k := range value.PropertyCount() { - if prop := value.Property(k); prop != nil && prop.Type() == TextNode { + for prop := range value.Properties() { + if prop.Type() == TextNode { params[PropertyName(prop.Tag())] = prop.Text() } } diff --git a/theme.go b/theme.go index 03870dc..eb3d080 100644 --- a/theme.go +++ b/theme.go @@ -694,127 +694,115 @@ func (theme *theme) addText(themeText string) bool { return false } - count := data.PropertyCount() - objToStyle := func(obj DataObject) ViewStyle { params := Params{} - for i := range obj.PropertyCount() { - if node := obj.Property(i); node != nil { - switch node.Type() { - case ArrayNode: - params[PropertyName(node.Tag())] = node.ArrayElements() + for node := range obj.Properties() { + switch node.Type() { + case ArrayNode: + params[PropertyName(node.Tag())] = node.ArrayElements() - case ObjectNode: - params[PropertyName(node.Tag())] = node.Object() + case ObjectNode: + params[PropertyName(node.Tag())] = node.Object() - default: - params[PropertyName(node.Tag())] = node.Text() - } + default: + params[PropertyName(node.Tag())] = node.Text() } } return NewViewStyle(params) } - for i := range count { - if d := data.Property(i); d != nil { - switch tag := d.Tag(); tag { - case "constants": - if d.Type() == ObjectNode { - if obj := d.Object(); obj != nil { - objCount := obj.PropertyCount() - for k := range objCount { - if prop := obj.Property(k); prop != nil && prop.Type() == TextNode { - theme.constants[prop.Tag()] = prop.Text() - } + for d := range data.Properties() { + switch tag := d.Tag(); tag { + case "constants": + if d.Type() == ObjectNode { + if obj := d.Object(); obj != nil { + for prop := range obj.Properties() { + if prop.Type() == TextNode { + theme.constants[prop.Tag()] = prop.Text() } } } + } - case "constants:touch": - if d.Type() == ObjectNode { - if obj := d.Object(); obj != nil { - objCount := obj.PropertyCount() - for k := range objCount { - if prop := obj.Property(k); prop != nil && prop.Type() == TextNode { - theme.touchConstants[prop.Tag()] = prop.Text() - } + case "constants:touch": + if d.Type() == ObjectNode { + if obj := d.Object(); obj != nil { + for prop := range obj.Properties() { + if prop.Type() == TextNode { + theme.touchConstants[prop.Tag()] = prop.Text() } } } + } - case "colors": - if d.Type() == ObjectNode { - if obj := d.Object(); obj != nil { - objCount := obj.PropertyCount() - for k := range objCount { - if prop := obj.Property(k); prop != nil && prop.Type() == TextNode { - theme.colors[prop.Tag()] = prop.Text() - } + case "colors": + if d.Type() == ObjectNode { + if obj := d.Object(); obj != nil { + for prop := range obj.Properties() { + if prop.Type() == TextNode { + theme.colors[prop.Tag()] = prop.Text() } } } + } - case "colors:dark": - if d.Type() == ObjectNode { - if obj := d.Object(); obj != nil { - objCount := obj.PropertyCount() - for k := range objCount { - if prop := obj.Property(k); prop != nil && prop.Type() == TextNode { - theme.darkColors[prop.Tag()] = prop.Text() - } + case "colors:dark": + if d.Type() == ObjectNode { + if obj := d.Object(); obj != nil { + for prop := range obj.Properties() { + if prop.Type() == TextNode { + theme.darkColors[prop.Tag()] = prop.Text() } } } + } - case "images": - if d.Type() == ObjectNode { - if obj := d.Object(); obj != nil { - objCount := obj.PropertyCount() - for k := range objCount { - if prop := obj.Property(k); prop != nil && prop.Type() == TextNode { - theme.images[prop.Tag()] = prop.Text() - } + case "images": + if d.Type() == ObjectNode { + if obj := d.Object(); obj != nil { + for prop := range obj.Properties() { + if prop.Type() == TextNode { + theme.images[prop.Tag()] = prop.Text() } } } + } - case "images:dark": - if d.Type() == ObjectNode { - if obj := d.Object(); obj != nil { - objCount := obj.PropertyCount() - for k := range objCount { - if prop := obj.Property(k); prop != nil && prop.Type() == TextNode { - theme.darkImages[prop.Tag()] = prop.Text() - } + case "images:dark": + if d.Type() == ObjectNode { + if obj := d.Object(); obj != nil { + for prop := range obj.Properties() { + if prop.Type() == TextNode { + theme.darkImages[prop.Tag()] = prop.Text() } } } + } - case "styles": - if d.Type() == ArrayNode { + case "styles": + if d.Type() == ArrayNode { + arraySize := d.ArraySize() + for k := range arraySize { + if element := d.ArrayElement(k); element != nil && element.IsObject() { + if obj := element.Object(); obj != nil { + theme.styles[obj.Tag()] = objToStyle(obj) + } + } + } + } + + default: + if d.Type() == ArrayNode && strings.HasPrefix(tag, "styles:") { + if rule, ok := parseMediaRule(tag); ok { arraySize := d.ArraySize() for k := range arraySize { if element := d.ArrayElement(k); element != nil && element.IsObject() { if obj := element.Object(); obj != nil { - theme.styles[obj.Tag()] = objToStyle(obj) + rule.styles[obj.Tag()] = objToStyle(obj) } } } - } - - default: - if d.Type() == ArrayNode && strings.HasPrefix(tag, "styles:") { - if rule, ok := parseMediaRule(tag); ok { - arraySize := d.ArraySize() - for k := range arraySize { - if element := d.ArrayElement(k); element != nil && element.IsObject() { - if obj := element.Object(); obj != nil { - rule.styles[obj.Tag()] = objToStyle(obj) - } - } - } - theme.mediaStyles = append(theme.mediaStyles, rule) - } + theme.mediaStyles = append(theme.mediaStyles, rule) } } } diff --git a/transform.go b/transform.go index f359e38..7807bd9 100644 --- a/transform.go +++ b/transform.go @@ -352,8 +352,8 @@ func valueToTransformProperty(value any) TransformProperty { parseObject := func(obj DataObject) TransformProperty { transform := NewTransformProperty(nil) ok := true - for i := range obj.PropertyCount() { - if prop := obj.Property(i); prop.Type() == TextNode { + for prop := range obj.Properties() { + if prop.Type() == TextNode { if !transform.Set(PropertyName(prop.Tag()), prop.Text()) { ok = false } diff --git a/view.go b/view.go index e1d1afc..9f03e5f 100644 --- a/view.go +++ b/view.go @@ -371,8 +371,7 @@ func (view *viewData) setFunc(tag PropertyName, value any) []PropertyName { case changeListeners: switch value := value.(type) { case DataObject: - for i := range value.PropertyCount() { - node := value.Property(i) + for node := range value.Properties() { if node.Type() == TextNode { if text := node.Text(); text != "" { view.changeListener[PropertyName(node.Tag())] = newOneArgListenerBinding[View, PropertyName](text) @@ -496,9 +495,7 @@ func (view *viewData) setFunc(tag PropertyName, value any) []PropertyName { case DataObject: data := map[string]string{} - count := value.PropertyCount() - for i := range count { - node := value.Property(i) + for node := range value.Properties() { if node.Type() == TextNode { data[node.Tag()] = node.Text() } else {