OriginX, OriginY, and OriginZ properties renamed to TransformOriginX, TransformOriginY, and TransformOriginZ. GetOrigin function renamed to GetTransformOrigin

This commit is contained in:
Alexei Anoshenko 2024-11-18 16:20:25 +02:00
parent e2775d52f2
commit 0f2e7e55ea
45 changed files with 910 additions and 930 deletions

View File

@ -1,4 +1,7 @@
# v0.18.0 # v0.18.0
* OriginX, OriginY, and OriginZ properties renamed to TransformOriginX, TransformOriginY, and TransformOriginZ
* GetOrigin function renamed to GetTransformOrigin
* Added LineJoin type. Type of constants MiterJoin, RoundJoin, and BevelJoin changed to LineJoin. Type of Canvas.SetLineJoin function argument changed to LineJoin. * Added LineJoin type. Type of constants MiterJoin, RoundJoin, and BevelJoin changed to LineJoin. Type of Canvas.SetLineJoin function argument changed to LineJoin.
* Added LineCap type. Type of constants ButtCap, RoundCap, and SquareCap changed to LineCap. Type of Canvas.SetLineCap function argument changed to LineCap. * Added LineCap type. Type of constants ButtCap, RoundCap, and SquareCap changed to LineCap. Type of Canvas.SetLineCap function argument changed to LineCap.

View File

@ -157,7 +157,7 @@ const (
/* /*
func setTransitionListener(properties Properties, tag PropertyName, value any) bool { func setTransitionListener(properties Properties, tag PropertyName, value any) bool {
if listeners, ok := valueToEventListeners[View, string](value); ok { if listeners, ok := valueToOneArgEventListeners[View, string](value); ok {
if len(listeners) == 0 { if len(listeners) == 0 {
properties.setRaw(tag, nil) properties.setRaw(tag, nil)
} else { } else {
@ -206,7 +206,7 @@ func (view *viewData) handleTransitionEvents(tag PropertyName, data DataObject)
} }
} }
for _, listener := range getEventListeners[View, PropertyName](view, nil, tag) { for _, listener := range getOneArgEventListeners[View, PropertyName](view, nil, tag) {
listener(view, property) listener(view, property)
} }
} }
@ -214,7 +214,7 @@ func (view *viewData) handleTransitionEvents(tag PropertyName, data DataObject)
/* /*
func setAnimationListener(properties Properties, tag PropertyName, value any) bool { func setAnimationListener(properties Properties, tag PropertyName, value any) bool {
if listeners, ok := valueToEventListeners[View, string](value); ok { if listeners, ok := valueToOneArgEventListeners[View, string](value); ok {
if len(listeners) == 0 { if len(listeners) == 0 {
properties.setRaw(tag, nil) properties.setRaw(tag, nil)
} else { } else {
@ -252,7 +252,7 @@ func animationEventsHtml(view View, buffer *strings.Builder) {
*/ */
func (view *viewData) handleAnimationEvents(tag PropertyName, data DataObject) { func (view *viewData) handleAnimationEvents(tag PropertyName, data DataObject) {
if listeners := getEventListeners[View, string](view, nil, tag); len(listeners) > 0 { if listeners := getOneArgEventListeners[View, string](view, nil, tag); len(listeners) > 0 {
id := "" id := ""
if name, ok := data.PropertyValue("name"); ok { if name, ok := data.PropertyValue("name"); ok {
for _, animation := range GetAnimation(view) { for _, animation := range GetAnimation(view) {
@ -271,54 +271,54 @@ func (view *viewData) handleAnimationEvents(tag PropertyName, data DataObject) {
// If there are no listeners then the empty list is returned. // 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTransitionRunListeners(view View, subviewID ...string) []func(View, string) { func GetTransitionRunListeners(view View, subviewID ...string) []func(View, string) {
return getEventListeners[View, string](view, subviewID, TransitionRunEvent) return getOneArgEventListeners[View, string](view, subviewID, TransitionRunEvent)
} }
// GetTransitionStartListeners returns the "transition-start-event" listener list. // GetTransitionStartListeners returns the "transition-start-event" listener list.
// If there are no listeners then the empty list is returned. // 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTransitionStartListeners(view View, subviewID ...string) []func(View, string) { func GetTransitionStartListeners(view View, subviewID ...string) []func(View, string) {
return getEventListeners[View, string](view, subviewID, TransitionStartEvent) return getOneArgEventListeners[View, string](view, subviewID, TransitionStartEvent)
} }
// GetTransitionEndListeners returns the "transition-end-event" listener list. // GetTransitionEndListeners returns the "transition-end-event" listener list.
// If there are no listeners then the empty list is returned. // 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTransitionEndListeners(view View, subviewID ...string) []func(View, string) { func GetTransitionEndListeners(view View, subviewID ...string) []func(View, string) {
return getEventListeners[View, string](view, subviewID, TransitionEndEvent) return getOneArgEventListeners[View, string](view, subviewID, TransitionEndEvent)
} }
// GetTransitionCancelListeners returns the "transition-cancel-event" listener list. // GetTransitionCancelListeners returns the "transition-cancel-event" listener list.
// If there are no listeners then the empty list is returned. // 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTransitionCancelListeners(view View, subviewID ...string) []func(View, string) { func GetTransitionCancelListeners(view View, subviewID ...string) []func(View, string) {
return getEventListeners[View, string](view, subviewID, TransitionCancelEvent) return getOneArgEventListeners[View, string](view, subviewID, TransitionCancelEvent)
} }
// GetAnimationStartListeners returns the "animation-start-event" listener list. // GetAnimationStartListeners returns the "animation-start-event" listener list.
// If there are no listeners then the empty list is returned. // 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetAnimationStartListeners(view View, subviewID ...string) []func(View, string) { func GetAnimationStartListeners(view View, subviewID ...string) []func(View, string) {
return getEventListeners[View, string](view, subviewID, AnimationStartEvent) return getOneArgEventListeners[View, string](view, subviewID, AnimationStartEvent)
} }
// GetAnimationEndListeners returns the "animation-end-event" listener list. // GetAnimationEndListeners returns the "animation-end-event" listener list.
// If there are no listeners then the empty list is returned. // 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetAnimationEndListeners(view View, subviewID ...string) []func(View, string) { func GetAnimationEndListeners(view View, subviewID ...string) []func(View, string) {
return getEventListeners[View, string](view, subviewID, AnimationEndEvent) return getOneArgEventListeners[View, string](view, subviewID, AnimationEndEvent)
} }
// GetAnimationCancelListeners returns the "animation-cancel-event" listener list. // GetAnimationCancelListeners returns the "animation-cancel-event" listener list.
// If there are no listeners then the empty list is returned. // 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetAnimationCancelListeners(view View, subviewID ...string) []func(View, string) { func GetAnimationCancelListeners(view View, subviewID ...string) []func(View, string) {
return getEventListeners[View, string](view, subviewID, AnimationCancelEvent) return getOneArgEventListeners[View, string](view, subviewID, AnimationCancelEvent)
} }
// GetAnimationIterationListeners returns the "animation-iteration-event" listener list. // GetAnimationIterationListeners returns the "animation-iteration-event" listener list.
// If there are no listeners then the empty list is returned. // 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetAnimationIterationListeners(view View, subviewID ...string) []func(View, string) { func GetAnimationIterationListeners(view View, subviewID ...string) []func(View, string) {
return getEventListeners[View, string](view, subviewID, AnimationIterationEvent) return getOneArgEventListeners[View, string](view, subviewID, AnimationIterationEvent)
} }

View File

