package rui import ( "fmt" "strconv" "strings" ) // Frame - the location and size of a rectangle area type Frame struct { // Left - the left border Left float64 // Top - the top border Top float64 // Width - the width of a rectangle area Width float64 // Height - the height of a rectangle area Height float64 } // Right returns the right border func (frame Frame) Right() float64 { return frame.Left + frame.Width } // Bottom returns the bottom border func (frame Frame) Bottom() float64 { return frame.Top + frame.Height } // View represents a base view interface type View interface { ViewStyle fmt.Stringer // Session returns the current Session interface Session() Session // Parent returns the parent view Parent() View // Tag returns the tag of View interface Tag() string // ID returns the id of the view ID() string // Focusable returns true if the view receives the focus Focusable() bool // Frame returns the location and size of the view in pixels Frame() Frame // Scroll returns the location size of the scrollable view in pixels Scroll() Frame // SetParams sets properties with name "tag" of the "rootView" subview. Result: // * true - all properties were set successful, // * false - error (incompatible type or invalid format of a string value, see AppLog). SetParams(params Params) bool // SetAnimated sets the value (second argument) of the property with name defined by the first argument. // Return "true" if the value has been set, in the opposite case "false" are returned and // a description of the error is written to the log SetAnimated(tag PropertyName, value any, animation Animation) bool // SetChangeListener set the function to track the change of the View property SetChangeListener(tag PropertyName, listener func(View, PropertyName)) // HasFocus returns 'true' if the view has focus HasFocus() bool init(session Session) handleCommand(self View, command PropertyName, data DataObject) bool htmlClass(disabled bool) string htmlTag() string closeHTMLTag() bool htmlID() string parentHTMLID() string setParentID(parentID string) htmlSubviews(self View, buffer *strings.Builder) htmlProperties(self View, buffer *strings.Builder) cssStyle(self View, builder cssBuilder) addToCSSStyle(addCSS map[string]string) exscludeTags() []PropertyName htmlDisabledProperty() bool onResize(self View, x, y, width, height float64) onItemResize(self View, index string, x, y, width, height float64) setNoResizeEvent() isNoResizeEvent() bool setScroll(x, y, width, height float64) } // viewData - base implementation of View interface type viewData struct { viewStyle session Session tag string viewID string _htmlID string parentID string systemClass string changeListener map[PropertyName]func(View, PropertyName) singleTransition map[PropertyName]Animation addCSS map[string]string frame Frame scroll Frame noResizeEvent bool created bool hasFocus bool hasHtmlDisabled bool get func(tag PropertyName) any set func(tag PropertyName, value any) []PropertyName remove func(tag PropertyName) []PropertyName changed func(tag PropertyName) } func newView(session Session) View { return new(viewData) } // NewView create new View object and return it func NewView(session Session, params Params) View { view := new(viewData) view.init(session) setInitParams(view, params) return view } func setInitParams(view View, params Params) { if params != nil { session := view.Session() if !session.ignoreViewUpdates() { session.setIgnoreViewUpdates(true) defer session.setIgnoreViewUpdates(false) } for _, tag := range params.AllTags() { if value, ok := params[tag]; ok { view.Set(tag, value) } } } } func (view *viewData) init(session Session) { view.viewStyle.init() view.get = view.getFunc view.set = view.setFunc view.remove = view.removeFunc view.normalize = normalizeViewTag view.changed = view.propertyChanged view.tag = "View" view.session = session view.changeListener = map[PropertyName]func(View, PropertyName){} view.addCSS = map[string]string{} //view.animation = map[string]AnimationEndListener{} view.singleTransition = map[PropertyName]Animation{} view.noResizeEvent = false view.created = false view.hasHtmlDisabled = false } func (view *viewData) Session() Session { return view.session } func (view *viewData) Parent() View { return view.session.viewByHTMLID(view.parentID) } func (view *viewData) parentHTMLID() string { return view.parentID } func (view *viewData) setParentID(parentID string) { view.parentID = parentID } func (view *viewData) Tag() string { return view.tag } func (view *viewData) ID() string { return view.viewID } func (view *viewData) ViewByID(id string) View { if id == view.ID() { if v := view.session.viewByHTMLID(view.htmlID()); v != nil { return v } return view } return nil } func (view *viewData) Focusable() bool { if focus, ok := boolProperty(view, Focusable, view.session); ok { return focus } if style, ok := stringProperty(view, Style, view.session); ok { if style, ok := view.session.resolveConstants(style); ok { if value := view.session.styleProperty(style, Focusable); ok { if focus, ok := valueToBool(value, view.Session()); ok { return focus } } } } return false } func (view *viewData) Remove(tag PropertyName) { changedTags := view.removeFunc(view.normalize(tag)) if view.created && len(changedTags) > 0 { for _, tag := range changedTags { view.changed(tag) } for _, tag := range changedTags { if listener, ok := view.changeListener[tag]; ok { listener(view, tag) } } } } 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 { if value == nil { view.Remove(tag) return true } tag = view.normalize(tag) changedTags := view.set(tag, value) if view.created && len(changedTags) > 0 { for _, tag := range changedTags { view.changed(tag) } for _, tag := range changedTags { if listener, ok := view.changeListener[tag]; ok { listener(view, tag) } } } return changedTags != nil } func normalizeViewTag(tag PropertyName) PropertyName { tag = normalizeViewStyleTag(tag) switch tag { case "tab-index": return TabIndex } return tag } func (view *viewData) getFunc(tag PropertyName) any { if tag == ID { if id := view.ID(); id != "" { return id } else { return nil } } return viewStyleGet(view, tag) } func (view *viewData) removeFunc(tag PropertyName) []PropertyName { var changedTags []PropertyName = nil switch tag { case ID: if view.viewID != "" { view.viewID = "" changedTags = []PropertyName{ID} } else { changedTags = []PropertyName{} } 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 = viewStyleRemove(view, tag) } return changedTags } func (view *viewData) setFunc(tag PropertyName, value any) []PropertyName { 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": return setIntProperty(view, TabIndex, value) case UserData: view.setRaw(tag, value) return []PropertyName{UserData} case Style, StyleDisabled: if text, ok := value.(string); ok { view.setRaw(tag, text) return []PropertyName{tag} } notCompatibleType(ID, value) return nil case FocusEvent, LostFocusEvent: return setNoArgEventListener[View](view, tag, value) case KeyDownEvent, KeyUpEvent: return setOneArgEventListener[View, KeyEvent](view, tag, value) case ClickEvent, DoubleClickEvent, MouseDown, MouseUp, MouseMove, MouseOut, MouseOver, ContextMenuEvent: return setOneArgEventListener[View, MouseEvent](view, tag, value) case PointerDown, PointerUp, PointerMove, PointerOut, PointerOver, PointerCancel: return setOneArgEventListener[View, PointerEvent](view, tag, value) case TouchStart, TouchEnd, TouchMove, TouchCancel: return setOneArgEventListener[View, TouchEvent](view, tag, value) case TransitionRunEvent, TransitionStartEvent, TransitionEndEvent, TransitionCancelEvent: result := setOneArgEventListener[View, PropertyName](view, tag, value) if result == nil { result = setOneArgEventListener[View, string](view, tag, value) 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: return setOneArgEventListener[View, Frame](view, tag, value) } return viewStyleSet(view, tag, value) } func (view *viewData) SetParams(params Params) bool { if params == nil { errorLog("Argument of function SetParams is nil") return false } session := view.Session() session.startUpdateScript(view.htmlID()) result := true for _, tag := range params.AllTags() { if value, ok := params[tag]; ok { result = view.Set(tag, value) && result } } session.finishUpdateScript(view.htmlID()) return result } func (view *viewData) propertyChanged(tag PropertyName) { htmlID := view.htmlID() session := view.Session() switch tag { case TabIndex: if value, ok := intProperty(view, TabIndex, view.Session(), 0); ok { session.updateProperty(view.htmlID(), "tabindex", strconv.Itoa(value)) } else if view.Focusable() { session.updateProperty(view.htmlID(), "tabindex", "0") } else { session.updateProperty(view.htmlID(), "tabindex", "-1") } case Style, StyleDisabled: session.updateProperty(view.htmlID(), "class", view.htmlClass(IsDisabled(view))) case Disabled: tabIndex := GetTabIndex(view, htmlID) enabledClass := view.htmlClass(false) disabledClass := view.htmlClass(true) session.startUpdateScript(htmlID) if IsDisabled(view) { session.updateProperty(htmlID, "data-disabled", "1") if view.htmlDisabledProperty() { session.updateProperty(htmlID, "disabled", true) } if tabIndex >= 0 { session.updateProperty(htmlID, "tabindex", -1) } if enabledClass != disabledClass { session.updateProperty(htmlID, "class", disabledClass) } } else { session.updateProperty(htmlID, "data-disabled", "0") if view.htmlDisabledProperty() { session.removeProperty(htmlID, "disabled") } if tabIndex >= 0 { session.updateProperty(htmlID, "tabindex", tabIndex) } if enabledClass != disabledClass { session.updateProperty(htmlID, "class", enabledClass) } } session.finishUpdateScript(htmlID) updateInnerHTML(htmlID, session) case Visibility: switch GetVisibility(view) { case Invisible: session.updateCSSProperty(htmlID, string(Visibility), "hidden") session.updateCSSProperty(htmlID, "display", "") session.callFunc("hideTooltip") case Gone: session.updateCSSProperty(htmlID, string(Visibility), "hidden") session.updateCSSProperty(htmlID, "display", "none") session.callFunc("hideTooltip") default: session.updateCSSProperty(htmlID, string(Visibility), "visible") session.updateCSSProperty(htmlID, "display", "") } case Background: session.updateCSSProperty(htmlID, string(Background), backgroundCSS(view, session)) case Border, BorderLeft, BorderRight, BorderTop, BorderBottom: cssWidth := "" cssColor := "" cssStyle := "none" if border := getBorderProperty(view, Border); border != nil { cssWidth = border.cssWidthValue(session) cssColor = border.cssColorValue(session) cssStyle = border.cssStyleValue(session) } session.updateCSSProperty(htmlID, string(BorderWidth), cssWidth) session.updateCSSProperty(htmlID, string(BorderColor), cssColor) session.updateCSSProperty(htmlID, string(BorderStyle), cssStyle) case BorderStyle, BorderLeftStyle, BorderRightStyle, BorderTopStyle, BorderBottomStyle: if border := getBorderProperty(view, Border); border != nil { session.updateCSSProperty(htmlID, string(BorderStyle), border.cssStyleValue(session)) } case BorderColor, BorderLeftColor, BorderRightColor, BorderTopColor, BorderBottomColor: if border := getBorderProperty(view, Border); border != nil { session.updateCSSProperty(htmlID, string(BorderColor), border.cssColorValue(session)) } case BorderWidth, BorderLeftWidth, BorderRightWidth, BorderTopWidth, BorderBottomWidth: if border := getBorderProperty(view, Border); border != nil { session.updateCSSProperty(htmlID, string(BorderWidth), border.cssWidthValue(session)) } case Outline, OutlineColor, OutlineStyle, OutlineWidth: session.updateCSSProperty(htmlID, string(Outline), GetOutline(view).cssString(session)) case Shadow: session.updateCSSProperty(htmlID, "box-shadow", shadowCSS(view, Shadow, session)) case TextShadow: session.updateCSSProperty(htmlID, "text-shadow", shadowCSS(view, TextShadow, session)) case Radius, RadiusX, RadiusY, RadiusTopLeft, RadiusTopLeftX, RadiusTopLeftY, RadiusTopRight, RadiusTopRightX, RadiusTopRightY, RadiusBottomLeft, RadiusBottomLeftX, RadiusBottomLeftY, RadiusBottomRight, RadiusBottomRightX, RadiusBottomRightY: radius := GetRadius(view) session.updateCSSProperty(htmlID, "border-radius", radius.cssString(session)) case Margin, MarginTop, MarginRight, MarginBottom, MarginLeft, "top-margin", "right-margin", "bottom-margin", "left-margin": margin := GetMargin(view) session.updateCSSProperty(htmlID, string(Margin), margin.cssString(session)) case Padding, PaddingTop, PaddingRight, PaddingBottom, PaddingLeft, "top-padding", "right-padding", "bottom-padding", "left-padding": padding := GetPadding(view) session.updateCSSProperty(htmlID, string(Padding), padding.cssString(session)) case AvoidBreak: if avoid, ok := boolProperty(view, AvoidBreak, session); ok { if avoid { session.updateCSSProperty(htmlID, "break-inside", "avoid") } else { session.updateCSSProperty(htmlID, "break-inside", "auto") } } case Clip: if clip := getClipShape(view, Clip, session); clip != nil && clip.valid(session) { session.updateCSSProperty(htmlID, `clip-path`, clip.cssStyle(session)) } else { session.updateCSSProperty(htmlID, `clip-path`, "none") } case ShapeOutside: if clip := getClipShape(view, ShapeOutside, session); clip != nil && clip.valid(session) { session.updateCSSProperty(htmlID, string(ShapeOutside), clip.cssStyle(session)) } else { session.updateCSSProperty(htmlID, string(ShapeOutside), "none") } case Filter: text := "" if value := view.getRaw(Filter); value != nil { if filter, ok := value.(ViewFilter); ok { text = filter.cssStyle(session) } } session.updateCSSProperty(htmlID, string(Filter), text) case BackdropFilter: text := "" if value := view.getRaw(BackdropFilter); value != nil { if filter, ok := value.(ViewFilter); ok { text = filter.cssStyle(session) } } if session.startUpdateScript(htmlID) { defer session.finishUpdateScript(htmlID) } session.updateCSSProperty(htmlID, "-webkit-backdrop-filter", text) session.updateCSSProperty(htmlID, string(BackdropFilter), text) case FontName: if font, ok := stringProperty(view, FontName, session); ok { session.updateCSSProperty(htmlID, "font-family", font) } else { session.updateCSSProperty(htmlID, "font-family", "") } case Italic: if state, ok := boolProperty(view, tag, session); ok { if state { session.updateCSSProperty(htmlID, "font-style", "italic") } else { session.updateCSSProperty(htmlID, "font-style", "normal") } } else { session.updateCSSProperty(htmlID, "font-style", "") } case SmallCaps: if state, ok := boolProperty(view, tag, session); ok { if state { session.updateCSSProperty(htmlID, "font-variant", "small-caps") } else { session.updateCSSProperty(htmlID, "font-variant", "normal") } } else { session.updateCSSProperty(htmlID, "font-variant", "") } case Strikethrough, Overline, Underline: session.updateCSSProperty(htmlID, "text-decoration", textDecorationCSS(view, session)) /* for _, tag2 := range []PropertyName{TextLineColor, TextLineStyle, TextLineThickness} { view.propertyChanged(tag2) } */ case Transition: session.updateCSSProperty(htmlID, "transition", transitionCSS(view, session)) case AnimationTag: session.updateCSSProperty(htmlID, "animation", animationCSS(view, session)) case AnimationPaused: paused, ok := boolProperty(view, AnimationPaused, session) if !ok { session.updateCSSProperty(htmlID, `animation-play-state`, ``) } else if paused { session.updateCSSProperty(htmlID, `animation-play-state`, `paused`) } else { session.updateCSSProperty(htmlID, `animation-play-state`, `running`) } case ZIndex, Order, TabSize: if i, ok := intProperty(view, tag, session, 0); ok { session.updateCSSProperty(htmlID, string(tag), strconv.Itoa(i)) } else { session.updateCSSProperty(htmlID, string(tag), "") } case Row, Column: if parentID := view.parentHTMLID(); parentID != "" { updateInnerHTML(parentID, session) } case UserSelect: if session.startUpdateScript(htmlID) { defer session.finishUpdateScript(htmlID) } if userSelect, ok := boolProperty(view, UserSelect, session); ok { if userSelect { session.updateCSSProperty(htmlID, "-webkit-user-select", "auto") session.updateCSSProperty(htmlID, "user-select", "auto") } else { session.updateCSSProperty(htmlID, "-webkit-user-select", "none") session.updateCSSProperty(htmlID, "user-select", "none") } } else { session.updateCSSProperty(htmlID, "-webkit-user-select", "") session.updateCSSProperty(htmlID, "user-select", "") } case ColumnSpanAll: if spanAll, ok := boolProperty(view, ColumnSpanAll, session); ok && spanAll { session.updateCSSProperty(htmlID, `column-span`, `all`) } else { session.updateCSSProperty(htmlID, `column-span`, `none`) } case Tooltip: if tooltip := GetTooltip(view); tooltip == "" { session.removeProperty(htmlID, "data-tooltip") } else { session.updateProperty(htmlID, "data-tooltip", tooltip) session.updateProperty(htmlID, "onmouseenter", "mouseEnterEvent(this, event)") session.updateProperty(htmlID, "onmouseleave", "mouseLeaveEvent(this, event)") } case PerspectiveOriginX, PerspectiveOriginY: x, y := GetPerspectiveOrigin(view) session.updateCSSProperty(htmlID, "perspective-origin", transformOriginCSS(x, y, AutoSize(), view.Session())) case BackfaceVisible: if GetBackfaceVisible(view) { session.updateCSSProperty(htmlID, string(BackfaceVisible), "visible") } else { session.updateCSSProperty(htmlID, string(BackfaceVisible), "hidden") } case TransformOriginX, TransformOriginY, TransformOriginZ: x, y, z := getTransformOrigin(view, session) session.updateCSSProperty(htmlID, "transform-origin", transformOriginCSS(x, y, z, view.Session())) case Transform: css := "" if transform := getTransformProperty(view, Transform); transform != nil { css = transform.transformCSS(session) } session.updateCSSProperty(htmlID, "transform", css) case Perspective, SkewX, SkewY, TranslateX, TranslateY, TranslateZ, ScaleX, ScaleY, ScaleZ, Rotate, RotateX, RotateY, RotateZ: // do nothing case FocusEvent, LostFocusEvent, ResizeEvent, ScrollEvent, KeyDownEvent, KeyUpEvent, ClickEvent, DoubleClickEvent, MouseDown, MouseUp, MouseMove, MouseOut, MouseOver, ContextMenuEvent, PointerDown, PointerUp, PointerMove, PointerOut, PointerOver, PointerCancel, TouchStart, TouchEnd, TouchMove, TouchCancel, TransitionRunEvent, TransitionStartEvent, TransitionEndEvent, TransitionCancelEvent, AnimationStartEvent, AnimationEndEvent, AnimationIterationEvent, AnimationCancelEvent: updateEventListenerHtml(view, tag) case DataList: updateInnerHTML(view.htmlID(), view.Session()) default: if cssTag, ok := sizeProperties[tag]; ok { if size, ok := sizeProperty(view, tag, session); ok { session.updateCSSProperty(htmlID, cssTag, size.cssString("", session)) } else { session.updateCSSProperty(htmlID, cssTag, "") } return } colorTags := map[PropertyName]string{ BackgroundColor: string(BackgroundColor), TextColor: "color", TextLineColor: "text-decoration-color", CaretColor: string(CaretColor), AccentColor: string(AccentColor), } if cssTag, ok := colorTags[tag]; ok { if color, ok := colorProperty(view, tag, session); ok { session.updateCSSProperty(htmlID, cssTag, color.cssString()) } else { session.updateCSSProperty(htmlID, cssTag, "") } return } if valuesData, ok := enumProperties[tag]; ok && valuesData.cssTag != "" { if n, ok := enumProperty(view, tag, session, 0); ok { session.updateCSSProperty(htmlID, valuesData.cssTag, valuesData.cssValues[n]) } else { session.updateCSSProperty(htmlID, valuesData.cssTag, "") } return } if f, ok := floatTextProperty(view, Opacity, session, 0); ok { session.updateCSSProperty(htmlID, string(Opacity), f) } else { session.updateCSSProperty(htmlID, string(Opacity), "") } } } func (view *viewData) htmlTag() string { if semantics := GetSemantics(view); semantics > DefaultSemantics { values := enumProperties[Semantics].cssValues if semantics < len(values) { return values[semantics] } } return "div" } func (view *viewData) closeHTMLTag() bool { return true } func (view *viewData) htmlID() string { if view._htmlID == "" { view._htmlID = view.session.nextViewID() } return view._htmlID } func (view *viewData) htmlSubviews(self View, buffer *strings.Builder) { } func (view *viewData) addToCSSStyle(addCSS map[string]string) { view.addCSS = addCSS } func (view *viewData) cssStyle(self View, builder cssBuilder) { view.viewStyle.cssViewStyle(builder, view.session) /* switch GetVisibility(view) { case Invisible: builder.add(`visibility`, `hidden`) case Gone: builder.add(`display`, `none`) } */ if view.addCSS != nil { for tag, value := range view.addCSS { builder.add(tag, value) } } } func (view *viewData) htmlDisabledProperty() bool { return view.hasHtmlDisabled } func (view *viewData) htmlProperties(self View, buffer *strings.Builder) { view.created = true if IsDisabled(self) { buffer.WriteString(` data-disabled="1"`) if view.hasHtmlDisabled { buffer.WriteString(` disabled`) } } else { buffer.WriteString(` data-disabled="0"`) } if view.frame.Left != 0 || view.frame.Top != 0 || view.frame.Width != 0 || view.frame.Height != 0 { buffer.WriteString(fmt.Sprintf(` data-left="%g" data-top="%g" data-width="%g" data-height="%g"`, view.frame.Left, view.frame.Top, view.frame.Width, view.frame.Height)) } } func viewHTML(view View, buffer *strings.Builder, htmlTag string) { if htmlTag == "" { htmlTag = view.htmlTag() } //viewHTMLTag := view.htmlTag() buffer.WriteRune('<') buffer.WriteString(htmlTag) buffer.WriteString(` id="`) buffer.WriteString(view.htmlID()) buffer.WriteRune('"') disabled := IsDisabled(view) if cls := view.htmlClass(disabled); cls != "" { buffer.WriteString(` class="`) buffer.WriteString(cls) buffer.WriteRune('"') } cssBuilder := viewCSSBuilder{buffer: allocStringBuilder()} view.cssStyle(view, &cssBuilder) if style := cssBuilder.finish(); style != "" { buffer.WriteString(` style="`) buffer.WriteString(style) buffer.WriteRune('"') } buffer.WriteRune(' ') view.htmlProperties(view, buffer) if view.isNoResizeEvent() { buffer.WriteString(` data-noresize="1" `) } else { buffer.WriteRune(' ') } if !disabled { if tabIndex := GetTabIndex(view); tabIndex >= 0 { buffer.WriteString(`tabindex="`) buffer.WriteString(strconv.Itoa(tabIndex)) buffer.WriteString(`" `) } } if tooltip := GetTooltip(view); tooltip != "" { buffer.WriteString(`data-tooltip=" `) buffer.WriteString(tooltip) buffer.WriteString(`" onmouseenter="mouseEnterEvent(this, event)" onmouseleave="mouseLeaveEvent(this, event)" `) } buffer.WriteString(`onscroll="scrollEvent(this, event)" `) focusEventsHtml(view, buffer) keyEventsHtml(view, buffer) viewEventsHtml[MouseEvent](view, []PropertyName{ClickEvent, DoubleClickEvent, MouseDown, MouseUp, MouseMove, MouseOut, MouseOver, ContextMenuEvent}, buffer) //mouseEventsHtml(view, buffer, hasTooltip) viewEventsHtml[PointerEvent](view, []PropertyName{PointerDown, PointerUp, PointerMove, PointerOut, PointerOver, PointerCancel}, buffer) //pointerEventsHtml(view, buffer) viewEventsHtml[TouchEvent](view, []PropertyName{TouchStart, TouchEnd, TouchMove, TouchCancel}, buffer) //touchEventsHtml(view, buffer) viewEventsHtml[string](view, []PropertyName{TransitionRunEvent, TransitionStartEvent, TransitionEndEvent, TransitionCancelEvent, AnimationStartEvent, AnimationEndEvent, AnimationIterationEvent, AnimationCancelEvent}, buffer) //transitionEventsHtml(view, buffer) //animationEventsHtml(view, buffer) buffer.WriteRune('>') view.htmlSubviews(view, buffer) if view.closeHTMLTag() { buffer.WriteString(`') } } func (view *viewData) htmlClass(disabled bool) string { cls := "ruiView" disabledStyle := false if disabled { if value, ok := stringProperty(view, StyleDisabled, view.Session()); ok && value != "" { cls += " " + value disabledStyle = true } } if !disabledStyle { if value, ok := stringProperty(view, Style, view.Session()); ok { cls += " " + value } } if view.systemClass != "" { cls = view.systemClass + " " + cls } return cls } func (view *viewData) handleCommand(self View, command PropertyName, data DataObject) bool { switch command { case KeyDownEvent, KeyUpEvent: if !IsDisabled(self) { handleKeyEvents(self, command, data) } case ClickEvent, DoubleClickEvent, MouseDown, MouseUp, MouseMove, MouseOut, MouseOver, ContextMenuEvent: handleMouseEvents(self, command, data) case PointerDown, PointerUp, PointerMove, PointerOut, PointerOver, PointerCancel: handlePointerEvents(self, command, data) case TouchStart, TouchEnd, TouchMove, TouchCancel: handleTouchEvents(self, command, data) case FocusEvent: view.hasFocus = true for _, listener := range getNoArgEventListeners[View](view, nil, command) { listener(self) } case LostFocusEvent: view.hasFocus = false for _, listener := range getNoArgEventListeners[View](view, nil, command) { listener(self) } case TransitionRunEvent, TransitionStartEvent, TransitionEndEvent, TransitionCancelEvent: view.handleTransitionEvents(command, data) case AnimationStartEvent, AnimationEndEvent, AnimationIterationEvent, AnimationCancelEvent: view.handleAnimationEvents(command, data) case "scroll": view.onScroll(view, dataFloatProperty(data, "x"), dataFloatProperty(data, "y"), dataFloatProperty(data, "width"), dataFloatProperty(data, "height")) case "widthChanged": if value, ok := data.PropertyValue("width"); ok { if width, ok := StringToSizeUnit(value); ok { self.setRaw(Width, width) } } case "heightChanged": if value, ok := data.PropertyValue("height"); ok { if height, ok := StringToSizeUnit(value); ok { self.setRaw(Height, height) } } /* case "resize": floatProperty := func(tag string) float64 { if value, ok := data.PropertyValue(tag); ok { if result, err := strconv.ParseFloat(value, 64); err == nil { return result } } return 0 } self.onResize(self, floatProperty("x"), floatProperty("y"), floatProperty("width"), floatProperty("height")) return true */ default: return false } return true } func (view *viewData) SetChangeListener(tag PropertyName, listener func(View, PropertyName)) { if listener == nil { delete(view.changeListener, tag) } else { view.changeListener[tag] = listener } } func (view *viewData) HasFocus() bool { return view.hasFocus } func (view *viewData) String() string { buffer := allocStringBuilder() defer freeStringBuilder(buffer) writeViewStyle(view.tag, view, buffer, "", nil) return buffer.String() } func (view *viewData) exscludeTags() []PropertyName { return nil }