2021-09-07 17:36:50 +03:00
|
|
|
package rui
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2022-05-03 12:12:57 +03:00
|
|
|
"math"
|
2021-09-07 17:36:50 +03:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
NumberChangedEvent = "number-changed"
|
|
|
|
NumberPickerType = "number-picker-type"
|
|
|
|
NumberPickerMin = "number-picker-min"
|
|
|
|
NumberPickerMax = "number-picker-max"
|
|
|
|
NumberPickerStep = "number-picker-step"
|
|
|
|
NumberPickerValue = "number-picker-value"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
// NumberEditor - type of NumberPicker. NumberPicker is presented by editor
|
|
|
|
NumberEditor = 0
|
|
|
|
// NumberSlider - type of NumberPicker. NumberPicker is presented by slider
|
|
|
|
NumberSlider = 1
|
|
|
|
)
|
|
|
|
|
|
|
|
// NumberPicker - NumberPicker view
|
|
|
|
type NumberPicker interface {
|
|
|
|
View
|
|
|
|
}
|
|
|
|
|
|
|
|
type numberPickerData struct {
|
|
|
|
viewData
|
|
|
|
numberChangedListeners []func(NumberPicker, float64)
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewNumberPicker create new NumberPicker object and return it
|
|
|
|
func NewNumberPicker(session Session, params Params) NumberPicker {
|
|
|
|
view := new(numberPickerData)
|
|
|
|
view.Init(session)
|
|
|
|
setInitParams(view, params)
|
|
|
|
return view
|
|
|
|
}
|
|
|
|
|
|
|
|
func newNumberPicker(session Session) View {
|
|
|
|
return NewNumberPicker(session, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (picker *numberPickerData) Init(session Session) {
|
|
|
|
picker.viewData.Init(session)
|
|
|
|
picker.tag = "NumberPicker"
|
|
|
|
picker.numberChangedListeners = []func(NumberPicker, float64){}
|
|
|
|
}
|
|
|
|
|
2022-05-22 12:54:02 +03:00
|
|
|
func (picker *numberPickerData) String() string {
|
|
|
|
return getViewString(picker)
|
|
|
|
}
|
|
|
|
|
2022-01-15 01:20:04 +03:00
|
|
|
func (picker *numberPickerData) Focusable() bool {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2021-09-07 17:36:50 +03:00
|
|
|
func (picker *numberPickerData) normalizeTag(tag string) string {
|
|
|
|
tag = strings.ToLower(tag)
|
|
|
|
switch tag {
|
|
|
|
case Type, Min, Max, Step, Value:
|
|
|
|
return "number-picker-" + tag
|
|
|
|
}
|
|
|
|
|
|
|
|
return tag
|
|
|
|
}
|
|
|
|
|
|
|
|
func (picker *numberPickerData) Remove(tag string) {
|
|
|
|
picker.remove(picker.normalizeTag(tag))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (picker *numberPickerData) remove(tag string) {
|
|
|
|
switch tag {
|
|
|
|
case NumberChangedEvent:
|
|
|
|
if len(picker.numberChangedListeners) > 0 {
|
|
|
|
picker.numberChangedListeners = []func(NumberPicker, float64){}
|
2021-11-20 11:15:28 +03:00
|
|
|
picker.propertyChangedEvent(tag)
|
2021-09-07 17:36:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
picker.viewData.remove(tag)
|
|
|
|
picker.propertyChanged(tag)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-26 18:36:00 +03:00
|
|
|
func (picker *numberPickerData) Set(tag string, value any) bool {
|
2021-09-07 17:36:50 +03:00
|
|
|
return picker.set(picker.normalizeTag(tag), value)
|
|
|
|
}
|
|
|
|
|
2022-07-26 18:36:00 +03:00
|
|
|
func (picker *numberPickerData) set(tag string, value any) bool {
|
2021-09-07 17:36:50 +03:00
|
|
|
if value == nil {
|
|
|
|
picker.remove(tag)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
switch tag {
|
|
|
|
case NumberChangedEvent:
|
2022-07-27 20:31:57 +03:00
|
|
|
listeners, ok := valueToEventListeners[NumberPicker, float64](value)
|
|
|
|
if !ok {
|
|
|
|
notCompatibleType(tag, value)
|
|
|
|
return false
|
|
|
|
} else if listeners == nil {
|
|
|
|
listeners = []func(NumberPicker, float64){}
|
2021-09-07 17:36:50 +03:00
|
|
|
}
|
2022-07-27 20:31:57 +03:00
|
|
|
picker.numberChangedListeners = listeners
|
2021-11-20 11:15:28 +03:00
|
|
|
picker.propertyChangedEvent(tag)
|
2021-09-07 17:36:50 +03:00
|
|
|
return true
|
|
|
|
|
|
|
|
case NumberPickerValue:
|
|
|
|
oldValue := GetNumberPickerValue(picker, "")
|
|
|
|
min, max := GetNumberPickerMinMax(picker, "")
|
|
|
|
if picker.setFloatProperty(NumberPickerValue, value, min, max) {
|
2021-11-20 11:15:28 +03:00
|
|
|
if newValue := GetNumberPickerValue(picker, ""); oldValue != newValue {
|
|
|
|
if picker.created {
|
|
|
|
picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%f')`, picker.htmlID(), newValue))
|
|
|
|
}
|
2021-09-07 17:36:50 +03:00
|
|
|
for _, listener := range picker.numberChangedListeners {
|
|
|
|
listener(picker, newValue)
|
|
|
|
}
|
2021-11-20 11:15:28 +03:00
|
|
|
picker.propertyChangedEvent(tag)
|
2021-09-07 17:36:50 +03:00
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
if picker.viewData.set(tag, value) {
|
|
|
|
picker.propertyChanged(tag)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (picker *numberPickerData) propertyChanged(tag string) {
|
2021-11-20 11:15:28 +03:00
|
|
|
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)
|
|
|
|
}
|
2021-09-07 17:36:50 +03:00
|
|
|
|
2021-11-20 11:15:28 +03:00
|
|
|
case NumberPickerMin:
|
|
|
|
min, _ := GetNumberPickerMinMax(picker, "")
|
|
|
|
updateProperty(picker.htmlID(), Min, strconv.FormatFloat(min, 'f', -1, 32), picker.session)
|
2021-09-07 17:36:50 +03:00
|
|
|
|
2021-11-20 11:15:28 +03:00
|
|
|
case NumberPickerMax:
|
|
|
|
_, max := GetNumberPickerMinMax(picker, "")
|
|
|
|
updateProperty(picker.htmlID(), Max, strconv.FormatFloat(max, 'f', -1, 32), picker.session)
|
2021-09-07 17:36:50 +03:00
|
|
|
|
2021-11-20 11:15:28 +03:00
|
|
|
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)
|
|
|
|
}
|
2021-09-07 17:36:50 +03:00
|
|
|
|
2021-11-20 11:15:28 +03:00
|
|
|
case NumberPickerValue:
|
|
|
|
value := GetNumberPickerValue(picker, "")
|
|
|
|
picker.session.runScript(fmt.Sprintf(`setInputValue('%s', '%f')`, picker.htmlID(), value))
|
|
|
|
for _, listener := range picker.numberChangedListeners {
|
|
|
|
listener(picker, value)
|
|
|
|
}
|
2021-09-07 17:36:50 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-26 18:36:00 +03:00
|
|
|
func (picker *numberPickerData) Get(tag string) any {
|
2021-09-07 17:36:50 +03:00
|
|
|
return picker.get(picker.normalizeTag(tag))
|
|
|
|
}
|
|
|
|
|
2022-07-26 18:36:00 +03:00
|
|
|
func (picker *numberPickerData) get(tag string) any {
|
2021-09-07 17:36:50 +03:00
|
|
|
switch tag {
|
|
|
|
case NumberChangedEvent:
|
|
|
|
return picker.numberChangedListeners
|
|
|
|
|
|
|
|
default:
|
|
|
|
return picker.viewData.get(tag)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (picker *numberPickerData) htmlTag() string {
|
|
|
|
return "input"
|
|
|
|
}
|
|
|
|
|
|
|
|
func (picker *numberPickerData) htmlProperties(self View, buffer *strings.Builder) {
|
|
|
|
picker.viewData.htmlProperties(self, buffer)
|
|
|
|
|
|
|
|
if GetNumberPickerType(picker, "") == NumberSlider {
|
|
|
|
buffer.WriteString(` type="range"`)
|
|
|
|
} else {
|
|
|
|
buffer.WriteString(` type="number"`)
|
|
|
|
}
|
|
|
|
|
|
|
|
min, max := GetNumberPickerMinMax(picker, "")
|
2022-05-03 12:12:57 +03:00
|
|
|
if min != math.Inf(-1) {
|
|
|
|
buffer.WriteString(` min="`)
|
|
|
|
buffer.WriteString(strconv.FormatFloat(min, 'f', -1, 64))
|
|
|
|
buffer.WriteByte('"')
|
|
|
|
}
|
2021-09-07 17:36:50 +03:00
|
|
|
|
2022-05-03 12:12:57 +03:00
|
|
|
if max != math.Inf(1) {
|
|
|
|
buffer.WriteString(` max="`)
|
|
|
|
buffer.WriteString(strconv.FormatFloat(max, 'f', -1, 64))
|
|
|
|
buffer.WriteByte('"')
|
|
|
|
}
|
2021-09-07 17:36:50 +03:00
|
|
|
|
|
|
|
step := GetNumberPickerStep(picker, "")
|
|
|
|
if step != 0 {
|
|
|
|
buffer.WriteString(` step="`)
|
|
|
|
buffer.WriteString(strconv.FormatFloat(step, 'f', -1, 64))
|
|
|
|
buffer.WriteByte('"')
|
|
|
|
} else {
|
|
|
|
buffer.WriteString(` step="any"`)
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer.WriteString(` value="`)
|
|
|
|
buffer.WriteString(strconv.FormatFloat(GetNumberPickerValue(picker, ""), 'f', -1, 64))
|
|
|
|
buffer.WriteByte('"')
|
|
|
|
|
|
|
|
buffer.WriteString(` oninput="editViewInputEvent(this)"`)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (picker *numberPickerData) htmlDisabledProperties(self View, buffer *strings.Builder) {
|
2021-11-20 11:15:28 +03:00
|
|
|
if IsDisabled(self, "") {
|
2021-09-07 17:36:50 +03:00
|
|
|
buffer.WriteString(` disabled`)
|
|
|
|
}
|
|
|
|
picker.viewData.htmlDisabledProperties(self, buffer)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (picker *numberPickerData) handleCommand(self View, command string, data DataObject) bool {
|
|
|
|
switch command {
|
|
|
|
case "textChanged":
|
|
|
|
if text, ok := data.PropertyValue("text"); ok {
|
|
|
|
if value, err := strconv.ParseFloat(text, 32); err == nil {
|
|
|
|
oldValue := GetNumberPickerValue(picker, "")
|
|
|
|
picker.properties[NumberPickerValue] = value
|
|
|
|
if value != oldValue {
|
|
|
|
for _, listener := range picker.numberChangedListeners {
|
|
|
|
listener(picker, value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
return picker.viewData.handleCommand(self, command, data)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetNumberPickerType returns the type of NumberPicker subview. Valid values:
|
2021-11-04 21:13:34 +03:00
|
|
|
// NumberEditor (0) - NumberPicker is presented by editor (default type);
|
|
|
|
// NumberSlider (1) - NumberPicker is presented by slider.
|
2021-09-07 17:36:50 +03:00
|
|
|
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
|
|
|
|
func GetNumberPickerType(view View, subviewID string) int {
|
2022-07-28 12:41:50 +03:00
|
|
|
return enumStyledProperty(view, subviewID, NumberPickerType, NumberEditor, false)
|
2021-09-07 17:36:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetNumberPickerMinMax returns the min and max value of NumberPicker subview.
|
|
|
|
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
|
|
|
|
func GetNumberPickerMinMax(view View, subviewID string) (float64, float64) {
|
2022-07-28 12:11:27 +03:00
|
|
|
var defMin, defMax float64
|
|
|
|
if GetNumberPickerType(view, subviewID) == NumberSlider {
|
|
|
|
defMin = 0
|
|
|
|
defMax = 1
|
|
|
|
} else {
|
|
|
|
defMin = math.Inf(-1)
|
|
|
|
defMax = math.Inf(1)
|
2021-09-07 17:36:50 +03:00
|
|
|
}
|
|
|
|
|
2022-07-28 12:11:27 +03:00
|
|
|
min := floatStyledProperty(view, subviewID, NumberPickerMin, defMin)
|
|
|
|
max := floatStyledProperty(view, subviewID, NumberPickerMax, defMax)
|
2021-09-07 17:36:50 +03:00
|
|
|
|
2022-07-28 12:11:27 +03:00
|
|
|
if min > max {
|
|
|
|
return max, min
|
2021-09-07 17:36:50 +03:00
|
|
|
}
|
2022-07-28 12:11:27 +03:00
|
|
|
return min, max
|
2021-09-07 17:36:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetNumberPickerStep returns the value changing step of NumberPicker subview.
|
|
|
|
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
|
|
|
|
func GetNumberPickerStep(view View, subviewID string) float64 {
|
2022-07-28 12:11:27 +03:00
|
|
|
_, max := GetNumberPickerMinMax(view, subviewID)
|
|
|
|
result := floatStyledProperty(view, subviewID, NumberPickerStep, 0)
|
2021-09-07 17:36:50 +03:00
|
|
|
if result > max {
|
|
|
|
return max
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetNumberPickerValue returns the value of NumberPicker subview.
|
|
|
|
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
|
|
|
|
func GetNumberPickerValue(view View, subviewID string) float64 {
|
2022-07-28 12:11:27 +03:00
|
|
|
min, _ := GetNumberPickerMinMax(view, subviewID)
|
|
|
|
result := floatStyledProperty(view, subviewID, NumberPickerValue, min)
|
2021-09-07 17:36:50 +03:00
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetNumberChangedListeners returns the NumberChangedListener list of an NumberPicker subview.
|
|
|
|
// If there are no listeners then the empty list is returned
|
|
|
|
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
|
|
|
|
func GetNumberChangedListeners(view View, subviewID string) []func(NumberPicker, float64) {
|
2022-07-27 20:31:57 +03:00
|
|
|
return getEventListeners[NumberPicker, float64](view, subviewID, NumberChangedEvent)
|
2021-09-07 17:36:50 +03:00
|
|
|
}
|