rui_orig/view.go

1035 lines
29 KiB
Go
Raw Normal View History

2021-09-07 17:36:50 +03:00
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
2021-09-07 17:36:50 +03:00
type View interface {
2022-05-22 12:54:02 +03:00
ViewStyle
2021-09-07 17:36:50 +03:00
fmt.Stringer
// Session returns the current Session interface
Session() Session
2024-04-23 19:34:36 +03:00
2021-09-07 17:36:50 +03:00
// Parent returns the parent view
Parent() View
2024-04-23 19:34:36 +03:00
2021-09-07 17:36:50 +03:00
// Tag returns the tag of View interface
Tag() string
2024-04-23 19:34:36 +03:00
2021-09-07 17:36:50 +03:00
// ID returns the id of the view
ID() string
2024-04-23 19:34:36 +03:00
2021-09-07 17:36:50 +03:00
// Focusable returns true if the view receives the focus
Focusable() bool
2024-04-23 19:34:36 +03:00
2021-09-07 17:36:50 +03:00
// Frame returns the location and size of the view in pixels
Frame() Frame
2024-04-23 19:34:36 +03:00
2022-11-23 15:10:29 +03:00
// Scroll returns the location size of the scrollable view in pixels
2021-09-07 17:36:50 +03:00
Scroll() Frame
2024-04-23 19:34:36 +03:00
2024-09-25 13:45:47 +03:00
// 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
2021-09-07 17:36:50 +03:00
// 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
2024-11-13 12:56:39 +03:00
SetAnimated(tag PropertyName, value any, animation Animation) bool
2024-04-23 19:34:36 +03:00
// SetChangeListener set the function to track the change of the View property
2024-11-13 12:56:39 +03:00
SetChangeListener(tag PropertyName, listener func(View, PropertyName))
2024-04-23 19:34:36 +03:00
// HasFocus returns 'true' if the view has focus
HasFocus() bool
2024-11-13 12:56:39 +03:00
init(session Session)
handleCommand(self View, command PropertyName, data DataObject) bool
2021-09-07 17:36:50 +03:00
htmlClass(disabled bool) string
htmlTag() string
closeHTMLTag() bool
htmlID() string
parentHTMLID() string
setParentID(parentID string)
2021-09-07 17:36:50 +03:00
htmlSubviews(self View, buffer *strings.Builder)
htmlProperties(self View, buffer *strings.Builder)
cssStyle(self View, builder cssBuilder)
addToCSSStyle(addCSS map[string]string)
2024-11-13 12:56:39 +03:00
exscludeTags() []PropertyName
htmlDisabledProperty() bool
2021-09-07 17:36:50 +03:00
onResize(self View, x, y, width, height float64)
onItemResize(self View, index string, x, y, width, height float64)
2021-09-07 17:36:50 +03:00
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
2024-11-13 12:56:39 +03:00
changeListener map[PropertyName]func(View, PropertyName)
singleTransition map[PropertyName]Animation
addCSS map[string]string
frame Frame
scroll Frame
noResizeEvent bool
created bool
hasFocus bool
2024-04-23 18:24:51 +03:00
hasHtmlDisabled bool
get func(tag PropertyName) any
set func(tag PropertyName, value any) []PropertyName
remove func(tag PropertyName) []PropertyName
changed func(tag PropertyName)
2021-09-07 17:36:50 +03:00
}
func newView(session Session) View {
2024-11-13 12:56:39 +03:00
return new(viewData)
}
// NewView create new View object and return it
func NewView(session Session, params Params) View {
2021-09-07 17:36:50 +03:00
view := new(viewData)
view.init(session)
2024-11-13 12:56:39 +03:00
setInitParams(view, params)
2021-09-07 17:36:50 +03:00
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) {
2021-09-07 17:36:50 +03:00
view.viewStyle.init()
view.get = view.getFunc
view.set = view.setFunc
view.remove = view.removeFunc
2024-11-13 12:56:39 +03:00
view.normalize = normalizeViewTag
view.changed = view.propertyChanged
2021-09-07 17:36:50 +03:00
view.tag = "View"
view.session = session
2024-11-13 12:56:39 +03:00
view.changeListener = map[PropertyName]func(View, PropertyName){}
2021-09-07 17:36:50 +03:00
view.addCSS = map[string]string{}
//view.animation = map[string]AnimationEndListener{}
2024-11-13 12:56:39 +03:00
view.singleTransition = map[PropertyName]Animation{}
2021-09-07 17:36:50 +03:00
view.noResizeEvent = false
view.created = false
2024-04-23 18:24:51 +03:00
view.hasHtmlDisabled = false
2021-09-07 17:36:50 +03:00
}
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
}
2023-05-02 14:49:35 +03:00
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
}
}
}
}
2021-09-07 17:36:50 +03:00
return false
}
2024-11-13 12:56:39 +03:00
func (view *viewData) Remove(tag PropertyName) {
changedTags := view.removeFunc(view.normalize(tag))
2021-09-07 17:36:50 +03:00
2024-11-13 12:56:39 +03:00
if view.created && len(changedTags) > 0 {
for _, tag := range changedTags {
view.changed(tag)
2024-11-13 12:56:39 +03:00
}
2021-09-07 17:36:50 +03:00
2024-11-13 12:56:39 +03:00
for _, tag := range changedTags {
if listener, ok := view.changeListener[tag]; ok {
listener(view, tag)
}
}
}
}
2021-09-07 17:36:50 +03:00
func (view *viewData) Get(tag PropertyName) any {
switch tag {
case ID:
return view.ID()
}
return view.get(view.normalize(tag))
}
2024-11-13 12:56:39 +03:00
func (view *viewData) Set(tag PropertyName, value any) bool {
if value == nil {
view.Remove(tag)
return true
}
2021-09-07 17:36:50 +03:00
2024-11-13 12:56:39 +03:00
tag = view.normalize(tag)
changedTags := view.set(tag, value)
2024-11-13 12:56:39 +03:00
if view.created && len(changedTags) > 0 {
for _, tag := range changedTags {
view.changed(tag)
2024-11-13 12:56:39 +03:00
}
for _, tag := range changedTags {
if listener, ok := view.changeListener[tag]; ok {
listener(view, tag)
}
}
}
2024-11-13 12:56:39 +03:00
return changedTags != nil
}
2024-11-13 12:56:39 +03:00
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)
2021-09-07 17:36:50 +03:00
}
return changedTags
2021-09-07 17:36:50 +03:00
}
func (view *viewData) setFunc(tag PropertyName, value any) []PropertyName {
2021-09-07 17:36:50 +03:00
switch tag {
2024-07-01 19:17:03 +03:00
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}
2022-12-20 18:38:39 +03:00
case TabIndex, "tab-index":
2024-11-13 12:56:39 +03:00
return setIntProperty(view, TabIndex, value)
2022-12-20 18:38:39 +03:00
case UserData:
2024-11-13 12:56:39 +03:00
view.setRaw(tag, value)
return []PropertyName{UserData}
2021-09-07 17:36:50 +03:00
case Style, StyleDisabled:
2024-11-13 12:56:39 +03:00
if text, ok := value.(string); ok {
view.setRaw(tag, text)
return []PropertyName{tag}
2021-09-07 17:36:50 +03:00
}
2024-11-13 12:56:39 +03:00
notCompatibleType(ID, value)
return nil
2021-09-07 17:36:50 +03:00
case FocusEvent, LostFocusEvent:
return setNoArgEventListener[View](view, tag, value)
2021-09-07 17:36:50 +03:00
case KeyDownEvent, KeyUpEvent:
return setOneArgEventListener[View, KeyEvent](view, tag, value)
2021-09-07 17:36:50 +03:00
case ClickEvent, DoubleClickEvent, MouseDown, MouseUp, MouseMove, MouseOut, MouseOver, ContextMenuEvent:
return setOneArgEventListener[View, MouseEvent](view, tag, value)
2021-09-07 17:36:50 +03:00
case PointerDown, PointerUp, PointerMove, PointerOut, PointerOver, PointerCancel:
return setOneArgEventListener[View, PointerEvent](view, tag, value)
2021-09-07 17:36:50 +03:00
case TouchStart, TouchEnd, TouchMove, TouchCancel:
return setOneArgEventListener[View, TouchEvent](view, tag, value)
2021-09-07 17:36:50 +03:00
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)
2021-09-07 17:36:50 +03:00
case ResizeEvent, ScrollEvent:
return setOneArgEventListener[View, Frame](view, tag, value)
2021-09-07 17:36:50 +03:00
}
2024-11-13 12:56:39 +03:00
return viewStyleSet(view, tag, value)
2021-09-07 17:36:50 +03:00
}
2024-09-25 13:45:47 +03:00
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) {
2021-09-07 17:36:50 +03:00
htmlID := view.htmlID()
2024-11-13 12:56:39 +03:00
session := view.Session()
2021-09-07 17:36:50 +03:00
switch tag {
2024-11-13 12:56:39 +03:00
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)))
2021-09-07 17:36:50 +03:00
case Disabled:
2024-04-23 18:24:51 +03:00
tabIndex := GetTabIndex(view, htmlID)
enabledClass := view.htmlClass(false)
disabledClass := view.htmlClass(true)
session.startUpdateScript(htmlID)
if IsDisabled(view) {
session.updateProperty(htmlID, "data-disabled", "1")
2024-11-13 12:56:39 +03:00
if view.htmlDisabledProperty() {
2024-04-23 18:24:51 +03:00
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")
2024-11-13 12:56:39 +03:00
if view.htmlDisabledProperty() {
2024-04-23 18:24:51 +03:00
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)
2022-03-31 19:59:10 +03:00
case Visibility:
switch GetVisibility(view) {
2022-03-31 19:59:10 +03:00
case Invisible:
2024-11-13 12:56:39 +03:00
session.updateCSSProperty(htmlID, string(Visibility), "hidden")
2022-10-30 17:22:33 +03:00
session.updateCSSProperty(htmlID, "display", "")
2023-05-07 20:58:51 +03:00
session.callFunc("hideTooltip")
2022-03-31 19:59:10 +03:00
case Gone:
2024-11-13 12:56:39 +03:00
session.updateCSSProperty(htmlID, string(Visibility), "hidden")
2022-10-30 17:22:33 +03:00
session.updateCSSProperty(htmlID, "display", "none")
2023-05-07 20:58:51 +03:00
session.callFunc("hideTooltip")
2022-03-31 19:59:10 +03:00
default:
2024-11-13 12:56:39 +03:00
session.updateCSSProperty(htmlID, string(Visibility), "visible")
2022-10-30 17:22:33 +03:00
session.updateCSSProperty(htmlID, "display", "")
2022-03-31 19:59:10 +03:00
}
2021-09-07 17:36:50 +03:00
case Background:
2024-11-13 12:56:39 +03:00
session.updateCSSProperty(htmlID, string(Background), backgroundCSS(view, session))
2021-09-07 17:36:50 +03:00
2024-11-13 12:56:39 +03:00
case Border, BorderLeft, BorderRight, BorderTop, BorderBottom:
cssWidth := ""
cssColor := ""
cssStyle := "none"
2021-09-07 17:36:50 +03:00
2024-11-13 12:56:39 +03:00
if border := getBorderProperty(view, Border); border != nil {
cssWidth = border.cssWidthValue(session)
cssColor = border.cssColorValue(session)
cssStyle = border.cssStyleValue(session)
2021-09-07 17:36:50 +03:00
}
2024-11-13 12:56:39 +03:00
session.updateCSSProperty(htmlID, string(BorderWidth), cssWidth)
session.updateCSSProperty(htmlID, string(BorderColor), cssColor)
session.updateCSSProperty(htmlID, string(BorderStyle), cssStyle)
2021-09-07 17:36:50 +03:00
case BorderStyle, BorderLeftStyle, BorderRightStyle, BorderTopStyle, BorderBottomStyle:
2024-11-13 12:56:39 +03:00
if border := getBorderProperty(view, Border); border != nil {
session.updateCSSProperty(htmlID, string(BorderStyle), border.cssStyleValue(session))
2021-09-07 17:36:50 +03:00
}
case BorderColor, BorderLeftColor, BorderRightColor, BorderTopColor, BorderBottomColor:
2024-11-13 12:56:39 +03:00
if border := getBorderProperty(view, Border); border != nil {
session.updateCSSProperty(htmlID, string(BorderColor), border.cssColorValue(session))
2021-09-07 17:36:50 +03:00
}
case BorderWidth, BorderLeftWidth, BorderRightWidth, BorderTopWidth, BorderBottomWidth:
2024-11-13 12:56:39 +03:00
if border := getBorderProperty(view, Border); border != nil {
session.updateCSSProperty(htmlID, string(BorderWidth), border.cssWidthValue(session))
2021-09-07 17:36:50 +03:00
}
case Outline, OutlineColor, OutlineStyle, OutlineWidth:
2024-11-13 12:56:39 +03:00
session.updateCSSProperty(htmlID, string(Outline), GetOutline(view).cssString(session))
2021-09-07 17:36:50 +03:00
case Shadow:
2022-10-30 17:22:33 +03:00
session.updateCSSProperty(htmlID, "box-shadow", shadowCSS(view, Shadow, session))
2021-09-07 17:36:50 +03:00
case TextShadow:
2022-10-30 17:22:33 +03:00
session.updateCSSProperty(htmlID, "text-shadow", shadowCSS(view, TextShadow, session))
2021-09-07 17:36:50 +03:00
case Radius, RadiusX, RadiusY, RadiusTopLeft, RadiusTopLeftX, RadiusTopLeftY,
RadiusTopRight, RadiusTopRightX, RadiusTopRightY,
RadiusBottomLeft, RadiusBottomLeftX, RadiusBottomLeftY,
RadiusBottomRight, RadiusBottomRightX, RadiusBottomRightY:
radius := GetRadius(view)
2022-10-30 17:22:33 +03:00
session.updateCSSProperty(htmlID, "border-radius", radius.cssString(session))
2021-09-07 17:36:50 +03:00
case Margin, MarginTop, MarginRight, MarginBottom, MarginLeft,
"top-margin", "right-margin", "bottom-margin", "left-margin":
margin := GetMargin(view)
2024-11-13 12:56:39 +03:00
session.updateCSSProperty(htmlID, string(Margin), margin.cssString(session))
2021-09-07 17:36:50 +03:00
case Padding, PaddingTop, PaddingRight, PaddingBottom, PaddingLeft,
"top-padding", "right-padding", "bottom-padding", "left-padding":
padding := GetPadding(view)
2024-11-13 12:56:39 +03:00
session.updateCSSProperty(htmlID, string(Padding), padding.cssString(session))
2021-09-07 17:36:50 +03:00
case AvoidBreak:
if avoid, ok := boolProperty(view, AvoidBreak, session); ok {
if avoid {
2022-10-30 17:22:33 +03:00
session.updateCSSProperty(htmlID, "break-inside", "avoid")
2021-09-07 17:36:50 +03:00
} else {
2022-10-30 17:22:33 +03:00
session.updateCSSProperty(htmlID, "break-inside", "auto")
2021-09-07 17:36:50 +03:00
}
}
case Clip:
if clip := getClipShape(view, Clip, session); clip != nil && clip.valid(session) {
2022-10-30 17:22:33 +03:00
session.updateCSSProperty(htmlID, `clip-path`, clip.cssStyle(session))
2021-09-07 17:36:50 +03:00
} else {
2022-10-30 17:22:33 +03:00
session.updateCSSProperty(htmlID, `clip-path`, "none")
2021-09-07 17:36:50 +03:00
}
case ShapeOutside:
if clip := getClipShape(view, ShapeOutside, session); clip != nil && clip.valid(session) {
2024-11-13 12:56:39 +03:00
session.updateCSSProperty(htmlID, string(ShapeOutside), clip.cssStyle(session))
2021-09-07 17:36:50 +03:00
} else {
2024-11-13 12:56:39 +03:00
session.updateCSSProperty(htmlID, string(ShapeOutside), "none")
2021-09-07 17:36:50 +03:00
}
case Filter:
text := ""
2024-11-13 12:56:39 +03:00
if value := view.getRaw(Filter); value != nil {
2021-09-07 17:36:50 +03:00
if filter, ok := value.(ViewFilter); ok {
text = filter.cssStyle(session)
}
}
2024-11-13 12:56:39 +03:00
session.updateCSSProperty(htmlID, string(Filter), text)
case BackdropFilter:
text := ""
2024-11-13 12:56:39 +03:00
if value := view.getRaw(BackdropFilter); value != nil {
if filter, ok := value.(ViewFilter); ok {
text = filter.cssStyle(session)
}
}
2022-10-31 16:07:59 +03:00
if session.startUpdateScript(htmlID) {
defer session.finishUpdateScript(htmlID)
2022-07-22 13:10:55 +03:00
}
2022-10-30 17:22:33 +03:00
session.updateCSSProperty(htmlID, "-webkit-backdrop-filter", text)
2024-11-13 12:56:39 +03:00
session.updateCSSProperty(htmlID, string(BackdropFilter), text)
2021-09-07 17:36:50 +03:00
case FontName:
if font, ok := stringProperty(view, FontName, session); ok {
2022-10-30 17:22:33 +03:00
session.updateCSSProperty(htmlID, "font-family", font)
2021-09-07 17:36:50 +03:00
} else {
2022-10-30 17:22:33 +03:00
session.updateCSSProperty(htmlID, "font-family", "")
2021-09-07 17:36:50 +03:00
}
case Italic:
if state, ok := boolProperty(view, tag, session); ok {
if state {
2022-10-30 17:22:33 +03:00
session.updateCSSProperty(htmlID, "font-style", "italic")
2021-09-07 17:36:50 +03:00
} else {
2022-10-30 17:22:33 +03:00
session.updateCSSProperty(htmlID, "font-style", "normal")
2021-09-07 17:36:50 +03:00
}
} else {
2022-10-30 17:22:33 +03:00
session.updateCSSProperty(htmlID, "font-style", "")
2021-09-07 17:36:50 +03:00
}
case SmallCaps:
if state, ok := boolProperty(view, tag, session); ok {
if state {
2022-10-30 17:22:33 +03:00
session.updateCSSProperty(htmlID, "font-variant", "small-caps")
2021-09-07 17:36:50 +03:00
} else {
2022-10-30 17:22:33 +03:00
session.updateCSSProperty(htmlID, "font-variant", "normal")
2021-09-07 17:36:50 +03:00
}
} else {
2022-10-30 17:22:33 +03:00
session.updateCSSProperty(htmlID, "font-variant", "")
2021-09-07 17:36:50 +03:00
}
case Strikethrough, Overline, Underline:
2024-11-13 12:56:39 +03:00
session.updateCSSProperty(htmlID, "text-decoration", textDecorationCSS(view, session))
/*
for _, tag2 := range []PropertyName{TextLineColor, TextLineStyle, TextLineThickness} {
view.propertyChanged(tag2)
}
*/
2021-09-07 17:36:50 +03:00
case Transition:
2024-11-13 12:56:39 +03:00
session.updateCSSProperty(htmlID, "transition", transitionCSS(view, session))
case AnimationTag:
2024-11-13 12:56:39 +03:00
session.updateCSSProperty(htmlID, "animation", animationCSS(view, session))
case AnimationPaused:
paused, ok := boolProperty(view, AnimationPaused, session)
if !ok {
2022-10-30 17:22:33 +03:00
session.updateCSSProperty(htmlID, `animation-play-state`, ``)
} else if paused {
2022-10-30 17:22:33 +03:00
session.updateCSSProperty(htmlID, `animation-play-state`, `paused`)
} else {
2022-10-30 17:22:33 +03:00
session.updateCSSProperty(htmlID, `animation-play-state`, `running`)
}
2022-04-21 18:22:17 +03:00
2022-12-18 18:37:36 +03:00
case ZIndex, Order, TabSize:
2022-08-20 20:05:56 +03:00
if i, ok := intProperty(view, tag, session, 0); ok {
2024-11-13 12:56:39 +03:00
session.updateCSSProperty(htmlID, string(tag), strconv.Itoa(i))
} else {
2024-11-13 12:56:39 +03:00
session.updateCSSProperty(htmlID, string(tag), "")
2022-04-21 18:22:17 +03:00
}
2022-05-03 12:12:57 +03:00
case Row, Column:
2022-10-30 17:22:33 +03:00
if parentID := view.parentHTMLID(); parentID != "" {
updateInnerHTML(parentID, session)
2022-05-03 12:12:57 +03:00
}
case UserSelect:
2022-10-31 16:07:59 +03:00
if session.startUpdateScript(htmlID) {
defer session.finishUpdateScript(htmlID)
2022-07-22 13:10:55 +03:00
}
if userSelect, ok := boolProperty(view, UserSelect, session); ok {
if userSelect {
2022-10-30 17:22:33 +03:00
session.updateCSSProperty(htmlID, "-webkit-user-select", "auto")
session.updateCSSProperty(htmlID, "user-select", "auto")
} else {
2022-10-30 17:22:33 +03:00
session.updateCSSProperty(htmlID, "-webkit-user-select", "none")
session.updateCSSProperty(htmlID, "user-select", "none")
}
} else {
2022-10-30 17:22:33 +03:00
session.updateCSSProperty(htmlID, "-webkit-user-select", "")
session.updateCSSProperty(htmlID, "user-select", "")
}
2023-01-03 14:56:57 +03:00
case ColumnSpanAll:
if spanAll, ok := boolProperty(view, ColumnSpanAll, session); ok && spanAll {
session.updateCSSProperty(htmlID, `column-span`, `all`)
} else {
session.updateCSSProperty(htmlID, `column-span`, `none`)
}
2023-04-25 17:20:47 +03:00
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)")
}
2021-09-07 17:36:50 +03:00
2024-11-13 12:56:39 +03:00
case PerspectiveOriginX, PerspectiveOriginY:
x, y := GetPerspectiveOrigin(view)
session.updateCSSProperty(htmlID, "perspective-origin", transformOriginCSS(x, y, AutoSize(), view.Session()))
2021-09-07 17:36:50 +03:00
2024-11-13 12:56:39 +03:00
case BackfaceVisible:
if GetBackfaceVisible(view) {
session.updateCSSProperty(htmlID, string(BackfaceVisible), "visible")
} else {
session.updateCSSProperty(htmlID, string(BackfaceVisible), "hidden")
2021-09-07 17:36:50 +03:00
}
case TransformOriginX, TransformOriginY, TransformOriginZ:
x, y, z := getTransformOrigin(view, session)
session.updateCSSProperty(htmlID, "transform-origin", transformOriginCSS(x, y, z, view.Session()))
2024-11-13 12:56:39 +03:00
case Transform, Perspective, SkewX, SkewY, TranslateX, TranslateY, TranslateZ,
2024-11-13 12:56:39 +03:00
ScaleX, ScaleY, ScaleZ, Rotate, RotateX, RotateY, RotateZ:
css := ""
if transform := getTransformProperty(view); transform != nil {
css = transform.transformCSS(session)
}
session.updateCSSProperty(htmlID, "transform", css)
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())
2021-09-07 17:36:50 +03:00
2024-11-13 12:56:39 +03:00
default:
if cssTag, ok := sizeProperties[tag]; ok {
if size, ok := sizeProperty(view, tag, session); ok {
session.updateCSSProperty(htmlID, cssTag, size.cssString("", session))
} else {
2024-11-13 12:56:39 +03:00
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, "")
2021-09-07 17:36:50 +03:00
}
return
}
2024-11-13 12:56:39 +03:00
if f, ok := floatTextProperty(view, Opacity, session, 0); ok {
session.updateCSSProperty(htmlID, string(Opacity), f)
} else {
session.updateCSSProperty(htmlID, string(Opacity), "")
}
}
2021-09-07 17:36:50 +03:00
}
func (view *viewData) htmlTag() string {
if semantics := GetSemantics(view); semantics > DefaultSemantics {
2021-09-07 17:36:50 +03:00
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)
2024-06-19 16:36:50 +03:00
/*
switch GetVisibility(view) {
case Invisible:
builder.add(`visibility`, `hidden`)
2021-09-07 17:36:50 +03:00
2024-06-19 16:36:50 +03:00
case Gone:
builder.add(`display`, `none`)
}
*/
2021-09-07 17:36:50 +03:00
if view.addCSS != nil {
for tag, value := range view.addCSS {
builder.add(tag, value)
}
}
}
2024-11-13 12:56:39 +03:00
func (view *viewData) htmlDisabledProperty() bool {
return view.hasHtmlDisabled
}
2021-09-07 17:36:50 +03:00
func (view *viewData) htmlProperties(self View, buffer *strings.Builder) {
view.created = true
if IsDisabled(self) {
2021-09-07 17:36:50 +03:00
buffer.WriteString(` data-disabled="1"`)
2024-04-23 18:24:51 +03:00
if view.hasHtmlDisabled {
buffer.WriteString(` disabled`)
}
2021-09-07 17:36:50 +03:00
} else {
buffer.WriteString(` data-disabled="0"`)
}
2024-04-23 18:24:51 +03:00
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))
}
2021-09-07 17:36:50 +03:00
}
2024-11-21 09:25:46 +03:00
func viewHTML(view View, buffer *strings.Builder, htmlTag string) {
if htmlTag == "" {
htmlTag = view.htmlTag()
}
//viewHTMLTag := view.htmlTag()
2021-09-07 17:36:50 +03:00
buffer.WriteRune('<')
2024-11-21 09:25:46 +03:00
buffer.WriteString(htmlTag)
2021-09-07 17:36:50 +03:00
buffer.WriteString(` id="`)
buffer.WriteString(view.htmlID())
buffer.WriteRune('"')
disabled := IsDisabled(view)
2021-09-07 17:36:50 +03:00
if cls := view.htmlClass(disabled); cls != "" {
buffer.WriteString(` class="`)
buffer.WriteString(cls)
buffer.WriteRune('"')
}
2022-10-29 20:16:40 +03:00
cssBuilder := viewCSSBuilder{buffer: allocStringBuilder()}
2021-09-07 17:36:50 +03:00
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(' ')
}
2022-12-20 18:38:39 +03:00
if !disabled {
2024-04-23 18:24:51 +03:00
if tabIndex := GetTabIndex(view); tabIndex >= 0 {
2022-12-20 18:38:39 +03:00
buffer.WriteString(`tabindex="`)
2024-04-23 18:24:51 +03:00
buffer.WriteString(strconv.Itoa(tabIndex))
2022-12-20 18:38:39 +03:00
buffer.WriteString(`" `)
}
2021-09-07 17:36:50 +03:00
}
2023-04-25 17:20:47 +03:00
if tooltip := GetTooltip(view); tooltip != "" {
buffer.WriteString(`data-tooltip=" `)
buffer.WriteString(tooltip)
2024-11-13 12:56:39 +03:00
buffer.WriteString(`" onmouseenter="mouseEnterEvent(this, event)" onmouseleave="mouseLeaveEvent(this, event)" `)
2023-04-25 17:20:47 +03:00
}
2021-09-07 17:36:50 +03:00
buffer.WriteString(`onscroll="scrollEvent(this, event)" `)
focusEventsHtml(view, buffer)
2024-11-13 12:56:39 +03:00
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)
2021-09-07 17:36:50 +03:00
buffer.WriteRune('>')
view.htmlSubviews(view, buffer)
if view.closeHTMLTag() {
buffer.WriteString(`</`)
2024-11-21 09:25:46 +03:00
buffer.WriteString(htmlTag)
2021-09-07 17:36:50 +03:00
buffer.WriteRune('>')
}
}
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
}
2024-11-13 12:56:39 +03:00
func (view *viewData) handleCommand(self View, command PropertyName, data DataObject) bool {
2021-09-07 17:36:50 +03:00
switch command {
case KeyDownEvent, KeyUpEvent:
if !IsDisabled(self) {
2021-09-07 17:36:50 +03:00
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) {
2021-09-07 17:36:50 +03:00
listener(self)
}
case TransitionRunEvent, TransitionStartEvent, TransitionEndEvent, TransitionCancelEvent:
view.handleTransitionEvents(command, data)
case AnimationStartEvent, AnimationEndEvent, AnimationIterationEvent, AnimationCancelEvent:
view.handleAnimationEvents(command, data)
2021-09-07 17:36:50 +03:00
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
}
2024-11-13 12:56:39 +03:00
func (view *viewData) SetChangeListener(tag PropertyName, listener func(View, PropertyName)) {
if listener == nil {
delete(view.changeListener, tag)
} else {
view.changeListener[tag] = listener
2021-09-07 17:36:50 +03:00
}
}
func (view *viewData) HasFocus() bool {
return view.hasFocus
}
2022-05-22 12:54:02 +03:00
func (view *viewData) String() string {
2024-11-13 12:56:39 +03:00
buffer := allocStringBuilder()
defer freeStringBuilder(buffer)
writeViewStyle(view.tag, view, buffer, "", nil)
return buffer.String()
}
2024-11-13 12:56:39 +03:00
func (view *viewData) exscludeTags() []PropertyName {
return nil
2022-05-22 12:54:02 +03:00
}