diff --git a/README-ru.md b/README-ru.md index c75861b..9dc899d 100644 --- a/README-ru.md +++ b/README-ru.md @@ -3107,13 +3107,21 @@ string свойство "edit-view-pattern" (константа EditViewPattern) Для отслеживания изменения текста используется событие "edit-text-changed" (константа EditTextChangedEvent). Основной слушатель события имеет следующий формат: - func(EditView, string) + func(EditView, string, string) -где второй аргумент это новое значение текста +где второй аргумент это новое значение текста, третий аргумент - предыдущее значение текста. + +Дополнительные слушатели события могут иметь следующий формат + + func(EditView, newText string) + func(newText, oldText string) + func(newText string) + func(EditView) + func() Получить текущий список слушателей изменения текста можно с помощью функции - func GetTextChangedListeners(view View, subviewID ...string) []func(EditView, string) + func GetTextChangedListeners(view View, subviewID ...string) []func(EditView, string, string) ## NumberPicker @@ -3425,13 +3433,21 @@ float32, float64, int, int8…int64, uint, uint8…uint64. Для отслеживания изменения свойства "current" используется событие "drop-down-event" (константа DropDownEvent). Основной слушатель события имеет следующий формат: - func(list DropDownList, newCurrent int) + func(list DropDownList, newCurrent, oldCurrent int) -где второй аргумент это индекс выбранного элемента +где второй аргумент это индекс выбранного элемента, третий аргумент - предыдущее значение индекса. + +Дополнительные слушатели события могут иметь следующий формат + + func(list DropDownList, newCurrent int) + func(newCurrent, oldCurrent int) + func(newCurrent int) + func(list DropDownList) + func() Получить текущий список слушателей изменения даты можно с помощью функции - func GetDropDownListeners(view View, subviewID ...string) []func(DropDownList, int) + func GetDropDownListeners(view View, subviewID ...string) []func(DropDownList, int, int) ## ProgressBar diff --git a/README.md b/README.md index 86d4206..253b730 100644 --- a/README.md +++ b/README.md @@ -3071,13 +3071,21 @@ The following functions can be used to get the values of the properties of an Ed The "edit-text-changed" event (EditTextChangedEvent constant) is used to track changes to the text. The main event listener has the following format: - func(EditView, string) + func(EditView, string, string) -where the second argument is the new text value +where the second argument is the new text value the third argument is the previous text value. + +Additional event listeners can have the following format + + func(EditView, newText string) + func(newText, oldText string) + func(newText string) + func(EditView) + func() You can get the current list of text change listeners using the function - func GetTextChangedListeners(view View, subviewID ...string) []func(EditView, string) + func GetTextChangedListeners(view View, subviewID ...string) []func(EditView, string, string) ## NumberPicker @@ -3393,11 +3401,19 @@ The main event listener has the following format: func(list DropDownList, newCurrent int) -where the second argument is the index of the selected item +where the second argument is the index of the selected item, the third argument is the previous index value. + +Additional event listeners can have the following format + + func(list DropDownList, newCurrent int) + func(newCurrent, oldCurrent int) + func(newCurrent int) + func(list DropDownList) + func() You can get the current list of date change listeners using the function - func GetDropDownListeners(view View, subviewID ...string) []func(DropDownList, int) + func GetDropDownListeners(view View, subviewID ...string) []func(DropDownList, int, int) ## ProgressBar diff --git a/dropDownList.go b/dropDownList.go index 8394a42..d6f84f0 100644 --- a/dropDownList.go +++ b/dropDownList.go @@ -21,7 +21,7 @@ type dropDownListData struct { viewData items []string disabledItems []any - dropDownListener []func(DropDownList, int) + dropDownListener []func(DropDownList, int, int) } // NewDropDownList create new DropDownList object and return it @@ -41,7 +41,7 @@ func (list *dropDownListData) init(session Session) { list.tag = "DropDownList" list.items = []string{} list.disabledItems = []any{} - list.dropDownListener = []func(DropDownList, int){} + list.dropDownListener = []func(DropDownList, int, int){} } func (list *dropDownListData) String() string { @@ -78,7 +78,7 @@ func (list *dropDownListData) remove(tag string) { case DropDownEvent: if len(list.dropDownListener) > 0 { - list.dropDownListener = []func(DropDownList, int){} + list.dropDownListener = []func(DropDownList, int, int){} list.propertyChangedEvent(tag) } @@ -89,7 +89,7 @@ func (list *dropDownListData) remove(tag string) { if list.created { list.session.callFunc("selectDropDownListItem", list.htmlID(), 0) } - list.onSelectedItemChanged(0) + list.onSelectedItemChanged(0, oldCurrent) } default: @@ -116,12 +116,12 @@ func (list *dropDownListData) set(tag string, value any) bool { return list.setDisabledItems(value) case DropDownEvent: - listeners, ok := valueToEventListeners[DropDownList, int](value) + listeners, ok := valueToEventWithOldListeners[DropDownList, int](value) if !ok { notCompatibleType(tag, value) return false } else if listeners == nil { - listeners = []func(DropDownList, int){} + listeners = []func(DropDownList, int, int){} } list.dropDownListener = listeners list.propertyChangedEvent(tag) @@ -137,7 +137,7 @@ func (list *dropDownListData) set(tag string, value any) bool { if list.created { list.session.callFunc("selectDropDownListItem", list.htmlID(), current) } - list.onSelectedItemChanged(current) + list.onSelectedItemChanged(current, oldCurrent) } return true } @@ -377,9 +377,9 @@ func (list *dropDownListData) htmlDisabledProperties(self View, buffer *strings. } } -func (list *dropDownListData) onSelectedItemChanged(number int) { +func (list *dropDownListData) onSelectedItemChanged(number, old int) { for _, listener := range list.dropDownListener { - listener(list, number) + listener(list, number, old) } list.propertyChangedEvent(Current) } @@ -390,8 +390,9 @@ func (list *dropDownListData) handleCommand(self View, command string, data Data if text, ok := data.PropertyValue("number"); ok { if number, err := strconv.Atoi(text); err == nil { if GetCurrent(list) != number && number >= 0 && number < len(list.items) { + old := GetCurrent(list) list.properties[Current] = number - list.onSelectedItemChanged(number) + list.onSelectedItemChanged(number, old) } } else { ErrorLog(err.Error()) @@ -406,8 +407,8 @@ func (list *dropDownListData) handleCommand(self View, command string, data Data // GetDropDownListeners returns the "drop-down-event" listener list. If there are no listeners then the empty list is returned. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned. -func GetDropDownListeners(view View, subviewID ...string) []func(DropDownList, int) { - return getEventListeners[DropDownList, int](view, subviewID, DropDownEvent) +func GetDropDownListeners(view View, subviewID ...string) []func(DropDownList, int, int) { + return getEventWithOldListeners[DropDownList, int](view, subviewID, DropDownEvent) } // GetDropDownItems return the DropDownList items list. diff --git a/editView.go b/editView.go index 73939f3..d8f9e9a 100644 --- a/editView.go +++ b/editView.go @@ -41,7 +41,7 @@ type EditView interface { type editViewData struct { viewData - textChangeListeners []func(EditView, string) + textChangeListeners []func(EditView, string, string) } // NewEditView create new EditView object and return it @@ -58,7 +58,7 @@ func newEditView(session Session) View { func (edit *editViewData) init(session Session) { edit.viewData.init(session) - edit.textChangeListeners = []func(EditView, string){} + edit.textChangeListeners = []func(EditView, string, string){} edit.tag = "EditView" } @@ -125,7 +125,7 @@ func (edit *editViewData) remove(tag string) { case EditTextChangedEvent: if len(edit.textChangeListeners) > 0 { - edit.textChangeListeners = []func(EditView, string){} + edit.textChangeListeners = []func(EditView, string, string){} edit.propertyChangedEvent(tag) } @@ -134,7 +134,7 @@ func (edit *editViewData) remove(tag string) { oldText := GetText(edit) delete(edit.properties, tag) if oldText != "" { - edit.textChanged("") + edit.textChanged("", oldText) if edit.created { edit.session.callFunc("setInputValue", edit.htmlID(), "") } @@ -205,7 +205,7 @@ func (edit *editViewData) set(tag string, value any) bool { if text, ok := value.(string); ok { edit.properties[Text] = text if text = GetText(edit); oldText != text { - edit.textChanged(text) + edit.textChanged(text, oldText) if edit.created { if GetEditViewType(edit) == MultiLineText { updateInnerHTML(edit.htmlID(), edit.Session()) @@ -328,12 +328,12 @@ func (edit *editViewData) set(tag string, value any) bool { return false case EditTextChangedEvent: - listeners, ok := valueToEventListeners[EditView, string](value) + listeners, ok := valueToEventWithOldListeners[EditView, string](value) if !ok { notCompatibleType(tag, value) return false } else if listeners == nil { - listeners = []func(EditView, string){} + listeners = []func(EditView, string, string){} } edit.textChangeListeners = listeners edit.propertyChangedEvent(tag) @@ -358,10 +358,11 @@ func (edit *editViewData) AppendText(text string) { if GetEditViewType(edit) == MultiLineText { if value := edit.getRaw(Text); value != nil { if textValue, ok := value.(string); ok { + oldText := textValue textValue += text edit.properties[Text] = textValue edit.session.callFunc("appendToInnerHTML", edit.htmlID(), text) - edit.textChanged(textValue) + edit.textChanged(textValue, oldText) return } } @@ -371,9 +372,9 @@ func (edit *editViewData) AppendText(text string) { } } -func (edit *editViewData) textChanged(newText string) { +func (edit *editViewData) textChanged(newText, oldText string) { for _, listener := range edit.textChangeListeners { - listener(edit, newText) + listener(edit, newText, oldText) } edit.propertyChangedEvent(Text) } @@ -485,7 +486,7 @@ func (edit *editViewData) handleCommand(self View, command string, data DataObje if text, ok := data.PropertyValue("text"); ok { edit.properties[Text] = text if text := GetText(edit); text != oldText { - edit.textChanged(text) + edit.textChanged(text, oldText) } } return true @@ -552,8 +553,8 @@ func IsSpellcheck(view View, subviewID ...string) bool { // GetTextChangedListeners returns the TextChangedListener list of an EditView or MultiLineEditView subview. // If there are no listeners then the empty list is returned // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned. -func GetTextChangedListeners(view View, subviewID ...string) []func(EditView, string) { - return getEventListeners[EditView, string](view, subviewID, EditTextChangedEvent) +func GetTextChangedListeners(view View, subviewID ...string) []func(EditView, string, string) { + return getEventWithOldListeners[EditView, string](view, subviewID, EditTextChangedEvent) } // GetEditViewType returns a value of the Type property of EditView. diff --git a/keyEvents.go b/keyEvents.go index b65c43c..f1c5188 100644 --- a/keyEvents.go +++ b/keyEvents.go @@ -193,6 +193,185 @@ func valueToEventListeners[V View, E any](value any) ([]func(V, E), bool) { return nil, false } +func valueToEventWithOldListeners[V View, E any](value any) ([]func(V, E, E), bool) { + if value == nil { + return nil, true + } + + switch value := value.(type) { + case func(V, E, E): + return []func(V, E, E){value}, true + + case func(V, E): + fn := func(v V, val, _ E) { + value(v, val) + } + return []func(V, E, E){fn}, true + + case func(E, E): + fn := func(_ V, val, old E) { + value(val, old) + } + return []func(V, E, E){fn}, true + + case func(E): + fn := func(_ V, val, _ E) { + value(val) + } + return []func(V, E, E){fn}, true + + case func(V): + fn := func(v V, _, _ E) { + value(v) + } + return []func(V, E, E){fn}, true + + case func(): + fn := func(V, E, E) { + value() + } + return []func(V, E, E){fn}, true + + case []func(V, E, E): + if len(value) == 0 { + return nil, true + } + for _, fn := range value { + if fn == nil { + return nil, false + } + } + return value, true + + case []func(V, E): + count := len(value) + if count == 0 { + return nil, true + } + listeners := make([]func(V, E, E), count) + for i, fn := range value { + if fn == nil { + return nil, false + } + listeners[i] = func(view V, val, _ E) { + fn(view, val) + } + } + return listeners, true + + case []func(E): + count := len(value) + if count == 0 { + return nil, true + } + listeners := make([]func(V, E, E), count) + for i, fn := range value { + if fn == nil { + return nil, false + } + listeners[i] = func(_ V, val, _ E) { + fn(val) + } + } + return listeners, true + + case []func(E, E): + count := len(value) + if count == 0 { + return nil, true + } + listeners := make([]func(V, E, E), count) + for i, fn := range value { + if fn == nil { + return nil, false + } + listeners[i] = func(_ V, val, old E) { + fn(val, old) + } + } + return listeners, true + + case []func(V): + count := len(value) + if count == 0 { + return nil, true + } + listeners := make([]func(V, E, E), count) + for i, fn := range value { + if fn == nil { + return nil, false + } + listeners[i] = func(view V, _, _ E) { + fn(view) + } + } + return listeners, true + + case []func(): + count := len(value) + if count == 0 { + return nil, true + } + listeners := make([]func(V, E, E), count) + for i, fn := range value { + if fn == nil { + return nil, false + } + listeners[i] = func(V, E, E) { + fn() + } + } + return listeners, true + + case []any: + count := len(value) + if count == 0 { + return nil, true + } + listeners := make([]func(V, E, E), count) + for i, v := range value { + if v == nil { + return nil, false + } + switch fn := v.(type) { + case func(V, E, E): + listeners[i] = fn + + case func(V, E): + listeners[i] = func(view V, val, _ E) { + fn(view, val) + } + + case func(E, E): + listeners[i] = func(_ V, val, old E) { + fn(val, old) + } + + case func(E): + listeners[i] = func(_ V, val, _ E) { + fn(val) + } + + case func(V): + listeners[i] = func(view V, _, _ E) { + fn(view) + } + + case func(): + listeners[i] = func(V, E, E) { + fn() + } + + default: + return nil, false + } + } + return listeners, true + } + + return nil, false +} + var keyEvents = map[string]struct{ jsEvent, jsFunc string }{ KeyDownEvent: {jsEvent: "onkeydown", jsFunc: "keyDownEvent"}, KeyUpEvent: {jsEvent: "onkeyup", jsFunc: "keyUpEvent"}, @@ -227,6 +406,19 @@ func (view *viewData) removeKeyListener(tag string) { } } +func getEventWithOldListeners[V View, E any](view View, subviewID []string, tag string) []func(V, E, E) { + if len(subviewID) > 0 && subviewID[0] != "" { + view = ViewByID(view, subviewID[0]) + } + if view != nil { + if value := view.Get(tag); value != nil { + if result, ok := value.([]func(V, E, E)); ok { + return result + } + } + } + return []func(V, E, E){} +} func getEventListeners[V View, E any](view View, subviewID []string, tag string) []func(V, E) { if len(subviewID) > 0 && subviewID[0] != "" { view = ViewByID(view, subviewID[0])