diff --git a/animation.go b/animation.go index 5be2054..2326307 100644 --- a/animation.go +++ b/animation.go @@ -593,7 +593,7 @@ func (animation *animationData) String() string { for _, tag := range animation.AllTags() { if tag != PropertyTag { - if value, ok := animation.properties[tag]; ok && value != nil { + if value := animation.getRaw(tag); value != nil { text := propertyValueToString(tag, value, "\t") if text != "" { buffer.WriteString("\n\t") diff --git a/animationRun.go b/animationRun.go index 64c3c3d..689102e 100644 --- a/animationRun.go +++ b/animationRun.go @@ -93,7 +93,7 @@ func (animation *animationData) onAnimationEnd(view View, _ PropertyName) { animationView := animation.view listener := animation.listener - if value, ok := animation.properties[PropertyTag]; ok { + if value := animation.getRaw(PropertyTag); value != nil { if props, ok := value.([]AnimatedProperty); ok { for _, prop := range props { animationView.setRaw(prop.Tag, prop.To) @@ -119,7 +119,7 @@ func (animation *animationData) onAnimationCancel(view View, _ PropertyName) { animationView := animation.view listener := animation.listener - if value, ok := animation.properties[PropertyTag]; ok { + if value := animation.getRaw(PropertyTag); value != nil { if props, ok := value.([]AnimatedProperty); ok { for _, prop := range props { animationView.Set(prop.Tag, prop.To) diff --git a/backgroundConicGradient.go b/backgroundConicGradient.go index 1534ebd..1a73589 100644 --- a/backgroundConicGradient.go +++ b/backgroundConicGradient.go @@ -141,9 +141,9 @@ func (gradient *backgroundConicGradient) Tag() string { func (image *backgroundConicGradient) Clone() BackgroundElement { result := NewBackgroundConicGradient(nil) - for tag, value := range image.properties { - result.setRaw(tag, value) - } + image.mutex.Lock() + result.setAll(image.properties) + image.mutex.Unlock() return result } @@ -268,7 +268,7 @@ func (gradient *backgroundConicGradient) parseGradientText(value string) []Backg func (gradient *backgroundConicGradient) cssStyle(session Session) string { points := []BackgroundGradientAngle{} - if value, ok := gradient.properties[Gradient]; ok { + if value := gradient.getRaw(Gradient); value != nil { switch value := value.(type) { case string: if text, ok := session.resolveConstants(value); ok && text != "" { diff --git a/backgroundImage.go b/backgroundImage.go index d8dd18b..02dd7fa 100644 --- a/backgroundImage.go +++ b/backgroundImage.go @@ -105,9 +105,9 @@ func (image *backgroundImage) Tag() string { func (image *backgroundImage) Clone() BackgroundElement { result := NewBackgroundImage(nil) - for tag, value := range image.properties { - result.setRaw(tag, value) - } + image.mutex.Lock() + result.setAll(image.properties) + image.mutex.Unlock() return result } diff --git a/backgroundLinearGradient.go b/backgroundLinearGradient.go index 4d8b71a..2599746 100644 --- a/backgroundLinearGradient.go +++ b/backgroundLinearGradient.go @@ -253,8 +253,8 @@ func (point *BackgroundGradientPoint) String() string { func (gradient *backgroundGradient) writeGradient(session Session, buffer *strings.Builder) bool { - value, ok := gradient.properties[Gradient] - if !ok { + value := gradient.getRaw(Gradient) + if value == nil { return false } @@ -322,9 +322,9 @@ func (gradient *backgroundLinearGradient) Tag() string { func (image *backgroundLinearGradient) Clone() BackgroundElement { result := NewBackgroundLinearGradient(nil) - for tag, value := range image.properties { - result.setRaw(tag, value) - } + image.mutex.Lock() + result.setAll(image.properties) + image.mutex.Unlock() return result } @@ -361,7 +361,7 @@ func (gradient *backgroundLinearGradient) cssStyle(session Session) string { buffer.WriteString(`linear-gradient(`) } - if value, ok := gradient.properties[Direction]; ok { + if value := gradient.getRaw(Direction); value != nil { switch value := value.(type) { case string: if text, ok := session.resolveConstants(value); ok { diff --git a/backgroundRadialGradient.go b/backgroundRadialGradient.go index e8029aa..7712872 100644 --- a/backgroundRadialGradient.go +++ b/backgroundRadialGradient.go @@ -109,9 +109,9 @@ func (gradient *backgroundRadialGradient) Tag() string { func (image *backgroundRadialGradient) Clone() BackgroundElement { result := NewBackgroundRadialGradient(nil) - for tag, value := range image.properties { - result.setRaw(tag, value) - } + image.mutex.Lock() + result.setAll(image.properties) + image.mutex.Unlock() return result } @@ -221,7 +221,7 @@ func (gradient *backgroundRadialGradient) cssStyle(session Session) string { shapeText = `ellipse ` } - if value, ok := gradient.properties[RadialGradientRadius]; ok { + if value := gradient.getRaw(RadialGradientRadius); value != nil { switch value := value.(type) { case string: if text, ok := session.resolveConstants(value); ok { diff --git a/border.go b/border.go index 8c0e3b6..76456c3 100644 --- a/border.go +++ b/border.go @@ -393,6 +393,9 @@ func (border *borderProperty) writeString(buffer *strings.Builder, indent string } } + border.mutex.Lock() + defer border.mutex.Unlock() + for _, tag := range []PropertyName{Style, Width, ColorTag} { if value, ok := border.properties[tag]; ok { write(tag, value) @@ -722,7 +725,7 @@ func (border *borderProperty) deleteTag(tag PropertyName) bool { case Left, Right, Top, Bottom: if border.Get(Style) != nil { - border.properties[tag+"-"+Style] = 0 + border.setRaw(tag+"-"+Style, 0) result = true removeTags([]PropertyName{tag + "-" + ColorTag, tag + "-" + Width}) } else { @@ -732,7 +735,7 @@ func (border *borderProperty) deleteTag(tag PropertyName) bool { case LeftStyle, RightStyle, TopStyle, BottomStyle: if border.getRaw(tag) != nil { if border.Get(Style) != nil { - border.properties[tag] = 0 + border.setRaw(tag, 0) result = true } else { removeTags([]PropertyName{tag}) @@ -742,7 +745,7 @@ func (border *borderProperty) deleteTag(tag PropertyName) bool { case LeftWidth, RightWidth, TopWidth, BottomWidth: if border.getRaw(tag) != nil { if border.Get(Width) != nil { - border.properties[tag] = AutoSize() + border.setRaw(tag, AutoSize()) result = true } else { removeTags([]PropertyName{tag}) @@ -752,7 +755,7 @@ func (border *borderProperty) deleteTag(tag PropertyName) bool { case LeftColor, RightColor, TopColor, BottomColor: if border.getRaw(tag) != nil { if border.Get(ColorTag) != nil { - border.properties[tag] = 0 + border.setRaw(tag, 0) result = true } else { removeTags([]PropertyName{tag}) diff --git a/clipShape.go b/clipShape.go index 03d069b..77dc00a 100644 --- a/clipShape.go +++ b/clipShape.go @@ -243,7 +243,7 @@ func (clip *insetClipData) writeString(buffer *strings.Builder, indent string) { buffer.WriteString("inset { ") comma := false for _, tag := range []PropertyName{Top, Right, Bottom, Left, Radius} { - if value, ok := clip.properties[tag]; ok { + if value := clip.getRaw(tag); value != nil { text := propertyValueToString(tag, value, indent) if text != "" { if comma { @@ -319,7 +319,7 @@ func (clip *circleClipData) writeString(buffer *strings.Builder, indent string) buffer.WriteString("circle { ") comma := false for _, tag := range []PropertyName{Radius, X, Y} { - if value, ok := clip.properties[tag]; ok { + if value := clip.getRaw(tag); value != nil { text := propertyValueToString(tag, value, indent) if text != "" { if comma { @@ -399,7 +399,7 @@ func (clip *ellipseClipData) writeString(buffer *strings.Builder, indent string) buffer.WriteString("ellipse { ") comma := false for _, tag := range []PropertyName{RadiusX, RadiusY, X, Y} { - if value, ok := clip.properties[tag]; ok { + if value := clip.getRaw(tag); value != nil { text := propertyValueToString(tag, value, indent) if text != "" { if comma { diff --git a/colorPicker.go b/colorPicker.go index 44d19b5..0d063e2 100644 --- a/colorPicker.go +++ b/colorPicker.go @@ -165,7 +165,7 @@ func (picker *colorPickerData) handleCommand(self View, command PropertyName, da if text, ok := data.PropertyValue("text"); ok { if color, ok := StringToColor(text); ok { oldColor := GetColorPickerValue(picker) - picker.properties[ColorPickerValue] = color + picker.setRaw(ColorPickerValue, color) if color != oldColor { for _, listener := range getTwoArgEventListeners[ColorPicker, Color](picker, nil, ColorChangedEvent) { listener.Run(picker, color, oldColor) diff --git a/customView.go b/customView.go index f6f6d89..04dffb2 100644 --- a/customView.go +++ b/customView.go @@ -75,11 +75,8 @@ func (customView *CustomViewData) setRaw(tag PropertyName, value any) { customView.superView.setRaw(tag, value) } -func (customView *CustomViewData) setContent(value any) bool { - if container, ok := customView.superView.(ViewsContainer); ok { - return container.setContent(value) - } - return false +func (customView *CustomViewData) setAll(props map[PropertyName]any) { + customView.superView.setAll(props) } // Set sets the value (second argument) of the property with name defined by the first argument. diff --git a/datePicker.go b/datePicker.go index 2cfef6a..daaa0cb 100644 --- a/datePicker.go +++ b/datePicker.go @@ -354,7 +354,7 @@ func (picker *datePickerData) handleCommand(self View, command PropertyName, dat if text, ok := data.PropertyValue("text"); ok { if value, err := time.Parse(dateFormat, text); err == nil { oldValue := GetDatePickerValue(picker) - picker.properties[DatePickerValue] = value + picker.setRaw(DatePickerValue, value) if value != oldValue { for _, listener := range getTwoArgEventListeners[DatePicker, time.Time](picker, nil, DateChangedEvent) { listener.Run(picker, value, oldValue) diff --git a/detailsView.go b/detailsView.go index bc2e359..815a1f8 100644 --- a/detailsView.go +++ b/detailsView.go @@ -171,7 +171,7 @@ func (detailsView *detailsViewData) htmlSubviews(self View, buffer *strings.Buil summary := false hidden := IsSummaryMarkerHidden(detailsView) - if value, ok := detailsView.properties[Summary]; ok { + if value := detailsView.getRaw(Summary); value != nil { switch value := value.(type) { case string: @@ -217,7 +217,7 @@ func (detailsView *detailsViewData) htmlSubviews(self View, buffer *strings.Buil func (detailsView *detailsViewData) handleCommand(self View, command PropertyName, data DataObject) bool { if command == "details-open" { if n, ok := dataIntProperty(data, "open"); ok { - detailsView.properties[Expanded] = (n != 0) + detailsView.setRaw(Expanded, n != 0) detailsView.runChangeListener(Expanded) } return true diff --git a/dropDownList.go b/dropDownList.go index b7a438c..e3e8a23 100644 --- a/dropDownList.go +++ b/dropDownList.go @@ -255,7 +255,7 @@ func (list *dropDownListData) handleCommand(self View, command PropertyName, dat items := GetDropDownItems(list) if GetCurrent(list) != number && number >= 0 && number < len(items) { old := GetCurrent(list) - list.properties[Current] = number + list.setRaw(Current, number) for _, listener := range getTwoArgEventListeners[DropDownList, int](list, nil, DropDownEvent) { listener.Run(list, number, old) } diff --git a/editView.go b/editView.go index 3e0f6aa..48fc578 100644 --- a/editView.go +++ b/editView.go @@ -268,7 +268,7 @@ func (edit *editViewData) AppendText(text string) { if textValue, ok := value.(string); ok { oldText := textValue textValue += text - edit.properties[Text] = textValue + edit.setRaw(Text, textValue) edit.session.callFunc("appendToInnerHTML", edit.htmlID(), text) edit.session.callFunc("appendToInputValue", edit.htmlID(), text) edit.textChanged(textValue, oldText) diff --git a/filter.go b/filter.go index a5fa8d2..b5889b2 100644 --- a/filter.go +++ b/filter.go @@ -189,7 +189,7 @@ func newFilterProperty(obj DataObject) FilterProperty { } } - if len(filter.properties) > 0 { + if !filter.IsEmpty() { return filter } ErrorLog("Empty view filter") diff --git a/listView.go b/listView.go index 874b8d7..6fffeac 100644 --- a/listView.go +++ b/listView.go @@ -986,7 +986,7 @@ func (listView *listViewData) handleCommand(self View, command PropertyName, dat } case "itemUnselected": - if _, ok := listView.properties[Current]; ok { + if listView.getRaw(Current) != nil { listView.handleCurrent(-1) } @@ -1003,7 +1003,7 @@ func (listView *listViewData) handleCommand(self View, command PropertyName, dat } func (listView *listViewData) handleCurrent(number int) { - listView.properties[Current] = number + listView.setRaw(Current, number) for _, listener := range getOneArgEventListeners[ListView, int](listView, nil, ListItemSelectedEvent) { listener.Run(listView, number) } diff --git a/numberPicker.go b/numberPicker.go index 3b03d04..812d80d 100644 --- a/numberPicker.go +++ b/numberPicker.go @@ -289,7 +289,7 @@ func (picker *numberPickerData) handleCommand(self View, command PropertyName, d if text, ok := data.PropertyValue("text"); ok { if value, err := strconv.ParseFloat(text, 32); err == nil { oldValue := GetNumberPickerValue(picker) - picker.properties[NumberPickerValue] = text + picker.setRaw(NumberPickerValue, text) if value != oldValue { for _, listener := range getTwoArgEventListeners[NumberPicker, float64](picker, nil, NumberChangedEvent) { listener.Run(picker, value, oldValue) diff --git a/popup.go b/popup.go index 2ed1b89..fc37d6f 100644 --- a/popup.go +++ b/popup.go @@ -1428,11 +1428,13 @@ func (popup *popupData) createLayerView() GridLayout { CellHorizontalAlign, } + popup.mutex.Lock() for tag, value := range popup.properties { if !slices.Contains(popupProperties, tag) { params[tag] = value } } + popup.mutex.Unlock() views := make([]View, 0, 3) if title := popup.createTitleView(); title != nil { @@ -1507,9 +1509,9 @@ func NewPopup(view View, param Params) Popup { } popup := new(popupData) + popup.init() popup.session = view.Session() popup.contentView = view - popup.properties = map[PropertyName]any{} popup.hotkeys = map[string]func(Popup){} for tag, value := range popup.session.PopupDefaultsSeq() { @@ -1547,8 +1549,8 @@ func CreatePopupFromObject(session Session, object DataObject, binding any) Popu } popup := new(popupData) + popup.init() popup.session = session - popup.properties = map[PropertyName]any{} popup.hotkeys = map[string]func(Popup){} for key, value := range object.ToParams() { diff --git a/properties.go b/properties.go index bd43877..6df0f87 100644 --- a/properties.go +++ b/properties.go @@ -2,6 +2,7 @@ package rui import ( "iter" + "maps" "slices" "strings" "sync" @@ -19,6 +20,7 @@ type Properties interface { // a description of the error is written to the log Set(tag PropertyName, value any) bool setRaw(tag PropertyName, value any) + setAll(properties map[PropertyName]any) // Remove removes the property with name defined by the argument Remove(tag PropertyName) @@ -56,9 +58,6 @@ func defaultNormalize(tag PropertyName) PropertyName { func (properties *propertyList) init() { properties.properties = map[PropertyName]any{} properties.normalize = defaultNormalize - //properties.getFunc = properties.getRaw - //properties.set = propertiesSet - //properties.remove = propertiesRemove } func (properties *propertyList) IsEmpty() bool { @@ -87,6 +86,12 @@ func (properties *propertyList) setRaw(tag PropertyName, value any) { properties.mutex.Unlock() } +func (properties *propertyList) setAll(props map[PropertyName]any) { + properties.mutex.Lock() + maps.Copy(properties.properties, props) + properties.mutex.Unlock() +} + /* func (properties *propertyList) Remove(tag PropertyName) { properties.remove(properties, properties.normalize(tag)) @@ -100,6 +105,8 @@ func (properties *propertyList) Clear() { } func (properties *propertyList) All() iter.Seq2[PropertyName, any] { + properties.mutex.Lock() + defer properties.mutex.Unlock() return func(yield func(PropertyName, any) bool) { for tag, value := range properties.properties { if !yield(tag, value) { @@ -204,6 +211,9 @@ func (data *dataProperty) Remove(tag PropertyName) { } func (data *dataProperty) writeToBuffer(buffer *strings.Builder, indent string, objectName string, tags []PropertyName) { + data.mutex.Lock() + defer data.mutex.Unlock() + buffer.WriteString(objectName) buffer.WriteString("{ ") comma := false diff --git a/tableView.go b/tableView.go index 3e06f4d..9b43c05 100644 --- a/tableView.go +++ b/tableView.go @@ -1201,7 +1201,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) { } } - if len(view.properties) > 0 { + if !view.IsEmpty() { view.cssStyle(view, &cssBuilder) } diff --git a/timePicker.go b/timePicker.go index 752cbb9..9e19cc7 100644 --- a/timePicker.go +++ b/timePicker.go @@ -329,7 +329,7 @@ func (picker *timePickerData) handleCommand(self View, command PropertyName, dat if text, ok := data.PropertyValue("text"); ok { if value, ok := stringToTime(text); ok { oldValue := GetTimePickerValue(picker) - picker.properties[TimePickerValue] = value + picker.setRaw(TimePickerValue, value) if value != oldValue { for _, listener := range getTwoArgEventListeners[TimePicker, time.Time](picker, nil, TimeChangedEvent) { listener.Run(picker, value, oldValue)