@ -623,21 +623,10 @@ function listItemClickEvent(element, event) {
return return
} }
let selected = false;
if (element.classList) {
const focusStyle = getListFocusedItemStyle(element);
const blurStyle = getListSelectedItemStyle(element);
selected = (element.classList.contains(focusStyle) || element.classList.contains(blurStyle));
}
const list = element.parentNode.parentNode const list = element.parentNode.parentNode
if (list) { if (list) {
if (!selected) { const number = getListItemNumber(element.id)
selectListItem(list, element, true) sendMessage("itemClick{session=" + sessionID + ",id=" + list.id + ",number=" + number + "}");
}
const message = "itemClick{session=" + sessionID + ",id=" + list.id + "}"
sendMessage(message);
} }
} }
@ -664,7 +653,7 @@ function getListSelectedItemStyle(element) {
return getStyleAttribute(element, "data-bluritemstyle", "ruiListItemSelected"); return getStyleAttribute(element, "data-bluritemstyle", "ruiListItemSelected");
} }
function selectListItem(element, item, needSendMessage) { function selectListItem(element, item) {
const currentId = element.getAttribute("data-current"); const currentId = element.getAttribute("data-current");
let message; let message;
const focusStyle = getListFocusedItemStyle(element); const focusStyle = getListFocusedItemStyle(element);
@ -676,9 +665,7 @@ function selectListItem(element, item, needSendMessage) {
if (current.classList) { if (current.classList) {
current.classList.remove(focusStyle, blurStyle); current.classList.remove(focusStyle, blurStyle);
} }
if (sendMessage) { message = "itemUnselected{session=" + sessionID + ",id=" + element.id + "}";
message = "itemUnselected{session=" + sessionID + ",id=" + element.id + "}";
}
} }
} }
@ -694,41 +681,20 @@ function selectListItem(element, item, needSendMessage) {
} }
element.setAttribute("data-current", item.id); element.setAttribute("data-current", item.id);
if (sendMessage) { const number = getListItemNumber(item.id)
const number = getListItemNumber(item.id) if (number != undefined) {
if (number != undefined) { message = "itemSelected{session=" + sessionID + ",id=" + element.id + ",number=" + number + "}";
message = "itemSelected{session=" + sessionID + ",id=" + element.id + ",number=" + number + "}";
}
} }
if (item.scrollIntoViewIfNeeded) { if (item.scrollIntoViewIfNeeded) {
item.scrollIntoViewIfNeeded() item.scrollIntoViewIfNeeded()
} else { } else {
item.scrollIntoView({block: "nearest", inline: "nearest"}); item.scrollIntoView({block: "nearest", inline: "nearest"});
} }
/*
let left = item.offsetLeft - element.offsetLeft;
if (left < element.scrollLeft) {
element.scrollLeft = left;
}
let top = item.offsetTop - element.offsetTop;
if (top < element.scrollTop) {
element.scrollTop = top;
}
let right = left + item.offsetWidth;
if (right > element.scrollLeft + element.clientWidth) {
element.scrollLeft = right - element.clientWidth;
}
let bottom = top + item.offsetHeight
if (bottom > element.scrollTop + element.clientHeight) {
element.scrollTop = bottom - element.clientHeight;
}*/
} }
if (needSendMessage && message != undefined) { if (message != undefined) {
sendMessage(message); sendMessage(message);
} }
scanElementsSize(); scanElementsSize();
@ -857,7 +823,7 @@ function listViewKeyDownEvent(element, event) {
switch (key) { switch (key) {
case " ": case " ":
case "Enter": case "Enter":
const message = "itemClick{session=" + sessionID + ",id=" + element.id + "}"; const message = "itemClick{session=" + sessionID + ",id=" + element.id + ",number=" + getListItemNumber(currentId) + "}";
sendMessage(message); sendMessage(message);
break; break;
@ -897,7 +863,7 @@ function listViewKeyDownEvent(element, event) {
return; return;
} }
if (item && item !== current) { if (item && item !== current) {
selectListItem(element, item, true); selectListItem(element, item);
} }
} else { } else {
switch (key) { switch (key) {
@ -916,7 +882,7 @@ function listViewKeyDownEvent(element, event) {
if (item.getAttribute("data-disabled") == "1") { if (item.getAttribute("data-disabled") == "1") {
continue; continue;
} }
selectListItem(element, item, true); selectListItem(element, item);
return; return;
} }
break; break;

View File

@ -37,8 +37,8 @@ func (canvasView *canvasViewData) init(session Session) {
canvasView.viewData.init(session) canvasView.viewData.init(session)
canvasView.tag = "CanvasView" canvasView.tag = "CanvasView"
canvasView.normalize = normalizeCanvasViewTag canvasView.normalize = normalizeCanvasViewTag
canvasView.set = canvasViewSet canvasView.set = canvasView.setFunc
canvasView.remove = canvasViewRemove canvasView.remove = canvasView.removeFunc
} }
@ -51,36 +51,32 @@ func normalizeCanvasViewTag(tag PropertyName) PropertyName {
return tag return tag
} }
func canvasViewRemove(view View, tag PropertyName) []PropertyName { func (canvasView *canvasViewData) removeFunc(tag PropertyName) []PropertyName {
if tag == DrawFunction { if tag == DrawFunction {
if view.getRaw(DrawFunction) != nil { if canvasView.getRaw(DrawFunction) != nil {
view.setRaw(DrawFunction, nil) canvasView.setRaw(DrawFunction, nil)
if canvasView, ok := view.(CanvasView); ok { canvasView.Redraw()
canvasView.Redraw()
}
return []PropertyName{DrawFunction} return []PropertyName{DrawFunction}
} }
return []PropertyName{} return []PropertyName{}
} }
return viewRemove(view, tag) return canvasView.viewData.removeFunc(tag)
} }
func canvasViewSet(view View, tag PropertyName, value any) []PropertyName { func (canvasView *canvasViewData) setFunc(tag PropertyName, value any) []PropertyName {
if tag == DrawFunction { if tag == DrawFunction {
if fn, ok := value.(func(Canvas)); ok { if fn, ok := value.(func(Canvas)); ok {
view.setRaw(DrawFunction, fn) canvasView.setRaw(DrawFunction, fn)
} else { } else {
notCompatibleType(tag, value) notCompatibleType(tag, value)
return nil return nil
} }
if canvasView, ok := view.(CanvasView); ok { canvasView.Redraw()
canvasView.Redraw()
}
return []PropertyName{DrawFunction} return []PropertyName{DrawFunction}
} }
return viewSet(view, tag, value) return canvasView.viewData.setFunc(tag, value)
} }
func (canvasView *canvasViewData) htmlTag() string { func (canvasView *canvasViewData) htmlTag() string {

View File

@ -49,117 +49,98 @@ func (button *checkboxData) init(session Session) {
button.systemClass = "ruiGridLayout ruiCheckbox" button.systemClass = "ruiGridLayout ruiCheckbox"
button.set = button.setFunc button.set = button.setFunc
button.remove = button.removeFunc button.remove = button.removeFunc
button.changed = checkboxPropertyChanged button.changed = button.propertyChanged
button.setRaw(ClickEvent, checkboxClickListener) button.setRaw(ClickEvent, []func(View, MouseEvent){checkboxClickListener})
button.setRaw(KeyDownEvent, checkboxKeyListener) button.setRaw(KeyDownEvent, []func(View, KeyEvent){checkboxKeyListener})
} }
func (button *checkboxData) Focusable() bool { func (button *checkboxData) Focusable() bool {
return true return true
} }
func checkboxPropertyChanged(view View, tag PropertyName) { func (button *checkboxData) propertyChanged(tag PropertyName) {
switch tag { switch tag {
case Checked: case Checked:
session := view.Session() session := button.Session()
checked := IsCheckboxChecked(view) checked := IsCheckboxChecked(button)
if listeners := GetCheckboxChangedListeners(view); len(listeners) > 0 { if listeners := GetCheckboxChangedListeners(button); len(listeners) > 0 {
if checkbox, ok := view.(Checkbox); ok { for _, listener := range listeners {
for _, listener := range listeners { listener(button, checked)
listener(checkbox, checked)
}
} }
} }
buffer := allocStringBuilder() buffer := allocStringBuilder()
defer freeStringBuilder(buffer) defer freeStringBuilder(buffer)
checkboxHtml(view, buffer, checked) checkboxHtml(button, buffer, checked)
session.updateInnerHTML(view.htmlID()+"checkbox", buffer.String()) session.updateInnerHTML(button.htmlID()+"checkbox", buffer.String())
case CheckboxHorizontalAlign, CheckboxVerticalAlign: case CheckboxHorizontalAlign, CheckboxVerticalAlign:
htmlID := view.htmlID() htmlID := button.htmlID()
session := view.Session() session := button.Session()
updateCSSStyle(htmlID, session) updateCSSStyle(htmlID, session)
updateInnerHTML(htmlID, session) updateInnerHTML(htmlID, session)
case VerticalAlign: case VerticalAlign:
view.Session().updateCSSProperty(view.htmlID()+"content", "align-items", checkboxVerticalAlignCSS(view)) button.Session().updateCSSProperty(button.htmlID()+"content", "align-items", checkboxVerticalAlignCSS(button))
case HorizontalAlign: case HorizontalAlign:
view.Session().updateCSSProperty(view.htmlID()+"content", "justify-items", checkboxHorizontalAlignCSS(view)) button.Session().updateCSSProperty(button.htmlID()+"content", "justify-items", checkboxHorizontalAlignCSS(button))
case AccentColor: case AccentColor:
updateInnerHTML(view.htmlID(), view.Session()) updateInnerHTML(button.htmlID(), button.Session())
default: default:
viewsContainerPropertyChanged(view, tag) button.viewsContainerData.propertyChanged(tag)
} }
} }
func (button *checkboxData) setFunc(view View, tag PropertyName, value any) []PropertyName { func (button *checkboxData) setFunc(tag PropertyName, value any) []PropertyName {
switch tag { switch tag {
case ClickEvent: case ClickEvent:
if button.viewsContainerData.setFunc(view, ClickEvent, value) != nil { if listeners, ok := valueToOneArgEventListeners[View, MouseEvent](value); ok && listeners != nil {
if value := view.getRaw(ClickEvent); value != nil { listeners = append(listeners, checkboxClickListener)
if listeners, ok := value.([]func(View, MouseEvent)); ok { button.setRaw(tag, listeners)
listeners = append(listeners, checkboxClickListener) return []PropertyName{tag}
view.setRaw(ClickEvent, listeners)
return []PropertyName{ClickEvent}
}
}
return button.viewsContainerData.setFunc(view, ClickEvent, checkboxClickListener)
} }
return nil return nil
case KeyDownEvent: case KeyDownEvent:
if button.viewsContainerData.setFunc(view, KeyDownEvent, value) != nil { if listeners, ok := valueToOneArgEventListeners[View, KeyEvent](value); ok && listeners != nil {
if value := view.getRaw(KeyDownEvent); value != nil { listeners = append(listeners, checkboxKeyListener)
if listeners, ok := value.([]func(View, KeyEvent)); ok { button.setRaw(tag, listeners)
listeners = append(listeners, checkboxKeyListener) return []PropertyName{tag}
view.setRaw(KeyDownEvent, listeners)
return []PropertyName{KeyDownEvent}
}
}
return button.viewsContainerData.setFunc(view, KeyDownEvent, checkboxKeyListener)
} }
return nil return nil
case CheckboxChangedEvent: case CheckboxChangedEvent:
return setViewEventListener[Checkbox, bool](view, tag, value) return setOneArgEventListener[Checkbox, bool](button, tag, value)
case Checked: case Checked:
return setBoolProperty(view, Checked, value) return setBoolProperty(button, Checked, value)
case CellVerticalAlign, CellHorizontalAlign, CellWidth, CellHeight: case CellVerticalAlign, CellHorizontalAlign, CellWidth, CellHeight:
ErrorLogF(`"%s" property is not compatible with the BoundsProperty`, string(tag)) ErrorLogF(`"%s" property is not compatible with the BoundsProperty`, string(tag))
return nil return nil
} }
return button.viewsContainerData.setFunc(view, tag, value) return button.viewsContainerData.setFunc(tag, value)
} }
func (button *checkboxData) removeFunc(view View, tag PropertyName) []PropertyName { func (button *checkboxData) removeFunc(tag PropertyName) []PropertyName {
switch tag { switch tag {
case ClickEvent: case ClickEvent:
button.setRaw(ClickEvent, checkboxClickListener) button.setRaw(ClickEvent, []func(View, MouseEvent){checkboxClickListener})
return []PropertyName{ClickEvent} return []PropertyName{ClickEvent}
case KeyDownEvent: case KeyDownEvent:
button.setRaw(KeyDownEvent, checkboxKeyListener) button.setRaw(KeyDownEvent, []func(View, KeyEvent){checkboxKeyListener})
return []PropertyName{ClickEvent} return []PropertyName{ClickEvent}
} }
return button.viewsContainerData.removeFunc(view, tag) return button.viewsContainerData.removeFunc(tag)
}
func (button *checkboxData) checked() bool {
checked, _ := boolProperty(button, Checked, button.Session())
return checked
} }
/* /*
@ -340,5 +321,5 @@ func GetCheckboxHorizontalAlign(view View, subviewID ...string) int {
// If there are no listeners then the empty list is returned // 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetCheckboxChangedListeners(view View, subviewID ...string) []func(Checkbox, bool) { func GetCheckboxChangedListeners(view View, subviewID ...string) []func(Checkbox, bool) {
return getEventListeners[Checkbox, bool](view, subviewID, CheckboxChangedEvent) return getOneArgEventListeners[Checkbox, bool](view, subviewID, CheckboxChangedEvent)
} }

View File

@ -66,8 +66,8 @@ func (picker *colorPickerData) init(session Session) {
picker.hasHtmlDisabled = true picker.hasHtmlDisabled = true
picker.properties[Padding] = Px(0) picker.properties[Padding] = Px(0)
picker.normalize = normalizeColorPickerTag picker.normalize = normalizeColorPickerTag
picker.set = colorPickerSet picker.set = picker.setFunc
picker.changed = colorPickerPropertyChanged picker.changed = picker.propertyChanged
} }
func normalizeColorPickerTag(tag PropertyName) PropertyName { func normalizeColorPickerTag(tag PropertyName) PropertyName {
@ -80,44 +80,44 @@ func normalizeColorPickerTag(tag PropertyName) PropertyName {
return normalizeDataListTag(tag) return normalizeDataListTag(tag)
} }
func colorPickerSet(view View, tag PropertyName, value any) []PropertyName { func (picker *colorPickerData) setFunc(tag PropertyName, value any) []PropertyName {
switch tag { switch tag {
case ColorChangedEvent: case ColorChangedEvent:
return setEventWithOldListener[ColorPicker, Color](view, tag, value) return setTwoArgEventListener[ColorPicker, Color](picker, tag, value)
case ColorPickerValue: case ColorPickerValue:
oldColor := GetColorPickerValue(view) oldColor := GetColorPickerValue(picker)
result := setColorProperty(view, ColorPickerValue, value) result := setColorProperty(picker, ColorPickerValue, value)
if result != nil { if result != nil {
view.setRaw("old-color", oldColor) picker.setRaw("old-color", oldColor)
} }
return result return result
case DataList: case DataList:
return setDataList(view, value, "") return setDataList(picker, value, "")
} }
return viewSet(view, tag, value) return picker.viewData.setFunc(tag, value)
} }
func colorPickerPropertyChanged(view View, tag PropertyName) { func (picker *colorPickerData) propertyChanged(tag PropertyName) {
switch tag { switch tag {
case ColorPickerValue: case ColorPickerValue:
color := GetColorPickerValue(view) color := GetColorPickerValue(picker)
view.Session().callFunc("setInputValue", view.htmlID(), color.rgbString()) picker.Session().callFunc("setInputValue", picker.htmlID(), color.rgbString())
if listeners := GetColorChangedListeners(view); len(listeners) > 0 { if listeners := GetColorChangedListeners(picker); len(listeners) > 0 {
oldColor := Color(0) oldColor := Color(0)
if value := view.getRaw("old-color"); value != nil { if value := picker.getRaw("old-color"); value != nil {
oldColor = value.(Color) oldColor = value.(Color)
} }
for _, listener := range listeners { for _, listener := range listeners {
listener(view, color, oldColor) listener(picker, color, oldColor)
} }
} }
default: default:
viewPropertyChanged(view, tag) picker.viewData.propertyChanged(tag)
} }
} }
@ -196,5 +196,5 @@ func GetColorPickerValue(view View, subviewID ...string) Color {
// If there are no listeners then the empty list is returned // 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetColorChangedListeners(view View, subviewID ...string) []func(ColorPicker, Color, Color) { func GetColorChangedListeners(view View, subviewID ...string) []func(ColorPicker, Color, Color) {
return getEventWithOldListeners[ColorPicker, Color](view, subviewID, ColorChangedEvent) return getTwoArgEventListeners[ColorPicker, Color](view, subviewID, ColorChangedEvent)
} }

View File

@ -141,7 +141,7 @@ func (columnLayout *columnLayoutData) init(session Session) {
columnLayout.viewsContainerData.init(session) columnLayout.viewsContainerData.init(session)
columnLayout.tag = "ColumnLayout" columnLayout.tag = "ColumnLayout"
columnLayout.normalize = normalizeColumnLayoutTag columnLayout.normalize = normalizeColumnLayoutTag
columnLayout.changed = columnLayoutPropertyChanged columnLayout.changed = columnLayout.propertyChanged
//columnLayout.systemClass = "ruiColumnLayout" //columnLayout.systemClass = "ruiColumnLayout"
} }
@ -154,27 +154,27 @@ func normalizeColumnLayoutTag(tag PropertyName) PropertyName {
return tag return tag
} }
func columnLayoutPropertyChanged(view View, tag PropertyName) { func (columnLayout *columnLayoutData) propertyChanged(tag PropertyName) {
switch tag { switch tag {
case ColumnSeparator: case ColumnSeparator:
css := "" css := ""
session := view.Session() session := columnLayout.Session()
if value := view.getRaw(ColumnSeparator); value != nil { if value := columnLayout.getRaw(ColumnSeparator); value != nil {
separator := value.(ColumnSeparatorProperty) separator := value.(ColumnSeparatorProperty)
css = separator.cssValue(view.Session()) css = separator.cssValue(session)
} }
session.updateCSSProperty(view.htmlID(), "column-rule", css) session.updateCSSProperty(columnLayout.htmlID(), "column-rule", css)
case ColumnCount: case ColumnCount:
session := view.Session() session := columnLayout.Session()
if count, ok := intProperty(view, tag, session, 0); ok && count > 0 { if count := GetColumnCount(columnLayout); count > 0 {
session.updateCSSProperty(view.htmlID(), string(ColumnCount), strconv.Itoa(count)) session.updateCSSProperty(columnLayout.htmlID(), string(ColumnCount), strconv.Itoa(count))
} else { } else {
session.updateCSSProperty(view.htmlID(), string(ColumnCount), "auto") session.updateCSSProperty(columnLayout.htmlID(), string(ColumnCount), "auto")
} }
default: default:
viewsContainerPropertyChanged(view, tag) columnLayout.viewsContainerData.propertyChanged(tag)
} }
} }

View File

@ -82,6 +82,7 @@ func NewColumnSeparator(params Params) ColumnSeparatorProperty {
func (separator *columnSeparatorProperty) init() { func (separator *columnSeparatorProperty) init() {
separator.dataProperty.init() separator.dataProperty.init()
separator.normalize = normalizeVolumnSeparatorTag separator.normalize = normalizeVolumnSeparatorTag
separator.set = columnSeparatorSet
separator.supportedProperties = []PropertyName{Style, Width, ColorTag} separator.supportedProperties = []PropertyName{Style, Width, ColorTag}
} }
@ -171,3 +172,10 @@ func (separator *columnSeparatorProperty) cssValue(session Session) string {
return buffer.String() return buffer.String()
} }
func columnSeparatorSet(properties Properties, tag PropertyName, value any) []PropertyName {
if tag == Style {
return setEnumProperty(properties, Style, value, enumProperties[BorderStyle].values)
}
return propertiesSet(properties, tag, value)
}

View File

@ -120,7 +120,7 @@ func normalizeDataListTag(tag PropertyName) PropertyName {
} }
func setDataList(properties Properties, value any, dateTimeFormat string) []PropertyName { func setDataList(properties Properties, value any, dateTimeFormat string) []PropertyName {
if items, ok := anyToStringArray(value, timeFormat); ok { if items, ok := anyToStringArray(value, dateTimeFormat); ok {
properties.setRaw(DataList, items) properties.setRaw(DataList, items)
return []PropertyName{DataList} return []PropertyName{DataList}
} }

View File

@ -138,8 +138,8 @@ func (picker *datePickerData) init(session Session) {
picker.tag = "DatePicker" picker.tag = "DatePicker"
picker.hasHtmlDisabled = true picker.hasHtmlDisabled = true
picker.normalize = normalizeDatePickerTag picker.normalize = normalizeDatePickerTag
picker.set = datePickerSet picker.set = picker.setFunc
picker.changed = datePickerPropertyChanged picker.changed = picker.propertyChanged
} }
func (picker *datePickerData) Focusable() bool { func (picker *datePickerData) Focusable() bool {
@ -198,22 +198,22 @@ func stringToDate(value string) (time.Time, bool) {
return time.Now(), false return time.Now(), false
} }
func datePickerSet(view View, tag PropertyName, value any) []PropertyName { func (picker *datePickerData) setFunc(tag PropertyName, value any) []PropertyName {
setDateValue := func(tag PropertyName) []PropertyName { setDateValue := func(tag PropertyName) []PropertyName {
switch value := value.(type) { switch value := value.(type) {
case time.Time: case time.Time:
view.setRaw(tag, value) picker.setRaw(tag, value)
return []PropertyName{tag} return []PropertyName{tag}
case string: case string:
if isConstantName(value) { if isConstantName(value) {
view.setRaw(tag, value) picker.setRaw(tag, value)
return []PropertyName{tag} return []PropertyName{tag}
} }
if date, ok := stringToDate(value); ok { if date, ok := stringToDate(value); ok {
view.setRaw(tag, date) picker.setRaw(tag, date)
return []PropertyName{tag} return []PropertyName{tag}
} }
} }
@ -227,67 +227,67 @@ func datePickerSet(view View, tag PropertyName, value any) []PropertyName {
return setDateValue(tag) return setDateValue(tag)
case DatePickerStep: case DatePickerStep:
return setIntProperty(view, DatePickerStep, value) return setIntProperty(picker, DatePickerStep, value)
case DatePickerValue: case DatePickerValue:
view.setRaw("old-date", GetDatePickerValue(view)) picker.setRaw("old-date", GetDatePickerValue(picker))
return setDateValue(tag) return setDateValue(tag)
case DateChangedEvent: case DateChangedEvent:
return setEventWithOldListener[DatePicker, time.Time](view, tag, value) return setTwoArgEventListener[DatePicker, time.Time](picker, tag, value)
case DataList: case DataList:
return setDataList(view, value, dateFormat) return setDataList(picker, value, dateFormat)
} }
return viewSet(view, tag, value) return picker.viewData.setFunc(tag, value)
} }
func datePickerPropertyChanged(view View, tag PropertyName) { func (picker *datePickerData) propertyChanged(tag PropertyName) {
session := view.Session() session := picker.Session()
switch tag { switch tag {
case DatePickerMin: case DatePickerMin:
if date, ok := GetDatePickerMin(view); ok { if date, ok := GetDatePickerMin(picker); ok {
session.updateProperty(view.htmlID(), "min", date.Format(dateFormat)) session.updateProperty(picker.htmlID(), "min", date.Format(dateFormat))
} else { } else {
session.removeProperty(view.htmlID(), "min") session.removeProperty(picker.htmlID(), "min")
} }
case DatePickerMax: case DatePickerMax:
if date, ok := GetDatePickerMax(view); ok { if date, ok := GetDatePickerMax(picker); ok {
session.updateProperty(view.htmlID(), "max", date.Format(dateFormat)) session.updateProperty(picker.htmlID(), "max", date.Format(dateFormat))
} else { } else {
session.removeProperty(view.htmlID(), "max") session.removeProperty(picker.htmlID(), "max")
} }
case DatePickerStep: case DatePickerStep:
if step := GetDatePickerStep(view); step > 0 { if step := GetDatePickerStep(picker); step > 0 {
session.updateProperty(view.htmlID(), "step", strconv.Itoa(step)) session.updateProperty(picker.htmlID(), "step", strconv.Itoa(step))
} else { } else {
session.removeProperty(view.htmlID(), "step") session.removeProperty(picker.htmlID(), "step")
} }
case DatePickerValue: case DatePickerValue:
date := GetDatePickerValue(view) date := GetDatePickerValue(picker)
session.callFunc("setInputValue", view.htmlID(), date.Format(dateFormat)) session.callFunc("setInputValue", picker.htmlID(), date.Format(dateFormat))
if listeners := GetDateChangedListeners(view); len(listeners) > 0 { if listeners := GetDateChangedListeners(picker); len(listeners) > 0 {
oldDate := time.Now() oldDate := time.Now()
if value := view.getRaw("old-date"); value != nil { if value := picker.getRaw("old-date"); value != nil {
if date, ok := value.(time.Time); ok { if date, ok := value.(time.Time); ok {
oldDate = date oldDate = date
} }
} }
for _, listener := range listeners { for _, listener := range listeners {
listener(view, date, oldDate) listener(picker, date, oldDate)
} }
} }
default: default:
viewPropertyChanged(view, tag) picker.viewData.propertyChanged(tag)
} }
} }
@ -447,5 +447,5 @@ func GetDatePickerValue(view View, subviewID ...string) time.Time {
// If there are no listeners then the empty list is returned // 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetDateChangedListeners(view View, subviewID ...string) []func(DatePicker, time.Time, time.Time) { func GetDateChangedListeners(view View, subviewID ...string) []func(DatePicker, time.Time, time.Time) {
return getEventWithOldListeners[DatePicker, time.Time](view, subviewID, DateChangedEvent) return getTwoArgEventListeners[DatePicker, time.Time](view, subviewID, DateChangedEvent)
} }

View File

@ -54,7 +54,7 @@ func (detailsView *detailsViewData) init(session Session) {
detailsView.viewsContainerData.init(session) detailsView.viewsContainerData.init(session)
detailsView.tag = "DetailsView" detailsView.tag = "DetailsView"
detailsView.set = detailsView.setFunc detailsView.set = detailsView.setFunc
detailsView.changed = detailsViewPropertyChanged detailsView.changed = detailsView.propertyChanged
//detailsView.systemClass = "ruiDetailsView" //detailsView.systemClass = "ruiDetailsView"
} }
@ -69,7 +69,7 @@ func (detailsView *detailsViewData) Views() []View {
return views return views
} }
func (detailsView *detailsViewData) setFunc(self View, tag PropertyName, value any) []PropertyName { func (detailsView *detailsViewData) setFunc(tag PropertyName, value any) []PropertyName {
switch tag { switch tag {
case Summary: case Summary:
switch value := value.(type) { switch value := value.(type) {
@ -95,26 +95,26 @@ func (detailsView *detailsViewData) setFunc(self View, tag PropertyName, value a
return []PropertyName{tag} return []PropertyName{tag}
} }
return detailsView.viewsContainerData.setFunc(detailsView, tag, value) return detailsView.viewsContainerData.setFunc(tag, value)
} }
func detailsViewPropertyChanged(view View, tag PropertyName) { func (detailsView *detailsViewData) propertyChanged(tag PropertyName) {
switch tag { switch tag {
case Summary: case Summary:
updateInnerHTML(view.htmlID(), view.Session()) updateInnerHTML(detailsView.htmlID(), detailsView.Session())
case Expanded: case Expanded:
if IsDetailsExpanded(view) { if IsDetailsExpanded(detailsView) {
view.Session().updateProperty(view.htmlID(), "open", "") detailsView.Session().updateProperty(detailsView.htmlID(), "open", "")
} else { } else {
view.Session().removeProperty(view.htmlID(), "open") detailsView.Session().removeProperty(detailsView.htmlID(), "open")
} }
case NotTranslate: case NotTranslate:
updateInnerHTML(view.htmlID(), view.Session()) updateInnerHTML(detailsView.htmlID(), detailsView.Session())
default: default:
viewsContainerPropertyChanged(view, tag) detailsView.viewsContainerData.propertyChanged(tag)
} }
} }
@ -155,8 +155,8 @@ func (detailsView *detailsViewData) handleCommand(self View, command PropertyNam
if command == "details-open" { if command == "details-open" {
if n, ok := dataIntProperty(data, "open"); ok { if n, ok := dataIntProperty(data, "open"); ok {
detailsView.properties[Expanded] = (n != 0) detailsView.properties[Expanded] = (n != 0)
if listener, ok := detailsView.changeListener[Current]; ok { if listener, ok := detailsView.changeListener[Expanded]; ok {
listener(detailsView, Current) listener(detailsView, Expanded)
} }
} }
return true return true

View File

@ -46,8 +46,8 @@ func (list *dropDownListData) init(session Session) {
list.tag = "DropDownList" list.tag = "DropDownList"
list.hasHtmlDisabled = true list.hasHtmlDisabled = true
list.normalize = normalizeDropDownListTag list.normalize = normalizeDropDownListTag
list.set = dropDownListSet list.set = list.setFunc
list.changed = dropDownListPropertyChanged list.changed = list.propertyChanged
} }
func (list *dropDownListData) Focusable() bool { func (list *dropDownListData) Focusable() bool {
@ -62,59 +62,49 @@ func normalizeDropDownListTag(tag PropertyName) PropertyName {
return tag return tag
} }
func dropDownListSet(view View, tag PropertyName, value any) []PropertyName { func (list *dropDownListData) setFunc(tag PropertyName, value any) []PropertyName {
switch tag { switch tag {
case Items: case Items:
if items, ok := anyToStringArray(value, ""); ok { if items, ok := anyToStringArray(value, ""); ok {
return setArrayPropertyValue(view, tag, items) return setArrayPropertyValue(list, tag, items)
} }
notCompatibleType(Items, value) notCompatibleType(Items, value)
return nil return nil
case DisabledItems, ItemSeparators: case DisabledItems, ItemSeparators:
if items, ok := parseIndicesArray(value); ok { if items, ok := parseIndicesArray(value); ok {
return setArrayPropertyValue(view, tag, items) return setArrayPropertyValue(list, tag, items)
} }
notCompatibleType(tag, value) notCompatibleType(tag, value)
return nil return nil
case DropDownEvent: case DropDownEvent:
return setEventWithOldListener[DropDownList, int](view, tag, value) return setTwoArgEventListener[DropDownList, int](list, tag, value)
case Current: case Current:
if view, ok := view.(View); ok { list.setRaw("old-current", GetCurrent(list))
view.setRaw("old-current", GetCurrent(view)) return setIntProperty(list, Current, value)
}
return setIntProperty(view, Current, value)
} }
return viewSet(view, tag, value) return list.viewData.setFunc(tag, value)
} }
func dropDownListPropertyChanged(view View, tag PropertyName) { func (list *dropDownListData) propertyChanged(tag PropertyName) {
switch tag { switch tag {
case Items, DisabledItems, ItemSeparators: case Items, DisabledItems, ItemSeparators:
updateInnerHTML(view.htmlID(), view.Session()) updateInnerHTML(list.htmlID(), list.Session())
case Current: case Current:
current := GetCurrent(view) current := GetCurrent(list)
view.Session().callFunc("selectDropDownListItem", view.htmlID(), current) list.Session().callFunc("selectDropDownListItem", list.htmlID(), current)
if list, ok := view.(DropDownList); ok { oldCurrent, _ := intProperty(list, "old-current", list.Session(), -1)
oldCurrent := -1 for _, listener := range GetDropDownListeners(list) {
if value := view.getRaw("old-current"); value != nil { listener(list, current, oldCurrent)
if n, ok := value.(int); ok {
oldCurrent = n
}
}
for _, listener := range GetDropDownListeners(view) {
listener(list, current, oldCurrent)
}
} }
default: default:
viewPropertyChanged(view, tag) list.viewData.propertyChanged(tag)
} }
} }
@ -253,6 +243,9 @@ func (list *dropDownListData) handleCommand(self View, command PropertyName, dat
for _, listener := range GetDropDownListeners(list) { for _, listener := range GetDropDownListeners(list) {
listener(list, number, old) listener(list, number, old)
} }
if listener, ok := list.changeListener[Current]; ok {
listener(list, Current)
}
} }
} else { } else {
ErrorLog(err.Error()) ErrorLog(err.Error())
@ -268,7 +261,7 @@ func (list *dropDownListData) handleCommand(self View, command PropertyName, dat
// GetDropDownListeners returns the "drop-down-event" listener list. If there are no listeners then the empty list is returned. // 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. // 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, int) { func GetDropDownListeners(view View, subviewID ...string) []func(DropDownList, int, int) {
return getEventWithOldListeners[DropDownList, int](view, subviewID, DropDownEvent) return getTwoArgEventListeners[DropDownList, int](view, subviewID, DropDownEvent)
} }
// GetDropDownItems return the DropDownList items list. // GetDropDownItems return the DropDownList items list.

View File

@ -121,8 +121,8 @@ func (edit *editViewData) init(session Session) {
edit.hasHtmlDisabled = true edit.hasHtmlDisabled = true
edit.tag = "EditView" edit.tag = "EditView"
edit.normalize = normalizeEditViewTag edit.normalize = normalizeEditViewTag
edit.set = editViewSet edit.set = edit.setFunc
edit.changed = editViewPropertyChanged edit.changed = edit.propertyChanged
} }
func (edit *editViewData) Focusable() bool { func (edit *editViewData) Focusable() bool {
@ -148,18 +148,18 @@ func normalizeEditViewTag(tag PropertyName) PropertyName {
return normalizeDataListTag(tag) return normalizeDataListTag(tag)
} }
func editViewSet(view View, tag PropertyName, value any) []PropertyName { func (edit *editViewData) setFunc(tag PropertyName, value any) []PropertyName {
switch tag { switch tag {
case Text: case Text:
if text, ok := value.(string); ok { if text, ok := value.(string); ok {
old := "" old := ""
if val := view.getRaw(Text); val != nil { if val := edit.getRaw(Text); val != nil {
if txt, ok := val.(string); ok { if txt, ok := val.(string); ok {
old = txt old = txt
} }
} }
view.setRaw("old-text", old) edit.setRaw("old-text", old)
view.setRaw(tag, text) edit.setRaw(tag, text)
return []PropertyName{tag} return []PropertyName{tag}
} }
@ -168,85 +168,83 @@ func editViewSet(view View, tag PropertyName, value any) []PropertyName {
case Hint: case Hint:
if text, ok := value.(string); ok { if text, ok := value.(string); ok {
return setStringPropertyValue(view, tag, strings.Trim(text, " \t\n")) return setStringPropertyValue(edit, tag, strings.Trim(text, " \t\n"))
} }
notCompatibleType(tag, value) notCompatibleType(tag, value)
return nil return nil
case DataList: case DataList:
setDataList(view, value, "") setDataList(edit, value, "")
case EditTextChangedEvent: case EditTextChangedEvent:
return setEventWithOldListener[EditView, string](view, tag, value) return setTwoArgEventListener[EditView, string](edit, tag, value)
} }
return viewSet(view, tag, value) return edit.viewData.setFunc(tag, value)
} }
func editViewPropertyChanged(view View, tag PropertyName) { func (edit *editViewData) propertyChanged(tag PropertyName) {
session := view.Session() session := edit.Session()
switch tag { switch tag {
case Text: case Text:
text := GetText(view) text := GetText(edit)
session.callFunc("setInputValue", view.htmlID(), text) session.callFunc("setInputValue", edit.htmlID(), text)
if edit, ok := view.(EditView); ok { old := ""
old := "" if val := edit.getRaw("old-text"); val != nil {
if val := view.getRaw("old-text"); val != nil { if txt, ok := val.(string); ok {
if txt, ok := val.(string); ok { old = txt
old = txt
}
} }
edit.textChanged(text, old)
} }
edit.textChanged(text, old)
case Hint: case Hint:
if text := GetHint(view); text != "" { if text := GetHint(edit); text != "" {
session.updateProperty(view.htmlID(), "placeholder", text) session.updateProperty(edit.htmlID(), "placeholder", text)
} else { } else {
session.removeProperty(view.htmlID(), "placeholder") session.removeProperty(edit.htmlID(), "placeholder")
} }
case MaxLength: case MaxLength:
if maxLength := GetMaxLength(view); maxLength > 0 { if maxLength := GetMaxLength(edit); maxLength > 0 {
session.updateProperty(view.htmlID(), "maxlength", strconv.Itoa(maxLength)) session.updateProperty(edit.htmlID(), "maxlength", strconv.Itoa(maxLength))
} else { } else {
session.removeProperty(view.htmlID(), "maxlength") session.removeProperty(edit.htmlID(), "maxlength")
} }
case ReadOnly: case ReadOnly:
if IsReadOnly(view) { if IsReadOnly(edit) {
session.updateProperty(view.htmlID(), "readonly", "") session.updateProperty(edit.htmlID(), "readonly", "")
} else { } else {
session.removeProperty(view.htmlID(), "readonly") session.removeProperty(edit.htmlID(), "readonly")
} }
case Spellcheck: case Spellcheck:
session.updateProperty(view.htmlID(), "spellcheck", IsSpellcheck(view)) session.updateProperty(edit.htmlID(), "spellcheck", IsSpellcheck(edit))
case EditViewPattern: case EditViewPattern:
if text := GetEditViewPattern(view); text != "" { if text := GetEditViewPattern(edit); text != "" {
session.updateProperty(view.htmlID(), "pattern", text) session.updateProperty(edit.htmlID(), "pattern", text)
} else { } else {
session.removeProperty(view.htmlID(), "pattern") session.removeProperty(edit.htmlID(), "pattern")
} }
case EditViewType: case EditViewType:
updateInnerHTML(view.parentHTMLID(), session) updateInnerHTML(edit.parentHTMLID(), session)
case EditWrap: case EditWrap:
if wrap := IsEditViewWrap(view); wrap { if wrap := IsEditViewWrap(edit); wrap {
session.updateProperty(view.htmlID(), "wrap", "soft") session.updateProperty(edit.htmlID(), "wrap", "soft")
} else { } else {
session.updateProperty(view.htmlID(), "wrap", "off") session.updateProperty(edit.htmlID(), "wrap", "off")
} }
case DataList: case DataList:
updateInnerHTML(view.htmlID(), session) updateInnerHTML(edit.htmlID(), session)
default: default:
viewPropertyChanged(view, tag) edit.viewData.propertyChanged(tag)
} }
} }
@ -467,7 +465,7 @@ func IsSpellcheck(view View, subviewID ...string) bool {
// If there are no listeners then the empty list is returned // 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. // 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, string) { func GetTextChangedListeners(view View, subviewID ...string) []func(EditView, string, string) {
return getEventWithOldListeners[EditView, string](view, subviewID, EditTextChangedEvent) return getTwoArgEventListeners[EditView, string](view, subviewID, EditTextChangedEvent)
} }
// GetEditViewType returns a value of the Type property of EditView. // GetEditViewType returns a value of the Type property of EditView.

View File

@ -35,7 +35,7 @@ var eventJsFunc = map[PropertyName]struct{ jsEvent, jsFunc string }{
AnimationCancelEvent: {jsEvent: "onanimationcancel", jsFunc: "animationCancelEvent"}, AnimationCancelEvent: {jsEvent: "onanimationcancel", jsFunc: "animationCancelEvent"},
} }
func valueToNoParamListeners[V any](value any) ([]func(V), bool) { func valueToNoArgEventListeners[V any](value any) ([]func(V), bool) {
if value == nil { if value == nil {
return nil, true return nil, true
} }
@ -106,7 +106,7 @@ func valueToNoParamListeners[V any](value any) ([]func(V), bool) {
return nil, false return nil, false
} }
func valueToEventListeners[V View, E any](value any) ([]func(V, E), bool) { func valueToOneArgEventListeners[V View, E any](value any) ([]func(V, E), bool) {
if value == nil { if value == nil {
return nil, true return nil, true
} }
@ -231,7 +231,7 @@ func valueToEventListeners[V View, E any](value any) ([]func(V, E), bool) {
return nil, false return nil, false
} }
func valueToEventWithOldListeners[V View, E any](value any) ([]func(V, E, E), bool) { func valueToTwoArgEventListeners[V View, E any](value any) ([]func(V, E, E), bool) {
if value == nil { if value == nil {
return nil, true return nil, true
} }
@ -410,7 +410,7 @@ func valueToEventWithOldListeners[V View, E any](value any) ([]func(V, E, E), bo
return nil, false return nil, false
} }
func getNoParamEventListeners[V View](view View, subviewID []string, tag PropertyName) []func(V) { func getNoArgEventListeners[V View](view View, subviewID []string, tag PropertyName) []func(V) {
if len(subviewID) > 0 && subviewID[0] != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0]) view = ViewByID(view, subviewID[0])
} }
@ -424,7 +424,7 @@ func getNoParamEventListeners[V View](view View, subviewID []string, tag Propert
return []func(V){} return []func(V){}
} }
func getEventListeners[V View, E any](view View, subviewID []string, tag PropertyName) []func(V, E) { func getOneArgEventListeners[V View, E any](view View, subviewID []string, tag PropertyName) []func(V, E) {
if len(subviewID) > 0 && subviewID[0] != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0]) view = ViewByID(view, subviewID[0])
} }
@ -438,7 +438,7 @@ func getEventListeners[V View, E any](view View, subviewID []string, tag Propert
return []func(V, E){} return []func(V, E){}
} }
func getEventWithOldListeners[V View, E any](view View, subviewID []string, tag PropertyName) []func(V, E, E) { func getTwoArgEventListeners[V View, E any](view View, subviewID []string, tag PropertyName) []func(V, E, E) {
if len(subviewID) > 0 && subviewID[0] != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0]) view = ViewByID(view, subviewID[0])
} }
@ -452,8 +452,8 @@ func getEventWithOldListeners[V View, E any](view View, subviewID []string, tag
return []func(V, E, E){} return []func(V, E, E){}
} }
func setNoParamEventListener[V View](properties Properties, tag PropertyName, value any) []PropertyName { func setNoArgEventListener[V View](properties Properties, tag PropertyName, value any) []PropertyName {
if listeners, ok := valueToNoParamListeners[V](value); ok { if listeners, ok := valueToNoArgEventListeners[V](value); ok {
if len(listeners) > 0 { if len(listeners) > 0 {
properties.setRaw(tag, listeners) properties.setRaw(tag, listeners)
} else if properties.getRaw(tag) != nil { } else if properties.getRaw(tag) != nil {
@ -467,8 +467,8 @@ func setNoParamEventListener[V View](properties Properties, tag PropertyName, va
return nil return nil
} }
func setViewEventListener[V View, T any](properties Properties, tag PropertyName, value any) []PropertyName { func setOneArgEventListener[V View, T any](properties Properties, tag PropertyName, value any) []PropertyName {
if listeners, ok := valueToEventListeners[V, T](value); ok { if listeners, ok := valueToOneArgEventListeners[V, T](value); ok {
if len(listeners) > 0 { if len(listeners) > 0 {
properties.setRaw(tag, listeners) properties.setRaw(tag, listeners)
} else if properties.getRaw(tag) != nil { } else if properties.getRaw(tag) != nil {
@ -482,8 +482,8 @@ func setViewEventListener[V View, T any](properties Properties, tag PropertyName
return nil return nil
} }
func setEventWithOldListener[V View, T any](properties Properties, tag PropertyName, value any) []PropertyName { func setTwoArgEventListener[V View, T any](properties Properties, tag PropertyName, value any) []PropertyName {
listeners, ok := valueToEventWithOldListeners[V, T](value) listeners, ok := valueToTwoArgEventListeners[V, T](value)
if !ok { if !ok {
notCompatibleType(tag, value) notCompatibleType(tag, value)
return nil return nil
@ -498,7 +498,7 @@ func setEventWithOldListener[V View, T any](properties Properties, tag PropertyN
} }
func viewEventsHtml[T any](view View, events []PropertyName, buffer *strings.Builder) { func viewEventsHtml[T any](view View, events []PropertyName, buffer *strings.Builder) {
for _, tag := range []PropertyName{AnimationStartEvent, AnimationEndEvent, AnimationIterationEvent, AnimationCancelEvent} { for _, tag := range events {
if value := view.getRaw(tag); value != nil { if value := view.getRaw(tag); value != nil {
if js, ok := eventJsFunc[tag]; ok { if js, ok := eventJsFunc[tag]; ok {
if listeners, ok := value.([]func(View, T)); ok && len(listeners) > 0 { if listeners, ok := value.([]func(View, T)); ok && len(listeners) > 0 {

View File

@ -123,8 +123,8 @@ func (picker *filePickerData) init(session Session) {
picker.hasHtmlDisabled = true picker.hasHtmlDisabled = true
picker.files = []FileInfo{} picker.files = []FileInfo{}
picker.loader = map[int]func(FileInfo, []byte){} picker.loader = map[int]func(FileInfo, []byte){}
picker.set = filePickerSet picker.set = picker.setFunc
picker.changed = filePickerPropertyChanged picker.changed = picker.propertyChanged
} }
@ -150,27 +150,16 @@ func (picker *filePickerData) LoadFile(file FileInfo, result func(FileInfo, []by
} }
} }
func filePickerSet(view View, tag PropertyName, value any) []PropertyName { func (picker *filePickerData) setFunc(tag PropertyName, value any) []PropertyName {
setAccept := func(value string) []PropertyName {
if value != "" {
view.setRaw(tag, value)
} else if view.getRaw(tag) != nil {
view.setRaw(tag, nil)
} else {
return []PropertyName{}
}
return []PropertyName{Accept}
}
switch tag { switch tag {
case FileSelectedEvent: case FileSelectedEvent:
return setViewEventListener[FilePicker, []FileInfo](view, tag, value) return setOneArgEventListener[FilePicker, []FileInfo](picker, tag, value)
case Accept: case Accept:
switch value := value.(type) { switch value := value.(type) {
case string: case string:
return setAccept(strings.Trim(value, " \t\n")) return setStringPropertyValue(picker, Accept, strings.Trim(value, " \t\n"))
case []string: case []string:
buffer := allocStringBuilder() buffer := allocStringBuilder()
@ -184,27 +173,27 @@ func filePickerSet(view View, tag PropertyName, value any) []PropertyName {
buffer.WriteString(val) buffer.WriteString(val)
} }
} }
return setAccept(buffer.String()) return setStringPropertyValue(picker, Accept, buffer.String())
} }
notCompatibleType(tag, value) notCompatibleType(tag, value)
return nil return nil
} }
return viewSet(view, tag, value) return picker.viewData.setFunc(tag, value)
} }
func filePickerPropertyChanged(view View, tag PropertyName) { func (picker *filePickerData) propertyChanged(tag PropertyName) {
switch tag { switch tag {
case Accept: case Accept:
session := view.Session() session := picker.Session()
if css := acceptPropertyCSS(view); css != "" { if css := acceptPropertyCSS(picker); css != "" {
session.updateProperty(view.htmlID(), "accept", css) session.updateProperty(picker.htmlID(), "accept", css)
} else { } else {
session.removeProperty(view.htmlID(), "accept") session.removeProperty(picker.htmlID(), "accept")
} }
default: default:
viewPropertyChanged(view, tag) picker.viewData.propertyChanged(tag)
} }
} }
@ -381,5 +370,5 @@ func GetFilePickerAccept(view View, subviewID ...string) []string {
// If there are no listeners then the empty list is returned. // 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetFileSelectedListeners(view View, subviewID ...string) []func(FilePicker, []FileInfo) { func GetFileSelectedListeners(view View, subviewID ...string) []func(FilePicker, []FileInfo) {
return getEventListeners[FilePicker, []FileInfo](view, subviewID, FileSelectedEvent) return getOneArgEventListeners[FilePicker, []FileInfo](view, subviewID, FileSelectedEvent)
} }

View File

@ -51,11 +51,11 @@ func focusEventsHtml(view View, buffer *strings.Builder) {
// GetFocusListeners returns a FocusListener list. If there are no listeners then the empty list is returned // GetFocusListeners returns a FocusListener 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetFocusListeners(view View, subviewID ...string) []func(View) { func GetFocusListeners(view View, subviewID ...string) []func(View) {
return getNoParamEventListeners[View](view, subviewID, FocusEvent) return getNoArgEventListeners[View](view, subviewID, FocusEvent)
} }
// GetLostFocusListeners returns a LostFocusListener list. If there are no listeners then the empty list is returned // GetLostFocusListeners returns a LostFocusListener 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetLostFocusListeners(view View, subviewID ...string) []func(View) { func GetLostFocusListeners(view View, subviewID ...string) []func(View) {
return getNoParamEventListeners[View](view, subviewID, LostFocusEvent) return getNoArgEventListeners[View](view, subviewID, LostFocusEvent)
} }

View File

@ -137,10 +137,10 @@ func (gridLayout *gridLayoutData) init(session Session) {
gridLayout.systemClass = "ruiGridLayout" gridLayout.systemClass = "ruiGridLayout"
gridLayout.adapter = nil gridLayout.adapter = nil
gridLayout.normalize = normalizeGridLayoutTag gridLayout.normalize = normalizeGridLayoutTag
gridLayout.getFunc = gridLayout.get gridLayout.get = gridLayout.getFunc
gridLayout.set = gridLayout.setFunc gridLayout.set = gridLayout.setFunc
gridLayout.remove = gridLayout.removeFunc gridLayout.remove = gridLayout.removeFunc
gridLayout.changed = gridLayout.propertyChanged
} }
func setGridCellSize(properties Properties, tag PropertyName, value any) []PropertyName { func setGridCellSize(properties Properties, tag PropertyName, value any) []PropertyName {
@ -303,7 +303,7 @@ func normalizeGridLayoutTag(tag PropertyName) PropertyName {
return tag return tag
} }
func (gridLayout *gridLayoutData) get(self View, tag PropertyName) any { func (gridLayout *gridLayoutData) getFunc(tag PropertyName) any {
switch tag { switch tag {
case Gap: case Gap:
rowGap := GetGridRowGap(gridLayout) rowGap := GetGridRowGap(gridLayout)
@ -319,10 +319,10 @@ func (gridLayout *gridLayoutData) get(self View, tag PropertyName) any {
} }
} }
return gridLayout.viewsContainerData.get(gridLayout, tag) return gridLayout.viewsContainerData.getFunc(tag)
} }
func (gridLayout *gridLayoutData) removeFunc(self View, tag PropertyName) []PropertyName { func (gridLayout *gridLayoutData) removeFunc(tag PropertyName) []PropertyName {
switch tag { switch tag {
case Gap: case Gap:
result := []PropertyName{} result := []PropertyName{}
@ -343,13 +343,13 @@ func (gridLayout *gridLayoutData) removeFunc(self View, tag PropertyName) []Prop
return []PropertyName{} return []PropertyName{}
} }
return gridLayout.viewsContainerData.removeFunc(gridLayout, tag) return gridLayout.viewsContainerData.removeFunc(tag)
} }
func (gridLayout *gridLayoutData) setFunc(self View, tag PropertyName, value any) []PropertyName { func (gridLayout *gridLayoutData) setFunc(tag PropertyName, value any) []PropertyName {
switch tag { switch tag {
case Gap: case Gap:
result := gridLayout.setFunc(gridLayout, GridRowGap, value) result := gridLayout.setFunc(GridRowGap, value)
if result != nil { if result != nil {
if gap := gridLayout.getRaw(GridRowGap); gap != nil { if gap := gridLayout.getRaw(GridRowGap); gap != nil {
gridLayout.setRaw(GridColumnGap, gap) gridLayout.setRaw(GridColumnGap, gap)
@ -370,21 +370,23 @@ func (gridLayout *gridLayoutData) setFunc(self View, tag PropertyName, value any
return []PropertyName{Content} return []PropertyName{Content}
} }
return gridLayout.viewsContainerData.setFunc(gridLayout, tag, value) return gridLayout.viewsContainerData.setFunc(tag, value)
} }
func gridLayoutPropertyChanged(view View, tag PropertyName) { func (gridLayout *gridLayoutData) propertyChanged(tag PropertyName) {
switch tag { switch tag {
case CellWidth: case CellWidth:
view.Session().updateCSSProperty(view.htmlID(), `grid-template-columns`, session := gridLayout.Session()
gridCellSizesCSS(view, CellWidth, view.Session())) session.updateCSSProperty(gridLayout.htmlID(), `grid-template-columns`,
gridCellSizesCSS(gridLayout, CellWidth, session))
case CellHeight: case CellHeight:
view.Session().updateCSSProperty(view.htmlID(), `grid-template-rows`, session := gridLayout.Session()
gridCellSizesCSS(view, CellHeight, view.Session())) session.updateCSSProperty(gridLayout.htmlID(), `grid-template-rows`,
gridCellSizesCSS(gridLayout, CellHeight, session))
default: default:
viewsContainerPropertyChanged(view, tag) gridLayout.viewsContainerData.propertyChanged(tag)
} }
} }

View File

@ -97,8 +97,8 @@ func (imageView *imageViewData) init(session Session) {
imageView.tag = "ImageView" imageView.tag = "ImageView"
imageView.systemClass = "ruiImageView" imageView.systemClass = "ruiImageView"
imageView.normalize = normalizeImageViewTag imageView.normalize = normalizeImageViewTag
imageView.set = imageViewSet imageView.set = imageView.setFunc
imageView.changed = imageViewPropertyChanged imageView.changed = imageView.propertyChanged
} }
func normalizeImageViewTag(tag PropertyName) PropertyName { func normalizeImageViewTag(tag PropertyName) PropertyName {
@ -122,30 +122,30 @@ func normalizeImageViewTag(tag PropertyName) PropertyName {
return tag return tag
} }
func imageViewSet(view View, tag PropertyName, value any) []PropertyName { func (imageView *imageViewData) setFunc(tag PropertyName, value any) []PropertyName {
switch tag { switch tag {
case Source, SrcSet, AltText: case Source, SrcSet, AltText:
if text, ok := value.(string); ok { if text, ok := value.(string); ok {
return setStringPropertyValue(view, tag, text) return setStringPropertyValue(imageView, tag, text)
} }
notCompatibleType(tag, value) notCompatibleType(tag, value)
return nil return nil
case LoadedEvent, ErrorEvent: case LoadedEvent, ErrorEvent:
return setNoParamEventListener[ImageView](view, tag, value) return setNoArgEventListener[ImageView](imageView, tag, value)
} }
return viewSet(view, tag, value) return imageView.viewData.setFunc(tag, value)
} }
func imageViewPropertyChanged(view View, tag PropertyName) { func (imageView *imageViewData) propertyChanged(tag PropertyName) {
session := view.Session() session := imageView.Session()
htmlID := view.htmlID() htmlID := imageView.htmlID()
switch tag { switch tag {
case Source: case Source:
src, srcset := imageViewSrc(view, GetImageViewSource(view)) src, srcset := imageViewSrc(imageView, GetImageViewSource(imageView))
session.updateProperty(htmlID, "src", src) session.updateProperty(htmlID, "src", src)
if srcset != "" { if srcset != "" {
session.updateProperty(htmlID, "srcset", srcset) session.updateProperty(htmlID, "srcset", srcset)
@ -154,7 +154,7 @@ func imageViewPropertyChanged(view View, tag PropertyName) {
} }
case SrcSet: case SrcSet:
_, srcset := imageViewSrc(view, GetImageViewSource(view)) _, srcset := imageViewSrc(imageView, GetImageViewSource(imageView))
if srcset != "" { if srcset != "" {
session.updateProperty(htmlID, "srcset", srcset) session.updateProperty(htmlID, "srcset", srcset)
} else { } else {
@ -168,7 +168,7 @@ func imageViewPropertyChanged(view View, tag PropertyName) {
updateCSSStyle(htmlID, session) updateCSSStyle(htmlID, session)
default: default:
viewPropertyChanged(view, tag) imageView.viewData.propertyChanged(tag)
} }
} }
@ -256,7 +256,7 @@ func (imageView *imageViewData) htmlProperties(self View, buffer *strings.Builde
buffer.WriteString(` onload="imageLoaded(this, event)"`) buffer.WriteString(` onload="imageLoaded(this, event)"`)
if len(getNoParamEventListeners[ImageView](imageView, nil, ErrorEvent)) > 0 { if len(getNoArgEventListeners[ImageView](imageView, nil, ErrorEvent)) > 0 {
buffer.WriteString(` onerror="imageError(this, event)"`) buffer.WriteString(` onerror="imageError(this, event)"`)
} }
} }
@ -299,7 +299,7 @@ func (imageView *imageViewData) cssStyle(self View, builder cssBuilder) {
func (imageView *imageViewData) handleCommand(self View, command PropertyName, data DataObject) bool { func (imageView *imageViewData) handleCommand(self View, command PropertyName, data DataObject) bool {
switch command { switch command {
case "imageViewError": case "imageViewError":
for _, listener := range getNoParamEventListeners[ImageView](imageView, nil, ErrorEvent) { for _, listener := range getNoArgEventListeners[ImageView](imageView, nil, ErrorEvent) {
listener(imageView) listener(imageView)
} }
@ -308,7 +308,7 @@ func (imageView *imageViewData) handleCommand(self View, command PropertyName, d
imageView.naturalHeight = dataFloatProperty(data, "natural-height") imageView.naturalHeight = dataFloatProperty(data, "natural-height")
imageView.currentSrc, _ = data.PropertyValue("current-src") imageView.currentSrc, _ = data.PropertyValue("current-src")
for _, listener := range getNoParamEventListeners[ImageView](imageView, nil, LoadedEvent) { for _, listener := range getNoArgEventListeners[ImageView](imageView, nil, LoadedEvent) {
listener(imageView) listener(imageView)
} }

View File

@ -431,7 +431,7 @@ func (event *KeyEvent) init(data DataObject) {
/* /*
func setKeyListener(properties Properties, tag PropertyName, value any) bool { func setKeyListener(properties Properties, tag PropertyName, value any) bool {
if listeners, ok := valueToEventListeners[View, KeyEvent](value); ok { if listeners, ok := valueToOneArgEventListeners[View, KeyEvent](value); ok {
if len(listeners) == 0 { if len(listeners) == 0 {
properties.setRaw(tag, nil) properties.setRaw(tag, nil)
} else { } else {
@ -460,15 +460,15 @@ func (view *viewData) removeKeyListener(tag PropertyName) {
*/ */
func keyEventsHtml(view View, buffer *strings.Builder) { func keyEventsHtml(view View, buffer *strings.Builder) {
if len(getEventListeners[View, KeyEvent](view, nil, KeyDownEvent)) > 0 { if len(getOneArgEventListeners[View, KeyEvent](view, nil, KeyDownEvent)) > 0 {
buffer.WriteString(`onkeydown="keyDownEvent(this, event)" `) buffer.WriteString(`onkeydown="keyDownEvent(this, event)" `)
} else if view.Focusable() { } else if view.Focusable() {
if len(getEventListeners[View, MouseEvent](view, nil, ClickEvent)) > 0 { if len(getOneArgEventListeners[View, MouseEvent](view, nil, ClickEvent)) > 0 {
buffer.WriteString(`onkeydown="keyDownEvent(this, event)" `) buffer.WriteString(`onkeydown="keyDownEvent(this, event)" `)
} }
} }
if listeners := getEventListeners[View, KeyEvent](view, nil, KeyUpEvent); len(listeners) > 0 { if listeners := getOneArgEventListeners[View, KeyEvent](view, nil, KeyUpEvent); len(listeners) > 0 {
buffer.WriteString(`onkeyup="keyUpEvent(this, event)" `) buffer.WriteString(`onkeyup="keyUpEvent(this, event)" `)
} }
} }
@ -476,7 +476,7 @@ func keyEventsHtml(view View, buffer *strings.Builder) {
func handleKeyEvents(view View, tag PropertyName, data DataObject) { func handleKeyEvents(view View, tag PropertyName, data DataObject) {
var event KeyEvent var event KeyEvent
event.init(data) event.init(data)
listeners := getEventListeners[View, KeyEvent](view, nil, tag) listeners := getOneArgEventListeners[View, KeyEvent](view, nil, tag)
if len(listeners) > 0 { if len(listeners) > 0 {
for _, listener := range listeners { for _, listener := range listeners {
@ -486,7 +486,7 @@ func handleKeyEvents(view View, tag PropertyName, data DataObject) {
} }
if tag == KeyDownEvent && view.Focusable() && (event.Key == " " || event.Key == "Enter") && !IsDisabled(view) { if tag == KeyDownEvent && view.Focusable() && (event.Key == " " || event.Key == "Enter") && !IsDisabled(view) {
if listeners := getEventListeners[View, MouseEvent](view, nil, ClickEvent); len(listeners) > 0 { if listeners := getOneArgEventListeners[View, MouseEvent](view, nil, ClickEvent); len(listeners) > 0 {
clickEvent := MouseEvent{ clickEvent := MouseEvent{
TimeStamp: event.TimeStamp, TimeStamp: event.TimeStamp,
Button: PrimaryMouseButton, Button: PrimaryMouseButton,
@ -512,11 +512,11 @@ func handleKeyEvents(view View, tag PropertyName, data DataObject) {
// GetKeyDownListeners returns the "key-down-event" listener list. If there are no listeners then the empty list is returned. // GetKeyDownListeners returns the "key-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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetKeyDownListeners(view View, subviewID ...string) []func(View, KeyEvent) { func GetKeyDownListeners(view View, subviewID ...string) []func(View, KeyEvent) {
return getEventListeners[View, KeyEvent](view, subviewID, KeyDownEvent) return getOneArgEventListeners[View, KeyEvent](view, subviewID, KeyDownEvent)
} }
// GetKeyUpListeners returns the "key-up-event" listener list. If there are no listeners then the empty list is returned. // GetKeyUpListeners returns the "key-up-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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetKeyUpListeners(view View, subviewID ...string) []func(View, KeyEvent) { func GetKeyUpListeners(view View, subviewID ...string) []func(View, KeyEvent) {
return getEventListeners[View, KeyEvent](view, subviewID, KeyUpEvent) return getOneArgEventListeners[View, KeyEvent](view, subviewID, KeyUpEvent)
} }

View File

@ -64,10 +64,10 @@ func (listLayout *listLayoutData) init(session Session) {
listLayout.tag = "ListLayout" listLayout.tag = "ListLayout"
listLayout.systemClass = "ruiListLayout" listLayout.systemClass = "ruiListLayout"
listLayout.normalize = normalizeListLayoutTag listLayout.normalize = normalizeListLayoutTag
listLayout.getFunc = listLayout.get listLayout.get = listLayout.getFunc
listLayout.set = listLayout.setFunc listLayout.set = listLayout.setFunc
listLayout.remove = listLayout.removeFunc listLayout.remove = listLayout.removeFunc
listLayout.changed = listLayoutPropertyChanged listLayout.changed = listLayout.propertyChanged
} }
@ -86,7 +86,7 @@ func normalizeListLayoutTag(tag PropertyName) PropertyName {
return tag return tag
} }
func (listLayout *listLayoutData) get(self View, tag PropertyName) any { func (listLayout *listLayoutData) getFunc(tag PropertyName) any {
switch tag { switch tag {
case Gap: case Gap:
if rowGap := GetListRowGap(listLayout); rowGap.Equal(GetListColumnGap(listLayout)) { if rowGap := GetListRowGap(listLayout); rowGap.Equal(GetListColumnGap(listLayout)) {
@ -100,10 +100,10 @@ func (listLayout *listLayoutData) get(self View, tag PropertyName) any {
} }
} }
return listLayout.viewsContainerData.get(listLayout, tag) return listLayout.viewsContainerData.getFunc(tag)
} }
func (listLayout *listLayoutData) removeFunc(self View, tag PropertyName) []PropertyName { func (listLayout *listLayoutData) removeFunc(tag PropertyName) []PropertyName {
switch tag { switch tag {
case Gap: case Gap:
result := []PropertyName{} result := []PropertyName{}
@ -116,18 +116,18 @@ func (listLayout *listLayoutData) removeFunc(self View, tag PropertyName) []Prop
return result return result
case Content: case Content:
listLayout.viewsContainerData.removeFunc(listLayout, Content) listLayout.viewsContainerData.removeFunc(Content)
listLayout.adapter = nil listLayout.adapter = nil
return []PropertyName{Content} return []PropertyName{Content}
} }
return listLayout.viewsContainerData.removeFunc(listLayout, tag) return listLayout.viewsContainerData.removeFunc(tag)
} }
func (listLayout *listLayoutData) setFunc(self View, tag PropertyName, value any) []PropertyName { func (listLayout *listLayoutData) setFunc(tag PropertyName, value any) []PropertyName {
switch tag { switch tag {
case Gap: case Gap:
result := listLayout.setFunc(listLayout, ListRowGap, value) result := listLayout.setFunc(ListRowGap, value)
if result != nil { if result != nil {
if gap := listLayout.getRaw(ListRowGap); gap != nil { if gap := listLayout.getRaw(ListRowGap); gap != nil {
listLayout.setRaw(ListColumnGap, gap) listLayout.setRaw(ListColumnGap, gap)
@ -147,16 +147,16 @@ func (listLayout *listLayoutData) setFunc(self View, tag PropertyName, value any
} }
return []PropertyName{Content} return []PropertyName{Content}
} }
return listLayout.viewsContainerData.setFunc(listLayout, tag, value) return listLayout.viewsContainerData.setFunc(tag, value)
} }
func listLayoutPropertyChanged(view View, tag PropertyName) { func (listLayout *listLayoutData) propertyChanged(tag PropertyName) {
switch tag { switch tag {
case Orientation, ListWrap, HorizontalAlign, VerticalAlign: case Orientation, ListWrap, HorizontalAlign, VerticalAlign:
updateCSSStyle(view.htmlID(), view.Session()) updateCSSStyle(listLayout.htmlID(), listLayout.Session())
default: default:
viewsContainerPropertyChanged(view, tag) listLayout.viewsContainerData.propertyChanged(tag)
} }
} }

View File

@ -148,11 +148,10 @@ func (listView *listViewData) init(session Session) {
listView.items = []View{} listView.items = []View{}
listView.itemFrame = []Frame{} listView.itemFrame = []Frame{}
listView.normalize = normalizeListViewTag listView.normalize = normalizeListViewTag
listView.getFunc = listView.get listView.get = listView.getFunc
listView.set = listViewSet listView.set = listView.setFunc
listView.changed = listViewPropertyChanged listView.remove = listView.removeFunc
listView.remove = listViewRemove listView.changed = listView.propertyChanged
} }
func (listView *listViewData) Views() []View { func (listView *listViewData) Views() []View {
@ -183,36 +182,36 @@ func normalizeListViewTag(tag PropertyName) PropertyName {
return tag return tag
} }
func listViewRemove(view View, tag PropertyName) []PropertyName { func (listView *listViewData) removeFunc(tag PropertyName) []PropertyName {
switch tag { switch tag {
case Gap: case Gap:
result := viewRemove(view, ListRowGap) result := listView.removeFunc(ListRowGap)
if result != nil { if result != nil {
if result2 := viewRemove(view, ListColumnGap); result2 != nil { if result2 := listView.removeFunc(ListColumnGap); result2 != nil {
result = append(result, result2...) result = append(result, result2...)
} }
} }
return result return result
} }
return viewRemove(view, tag) return listView.viewData.removeFunc(tag)
} }
func listViewSet(view View, tag PropertyName, value any) []PropertyName { func (listView *listViewData) setFunc(tag PropertyName, value any) []PropertyName {
switch tag { switch tag {
case Gap: case Gap:
result := listViewSet(view, ListRowGap, value) result := listView.setFunc(ListRowGap, value)
if result != nil { if result != nil {
if result2 := listViewSet(view, ListColumnGap, value); result2 != nil { if result2 := listView.setFunc(ListColumnGap, value); result2 != nil {
result = append(result, result2...) result = append(result, result2...)
} }
} }
return result return result
case ListItemClickedEvent, ListItemSelectedEvent: case ListItemClickedEvent, ListItemSelectedEvent:
return setViewEventListener[ListView, int](view, tag, value) return setOneArgEventListener[ListView, int](listView, tag, value)
case ListItemCheckedEvent: case ListItemCheckedEvent:
return setViewEventListener[ListView, []int](view, tag, value) return setOneArgEventListener[ListView, []int](listView, tag, value)
case Checked: case Checked:
var checked []int var checked []int
@ -243,69 +242,65 @@ func listViewSet(view View, tag PropertyName, value any) []PropertyName {
return nil return nil
} }
return setArrayPropertyValue(view, Checked, checked) return setArrayPropertyValue(listView, Checked, checked)
case Items: case Items:
return listViewSetItems(view, value) return listView.setItems(value)
case ListItemStyle, CurrentStyle, CurrentInactiveStyle: case ListItemStyle, CurrentStyle, CurrentInactiveStyle:
if text, ok := value.(string); ok { if text, ok := value.(string); ok {
return setStringPropertyValue(view, tag, text) return setStringPropertyValue(listView, tag, text)
} }
notCompatibleType(tag, value) notCompatibleType(tag, value)
return nil return nil
case Current: case Current:
return setIntProperty(view, Current, value) return setIntProperty(listView, Current, value)
} }
return viewSet(view, tag, value) return listView.viewData.setFunc(tag, value)
} }
func listViewPropertyChanged(view View, tag PropertyName) { func (listView *listViewData) propertyChanged(tag PropertyName) {
switch tag { switch tag {
case Current: case Current:
updateInnerHTML(view.htmlID(), view.Session()) updateInnerHTML(listView.htmlID(), listView.Session())
if listeners := getEventListeners[ListView, int](view, nil, ListItemSelectedEvent); len(listeners) > 0 { if listeners := getOneArgEventListeners[ListView, int](listView, nil, ListItemSelectedEvent); len(listeners) > 0 {
if listView, ok := view.(ListView); ok { current := GetCurrent(listView)
current := GetCurrent(view) for _, listener := range listeners {
for _, listener := range listeners { listener(listView, current)
listener(listView, current)
}
} }
} }
case Checked: case Checked:
updateInnerHTML(view.htmlID(), view.Session()) updateInnerHTML(listView.htmlID(), listView.Session())
if listeners := getEventListeners[ListView, []int](view, nil, ListItemCheckedEvent); len(listeners) > 0 { if listeners := getOneArgEventListeners[ListView, []int](listView, nil, ListItemCheckedEvent); len(listeners) > 0 {
if listView, ok := view.(ListView); ok { checked := GetListViewCheckedItems(listView)
checked := GetListViewCheckedItems(view) for _, listener := range listeners {
for _, listener := range listeners { listener(listView, checked)
listener(listView, checked)
}
} }
} }
case Items, Orientation, ListWrap, ListRowGap, ListColumnGap, VerticalAlign, HorizontalAlign, Style, StyleDisabled, ItemWidth, ItemHeight, case Items, Orientation, ListWrap, ListRowGap, ListColumnGap, VerticalAlign, HorizontalAlign, Style, StyleDisabled, ItemWidth, ItemHeight,
ItemHorizontalAlign, ItemVerticalAlign, ItemCheckbox, CheckboxHorizontalAlign, CheckboxVerticalAlign, ListItemStyle, AccentColor: ItemHorizontalAlign, ItemVerticalAlign, ItemCheckbox, CheckboxHorizontalAlign, CheckboxVerticalAlign, ListItemStyle, AccentColor:
updateInnerHTML(view.htmlID(), view.Session()) updateInnerHTML(listView.htmlID(), listView.Session())
case CurrentStyle: case CurrentStyle:
view.Session().updateProperty(view.htmlID(), "data-focusitemstyle", listViewCurrentStyle(view)) listView.Session().updateProperty(listView.htmlID(), "data-focusitemstyle", listViewCurrentStyle(listView))
updateInnerHTML(view.htmlID(), view.Session()) updateInnerHTML(listView.htmlID(), listView.Session())
case CurrentInactiveStyle: case CurrentInactiveStyle:
view.Session().updateProperty(view.htmlID(), "data-bluritemstyle", listViewCurrentInactiveStyle(view)) listView.Session().updateProperty(listView.htmlID(), "data-bluritemstyle", listViewCurrentInactiveStyle(listView))
updateInnerHTML(view.htmlID(), view.Session()) updateInnerHTML(listView.htmlID(), listView.Session())
default: default:
viewPropertyChanged(view, tag) listView.viewData.propertyChanged(tag)
} }
} }
func (listView *listViewData) get(self View, tag PropertyName) any { func (listView *listViewData) getFunc(tag PropertyName) any {
switch tag { switch tag {
case Gap: case Gap:
if rowGap := GetListRowGap(listView); rowGap.Equal(GetListColumnGap(listView)) { if rowGap := GetListRowGap(listView); rowGap.Equal(GetListColumnGap(listView)) {
@ -313,16 +308,13 @@ func (listView *listViewData) get(self View, tag PropertyName) any {
} }
return AutoSize() return AutoSize()
} }
return viewGet(self, tag) return listView.viewData.getFunc(tag)
} }
func listViewSetItems(properties Properties, value any) []PropertyName { func (listView *listViewData) setItems(value any) []PropertyName {
var adapter ListAdapter = nil var adapter ListAdapter = nil
var session Session = nil session := listView.session
if view, ok := properties.(View); ok {
session = view.Session()
}
switch value := value.(type) { switch value := value.(type) {
case []string: case []string:
@ -400,7 +392,7 @@ func listViewSetItems(properties Properties, value any) []PropertyName {
return nil return nil
} }
properties.setRaw(Items, adapter) listView.setRaw(Items, adapter)
return []PropertyName{Items} return []PropertyName{Items}
} }
@ -951,7 +943,9 @@ func (listView *listViewData) handleCommand(self View, command PropertyName, dat
} }
case "itemClick": case "itemClick":
listView.onItemClick() if number, ok := dataIntProperty(data, `number`); ok {
listView.onItemClick(number)
}
default: default:
return listView.viewData.handleCommand(self, command, data) return listView.viewData.handleCommand(self, command, data)
@ -962,7 +956,7 @@ func (listView *listViewData) handleCommand(self View, command PropertyName, dat
func (listView *listViewData) handleCurrent(number int) { func (listView *listViewData) handleCurrent(number int) {
listView.properties[Current] = number listView.properties[Current] = number
for _, listener := range getEventListeners[ListView, int](listView, nil, ListItemSelectedEvent) { for _, listener := range getOneArgEventListeners[ListView, int](listView, nil, ListItemSelectedEvent) {
listener(listView, number) listener(listView, number)
} }
if listener, ok := listView.changeListener[Current]; ok { if listener, ok := listView.changeListener[Current]; ok {
@ -970,33 +964,37 @@ func (listView *listViewData) handleCurrent(number int) {
} }
} }
func (listView *listViewData) onItemClick() { func (listView *listViewData) onItemClick(number int) {
checkbox := GetListViewCheckbox(listView)
if checkbox == NoneCheckbox { if IsDisabled(listView) {
return return
} }
if current := GetCurrent(listView); current >= 0 && !IsDisabled(listView) { if current := GetCurrent(listView); current != number {
listView.Set(Current, number)
}
if checkbox := GetListViewCheckbox(listView); checkbox != NoneCheckbox {
checkedItem := GetListViewCheckedItems(listView) checkedItem := GetListViewCheckedItems(listView)
switch checkbox { switch checkbox {
case SingleCheckbox: case SingleCheckbox:
if len(checkedItem) == 0 { if len(checkedItem) == 0 {
checkedItem = []int{current} checkedItem = []int{number}
listView.updateCheckboxItem(current, true) listView.updateCheckboxItem(number, true)
} else if checkedItem[0] != current { } else if checkedItem[0] != number {
listView.updateCheckboxItem(checkedItem[0], false) listView.updateCheckboxItem(checkedItem[0], false)
checkedItem = []int{current} checkedItem = []int{number}
listView.updateCheckboxItem(current, true) listView.updateCheckboxItem(number, true)
} else { } else {
checkedItem = []int{} checkedItem = []int{}
listView.updateCheckboxItem(current, false) listView.updateCheckboxItem(number, false)
} }
case MultipleCheckbox: case MultipleCheckbox:
uncheck := false uncheck := false
for i, index := range checkedItem { for i, index := range checkedItem {
if index == current { if index == number {
uncheck = true uncheck = true
listView.updateCheckboxItem(index, false) listView.updateCheckboxItem(index, false)
count := len(checkedItem) count := len(checkedItem)
@ -1014,8 +1012,8 @@ func (listView *listViewData) onItemClick() {
} }
if !uncheck { if !uncheck {
listView.updateCheckboxItem(current, true) listView.updateCheckboxItem(number, true)
checkedItem = append(checkedItem, current) checkedItem = append(checkedItem, number)
} }
} }
@ -1024,10 +1022,15 @@ func (listView *listViewData) onItemClick() {
listener(listView, Checked) listener(listView, Checked)
} }
for _, listener := range getEventListeners[ListView, []int](listView, nil, ListItemCheckedEvent) { for _, listener := range getOneArgEventListeners[ListView, []int](listView, nil, ListItemCheckedEvent) {
listener(listView, checkedItem) listener(listView, checkedItem)
} }
} }
for _, listener := range getOneArgEventListeners[ListView, int](listView, nil, ListItemClickedEvent) {
listener(listView, number)
}
} }
func (listView *listViewData) onItemResize(self View, index string, x, y, width, height float64) { func (listView *listViewData) onItemResize(self View, index string, x, y, width, height float64) {
@ -1057,21 +1060,21 @@ func GetHorizontalAlign(view View, subviewID ...string) int {
// If there are no listeners then the empty list is returned // 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetListItemClickedListeners(view View, subviewID ...string) []func(ListView, int) { func GetListItemClickedListeners(view View, subviewID ...string) []func(ListView, int) {
return getEventListeners[ListView, int](view, subviewID, ListItemClickedEvent) return getOneArgEventListeners[ListView, int](view, subviewID, ListItemClickedEvent)
} }
// GetListItemSelectedListeners returns a ListItemSelectedListener of the ListView. // GetListItemSelectedListeners returns a ListItemSelectedListener of the ListView.
// If there are no listeners then the empty list is returned // 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetListItemSelectedListeners(view View, subviewID ...string) []func(ListView, int) { func GetListItemSelectedListeners(view View, subviewID ...string) []func(ListView, int) {
return getEventListeners[ListView, int](view, subviewID, ListItemSelectedEvent) return getOneArgEventListeners[ListView, int](view, subviewID, ListItemSelectedEvent)
} }
// GetListItemCheckedListeners returns a ListItemCheckedListener of the ListView. // GetListItemCheckedListeners returns a ListItemCheckedListener of the ListView.
// If there are no listeners then the empty list is returned // 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetListItemCheckedListeners(view View, subviewID ...string) []func(ListView, []int) { func GetListItemCheckedListeners(view View, subviewID ...string) []func(ListView, []int) {
return getEventListeners[ListView, []int](view, subviewID, ListItemCheckedEvent) return getOneArgEventListeners[ListView, []int](view, subviewID, ListItemCheckedEvent)
} }
// GetListItemWidth returns the width of a ListView item. // GetListItemWidth returns the width of a ListView item.

View File

@ -907,46 +907,39 @@ type MediaSource struct {
func (player *mediaPlayerData) init(session Session) { func (player *mediaPlayerData) init(session Session) {
player.viewData.init(session) player.viewData.init(session)
player.tag = "MediaPlayer" player.tag = "MediaPlayer"
player.set = mediaPlayerSet player.set = player.setFunc
player.changed = mediaPlayerPropertyChanged player.changed = player.propertyChanged
} }
func (player *mediaPlayerData) Focusable() bool { func (player *mediaPlayerData) Focusable() bool {
return true return true
} }
func mediaPlayerSet(view View, tag PropertyName, value any) []PropertyName { func (player *mediaPlayerData) setFunc(tag PropertyName, value any) []PropertyName {
switch tag { switch tag {
case AbortEvent, CanPlayEvent, CanPlayThroughEvent, CompleteEvent, EmptiedEvent, LoadStartEvent, case AbortEvent, CanPlayEvent, CanPlayThroughEvent, CompleteEvent, EmptiedEvent, LoadStartEvent,
EndedEvent, LoadedDataEvent, LoadedMetadataEvent, PauseEvent, PlayEvent, PlayingEvent, EndedEvent, LoadedDataEvent, LoadedMetadataEvent, PauseEvent, PlayEvent, PlayingEvent,
ProgressEvent, SeekedEvent, SeekingEvent, StalledEvent, SuspendEvent, WaitingEvent: ProgressEvent, SeekedEvent, SeekingEvent, StalledEvent, SuspendEvent, WaitingEvent:
return setNoParamEventListener[MediaPlayer](view, tag, value) return setNoArgEventListener[MediaPlayer](player, tag, value)
case DurationChangedEvent, RateChangedEvent, TimeUpdateEvent, VolumeChangedEvent: case DurationChangedEvent, RateChangedEvent, TimeUpdateEvent, VolumeChangedEvent:
return setViewEventListener[MediaPlayer, float64](view, tag, value) return setOneArgEventListener[MediaPlayer, float64](player, tag, value)
case PlayerErrorEvent: case PlayerErrorEvent:
if listeners, ok := valueToPlayerErrorListeners(value); ok { if listeners, ok := valueToPlayerErrorListeners(value); ok {
if len(listeners) > 0 { return setArrayPropertyValue(player, tag, listeners)
view.setRaw(tag, listeners)
} else if view.getRaw(tag) != nil {
view.setRaw(tag, nil)
} else {
return []PropertyName{}
}
return []PropertyName{tag}
} }
notCompatibleType(tag, value) notCompatibleType(tag, value)
return nil return nil
case Source: case Source:
return setMediaPlayerSource(view, value) return setMediaPlayerSource(player, value)
} }
return viewSet(view, tag, value) return player.viewData.setFunc(tag, value)
} }
func setMediaPlayerSource(properties Properties, value any) []PropertyName { func setMediaPlayerSource(properties Properties, value any) []PropertyName {
@ -1151,26 +1144,26 @@ func mediaPlayerEvents() map[PropertyName]string {
} }
} }
func mediaPlayerPropertyChanged(view View, tag PropertyName) { func (player *mediaPlayerData) propertyChanged(tag PropertyName) {
session := view.Session() session := player.Session()
switch tag { switch tag {
case Controls, Loop: case Controls, Loop:
value, _ := boolProperty(view, tag, session) value, _ := boolProperty(player, tag, session)
if value { if value {
session.updateProperty(view.htmlID(), string(tag), value) session.updateProperty(player.htmlID(), string(tag), value)
} else { } else {
session.removeProperty(view.htmlID(), string(tag)) session.removeProperty(player.htmlID(), string(tag))
} }
case Muted: case Muted:
value, _ := boolProperty(view, Muted, session) value, _ := boolProperty(player, Muted, session)
session.callFunc("setMediaMuted", view.htmlID(), value) session.callFunc("setMediaMuted", player.htmlID(), value)
case Preload: case Preload:
value, _ := enumProperty(view, Preload, session, 0) value, _ := enumProperty(player, Preload, session, 0)
values := enumProperties[Preload].values values := enumProperties[Preload].values
session.updateProperty(view.htmlID(), string(Preload), values[value]) session.updateProperty(player.htmlID(), string(Preload), values[value])
case AbortEvent, CanPlayEvent, CanPlayThroughEvent, CompleteEvent, EmptiedEvent, case AbortEvent, CanPlayEvent, CanPlayThroughEvent, CompleteEvent, EmptiedEvent,
EndedEvent, LoadedDataEvent, LoadedMetadataEvent, PauseEvent, PlayEvent, PlayingEvent, ProgressEvent, EndedEvent, LoadedDataEvent, LoadedMetadataEvent, PauseEvent, PlayEvent, PlayingEvent, ProgressEvent,
@ -1178,54 +1171,54 @@ func mediaPlayerPropertyChanged(view View, tag PropertyName) {
if cssTag, ok := mediaPlayerEvents()[tag]; ok { if cssTag, ok := mediaPlayerEvents()[tag]; ok {
fn := "" fn := ""
if value := view.getRaw(tag); value != nil { if value := player.getRaw(tag); value != nil {
if listeners, ok := value.([]func(MediaPlayer)); ok && len(listeners) > 0 { if listeners, ok := value.([]func(MediaPlayer)); ok && len(listeners) > 0 {
fn = fmt.Sprintf(`viewEvent(this, "%s")`, string(tag)) fn = fmt.Sprintf(`viewEvent(this, "%s")`, string(tag))
} }
} }
session.updateProperty(view.htmlID(), cssTag, fn) session.updateProperty(player.htmlID(), cssTag, fn)
} }
case TimeUpdateEvent: case TimeUpdateEvent:
if value := view.getRaw(tag); value != nil { if value := player.getRaw(tag); value != nil {
session.updateProperty(view.htmlID(), "ontimeupdate", "viewTimeUpdatedEvent(this)") session.updateProperty(player.htmlID(), "ontimeupdate", "viewTimeUpdatedEvent(this)")
} else { } else {
session.updateProperty(view.htmlID(), "ontimeupdate", "") session.updateProperty(player.htmlID(), "ontimeupdate", "")
} }
case VolumeChangedEvent: case VolumeChangedEvent:
if value := view.getRaw(tag); value != nil { if value := player.getRaw(tag); value != nil {
session.updateProperty(view.htmlID(), "onvolumechange", "viewVolumeChangedEvent(this)") session.updateProperty(player.htmlID(), "onvolumechange", "viewVolumeChangedEvent(this)")
} else { } else {
session.updateProperty(view.htmlID(), "onvolumechange", "") session.updateProperty(player.htmlID(), "onvolumechange", "")
} }
case DurationChangedEvent: case DurationChangedEvent:
if value := view.getRaw(tag); value != nil { if value := player.getRaw(tag); value != nil {
session.updateProperty(view.htmlID(), "ondurationchange", "viewDurationChangedEvent(this)") session.updateProperty(player.htmlID(), "ondurationchange", "viewDurationChangedEvent(this)")
} else { } else {
session.updateProperty(view.htmlID(), "ondurationchange", "") session.updateProperty(player.htmlID(), "ondurationchange", "")
} }
case RateChangedEvent: case RateChangedEvent:
if value := view.getRaw(tag); value != nil { if value := player.getRaw(tag); value != nil {
session.updateProperty(view.htmlID(), "onratechange", "viewRateChangedEvent(this)") session.updateProperty(player.htmlID(), "onratechange", "viewRateChangedEvent(this)")
} else { } else {
session.updateProperty(view.htmlID(), "onratechange", "") session.updateProperty(player.htmlID(), "onratechange", "")
} }
case PlayerErrorEvent: case PlayerErrorEvent:
if value := view.getRaw(tag); value != nil { if value := player.getRaw(tag); value != nil {
session.updateProperty(view.htmlID(), "onerror", "viewErrorEvent(this)") session.updateProperty(player.htmlID(), "onerror", "viewErrorEvent(this)")
} else { } else {
session.updateProperty(view.htmlID(), "onerror", "") session.updateProperty(player.htmlID(), "onerror", "")
} }
case Source: case Source:
updateInnerHTML(view.htmlID(), session) updateInnerHTML(player.htmlID(), session)
default: default:
viewPropertyChanged(view, tag) player.viewData.propertyChanged(tag)
} }
} }

View File

@ -230,7 +230,7 @@ type MouseEvent struct {
/* /*
func setMouseListener(properties Properties, tag PropertyName, value any) bool { func setMouseListener(properties Properties, tag PropertyName, value any) bool {
if listeners, ok := valueToEventListeners[View, MouseEvent](value); ok { if listeners, ok := valueToOneArgEventListeners[View, MouseEvent](value); ok {
if len(listeners) == 0 { if len(listeners) == 0 {
properties.setRaw(tag, nil) properties.setRaw(tag, nil)
} else { } else {
@ -302,7 +302,7 @@ func (event *MouseEvent) init(data DataObject) {
} }
func handleMouseEvents(view View, tag PropertyName, data DataObject) { func handleMouseEvents(view View, tag PropertyName, data DataObject) {
listeners := getEventListeners[View, MouseEvent](view, nil, tag) listeners := getOneArgEventListeners[View, MouseEvent](view, nil, tag)
if len(listeners) > 0 { if len(listeners) > 0 {
var event MouseEvent var event MouseEvent
event.init(data) event.init(data)
@ -316,48 +316,48 @@ func handleMouseEvents(view View, tag PropertyName, data DataObject) {
// GetClickListeners returns the "click-event" listener list. If there are no listeners then the empty list is returned. // GetClickListeners returns the "click-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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetClickListeners(view View, subviewID ...string) []func(View, MouseEvent) { func GetClickListeners(view View, subviewID ...string) []func(View, MouseEvent) {
return getEventListeners[View, MouseEvent](view, subviewID, ClickEvent) return getOneArgEventListeners[View, MouseEvent](view, subviewID, ClickEvent)
} }
// GetDoubleClickListeners returns the "double-click-event" listener list. If there are no listeners then the empty list is returned. // GetDoubleClickListeners returns the "double-click-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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetDoubleClickListeners(view View, subviewID ...string) []func(View, MouseEvent) { func GetDoubleClickListeners(view View, subviewID ...string) []func(View, MouseEvent) {
return getEventListeners[View, MouseEvent](view, subviewID, DoubleClickEvent) return getOneArgEventListeners[View, MouseEvent](view, subviewID, DoubleClickEvent)
} }
// GetContextMenuListeners returns the "context-menu" listener list. // GetContextMenuListeners returns the "context-menu" listener list.
// If there are no listeners then the empty list is returned. // 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetContextMenuListeners(view View, subviewID ...string) []func(View, MouseEvent) { func GetContextMenuListeners(view View, subviewID ...string) []func(View, MouseEvent) {
return getEventListeners[View, MouseEvent](view, subviewID, ContextMenuEvent) return getOneArgEventListeners[View, MouseEvent](view, subviewID, ContextMenuEvent)
} }
// GetMouseDownListeners returns the "mouse-down" listener list. If there are no listeners then the empty list is returned. // GetMouseDownListeners returns the "mouse-down" 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetMouseDownListeners(view View, subviewID ...string) []func(View, MouseEvent) { func GetMouseDownListeners(view View, subviewID ...string) []func(View, MouseEvent) {
return getEventListeners[View, MouseEvent](view, subviewID, MouseDown) return getOneArgEventListeners[View, MouseEvent](view, subviewID, MouseDown)
} }
// GetMouseUpListeners returns the "mouse-up" listener list. If there are no listeners then the empty list is returned. // GetMouseUpListeners returns the "mouse-up" 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetMouseUpListeners(view View, subviewID ...string) []func(View, MouseEvent) { func GetMouseUpListeners(view View, subviewID ...string) []func(View, MouseEvent) {
return getEventListeners[View, MouseEvent](view, subviewID, MouseUp) return getOneArgEventListeners[View, MouseEvent](view, subviewID, MouseUp)
} }
// GetMouseMoveListeners returns the "mouse-move" listener list. If there are no listeners then the empty list is returned. // GetMouseMoveListeners returns the "mouse-move" 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetMouseMoveListeners(view View, subviewID ...string) []func(View, MouseEvent) { func GetMouseMoveListeners(view View, subviewID ...string) []func(View, MouseEvent) {
return getEventListeners[View, MouseEvent](view, subviewID, MouseMove) return getOneArgEventListeners[View, MouseEvent](view, subviewID, MouseMove)
} }
// GetMouseOverListeners returns the "mouse-over" listener list. If there are no listeners then the empty list is returned. // GetMouseOverListeners returns the "mouse-over" 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetMouseOverListeners(view View, subviewID ...string) []func(View, MouseEvent) { func GetMouseOverListeners(view View, subviewID ...string) []func(View, MouseEvent) {
return getEventListeners[View, MouseEvent](view, subviewID, MouseOver) return getOneArgEventListeners[View, MouseEvent](view, subviewID, MouseOver)
} }
// GetMouseOutListeners returns the "mouse-out" listener list. If there are no listeners then the empty list is returned. // GetMouseOutListeners returns the "mouse-out" 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetMouseOutListeners(view View, subviewID ...string) []func(View, MouseEvent) { func GetMouseOutListeners(view View, subviewID ...string) []func(View, MouseEvent) {
return getEventListeners[View, MouseEvent](view, subviewID, MouseOut) return getOneArgEventListeners[View, MouseEvent](view, subviewID, MouseOut)
} }

View File

@ -1,6 +1,7 @@
package rui package rui
import ( import (
"fmt"
"math" "math"
"strconv" "strconv"
"strings" "strings"
@ -79,6 +80,16 @@ const (
// //
// Internal type is `float`, other types converted to it during assignment. // Internal type is `float`, other types converted to it during assignment.
NumberPickerValue PropertyName = "number-picker-value" NumberPickerValue PropertyName = "number-picker-value"
// NumberPickerValue is the constant for "number-picker-value" property tag.
//
// Used by `NumberPicker`.
// Precision of displaying fractional part in editor. The default value is 0 (not used).
//
// Supported types: `int`, `int8`...`int64`, `uint`, `uint8`...`uint64`, `string`.
//
// Internal type is `float`, other types converted to it during assignment.
NumberPickerPrecision PropertyName = "number-picker-precision"
) )
// Constants which describe values of the "number-picker-type" property of a [NumberPicker] // Constants which describe values of the "number-picker-type" property of a [NumberPicker]
@ -116,8 +127,8 @@ func (picker *numberPickerData) init(session Session) {
picker.tag = "NumberPicker" picker.tag = "NumberPicker"
picker.hasHtmlDisabled = true picker.hasHtmlDisabled = true
picker.normalize = normalizeNumberPickerTag picker.normalize = normalizeNumberPickerTag
picker.set = numberPickerSet picker.set = picker.setFunc
picker.changed = numberPickerPropertyChanged picker.changed = picker.propertyChanged
} }
func (picker *numberPickerData) Focusable() bool { func (picker *numberPickerData) Focusable() bool {
@ -127,73 +138,83 @@ func (picker *numberPickerData) Focusable() bool {
func normalizeNumberPickerTag(tag PropertyName) PropertyName { func normalizeNumberPickerTag(tag PropertyName) PropertyName {
tag = defaultNormalize(tag) tag = defaultNormalize(tag)
switch tag { switch tag {
case Type, Min, Max, Step, Value: case Type, Min, Max, Step, Value, "precision":
return "number-picker-" + tag return "number-picker-" + tag
} }
return normalizeDataListTag(tag) return normalizeDataListTag(tag)
} }
func numberPickerSet(view View, tag PropertyName, value any) []PropertyName { func (picker *numberPickerData) setFunc(tag PropertyName, value any) []PropertyName {
switch tag { switch tag {
case NumberChangedEvent: case NumberChangedEvent:
return setEventWithOldListener[NumberPicker, float64](view, tag, value) return setTwoArgEventListener[NumberPicker, float64](picker, tag, value)
case NumberPickerValue: case NumberPickerValue:
view.setRaw("old-number", GetNumberPickerValue(view)) picker.setRaw("old-number", GetNumberPickerValue(picker))
min, max := GetNumberPickerMinMax(view) min, max := GetNumberPickerMinMax(picker)
return setFloatProperty(view, NumberPickerValue, value, min, max) return setFloatProperty(picker, NumberPickerValue, value, min, max)
case DataList: case DataList:
return setDataList(view, value, "") return setDataList(picker, value, "")
} }
return viewSet(view, tag, value) return picker.viewData.setFunc(tag, value)
} }
func numberPickerPropertyChanged(view View, tag PropertyName) { func (picker *numberPickerData) numberFormat() string {
if precission := GetNumberPickerPrecision(picker); precission > 0 {
return fmt.Sprintf("%%.%df", precission)
}
return "%g"
}
func (picker *numberPickerData) propertyChanged(tag PropertyName) {
switch tag { switch tag {
case NumberPickerType: case NumberPickerType:
if GetNumberPickerType(view) == NumberSlider { if GetNumberPickerType(picker) == NumberSlider {
view.Session().updateProperty(view.htmlID(), "type", "range") picker.Session().updateProperty(picker.htmlID(), "type", "range")
} else { } else {
view.Session().updateProperty(view.htmlID(), "type", "number") picker.Session().updateProperty(picker.htmlID(), "type", "number")
} }
case NumberPickerMin: case NumberPickerMin:
min, _ := GetNumberPickerMinMax(view) min, _ := GetNumberPickerMinMax(picker)
view.Session().updateProperty(view.htmlID(), "min", strconv.FormatFloat(min, 'f', -1, 32)) picker.Session().updateProperty(picker.htmlID(), "min", fmt.Sprintf(picker.numberFormat(), min))
case NumberPickerMax: case NumberPickerMax:
_, max := GetNumberPickerMinMax(view) _, max := GetNumberPickerMinMax(picker)
view.Session().updateProperty(view.htmlID(), "max", strconv.FormatFloat(max, 'f', -1, 32)) picker.Session().updateProperty(picker.htmlID(), "max", fmt.Sprintf(picker.numberFormat(), max))
case NumberPickerStep: case NumberPickerStep:
if step := GetNumberPickerStep(view); step > 0 { if step := GetNumberPickerStep(picker); step > 0 {
view.Session().updateProperty(view.htmlID(), "step", strconv.FormatFloat(step, 'f', -1, 32)) picker.Session().updateProperty(picker.htmlID(), "step", fmt.Sprintf(picker.numberFormat(), step))
} else { } else {
view.Session().updateProperty(view.htmlID(), "step", "any") picker.Session().updateProperty(picker.htmlID(), "step", "any")
} }
case TimePickerValue: case NumberPickerValue:
value := GetNumberPickerValue(view) value := GetNumberPickerValue(picker)
view.Session().callFunc("setInputValue", view.htmlID(), value) format := picker.numberFormat()
picker.Session().callFunc("setInputValue", picker.htmlID(), fmt.Sprintf(format, value))
if listeners := GetNumberChangedListeners(view); len(listeners) > 0 { if listeners := GetNumberChangedListeners(picker); len(listeners) > 0 {
old := 0.0 old := 0.0
if val := view.getRaw("old-number"); val != nil { if val := picker.getRaw("old-number"); val != nil {
if n, ok := val.(float64); ok { if n, ok := val.(float64); ok {
old = n old = n
} }
} }
for _, listener := range listeners { if old != value {
listener(view, value, old) for _, listener := range listeners {
listener(picker, value, old)
}
} }
} }
default: default:
viewPropertyChanged(view, tag) picker.viewData.propertyChanged(tag)
} }
} }
@ -217,30 +238,31 @@ func (picker *numberPickerData) htmlProperties(self View, buffer *strings.Builde
buffer.WriteString(` type="number"`) buffer.WriteString(` type="number"`)
} }
format := picker.numberFormat()
min, max := GetNumberPickerMinMax(picker) min, max := GetNumberPickerMinMax(picker)
if min != math.Inf(-1) { if min != math.Inf(-1) {
buffer.WriteString(` min="`) buffer.WriteString(` min="`)
buffer.WriteString(strconv.FormatFloat(min, 'f', -1, 64)) buffer.WriteString(fmt.Sprintf(format, min))
buffer.WriteByte('"') buffer.WriteByte('"')
} }
if max != math.Inf(1) { if max != math.Inf(1) {
buffer.WriteString(` max="`) buffer.WriteString(` max="`)
buffer.WriteString(strconv.FormatFloat(max, 'f', -1, 64)) buffer.WriteString(fmt.Sprintf(format, max))
buffer.WriteByte('"') buffer.WriteByte('"')
} }
step := GetNumberPickerStep(picker) step := GetNumberPickerStep(picker)
if step != 0 { if step != 0 {
buffer.WriteString(` step="`) buffer.WriteString(` step="`)
buffer.WriteString(strconv.FormatFloat(step, 'f', -1, 64)) buffer.WriteString(fmt.Sprintf(format, step))
buffer.WriteByte('"') buffer.WriteByte('"')
} else { } else {
buffer.WriteString(` step="any"`) buffer.WriteString(` step="any"`)
} }
buffer.WriteString(` value="`) buffer.WriteString(` value="`)
buffer.WriteString(strconv.FormatFloat(GetNumberPickerValue(picker), 'f', -1, 64)) buffer.WriteString(fmt.Sprintf(format, GetNumberPickerValue(picker)))
buffer.WriteByte('"') buffer.WriteByte('"')
buffer.WriteString(` oninput="editViewInputEvent(this)"`) buffer.WriteString(` oninput="editViewInputEvent(this)"`)
@ -342,5 +364,11 @@ func GetNumberPickerValue(view View, subviewID ...string) float64 {
// If there are no listeners then the empty list is returned // 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetNumberChangedListeners(view View, subviewID ...string) []func(NumberPicker, float64, float64) { func GetNumberChangedListeners(view View, subviewID ...string) []func(NumberPicker, float64, float64) {
return getEventWithOldListeners[NumberPicker, float64](view, subviewID, NumberChangedEvent) return getTwoArgEventListeners[NumberPicker, float64](view, subviewID, NumberChangedEvent)
}
// GetNumberPickerPrecision returns the precision of displaying fractional part in editor of NumberPicker subview.
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetNumberPickerPrecision(view View, subviewID ...string) int {
return intStyledProperty(view, subviewID, NumberPickerPrecision, 0)
} }

View File

@ -156,7 +156,7 @@ type PointerEvent struct {
/* /*
func setPointerListener(properties Properties, tag PropertyName, value any) bool { func setPointerListener(properties Properties, tag PropertyName, value any) bool {
if listeners, ok := valueToEventListeners[View, PointerEvent](value); ok { if listeners, ok := valueToOneArgEventListeners[View, PointerEvent](value); ok {
if len(listeners) == 0 { if len(listeners) == 0 {
properties.setRaw(tag, nil) properties.setRaw(tag, nil)
} else { } else {
@ -210,7 +210,7 @@ func (event *PointerEvent) init(data DataObject) {
} }
func handlePointerEvents(view View, tag PropertyName, data DataObject) { func handlePointerEvents(view View, tag PropertyName, data DataObject) {
listeners := getEventListeners[View, PointerEvent](view, nil, tag) listeners := getOneArgEventListeners[View, PointerEvent](view, nil, tag)
if len(listeners) == 0 { if len(listeners) == 0 {
return return
} }
@ -226,35 +226,35 @@ func handlePointerEvents(view View, tag PropertyName, data DataObject) {
// GetPointerDownListeners returns the "pointer-down" listener list. If there are no listeners then the empty list is returned. // GetPointerDownListeners returns the "pointer-down" 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetPointerDownListeners(view View, subviewID ...string) []func(View, PointerEvent) { func GetPointerDownListeners(view View, subviewID ...string) []func(View, PointerEvent) {
return getEventListeners[View, PointerEvent](view, subviewID, PointerDown) return getOneArgEventListeners[View, PointerEvent](view, subviewID, PointerDown)
} }
// GetPointerUpListeners returns the "pointer-up" listener list. If there are no listeners then the empty list is returned. // GetPointerUpListeners returns the "pointer-up" 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetPointerUpListeners(view View, subviewID ...string) []func(View, PointerEvent) { func GetPointerUpListeners(view View, subviewID ...string) []func(View, PointerEvent) {
return getEventListeners[View, PointerEvent](view, subviewID, PointerUp) return getOneArgEventListeners[View, PointerEvent](view, subviewID, PointerUp)
} }
// GetPointerMoveListeners returns the "pointer-move" listener list. If there are no listeners then the empty list is returned. // GetPointerMoveListeners returns the "pointer-move" 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetPointerMoveListeners(view View, subviewID ...string) []func(View, PointerEvent) { func GetPointerMoveListeners(view View, subviewID ...string) []func(View, PointerEvent) {
return getEventListeners[View, PointerEvent](view, subviewID, PointerMove) return getOneArgEventListeners[View, PointerEvent](view, subviewID, PointerMove)
} }
// GetPointerCancelListeners returns the "pointer-cancel" listener list. If there are no listeners then the empty list is returned. // GetPointerCancelListeners returns the "pointer-cancel" 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetPointerCancelListeners(view View, subviewID ...string) []func(View, PointerEvent) { func GetPointerCancelListeners(view View, subviewID ...string) []func(View, PointerEvent) {
return getEventListeners[View, PointerEvent](view, subviewID, PointerCancel) return getOneArgEventListeners[View, PointerEvent](view, subviewID, PointerCancel)
} }
// GetPointerOverListeners returns the "pointer-over" listener list. If there are no listeners then the empty list is returned. // GetPointerOverListeners returns the "pointer-over" 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetPointerOverListeners(view View, subviewID ...string) []func(View, PointerEvent) { func GetPointerOverListeners(view View, subviewID ...string) []func(View, PointerEvent) {
return getEventListeners[View, PointerEvent](view, subviewID, PointerOver) return getOneArgEventListeners[View, PointerEvent](view, subviewID, PointerOver)
} }
// GetPointerOutListeners returns the "pointer-out" listener list. If there are no listeners then the empty list is returned. // GetPointerOutListeners returns the "pointer-out" 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetPointerOutListeners(view View, subviewID ...string) []func(View, PointerEvent) { func GetPointerOutListeners(view View, subviewID ...string) []func(View, PointerEvent) {
return getEventListeners[View, PointerEvent](view, subviewID, PointerOut) return getOneArgEventListeners[View, PointerEvent](view, subviewID, PointerOut)
} }

View File

@ -560,7 +560,7 @@ func (popup *popupData) init(view View, popupParams Params) {
} }
case DismissEvent: case DismissEvent:
if listeners, ok := valueToNoParamListeners[Popup](value); ok { if listeners, ok := valueToNoArgEventListeners[Popup](value); ok {
if listeners != nil { if listeners != nil {
popup.dismissListener = listeners popup.dismissListener = listeners
} }

View File

@ -53,7 +53,7 @@ func (progress *progressBarData) init(session Session) {
progress.viewData.init(session) progress.viewData.init(session)
progress.tag = "ProgressBar" progress.tag = "ProgressBar"
progress.normalize = normalizeProgressBarTag progress.normalize = normalizeProgressBarTag
progress.changed = progressBarPropertyChanged progress.changed = progress.propertyChanged
} }
func normalizeProgressBarTag(tag PropertyName) PropertyName { func normalizeProgressBarTag(tag PropertyName) PropertyName {
@ -68,19 +68,19 @@ func normalizeProgressBarTag(tag PropertyName) PropertyName {
return tag return tag
} }
func progressBarPropertyChanged(view View, tag PropertyName) { func (progress *progressBarData) propertyChanged(tag PropertyName) {
switch tag { switch tag {
case ProgressBarMax: case ProgressBarMax:
view.Session().updateProperty(view.htmlID(), "max", progress.Session().updateProperty(progress.htmlID(), "max",
strconv.FormatFloat(GetProgressBarMax(view), 'f', -1, 32)) strconv.FormatFloat(GetProgressBarMax(progress), 'f', -1, 32))
case ProgressBarValue: case ProgressBarValue:
view.Session().updateProperty(view.htmlID(), "value", progress.Session().updateProperty(progress.htmlID(), "value",
strconv.FormatFloat(GetProgressBarValue(view), 'f', -1, 32)) strconv.FormatFloat(GetProgressBarValue(progress), 'f', -1, 32))
default: default:
viewPropertyChanged(view, tag) progress.viewData.propertyChanged(tag)
} }
} }

View File

@ -19,6 +19,7 @@ var colorProperties = []PropertyName{
OutlineColor, OutlineColor,
TextLineColor, TextLineColor,
ColorPickerValue, ColorPickerValue,
AccentColor,
} }
func isPropertyInList(tag PropertyName, list []PropertyName) bool { func isPropertyInList(tag PropertyName, list []PropertyName) bool {
@ -74,6 +75,7 @@ var intProperties = []PropertyName{
Order, Order,
TabIndex, TabIndex,
MaxLength, MaxLength,
NumberPickerPrecision,
} }
var floatProperties = map[PropertyName]struct{ min, max float64 }{ var floatProperties = map[PropertyName]struct{ min, max float64 }{
@ -136,9 +138,9 @@ var sizeProperties = map[PropertyName]string{
Perspective: string(Perspective), Perspective: string(Perspective),
PerspectiveOriginX: string(PerspectiveOriginX), PerspectiveOriginX: string(PerspectiveOriginX),
PerspectiveOriginY: string(PerspectiveOriginY), PerspectiveOriginY: string(PerspectiveOriginY),
OriginX: string(OriginX), TransformOriginX: string(TransformOriginX),
OriginY: string(OriginY), TransformOriginY: string(TransformOriginY),
OriginZ: string(OriginZ), TransformOriginZ: string(TransformOriginZ),
Radius: string(Radius), Radius: string(Radius),
RadiusX: string(RadiusX), RadiusX: string(RadiusX),
RadiusY: string(RadiusY), RadiusY: string(RadiusY),

View File

@ -80,8 +80,8 @@ func (resizable *resizableData) init(session Session) {
resizable.viewData.init(session) resizable.viewData.init(session)
resizable.tag = "Resizable" resizable.tag = "Resizable"
resizable.systemClass = "ruiGridLayout" resizable.systemClass = "ruiGridLayout"
resizable.set = resizableSet resizable.set = resizable.setFunc
resizable.changed = resizablePropertyChanged resizable.changed = resizable.propertyChanged
} }
func (resizable *resizableData) Views() []View { func (resizable *resizableData) Views() []View {
@ -100,25 +100,25 @@ func (resizable *resizableData) content() View {
return nil return nil
} }
func resizableSet(view View, tag PropertyName, value any) []PropertyName { func (resizable *resizableData) setFunc(tag PropertyName, value any) []PropertyName {
switch tag { switch tag {
case Side: case Side:
return resizableSetSide(view, value) return resizableSetSide(resizable, value)
case ResizeBorderWidth: case ResizeBorderWidth:
return setSizeProperty(view, tag, value) return setSizeProperty(resizable, tag, value)
case Content: case Content:
var newContent View = nil var newContent View = nil
switch value := value.(type) { switch value := value.(type) {
case string: case string:
newContent = NewTextView(view.Session(), Params{Text: value}) newContent = NewTextView(resizable.Session(), Params{Text: value})
case View: case View:
newContent = value newContent = value
case DataObject: case DataObject:
if newContent = CreateViewFromObject(view.Session(), value); newContent == nil { if newContent = CreateViewFromObject(resizable.Session(), value); newContent == nil {
return nil return nil
} }
@ -127,7 +127,7 @@ func resizableSet(view View, tag PropertyName, value any) []PropertyName {
return nil return nil
} }
view.setRaw(Content, newContent) resizable.setRaw(Content, newContent)
return []PropertyName{} return []PropertyName{}
case CellWidth, CellHeight, GridRowGap, GridColumnGap, CellVerticalAlign, CellHorizontalAlign: case CellWidth, CellHeight, GridRowGap, GridColumnGap, CellVerticalAlign, CellHorizontalAlign:
@ -135,28 +135,28 @@ func resizableSet(view View, tag PropertyName, value any) []PropertyName {
return nil return nil
} }
return viewSet(view, tag, value) return resizable.viewData.setFunc(tag, value)
} }
func resizablePropertyChanged(view View, tag PropertyName) { func (resizable *resizableData) propertyChanged(tag PropertyName) {
switch tag { switch tag {
case Side: case Side:
updateInnerHTML(view.htmlID(), view.Session()) updateInnerHTML(resizable.htmlID(), resizable.Session())
fallthrough fallthrough
case ResizeBorderWidth: case ResizeBorderWidth:
htmlID := view.htmlID() htmlID := resizable.htmlID()
session := view.Session() session := resizable.Session()
column, row := resizableCellSizeCSS(view) column, row := resizableCellSizeCSS(resizable)
session.updateCSSProperty(htmlID, "grid-template-columns", column) session.updateCSSProperty(htmlID, "grid-template-columns", column)
session.updateCSSProperty(htmlID, "grid-template-rows", row) session.updateCSSProperty(htmlID, "grid-template-rows", row)
case Content: case Content:
updateInnerHTML(view.htmlID(), view.Session()) updateInnerHTML(resizable.htmlID(), resizable.Session())
default: default:
viewPropertyChanged(view, tag) resizable.viewData.propertyChanged(tag)
} }
} }

View File

@ -33,7 +33,7 @@ func (view *viewData) onItemResize(self View, index string, x, y, width, height
/* /*
func setFrameListener(properties Properties, tag PropertyName, value any) bool { func setFrameListener(properties Properties, tag PropertyName, value any) bool {
if listeners, ok := valueToEventListeners[View, Frame](value); ok { if listeners, ok := valueToOneArgEventListeners[View, Frame](value); ok {
if len(listeners) == 0 { if len(listeners) == 0 {
properties.setRaw(tag, nil) properties.setRaw(tag, nil)
} else { } else {
@ -85,5 +85,5 @@ func GetViewFrame(view View, subviewID ...string) Frame {
// GetResizeListeners returns the list of "resize-event" listeners. If there are no listeners then the empty list is returned // GetResizeListeners returns the list of "resize-event" listeners. If there are no listeners then the empty list is returned
// If the second argument (subviewID) is not specified or it is "" then the listeners list of the first argument (view) is returned // If the second argument (subviewID) is not specified or it is "" then the listeners list of the first argument (view) is returned
func GetResizeListeners(view View, subviewID ...string) []func(View, Frame) { func GetResizeListeners(view View, subviewID ...string) []func(View, Frame) {
return getEventListeners[View, Frame](view, subviewID, ResizeEvent) return getOneArgEventListeners[View, Frame](view, subviewID, ResizeEvent)
} }

View File

@ -54,7 +54,7 @@ func GetViewScroll(view View, subviewID ...string) Frame {
// GetScrollListeners returns the list of "scroll-event" listeners. If there are no listeners then the empty list is returned // GetScrollListeners returns the list of "scroll-event" listeners. If there are no listeners then the empty list is returned
// If the second argument (subviewID) is not specified or it is "" then the listeners list of the first argument (view) is returned // If the second argument (subviewID) is not specified or it is "" then the listeners list of the first argument (view) is returned
func GetScrollListeners(view View, subviewID ...string) []func(View, Frame) { func GetScrollListeners(view View, subviewID ...string) []func(View, Frame) {
return getEventListeners[View, Frame](view, subviewID, ResizeEvent) return getOneArgEventListeners[View, Frame](view, subviewID, ResizeEvent)
} }
// ScrollTo scrolls the view's content to the given position. // ScrollTo scrolls the view's content to the given position.

View File

@ -81,9 +81,10 @@ func (layout *stackLayoutData) init(session Session) {
layout.tag = "StackLayout" layout.tag = "StackLayout"
layout.systemClass = "ruiStackLayout" layout.systemClass = "ruiStackLayout"
layout.properties[TransitionEndEvent] = []func(View, string){layout.pushFinished, layout.popFinished} layout.properties[TransitionEndEvent] = []func(View, string){layout.pushFinished, layout.popFinished}
layout.getFunc = layout.get layout.get = layout.getFunc
layout.set = layout.setFunc layout.set = layout.setFunc
layout.remove = layout.removeFunc layout.remove = layout.removeFunc
layout.changed = layout.propertyChanged
} }
func (layout *stackLayoutData) pushFinished(view View, tag string) { func (layout *stackLayoutData) pushFinished(view View, tag string) {
@ -121,14 +122,14 @@ func (layout *stackLayoutData) popFinished(view View, tag string) {
} }
} }
func (layout *stackLayoutData) setFunc(view View, tag PropertyName, value any) []PropertyName { func (layout *stackLayoutData) setFunc(tag PropertyName, value any) []PropertyName {
switch tag { switch tag {
case TransitionEndEvent: case TransitionEndEvent:
listeners, ok := valueToEventListeners[View, string](value) listeners, ok := valueToOneArgEventListeners[View, string](value)
if ok && listeners != nil { if ok && listeners != nil {
listeners = append(listeners, layout.pushFinished) listeners = append(listeners, layout.pushFinished)
listeners = append(listeners, layout.popFinished) listeners = append(listeners, layout.popFinished)
view.setRaw(TransitionEndEvent, listeners) layout.setRaw(TransitionEndEvent, listeners)
return []PropertyName{tag} return []PropertyName{tag}
} }
return nil return nil
@ -170,42 +171,42 @@ func (layout *stackLayoutData) setFunc(view View, tag PropertyName, value any) [
layout.peek = newCurrent layout.peek = newCurrent
return []PropertyName{tag} return []PropertyName{tag}
} }
return layout.viewsContainerData.setFunc(view, tag, value) return layout.viewsContainerData.setFunc(tag, value)
} }
func (layout *stackLayoutData) propertyChanged(view View, tag PropertyName) { func (layout *stackLayoutData) propertyChanged(tag PropertyName) {
switch tag { switch tag {
case Current: case Current:
if layout.prevPeek != layout.peek { if layout.prevPeek != layout.peek {
if layout.prevPeek < len(layout.views) { if layout.prevPeek < len(layout.views) {
layout.Session().updateCSSProperty(layout.htmlID()+"page"+strconv.Itoa(layout.prevPeek), "visibility", "hidden") layout.Session().updateCSSProperty(layout.htmlID()+"page"+strconv.Itoa(layout.prevPeek), "visibility", "hidden")
} }
layout.Session().updateCSSProperty(layout.htmlID()+"page"+strconv.Itoa(layout.prevPeek), "visibility", "visible") layout.Session().updateCSSProperty(layout.htmlID()+"page"+strconv.Itoa(layout.peek), "visibility", "visible")
layout.prevPeek = layout.peek layout.prevPeek = layout.peek
} }
default: default:
viewsContainerPropertyChanged(view, tag) layout.viewsContainerData.propertyChanged(tag)
} }
} }
func (layout *stackLayoutData) removeFunc(view View, tag PropertyName) []PropertyName { func (layout *stackLayoutData) removeFunc(tag PropertyName) []PropertyName {
switch tag { switch tag {
case TransitionEndEvent: case TransitionEndEvent:
view.setRaw(TransitionEndEvent, []func(View, string){layout.pushFinished, layout.popFinished}) layout.setRaw(TransitionEndEvent, []func(View, string){layout.pushFinished, layout.popFinished})
return []PropertyName{tag} return []PropertyName{tag}
case Current: case Current:
view.setRaw(Current, 0) layout.setRaw(Current, 0)
return []PropertyName{tag} return []PropertyName{tag}
} }
return layout.viewsContainerData.removeFunc(view, tag) return layout.viewsContainerData.removeFunc(tag)
} }
func (layout *stackLayoutData) get(view View, tag PropertyName) any { func (layout *stackLayoutData) getFunc(tag PropertyName) any {
if tag == Current { if tag == Current {
return layout.peek return layout.peek
} }
return layout.viewsContainerData.get(view, tag) return layout.viewsContainerData.getFunc(tag)
} }
func (layout *stackLayoutData) Peek() View { func (layout *stackLayoutData) Peek() View {

View File

@ -34,8 +34,8 @@ func (imageView *svgImageViewData) init(session Session) {
imageView.tag = "SvgImageView" imageView.tag = "SvgImageView"
imageView.systemClass = "ruiSvgImageView" imageView.systemClass = "ruiSvgImageView"
imageView.normalize = normalizeSvgImageViewTag imageView.normalize = normalizeSvgImageViewTag
imageView.set = svgImageViewSet imageView.set = imageView.setFunc
imageView.changed = svgImageViewPropertyChanged imageView.changed = imageView.propertyChanged
} }
@ -54,28 +54,28 @@ func normalizeSvgImageViewTag(tag PropertyName) PropertyName {
return tag return tag
} }
func svgImageViewSet(view View, tag PropertyName, value any) []PropertyName { func (imageView *svgImageViewData) setFunc(tag PropertyName, value any) []PropertyName {
switch tag { switch tag {
case Content: case Content:
if text, ok := value.(string); ok { if text, ok := value.(string); ok {
view.setRaw(Content, text) imageView.setRaw(Content, text)
return []PropertyName{tag} return []PropertyName{tag}
} }
notCompatibleType(Source, value) notCompatibleType(Source, value)
return nil return nil
default: default:
return viewSet(view, tag, value) return imageView.viewData.setFunc(tag, value)
} }
} }
func svgImageViewPropertyChanged(view View, tag PropertyName) { func (imageView *svgImageViewData) propertyChanged(tag PropertyName) {
switch tag { switch tag {
case Content: case Content:
updateInnerHTML(view.htmlID(), view.Session()) updateInnerHTML(imageView.htmlID(), imageView.Session())
default: default:
viewPropertyChanged(view, tag) imageView.viewData.propertyChanged(tag)
} }
} }

View File

@ -593,8 +593,8 @@ func (table *tableViewData) init(session Session) {
table.current.Column = -1 table.current.Column = -1
*/ */
table.normalize = normalizeTableViewTag table.normalize = normalizeTableViewTag
table.set = tableViewSet table.set = table.setFunc
table.changed = tableViewPropertyChanged table.changed = table.propertyChanged
} }
func normalizeTableViewTag(tag PropertyName) PropertyName { func normalizeTableViewTag(tag PropertyName) PropertyName {
@ -618,7 +618,7 @@ func (table *tableViewData) Focusable() bool {
return GetTableSelectionMode(table) != NoneSelection return GetTableSelectionMode(table) != NoneSelection
} }
func tableViewSet(view View, tag PropertyName, value any) []PropertyName { func (table *tableViewData) setFunc(tag PropertyName, value any) []PropertyName {
setLineStyle := func() []PropertyName { setLineStyle := func() []PropertyName {
params := []Params{} params := []Params{}
@ -637,9 +637,9 @@ func tableViewSet(view View, tag PropertyName, value any) []PropertyName {
if len(params) > 0 { if len(params) > 0 {
style := new(simpleTableLineStyle) style := new(simpleTableLineStyle)
style.params = params style.params = params
view.setRaw(tag, style) table.setRaw(tag, style)
} else if view.getRaw(tag) != nil { } else if table.getRaw(tag) != nil {
view.setRaw(tag, nil) table.setRaw(tag, nil)
} else { } else {
return []PropertyName{} return []PropertyName{}
} }
@ -650,13 +650,13 @@ func tableViewSet(view View, tag PropertyName, value any) []PropertyName {
case Content: case Content:
switch val := value.(type) { switch val := value.(type) {
case TableAdapter: case TableAdapter:
view.setRaw(Content, value) table.setRaw(Content, value)
case [][]any: case [][]any:
view.setRaw(Content, NewSimpleTableAdapter(val)) table.setRaw(Content, NewSimpleTableAdapter(val))
case [][]string: case [][]string:
view.setRaw(Content, NewTextTableAdapter(val)) table.setRaw(Content, NewTextTableAdapter(val))
default: default:
notCompatibleType(tag, value) notCompatibleType(tag, value)
@ -665,14 +665,14 @@ func tableViewSet(view View, tag PropertyName, value any) []PropertyName {
return []PropertyName{tag} return []PropertyName{tag}
case TableCellClickedEvent, TableCellSelectedEvent: case TableCellClickedEvent, TableCellSelectedEvent:
return setEventWithOldListener[TableView, int](view, tag, value) return setTwoArgEventListener[TableView, int](table, tag, value)
case TableRowClickedEvent, TableRowSelectedEvent: case TableRowClickedEvent, TableRowSelectedEvent:
return setViewEventListener[TableView, int](view, tag, value) return setOneArgEventListener[TableView, int](table, tag, value)
case CellStyle: case CellStyle:
if style, ok := value.(TableCellStyle); ok { if style, ok := value.(TableCellStyle); ok {
view.setRaw(tag, style) table.setRaw(tag, style)
return []PropertyName{tag} return []PropertyName{tag}
} }
notCompatibleType(tag, value) notCompatibleType(tag, value)
@ -680,14 +680,14 @@ func tableViewSet(view View, tag PropertyName, value any) []PropertyName {
case RowStyle: case RowStyle:
if style, ok := value.(TableRowStyle); ok { if style, ok := value.(TableRowStyle); ok {
view.setRaw(tag, style) table.setRaw(tag, style)
return []PropertyName{tag} return []PropertyName{tag}
} }
return setLineStyle() return setLineStyle()
case ColumnStyle: case ColumnStyle:
if style, ok := value.(TableColumnStyle); ok { if style, ok := value.(TableColumnStyle); ok {
view.setRaw(tag, style) table.setRaw(tag, style)
return []PropertyName{tag} return []PropertyName{tag}
} }
return setLineStyle() return setLineStyle()
@ -696,9 +696,9 @@ func tableViewSet(view View, tag PropertyName, value any) []PropertyName {
switch value := value.(type) { switch value := value.(type) {
case string: case string:
if isConstantName(value) { if isConstantName(value) {
view.setRaw(tag, value) table.setRaw(tag, value)
} else if n, err := strconv.Atoi(value); err == nil { } else if n, err := strconv.Atoi(value); err == nil {
view.setRaw(tag, n) table.setRaw(tag, n)
} else { } else {
ErrorLog(err.Error()) ErrorLog(err.Error())
notCompatibleType(tag, value) notCompatibleType(tag, value)
@ -707,7 +707,7 @@ func tableViewSet(view View, tag PropertyName, value any) []PropertyName {
default: default:
if n, ok := isInt(value); ok { if n, ok := isInt(value); ok {
view.setRaw(tag, n) table.setRaw(tag, n)
} else { } else {
notCompatibleType(tag, value) notCompatibleType(tag, value)
return nil return nil
@ -718,13 +718,13 @@ func tableViewSet(view View, tag PropertyName, value any) []PropertyName {
case HeadStyle, FootStyle: case HeadStyle, FootStyle:
switch value := value.(type) { switch value := value.(type) {
case string: case string:
view.setRaw(tag, value) table.setRaw(tag, value)
case Params: case Params:
if len(value) > 0 { if len(value) > 0 {
view.setRaw(tag, value) table.setRaw(tag, value)
} else if view.getRaw(tag) != nil { } else if table.getRaw(tag) != nil {
view.setRaw(tag, nil) table.setRaw(tag, nil)
} else { } else {
return []PropertyName{} return []PropertyName{}
} }
@ -736,15 +736,15 @@ func tableViewSet(view View, tag PropertyName, value any) []PropertyName {
params[PropertyName(prop.Tag())] = prop.Text() params[PropertyName(prop.Tag())] = prop.Text()
} }
} }
return tableViewSet(view, tag, params) return table.setFunc(tag, params)
case DataNode: case DataNode:
switch value.Type() { switch value.Type() {
case ObjectNode: case ObjectNode:
return tableViewSet(view, tag, value.Object()) return table.setFunc(tag, value.Object())
case TextNode: case TextNode:
view.setRaw(tag, value.Text()) table.setRaw(tag, value.Text())
default: default:
notCompatibleType(tag, value) notCompatibleType(tag, value)
@ -760,10 +760,10 @@ func tableViewSet(view View, tag PropertyName, value any) []PropertyName {
case AllowSelection: case AllowSelection:
switch value.(type) { switch value.(type) {
case TableAllowCellSelection: case TableAllowCellSelection:
view.setRaw(tag, value) table.setRaw(tag, value)
case TableAllowRowSelection: case TableAllowRowSelection:
view.setRaw(tag, value) table.setRaw(tag, value)
default: default:
notCompatibleType(tag, value) notCompatibleType(tag, value)
@ -819,17 +819,17 @@ func tableViewSet(view View, tag PropertyName, value any) []PropertyName {
return nil return nil
} }
} }
view.setRaw(Current, current) table.setRaw(Current, current)
return []PropertyName{tag} return []PropertyName{tag}
} }
return viewSet(view, tag, value) return table.viewData.setFunc(tag, value)
} }
func tableViewPropertyChanged(view View, tag PropertyName) { func (table *tableViewData) propertyChanged(tag PropertyName) {
htmlID := view.htmlID() htmlID := table.htmlID()
session := view.Session() session := table.Session()
switch tag { switch tag {
case Content, TableVerticalAlign, RowStyle, ColumnStyle, CellStyle, CellPadding, case Content, TableVerticalAlign, RowStyle, ColumnStyle, CellStyle, CellPadding,
@ -837,20 +837,20 @@ func tableViewPropertyChanged(view View, tag PropertyName) {
CellPaddingTop, CellPaddingRight, CellPaddingBottom, CellPaddingLeft, CellPaddingTop, CellPaddingRight, CellPaddingBottom, CellPaddingLeft,
TableCellClickedEvent, TableCellSelectedEvent, TableRowClickedEvent, TableCellClickedEvent, TableCellSelectedEvent, TableRowClickedEvent,
TableRowSelectedEvent, AllowSelection, AccentColor: TableRowSelectedEvent, AllowSelection, AccentColor:
ReloadTableViewData(view) ReloadTableViewData(table)
case Current: case Current:
switch GetTableSelectionMode(view) { switch GetTableSelectionMode(table) {
case CellSelection: case CellSelection:
current := tableViewCurrent(view) current := tableViewCurrent(table)
session.callFunc("setTableCellCursorByID", htmlID, current.Row, current.Column) session.callFunc("setTableCellCursorByID", htmlID, current.Row, current.Column)
case RowSelection: case RowSelection:
session.callFunc("setTableRowCursorByID", htmlID, tableViewCurrent(view).Row) session.callFunc("setTableRowCursorByID", htmlID, tableViewCurrent(table).Row)
} }
case Gap: case Gap:
gap, ok := sizeProperty(view, Gap, session) gap, ok := sizeProperty(table, Gap, session)
if !ok || gap.Type == Auto || gap.Value <= 0 { if !ok || gap.Type == Auto || gap.Value <= 0 {
session.updateCSSProperty(htmlID, "border-spacing", "0") session.updateCSSProperty(htmlID, "border-spacing", "0")
session.updateCSSProperty(htmlID, "border-collapse", "collapse") session.updateCSSProperty(htmlID, "border-collapse", "collapse")
@ -860,43 +860,43 @@ func tableViewPropertyChanged(view View, tag PropertyName) {
} }
case SelectionMode: case SelectionMode:
switch GetTableSelectionMode(view) { switch GetTableSelectionMode(table) {
case CellSelection: case CellSelection:
tabIndex, _ := intProperty(view, TabIndex, session, 0) tabIndex, _ := intProperty(table, TabIndex, session, 0)
session.updateProperty(htmlID, "tabindex", tabIndex) session.updateProperty(htmlID, "tabindex", tabIndex)
session.updateProperty(htmlID, "onfocus", "tableViewFocusEvent(this, event)") session.updateProperty(htmlID, "onfocus", "tableViewFocusEvent(this, event)")
session.updateProperty(htmlID, "onblur", "tableViewBlurEvent(this, event)") session.updateProperty(htmlID, "onblur", "tableViewBlurEvent(this, event)")
session.updateProperty(htmlID, "data-selection", "cell") session.updateProperty(htmlID, "data-selection", "cell")
session.updateProperty(htmlID, "data-focusitemstyle", tableViewCurrentStyle(view)) session.updateProperty(htmlID, "data-focusitemstyle", tableViewCurrentStyle(table))
session.updateProperty(htmlID, "data-bluritemstyle", tableViewCurrentInactiveStyle(view)) session.updateProperty(htmlID, "data-bluritemstyle", tableViewCurrentInactiveStyle(table))
current := tableViewCurrent(view) current := tableViewCurrent(table)
if current.Row >= 0 && current.Column >= 0 { if current.Row >= 0 && current.Column >= 0 {
session.updateProperty(htmlID, "data-current", tableViewCellID(view, current.Row, current.Column)) session.updateProperty(htmlID, "data-current", tableViewCellID(table, current.Row, current.Column))
} else { } else {
session.removeProperty(htmlID, "data-current") session.removeProperty(htmlID, "data-current")
} }
session.updateProperty(htmlID, "onkeydown", "tableViewCellKeyDownEvent(this, event)") session.updateProperty(htmlID, "onkeydown", "tableViewCellKeyDownEvent(this, event)")
case RowSelection: case RowSelection:
tabIndex, _ := intProperty(view, TabIndex, session, 0) tabIndex, _ := intProperty(table, TabIndex, session, 0)
session.updateProperty(htmlID, "tabindex", tabIndex) session.updateProperty(htmlID, "tabindex", tabIndex)
session.updateProperty(htmlID, "onfocus", "tableViewFocusEvent(this, event)") session.updateProperty(htmlID, "onfocus", "tableViewFocusEvent(this, event)")
session.updateProperty(htmlID, "onblur", "tableViewBlurEvent(this, event)") session.updateProperty(htmlID, "onblur", "tableViewBlurEvent(this, event)")
session.updateProperty(htmlID, "data-selection", "row") session.updateProperty(htmlID, "data-selection", "row")
session.updateProperty(htmlID, "data-focusitemstyle", tableViewCurrentStyle(view)) session.updateProperty(htmlID, "data-focusitemstyle", tableViewCurrentStyle(table))
session.updateProperty(htmlID, "data-bluritemstyle", tableViewCurrentInactiveStyle(view)) session.updateProperty(htmlID, "data-bluritemstyle", tableViewCurrentInactiveStyle(table))
current := tableViewCurrent(view) current := tableViewCurrent(table)
if current.Row >= 0 { if current.Row >= 0 {
session.updateProperty(htmlID, "data-current", tableViewRowID(view, current.Row)) session.updateProperty(htmlID, "data-current", tableViewRowID(table, current.Row))
} else { } else {
session.removeProperty(htmlID, "data-current") session.removeProperty(htmlID, "data-current")
} }
session.updateProperty(htmlID, "onkeydown", "tableViewRowKeyDownEvent(this, event)") session.updateProperty(htmlID, "onkeydown", "tableViewRowKeyDownEvent(this, event)")
default: // NoneSelection default: // NoneSelection
if tabIndex, ok := intProperty(view, TabIndex, session, -1); !ok || tabIndex < 0 { if tabIndex, ok := intProperty(table, TabIndex, session, -1); !ok || tabIndex < 0 {
session.removeProperty(htmlID, "tabindex") session.removeProperty(htmlID, "tabindex")
} }
@ -907,7 +907,7 @@ func tableViewPropertyChanged(view View, tag PropertyName) {
updateInnerHTML(htmlID, session) updateInnerHTML(htmlID, session)
default: default:
viewPropertyChanged(view, tag) table.viewData.propertyChanged(tag)
} }
} }

View File

@ -152,28 +152,28 @@ func GetTableCurrent(view View, subviewID ...string) CellIndex {
// If there are no listeners then the empty list is returned. // 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTableCellClickedListeners(view View, subviewID ...string) []func(TableView, int, int) { func GetTableCellClickedListeners(view View, subviewID ...string) []func(TableView, int, int) {
return getEventWithOldListeners[TableView, int](view, subviewID, TableCellClickedEvent) return getTwoArgEventListeners[TableView, int](view, subviewID, TableCellClickedEvent)
} }
// GetTableCellSelectedListeners returns listeners of event which occurs when a table cell becomes selected. // GetTableCellSelectedListeners returns listeners of event which occurs when a table cell becomes selected.
// If there are no listeners then the empty list is returned. // 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTableCellSelectedListeners(view View, subviewID ...string) []func(TableView, int, int) { func GetTableCellSelectedListeners(view View, subviewID ...string) []func(TableView, int, int) {
return getEventWithOldListeners[TableView, int](view, subviewID, TableCellSelectedEvent) return getTwoArgEventListeners[TableView, int](view, subviewID, TableCellSelectedEvent)
} }
// GetTableRowClickedListeners returns listeners of event which occurs when the user clicks on a table row. // GetTableRowClickedListeners returns listeners of event which occurs when the user clicks on a table row.
// If there are no listeners then the empty list is returned. // 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTableRowClickedListeners(view View, subviewID ...string) []func(TableView, int) { func GetTableRowClickedListeners(view View, subviewID ...string) []func(TableView, int) {
return getEventListeners[TableView, int](view, subviewID, TableRowClickedEvent) return getOneArgEventListeners[TableView, int](view, subviewID, TableRowClickedEvent)
} }
// GetTableRowSelectedListeners returns listeners of event which occurs when a table row becomes selected. // GetTableRowSelectedListeners returns listeners of event which occurs when a table row becomes selected.
// If there are no listeners then the empty list is returned. // 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTableRowSelectedListeners(view View, subviewID ...string) []func(TableView, int) { func GetTableRowSelectedListeners(view View, subviewID ...string) []func(TableView, int) {
return getEventListeners[TableView, int](view, subviewID, TableRowSelectedEvent) return getOneArgEventListeners[TableView, int](view, subviewID, TableRowSelectedEvent)
} }
// ReloadTableViewData updates TableView // ReloadTableViewData updates TableView

View File

@ -168,69 +168,69 @@ func tabsLayoutCurrent(view View, defaultValue int) int {
return result return result
} }
func (tabsLayout *tabsLayoutData) setFunc(view View, tag PropertyName, value any) []PropertyName { func (tabsLayout *tabsLayoutData) setFunc(tag PropertyName, value any) []PropertyName {
switch tag { switch tag {
case CurrentTabChangedEvent: case CurrentTabChangedEvent:
return setEventWithOldListener[TabsLayout, int](view, tag, value) return setTwoArgEventListener[TabsLayout, int](tabsLayout, tag, value)
case TabCloseEvent: case TabCloseEvent:
return setViewEventListener[TabsLayout, int](view, tag, value) return setOneArgEventListener[TabsLayout, int](tabsLayout, tag, value)
case Current: case Current:
view.setRaw("old-current", tabsLayoutCurrent(view, -1)) tabsLayout.setRaw("old-current", tabsLayoutCurrent(tabsLayout, -1))
if current, ok := value.(int); ok && current < 0 { if current, ok := value.(int); ok && current < 0 {
view.setRaw(Current, nil) tabsLayout.setRaw(Current, nil)
return []PropertyName{tag} return []PropertyName{tag}
} }
return setIntProperty(view, Current, value) return setIntProperty(tabsLayout, Current, value)
case TabStyle, CurrentTabStyle, TabBarStyle: case TabStyle, CurrentTabStyle, TabBarStyle:
if text, ok := value.(string); ok { if text, ok := value.(string); ok {
return setStringPropertyValue(view, tag, text) return setStringPropertyValue(tabsLayout, tag, text)
} }
notCompatibleType(tag, value) notCompatibleType(tag, value)
return nil return nil
} }
return tabsLayout.viewsContainerData.setFunc(tabsLayout, tag, value) return tabsLayout.viewsContainerData.setFunc(tag, value)
} }
func (tabsLayout *tabsLayoutData) propertyChanged(view View, tag PropertyName) { func (tabsLayout *tabsLayoutData) propertyChanged(tag PropertyName) {
switch tag { switch tag {
case Current: case Current:
session := view.Session() session := tabsLayout.Session()
current := GetCurrent(view) current := GetCurrent(tabsLayout)
session.callFunc("activateTab", view.htmlID(), current) session.callFunc("activateTab", tabsLayout.htmlID(), current)
if listeners := getEventWithOldListeners[TabsLayout, int](view, nil, CurrentTabChangedEvent); len(listeners) > 0 { if listeners := getTwoArgEventListeners[TabsLayout, int](tabsLayout, nil, CurrentTabChangedEvent); len(listeners) > 0 {
oldCurrent, _ := intProperty(view, "old-current", session, -1) oldCurrent, _ := intProperty(tabsLayout, "old-current", session, -1)
for _, listener := range listeners { for _, listener := range listeners {
listener(tabsLayout, current, oldCurrent) listener(tabsLayout, current, oldCurrent)
} }
} }
case Tabs: case Tabs:
htmlID := view.htmlID() htmlID := tabsLayout.htmlID()
session := view.Session() session := tabsLayout.Session()
session.updateProperty(htmlID, inactiveTabStyle, tabsLayoutInactiveTabStyle(view)) session.updateProperty(htmlID, inactiveTabStyle, tabsLayoutInactiveTabStyle(tabsLayout))
session.updateProperty(htmlID, activeTabStyle, tabsLayoutActiveTabStyle(view)) session.updateProperty(htmlID, activeTabStyle, tabsLayoutActiveTabStyle(tabsLayout))
updateCSSStyle(htmlID, session) updateCSSStyle(htmlID, session)
updateInnerHTML(htmlID, session) updateInnerHTML(htmlID, session)
case TabStyle, CurrentTabStyle, TabBarStyle: case TabStyle, CurrentTabStyle, TabBarStyle:
htmlID := view.htmlID() htmlID := tabsLayout.htmlID()
session := view.Session() session := tabsLayout.Session()
session.updateProperty(htmlID, inactiveTabStyle, tabsLayoutInactiveTabStyle(view)) session.updateProperty(htmlID, inactiveTabStyle, tabsLayoutInactiveTabStyle(tabsLayout))
session.updateProperty(htmlID, activeTabStyle, tabsLayoutActiveTabStyle(view)) session.updateProperty(htmlID, activeTabStyle, tabsLayoutActiveTabStyle(tabsLayout))
updateInnerHTML(htmlID, session) updateInnerHTML(htmlID, session)
case TabCloseButton: case TabCloseButton:
updateInnerHTML(view.htmlID(), view.Session()) updateInnerHTML(tabsLayout.htmlID(), tabsLayout.Session())
default: default:
viewsContainerPropertyChanged(view, tag) tabsLayout.viewsContainerData.propertyChanged(tag)
} }
} }
@ -499,7 +499,7 @@ func (tabsLayout *tabsLayoutData) ListItem(index int, session Session) View {
Column: 2, Column: 2,
Content: "✕", Content: "✕",
ClickEvent: func() { ClickEvent: func() {
for _, listener := range getEventListeners[TabsLayout, int](tabsLayout, nil, TabCloseEvent) { for _, listener := range getOneArgEventListeners[TabsLayout, int](tabsLayout, nil, TabCloseEvent) {
listener(tabsLayout, index) listener(tabsLayout, index)
} }
}, },
@ -565,7 +565,7 @@ func (tabsLayout *tabsLayoutData) Insert(view View, index int) {
if view != nil { if view != nil {
if current := GetCurrent(tabsLayout); current >= index { if current := GetCurrent(tabsLayout); current >= index {
tabsLayout.setRaw(Current, current+1) tabsLayout.setRaw(Current, current+1)
defer tabsLayout.currentChanged() defer tabsLayout.currentChanged(current+1, current)
} }
tabsLayout.viewsContainerData.Insert(view, index) tabsLayout.viewsContainerData.Insert(view, index)
view.SetChangeListener(Title, tabsLayout.updateTitle) view.SetChangeListener(Title, tabsLayout.updateTitle)
@ -574,7 +574,10 @@ func (tabsLayout *tabsLayoutData) Insert(view View, index int) {
} }
} }
func (tabsLayout *tabsLayoutData) currentChanged() { func (tabsLayout *tabsLayoutData) currentChanged(newCurrent, oldCurrent int) {
for _, listener := range getTwoArgEventListeners[TabsLayout, int](tabsLayout, nil, CurrentTabChangedEvent) {
listener(tabsLayout, newCurrent, oldCurrent)
}
if listener, ok := tabsLayout.changeListener[Current]; ok { if listener, ok := tabsLayout.changeListener[Current]; ok {
listener(tabsLayout, Current) listener(tabsLayout, Current)
} }
@ -600,7 +603,7 @@ func (tabsLayout *tabsLayoutData) RemoveView(index int) View {
if newCurrent != oldCurrent { if newCurrent != oldCurrent {
tabsLayout.setRaw(Current, newCurrent) tabsLayout.setRaw(Current, newCurrent)
tabsLayout.currentChanged() tabsLayout.currentChanged(newCurrent, oldCurrent)
} }
} }
return nil return nil
@ -871,10 +874,8 @@ func (tabsLayout *tabsLayoutData) handleCommand(self View, command PropertyName,
current := GetCurrent(tabsLayout) current := GetCurrent(tabsLayout)
if current != number { if current != number {
tabsLayout.setRaw(Current, number) tabsLayout.setRaw(Current, number)
for _, listener := range getEventWithOldListeners[TabsLayout, int](tabsLayout, nil, CurrentTabChangedEvent) {
listener(tabsLayout, number, current) tabsLayout.currentChanged(number, current)
}
tabsLayout.currentChanged()
} }
} }
} }
@ -883,7 +884,7 @@ func (tabsLayout *tabsLayoutData) handleCommand(self View, command PropertyName,
case "tabCloseClick": case "tabCloseClick":
if numberText, ok := data.PropertyValue("number"); ok { if numberText, ok := data.PropertyValue("number"); ok {
if number, err := strconv.Atoi(numberText); err == nil { if number, err := strconv.Atoi(numberText); err == nil {
for _, listener := range getEventListeners[TabsLayout, int](tabsLayout, nil, TabCloseEvent) { for _, listener := range getOneArgEventListeners[TabsLayout, int](tabsLayout, nil, TabCloseEvent) {
listener(tabsLayout, number) listener(tabsLayout, number)
} }
} }

View File

@ -30,63 +30,63 @@ func newTextView(session Session) View {
func (textView *textViewData) init(session Session) { func (textView *textViewData) init(session Session) {
textView.viewData.init(session) textView.viewData.init(session)
textView.tag = "TextView" textView.tag = "TextView"
textView.set = textViewSet textView.set = textView.setFunc
textView.changed = textViewPropertyChanged textView.changed = textView.propertyChanged
} }
func textViewPropertyChanged(view View, tag PropertyName) { func (textView *textViewData) propertyChanged(tag PropertyName) {
switch tag { switch tag {
case Text: case Text:
updateInnerHTML(view.htmlID(), view.Session()) updateInnerHTML(textView.htmlID(), textView.Session())
case TextOverflow: case TextOverflow:
session := view.Session() session := textView.Session()
if n, ok := enumProperty(view, TextOverflow, session, 0); ok { if n, ok := enumProperty(textView, TextOverflow, session, 0); ok {
values := enumProperties[TextOverflow].cssValues values := enumProperties[TextOverflow].cssValues
if n >= 0 && n < len(values) { if n >= 0 && n < len(values) {
session.updateCSSProperty(view.htmlID(), string(TextOverflow), values[n]) session.updateCSSProperty(textView.htmlID(), string(TextOverflow), values[n])
return return
} }
} }
session.updateCSSProperty(view.htmlID(), string(TextOverflow), "") session.updateCSSProperty(textView.htmlID(), string(TextOverflow), "")
case NotTranslate: case NotTranslate:
updateInnerHTML(view.htmlID(), view.Session()) updateInnerHTML(textView.htmlID(), textView.Session())
default: default:
viewPropertyChanged(view, tag) textView.viewData.propertyChanged(tag)
} }
} }
func textViewSet(view View, tag PropertyName, value any) []PropertyName { func (textView *textViewData) setFunc(tag PropertyName, value any) []PropertyName {
switch tag { switch tag {
case Text: case Text:
switch value := value.(type) { switch value := value.(type) {
case string: case string:
view.setRaw(Text, value) textView.setRaw(Text, value)
case fmt.Stringer: case fmt.Stringer:
view.setRaw(Text, value.String()) textView.setRaw(Text, value.String())
case float32: case float32:
view.setRaw(Text, fmt.Sprintf("%g", float64(value))) textView.setRaw(Text, fmt.Sprintf("%g", float64(value)))
case float64: case float64:
view.setRaw(Text, fmt.Sprintf("%g", value)) textView.setRaw(Text, fmt.Sprintf("%g", value))
case []rune: case []rune:
view.setRaw(Text, string(value)) textView.setRaw(Text, string(value))
case bool: case bool:
if value { if value {
view.setRaw(Text, "true") textView.setRaw(Text, "true")
} else { } else {
view.setRaw(Text, "false") textView.setRaw(Text, "false")
} }
default: default:
if n, ok := isInt(value); ok { if n, ok := isInt(value); ok {
view.setRaw(Text, fmt.Sprintf("%d", n)) textView.setRaw(Text, fmt.Sprintf("%d", n))
} else { } else {
notCompatibleType(tag, value) notCompatibleType(tag, value)
return nil return nil
@ -95,7 +95,7 @@ func textViewSet(view View, tag PropertyName, value any) []PropertyName {
return []PropertyName{Text} return []PropertyName{Text}
} }
return viewSet(view, tag, value) return textView.viewData.setFunc(tag, value)
} }
func (textView *textViewData) htmlSubviews(self View, buffer *strings.Builder) { func (textView *textViewData) htmlSubviews(self View, buffer *strings.Builder) {

View File

@ -120,8 +120,8 @@ func (picker *timePickerData) init(session Session) {
picker.tag = "TimePicker" picker.tag = "TimePicker"
picker.hasHtmlDisabled = true picker.hasHtmlDisabled = true
picker.normalize = normalizeTimePickerTag picker.normalize = normalizeTimePickerTag
picker.set = timePickerSet picker.set = picker.setFunc
picker.changed = timePickerPropertyChanged picker.changed = picker.propertyChanged
} }
func (picker *timePickerData) Focusable() bool { func (picker *timePickerData) Focusable() bool {
@ -167,22 +167,22 @@ func stringToTime(value string) (time.Time, bool) {
return result, true return result, true
} }
func timePickerSet(view View, tag PropertyName, value any) []PropertyName { func (picker *timePickerData) setFunc(tag PropertyName, value any) []PropertyName {
setTimeValue := func(tag PropertyName) []PropertyName { setTimeValue := func(tag PropertyName) []PropertyName {
switch value := value.(type) { switch value := value.(type) {
case time.Time: case time.Time:
view.setRaw(tag, value) picker.setRaw(tag, value)
return []PropertyName{tag} return []PropertyName{tag}
case string: case string:
if isConstantName(value) { if isConstantName(value) {
view.setRaw(tag, value) picker.setRaw(tag, value)
return []PropertyName{tag} return []PropertyName{tag}
} }
if time, ok := stringToTime(value); ok { if time, ok := stringToTime(value); ok {
view.setRaw(tag, time) picker.setRaw(tag, time)
return []PropertyName{tag} return []PropertyName{tag}
} }
} }
@ -199,67 +199,67 @@ func timePickerSet(view View, tag PropertyName, value any) []PropertyName {
return setTimeValue(TimePickerMax) return setTimeValue(TimePickerMax)
case TimePickerStep: case TimePickerStep:
return setIntProperty(view, TimePickerStep, value) return setIntProperty(picker, TimePickerStep, value)
case TimePickerValue: case TimePickerValue:
view.setRaw("old-time", GetTimePickerValue(view)) picker.setRaw("old-time", GetTimePickerValue(picker))
return setTimeValue(tag) return setTimeValue(tag)
case TimeChangedEvent: case TimeChangedEvent:
return setEventWithOldListener[TimePicker, time.Time](view, tag, value) return setTwoArgEventListener[TimePicker, time.Time](picker, tag, value)
case DataList: case DataList:
return setDataList(view, value, timeFormat) return setDataList(picker, value, timeFormat)
} }
return viewSet(view, tag, value) return picker.viewData.setFunc(tag, value)
} }
func timePickerPropertyChanged(view View, tag PropertyName) { func (picker *timePickerData) propertyChanged(tag PropertyName) {
session := view.Session() session := picker.Session()
switch tag { switch tag {
case TimePickerMin: case TimePickerMin:
if time, ok := GetTimePickerMin(view); ok { if time, ok := GetTimePickerMin(picker); ok {
session.updateProperty(view.htmlID(), "min", time.Format(timeFormat)) session.updateProperty(picker.htmlID(), "min", time.Format(timeFormat))
} else { } else {
session.removeProperty(view.htmlID(), "min") session.removeProperty(picker.htmlID(), "min")
} }
case TimePickerMax: case TimePickerMax:
if time, ok := GetTimePickerMax(view); ok { if time, ok := GetTimePickerMax(picker); ok {
session.updateProperty(view.htmlID(), "max", time.Format(timeFormat)) session.updateProperty(picker.htmlID(), "max", time.Format(timeFormat))
} else { } else {
session.removeProperty(view.htmlID(), "max") session.removeProperty(picker.htmlID(), "max")
} }
case TimePickerStep: case TimePickerStep:
if step := GetTimePickerStep(view); step > 0 { if step := GetTimePickerStep(picker); step > 0 {
session.updateProperty(view.htmlID(), "step", strconv.Itoa(step)) session.updateProperty(picker.htmlID(), "step", strconv.Itoa(step))
} else { } else {
session.removeProperty(view.htmlID(), "step") session.removeProperty(picker.htmlID(), "step")
} }
case TimePickerValue: case TimePickerValue:
value := GetTimePickerValue(view) value := GetTimePickerValue(picker)
session.callFunc("setInputValue", view.htmlID(), value.Format(timeFormat)) session.callFunc("setInputValue", picker.htmlID(), value.Format(timeFormat))
if listeners := GetTimeChangedListeners(view); len(listeners) > 0 { if listeners := GetTimeChangedListeners(picker); len(listeners) > 0 {
oldTime := time.Now() oldTime := time.Now()
if val := view.getRaw("old-time"); val != nil { if val := picker.getRaw("old-time"); val != nil {
if time, ok := val.(time.Time); ok { if time, ok := val.(time.Time); ok {
oldTime = time oldTime = time
} }
} }
for _, listener := range listeners { for _, listener := range listeners {
listener(view, value, oldTime) listener(picker, value, oldTime)
} }
} }
default: default:
viewPropertyChanged(view, tag) picker.viewData.propertyChanged(tag)
} }
} }
@ -420,5 +420,5 @@ func GetTimePickerValue(view View, subviewID ...string) time.Time {
// If there are no listeners then the empty list is returned // 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTimeChangedListeners(view View, subviewID ...string) []func(TimePicker, time.Time, time.Time) { func GetTimeChangedListeners(view View, subviewID ...string) []func(TimePicker, time.Time, time.Time) {
return getEventWithOldListeners[TimePicker, time.Time](view, subviewID, TimeChangedEvent) return getTwoArgEventListeners[TimePicker, time.Time](view, subviewID, TimeChangedEvent)
} }

View File

@ -144,7 +144,7 @@ type TouchEvent struct {
/* /*
func setTouchListener(properties Properties, tag PropertyName, value any) bool { func setTouchListener(properties Properties, tag PropertyName, value any) bool {
if listeners, ok := valueToEventListeners[View, TouchEvent](value); ok { if listeners, ok := valueToOneArgEventListeners[View, TouchEvent](value); ok {
if len(listeners) == 0 { if len(listeners) == 0 {
properties.setRaw(tag, nil) properties.setRaw(tag, nil)
} else { } else {
@ -215,7 +215,7 @@ func (event *TouchEvent) init(data DataObject) {
} }
func handleTouchEvents(view View, tag PropertyName, data DataObject) { func handleTouchEvents(view View, tag PropertyName, data DataObject) {
listeners := getEventListeners[View, TouchEvent](view, nil, tag) listeners := getOneArgEventListeners[View, TouchEvent](view, nil, tag)
if len(listeners) == 0 { if len(listeners) == 0 {
return return
} }
@ -231,23 +231,23 @@ func handleTouchEvents(view View, tag PropertyName, data DataObject) {
// GetTouchStartListeners returns the "touch-start" listener list. If there are no listeners then the empty list is returned. // GetTouchStartListeners returns the "touch-start" 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTouchStartListeners(view View, subviewID ...string) []func(View, TouchEvent) { func GetTouchStartListeners(view View, subviewID ...string) []func(View, TouchEvent) {
return getEventListeners[View, TouchEvent](view, subviewID, TouchStart) return getOneArgEventListeners[View, TouchEvent](view, subviewID, TouchStart)
} }
// GetTouchEndListeners returns the "touch-end" listener list. If there are no listeners then the empty list is returned. // GetTouchEndListeners returns the "touch-end" 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTouchEndListeners(view View, subviewID ...string) []func(View, TouchEvent) { func GetTouchEndListeners(view View, subviewID ...string) []func(View, TouchEvent) {
return getEventListeners[View, TouchEvent](view, subviewID, TouchEnd) return getOneArgEventListeners[View, TouchEvent](view, subviewID, TouchEnd)
} }
// GetTouchMoveListeners returns the "touch-move" listener list. If there are no listeners then the empty list is returned. // GetTouchMoveListeners returns the "touch-move" 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTouchMoveListeners(view View, subviewID ...string) []func(View, TouchEvent) { func GetTouchMoveListeners(view View, subviewID ...string) []func(View, TouchEvent) {
return getEventListeners[View, TouchEvent](view, subviewID, TouchMove) return getOneArgEventListeners[View, TouchEvent](view, subviewID, TouchMove)
} }
// GetTouchCancelListeners returns the "touch-cancel" listener list. If there are no listeners then the empty list is returned. // GetTouchCancelListeners returns the "touch-cancel" 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. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetTouchCancelListeners(view View, subviewID ...string) []func(View, TouchEvent) { func GetTouchCancelListeners(view View, subviewID ...string) []func(View, TouchEvent) {
return getEventListeners[View, TouchEvent](view, subviewID, TouchCancel) return getOneArgEventListeners[View, TouchEvent](view, subviewID, TouchCancel)
} }

View File

@ -61,22 +61,22 @@ func newVideoPlayer(session Session) View {
func (player *videoPlayerData) init(session Session) { func (player *videoPlayerData) init(session Session) {
player.mediaPlayerData.init(session) player.mediaPlayerData.init(session)
player.tag = "VideoPlayer" player.tag = "VideoPlayer"
player.changed = videoPlayerPropertyChanged player.changed = player.propertyChanged
} }
func (player *videoPlayerData) htmlTag() string { func (player *videoPlayerData) htmlTag() string {
return "video" return "video"
} }
func videoPlayerPropertyChanged(view View, tag PropertyName) { func (player *videoPlayerData) propertyChanged(tag PropertyName) {
session := view.Session() session := player.Session()
updateSize := func(cssTag string) { updateSize := func(cssTag string) {
if size, ok := floatTextProperty(view, tag, session, 0); ok { if size, ok := floatTextProperty(player, tag, session, 0); ok {
if size != "0" { if size != "0" {
session.updateProperty(view.htmlID(), cssTag, size) session.updateProperty(player.htmlID(), cssTag, size)
} else { } else {
session.removeProperty(view.htmlID(), cssTag) session.removeProperty(player.htmlID(), cssTag)
} }
} }
} }
@ -89,14 +89,14 @@ func videoPlayerPropertyChanged(view View, tag PropertyName) {
updateSize("height") updateSize("height")
case Poster: case Poster:
if url, ok := stringProperty(view, Poster, session); ok { if url, ok := stringProperty(player, Poster, session); ok {
session.updateProperty(view.htmlID(), string(Poster), url) session.updateProperty(player.htmlID(), string(Poster), url)
} else { } else {
session.removeProperty(view.htmlID(), string(Poster)) session.removeProperty(player.htmlID(), string(Poster))
} }
default: default:
mediaPlayerPropertyChanged(view, tag) player.mediaPlayerData.propertyChanged(tag)
} }
} }

296
view.go
View File

@ -110,10 +110,10 @@ type viewData struct {
created bool created bool
hasFocus bool hasFocus bool
hasHtmlDisabled bool hasHtmlDisabled bool
getFunc func(view View, tag PropertyName) any get func(tag PropertyName) any
set func(view View, tag PropertyName, value any) []PropertyName set func(tag PropertyName, value any) []PropertyName
remove func(view View, tag PropertyName) []PropertyName remove func(tag PropertyName) []PropertyName
changed func(view View, tag PropertyName) changed func(tag PropertyName)
} }
func newView(session Session) View { func newView(session Session) View {
@ -145,10 +145,11 @@ func setInitParams(view View, params Params) {
func (view *viewData) init(session Session) { func (view *viewData) init(session Session) {
view.viewStyle.init() view.viewStyle.init()
view.getFunc = viewGet view.get = view.getFunc
view.set = viewSet view.set = view.setFunc
view.remove = view.removeFunc
view.normalize = normalizeViewTag view.normalize = normalizeViewTag
view.changed = viewPropertyChanged view.changed = view.propertyChanged
view.tag = "View" view.tag = "View"
view.session = session view.session = session
view.changeListener = map[PropertyName]func(View, PropertyName){} view.changeListener = map[PropertyName]func(View, PropertyName){}
@ -213,35 +214,11 @@ func (view *viewData) Focusable() bool {
} }
func (view *viewData) Remove(tag PropertyName) { func (view *viewData) Remove(tag PropertyName) {
tag = view.normalize(tag) changedTags := view.removeFunc(view.normalize(tag))
var changedTags []PropertyName = nil
switch tag {
case ID:
if view.viewID != "" {
view.viewID = ""
changedTags = []PropertyName{ID}
}
case AnimationTag:
if val := view.getRaw(AnimationTag); val != nil {
if animations, ok := val.([]Animation); ok {
for _, animation := range animations {
animation.unused(view.session)
}
}
view.setRaw(AnimationTag, nil)
changedTags = []PropertyName{AnimationTag}
}
default:
changedTags = view.remove(view, tag)
}
if view.created && len(changedTags) > 0 { if view.created && len(changedTags) > 0 {
for _, tag := range changedTags { for _, tag := range changedTags {
view.changed(view, tag) view.changed(tag)
} }
for _, tag := range changedTags { for _, tag := range changedTags {
@ -252,6 +229,15 @@ func (view *viewData) Remove(tag PropertyName) {
} }
} }
func (view *viewData) Get(tag PropertyName) any {
switch tag {
case ID:
return view.ID()
}
return view.get(view.normalize(tag))
}
func (view *viewData) Set(tag PropertyName, value any) bool { func (view *viewData) Set(tag PropertyName, value any) bool {
if value == nil { if value == nil {
view.Remove(tag) view.Remove(tag)
@ -259,42 +245,11 @@ func (view *viewData) Set(tag PropertyName, value any) bool {
} }
tag = view.normalize(tag) tag = view.normalize(tag)
var changedTags []PropertyName = nil changedTags := view.set(tag, value)
switch tag {
case ID:
text, ok := value.(string)
if !ok {
notCompatibleType(ID, value)
return false
}
view.viewID = text
changedTags = []PropertyName{ID}
case AnimationTag:
oldAnimations := []Animation{}
if val := view.getRaw(AnimationTag); val != nil {
if animation, ok := val.([]Animation); ok {
oldAnimations = animation
}
}
if !setAnimationProperty(view, tag, value) {
return false
}
for _, animation := range oldAnimations {
animation.unused(view.session)
}
changedTags = []PropertyName{AnimationTag}
default:
changedTags = viewSet(view, tag, value)
}
if view.created && len(changedTags) > 0 { if view.created && len(changedTags) > 0 {
for _, tag := range changedTags { for _, tag := range changedTags {
view.changed(view, tag) view.changed(tag)
} }
for _, tag := range changedTags { for _, tag := range changedTags {
@ -316,67 +271,78 @@ func normalizeViewTag(tag PropertyName) PropertyName {
return tag return tag
} }
/* func (view *viewData) getFunc(tag PropertyName) any {
func (view *viewData) propertyChangedEvent(tag PropertyName) { if tag == ID {
if listener, ok := view.changeListener[tag]; ok { if id := view.ID(); id != "" {
listener(view, tag) return id
} else {
return nil
}
} }
return viewStyleGet(view, tag)
}
func (view *viewData) removeFunc(tag PropertyName) []PropertyName {
var changedTags []PropertyName = nil
switch tag { switch tag {
case BorderLeft, BorderRight, BorderTop, BorderBottom, case ID:
BorderStyle, BorderLeftStyle, BorderRightStyle, BorderTopStyle, BorderBottomStyle, if view.viewID != "" {
BorderColor, BorderLeftColor, BorderRightColor, BorderTopColor, BorderBottomColor, view.viewID = ""
BorderWidth, BorderLeftWidth, BorderRightWidth, BorderTopWidth, BorderBottomWidth: changedTags = []PropertyName{ID}
tag = Border } else {
changedTags = []PropertyName{}
}
case CellBorderStyle, CellBorderColor, CellBorderWidth, case AnimationTag:
CellBorderLeft, CellBorderLeftStyle, CellBorderLeftColor, CellBorderLeftWidth, if val := view.getRaw(AnimationTag); val != nil {
CellBorderRight, CellBorderRightStyle, CellBorderRightColor, CellBorderRightWidth, if animations, ok := val.([]Animation); ok {
CellBorderTop, CellBorderTopStyle, CellBorderTopColor, CellBorderTopWidth, for _, animation := range animations {
CellBorderBottom, CellBorderBottomStyle, CellBorderBottomColor, CellBorderBottomWidth: animation.unused(view.session)
tag = CellBorder }
}
case OutlineColor, OutlineStyle, OutlineWidth: view.setRaw(AnimationTag, nil)
tag = Outline changedTags = []PropertyName{AnimationTag}
}
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: default:
return changedTags = viewStyleRemove(view, tag)
} }
if listener, ok := view.changeListener[tag]; ok { return changedTags
listener(view, tag)
}
}
*/
func viewRemove(properties Properties, tag PropertyName) []PropertyName {
return viewStyleRemove(properties, tag)
} }
func viewSet(view View, tag PropertyName, value any) []PropertyName { func (view *viewData) setFunc(tag PropertyName, value any) []PropertyName {
switch tag { switch tag {
case ID:
if text, ok := value.(string); ok {
view.viewID = text
view.setRaw(ID, text)
return []PropertyName{ID}
}
notCompatibleType(ID, value)
return nil
case AnimationTag:
oldAnimations := []Animation{}
if val := view.getRaw(AnimationTag); val != nil {
if animation, ok := val.([]Animation); ok {
oldAnimations = animation
}
}
if !setAnimationProperty(view, tag, value) {
return nil
}
for _, animation := range oldAnimations {
animation.unused(view.session)
}
return []PropertyName{AnimationTag}
case TabIndex, "tab-index": case TabIndex, "tab-index":
return setIntProperty(view, TabIndex, value) return setIntProperty(view, TabIndex, value)
@ -393,29 +359,46 @@ func viewSet(view View, tag PropertyName, value any) []PropertyName {
return nil return nil
case FocusEvent, LostFocusEvent: case FocusEvent, LostFocusEvent:
return setNoParamEventListener[View](view, tag, value) return setNoArgEventListener[View](view, tag, value)
case KeyDownEvent, KeyUpEvent: case KeyDownEvent, KeyUpEvent:
return setViewEventListener[View, KeyEvent](view, tag, value) return setOneArgEventListener[View, KeyEvent](view, tag, value)
case ClickEvent, DoubleClickEvent, MouseDown, MouseUp, MouseMove, MouseOut, MouseOver, ContextMenuEvent: case ClickEvent, DoubleClickEvent, MouseDown, MouseUp, MouseMove, MouseOut, MouseOver, ContextMenuEvent:
return setViewEventListener[View, MouseEvent](view, tag, value) return setOneArgEventListener[View, MouseEvent](view, tag, value)
case PointerDown, PointerUp, PointerMove, PointerOut, PointerOver, PointerCancel: case PointerDown, PointerUp, PointerMove, PointerOut, PointerOver, PointerCancel:
return setViewEventListener[View, PointerEvent](view, tag, value) return setOneArgEventListener[View, PointerEvent](view, tag, value)
case TouchStart, TouchEnd, TouchMove, TouchCancel: case TouchStart, TouchEnd, TouchMove, TouchCancel:
return setViewEventListener[View, TouchEvent](view, tag, value) return setOneArgEventListener[View, TouchEvent](view, tag, value)
case TransitionRunEvent, TransitionStartEvent, TransitionEndEvent, TransitionCancelEvent, case TransitionRunEvent, TransitionStartEvent, TransitionEndEvent, TransitionCancelEvent:
AnimationStartEvent, AnimationEndEvent, AnimationIterationEvent, AnimationCancelEvent: result := setOneArgEventListener[View, PropertyName](view, tag, value)
return setViewEventListener[View, string](view, tag, value) if result == nil {
//return setTransitionListener(view, tag, value), tag result = setOneArgEventListener[View, string](view, tag, value)
//return setAnimationListener(view, tag, value), tag if result != nil {
if listeners, ok := view.getRaw(tag).([]func(View, string)); ok {
newListeners := make([]func(View, PropertyName), len(listeners))
for i, listener := range listeners {
newListeners[i] = func(view View, name PropertyName) {
listener(view, string(name))
}
}
view.setRaw(tag, newListeners)
return result
}
view.setRaw(tag, nil)
return nil
}
}
return result
case AnimationStartEvent, AnimationEndEvent, AnimationIterationEvent, AnimationCancelEvent:
return setOneArgEventListener[View, string](view, tag, value)
case ResizeEvent, ScrollEvent: case ResizeEvent, ScrollEvent:
return setViewEventListener[View, Frame](view, tag, value) return setOneArgEventListener[View, Frame](view, tag, value)
//return setFrameListener(view, tag, value), tag
} }
return viewStyleSet(view, tag, value) return viewStyleSet(view, tag, value)
@ -439,12 +422,7 @@ func (view *viewData) SetParams(params Params) bool {
return result return result
} }
func viewPropertyChanged(view View, tag PropertyName) { func (view *viewData) propertyChanged(tag PropertyName) {
/*
if view.updateTransformProperty(tag) {
return
}
*/
htmlID := view.htmlID() htmlID := view.htmlID()
session := view.Session() session := view.Session()
@ -645,9 +623,11 @@ func viewPropertyChanged(view View, tag PropertyName) {
case Strikethrough, Overline, Underline: case Strikethrough, Overline, Underline:
session.updateCSSProperty(htmlID, "text-decoration", textDecorationCSS(view, session)) session.updateCSSProperty(htmlID, "text-decoration", textDecorationCSS(view, session))
for _, tag2 := range []PropertyName{TextLineColor, TextLineStyle, TextLineThickness} { /*
viewPropertyChanged(view, tag2) for _, tag2 := range []PropertyName{TextLineColor, TextLineStyle, TextLineThickness} {
} view.propertyChanged(tag2)
}
*/
case Transition: case Transition:
session.updateCSSProperty(htmlID, "transition", transitionCSS(view, session)) session.updateCSSProperty(htmlID, "transition", transitionCSS(view, session))
@ -711,34 +691,19 @@ func viewPropertyChanged(view View, tag PropertyName) {
} }
case PerspectiveOriginX, PerspectiveOriginY: case PerspectiveOriginX, PerspectiveOriginY:
if getTransform3D(view, session) { x, y := GetPerspectiveOrigin(view)
x, y := GetPerspectiveOrigin(view) session.updateCSSProperty(htmlID, "perspective-origin", transformOriginCSS(x, y, AutoSize(), view.Session()))
value := ""
if x.Type != Auto || y.Type != Auto {
value = x.cssString("50%", session) + " " + y.cssString("50%", session)
}
session.updateCSSProperty(htmlID, "perspective-origin", value)
}
case BackfaceVisible: case BackfaceVisible:
if getTransform3D(view, session) { if GetBackfaceVisible(view) {
if GetBackfaceVisible(view) { session.updateCSSProperty(htmlID, string(BackfaceVisible), "visible")
session.updateCSSProperty(htmlID, string(BackfaceVisible), "visible") } else {
} else { session.updateCSSProperty(htmlID, string(BackfaceVisible), "hidden")
session.updateCSSProperty(htmlID, string(BackfaceVisible), "hidden")
}
} }
case OriginX, OriginY, OriginZ: case TransformOriginX, TransformOriginY, TransformOriginZ:
x, y, z := getOrigin(view, session) x, y, z := getTransformOrigin(view, session)
value := "" session.updateCSSProperty(htmlID, "transform-origin", transformOriginCSS(x, y, z, view.Session()))
if z.Type != Auto {
value = x.cssString("50%", session) + " " + y.cssString("50%", session) + " " + z.cssString("50%", session)
} else if x.Type != Auto || y.Type != Auto {
value = x.cssString("50%", session) + " " + y.cssString("50%", session)
}
session.updateCSSProperty(htmlID, "transform-origin", value)
case TransformTag, Perspective, SkewX, SkewY, TranslateX, TranslateY, TranslateZ, case TransformTag, Perspective, SkewX, SkewY, TranslateX, TranslateY, TranslateZ,
ScaleX, ScaleY, ScaleZ, Rotate, RotateX, RotateY, RotateZ: ScaleX, ScaleY, ScaleZ, Rotate, RotateX, RotateY, RotateZ:
@ -803,17 +768,6 @@ func viewPropertyChanged(view View, tag PropertyName) {
} }
} }
func viewGet(view View, tag PropertyName) any {
if tag == ID {
if id := view.ID(); id != "" {
return id
} else {
return nil
}
}
return viewStyleGet(view, tag)
}
func (view *viewData) htmlTag() string { func (view *viewData) htmlTag() string {
if semantics := GetSemantics(view); semantics > DefaultSemantics { if semantics := GetSemantics(view); semantics > DefaultSemantics {
values := enumProperties[Semantics].cssValues values := enumProperties[Semantics].cssValues
@ -999,13 +953,13 @@ func (view *viewData) handleCommand(self View, command PropertyName, data DataOb
case FocusEvent: case FocusEvent:
view.hasFocus = true view.hasFocus = true
for _, listener := range getNoParamEventListeners[View](view, nil, command) { for _, listener := range getNoArgEventListeners[View](view, nil, command) {
listener(self) listener(self)
} }
case LostFocusEvent: case LostFocusEvent:
view.hasFocus = false view.hasFocus = false
for _, listener := range getNoParamEventListeners[View](view, nil, command) { for _, listener := range getNoArgEventListeners[View](view, nil, command) {
listener(self) listener(self)
} }

View File

@ -497,6 +497,16 @@ func normalizeViewStyleTag(tag PropertyName) PropertyName {
case "left-padding": case "left-padding":
return PaddingLeft return PaddingLeft
case "origin-x":
return TransformOriginX
case "origin-y":
return TransformOriginY
case "origin-z":
return TransformOriginZ
} }
return tag return tag
} }
@ -868,7 +878,8 @@ func writeViewStyle(name string, view Properties, buffer *strings.Builder, inden
} }
finalTags := []PropertyName{ finalTags := []PropertyName{
Perspective, PerspectiveOriginX, PerspectiveOriginY, BackfaceVisible, OriginX, OriginY, OriginZ, PerspectiveOriginX, PerspectiveOriginY, BackfaceVisible,
TransformOriginX, TransformOriginY, TransformOriginZ,
TransformTag, Clip, Filter, BackdropFilter, Summary, Content, Transition} TransformTag, Clip, Filter, BackdropFilter, Summary, Content, Transition}
for _, tag := range finalTags { for _, tag := range finalTags {
removeTag(tag) removeTag(tag)

View File

@ -79,7 +79,7 @@ const (
// //
// Internal type is `SizeUnit`, other types converted to it during assignment. // Internal type is `SizeUnit`, other types converted to it during assignment.
// See `SizeUnit` description for more details. // See `SizeUnit` description for more details.
OriginX PropertyName = "origin-x" TransformOriginX PropertyName = "transform-origin-x"
// OriginY is the constant for "origin-y" property tag. // OriginY is the constant for "origin-y" property tag.
// //
@ -90,7 +90,7 @@ const (
// //
// Internal type is `SizeUnit`, other types converted to it during assignment. // Internal type is `SizeUnit`, other types converted to it during assignment.
// See `SizeUnit` description for more details. // See `SizeUnit` description for more details.
OriginY PropertyName = "origin-y" TransformOriginY PropertyName = "transform-origin-y"
// OriginZ is the constant for "origin-z" property tag. // OriginZ is the constant for "origin-z" property tag.
// //
@ -101,7 +101,7 @@ const (
// //
// Internal type is `SizeUnit`, other types converted to it during assignment. // Internal type is `SizeUnit`, other types converted to it during assignment.
// See `SizeUnit` description for more details. // See `SizeUnit` description for more details.
OriginZ PropertyName = "origin-z" TransformOriginZ PropertyName = "transform-origin-z"
// TranslateX is the constant for "translate-x" property tag. // TranslateX is the constant for "translate-x" property tag.
// //
@ -527,29 +527,31 @@ func getTransformProperty(properties Properties) Transform {
func setTransformPropertyElement(properties Properties, tag PropertyName, value any) []PropertyName { func setTransformPropertyElement(properties Properties, tag PropertyName, value any) []PropertyName {
switch tag { switch tag {
case Perspective, RotateX, RotateY, RotateZ, Rotate, SkewX, SkewY, ScaleX, ScaleY, ScaleZ, TranslateX, TranslateY, TranslateZ: case Perspective, RotateX, RotateY, RotateZ, Rotate, SkewX, SkewY, ScaleX, ScaleY, ScaleZ, TranslateX, TranslateY, TranslateZ:
var result []PropertyName = nil
if transform := getTransformProperty(properties); transform != nil { if transform := getTransformProperty(properties); transform != nil {
if result := transformSet(transform, tag, value); result != nil { if result = transformSet(transform, tag, value); result != nil {
result = append(result, TransformTag) result = append(result, TransformTag)
} }
} else { } else {
transform := NewTransform(nil) transform := NewTransform(nil)
if result := transformSet(transform, tag, value); result != nil { if result = transformSet(transform, tag, value); result != nil {
properties.setRaw(TransformTag, transform) properties.setRaw(TransformTag, transform)
result = append(result, TransformTag) result = append(result, TransformTag)
} }
} }
return result
default:
ErrorLogF(`"Transform" interface does not support the "%s" property`, tag)
} }
ErrorLogF(`"Transform" interface does not support the "%s" property`, tag)
return nil return nil
} }
/*
func getTransform3D(style Properties, session Session) bool { func getTransform3D(style Properties, session Session) bool {
perspective, ok := sizeProperty(style, Perspective, session) perspective, ok := sizeProperty(style, Perspective, session)
return ok && perspective.Type != Auto && perspective.Value != 0 return ok && perspective.Type != Auto && perspective.Value != 0
} }
*/
func getPerspectiveOrigin(style Properties, session Session) (SizeUnit, SizeUnit) { func getPerspectiveOrigin(style Properties, session Session) (SizeUnit, SizeUnit) {
x, _ := sizeProperty(style, PerspectiveOriginX, session) x, _ := sizeProperty(style, PerspectiveOriginX, session)
@ -557,10 +559,10 @@ func getPerspectiveOrigin(style Properties, session Session) (SizeUnit, SizeUnit
return x, y return x, y
} }
func getOrigin(style Properties, session Session) (SizeUnit, SizeUnit, SizeUnit) { func getTransformOrigin(style Properties, session Session) (SizeUnit, SizeUnit, SizeUnit) {
x, _ := sizeProperty(style, OriginX, session) x, _ := sizeProperty(style, TransformOriginX, session)
y, _ := sizeProperty(style, OriginY, session) y, _ := sizeProperty(style, TransformOriginY, session)
z, _ := sizeProperty(style, OriginZ, session) z, _ := sizeProperty(style, TransformOriginZ, session)
return x, y, z return x, y, z
} }
@ -674,8 +676,9 @@ func (transform *transformData) transformCSS(session Session) string {
func (style *viewStyle) writeViewTransformCSS(builder cssBuilder, session Session) { func (style *viewStyle) writeViewTransformCSS(builder cssBuilder, session Session) {
x, y := getPerspectiveOrigin(style, session) x, y := getPerspectiveOrigin(style, session)
if x.Type != Auto || y.Type != Auto { z := AutoSize()
builder.addValues(`perspective-origin`, ` `, x.cssString("50%", session), y.cssString("50%", session)) if css := transformOriginCSS(x, y, z, session); css != "" {
builder.add(`perspective-origin`, css)
} }
if backfaceVisible, ok := boolProperty(style, BackfaceVisible, session); ok { if backfaceVisible, ok := boolProperty(style, BackfaceVisible, session); ok {
@ -686,11 +689,9 @@ func (style *viewStyle) writeViewTransformCSS(builder cssBuilder, session Sessio
} }
} }
x, y, z := getOrigin(style, session) x, y, z = getTransformOrigin(style, session)
if z.Type != Auto && z.Value != 0 { if css := transformOriginCSS(x, y, z, session); css != "" {
builder.addValues(`transform-origin`, ` `, x.cssString("50%", session), y.cssString("50%", session), z.cssString("0", session)) builder.add(`transform-origin`, css)
} else if x.Type != Auto || y.Type != Auto {
builder.addValues(`transform-origin`, ` `, x.cssString("50%", session), y.cssString("50%", session))
} }
if transform := getTransformProperty(style); transform != nil { if transform := getTransformProperty(style); transform != nil {
@ -698,6 +699,56 @@ func (style *viewStyle) writeViewTransformCSS(builder cssBuilder, session Sessio
} }
} }
func transformOriginCSS(x, y, z SizeUnit, session Session) string {
if z.Type == Auto && x.Type == Auto && y.Type == Auto {
return ""
}
buffer := allocStringBuilder()
defer freeStringBuilder(buffer)
if x.Type == SizeInPercent {
switch x.Value {
case 0:
buffer.WriteString("left")
case 50:
buffer.WriteString("center")
case 100:
buffer.WriteString("right")
default:
buffer.WriteString(x.cssString("center", session))
}
} else {
buffer.WriteString(x.cssString("center", session))
}
buffer.WriteRune(' ')
if y.Type == SizeInPercent {
switch y.Value {
case 0:
buffer.WriteString("top")
case 50:
buffer.WriteString("center")
case 100:
buffer.WriteString("bottom")
default:
buffer.WriteString(y.cssString("center", session))
}
} else {
buffer.WriteString(y.cssString("center", session))
}
if z.Type != Auto && z.Value != 0 {
buffer.WriteRune(' ')
buffer.WriteString(z.cssString("0", session))
}
return buffer.String()
}
/* /*
func (view *viewData) updateTransformProperty(tag PropertyName) bool { func (view *viewData) updateTransformProperty(tag PropertyName) bool {
htmlID := view.htmlID() htmlID := view.htmlID()

View File

@ -613,17 +613,17 @@ func GetBackfaceVisible(view View, subviewID ...string) bool {
return boolStyledProperty(view, subviewID, BackfaceVisible, false) return boolStyledProperty(view, subviewID, BackfaceVisible, false)
} }
// GetOrigin returns a x-, y-, and z-coordinate of the point around which a view transformation is applied. // GetTransformOrigin returns a x-, y-, and z-coordinate of the point around which a view transformation is applied.
// The default value is (50%, 50%, 50%). // The default value is (50%, 50%, 50%).
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetOrigin(view View, subviewID ...string) (SizeUnit, SizeUnit, SizeUnit) { func GetTransformOrigin(view View, subviewID ...string) (SizeUnit, SizeUnit, SizeUnit) {
if len(subviewID) > 0 && subviewID[0] != "" { if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0]) view = ViewByID(view, subviewID[0])
} }
if view == nil { if view == nil {
return AutoSize(), AutoSize(), AutoSize() return AutoSize(), AutoSize(), AutoSize()
} }
return getOrigin(view, view.Session()) return getTransformOrigin(view, view.Session())
} }
// GetTranslate returns a x-, y-, and z-axis translation value of a 2D/3D translation // GetTranslate returns a x-, y-, and z-axis translation value of a 2D/3D translation

View File

@ -38,10 +38,10 @@ func (container *viewsContainerData) init(session Session) {
container.viewData.init(session) container.viewData.init(session)
container.tag = "ViewsContainer" container.tag = "ViewsContainer"
container.views = []View{} container.views = []View{}
container.getFunc = container.get container.get = container.getFunc
container.set = container.setFunc container.set = container.setFunc
container.remove = container.removeFunc container.remove = container.removeFunc
container.changed = viewsContainerPropertyChanged container.changed = container.propertyChanged
} }
func (container *viewsContainerData) setParentID(parentID string) { func (container *viewsContainerData) setParentID(parentID string) {
@ -163,7 +163,7 @@ func viewFromTextValue(text string, session Session) View {
return NewTextView(session, Params{Text: text}) return NewTextView(session, Params{Text: text})
} }
func (container *viewsContainerData) removeFunc(view View, tag PropertyName) []PropertyName { func (container *viewsContainerData) removeFunc(tag PropertyName) []PropertyName {
switch tag { switch tag {
case Content: case Content:
if len(container.views) > 0 { if len(container.views) > 0 {
@ -173,18 +173,18 @@ func (container *viewsContainerData) removeFunc(view View, tag PropertyName) []P
return []PropertyName{} return []PropertyName{}
case Disabled: case Disabled:
if view.getRaw(Disabled) != nil { if container.getRaw(Disabled) != nil {
view.setRaw(Disabled, nil) container.setRaw(Disabled, nil)
for _, view := range container.views { for _, view := range container.views {
view.Remove(Disabled) view.Remove(Disabled)
} }
return []PropertyName{tag} return []PropertyName{tag}
} }
} }
return viewRemove(view, tag) return container.viewData.removeFunc(tag)
} }
func (container *viewsContainerData) setFunc(self View, tag PropertyName, value any) []PropertyName { func (container *viewsContainerData) setFunc(tag PropertyName, value any) []PropertyName {
switch tag { switch tag {
case Content: case Content:
if container.setContent(value) { if container.setContent(value) {
@ -194,7 +194,7 @@ func (container *viewsContainerData) setFunc(self View, tag PropertyName, value
case Disabled: case Disabled:
oldDisabled := IsDisabled(container) oldDisabled := IsDisabled(container)
result := viewSet(self, Disabled, value) result := container.viewData.setFunc(Disabled, value)
if result != nil { if result != nil {
disabled := IsDisabled(container) disabled := IsDisabled(container)
if oldDisabled != disabled { if oldDisabled != disabled {
@ -206,16 +206,16 @@ func (container *viewsContainerData) setFunc(self View, tag PropertyName, value
return result return result
} }
return viewSet(self, tag, value) return container.viewData.setFunc(tag, value)
} }
func viewsContainerPropertyChanged(view View, tag PropertyName) { func (container *viewsContainerData) propertyChanged(tag PropertyName) {
switch tag { switch tag {
case Content: case Content:
updateInnerHTML(view.htmlID(), view.Session()) updateInnerHTML(container.htmlID(), container.Session())
default: default:
viewPropertyChanged(view, tag) container.viewData.propertyChanged(tag)
} }
} }
@ -292,13 +292,13 @@ func (container *viewsContainerData) setContent(value any) bool {
return true return true
} }
func (container *viewsContainerData) get(view View, tag PropertyName) any { func (container *viewsContainerData) getFunc(tag PropertyName) any {
switch tag { switch tag {
case Content: case Content:
return container.views return container.views
default: default:
return viewGet(view, tag) return container.viewData.getFunc(tag)
} }
} }