diff --git a/canvasView.go b/canvasView.go index 7e5d02c..390f697 100644 --- a/canvasView.go +++ b/canvasView.go @@ -53,6 +53,7 @@ func (canvasView *canvasViewData) remove(tag string) { if tag == DrawFunction { canvasView.drawer = nil canvasView.Redraw() + canvasView.propertyChangedEvent(tag) } else { canvasView.viewData.remove(tag) } @@ -73,6 +74,7 @@ func (canvasView *canvasViewData) set(tag string, value interface{}) bool { return false } canvasView.Redraw() + canvasView.propertyChangedEvent(tag) return true } diff --git a/checkbox.go b/checkbox.go index 4ae2bfa..e337383 100644 --- a/checkbox.go +++ b/checkbox.go @@ -57,13 +57,16 @@ func (button *checkboxData) Get(tag string) interface{} { } func (button *checkboxData) Set(tag string, value interface{}) bool { - switch strings.ToLower(tag) { + return button.set(tag, value) +} + +func (button *checkboxData) set(tag string, value interface{}) bool { + switch tag { case CheckboxChangedEvent: - ok := button.setChangedListener(value) - if !ok { + if !button.setChangedListener(value) { notCompatibleType(tag, value) + return false } - return ok case Checked: oldChecked := button.checked() @@ -76,46 +79,60 @@ func (button *checkboxData) Set(tag string, value interface{}) bool { button.changedCheckboxState(checked) } } - return true case CheckboxHorizontalAlign, CheckboxVerticalAlign: - if button.setEnumProperty(tag, value, enumProperties[tag].values) { - if button.created { - htmlID := button.htmlID() - updateCSSStyle(htmlID, button.session) - updateInnerHTML(htmlID, button.session) - } - return true + if !button.setEnumProperty(tag, value, enumProperties[tag].values) { + return false + } + if button.created { + htmlID := button.htmlID() + updateCSSStyle(htmlID, button.session) + updateInnerHTML(htmlID, button.session) } - return false case VerticalAlign: - if button.setEnumProperty(tag, value, enumProperties[tag].values) { - if button.created { - updateCSSProperty(button.htmlID()+"content", "align-items", button.cssVerticalAlign(), button.session) - } - return true + if !button.setEnumProperty(tag, value, enumProperties[tag].values) { + return false + } + if button.created { + updateCSSProperty(button.htmlID()+"content", "align-items", button.cssVerticalAlign(), button.session) } - return false case HorizontalAlign: - if button.setEnumProperty(tag, value, enumProperties[tag].values) { - if button.created { - updateCSSProperty(button.htmlID()+"content", "justify-items", button.cssHorizontalAlign(), button.session) - } - return true + if !button.setEnumProperty(tag, value, enumProperties[tag].values) { + return false + } + if button.created { + updateCSSProperty(button.htmlID()+"content", "justify-items", button.cssHorizontalAlign(), button.session) } - return false case CellVerticalAlign, CellHorizontalAlign, CellWidth, CellHeight: return false + + default: + return button.viewsContainerData.set(tag, value) } - return button.viewsContainerData.Set(tag, value) + button.propertyChangedEvent(tag) + return true } func (button *checkboxData) Remove(tag string) { - switch strings.ToLower(tag) { + button.remove(strings.ToLower(tag)) +} + +func (button *checkboxData) remove(tag string) { + switch tag { + case ClickEvent: + if !button.viewsContainerData.set(ClickEvent, checkboxClickListener) { + delete(button.properties, tag) + } + + case KeyDownEvent: + if !button.viewsContainerData.set(KeyDownEvent, checkboxKeyListener) { + delete(button.properties, tag) + } + case CheckboxChangedEvent: if len(button.checkedListeners) > 0 { button.checkedListeners = []func(Checkbox, bool){} @@ -124,27 +141,35 @@ func (button *checkboxData) Remove(tag string) { case Checked: oldChecked := button.checked() delete(button.properties, tag) - if oldChecked { + if button.created && oldChecked { button.changedCheckboxState(false) } case CheckboxHorizontalAlign, CheckboxVerticalAlign: delete(button.properties, tag) - htmlID := button.htmlID() - updateCSSStyle(htmlID, button.session) - updateInnerHTML(htmlID, button.session) + if button.created { + htmlID := button.htmlID() + updateCSSStyle(htmlID, button.session) + updateInnerHTML(htmlID, button.session) + } case VerticalAlign: delete(button.properties, tag) - updateCSSProperty(button.htmlID()+"content", "align-items", button.cssVerticalAlign(), button.session) + if button.created { + updateCSSProperty(button.htmlID()+"content", "align-items", button.cssVerticalAlign(), button.session) + } case HorizontalAlign: delete(button.properties, tag) - updateCSSProperty(button.htmlID()+"content", "justify-items", button.cssHorizontalAlign(), button.session) + if button.created { + updateCSSProperty(button.htmlID()+"content", "justify-items", button.cssHorizontalAlign(), button.session) + } default: - button.viewsContainerData.Remove(tag) + button.viewsContainerData.remove(tag) + return } + button.propertyChangedEvent(tag) } func (button *checkboxData) checked() bool { diff --git a/colorPicker.go b/colorPicker.go index 62990f8..93c17f7 100644 --- a/colorPicker.go +++ b/colorPicker.go @@ -56,7 +56,10 @@ func (picker *colorPickerData) Remove(tag string) { func (picker *colorPickerData) remove(tag string) { switch tag { case ColorChangedEvent: - picker.colorChangedListeners = []func(ColorPicker, Color){} + if len(picker.colorChangedListeners) > 0 { + picker.colorChangedListeners = []func(ColorPicker, Color){} + picker.propertyChangedEvent(tag) + } case ColorPickerValue: oldColor := GetColorPickerValue(picker, "") @@ -131,15 +134,13 @@ func (picker *colorPickerData) set(tag string, value interface{}) bool { } picker.colorChangedListeners = listeners } + picker.propertyChangedEvent(tag) return true case ColorPickerValue: oldColor := GetColorPickerValue(picker, "") if picker.setColorProperty(ColorPickerValue, value) { - newValue := GetColorPickerValue(picker, "") - if oldColor != newValue { - picker.colorChanged(oldColor) - } + picker.colorChanged(oldColor) return true } @@ -150,12 +151,14 @@ func (picker *colorPickerData) set(tag string, value interface{}) bool { } func (picker *colorPickerData) colorChanged(oldColor Color) { - newColor := GetColorPickerValue(picker, "") - if oldColor != newColor { - picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, picker.htmlID(), newColor.rgbString())) + if newColor := GetColorPickerValue(picker, ""); oldColor != newColor { + if picker.created { + picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, picker.htmlID(), newColor.rgbString())) + } for _, listener := range picker.colorChangedListeners { listener(picker, newColor) } + picker.propertyChangedEvent(ColorTag) } } @@ -188,7 +191,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) diff --git a/columnLayout.go b/columnLayout.go index 1682d5a..f90e6b7 100644 --- a/columnLayout.go +++ b/columnLayout.go @@ -82,12 +82,14 @@ func (columnLayout *columnLayoutData) Remove(tag string) { func (columnLayout *columnLayoutData) remove(tag string) { columnLayout.viewsContainerData.remove(tag) - switch tag { - case ColumnCount, ColumnWidth, ColumnGap: - updateCSSProperty(columnLayout.htmlID(), tag, "", columnLayout.Session()) + if columnLayout.created { + switch tag { + case ColumnCount, ColumnWidth, ColumnGap: + updateCSSProperty(columnLayout.htmlID(), tag, "", columnLayout.Session()) - case ColumnSeparator: - updateCSSProperty(columnLayout.htmlID(), "column-rule", "", columnLayout.Session()) + case ColumnSeparator: + updateCSSProperty(columnLayout.htmlID(), "column-rule", "", columnLayout.Session()) + } } } @@ -101,22 +103,11 @@ func (columnLayout *columnLayoutData) set(tag string, value interface{}) bool { return true } - switch tag { - case ColumnCount: - if columnLayout.setIntProperty(tag, value) { - session := columnLayout.Session() - if count, ok := intProperty(columnLayout, tag, session, 0); ok && count > 0 { - updateCSSProperty(columnLayout.htmlID(), tag, strconv.Itoa(count), session) - } else { - updateCSSProperty(columnLayout.htmlID(), tag, "auto", session) - } - return true - } + if !columnLayout.viewsContainerData.set(tag, value) { return false } - ok := columnLayout.viewsContainerData.set(tag, value) - if ok { + if columnLayout.created { switch tag { case ColumnSeparator: css := "" @@ -126,9 +117,17 @@ func (columnLayout *columnLayoutData) set(tag string, value interface{}) bool { css = separator.cssValue(columnLayout.Session()) } updateCSSProperty(columnLayout.htmlID(), "column-rule", css, session) + + case ColumnCount: + session := columnLayout.Session() + if count, ok := intProperty(columnLayout, tag, session, 0); ok && count > 0 { + updateCSSProperty(columnLayout.htmlID(), tag, strconv.Itoa(count), session) + } else { + updateCSSProperty(columnLayout.htmlID(), tag, "auto", session) + } } } - return ok + return true } // GetColumnCount returns int value which specifies number of columns into which the content of diff --git a/customView.go b/customView.go index a07a435..15beadb 100644 --- a/customView.go +++ b/customView.go @@ -68,6 +68,10 @@ func (customView *CustomViewData) SetAnimated(tag string, value interface{}, ani return customView.superView.SetAnimated(tag, value, animation) } +func (customView *CustomViewData) SetChangeListener(tag string, listener func(View, string)) { + customView.superView.SetChangeListener(tag, listener) +} + // Remove removes the property with name defined by the argument func (customView *CustomViewData) Remove(tag string) { customView.superView.Remove(tag) diff --git a/datePicker.go b/datePicker.go index a2310f9..129b32b 100644 --- a/datePicker.go +++ b/datePicker.go @@ -63,28 +63,47 @@ func (picker *datePickerData) remove(tag string) { case DateChangedEvent: if len(picker.dateChangedListeners) > 0 { picker.dateChangedListeners = []func(DatePicker, time.Time){} + picker.propertyChangedEvent(tag) } + return case DatePickerMin: delete(picker.properties, DatePickerMin) - removeProperty(picker.htmlID(), Min, picker.session) + if picker.created { + removeProperty(picker.htmlID(), Min, picker.session) + } case DatePickerMax: delete(picker.properties, DatePickerMax) - removeProperty(picker.htmlID(), Max, picker.session) + if picker.created { + removeProperty(picker.htmlID(), Max, picker.session) + } case DatePickerStep: delete(picker.properties, DatePickerMax) - removeProperty(picker.htmlID(), Step, picker.session) + if picker.created { + removeProperty(picker.htmlID(), Step, picker.session) + } case DatePickerValue: - delete(picker.properties, DatePickerValue) - updateProperty(picker.htmlID(), Value, time.Now().Format(dateFormat), picker.session) + if _, ok := picker.properties[DatePickerValue]; ok { + delete(picker.properties, DatePickerValue) + date := GetDatePickerValue(picker, "") + if picker.created { + picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, picker.htmlID(), date.Format(dateFormat))) + } + for _, listener := range picker.dateChangedListeners { + listener(picker, date) + } + } else { + return + } default: picker.viewData.remove(tag) - picker.propertyChanged(tag) + return } + picker.propertyChangedEvent(tag) } func (picker *datePickerData) Set(tag string, value interface{}) bool { @@ -98,7 +117,6 @@ func (picker *datePickerData) set(tag string, value interface{}) bool { } setTimeValue := func(tag string) (time.Time, bool) { - //old, oldOK := getDateProperty(picker, tag, shortTag) switch value := value.(type) { case time.Time: picker.properties[tag] = value @@ -122,7 +140,10 @@ func (picker *datePickerData) set(tag string, value interface{}) bool { old, oldOK := getDateProperty(picker, DatePickerMin, Min) if date, ok := setTimeValue(DatePickerMin); ok { if !oldOK || date != old { - updateProperty(picker.htmlID(), Min, date.Format(dateFormat), picker.session) + if picker.created { + updateProperty(picker.htmlID(), Min, date.Format(dateFormat), picker.session) + } + picker.propertyChangedEvent(tag) } return true } @@ -131,7 +152,10 @@ func (picker *datePickerData) set(tag string, value interface{}) bool { old, oldOK := getDateProperty(picker, DatePickerMax, Max) if date, ok := setTimeValue(DatePickerMax); ok { if !oldOK || date != old { - updateProperty(picker.htmlID(), Max, date.Format(dateFormat), picker.session) + if picker.created { + updateProperty(picker.htmlID(), Max, date.Format(dateFormat), picker.session) + } + picker.propertyChangedEvent(tag) } return true } @@ -139,13 +163,15 @@ func (picker *datePickerData) set(tag string, value interface{}) bool { case DatePickerStep: oldStep := GetDatePickerStep(picker, "") if picker.setIntProperty(DatePickerStep, value) { - step := GetDatePickerStep(picker, "") - if oldStep != step { - if step > 0 { - updateProperty(picker.htmlID(), Step, strconv.Itoa(step), picker.session) - } else { - removeProperty(picker.htmlID(), Step, picker.session) + if step := GetDatePickerStep(picker, ""); oldStep != step { + if picker.created { + if step > 0 { + updateProperty(picker.htmlID(), Step, strconv.Itoa(step), picker.session) + } else { + removeProperty(picker.htmlID(), Step, picker.session) + } } + picker.propertyChangedEvent(tag) } return true } @@ -153,11 +179,14 @@ func (picker *datePickerData) set(tag string, value interface{}) bool { case DatePickerValue: oldDate := GetDatePickerValue(picker, "") if date, ok := setTimeValue(DatePickerMax); ok { - picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, picker.htmlID(), date.Format(dateFormat))) if date != oldDate { + if picker.created { + picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, picker.htmlID(), date.Format(dateFormat))) + } for _, listener := range picker.dateChangedListeners { listener(picker, date) } + picker.propertyChangedEvent(tag) } return true } @@ -214,13 +243,11 @@ func (picker *datePickerData) set(tag string, value interface{}) bool { } picker.dateChangedListeners = listeners } + picker.propertyChangedEvent(tag) return true default: - if picker.viewData.set(tag, value) { - picker.propertyChanged(tag) - return true - } + return picker.viewData.set(tag, value) } return false } @@ -274,7 +301,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) diff --git a/demo/textStyle.go b/demo/textStyle.go index 1666381..a6b2479 100644 --- a/demo/textStyle.go +++ b/demo/textStyle.go @@ -57,14 +57,24 @@ func createTextStyleDemo(session rui.Session) rui.View { return nil } - rui.Set(view, "textStyleFont", rui.DropDownEvent, func(number int) { + rui.SetChangeListener(view, "textStyleFont", rui.Current, func(v rui.View, tag string) { fonts := []string{"", "serif", "sans-serif", "\"Courier new\", monospace", "cursive", "fantasy"} - if number > 0 && number < len(fonts) { + if number := rui.GetDropDownCurrent(v, ""); number > 0 && number < len(fonts) { rui.Set(view, "textStyleText", rui.FontName, fonts[number]) } else { rui.Set(view, "textStyleText", rui.FontName, nil) } }) + /* + rui.Set(view, "textStyleFont", rui.DropDownEvent, func(number int) { + fonts := []string{"", "serif", "sans-serif", "\"Courier new\", monospace", "cursive", "fantasy"} + if number > 0 && number < len(fonts) { + rui.Set(view, "textStyleText", rui.FontName, fonts[number]) + } else { + rui.Set(view, "textStyleText", rui.FontName, nil) + } + }) + */ rui.Set(view, "textStyleSize", rui.DropDownEvent, func(number int) { sizes := []string{"1em", "14pt", "12px", "1.5em"} diff --git a/detailsView.go b/detailsView.go index 6614576..e519f32 100644 --- a/detailsView.go +++ b/detailsView.go @@ -2,6 +2,8 @@ package rui import "strings" +// TODO Expanded event + const ( // Summary is the constant for the "summary" property tag. // The contents of the "summary" property are used as the label for the disclosure widget. @@ -45,18 +47,14 @@ func (detailsView *detailsViewData) Remove(tag string) { } func (detailsView *detailsViewData) remove(tag string) { - if _, ok := detailsView.properties[tag]; ok { + detailsView.viewsContainerData.remove(tag) + if detailsView.created { switch tag { case Summary: - delete(detailsView.properties, tag) updateInnerHTML(detailsView.htmlID(), detailsView.Session()) case Expanded: - delete(detailsView.properties, tag) removeProperty(detailsView.htmlID(), "open", detailsView.Session()) - - default: - detailsView.viewsContainerData.remove(tag) } } } @@ -66,6 +64,11 @@ func (detailsView *detailsViewData) Set(tag string, value interface{}) bool { } func (detailsView *detailsViewData) set(tag string, value interface{}) bool { + if value == nil { + detailsView.remove(tag) + return true + } + switch tag { case Summary: switch value := value.(type) { @@ -86,23 +89,29 @@ func (detailsView *detailsViewData) set(tag string, value interface{}) bool { notCompatibleType(tag, value) return false } - updateInnerHTML(detailsView.htmlID(), detailsView.Session()) - return true + if detailsView.created { + updateInnerHTML(detailsView.htmlID(), detailsView.Session()) + } case Expanded: - if detailsView.setBoolProperty(tag, value) { + if !detailsView.setBoolProperty(tag, value) { + notCompatibleType(tag, value) + return false + } + if detailsView.created { if IsDetailsExpanded(detailsView, "") { updateProperty(detailsView.htmlID(), "open", "", detailsView.Session()) } else { removeProperty(detailsView.htmlID(), "open", detailsView.Session()) } - return true } - notCompatibleType(tag, value) - return false + + default: + return detailsView.viewsContainerData.Set(tag, value) } - return detailsView.viewsContainerData.Set(tag, value) + detailsView.propertyChangedEvent(tag) + return true } func (detailsView *detailsViewData) Get(tag string) interface{} { diff --git a/dropDownList.go b/dropDownList.go index 757d8a0..b3d84dc 100644 --- a/dropDownList.go +++ b/dropDownList.go @@ -48,19 +48,31 @@ func (list *dropDownListData) remove(tag string) { case Items: if len(list.items) > 0 { list.items = []string{} - updateInnerHTML(list.htmlID(), list.session) + if list.created { + updateInnerHTML(list.htmlID(), list.session) + } + list.propertyChangedEvent(tag) } - case Current: - list.set(Current, 0) - case DropDownEvent: if len(list.dropDownListener) > 0 { list.dropDownListener = []func(DropDownList, int){} + list.propertyChangedEvent(tag) + } + + case Current: + oldCurrent := GetDropDownCurrent(list, "") + delete(list.properties, Current) + if oldCurrent != 0 { + if list.created { + list.session.runScript(fmt.Sprintf(`selectDropDownListItem('%s', %d)`, list.htmlID(), 0)) + } + list.onSelectedItemChanged(0) } default: list.viewData.remove(tag) + return } } @@ -73,23 +85,22 @@ func (list *dropDownListData) set(tag string, value interface{}) bool { case Items: return list.setItems(value) + case DropDownEvent: + return list.setDropDownListener(value) + case Current: oldCurrent := GetDropDownCurrent(list, "") if !list.setIntProperty(Current, value) { return false } - if !list.session.ignoreViewUpdates() { - current := GetDropDownCurrent(list, "") - if oldCurrent != current { + if current := GetDropDownCurrent(list, ""); oldCurrent != current { + if list.created { list.session.runScript(fmt.Sprintf(`selectDropDownListItem('%s', %d)`, list.htmlID(), current)) - list.onSelectedItemChanged(current) } + list.onSelectedItemChanged(current) } return true - - case DropDownEvent: - return list.setDropDownListener(value) } return list.viewData.set(tag, value) @@ -160,9 +171,11 @@ func (list *dropDownListData) setItems(value interface{}) bool { return false } - if !list.session.ignoreViewUpdates() { + if list.created { updateInnerHTML(list.htmlID(), list.session) } + + list.propertyChangedEvent(Items) return true } @@ -170,17 +183,14 @@ func (list *dropDownListData) setDropDownListener(value interface{}) bool { switch value := value.(type) { case func(DropDownList, int): list.dropDownListener = []func(DropDownList, int){value} - return true case func(int): list.dropDownListener = []func(DropDownList, int){func(list DropDownList, index int) { value(index) }} - return true case []func(DropDownList, int): list.dropDownListener = value - return true case []func(int): listeners := make([]func(DropDownList, int), len(value)) @@ -194,7 +204,6 @@ func (list *dropDownListData) setDropDownListener(value interface{}) bool { } } list.dropDownListener = listeners - return true case []interface{}: listeners := make([]func(DropDownList, int), len(value)) @@ -216,13 +225,16 @@ func (list *dropDownListData) setDropDownListener(value interface{}) bool { notCompatibleType(DropDownEvent, value) return false } - list.dropDownListener = listeners } - return true + list.dropDownListener = listeners + + default: + notCompatibleType(DropDownEvent, value) + return false } - notCompatibleType(DropDownEvent, value) - return false + list.propertyChangedEvent(DropDownEvent) + return true } func (list *dropDownListData) Get(tag string) interface{} { @@ -280,7 +292,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`) } } @@ -289,6 +301,7 @@ func (list *dropDownListData) onSelectedItemChanged(number int) { for _, listener := range list.dropDownListener { listener(list, number) } + list.propertyChangedEvent(Current) } func (list *dropDownListData) handleCommand(self View, command string, data DataObject) bool { diff --git a/editView.go b/editView.go index 99ced44..959a0ff 100644 --- a/editView.go +++ b/editView.go @@ -84,63 +84,98 @@ func (edit *editViewData) Remove(tag string) { } func (edit *editViewData) remove(tag string) { - if _, ok := edit.properties[tag]; ok { - switch tag { - case Hint: + _, exists := edit.properties[tag] + switch tag { + case Hint: + if exists { delete(edit.properties, Hint) - removeProperty(edit.htmlID(), "placeholder", edit.session) - - case MaxLength: - delete(edit.properties, MaxLength) - removeProperty(edit.htmlID(), "maxlength", edit.session) - - case ReadOnly, Spellcheck: - delete(edit.properties, tag) - updateBoolProperty(edit.htmlID(), tag, false, edit.session) - - case EditTextChangedEvent: - if len(edit.textChangeListeners) > 0 { - edit.textChangeListeners = []func(EditView, string){} + if edit.created { + removeProperty(edit.htmlID(), "placeholder", edit.session) } + edit.propertyChangedEvent(tag) + } - case Text: + case MaxLength: + if exists { + delete(edit.properties, MaxLength) + if edit.created { + removeProperty(edit.htmlID(), "maxlength", edit.session) + } + edit.propertyChangedEvent(tag) + } + + case ReadOnly, Spellcheck: + if exists { + delete(edit.properties, tag) + if edit.created { + updateBoolProperty(edit.htmlID(), tag, false, edit.session) + } + edit.propertyChangedEvent(tag) + } + + case EditTextChangedEvent: + if len(edit.textChangeListeners) > 0 { + edit.textChangeListeners = []func(EditView, string){} + edit.propertyChangedEvent(tag) + } + + case Text: + if exists { oldText := GetText(edit, "") delete(edit.properties, tag) if oldText != "" { edit.textChanged("") - edit.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, edit.htmlID(), "")) + if edit.created { + edit.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, edit.htmlID(), "")) + } } + } - case EditViewPattern: + case EditViewPattern: + if exists { oldText := GetEditViewPattern(edit, "") delete(edit.properties, tag) if oldText != "" { - removeProperty(edit.htmlID(), Pattern, edit.session) + if edit.created { + removeProperty(edit.htmlID(), Pattern, edit.session) + } + edit.propertyChangedEvent(tag) } + } - case EditViewType: + case EditViewType: + if exists { oldType := GetEditViewType(edit, "") delete(edit.properties, tag) if oldType != 0 { - updateInnerHTML(edit.parentHTMLID(), edit.session) + if edit.created { + updateInnerHTML(edit.parentHTMLID(), edit.session) + } + edit.propertyChangedEvent(tag) } + } - case Wrap: + case Wrap: + if exists { oldWrap := IsEditViewWrap(edit, "") delete(edit.properties, tag) if GetEditViewType(edit, "") == MultiLineText { if wrap := IsEditViewWrap(edit, ""); wrap != oldWrap { - if wrap { - updateProperty(edit.htmlID(), "wrap", "soft", edit.session) - } else { - updateProperty(edit.htmlID(), "wrap", "off", edit.session) + if edit.created { + if wrap { + updateProperty(edit.htmlID(), "wrap", "soft", edit.session) + } else { + updateProperty(edit.htmlID(), "wrap", "off", edit.session) + } } + edit.propertyChangedEvent(tag) } } - - default: - edit.viewData.remove(tag) } + + default: + edit.viewData.remove(tag) + return } } @@ -161,14 +196,16 @@ func (edit *editViewData) set(tag string, value interface{}) bool { edit.properties[Text] = text if text = GetText(edit, ""); oldText != text { edit.textChanged(text) - if GetEditViewType(edit, "") == MultiLineText { - updateInnerHTML(edit.htmlID(), edit.Session()) - } else { - text = strings.ReplaceAll(text, `"`, `\"`) - text = strings.ReplaceAll(text, `'`, `\'`) - text = strings.ReplaceAll(text, "\n", `\n`) - text = strings.ReplaceAll(text, "\r", `\r`) - edit.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, edit.htmlID(), text)) + if edit.created { + if GetEditViewType(edit, "") == MultiLineText { + updateInnerHTML(edit.htmlID(), edit.Session()) + } else { + text = strings.ReplaceAll(text, `"`, `\"`) + text = strings.ReplaceAll(text, `'`, `\'`) + text = strings.ReplaceAll(text, "\n", `\n`) + text = strings.ReplaceAll(text, "\r", `\r`) + edit.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, edit.htmlID(), text)) + } } } return true @@ -180,11 +217,14 @@ func (edit *editViewData) set(tag string, value interface{}) bool { if text, ok := value.(string); ok { edit.properties[Hint] = text if text = GetHint(edit, ""); oldText != text { - if text != "" { - updateProperty(edit.htmlID(), "placeholder", text, edit.session) - } else { - removeProperty(edit.htmlID(), "placeholder", edit.session) + if edit.created { + if text != "" { + updateProperty(edit.htmlID(), "placeholder", text, edit.session) + } else { + removeProperty(edit.htmlID(), "placeholder", edit.session) + } } + edit.propertyChangedEvent(tag) } return true } @@ -194,11 +234,14 @@ func (edit *editViewData) set(tag string, value interface{}) bool { oldMaxLength := GetMaxLength(edit, "") if edit.setIntProperty(MaxLength, value) { if maxLength := GetMaxLength(edit, ""); maxLength != oldMaxLength { - if maxLength > 0 { - updateProperty(edit.htmlID(), "maxlength", strconv.Itoa(maxLength), edit.session) - } else { - removeProperty(edit.htmlID(), "maxlength", edit.session) + if edit.created { + if maxLength > 0 { + updateProperty(edit.htmlID(), "maxlength", strconv.Itoa(maxLength), edit.session) + } else { + removeProperty(edit.htmlID(), "maxlength", edit.session) + } } + edit.propertyChangedEvent(tag) } return true } @@ -206,18 +249,24 @@ func (edit *editViewData) set(tag string, value interface{}) bool { case ReadOnly: if edit.setBoolProperty(ReadOnly, value) { - if IsReadOnly(edit, "") { - updateProperty(edit.htmlID(), ReadOnly, "", edit.session) - } else { - removeProperty(edit.htmlID(), ReadOnly, edit.session) + if edit.created { + if IsReadOnly(edit, "") { + updateProperty(edit.htmlID(), ReadOnly, "", edit.session) + } else { + removeProperty(edit.htmlID(), ReadOnly, edit.session) + } } + edit.propertyChangedEvent(tag) return true } return false case Spellcheck: if edit.setBoolProperty(Spellcheck, value) { - updateBoolProperty(edit.htmlID(), Spellcheck, IsSpellcheck(edit, ""), edit.session) + if edit.created { + updateBoolProperty(edit.htmlID(), Spellcheck, IsSpellcheck(edit, ""), edit.session) + } + edit.propertyChangedEvent(tag) return true } return false @@ -227,11 +276,14 @@ func (edit *editViewData) set(tag string, value interface{}) bool { if text, ok := value.(string); ok { edit.properties[Pattern] = text if text = GetEditViewPattern(edit, ""); oldText != text { - if text != "" { - updateProperty(edit.htmlID(), Pattern, text, edit.session) - } else { - removeProperty(edit.htmlID(), Pattern, edit.session) + if edit.created { + if text != "" { + updateProperty(edit.htmlID(), Pattern, text, edit.session) + } else { + removeProperty(edit.htmlID(), Pattern, edit.session) + } } + edit.propertyChangedEvent(tag) } return true } @@ -241,7 +293,10 @@ func (edit *editViewData) set(tag string, value interface{}) bool { oldType := GetEditViewType(edit, "") if edit.setEnumProperty(EditViewType, value, enumProperties[EditViewType].values) { if GetEditViewType(edit, "") != oldType { - updateInnerHTML(edit.parentHTMLID(), edit.session) + if edit.created { + updateInnerHTML(edit.parentHTMLID(), edit.session) + } + edit.propertyChangedEvent(tag) } return true } @@ -252,11 +307,14 @@ func (edit *editViewData) set(tag string, value interface{}) bool { if edit.setBoolProperty(Wrap, value) { if GetEditViewType(edit, "") == MultiLineText { if wrap := IsEditViewWrap(edit, ""); wrap != oldWrap { - if wrap { - updateProperty(edit.htmlID(), "wrap", "soft", edit.session) - } else { - updateProperty(edit.htmlID(), "wrap", "off", edit.session) + if edit.created { + if wrap { + updateProperty(edit.htmlID(), "wrap", "soft", edit.session) + } else { + updateProperty(edit.htmlID(), "wrap", "off", edit.session) + } } + edit.propertyChangedEvent(tag) } } return true @@ -268,6 +326,7 @@ func (edit *editViewData) set(tag string, value interface{}) bool { if !ok { notCompatibleType(tag, value) } + edit.propertyChangedEvent(tag) return ok } @@ -363,6 +422,7 @@ func (edit *editViewData) textChanged(newText string) { for _, listener := range edit.textChangeListeners { listener(edit, newText) } + edit.propertyChangedEvent(Text) } func (edit *editViewData) htmlTag() string { @@ -446,7 +506,7 @@ 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) diff --git a/filePicker.go b/filePicker.go index 4cbff32..5faac07 100644 --- a/filePicker.go +++ b/filePicker.go @@ -114,7 +114,17 @@ func (picker *filePickerData) Remove(tag string) { func (picker *filePickerData) remove(tag string) { switch tag { case FileSelectedEvent: - picker.fileSelectedListeners = []func(FilePicker, []FileInfo){} + if len(picker.fileSelectedListeners) > 0 { + picker.fileSelectedListeners = []func(FilePicker, []FileInfo){} + picker.propertyChangedEvent(tag) + } + + case Accept: + delete(picker.properties, tag) + if picker.created { + removeProperty(picker.htmlID(), "accept", picker.Session()) + } + picker.propertyChangedEvent(tag) default: picker.viewData.remove(tag) @@ -184,6 +194,7 @@ func (picker *filePickerData) set(tag string, value interface{}) bool { } picker.fileSelectedListeners = listeners } + picker.propertyChangedEvent(tag) return true case Accept: @@ -218,6 +229,15 @@ func (picker *filePickerData) set(tag string, value interface{}) bool { notCompatibleType(tag, value) return false } + + if picker.created { + if css := picker.acceptCSS(); css != "" { + updateProperty(picker.htmlID(), "accept", css, picker.Session()) + } else { + removeProperty(picker.htmlID(), "accept", picker.Session()) + } + } + picker.propertyChangedEvent(tag) return true default: @@ -271,7 +291,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) diff --git a/gridLayout.go b/gridLayout.go index 3758f80..3313b63 100644 --- a/gridLayout.go +++ b/gridLayout.go @@ -216,19 +216,22 @@ func (gridLayout *gridLayoutData) remove(tag string) { if tag == Gap { gridLayout.remove(GridRowGap) gridLayout.remove(GridColumnGap) + gridLayout.propertyChangedEvent(Gap) return } gridLayout.viewsContainerData.remove(tag) - switch tag { - case CellWidth: - updateCSSProperty(gridLayout.htmlID(), `grid-template-columns`, - gridLayout.gridCellSizesCSS(CellWidth, gridLayout.session), gridLayout.session) + if gridLayout.created { + switch tag { + case CellWidth: + updateCSSProperty(gridLayout.htmlID(), `grid-template-columns`, + gridLayout.gridCellSizesCSS(CellWidth, gridLayout.session), gridLayout.session) - case CellHeight: - updateCSSProperty(gridLayout.htmlID(), `grid-template-rows`, - gridLayout.gridCellSizesCSS(CellHeight, gridLayout.session), gridLayout.session) + case CellHeight: + updateCSSProperty(gridLayout.htmlID(), `grid-template-rows`, + gridLayout.gridCellSizesCSS(CellHeight, gridLayout.session), gridLayout.session) + } } } @@ -243,19 +246,25 @@ func (gridLayout *gridLayoutData) set(tag string, value interface{}) bool { } if tag == Gap { - return gridLayout.set(GridRowGap, value) && gridLayout.set(GridColumnGap, value) + if gridLayout.set(GridRowGap, value) && gridLayout.set(GridColumnGap, value) { + gridLayout.propertyChangedEvent(Gap) + return true + } + return false } if gridLayout.viewsContainerData.set(tag, value) { - switch tag { - case CellWidth: - updateCSSProperty(gridLayout.htmlID(), `grid-template-columns`, - gridLayout.gridCellSizesCSS(CellWidth, gridLayout.session), gridLayout.session) + if gridLayout.created { + switch tag { + case CellWidth: + updateCSSProperty(gridLayout.htmlID(), `grid-template-columns`, + gridLayout.gridCellSizesCSS(CellWidth, gridLayout.session), gridLayout.session) - case CellHeight: - updateCSSProperty(gridLayout.htmlID(), `grid-template-rows`, - gridLayout.gridCellSizesCSS(CellHeight, gridLayout.session), gridLayout.session) + case CellHeight: + updateCSSProperty(gridLayout.htmlID(), `grid-template-rows`, + gridLayout.gridCellSizesCSS(CellHeight, gridLayout.session), gridLayout.session) + } } return true } diff --git a/imageView.go b/imageView.go index 15cd908..0e2eb6d 100644 --- a/imageView.go +++ b/imageView.go @@ -67,7 +67,7 @@ func (imageView *imageViewData) normalizeTag(tag string) string { case HorizontalAlign: tag = ImageHorizontalAlign - case altProperty: + case altTag: tag = AltText } return tag @@ -79,16 +79,18 @@ func (imageView *imageViewData) Remove(tag string) { func (imageView *imageViewData) remove(tag string) { imageView.viewData.remove(tag) - switch tag { - case Source: - updateProperty(imageView.htmlID(), "src", "", imageView.session) - removeProperty(imageView.htmlID(), "srcset", imageView.session) + if imageView.created { + switch tag { + case Source: + updateProperty(imageView.htmlID(), "src", "", imageView.session) + removeProperty(imageView.htmlID(), "srcset", imageView.session) - case AltText: - updateInnerHTML(imageView.htmlID(), imageView.session) + case AltText: + updateInnerHTML(imageView.htmlID(), imageView.session) - case ImageVerticalAlign, ImageHorizontalAlign: - updateCSSStyle(imageView.htmlID(), imageView.session) + case ImageVerticalAlign, ImageHorizontalAlign: + updateCSSStyle(imageView.htmlID(), imageView.session) + } } } @@ -106,29 +108,37 @@ func (imageView *imageViewData) set(tag string, value interface{}) bool { case Source: if text, ok := value.(string); ok { imageView.properties[Source] = text - updateProperty(imageView.htmlID(), "src", text, imageView.session) - if srcset := imageView.srcSet(text); srcset != "" { - updateProperty(imageView.htmlID(), "srcset", srcset, imageView.session) - } else { - removeProperty(imageView.htmlID(), "srcset", imageView.session) + if imageView.created { + updateProperty(imageView.htmlID(), "src", text, imageView.session) + if srcset := imageView.srcSet(text); srcset != "" { + updateProperty(imageView.htmlID(), "srcset", srcset, imageView.session) + } else { + removeProperty(imageView.htmlID(), "srcset", imageView.session) + } } + imageView.propertyChangedEvent(Source) return true } - notCompatibleType(tag, value) + notCompatibleType(Source, value) case AltText: if text, ok := value.(string); ok { imageView.properties[AltText] = text - updateInnerHTML(imageView.htmlID(), imageView.session) + if imageView.created { + updateInnerHTML(imageView.htmlID(), imageView.session) + } + imageView.propertyChangedEvent(Source) return true } notCompatibleType(tag, value) default: if imageView.viewData.set(tag, value) { - switch tag { - case ImageVerticalAlign, ImageHorizontalAlign: - updateCSSStyle(imageView.htmlID(), imageView.session) + if imageView.created { + switch tag { + case ImageVerticalAlign, ImageHorizontalAlign: + updateCSSStyle(imageView.htmlID(), imageView.session) + } } return true } diff --git a/listAdapter.go b/listAdapter.go index efa3d69..fccd642 100644 --- a/listAdapter.go +++ b/listAdapter.go @@ -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 } diff --git a/listLayout.go b/listLayout.go index 442efec..31ce6d6 100644 --- a/listLayout.go +++ b/listLayout.go @@ -55,9 +55,11 @@ func (listLayout *listLayoutData) Remove(tag string) { func (listLayout *listLayoutData) remove(tag string) { listLayout.viewsContainerData.remove(tag) - switch tag { - case Orientation, Wrap, HorizontalAlign, VerticalAlign: - updateCSSStyle(listLayout.htmlID(), listLayout.session) + if listLayout.created { + switch tag { + case Orientation, Wrap, HorizontalAlign, VerticalAlign: + updateCSSStyle(listLayout.htmlID(), listLayout.session) + } } } @@ -72,9 +74,11 @@ func (listLayout *listLayoutData) set(tag string, value interface{}) bool { } if listLayout.viewsContainerData.set(tag, value) { - switch tag { - case Orientation, Wrap, HorizontalAlign, VerticalAlign: - updateCSSStyle(listLayout.htmlID(), listLayout.session) + if listLayout.created { + switch tag { + case Orientation, Wrap, HorizontalAlign, VerticalAlign: + updateCSSStyle(listLayout.htmlID(), listLayout.session) + } } return true } diff --git a/listView.go b/listView.go index 0f4f0f3..44b2f6c 100644 --- a/listView.go +++ b/listView.go @@ -114,46 +114,71 @@ func (listView *listViewData) Remove(tag string) { func (listView *listViewData) remove(tag string) { switch tag { case Checked: - listView.checkedItem = []int{} - updateInnerHTML(listView.htmlID(), listView.session) + if len(listView.checkedItem) > 0 { + listView.checkedItem = []int{} + if listView.created { + updateInnerHTML(listView.htmlID(), listView.session) + } + listView.propertyChangedEvent(tag) + } case Items: - listView.adapter = nil - updateInnerHTML(listView.htmlID(), listView.session) + if listView.adapter != nil { + listView.adapter = nil + if listView.created { + updateInnerHTML(listView.htmlID(), listView.session) + } + listView.propertyChangedEvent(tag) + } case Orientation, Wrap: - delete(listView.properties, tag) - updateCSSStyle(listView.htmlID(), listView.session) + if _, ok := listView.properties[tag]; ok { + delete(listView.properties, tag) + if listView.created { + updateCSSStyle(listView.htmlID(), listView.session) + } + listView.propertyChangedEvent(tag) + } case Current: current := GetListViewCurrent(listView, "") delete(listView.properties, tag) - updateInnerHTML(listView.htmlID(), listView.session) + if listView.created { + updateInnerHTML(listView.htmlID(), listView.session) + } if current != -1 { for _, listener := range listView.selectedListeners { listener(listView, -1) } + listView.propertyChangedEvent(tag) } case ItemWidth, ItemHeight, ItemHorizontalAlign, ItemVerticalAlign, ItemCheckbox, CheckboxHorizontalAlign, CheckboxVerticalAlign, ListItemStyle, CurrentStyle, CurrentInactiveStyle: - - delete(listView.properties, tag) - updateInnerHTML(listView.htmlID(), listView.session) + if _, ok := listView.properties[tag]; ok { + delete(listView.properties, tag) + if listView.created { + updateInnerHTML(listView.htmlID(), listView.session) + } + listView.propertyChangedEvent(tag) + } case ListItemClickedEvent: if len(listView.clickedListeners) > 0 { listView.clickedListeners = []func(ListView, int){} + listView.propertyChangedEvent(tag) } case ListItemSelectedEvent: if len(listView.selectedListeners) > 0 { listView.selectedListeners = []func(ListView, int){} + listView.propertyChangedEvent(tag) } case ListItemCheckedEvent: if len(listView.checkedListeners) > 0 { listView.checkedListeners = []func(ListView, []int){} + listView.propertyChangedEvent(tag) } default: @@ -171,10 +196,7 @@ func (listView *listViewData) set(tag string, value interface{}) bool { return true } - result := false - switch tag { - case ListItemClickedEvent: listeners := listView.valueToItemListeners(value) if listeners == nil { @@ -182,6 +204,7 @@ func (listView *listViewData) set(tag string, value interface{}) bool { return false } listView.clickedListeners = listeners + listView.propertyChangedEvent(tag) return true case ListItemSelectedEvent: @@ -191,44 +214,56 @@ func (listView *listViewData) set(tag string, value interface{}) bool { return false } listView.selectedListeners = listeners + listView.propertyChangedEvent(tag) return true case ListItemCheckedEvent: - return listView.setItemCheckedEvent(value) + if !listView.setItemCheckedEvent(value) { + return false + } + listView.propertyChangedEvent(tag) + return true case Checked: - return listView.setChecked(value) + if !listView.setChecked(value) { + return false + } case Items: - result = listView.setItems(value) + if !listView.setItems(value) { + return false + } case Current: oldCurrent := GetListViewCurrent(listView, "") - if listView.setIntProperty(Current, value) { - current := GetListViewCurrent(listView, "") - if oldCurrent != current { - updateInnerHTML(listView.htmlID(), listView.session) - for _, listener := range listView.selectedListeners { - listener(listView, current) - } - } + if !listView.setIntProperty(Current, value) { + return false + } + current := GetListViewCurrent(listView, "") + if oldCurrent == current { return true } - case Orientation, Wrap, VerticalAlign, HorizontalAlign, Style, StyleDisabled: - result = listView.viewData.set(tag, value) + for _, listener := range listView.selectedListeners { + listener(listView, current) + } - case ItemWidth, ItemHeight: - result = listView.setSizeProperty(tag, value) + case Orientation, Wrap, VerticalAlign, HorizontalAlign, Style, StyleDisabled, ItemWidth, ItemHeight: + result := listView.viewData.set(tag, value) + if result && listView.created { + updateInnerHTML(listView.htmlID(), listView.session) + } + return result case ItemHorizontalAlign, ItemVerticalAlign, ItemCheckbox, CheckboxHorizontalAlign, CheckboxVerticalAlign: - result = listView.setEnumProperty(tag, value, enumProperties[tag].values) + if !listView.setEnumProperty(tag, value, enumProperties[tag].values) { + return false + } case ListItemStyle, CurrentStyle, CurrentInactiveStyle: switch value := value.(type) { case string: listView.properties[tag] = value - result = true default: notCompatibleType(tag, value) @@ -239,11 +274,11 @@ func (listView *listViewData) set(tag string, value interface{}) bool { return listView.viewData.set(tag, value) } - if result { + if listView.created { updateInnerHTML(listView.htmlID(), listView.session) } - - return result + listView.propertyChangedEvent(tag) + return true } func (listView *listViewData) setItemCheckedEvent(value interface{}) bool { @@ -1054,13 +1089,17 @@ func (listView *listViewData) handleCommand(self View, command string, data Data for _, listener := range listView.selectedListeners { listener(listView, number) } + listView.propertyChangedEvent(Current) } } case "itemUnselected": - delete(listView.properties, Current) - for _, listener := range listView.selectedListeners { - listener(listView, -1) + if _, ok := listView.properties[Current]; ok { + delete(listView.properties, Current) + for _, listener := range listView.selectedListeners { + listener(listView, -1) + } + listView.propertyChangedEvent(Current) } case "itemClick": @@ -1075,7 +1114,7 @@ func (listView *listViewData) handleCommand(self View, command string, data Data func (listView *listViewData) onItemClick() { current := GetListViewCurrent(listView, "") - if current >= 0 && !IsDisabled(listView) { + if current >= 0 && !IsDisabled(listView, "") { checkbox := GetListViewCheckbox(listView, "") m: switch checkbox { @@ -1115,6 +1154,7 @@ func (listView *listViewData) onItemClick() { for _, listener := range listView.checkedListeners { listener(listView, listView.checkedItem) } + listView.propertyChangedEvent(Checked) } for _, listener := range listView.clickedListeners { listener(listView, current) diff --git a/mediaPlayer.go b/mediaPlayer.go index d851f11..4c2534c 100644 --- a/mediaPlayer.go +++ b/mediaPlayer.go @@ -173,20 +173,8 @@ func (player *mediaPlayerData) Remove(tag string) { } func (player *mediaPlayerData) remove(tag string) { - switch tag { - - case Controls, Loop, Muted, Preload, AbortEvent, LoadStartEvent, PlayerErrorEvent, - CanPlayEvent, CanPlayThroughEvent, CompleteEvent, DurationChangedEvent, - EmptiedEvent, EndedEvent, LoadedDataEvent, LoadedMetadataEvent, PauseEvent, PlayEvent, - PlayingEvent, RateChangedEvent, SeekedEvent, SeekingEvent, StalledEvent, SuspendEvent, - ProgressEvent, TimeUpdateEvent, VolumeChangedEvent, WaitingEvent: - - player.viewData.remove(tag) - player.propertyChanged(tag) - - default: - player.viewData.remove(tag) - } + player.viewData.remove(tag) + player.propertyChanged(tag) } func (player *mediaPlayerData) Set(tag string, value interface{}) bool { @@ -216,6 +204,7 @@ func (player *mediaPlayerData) set(tag string, value interface{}) bool { player.properties[tag] = listeners } player.propertyChanged(tag) + player.propertyChangedEvent(tag) return true } notCompatibleType(tag, value) @@ -228,6 +217,7 @@ func (player *mediaPlayerData) set(tag string, value interface{}) bool { player.properties[tag] = listeners } player.propertyChanged(tag) + player.propertyChangedEvent(tag) return true } notCompatibleType(tag, value) @@ -240,6 +230,7 @@ func (player *mediaPlayerData) set(tag string, value interface{}) bool { player.properties[tag] = listeners } player.propertyChanged(tag) + player.propertyChangedEvent(tag) return true } notCompatibleType(tag, value) @@ -247,14 +238,14 @@ func (player *mediaPlayerData) set(tag string, value interface{}) bool { case Source: if player.setSource(value) { player.propertyChanged(tag) + player.propertyChangedEvent(tag) return true } default: - if player.viewData.set(tag, value) { - return true - } + return player.viewData.set(tag, value) } + return false } @@ -657,89 +648,88 @@ func playerEvents() []struct{ tag, cssTag string } { } func (player *mediaPlayerData) propertyChanged(tag string) { - switch tag { - case Controls, Loop: - value, _ := boolProperty(player, tag, player.Session()) - if value { - updateBoolProperty(player.htmlID(), tag, value, player.Session()) - } else { - removeProperty(player.htmlID(), tag, player.Session()) - } - - case Muted: - value, _ := boolProperty(player, tag, player.Session()) - if value { - player.Session().runScript("setMediaMuted('" + player.htmlID() + "', true)") - } else { - player.Session().runScript("setMediaMuted('" + player.htmlID() + "', false)") - } - - case Preload: - value, _ := enumProperty(player, tag, player.Session(), 0) - values := enumProperties[Preload].values - updateProperty(player.htmlID(), tag, values[value], player.Session()) - - case AbortEvent, CanPlayEvent, CanPlayThroughEvent, CompleteEvent, EmptiedEvent, - EndedEvent, LoadedDataEvent, LoadedMetadataEvent, PauseEvent, PlayEvent, PlayingEvent, ProgressEvent, - LoadStartEvent, SeekedEvent, SeekingEvent, StalledEvent, SuspendEvent, WaitingEvent: - - for _, event := range playerEvents() { - if event.tag == tag { - if value := player.getRaw(event.tag); value != nil { - switch value := value.(type) { - case []func(MediaPlayer): - if len(value) > 0 { - fn := fmt.Sprintf(`playerEvent(this, "%s")`, event.tag) - updateProperty(player.htmlID(), event.cssTag, fn, player.Session()) - return - } - } - } - updateProperty(player.htmlID(), tag, "", player.Session()) - break + if player.created { + switch tag { + case Controls, Loop: + value, _ := boolProperty(player, tag, player.Session()) + if value { + updateBoolProperty(player.htmlID(), tag, value, player.Session()) + } else { + removeProperty(player.htmlID(), tag, player.Session()) } - } - case TimeUpdateEvent: - if value := player.getRaw(tag); value != nil { - updateProperty(player.htmlID(), "ontimeupdate", "playerTimeUpdatedEvent(this)", player.Session()) - } else { - updateProperty(player.htmlID(), "ontimeupdate", "", player.Session()) - } + case Muted: + value, _ := boolProperty(player, tag, player.Session()) + if value { + player.Session().runScript("setMediaMuted('" + player.htmlID() + "', true)") + } else { + player.Session().runScript("setMediaMuted('" + player.htmlID() + "', false)") + } - case VolumeChangedEvent: - if value := player.getRaw(tag); value != nil { - updateProperty(player.htmlID(), "onvolumechange", "playerVolumeChangedEvent(this)", player.Session()) - } else { - updateProperty(player.htmlID(), "onvolumechange", "", player.Session()) + case Preload: + value, _ := enumProperty(player, tag, player.Session(), 0) + values := enumProperties[Preload].values + updateProperty(player.htmlID(), tag, values[value], player.Session()) + + case AbortEvent, CanPlayEvent, CanPlayThroughEvent, CompleteEvent, EmptiedEvent, + EndedEvent, LoadedDataEvent, LoadedMetadataEvent, PauseEvent, PlayEvent, PlayingEvent, ProgressEvent, + LoadStartEvent, SeekedEvent, SeekingEvent, StalledEvent, SuspendEvent, WaitingEvent: + + for _, event := range playerEvents() { + if event.tag == tag { + if value := player.getRaw(event.tag); value != nil { + switch value := value.(type) { + case []func(MediaPlayer): + if len(value) > 0 { + fn := fmt.Sprintf(`playerEvent(this, "%s")`, event.tag) + updateProperty(player.htmlID(), event.cssTag, fn, player.Session()) + return + } + } + } + updateProperty(player.htmlID(), tag, "", player.Session()) + break + } + + } + case TimeUpdateEvent: + if value := player.getRaw(tag); value != nil { + updateProperty(player.htmlID(), "ontimeupdate", "playerTimeUpdatedEvent(this)", player.Session()) + } else { + updateProperty(player.htmlID(), "ontimeupdate", "", player.Session()) + } + + case VolumeChangedEvent: + if value := player.getRaw(tag); value != nil { + updateProperty(player.htmlID(), "onvolumechange", "playerVolumeChangedEvent(this)", player.Session()) + } else { + updateProperty(player.htmlID(), "onvolumechange", "", player.Session()) + } + + case DurationChangedEvent: + if value := player.getRaw(tag); value != nil { + updateProperty(player.htmlID(), "ondurationchange", "playerDurationChangedEvent(this)", player.Session()) + } else { + updateProperty(player.htmlID(), "ondurationchange", "", player.Session()) + } + + case RateChangedEvent: + if value := player.getRaw(tag); value != nil { + updateProperty(player.htmlID(), "onratechange", "playerRateChangedEvent(this)", player.Session()) + } else { + updateProperty(player.htmlID(), "onratechange", "", player.Session()) + } + + case PlayerErrorEvent: + if value := player.getRaw(tag); value != nil { + updateProperty(player.htmlID(), "onerror", "playerErrorEvent(this)", player.Session()) + } else { + updateProperty(player.htmlID(), "onerror", "", player.Session()) + } + + case Source: + updateInnerHTML(player.htmlID(), player.Session()) } - - case DurationChangedEvent: - if value := player.getRaw(tag); value != nil { - updateProperty(player.htmlID(), "ondurationchange", "playerDurationChangedEvent(this)", player.Session()) - } else { - updateProperty(player.htmlID(), "ondurationchange", "", player.Session()) - } - - case RateChangedEvent: - if value := player.getRaw(tag); value != nil { - updateProperty(player.htmlID(), "onratechange", "playerRateChangedEvent(this)", player.Session()) - } else { - updateProperty(player.htmlID(), "onratechange", "", player.Session()) - } - - case PlayerErrorEvent: - if value := player.getRaw(tag); value != nil { - updateProperty(player.htmlID(), "onerror", "playerErrorEvent(this)", player.Session()) - } else { - updateProperty(player.htmlID(), "onerror", "", player.Session()) - } - - case Source: - updateInnerHTML(player.htmlID(), player.Session()) - - default: - player.viewData.propertyChanged(tag) } } diff --git a/numberPicker.go b/numberPicker.go index f7ffaab..5f5bf58 100644 --- a/numberPicker.go +++ b/numberPicker.go @@ -69,6 +69,7 @@ func (picker *numberPickerData) remove(tag string) { case NumberChangedEvent: if len(picker.numberChangedListeners) > 0 { picker.numberChangedListeners = []func(NumberPicker, float64){} + picker.propertyChangedEvent(tag) } default: @@ -135,18 +136,21 @@ func (picker *numberPickerData) set(tag string, value interface{}) bool { } picker.numberChangedListeners = listeners } + picker.propertyChangedEvent(tag) return true case NumberPickerValue: oldValue := GetNumberPickerValue(picker, "") min, max := GetNumberPickerMinMax(picker, "") if picker.setFloatProperty(NumberPickerValue, value, min, max) { - newValue := GetNumberPickerValue(picker, "") - if oldValue != newValue { - picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%f')`, picker.htmlID(), newValue)) + if newValue := GetNumberPickerValue(picker, ""); oldValue != newValue { + if picker.created { + picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%f')`, picker.htmlID(), newValue)) + } for _, listener := range picker.numberChangedListeners { listener(picker, newValue) } + picker.propertyChangedEvent(tag) } return true } @@ -161,34 +165,36 @@ func (picker *numberPickerData) set(tag string, value interface{}) bool { } func (picker *numberPickerData) propertyChanged(tag string) { - switch tag { - case NumberPickerType: - if GetNumberPickerType(picker, "") == NumberSlider { - updateProperty(picker.htmlID(), "type", "range", picker.session) - } else { - updateProperty(picker.htmlID(), "type", "number", picker.session) - } + if picker.created { + switch tag { + case NumberPickerType: + if GetNumberPickerType(picker, "") == NumberSlider { + updateProperty(picker.htmlID(), "type", "range", picker.session) + } else { + updateProperty(picker.htmlID(), "type", "number", picker.session) + } - case NumberPickerMin: - min, _ := GetNumberPickerMinMax(picker, "") - updateProperty(picker.htmlID(), Min, strconv.FormatFloat(min, 'f', -1, 32), picker.session) + case NumberPickerMin: + min, _ := GetNumberPickerMinMax(picker, "") + updateProperty(picker.htmlID(), Min, strconv.FormatFloat(min, 'f', -1, 32), picker.session) - case NumberPickerMax: - _, max := GetNumberPickerMinMax(picker, "") - updateProperty(picker.htmlID(), Max, strconv.FormatFloat(max, 'f', -1, 32), picker.session) + case NumberPickerMax: + _, max := GetNumberPickerMinMax(picker, "") + updateProperty(picker.htmlID(), Max, strconv.FormatFloat(max, 'f', -1, 32), picker.session) - case NumberPickerStep: - 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 NumberPickerStep: + 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, "") - picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%f')`, picker.htmlID(), value)) - for _, listener := range picker.numberChangedListeners { - listener(picker, value) + case NumberPickerValue: + value := GetNumberPickerValue(picker, "") + picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%f')`, picker.htmlID(), value)) + for _, listener := range picker.numberChangedListeners { + listener(picker, value) + } } } } @@ -246,7 +252,7 @@ func (picker *numberPickerData) htmlProperties(self View, buffer *strings.Builde } func (picker *numberPickerData) htmlDisabledProperties(self View, buffer *strings.Builder) { - if IsDisabled(self) { + if IsDisabled(self, "") { buffer.WriteString(` disabled`) } picker.viewData.htmlDisabledProperties(self, buffer) diff --git a/progressBar.go b/progressBar.go index c1b0814..386af5c 100644 --- a/progressBar.go +++ b/progressBar.go @@ -58,12 +58,14 @@ func (progress *progressBarData) remove(tag string) { } func (progress *progressBarData) propertyChanged(tag string) { - switch tag { - case ProgressBarMax: - updateProperty(progress.htmlID(), Max, strconv.FormatFloat(GetProgressBarMax(progress, ""), 'f', -1, 32), progress.session) + if progress.created { + switch tag { + case ProgressBarMax: + 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) + case ProgressBarValue: + updateProperty(progress.htmlID(), Value, strconv.FormatFloat(GetProgressBarValue(progress, ""), 'f', -1, 32), progress.session) + } } } diff --git a/propertyNames.go b/propertyNames.go index d460f8e..e4891ad 100644 --- a/propertyNames.go +++ b/propertyNames.go @@ -367,8 +367,8 @@ const ( // CenterY is the constant for the "center-x" property tag. CenterY = "center-y" // AltText is the constant for the "alt-text" property tag. - AltText = "alt-text" - altProperty = "alt" + AltText = "alt-text" + altTag = "alt" // AvoidBreak is the constant for the "avoid-break" property tag. // The "avoid-break" bool property sets how region breaks should behave inside a generated box. // If the property value is "true" then fvoids any break from being inserted within the principal box. diff --git a/propertySet.go b/propertySet.go index f7870f5..5948b8a 100644 --- a/propertySet.go +++ b/propertySet.go @@ -65,6 +65,7 @@ var intProperties = []string{ FootHeight, RowSpan, ColumnSpan, + ColumnCount, } var floatProperties = map[string]struct{ min, max float64 }{ diff --git a/resizable.go b/resizable.go index 52c28f9..816a206 100644 --- a/resizable.go +++ b/resizable.go @@ -75,8 +75,11 @@ func (resizable *resizableData) remove(tag string) { oldSide := resizable.getSide() delete(resizable.properties, Side) if oldSide != resizable.getSide() { - updateInnerHTML(resizable.htmlID(), resizable.Session()) - resizable.updateResizeBorderWidth() + if resizable.created { + updateInnerHTML(resizable.htmlID(), resizable.Session()) + resizable.updateResizeBorderWidth() + } + resizable.propertyChangedEvent(tag) } case ResizeBorderWidth: @@ -84,12 +87,16 @@ func (resizable *resizableData) remove(tag string) { delete(resizable.properties, ResizeBorderWidth) if !w.Equal(resizable.resizeBorderWidth()) { resizable.updateResizeBorderWidth() + resizable.propertyChangedEvent(tag) } case Content: if len(resizable.content) > 0 { resizable.content = []View{} - updateInnerHTML(resizable.htmlID(), resizable.Session()) + if resizable.created { + updateInnerHTML(resizable.htmlID(), resizable.Session()) + } + resizable.propertyChangedEvent(tag) } default: @@ -110,20 +117,25 @@ func (resizable *resizableData) set(tag string, value interface{}) bool { switch tag { case Side: oldSide := resizable.getSide() - ok := resizable.setSide(value) - if ok && oldSide != resizable.getSide() { - updateInnerHTML(resizable.htmlID(), resizable.Session()) - resizable.updateResizeBorderWidth() - } else { + if !resizable.setSide(value) { notCompatibleType(tag, value) + return false } - return ok + if oldSide != resizable.getSide() { + if resizable.created { + updateInnerHTML(resizable.htmlID(), resizable.Session()) + resizable.updateResizeBorderWidth() + } + resizable.propertyChangedEvent(tag) + } + return true case ResizeBorderWidth: w := resizable.resizeBorderWidth() ok := resizable.setSizeProperty(tag, value) if ok && !w.Equal(resizable.resizeBorderWidth()) { resizable.updateResizeBorderWidth() + resizable.propertyChangedEvent(tag) } return ok @@ -139,18 +151,25 @@ func (resizable *resizableData) set(tag string, value interface{}) bool { case DataObject: if view := CreateViewFromObject(resizable.Session(), value); view != nil { newContent = view + } else { + return false } + + default: + notCompatibleType(tag, value) + return false } - if newContent != nil { - if len(resizable.content) == 0 { - resizable.content = []View{newContent} - } else { - resizable.content[0] = newContent - } - updateInnerHTML(resizable.htmlID(), resizable.Session()) - return true + if len(resizable.content) == 0 { + resizable.content = []View{newContent} + } else { + resizable.content[0] = newContent } + if resizable.created { + updateInnerHTML(resizable.htmlID(), resizable.Session()) + } + resizable.propertyChangedEvent(tag) + return true case CellWidth, CellHeight, GridRowGap, GridColumnGap, CellVerticalAlign, CellHorizontalAlign: ErrorLogF(`Not supported "%s" property`, tag) @@ -306,12 +325,14 @@ func (resizable *resizableData) resizeBorderWidth() SizeUnit { } func (resizable *resizableData) updateResizeBorderWidth() { - htmlID := resizable.htmlID() - session := resizable.Session() - column, row := resizable.cellSizeCSS() + if resizable.created { + htmlID := resizable.htmlID() + session := resizable.Session() + column, row := resizable.cellSizeCSS() - updateCSSProperty(htmlID, "grid-template-columns", column, session) - updateCSSProperty(htmlID, "grid-template-rows", row, session) + updateCSSProperty(htmlID, "grid-template-columns", column, session) + updateCSSProperty(htmlID, "grid-template-rows", row, session) + } } func (resizable *resizableData) cellSizeCSS() (string, string) { diff --git a/stackLayout.go b/stackLayout.go index 0861779..9f1c7c5 100644 --- a/stackLayout.go +++ b/stackLayout.go @@ -6,6 +6,8 @@ import ( "strings" ) +// TODO PeekChangedEvent + const ( // DefaultAnimation - default animation of StackLayout push DefaultAnimation = 0 @@ -93,23 +95,33 @@ func (layout *stackLayoutData) popFinished(view View, tag string) { } func (layout *stackLayoutData) Set(tag string, value interface{}) bool { - if strings.ToLower(tag) == TransitionEndEvent { + return layout.set(strings.ToLower(tag), value) +} + +func (layout *stackLayoutData) set(tag string, value interface{}) bool { + if tag == TransitionEndEvent { listeners, ok := valueToAnimationListeners(value) if ok { listeners = append(listeners, layout.pushFinished) listeners = append(listeners, layout.popFinished) layout.properties[TransitionEndEvent] = listeners + layout.propertyChangedEvent(TransitionEndEvent) } return ok } - return layout.viewsContainerData.Set(tag, value) + return layout.viewsContainerData.set(tag, value) } func (layout *stackLayoutData) Remove(tag string) { - if strings.ToLower(tag) == TransitionEndEvent { + layout.remove(strings.ToLower(tag)) +} + +func (layout *stackLayoutData) remove(tag string) { + if tag == TransitionEndEvent { layout.properties[TransitionEndEvent] = []func(View, string){layout.pushFinished, layout.popFinished} + layout.propertyChangedEvent(TransitionEndEvent) } else { - layout.viewsContainerData.Remove(tag) + layout.viewsContainerData.remove(tag) } } @@ -238,6 +250,7 @@ func (layout *stackLayoutData) Push(view View, animation int, onPushFinished fun layout.views = append(layout.views, view) view.setParentID(htmlID) + layout.propertyChangedEvent(Content) } func (layout *stackLayoutData) Pop(animation int, onPopFinished func(View)) bool { @@ -286,6 +299,7 @@ func (layout *stackLayoutData) Pop(animation int, onPopFinished func(View)) bool } updateCSSProperty(htmlID+"pop", "transform", value, layout.session) + layout.propertyChangedEvent(Content) return true } diff --git a/tableView.go b/tableView.go index 9b391bc..b5d82ba 100644 --- a/tableView.go +++ b/tableView.go @@ -168,35 +168,51 @@ func (table *tableViewData) Init(session Session) { table.tag = "TableView" } +func (table *tableViewData) normalizeTag(tag string) string { + switch tag = strings.ToLower(tag); tag { + case "top-cell-padding": + tag = CellPaddingTop + + case "right-cell-padding": + tag = CellPaddingRight + + case "bottom-cell-padding": + tag = CellPaddingBottom + + case "left-cell-padding": + tag = CellPaddingLeft + } + return tag +} + func (table *tableViewData) Get(tag string) interface{} { - return table.get(strings.ToLower(tag)) + return table.get(table.normalizeTag(tag)) } func (table *tableViewData) Remove(tag string) { - table.remove(strings.ToLower(tag)) + table.remove(table.normalizeTag(tag)) } func (table *tableViewData) remove(tag string) { switch tag { - - case CellPaddingTop, CellPaddingRight, CellPaddingBottom, CellPaddingLeft, - "top-cell-padding", "right-cell-padding", "bottom-cell-padding", "left-cell-padding": + case CellPaddingTop, CellPaddingRight, CellPaddingBottom, CellPaddingLeft: table.removeBoundsSide(CellPadding, tag) + table.propertyChanged(tag) case Gap, CellBorder, CellPadding, RowStyle, ColumnStyle, CellStyle, HeadHeight, HeadStyle, FootHeight, FootStyle: - delete(table.properties, tag) + if _, ok := table.properties[tag]; ok { + delete(table.properties, tag) + table.propertyChanged(tag) + } default: table.viewData.remove(tag) - return } - - table.propertyChanged(tag) } func (table *tableViewData) Set(tag string, value interface{}) bool { - return table.set(strings.ToLower(tag), value) + return table.set(table.normalizeTag(tag), value) } func (table *tableViewData) set(tag string, value interface{}) bool { @@ -307,8 +323,7 @@ func (table *tableViewData) set(tag string, value interface{}) bool { return false } - case CellPaddingTop, CellPaddingRight, CellPaddingBottom, CellPaddingLeft, - "top-cell-padding", "right-cell-padding", "bottom-cell-padding", "left-cell-padding": + case CellPaddingTop, CellPaddingRight, CellPaddingBottom, CellPaddingLeft: if !table.setBoundsSide(CellPadding, tag, value) { return false } @@ -336,26 +351,27 @@ func (table *tableViewData) set(tag string, value interface{}) bool { } func (table *tableViewData) propertyChanged(tag string) { - switch tag { - case Content, RowStyle, ColumnStyle, CellStyle, CellPadding, CellBorder, - HeadHeight, HeadStyle, FootHeight, FootStyle, - CellPaddingTop, CellPaddingRight, CellPaddingBottom, CellPaddingLeft, - "top-cell-padding", "right-cell-padding", "bottom-cell-padding", "left-cell-padding": - table.ReloadTableData() + if table.created { + switch tag { + case Content, RowStyle, ColumnStyle, CellStyle, CellPadding, CellBorder, + HeadHeight, HeadStyle, FootHeight, FootStyle, + CellPaddingTop, CellPaddingRight, CellPaddingBottom, CellPaddingLeft: + table.ReloadTableData() - case Gap: - htmlID := table.htmlID() - session := table.Session() - gap, ok := sizeProperty(table, Gap, session) - if !ok || gap.Type == Auto || gap.Value <= 0 { - updateCSSProperty(htmlID, "border-spacing", "0", session) - updateCSSProperty(htmlID, "border-collapse", "collapse", session) - } else { - updateCSSProperty(htmlID, "border-spacing", gap.cssString("0"), session) - updateCSSProperty(htmlID, "border-collapse", "separate", session) + case Gap: + htmlID := table.htmlID() + session := table.Session() + gap, ok := sizeProperty(table, Gap, session) + if !ok || gap.Type == Auto || gap.Value <= 0 { + updateCSSProperty(htmlID, "border-spacing", "0", session) + updateCSSProperty(htmlID, "border-collapse", "collapse", session) + } else { + updateCSSProperty(htmlID, "border-spacing", gap.cssString("0"), session) + updateCSSProperty(htmlID, "border-collapse", "separate", session) + } } - } + table.propertyChangedEvent(tag) } func (table *tableViewData) htmlTag() string { diff --git a/tabsLayout.go b/tabsLayout.go index 0c00641..967bc98 100644 --- a/tabsLayout.go +++ b/tabsLayout.go @@ -123,15 +123,26 @@ func (tabsLayout *tabsLayoutData) Remove(tag string) { func (tabsLayout *tabsLayoutData) remove(tag string) { switch tag { case CurrentTabChangedEvent: - tabsLayout.tabListener = []func(TabsLayout, int, int){} + if len(tabsLayout.tabListener) > 0 { + tabsLayout.tabListener = []func(TabsLayout, int, int){} + tabsLayout.propertyChangedEvent(tag) + } + return case TabCloseEvent: - tabsLayout.tabCloseListener = []func(TabsLayout, int){} + if len(tabsLayout.tabCloseListener) > 0 { + tabsLayout.tabCloseListener = []func(TabsLayout, int){} + tabsLayout.propertyChangedEvent(tag) + } + return case Current: oldCurrent := tabsLayout.currentItem() delete(tabsLayout.properties, Current) - if !tabsLayout.session.ignoreViewUpdates() && oldCurrent != 0 { + if oldCurrent == 0 { + return + } + if tabsLayout.created { tabsLayout.session.runScript(fmt.Sprintf("activateTab(%v, %d);", tabsLayout.htmlID(), 0)) for _, listener := range tabsLayout.tabListener { listener(tabsLayout, 0, oldCurrent) @@ -140,7 +151,7 @@ func (tabsLayout *tabsLayoutData) remove(tag string) { case Tabs: delete(tabsLayout.properties, Tabs) - if !tabsLayout.session.ignoreViewUpdates() { + if tabsLayout.created { htmlID := tabsLayout.htmlID() updateProperty(htmlID, inactiveTabStyle, tabsLayout.inactiveTabStyle(), tabsLayout.session) updateProperty(htmlID, activeTabStyle, tabsLayout.activeTabStyle(), tabsLayout.session) @@ -150,7 +161,7 @@ func (tabsLayout *tabsLayoutData) remove(tag string) { case TabStyle, CurrentTabStyle: delete(tabsLayout.properties, tag) - if !tabsLayout.session.ignoreViewUpdates() { + if tabsLayout.created { htmlID := tabsLayout.htmlID() updateProperty(htmlID, inactiveTabStyle, tabsLayout.inactiveTabStyle(), tabsLayout.session) updateProperty(htmlID, activeTabStyle, tabsLayout.activeTabStyle(), tabsLayout.session) @@ -159,13 +170,16 @@ func (tabsLayout *tabsLayoutData) remove(tag string) { case TabCloseButton: delete(tabsLayout.properties, tag) - if !tabsLayout.session.ignoreViewUpdates() { + if tabsLayout.created { updateInnerHTML(tabsLayout.htmlID(), tabsLayout.session) } default: tabsLayout.viewsContainerData.remove(tag) + return } + + tabsLayout.propertyChangedEvent(tag) } func (tabsLayout *tabsLayoutData) Set(tag string, value interface{}) bool { @@ -201,13 +215,14 @@ func (tabsLayout *tabsLayoutData) set(tag string, value interface{}) bool { return false } - if !tabsLayout.session.ignoreViewUpdates() { - current := tabsLayout.currentItem() - if oldCurrent != current { - tabsLayout.session.runScript(fmt.Sprintf("activateTab(%v, %d);", tabsLayout.htmlID(), current)) - for _, listener := range tabsLayout.tabListener { - listener(tabsLayout, current, oldCurrent) - } + current := tabsLayout.currentItem() + if oldCurrent == current { + return true + } + if tabsLayout.created { + tabsLayout.session.runScript(fmt.Sprintf("activateTab(%v, %d);", tabsLayout.htmlID(), current)) + for _, listener := range tabsLayout.tabListener { + listener(tabsLayout, current, oldCurrent) } } @@ -215,7 +230,7 @@ func (tabsLayout *tabsLayoutData) set(tag string, value interface{}) bool { if !tabsLayout.setEnumProperty(Tabs, value, enumProperties[Tabs].values) { return false } - if !tabsLayout.session.ignoreViewUpdates() { + if tabsLayout.created { htmlID := tabsLayout.htmlID() updateProperty(htmlID, inactiveTabStyle, tabsLayout.inactiveTabStyle(), tabsLayout.session) updateProperty(htmlID, activeTabStyle, tabsLayout.activeTabStyle(), tabsLayout.session) @@ -235,7 +250,7 @@ func (tabsLayout *tabsLayoutData) set(tag string, value interface{}) bool { return false } - if !tabsLayout.session.ignoreViewUpdates() { + if tabsLayout.created { htmlID := tabsLayout.htmlID() updateProperty(htmlID, inactiveTabStyle, tabsLayout.inactiveTabStyle(), tabsLayout.session) updateProperty(htmlID, activeTabStyle, tabsLayout.activeTabStyle(), tabsLayout.session) @@ -246,7 +261,7 @@ func (tabsLayout *tabsLayoutData) set(tag string, value interface{}) bool { if !tabsLayout.setBoolProperty(tag, value) { return false } - if !tabsLayout.session.ignoreViewUpdates() { + if tabsLayout.created { updateInnerHTML(tabsLayout.htmlID(), tabsLayout.session) } @@ -254,6 +269,7 @@ func (tabsLayout *tabsLayoutData) set(tag string, value interface{}) bool { return tabsLayout.viewsContainerData.set(tag, value) } + tabsLayout.propertyChangedEvent(tag) return true } @@ -905,6 +921,7 @@ func (tabsLayout *tabsLayoutData) handleCommand(self View, command string, data for _, listener := range tabsLayout.tabListener { listener(tabsLayout, number, current) } + tabsLayout.propertyChangedEvent(Current) } } } diff --git a/textView.go b/textView.go index bf74308..8751481 100644 --- a/textView.go +++ b/textView.go @@ -43,12 +43,14 @@ func (textView *textViewData) Remove(tag string) { func (textView *textViewData) remove(tag string) { textView.viewData.remove(tag) - switch tag { - case Text: - updateInnerHTML(textView.htmlID(), textView.session) + if textView.created { + switch tag { + case Text: + updateInnerHTML(textView.htmlID(), textView.session) - case TextOverflow: - textView.textOverflowUpdated() + case TextOverflow: + textView.textOverflowUpdated() + } } } @@ -90,16 +92,24 @@ func (textView *textViewData) set(tag string, value interface{}) bool { return false } } - updateInnerHTML(textView.htmlID(), textView.session) - return true + if textView.created { + updateInnerHTML(textView.htmlID(), textView.session) + } case TextOverflow: - if textView.viewData.set(tag, value) { + if !textView.viewData.set(tag, value) { + return false + } + if textView.created { textView.textOverflowUpdated() } + + default: + return textView.viewData.set(tag, value) } - return textView.viewData.set(tag, value) + textView.propertyChangedEvent(tag) + return true } func (textView *textViewData) textOverflowUpdated() { diff --git a/timePicker.go b/timePicker.go index 996a34c..42fd616 100644 --- a/timePicker.go +++ b/timePicker.go @@ -63,28 +63,47 @@ func (picker *timePickerData) remove(tag string) { case TimeChangedEvent: if len(picker.timeChangedListeners) > 0 { picker.timeChangedListeners = []func(TimePicker, time.Time){} + picker.propertyChangedEvent(tag) } + return case TimePickerMin: delete(picker.properties, TimePickerMin) - removeProperty(picker.htmlID(), Min, picker.session) + if picker.created { + removeProperty(picker.htmlID(), Min, picker.session) + } case TimePickerMax: delete(picker.properties, TimePickerMax) - removeProperty(picker.htmlID(), Max, picker.session) + if picker.created { + removeProperty(picker.htmlID(), Max, picker.session) + } case TimePickerStep: delete(picker.properties, TimePickerMax) - removeProperty(picker.htmlID(), Step, picker.session) + if picker.created { + removeProperty(picker.htmlID(), Step, picker.session) + } case TimePickerValue: - delete(picker.properties, TimePickerValue) - updateProperty(picker.htmlID(), Value, time.Now().Format(timeFormat), picker.session) + if _, ok := picker.properties[TimePickerValue]; ok { + delete(picker.properties, TimePickerValue) + time := GetTimePickerValue(picker, "") + if picker.created { + picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, picker.htmlID(), time.Format(timeFormat))) + } + for _, listener := range picker.timeChangedListeners { + listener(picker, time) + } + } else { + return + } default: picker.viewData.remove(tag) - picker.propertyChanged(tag) + return } + picker.propertyChangedEvent(tag) } func (picker *timePickerData) Set(tag string, value interface{}) bool { @@ -121,7 +140,10 @@ func (picker *timePickerData) set(tag string, value interface{}) bool { old, oldOK := getTimeProperty(picker, TimePickerMin, Min) if time, ok := setTimeValue(TimePickerMin); ok { if !oldOK || time != old { - updateProperty(picker.htmlID(), Min, time.Format(timeFormat), picker.session) + if picker.created { + updateProperty(picker.htmlID(), Min, time.Format(timeFormat), picker.session) + } + picker.propertyChangedEvent(tag) } return true } @@ -130,7 +152,10 @@ func (picker *timePickerData) set(tag string, value interface{}) bool { old, oldOK := getTimeProperty(picker, TimePickerMax, Max) if time, ok := setTimeValue(TimePickerMax); ok { if !oldOK || time != old { - updateProperty(picker.htmlID(), Max, time.Format(timeFormat), picker.session) + if picker.created { + updateProperty(picker.htmlID(), Max, time.Format(timeFormat), picker.session) + } + picker.propertyChangedEvent(tag) } return true } @@ -138,13 +163,15 @@ func (picker *timePickerData) set(tag string, value interface{}) bool { case TimePickerStep: oldStep := GetTimePickerStep(picker, "") if picker.setIntProperty(TimePickerStep, value) { - step := GetTimePickerStep(picker, "") - if oldStep != step { - if step > 0 { - updateProperty(picker.htmlID(), Step, strconv.Itoa(step), picker.session) - } else { - removeProperty(picker.htmlID(), Step, picker.session) + if step := GetTimePickerStep(picker, ""); oldStep != step { + if picker.created { + if step > 0 { + updateProperty(picker.htmlID(), Step, strconv.Itoa(step), picker.session) + } else { + removeProperty(picker.htmlID(), Step, picker.session) + } } + picker.propertyChangedEvent(tag) } return true } @@ -152,11 +179,14 @@ func (picker *timePickerData) set(tag string, value interface{}) bool { case TimePickerValue: oldTime := GetTimePickerValue(picker, "") if time, ok := setTimeValue(TimePickerMax); ok { - picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, picker.htmlID(), time.Format(timeFormat))) if time != oldTime { + if picker.created { + picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%s')`, picker.htmlID(), time.Format(timeFormat))) + } for _, listener := range picker.timeChangedListeners { listener(picker, time) } + picker.propertyChangedEvent(tag) } return true } @@ -167,8 +197,8 @@ func (picker *timePickerData) set(tag string, value interface{}) bool { picker.timeChangedListeners = []func(TimePicker, time.Time){value} case func(time.Time): - fn := func(view TimePicker, date time.Time) { - value(date) + fn := func(view TimePicker, time time.Time) { + value(time) } picker.timeChangedListeners = []func(TimePicker, time.Time){fn} @@ -183,8 +213,8 @@ func (picker *timePickerData) set(tag string, value interface{}) bool { return false } - listeners[i] = func(view TimePicker, date time.Time) { - val(date) + listeners[i] = func(view TimePicker, time time.Time) { + val(time) } } picker.timeChangedListeners = listeners @@ -202,8 +232,8 @@ func (picker *timePickerData) set(tag string, value interface{}) bool { listeners[i] = val case func(time.Time): - listeners[i] = func(view TimePicker, date time.Time) { - val(date) + listeners[i] = func(view TimePicker, time time.Time) { + val(time) } default: @@ -213,13 +243,11 @@ func (picker *timePickerData) set(tag string, value interface{}) bool { } picker.timeChangedListeners = listeners } + picker.propertyChangedEvent(tag) return true default: - if picker.viewData.set(tag, value) { - picker.propertyChanged(tag) - return true - } + return picker.viewData.set(tag, value) } return false } @@ -273,7 +301,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) diff --git a/view.go b/view.go index 649f740..aa2bfd7 100644 --- a/view.go +++ b/view.go @@ -57,6 +57,8 @@ type View interface { // a description of the error is written to the log SetAnimated(tag string, value interface{}, animation Animation) bool + SetChangeListener(tag string, listener func(View, string)) + handleCommand(self View, command string, data DataObject) bool //updateEventHandlers() htmlClass(disabled bool) string @@ -87,6 +89,7 @@ type viewData struct { _htmlID string parentID string systemClass string + changeListener map[string]func(View, string) singleTransition map[string]Animation addCSS map[string]string frame Frame @@ -129,6 +132,7 @@ func (view *viewData) Init(session Session) { view.viewStyle.init() view.tag = "View" view.session = session + view.changeListener = map[string]func(View, string){} view.addCSS = map[string]string{} //view.animation = map[string]AnimationEndListener{} view.singleTransition = map[string]Animation{} @@ -186,7 +190,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: @@ -221,8 +225,62 @@ func (view *viewData) remove(tag string) { default: view.viewStyle.remove(tag) - view.propertyChanged(tag) + viewPropertyChanged(view, tag) } + + view.propertyChangedEvent(tag) +} + +func (view *viewData) propertyChangedEvent(tag string) { + if listener, ok := view.changeListener[tag]; ok { + listener(view, tag) + } + + switch tag { + case BorderLeft, BorderRight, BorderTop, BorderBottom, + BorderStyle, BorderLeftStyle, BorderRightStyle, BorderTopStyle, BorderBottomStyle, + BorderColor, BorderLeftColor, BorderRightColor, BorderTopColor, BorderBottomColor, + BorderWidth, BorderLeftWidth, BorderRightWidth, BorderTopWidth, BorderBottomWidth: + tag = Border + + case CellBorderStyle, CellBorderColor, CellBorderWidth, + CellBorderLeft, CellBorderLeftStyle, CellBorderLeftColor, CellBorderLeftWidth, + CellBorderRight, CellBorderRightStyle, CellBorderRightColor, CellBorderRightWidth, + CellBorderTop, CellBorderTopStyle, CellBorderTopColor, CellBorderTopWidth, + CellBorderBottom, CellBorderBottomStyle, CellBorderBottomColor, CellBorderBottomWidth: + tag = CellBorder + + case OutlineColor, OutlineStyle, OutlineWidth: + tag = Outline + + case RadiusX, RadiusY, RadiusTopLeft, RadiusTopLeftX, RadiusTopLeftY, + RadiusTopRight, RadiusTopRightX, RadiusTopRightY, + RadiusBottomLeft, RadiusBottomLeftX, RadiusBottomLeftY, + RadiusBottomRight, RadiusBottomRightX, RadiusBottomRightY: + tag = Radius + + case MarginTop, MarginRight, MarginBottom, MarginLeft, + "top-margin", "right-margin", "bottom-margin", "left-margin": + tag = Margin + + case PaddingTop, PaddingRight, PaddingBottom, PaddingLeft, + "top-padding", "right-padding", "bottom-padding", "left-padding": + tag = Padding + + case CellPaddingTop, CellPaddingRight, CellPaddingBottom, CellPaddingLeft: + tag = CellPadding + + case ColumnSeparatorStyle, ColumnSeparatorWidth, ColumnSeparatorColor: + tag = ColumnSeparator + + default: + return + } + + if listener, ok := view.changeListener[tag]; ok { + listener(view, tag) + } + } func (view *viewData) Set(tag string, value interface{}) bool { @@ -235,64 +293,71 @@ func (view *viewData) set(tag string, value interface{}) bool { return true } + result := func(res bool) bool { + if res { + view.propertyChangedEvent(tag) + } + return res + } + switch tag { case ID: - if text, ok := value.(string); ok { - view.viewID = text - return true + text, ok := value.(string) + if !ok { + notCompatibleType(ID, value) + return false } - notCompatibleType(ID, value) - return false + view.viewID = text case Style, StyleDisabled: - if text, ok := value.(string); ok { - view.properties[tag] = text - //updateInnerHTML(view.parentID, view.session) - if view.created { - updateProperty(view.htmlID(), "class", view.htmlClass(IsDisabled(view)), view.session) - } - return true + text, ok := value.(string) + if !ok { + notCompatibleType(ID, value) + return false + } + view.properties[tag] = text + if view.created { + updateProperty(view.htmlID(), "class", view.htmlClass(IsDisabled(view, "")), view.session) } - notCompatibleType(ID, value) - return false case FocusEvent, LostFocusEvent: - return view.setFocusListener(tag, value) + return result(view.setFocusListener(tag, value)) case KeyDownEvent, KeyUpEvent: - return view.setKeyListener(tag, value) + return result(view.setKeyListener(tag, value)) case ClickEvent, DoubleClickEvent, MouseDown, MouseUp, MouseMove, MouseOut, MouseOver, ContextMenuEvent: - return view.setMouseListener(tag, value) + return result(view.setMouseListener(tag, value)) case PointerDown, PointerUp, PointerMove, PointerOut, PointerOver, PointerCancel: - return view.setPointerListener(tag, value) + return result(view.setPointerListener(tag, value)) case TouchStart, TouchEnd, TouchMove, TouchCancel: - return view.setTouchListener(tag, value) + return result(view.setTouchListener(tag, value)) case TransitionRunEvent, TransitionStartEvent, TransitionEndEvent, TransitionCancelEvent: - return view.setTransitionListener(tag, value) + return result(view.setTransitionListener(tag, value)) case AnimationStartEvent, AnimationEndEvent, AnimationIterationEvent, AnimationCancelEvent: - return view.setAnimationListener(tag, value) + return result(view.setAnimationListener(tag, value)) case ResizeEvent, ScrollEvent: - return view.setFrameListener(tag, value) - } + return result(view.setFrameListener(tag, value)) - if view.viewStyle.set(tag, value) { - if view.created { - view.propertyChanged(tag) + default: + if !view.viewStyle.set(tag, value) { + return false + } + if view.created { + viewPropertyChanged(view, tag) } - return true } - return false + view.propertyChangedEvent(tag) + return true } -func (view *viewData) propertyChanged(tag string) { - +func viewPropertyChanged(view *viewData, tag string) { if view.updateTransformProperty(tag) { return } @@ -446,7 +511,7 @@ func (view *viewData) propertyChanged(tag string) { case Strikethrough, Overline, Underline: updateCSSProperty(htmlID, "text-decoration", view.cssTextDecoration(session), session) for _, tag2 := range []string{TextLineColor, TextLineStyle, TextLineThickness} { - view.propertyChanged(tag2) + viewPropertyChanged(view, tag2) } return @@ -568,7 +633,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"`) @@ -583,7 +648,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="`) @@ -660,7 +725,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) } @@ -767,13 +832,10 @@ func (view *viewData) String() string { return writer.finish() } -// IsDisabled returns "true" if the view is disabled -func IsDisabled(view View) bool { - if disabled, _ := boolProperty(view, Disabled, view.Session()); disabled { - return true +func (view *viewData) SetChangeListener(tag string, listener func(View, string)) { + if listener == nil { + delete(view.changeListener, tag) + } else { + view.changeListener[tag] = listener } - if parent := view.Parent(); parent != nil { - return IsDisabled(parent) - } - return false } diff --git a/viewUtils.go b/viewUtils.go index f59b84d..e877b18 100644 --- a/viewUtils.go +++ b/viewUtils.go @@ -4,7 +4,13 @@ package rui // The type of return value depends on the property. // If the subview don't exists or the property is not set then nil is returned. func Get(rootView View, viewID, tag string) interface{} { - if view := ViewByID(rootView, viewID); view != nil { + var view View + if viewID != "" { + view = ViewByID(rootView, viewID) + } else { + view = rootView + } + if view != nil { return view.Get(tag) } return nil @@ -14,12 +20,29 @@ func Get(rootView View, viewID, tag string) interface{} { // true - success, // false - error (incompatible type or invalid format of a string value, see AppLog). func Set(rootView View, viewID, tag string, value interface{}) bool { - if view := ViewByID(rootView, viewID); view != nil { + var view View + if viewID != "" { + view = ViewByID(rootView, viewID) + } else { + view = rootView + } + if view != nil { return view.Set(tag, value) } return false } +// SetChangeListener sets a listener for changing a subview property value. +// If the second argument (subviewID) is "" then a listener for the first argument (view) is set +func SetChangeListener(view View, viewID, tag string, listener func(View, string)) { + if viewID != "" { + view = ViewByID(view, viewID) + } + if view != nil { + view.SetChangeListener(tag, listener) + } +} + // SetParams sets properties with name "tag" of the "rootView" subview. Result: // true - all properties were set successful, // false - error (incompatible type or invalid format of a string value, see AppLog). @@ -36,6 +59,22 @@ func SetParams(rootView View, viewID string, params Params) bool { return result } +// IsDisabled returns "true" if the subview is disabled +// If the second argument (subviewID) is "" then a state of the first argument (view) is returned +func IsDisabled(view View, subviewID string) bool { + if subviewID != "" { + view = ViewByID(view, subviewID) + } + + if disabled, _ := boolProperty(view, Disabled, view.Session()); disabled { + return true + } + if parent := view.Parent(); parent != nil { + return IsDisabled(parent, "") + } + return false +} + // GetSemantics returns the subview semantics. Valid semantics values are // DefaultSemantics (0), ArticleSemantics (1), SectionSemantics (2), AsideSemantics (3), // HeaderSemantics (4), MainSemantics (5), FooterSemantics (6), NavigationSemantics (7), @@ -926,7 +965,7 @@ func valueFromStyle(view View, tag string) (string, bool) { return "", false } - if IsDisabled(view) { + if IsDisabled(view, "") { if value, ok := getValue(StyleDisabled); ok { return value, true } diff --git a/viewsContainer.go b/viewsContainer.go index 6b8094a..efcddbe 100644 --- a/viewsContainer.go +++ b/viewsContainer.go @@ -39,16 +39,6 @@ func (container *viewsContainerData) setParentID(parentID string) { } } -// SetDisabled set the View disabled state -func (container *viewsContainerData) SetDisabled(disabled bool) { - container.viewData.Set(Disabled, disabled) - if container.views != nil { - for _, view := range container.views { - view.Set(Disabled, disabled) - } - } -} - // Views return a list of child views func (container *viewsContainerData) Views() []View { if container.views == nil { @@ -68,6 +58,7 @@ func (container *viewsContainerData) Append(view View) { container.views = append(container.views, view) } updateInnerHTML(container.htmlID(), container.session) + container.propertyChangedEvent(Content) } } @@ -81,10 +72,12 @@ func (container *viewsContainerData) Insert(view View, index uint) { view.setParentID(htmlID) container.views = append(container.views[:index], append([]View{view}, container.views[index:]...)...) updateInnerHTML(container.htmlID(), container.session) + container.propertyChangedEvent(Content) } else { view.setParentID(htmlID) container.views = append([]View{view}, container.views...) updateInnerHTML(container.htmlID(), container.session) + container.propertyChangedEvent(Content) } } } @@ -112,6 +105,7 @@ func (container *viewsContainerData) RemoveView(index uint) View { view.setParentID("") updateInnerHTML(container.htmlID(), container.session) + container.propertyChangedEvent(Content) return view } @@ -151,6 +145,18 @@ func (container *viewsContainerData) remove(tag string) { container.views = []View{} updateInnerHTML(container.htmlID(), container.Session()) } + container.propertyChangedEvent(Content) + + case Disabled: + if _, ok := container.properties[Disabled]; ok { + delete(container.properties, Disabled) + if container.views != nil { + for _, view := range container.views { + view.Remove(Disabled) + } + } + container.propertyChangedEvent(tag) + } default: container.viewData.remove(tag) @@ -167,7 +173,27 @@ func (container *viewsContainerData) set(tag string, value interface{}) bool { return true } - if tag != Content { + switch tag { + case Content: + // do nothing + + case Disabled: + oldDisabled := IsDisabled(container, "") + if container.viewData.Set(Disabled, value) { + disabled := IsDisabled(container, "") + if oldDisabled != disabled { + if container.views != nil { + for _, view := range container.views { + view.Set(Disabled, disabled) + } + } + } + container.propertyChangedEvent(tag) + return true + } + return false + + default: return container.viewData.set(tag, value) } @@ -239,6 +265,8 @@ func (container *viewsContainerData) set(tag string, value interface{}) bool { if container.created { updateInnerHTML(htmlID, container.session) } + + container.propertyChangedEvent(Content) return true }