mirror of https://github.com/anoshenko/rui.git
Added PropertyName type
This commit is contained in:
parent
8fcc52de63
commit
e2775d52f2
|
@ -425,7 +425,7 @@ View имеет ряд свойств, таких как высота, шири
|
|||
(View реализует данный интерфейс):
|
||||
|
||||
type Properties interface {
|
||||
Get(tag string) any
|
||||
Get(tag PropertyName) any
|
||||
Set(tag string, value any) bool
|
||||
Remove(tag string)
|
||||
Clear()
|
||||
|
|
|
@ -429,7 +429,7 @@ View has a number of properties like height, width, color, text parameters, etc.
|
|||
The Properties interface is used to read and write the property value (View implements this interface):
|
||||
|
||||
type Properties interface {
|
||||
Get(tag string) any
|
||||
Get(tag PropertyName) any
|
||||
Set(tag string, value any) bool
|
||||
Remove(tag string)
|
||||
Clear()
|
||||
|
|
|
@ -20,7 +20,8 @@ func NewAbsoluteLayout(session Session, params Params) AbsoluteLayout {
|
|||
}
|
||||
|
||||
func newAbsoluteLayout(session Session) View {
|
||||
return NewAbsoluteLayout(session, nil)
|
||||
//return NewAbsoluteLayout(session, nil)
|
||||
return new(absoluteLayoutData)
|
||||
}
|
||||
|
||||
// Init initialize fields of ViewsContainer by default values
|
||||
|
|
324
animation.go
324
animation.go
|
@ -18,7 +18,7 @@ const (
|
|||
//
|
||||
// Internal type is `[]Animation`, other types converted to it during assignment.
|
||||
// See `Animation` description for more details.
|
||||
AnimationTag = "animation"
|
||||
AnimationTag PropertyName = "animation"
|
||||
|
||||
// AnimationPaused is the constant for "animation-paused" property tag.
|
||||
//
|
||||
|
@ -30,21 +30,21 @@ const (
|
|||
// Values:
|
||||
// `true` or `1` or "true", "yes", "on", "1" - Animation is paused.
|
||||
// `false` or `0` or "false", "no", "off", "0" - Animation is playing.
|
||||
AnimationPaused = "animation-paused"
|
||||
AnimationPaused PropertyName = "animation-paused"
|
||||
|
||||
// Transition is the constant for "transition" property tag.
|
||||
//
|
||||
// Used by `View`.
|
||||
// Sets transition animation of view properties. Each provided property must contain `Animation` which describe how
|
||||
// particular property will be animated on property value change. Transition animation can be applied to properties of the
|
||||
// type `SizeUnit`, `Color`, `AngleUnit`, `float64` and composite properties that contain elements of the listed types(for
|
||||
// example, "shadow", "border", etc.). If we'll try to animate other properties with internal type like `bool` or
|
||||
// Sets transition animation of view properties. Each provided property must contain `Animation` which describe how
|
||||
// particular property will be animated on property value change. Transition animation can be applied to properties of the
|
||||
// type `SizeUnit`, `Color`, `AngleUnit`, `float64` and composite properties that contain elements of the listed types(for
|
||||
// example, "shadow", "border", etc.). If we'll try to animate other properties with internal type like `bool` or
|
||||
// `string` no error will occur, simply there will be no animation.
|
||||
//
|
||||
// Supported types: `Params`.
|
||||
//
|
||||
// See `Params` description for more details.
|
||||
Transition = "transition"
|
||||
Transition PropertyName = "transition"
|
||||
|
||||
// PropertyTag is the constant for "property" property tag.
|
||||
//
|
||||
|
@ -55,7 +55,7 @@ const (
|
|||
//
|
||||
// Internal type is `[]AnimatedProperty`, other types converted to it during assignment.
|
||||
// See `AnimatedProperty` description for more details.
|
||||
PropertyTag = "property"
|
||||
PropertyTag PropertyName = "property"
|
||||
|
||||
// Duration is the constant for "duration" property tag.
|
||||
//
|
||||
|
@ -65,19 +65,19 @@ const (
|
|||
// Supported types: `float`, `int`, `string`.
|
||||
//
|
||||
// Internal type is `float`, other types converted to it during assignment.
|
||||
Duration = "duration"
|
||||
Duration PropertyName = "duration"
|
||||
|
||||
// Delay is the constant for "delay" property tag.
|
||||
//
|
||||
// Used by `Animation`.
|
||||
// Specifies the amount of time in seconds to wait from applying the animation to an element before beginning to perform
|
||||
// the animation. The animation can start later, immediately from its beginning or immediately and partway through the
|
||||
// Specifies the amount of time in seconds to wait from applying the animation to an element before beginning to perform
|
||||
// the animation. The animation can start later, immediately from its beginning or immediately and partway through the
|
||||
// animation.
|
||||
//
|
||||
// Supported types: `float`, `int`, `string`.
|
||||
//
|
||||
// Internal type is `float`, other types converted to it during assignment.
|
||||
Delay = "delay"
|
||||
Delay PropertyName = "delay"
|
||||
|
||||
// TimingFunction is the constant for "timing-function" property tag.
|
||||
//
|
||||
|
@ -92,7 +92,7 @@ const (
|
|||
// "ease-out"(`EaseOutTiming`) - Speed is fast at first, but decreases in the end.
|
||||
// "ease-in-out"(`EaseInOutTiming`) - Speed is slow at first, but quickly increases and at the end it decreases again.
|
||||
// "linear"(`LinearTiming`) - Constant speed.
|
||||
TimingFunction = "timing-function"
|
||||
TimingFunction PropertyName = "timing-function"
|
||||
|
||||
// IterationCount is the constant for "iteration-count" property tag.
|
||||
//
|
||||
|
@ -102,12 +102,12 @@ const (
|
|||
// Supported types: `int`, `string`.
|
||||
//
|
||||
// Internal type is `int`, other types converted to it during assignment.
|
||||
IterationCount = "iteration-count"
|
||||
IterationCount PropertyName = "iteration-count"
|
||||
|
||||
// AnimationDirection is the constant for "animation-direction" property tag.
|
||||
//
|
||||
// Used by `Animation`.
|
||||
// Whether an animation should play forward, backward, or alternate back and forth between playing the sequence forward
|
||||
// Whether an animation should play forward, backward, or alternate back and forth between playing the sequence forward
|
||||
// and backward. Used only for animation script.
|
||||
//
|
||||
// Supported types: `int`, `string`.
|
||||
|
@ -117,7 +117,7 @@ const (
|
|||
// `1`(`ReverseAnimation`) or "reverse" - The animation plays backwards, from the last position to the first, and then resets to the final position and plays again.
|
||||
// `2`(`AlternateAnimation`) or "alternate" - The animation changes direction in each cycle, that is, in the first cycle, it starts from the start position, reaches the end position, and in the second cycle, it continues from the end position and reaches the start position, and so on.
|
||||
// `3`(`AlternateReverseAnimation`) or "alternate-reverse" - The animation starts playing from the end position and reaches the start position, and in the next cycle, continuing from the start position, it goes to the end position.
|
||||
AnimationDirection = "animation-direction"
|
||||
AnimationDirection PropertyName = "animation-direction"
|
||||
|
||||
// NormalAnimation is value of the "animation-direction" property.
|
||||
// The animation plays forwards each cycle. In other words, each time the animation cycles,
|
||||
|
@ -180,7 +180,7 @@ func CubicBezierTiming(x1, y1, x2, y2 float64) string {
|
|||
// AnimatedProperty describes the change script of one property
|
||||
type AnimatedProperty struct {
|
||||
// Tag is the name of the property
|
||||
Tag string
|
||||
Tag PropertyName
|
||||
// From is the initial value of the property
|
||||
From any
|
||||
// To is the final value of the property
|
||||
|
@ -190,12 +190,12 @@ type AnimatedProperty struct {
|
|||
}
|
||||
|
||||
type animationData struct {
|
||||
propertyList
|
||||
dataProperty
|
||||
keyFramesName string
|
||||
usageCounter int
|
||||
view View
|
||||
listener func(view View, animation Animation, event string)
|
||||
oldListeners map[string][]func(View, string)
|
||||
listener func(view View, animation Animation, event PropertyName)
|
||||
oldListeners map[PropertyName][]func(View, PropertyName)
|
||||
oldAnimation []Animation
|
||||
}
|
||||
|
||||
|
@ -207,7 +207,7 @@ type Animation interface {
|
|||
|
||||
// Start starts the animation for the view specified by the first argument.
|
||||
// The second argument specifies the animation event listener (can be nil)
|
||||
Start(view View, listener func(view View, animation Animation, event string)) bool
|
||||
Start(view View, listener func(view View, animation Animation, event PropertyName)) bool
|
||||
// Stop stops the animation
|
||||
Stop()
|
||||
// Pause pauses the animation
|
||||
|
@ -215,7 +215,7 @@ type Animation interface {
|
|||
// Resume resumes an animation that was stopped using the Pause method
|
||||
Resume()
|
||||
|
||||
writeTransitionString(tag string, buffer *strings.Builder)
|
||||
writeTransitionString(tag PropertyName, buffer *strings.Builder)
|
||||
animationCSS(session Session) string
|
||||
transitionCSS(buffer *strings.Builder, session Session)
|
||||
hasAnimatedProperty() bool
|
||||
|
@ -230,10 +230,11 @@ func parseAnimation(obj DataObject) Animation {
|
|||
|
||||
for i := 0; i < obj.PropertyCount(); i++ {
|
||||
if node := obj.Property(i); node != nil {
|
||||
tag := PropertyName(node.Tag())
|
||||
if node.Type() == TextNode {
|
||||
animation.Set(node.Tag(), node.Text())
|
||||
animation.Set(tag, node.Text())
|
||||
} else {
|
||||
animation.Set(node.Tag(), node)
|
||||
animation.Set(tag, node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -251,6 +252,13 @@ func NewAnimation(params Params) Animation {
|
|||
return animation
|
||||
}
|
||||
|
||||
func (animation *animationData) init() {
|
||||
animation.dataProperty.init()
|
||||
animation.normalize = normalizeAnimationTag
|
||||
animation.set = animationSet
|
||||
animation.supportedProperties = []PropertyName{ID, PropertyTag, Duration, Delay, TimingFunction, IterationCount, AnimationDirection}
|
||||
}
|
||||
|
||||
func (animation *animationData) animatedProperties() []AnimatedProperty {
|
||||
value := animation.getRaw(PropertyTag)
|
||||
if value == nil {
|
||||
|
@ -291,33 +299,28 @@ func (animation *animationData) unused(session Session) {
|
|||
}
|
||||
}
|
||||
|
||||
func (animation *animationData) normalizeTag(tag string) string {
|
||||
tag = strings.ToLower(tag)
|
||||
func normalizeAnimationTag(tag PropertyName) PropertyName {
|
||||
tag = defaultNormalize(tag)
|
||||
if tag == Direction {
|
||||
return AnimationDirection
|
||||
}
|
||||
return tag
|
||||
}
|
||||
|
||||
func (animation *animationData) Set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
animation.Remove(tag)
|
||||
return true
|
||||
}
|
||||
|
||||
switch tag = animation.normalizeTag(tag); tag {
|
||||
func animationSet(properties Properties, tag PropertyName, value any) []PropertyName {
|
||||
switch tag {
|
||||
case ID:
|
||||
if text, ok := value.(string); ok {
|
||||
text = strings.Trim(text, " \t\n\r")
|
||||
if text == "" {
|
||||
delete(animation.properties, tag)
|
||||
properties.setRaw(tag, nil)
|
||||
} else {
|
||||
animation.properties[tag] = text
|
||||
properties.setRaw(tag, text)
|
||||
}
|
||||
return true
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
return nil
|
||||
|
||||
case PropertyTag:
|
||||
switch value := value.(type) {
|
||||
|
@ -340,8 +343,8 @@ func (animation *animationData) Set(tag string, value any) bool {
|
|||
} else if value.To == nil {
|
||||
ErrorLog("AnimatedProperty.To is nil")
|
||||
} else {
|
||||
animation.properties[tag] = []AnimatedProperty{value}
|
||||
return true
|
||||
properties.setRaw(tag, []AnimatedProperty{value})
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
case []AnimatedProperty:
|
||||
|
@ -369,8 +372,8 @@ func (animation *animationData) Set(tag string, value any) bool {
|
|||
}
|
||||
}
|
||||
if len(props) > 0 {
|
||||
animation.properties[tag] = props
|
||||
return true
|
||||
properties.setRaw(tag, props)
|
||||
return []PropertyName{tag}
|
||||
} else {
|
||||
ErrorLog("[]AnimatedProperty is empty")
|
||||
}
|
||||
|
@ -417,8 +420,8 @@ func (animation *animationData) Set(tag string, value any) bool {
|
|||
switch value.Type() {
|
||||
case ObjectNode:
|
||||
if prop, ok := parseObject(value.Object()); ok {
|
||||
animation.properties[tag] = []AnimatedProperty{prop}
|
||||
return true
|
||||
properties.setRaw(tag, []AnimatedProperty{prop})
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
case ArrayNode:
|
||||
|
@ -433,8 +436,8 @@ func (animation *animationData) Set(tag string, value any) bool {
|
|||
}
|
||||
}
|
||||
if len(props) > 0 {
|
||||
animation.properties[tag] = props
|
||||
return true
|
||||
properties.setRaw(tag, props)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
default:
|
||||
|
@ -446,36 +449,28 @@ func (animation *animationData) Set(tag string, value any) bool {
|
|||
}
|
||||
|
||||
case Duration:
|
||||
return animation.setFloatProperty(tag, value, 0, math.MaxFloat64)
|
||||
return setFloatProperty(properties, tag, value, 0, math.MaxFloat64)
|
||||
|
||||
case Delay:
|
||||
return animation.setFloatProperty(tag, value, -math.MaxFloat64, math.MaxFloat64)
|
||||
return setFloatProperty(properties, tag, value, -math.MaxFloat64, math.MaxFloat64)
|
||||
|
||||
case TimingFunction:
|
||||
if text, ok := value.(string); ok {
|
||||
animation.properties[tag] = text
|
||||
return true
|
||||
properties.setRaw(tag, text)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
case IterationCount:
|
||||
return animation.setIntProperty(tag, value)
|
||||
return setIntProperty(properties, tag, value)
|
||||
|
||||
case AnimationDirection:
|
||||
return animation.setEnumProperty(AnimationDirection, value, enumProperties[AnimationDirection].values)
|
||||
return setEnumProperty(properties, AnimationDirection, value, enumProperties[AnimationDirection].values)
|
||||
|
||||
default:
|
||||
ErrorLogF(`The "%s" property is not supported by Animation`, tag)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (animation *animationData) Remove(tag string) {
|
||||
delete(animation.properties, animation.normalizeTag(tag))
|
||||
}
|
||||
|
||||
func (animation *animationData) Get(tag string) any {
|
||||
return animation.getRaw(animation.normalizeTag(tag))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (animation *animationData) String() string {
|
||||
|
@ -488,7 +483,7 @@ func (animation *animationData) String() string {
|
|||
if tag != PropertyTag {
|
||||
if value, ok := animation.properties[tag]; ok && value != nil {
|
||||
buffer.WriteString("\n\t")
|
||||
buffer.WriteString(tag)
|
||||
buffer.WriteString(string(tag))
|
||||
buffer.WriteString(" = ")
|
||||
writePropertyValue(buffer, tag, value, "\t")
|
||||
buffer.WriteRune(',')
|
||||
|
@ -497,7 +492,7 @@ func (animation *animationData) String() string {
|
|||
}
|
||||
|
||||
writeProperty := func(prop AnimatedProperty, indent string) {
|
||||
buffer.WriteString(prop.Tag)
|
||||
buffer.WriteString(string(prop.Tag))
|
||||
buffer.WriteString("{\n")
|
||||
buffer.WriteString(indent)
|
||||
buffer.WriteString("from = ")
|
||||
|
@ -512,7 +507,7 @@ func (animation *animationData) String() string {
|
|||
tag := strconv.Itoa(key) + "%"
|
||||
buffer.WriteString(tag)
|
||||
buffer.WriteString(" = ")
|
||||
writePropertyValue(buffer, tag, value, indent)
|
||||
writePropertyValue(buffer, PropertyName(tag), value, indent)
|
||||
}
|
||||
buffer.WriteString("\n")
|
||||
buffer.WriteString(indent[1:])
|
||||
|
@ -522,7 +517,7 @@ func (animation *animationData) String() string {
|
|||
if props := animation.animatedProperties(); len(props) > 0 {
|
||||
|
||||
buffer.WriteString("\n\t")
|
||||
buffer.WriteString(PropertyTag)
|
||||
buffer.WriteString(string(PropertyTag))
|
||||
buffer.WriteString(" = ")
|
||||
if len(props) > 1 {
|
||||
buffer.WriteString("[\n")
|
||||
|
@ -606,15 +601,15 @@ func (animation *animationData) transitionCSS(buffer *strings.Builder, session S
|
|||
}
|
||||
}
|
||||
|
||||
func (animation *animationData) writeTransitionString(tag string, buffer *strings.Builder) {
|
||||
buffer.WriteString(tag)
|
||||
func (animation *animationData) writeTransitionString(tag PropertyName, buffer *strings.Builder) {
|
||||
buffer.WriteString(string(tag))
|
||||
buffer.WriteString("{")
|
||||
lead := " "
|
||||
|
||||
writeFloatProperty := func(name string) bool {
|
||||
writeFloatProperty := func(name PropertyName) bool {
|
||||
if value := animation.getRaw(name); value != nil {
|
||||
buffer.WriteString(lead)
|
||||
buffer.WriteString(name)
|
||||
buffer.WriteString(string(name))
|
||||
buffer.WriteString(" = ")
|
||||
writePropertyValue(buffer, name, value, "")
|
||||
lead = ", "
|
||||
|
@ -633,7 +628,7 @@ func (animation *animationData) writeTransitionString(tag string, buffer *string
|
|||
if value := animation.getRaw(TimingFunction); value != nil {
|
||||
if timingFunction, ok := value.(string); ok && timingFunction != "" {
|
||||
buffer.WriteString(lead)
|
||||
buffer.WriteString(TimingFunction)
|
||||
buffer.WriteString(string(TimingFunction))
|
||||
buffer.WriteString(" = ")
|
||||
if strings.ContainsAny(timingFunction, " ,()") {
|
||||
buffer.WriteRune('"')
|
||||
|
@ -767,7 +762,7 @@ func (session *sessionData) registerAnimation(props []AnimatedProperty) string {
|
|||
return name
|
||||
}
|
||||
|
||||
func (view *viewData) SetAnimated(tag string, value any, animation Animation) bool {
|
||||
func (view *viewData) SetAnimated(tag PropertyName, value any, animation Animation) bool {
|
||||
if animation == nil {
|
||||
return view.Set(tag, value)
|
||||
}
|
||||
|
@ -779,27 +774,30 @@ func (view *viewData) SetAnimated(tag string, value any, animation Animation) bo
|
|||
session.updateProperty(htmlID, "ontransitionend", "transitionEndEvent(this, event)")
|
||||
session.updateProperty(htmlID, "ontransitioncancel", "transitionCancelEvent(this, event)")
|
||||
|
||||
if prevAnimation, ok := view.transitions[tag]; ok {
|
||||
view.singleTransition[tag] = prevAnimation
|
||||
} else {
|
||||
view.singleTransition[tag] = nil
|
||||
transitions := getTransitionProperty(view)
|
||||
var prevAnimation Animation = nil
|
||||
if transitions != nil {
|
||||
if prev, ok := transitions[tag]; ok {
|
||||
prevAnimation = prev
|
||||
}
|
||||
}
|
||||
view.transitions[tag] = animation
|
||||
view.updateTransitionCSS()
|
||||
view.singleTransition[tag] = prevAnimation
|
||||
setTransition(view, tag, animation)
|
||||
view.session.updateCSSProperty(view.htmlID(), "transition", transitionCSS(view, view.session))
|
||||
|
||||
session.finishUpdateScript(htmlID)
|
||||
|
||||
result := view.Set(tag, value)
|
||||
if !result {
|
||||
delete(view.singleTransition, tag)
|
||||
view.updateTransitionCSS()
|
||||
view.session.updateCSSProperty(view.htmlID(), "transition", transitionCSS(view, view.session))
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (style *viewStyle) animationCSS(session Session) string {
|
||||
if value := style.getRaw(AnimationTag); value != nil {
|
||||
func animationCSS(properties Properties, session Session) string {
|
||||
if value := properties.getRaw(AnimationTag); value != nil {
|
||||
if animations, ok := value.([]Animation); ok {
|
||||
buffer := allocStringBuilder()
|
||||
defer freeStringBuilder(buffer)
|
||||
|
@ -820,78 +818,154 @@ func (style *viewStyle) animationCSS(session Session) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
func (style *viewStyle) transitionCSS(session Session) string {
|
||||
buffer := allocStringBuilder()
|
||||
defer freeStringBuilder(buffer)
|
||||
func transitionCSS(properties Properties, session Session) string {
|
||||
if transitions := getTransitionProperty(properties); len(transitions) > 0 {
|
||||
buffer := allocStringBuilder()
|
||||
defer freeStringBuilder(buffer)
|
||||
|
||||
convert := map[string]string{
|
||||
CellHeight: "grid-template-rows",
|
||||
CellWidth: "grid-template-columns",
|
||||
Row: "grid-row",
|
||||
Column: "grid-column",
|
||||
Clip: "clip-path",
|
||||
Shadow: "box-shadow",
|
||||
ColumnSeparator: "column-rule",
|
||||
FontName: "font",
|
||||
TextSize: "font-size",
|
||||
TextLineThickness: "text-decoration-thickness",
|
||||
}
|
||||
|
||||
for tag, animation := range style.transitions {
|
||||
if buffer.Len() > 0 {
|
||||
buffer.WriteString(", ")
|
||||
convert := map[PropertyName]string{
|
||||
CellHeight: "grid-template-rows",
|
||||
CellWidth: "grid-template-columns",
|
||||
Row: "grid-row",
|
||||
Column: "grid-column",
|
||||
Clip: "clip-path",
|
||||
Shadow: "box-shadow",
|
||||
ColumnSeparator: "column-rule",
|
||||
FontName: "font",
|
||||
TextSize: "font-size",
|
||||
TextLineThickness: "text-decoration-thickness",
|
||||
}
|
||||
|
||||
if cssTag, ok := convert[tag]; ok {
|
||||
buffer.WriteString(cssTag)
|
||||
} else {
|
||||
buffer.WriteString(tag)
|
||||
for tag, animation := range transitions {
|
||||
if buffer.Len() > 0 {
|
||||
buffer.WriteString(", ")
|
||||
}
|
||||
|
||||
if cssTag, ok := convert[tag]; ok {
|
||||
buffer.WriteString(cssTag)
|
||||
} else {
|
||||
buffer.WriteString(string(tag))
|
||||
}
|
||||
animation.transitionCSS(buffer, session)
|
||||
}
|
||||
animation.transitionCSS(buffer, session)
|
||||
return buffer.String()
|
||||
}
|
||||
return buffer.String()
|
||||
return ""
|
||||
}
|
||||
|
||||
/*
|
||||
func (view *viewData) updateTransitionCSS() {
|
||||
view.session.updateCSSProperty(view.htmlID(), "transition", view.transitionCSS(view.session))
|
||||
view.session.updateCSSProperty(view.htmlID(), "transition", transitionCSS(view, view.session))
|
||||
}
|
||||
*/
|
||||
|
||||
func (style *viewStyle) Transition(tag string) Animation {
|
||||
if style.transitions != nil {
|
||||
if anim, ok := style.transitions[tag]; ok {
|
||||
func (style *viewStyle) Transition(tag PropertyName) Animation {
|
||||
if transitions := getTransitionProperty(style); transitions != nil {
|
||||
if anim, ok := transitions[tag]; ok {
|
||||
return anim
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (style *viewStyle) Transitions() map[string]Animation {
|
||||
result := map[string]Animation{}
|
||||
for tag, animation := range style.transitions {
|
||||
func (style *viewStyle) Transitions() map[PropertyName]Animation {
|
||||
result := map[PropertyName]Animation{}
|
||||
for tag, animation := range getTransitionProperty(style) {
|
||||
result[tag] = animation
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (style *viewStyle) SetTransition(tag string, animation Animation) {
|
||||
if animation == nil {
|
||||
delete(style.transitions, tag)
|
||||
} else {
|
||||
style.transitions[tag] = animation
|
||||
func (style *viewStyle) SetTransition(tag PropertyName, animation Animation) {
|
||||
setTransition(style, style.normalize(tag), animation)
|
||||
}
|
||||
|
||||
func (view *viewData) SetTransition(tag PropertyName, animation Animation) {
|
||||
setTransition(view, view.normalize(tag), animation)
|
||||
if view.created {
|
||||
view.session.updateCSSProperty(view.htmlID(), "transition", transitionCSS(view, view.session))
|
||||
}
|
||||
}
|
||||
|
||||
func (view *viewData) SetTransition(tag string, animation Animation) {
|
||||
view.viewStyle.SetTransition(tag, animation)
|
||||
if view.created {
|
||||
view.session.updateCSSProperty(view.htmlID(), "transition", view.transitionCSS(view.session))
|
||||
func setTransition(properties Properties, tag PropertyName, animation Animation) {
|
||||
transitions := getTransitionProperty(properties)
|
||||
|
||||
if animation == nil {
|
||||
if transitions != nil {
|
||||
delete(transitions, tag)
|
||||
if len(transitions) == 0 {
|
||||
properties.setRaw(Transition, nil)
|
||||
}
|
||||
}
|
||||
} else if transitions != nil {
|
||||
transitions[tag] = animation
|
||||
} else {
|
||||
properties.setRaw(Transition, map[PropertyName]Animation{tag: animation})
|
||||
}
|
||||
}
|
||||
|
||||
func getTransitionProperty(properties Properties) map[PropertyName]Animation {
|
||||
if value := properties.getRaw(Transition); value != nil {
|
||||
if transitions, ok := value.(map[PropertyName]Animation); ok {
|
||||
return transitions
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setAnimationProperty(properties Properties, tag PropertyName, value any) bool {
|
||||
|
||||
set := func(animations []Animation) {
|
||||
properties.setRaw(tag, animations)
|
||||
for _, animation := range animations {
|
||||
animation.used()
|
||||
}
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case Animation:
|
||||
set([]Animation{value})
|
||||
return true
|
||||
|
||||
case []Animation:
|
||||
set(value)
|
||||
return true
|
||||
|
||||
case DataObject:
|
||||
if animation := parseAnimation(value); animation.hasAnimatedProperty() {
|
||||
set([]Animation{animation})
|
||||
return true
|
||||
}
|
||||
|
||||
case DataNode:
|
||||
animations := []Animation{}
|
||||
result := true
|
||||
for i := 0; i < value.ArraySize(); i++ {
|
||||
if obj := value.ArrayElement(i).Object(); obj != nil {
|
||||
if anim := parseAnimation(obj); anim.hasAnimatedProperty() {
|
||||
animations = append(animations, anim)
|
||||
} else {
|
||||
result = false
|
||||
}
|
||||
} else {
|
||||
notCompatibleType(tag, value.ArrayElement(i))
|
||||
result = false
|
||||
}
|
||||
}
|
||||
if result && len(animations) > 0 {
|
||||
set(animations)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
|
||||
// SetAnimated sets the property with name "tag" of the "rootView" subview with "viewID" id by value. Result:
|
||||
// true - success,
|
||||
// false - error (incompatible type or invalid format of a string value, see AppLog).
|
||||
func SetAnimated(rootView View, viewID, tag string, value any, animation Animation) bool {
|
||||
func SetAnimated(rootView View, viewID string, tag PropertyName, value any, animation Animation) bool {
|
||||
if view := ViewByID(rootView, viewID); view != nil {
|
||||
return view.SetAnimated(tag, value, animation)
|
||||
}
|
||||
|
@ -906,7 +980,7 @@ func IsAnimationPaused(view View, subviewID ...string) bool {
|
|||
|
||||
// GetTransitions returns the subview transitions. The result is always non-nil.
|
||||
// If the second argument (subviewID) is not specified or it is "" then transitions of the first argument (view) is returned
|
||||
func GetTransitions(view View, subviewID ...string) map[string]Animation {
|
||||
func GetTransitions(view View, subviewID ...string) map[PropertyName]Animation {
|
||||
if len(subviewID) > 0 && subviewID[0] != "" {
|
||||
view = ViewByID(view, subviewID[0])
|
||||
}
|
||||
|
@ -915,12 +989,12 @@ func GetTransitions(view View, subviewID ...string) map[string]Animation {
|
|||
return view.Transitions()
|
||||
}
|
||||
|
||||
return map[string]Animation{}
|
||||
return map[PropertyName]Animation{}
|
||||
}
|
||||
|
||||
// GetTransition returns the subview property transition. If there is no transition for the given property then nil is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then transitions of the first argument (view) is returned
|
||||
func GetTransition(view View, subviewID, tag string) Animation {
|
||||
func GetTransition(view View, subviewID string, tag PropertyName) Animation {
|
||||
if subviewID != "" {
|
||||
view = ViewByID(view, subviewID)
|
||||
}
|
||||
|
@ -934,7 +1008,7 @@ func GetTransition(view View, subviewID, tag string) Animation {
|
|||
|
||||
// AddTransition adds the transition for the subview property.
|
||||
// If the second argument (subviewID) is not specified or it is "" then the transition is added to the first argument (view)
|
||||
func AddTransition(view View, subviewID, tag string, animation Animation) bool {
|
||||
func AddTransition(view View, subviewID string, tag PropertyName, animation Animation) bool {
|
||||
if tag != "" {
|
||||
if subviewID != "" {
|
||||
view = ViewByID(view, subviewID)
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package rui
|
||||
|
||||
import "strings"
|
||||
|
||||
// Constants which describe values for view's animation events properties
|
||||
const (
|
||||
// TransitionRunEvent is the constant for "transition-run-event" property tag.
|
||||
|
@ -20,7 +18,7 @@ const (
|
|||
// `func(view rui.View)`,
|
||||
// `func(propertyName string)`,
|
||||
// `func()`.
|
||||
TransitionRunEvent = "transition-run-event"
|
||||
TransitionRunEvent PropertyName = "transition-run-event"
|
||||
|
||||
// TransitionStartEvent is the constant for "transition-start-event" property tag.
|
||||
//
|
||||
|
@ -38,7 +36,7 @@ const (
|
|||
// `func(view rui.View)`,
|
||||
// `func(propertyName string)`,
|
||||
// `func()`.
|
||||
TransitionStartEvent = "transition-start-event"
|
||||
TransitionStartEvent PropertyName = "transition-start-event"
|
||||
|
||||
// TransitionEndEvent is the constant for "transition-end-event" property tag.
|
||||
//
|
||||
|
@ -56,13 +54,13 @@ const (
|
|||
// `func(view rui.View)`,
|
||||
// `func(propertyName string)`,
|
||||
// `func()`.
|
||||
TransitionEndEvent = "transition-end-event"
|
||||
TransitionEndEvent PropertyName = "transition-end-event"
|
||||
|
||||
// TransitionCancelEvent is the constant for "transition-cancel-event" property tag.
|
||||
//
|
||||
// Used by `View`.
|
||||
// Is fired when a transition is cancelled. The transition is cancelled when: * A new property transition has begun. * The
|
||||
// "visibility" property is set to "gone". * The transition is stopped before it has run to completion, e.g. by moving the
|
||||
// Is fired when a transition is cancelled. The transition is cancelled when: * A new property transition has begun. * The
|
||||
// "visibility" property is set to "gone". * The transition is stopped before it has run to completion, e.g. by moving the
|
||||
// mouse off a hover-transitioning view.
|
||||
//
|
||||
// General listener format:
|
||||
|
@ -76,12 +74,12 @@ const (
|
|||
// `func(view rui.View)`,
|
||||
// `func(propertyName string)`,
|
||||
// `func()`.
|
||||
TransitionCancelEvent = "transition-cancel-event"
|
||||
TransitionCancelEvent PropertyName = "transition-cancel-event"
|
||||
|
||||
// AnimationStartEvent is the constant for "animation-start-event" property tag.
|
||||
//
|
||||
// Used by `View`.
|
||||
// Fired when an animation has started. If there is an "animation-delay", this event will fire once the delay period has
|
||||
// Fired when an animation has started. If there is an "animation-delay", this event will fire once the delay period has
|
||||
// expired.
|
||||
//
|
||||
// General listener format:
|
||||
|
@ -95,12 +93,12 @@ const (
|
|||
// `func(view rui.View)`,
|
||||
// `func(animationId string)`,
|
||||
// `func()`.
|
||||
AnimationStartEvent = "animation-start-event"
|
||||
AnimationStartEvent PropertyName = "animation-start-event"
|
||||
|
||||
// AnimationEndEvent is the constant for "animation-end-event" property tag.
|
||||
//
|
||||
// Used by `View`.
|
||||
// Fired when an animation has completed. If the animation aborts before reaching completion, such as if the element is
|
||||
// Fired when an animation has completed. If the animation aborts before reaching completion, such as if the element is
|
||||
// removed or the animation is removed from the element, the "animation-end-event" is not fired.
|
||||
//
|
||||
// General listener format:
|
||||
|
@ -114,14 +112,14 @@ const (
|
|||
// `func(view rui.View)`,
|
||||
// `func(animationId string)`,
|
||||
// `func()`.
|
||||
AnimationEndEvent = "animation-end-event"
|
||||
AnimationEndEvent PropertyName = "animation-end-event"
|
||||
|
||||
// AnimationCancelEvent is the constant for "animation-cancel-event" property tag.
|
||||
//
|
||||
// Used by `View`.
|
||||
// Fired when an animation unexpectedly aborts. In other words, any time it stops running without sending the
|
||||
// "animation-end-event". This might happen when the animation-name is changed such that the animation is removed, or when
|
||||
// the animating view is hidden. Therefore, either directly or because any of its containing views are hidden. The event
|
||||
// Fired when an animation unexpectedly aborts. In other words, any time it stops running without sending the
|
||||
// "animation-end-event". This might happen when the animation-name is changed such that the animation is removed, or when
|
||||
// the animating view is hidden. Therefore, either directly or because any of its containing views are hidden. The event
|
||||
// is not supported by all browsers.
|
||||
//
|
||||
// General listener format:
|
||||
|
@ -135,12 +133,12 @@ const (
|
|||
// `func(view rui.View)`,
|
||||
// `func(animationId string)`,
|
||||
// `func()`.
|
||||
AnimationCancelEvent = "animation-cancel-event"
|
||||
AnimationCancelEvent PropertyName = "animation-cancel-event"
|
||||
|
||||
// AnimationIterationEvent is the constant for "animation-iteration-event" property tag.
|
||||
//
|
||||
// Used by `View`.
|
||||
// Fired when an iteration of an animation ends, and another one begins. This event does not occur at the same time as the
|
||||
// Fired when an iteration of an animation ends, and another one begins. This event does not occur at the same time as the
|
||||
// animation end event, and therefore does not occur for animations with an "iteration-count" of one.
|
||||
//
|
||||
// General listener format:
|
||||
|
@ -154,128 +152,106 @@ const (
|
|||
// `func(view rui.View)`,
|
||||
// `func(animationId string)`,
|
||||
// `func()`.
|
||||
AnimationIterationEvent = "animation-iteration-event"
|
||||
AnimationIterationEvent PropertyName = "animation-iteration-event"
|
||||
)
|
||||
|
||||
var transitionEvents = map[string]struct{ jsEvent, jsFunc string }{
|
||||
TransitionRunEvent: {jsEvent: "ontransitionrun", jsFunc: "transitionRunEvent"},
|
||||
TransitionStartEvent: {jsEvent: "ontransitionstart", jsFunc: "transitionStartEvent"},
|
||||
TransitionEndEvent: {jsEvent: "ontransitionend", jsFunc: "transitionEndEvent"},
|
||||
TransitionCancelEvent: {jsEvent: "ontransitioncancel", jsFunc: "transitionCancelEvent"},
|
||||
}
|
||||
|
||||
func (view *viewData) setTransitionListener(tag string, value any) bool {
|
||||
listeners, ok := valueToEventListeners[View, string](value)
|
||||
if !ok {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
|
||||
if listeners == nil {
|
||||
view.removeTransitionListener(tag)
|
||||
} else if js, ok := transitionEvents[tag]; ok {
|
||||
view.properties[tag] = listeners
|
||||
if view.created {
|
||||
view.session.updateProperty(view.htmlID(), js.jsEvent, js.jsFunc+"(this, event)")
|
||||
/*
|
||||
func setTransitionListener(properties Properties, tag PropertyName, value any) bool {
|
||||
if listeners, ok := valueToEventListeners[View, string](value); ok {
|
||||
if len(listeners) == 0 {
|
||||
properties.setRaw(tag, nil)
|
||||
} else {
|
||||
properties.setRaw(tag, listeners)
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
return true
|
||||
}
|
||||
return true
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
|
||||
func (view *viewData) removeTransitionListener(tag string) {
|
||||
func (view *viewData) removeTransitionListener(tag PropertyName) {
|
||||
delete(view.properties, tag)
|
||||
if view.created {
|
||||
if js, ok := transitionEvents[tag]; ok {
|
||||
if js, ok := eventJsFunc[tag]; ok {
|
||||
view.session.removeProperty(view.htmlID(), js.jsEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func transitionEventsHtml(view View, buffer *strings.Builder) {
|
||||
for tag, js := range transitionEvents {
|
||||
for _, tag := range []PropertyName{TransitionRunEvent, TransitionStartEvent, TransitionEndEvent, TransitionCancelEvent} {
|
||||
if value := view.getRaw(tag); value != nil {
|
||||
if listeners, ok := value.([]func(View, string)); ok && len(listeners) > 0 {
|
||||
buffer.WriteString(js.jsEvent)
|
||||
buffer.WriteString(`="`)
|
||||
buffer.WriteString(js.jsFunc)
|
||||
buffer.WriteString(`(this, event)" `)
|
||||
if js, ok := eventJsFunc[tag]; ok {
|
||||
if listeners, ok := value.([]func(View, string)); ok && len(listeners) > 0 {
|
||||
buffer.WriteString(js.jsEvent)
|
||||
buffer.WriteString(`="`)
|
||||
buffer.WriteString(js.jsFunc)
|
||||
buffer.WriteString(`(this, event)" `)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func (view *viewData) handleTransitionEvents(tag string, data DataObject) {
|
||||
if property, ok := data.PropertyValue("property"); ok {
|
||||
func (view *viewData) handleTransitionEvents(tag PropertyName, data DataObject) {
|
||||
if propertyName, ok := data.PropertyValue("property"); ok {
|
||||
property := PropertyName(propertyName)
|
||||
if tag == TransitionEndEvent || tag == TransitionCancelEvent {
|
||||
if animation, ok := view.singleTransition[property]; ok {
|
||||
delete(view.singleTransition, property)
|
||||
if animation != nil {
|
||||
view.transitions[property] = animation
|
||||
} else {
|
||||
delete(view.transitions, property)
|
||||
}
|
||||
view.updateTransitionCSS()
|
||||
setTransition(view, tag, animation)
|
||||
session := view.session
|
||||
session.updateCSSProperty(view.htmlID(), "transition", transitionCSS(view, session))
|
||||
}
|
||||
}
|
||||
|
||||
for _, listener := range getEventListeners[View, string](view, nil, tag) {
|
||||
for _, listener := range getEventListeners[View, PropertyName](view, nil, tag) {
|
||||
listener(view, property)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var animationEvents = map[string]struct{ jsEvent, jsFunc string }{
|
||||
AnimationStartEvent: {jsEvent: "onanimationstart", jsFunc: "animationStartEvent"},
|
||||
AnimationEndEvent: {jsEvent: "onanimationend", jsFunc: "animationEndEvent"},
|
||||
AnimationIterationEvent: {jsEvent: "onanimationiteration", jsFunc: "animationIterationEvent"},
|
||||
AnimationCancelEvent: {jsEvent: "onanimationcancel", jsFunc: "animationCancelEvent"},
|
||||
}
|
||||
|
||||
func (view *viewData) setAnimationListener(tag string, value any) bool {
|
||||
listeners, ok := valueToEventListeners[View, string](value)
|
||||
if !ok {
|
||||
/*
|
||||
func setAnimationListener(properties Properties, tag PropertyName, value any) bool {
|
||||
if listeners, ok := valueToEventListeners[View, string](value); ok {
|
||||
if len(listeners) == 0 {
|
||||
properties.setRaw(tag, nil)
|
||||
} else {
|
||||
properties.setRaw(tag, listeners)
|
||||
}
|
||||
return true
|
||||
}
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
|
||||
if listeners == nil {
|
||||
view.removeAnimationListener(tag)
|
||||
} else if js, ok := animationEvents[tag]; ok {
|
||||
view.properties[tag] = listeners
|
||||
if view.created {
|
||||
view.session.updateProperty(view.htmlID(), js.jsEvent, js.jsFunc+"(this, event)")
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (view *viewData) removeAnimationListener(tag string) {
|
||||
func (view *viewData) removeAnimationListener(tag PropertyName) {
|
||||
delete(view.properties, tag)
|
||||
if view.created {
|
||||
if js, ok := animationEvents[tag]; ok {
|
||||
if js, ok := eventJsFunc[tag]; ok {
|
||||
view.session.removeProperty(view.htmlID(), js.jsEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func animationEventsHtml(view View, buffer *strings.Builder) {
|
||||
for tag, js := range animationEvents {
|
||||
for _, tag := range []PropertyName{AnimationStartEvent, AnimationEndEvent, AnimationIterationEvent, AnimationCancelEvent} {
|
||||
if value := view.getRaw(tag); value != nil {
|
||||
if listeners, ok := value.([]func(View)); ok && len(listeners) > 0 {
|
||||
buffer.WriteString(js.jsEvent)
|
||||
buffer.WriteString(`="`)
|
||||
buffer.WriteString(js.jsFunc)
|
||||
buffer.WriteString(`(this, event)" `)
|
||||
if js, ok := eventJsFunc[tag]; ok {
|
||||
if listeners, ok := value.([]func(View, string)); ok && len(listeners) > 0 {
|
||||
buffer.WriteString(js.jsEvent)
|
||||
buffer.WriteString(`="`)
|
||||
buffer.WriteString(js.jsFunc)
|
||||
buffer.WriteString(`(this, event)" `)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func (view *viewData) handleAnimationEvents(tag string, data DataObject) {
|
||||
func (view *viewData) handleAnimationEvents(tag PropertyName, data DataObject) {
|
||||
if listeners := getEventListeners[View, string](view, nil, tag); len(listeners) > 0 {
|
||||
id := ""
|
||||
if name, ok := data.PropertyValue("name"); ok {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package rui
|
||||
|
||||
func (animation *animationData) Start(view View, listener func(view View, animation Animation, event string)) bool {
|
||||
func (animation *animationData) Start(view View, listener func(view View, animation Animation, event PropertyName)) bool {
|
||||
if view == nil {
|
||||
ErrorLog("nil View in animation.Start() function")
|
||||
return false
|
||||
|
@ -19,12 +19,12 @@ func (animation *animationData) Start(view View, listener func(view View, animat
|
|||
}
|
||||
}
|
||||
|
||||
animation.oldListeners = map[string][]func(View, string){}
|
||||
animation.oldListeners = map[PropertyName][]func(View, PropertyName){}
|
||||
|
||||
setListeners := func(event string, listener func(View, string)) {
|
||||
var listeners []func(View, string) = nil
|
||||
setListeners := func(event PropertyName, listener func(View, PropertyName)) {
|
||||
var listeners []func(View, PropertyName) = nil
|
||||
if value := view.Get(event); value != nil {
|
||||
if oldListeners, ok := value.([]func(View, string)); ok && len(oldListeners) > 0 {
|
||||
if oldListeners, ok := value.([]func(View, PropertyName)); ok && len(oldListeners) > 0 {
|
||||
listeners = oldListeners
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ func (animation *animationData) Start(view View, listener func(view View, animat
|
|||
|
||||
func (animation *animationData) finish() {
|
||||
if animation.view != nil {
|
||||
for _, event := range []string{AnimationStartEvent, AnimationEndEvent, AnimationCancelEvent, AnimationIterationEvent} {
|
||||
for _, event := range []PropertyName{AnimationStartEvent, AnimationEndEvent, AnimationCancelEvent, AnimationIterationEvent} {
|
||||
if listeners, ok := animation.oldListeners[event]; ok {
|
||||
animation.view.Set(event, listeners)
|
||||
} else {
|
||||
|
@ -63,7 +63,7 @@ func (animation *animationData) finish() {
|
|||
animation.view.Set(AnimationTag, "")
|
||||
}
|
||||
|
||||
animation.oldListeners = map[string][]func(View, string){}
|
||||
animation.oldListeners = map[PropertyName][]func(View, PropertyName){}
|
||||
|
||||
animation.view = nil
|
||||
animation.listener = nil
|
||||
|
@ -86,13 +86,13 @@ func (animation *animationData) Resume() {
|
|||
}
|
||||
}
|
||||
|
||||
func (animation *animationData) onAnimationStart(view View, _ string) {
|
||||
func (animation *animationData) onAnimationStart(view View, _ PropertyName) {
|
||||
if animation.view != nil && animation.listener != nil {
|
||||
animation.listener(animation.view, animation, AnimationStartEvent)
|
||||
}
|
||||
}
|
||||
|
||||
func (animation *animationData) onAnimationEnd(view View, _ string) {
|
||||
func (animation *animationData) onAnimationEnd(view View, _ PropertyName) {
|
||||
if animation.view != nil {
|
||||
animationView := animation.view
|
||||
listener := animation.listener
|
||||
|
@ -112,13 +112,13 @@ func (animation *animationData) onAnimationEnd(view View, _ string) {
|
|||
}
|
||||
}
|
||||
|
||||
func (animation *animationData) onAnimationIteration(view View, _ string) {
|
||||
func (animation *animationData) onAnimationIteration(view View, _ PropertyName) {
|
||||
if animation.view != nil && animation.listener != nil {
|
||||
animation.listener(animation.view, animation, AnimationIterationEvent)
|
||||
}
|
||||
}
|
||||
|
||||
func (animation *animationData) onAnimationCancel(view View, _ string) {
|
||||
func (animation *animationData) onAnimationCancel(view View, _ PropertyName) {
|
||||
if animation.view != nil {
|
||||
animationView := animation.view
|
||||
listener := animation.listener
|
||||
|
|
|
@ -215,6 +215,14 @@ function appendToInnerHTML(elementId, content) {
|
|||
}
|
||||
}
|
||||
|
||||
function appendToInputValue(elementId, content) {
|
||||
const element = document.getElementById(elementId);
|
||||
if (element) {
|
||||
element.value += content;
|
||||
scanElementsSize();
|
||||
}
|
||||
}
|
||||
|
||||
function setDisabled(elementId, disabled) {
|
||||
const element = document.getElementById(elementId);
|
||||
if (element) {
|
||||
|
|
|
@ -13,13 +13,12 @@ type audioPlayerData struct {
|
|||
func NewAudioPlayer(session Session, params Params) AudioPlayer {
|
||||
view := new(audioPlayerData)
|
||||
view.init(session)
|
||||
view.tag = "AudioPlayer"
|
||||
setInitParams(view, params)
|
||||
return view
|
||||
}
|
||||
|
||||
func newAudioPlayer(session Session) View {
|
||||
return NewAudioPlayer(session, nil)
|
||||
return new(audioPlayerData) // NewAudioPlayer(session, nil)
|
||||
}
|
||||
|
||||
func (player *audioPlayerData) init(session Session) {
|
||||
|
@ -27,10 +26,6 @@ func (player *audioPlayerData) init(session Session) {
|
|||
player.tag = "AudioPlayer"
|
||||
}
|
||||
|
||||
func (player *audioPlayerData) String() string {
|
||||
return getViewString(player, nil)
|
||||
}
|
||||
|
||||
func (player *audioPlayerData) htmlTag() string {
|
||||
return "audio"
|
||||
}
|
||||
|
|
124
background.go
124
background.go
|
@ -78,7 +78,7 @@ type BackgroundElement interface {
|
|||
}
|
||||
|
||||
type backgroundElement struct {
|
||||
propertyList
|
||||
dataProperty
|
||||
}
|
||||
|
||||
type backgroundImage struct {
|
||||
|
@ -91,24 +91,16 @@ func createBackground(obj DataObject) BackgroundElement {
|
|||
|
||||
switch obj.Tag() {
|
||||
case "image":
|
||||
image := new(backgroundImage)
|
||||
image.properties = map[string]any{}
|
||||
result = image
|
||||
result = NewBackgroundImage(nil)
|
||||
|
||||
case "linear-gradient":
|
||||
gradient := new(backgroundLinearGradient)
|
||||
gradient.properties = map[string]any{}
|
||||
result = gradient
|
||||
result = NewBackgroundLinearGradient(nil)
|
||||
|
||||
case "radial-gradient":
|
||||
gradient := new(backgroundRadialGradient)
|
||||
gradient.properties = map[string]any{}
|
||||
result = gradient
|
||||
result = NewBackgroundRadialGradient(nil)
|
||||
|
||||
case "conic-gradient":
|
||||
gradient := new(backgroundConicGradient)
|
||||
gradient.properties = map[string]any{}
|
||||
result = gradient
|
||||
result = NewBackgroundConicGradient(nil)
|
||||
|
||||
default:
|
||||
return nil
|
||||
|
@ -118,7 +110,7 @@ func createBackground(obj DataObject) BackgroundElement {
|
|||
for i := 0; i < count; i++ {
|
||||
if node := obj.Property(i); node.Type() == TextNode {
|
||||
if value := node.Text(); value != "" {
|
||||
result.Set(node.Tag(), value)
|
||||
result.Set(PropertyName(node.Tag()), value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -129,13 +121,21 @@ func createBackground(obj DataObject) BackgroundElement {
|
|||
// NewBackgroundImage creates the new background image
|
||||
func NewBackgroundImage(params Params) BackgroundElement {
|
||||
result := new(backgroundImage)
|
||||
result.properties = map[string]any{}
|
||||
result.init()
|
||||
for tag, value := range params {
|
||||
result.Set(tag, value)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (image *backgroundImage) init() {
|
||||
image.backgroundElement.init()
|
||||
image.normalize = normalizeBackgroundImageTag
|
||||
image.supportedProperties = []PropertyName{
|
||||
Attachment, Width, Height, Repeat, ImageHorizontalAlign, ImageVerticalAlign, backgroundFit, Source,
|
||||
}
|
||||
}
|
||||
|
||||
func (image *backgroundImage) Tag() string {
|
||||
return "image"
|
||||
}
|
||||
|
@ -148,8 +148,8 @@ func (image *backgroundImage) Clone() BackgroundElement {
|
|||
return result
|
||||
}
|
||||
|
||||
func (image *backgroundImage) normalizeTag(tag string) string {
|
||||
tag = strings.ToLower(tag)
|
||||
func normalizeBackgroundImageTag(tag PropertyName) PropertyName {
|
||||
tag = defaultNormalize(tag)
|
||||
switch tag {
|
||||
case "source":
|
||||
tag = Source
|
||||
|
@ -167,21 +167,6 @@ func (image *backgroundImage) normalizeTag(tag string) string {
|
|||
return tag
|
||||
}
|
||||
|
||||
func (image *backgroundImage) Set(tag string, value any) bool {
|
||||
tag = image.normalizeTag(tag)
|
||||
switch tag {
|
||||
case Attachment, Width, Height, Repeat, ImageHorizontalAlign, ImageVerticalAlign,
|
||||
backgroundFit, Source:
|
||||
return image.backgroundElement.Set(tag, value)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (image *backgroundImage) Get(tag string) any {
|
||||
return image.backgroundElement.Get(image.normalizeTag(tag))
|
||||
}
|
||||
|
||||
func (image *backgroundImage) cssStyle(session Session) string {
|
||||
if src, ok := imageProperty(image, Source, session); ok && src != "" {
|
||||
buffer := allocStringBuilder()
|
||||
|
@ -252,7 +237,7 @@ func (image *backgroundImage) cssStyle(session Session) string {
|
|||
}
|
||||
|
||||
func (image *backgroundImage) writeString(buffer *strings.Builder, indent string) {
|
||||
image.writeToBuffer(buffer, indent, image.Tag(), []string{
|
||||
image.writeToBuffer(buffer, indent, image.Tag(), []PropertyName{
|
||||
Source,
|
||||
Width,
|
||||
Height,
|
||||
|
@ -267,3 +252,76 @@ func (image *backgroundImage) writeString(buffer *strings.Builder, indent string
|
|||
func (image *backgroundImage) String() string {
|
||||
return runStringWriter(image)
|
||||
}
|
||||
|
||||
func setBackgroundProperty(properties Properties, value any) []PropertyName {
|
||||
background := []BackgroundElement{}
|
||||
|
||||
error := func() []PropertyName {
|
||||
notCompatibleType(Background, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case BackgroundElement:
|
||||
background = []BackgroundElement{value}
|
||||
|
||||
case []BackgroundElement:
|
||||
background = value
|
||||
|
||||
case []DataValue:
|
||||
for _, el := range value {
|
||||
if el.IsObject() {
|
||||
if element := createBackground(el.Object()); element != nil {
|
||||
background = append(background, element)
|
||||
} else {
|
||||
return error()
|
||||
}
|
||||
} else if obj := ParseDataText(el.Value()); obj != nil {
|
||||
if element := createBackground(obj); element != nil {
|
||||
background = append(background, element)
|
||||
} else {
|
||||
return error()
|
||||
}
|
||||
} else {
|
||||
return error()
|
||||
}
|
||||
}
|
||||
|
||||
case DataObject:
|
||||
if element := createBackground(value); element != nil {
|
||||
background = []BackgroundElement{element}
|
||||
} else {
|
||||
return error()
|
||||
}
|
||||
|
||||
case []DataObject:
|
||||
for _, obj := range value {
|
||||
if element := createBackground(obj); element != nil {
|
||||
background = append(background, element)
|
||||
} else {
|
||||
return error()
|
||||
}
|
||||
}
|
||||
|
||||
case string:
|
||||
if obj := ParseDataText(value); obj != nil {
|
||||
if element := createBackground(obj); element != nil {
|
||||
background = []BackgroundElement{element}
|
||||
} else {
|
||||
return error()
|
||||
}
|
||||
} else {
|
||||
return error()
|
||||
}
|
||||
}
|
||||
|
||||
if len(background) > 0 {
|
||||
properties.setRaw(Background, background)
|
||||
} else if properties.getRaw(Background) != nil {
|
||||
properties.setRaw(Background, nil)
|
||||
} else {
|
||||
return []PropertyName{}
|
||||
}
|
||||
|
||||
return []PropertyName{Background}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ type BackgroundGradientAngle struct {
|
|||
// NewBackgroundConicGradient creates the new background conic gradient
|
||||
func NewBackgroundConicGradient(params Params) BackgroundElement {
|
||||
result := new(backgroundConicGradient)
|
||||
result.properties = map[string]any{}
|
||||
result.init()
|
||||
for tag, value := range params {
|
||||
result.Set(tag, value)
|
||||
}
|
||||
|
@ -48,7 +48,6 @@ func (point *BackgroundGradientAngle) String() string {
|
|||
|
||||
case AngleUnit:
|
||||
result += " " + value.String()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,6 +114,15 @@ func (point *BackgroundGradientAngle) cssString(session Session, buffer *strings
|
|||
}
|
||||
}
|
||||
|
||||
func (gradient *backgroundConicGradient) init() {
|
||||
gradient.backgroundElement.init()
|
||||
gradient.normalize = normalizeConicGradientTag
|
||||
gradient.set = backgroundConicGradientSet
|
||||
gradient.supportedProperties = []PropertyName{
|
||||
CenterX, CenterY, Repeating, From, Gradient,
|
||||
}
|
||||
}
|
||||
|
||||
func (gradient *backgroundConicGradient) Tag() string {
|
||||
return "conic-gradient"
|
||||
}
|
||||
|
@ -127,8 +135,8 @@ func (image *backgroundConicGradient) Clone() BackgroundElement {
|
|||
return result
|
||||
}
|
||||
|
||||
func (gradient *backgroundConicGradient) normalizeTag(tag string) string {
|
||||
tag = strings.ToLower(tag)
|
||||
func normalizeConicGradientTag(tag PropertyName) PropertyName {
|
||||
tag = defaultNormalize(tag)
|
||||
switch tag {
|
||||
case "x-center":
|
||||
tag = CenterX
|
||||
|
@ -140,18 +148,50 @@ func (gradient *backgroundConicGradient) normalizeTag(tag string) string {
|
|||
return tag
|
||||
}
|
||||
|
||||
func (gradient *backgroundConicGradient) Set(tag string, value any) bool {
|
||||
tag = gradient.normalizeTag(tag)
|
||||
func backgroundConicGradientSet(properties Properties, tag PropertyName, value any) []PropertyName {
|
||||
switch tag {
|
||||
case CenterX, CenterY, Repeating, From:
|
||||
return gradient.propertyList.Set(tag, value)
|
||||
|
||||
case Gradient:
|
||||
return gradient.setGradient(value)
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
if value == "" {
|
||||
return propertiesRemove(properties, tag)
|
||||
}
|
||||
|
||||
if strings.Contains(value, ",") || strings.Contains(value, " ") {
|
||||
if vector := parseGradientText(value); vector != nil {
|
||||
properties.setRaw(Gradient, vector)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
} else if isConstantName(value) {
|
||||
properties.setRaw(Gradient, value)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
ErrorLogF(`Invalid conic gradient: "%s"`, value)
|
||||
|
||||
case []BackgroundGradientAngle:
|
||||
count := len(value)
|
||||
if count < 2 {
|
||||
ErrorLog("The gradient must contain at least 2 points")
|
||||
return nil
|
||||
}
|
||||
|
||||
for i, point := range value {
|
||||
if point.Color == nil {
|
||||
ErrorLogF("Invalid %d element of the conic gradient: Color is nil", i)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
properties.setRaw(Gradient, value)
|
||||
return []PropertyName{tag}
|
||||
|
||||
default:
|
||||
notCompatibleType(tag, value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
ErrorLogF(`"%s" property is not supported by BackgroundConicGradient`, tag)
|
||||
return false
|
||||
return propertiesSet(properties, tag, value)
|
||||
}
|
||||
|
||||
func (gradient *backgroundConicGradient) stringToAngle(text string) (any, bool) {
|
||||
|
@ -216,57 +256,6 @@ func (gradient *backgroundConicGradient) parseGradientText(value string) []Backg
|
|||
}
|
||||
return vector
|
||||
}
|
||||
|
||||
func (gradient *backgroundConicGradient) setGradient(value any) bool {
|
||||
if value == nil {
|
||||
delete(gradient.properties, Gradient)
|
||||
return true
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
if value == "" {
|
||||
delete(gradient.properties, Gradient)
|
||||
return true
|
||||
}
|
||||
|
||||
if strings.Contains(value, ",") || strings.Contains(value, " ") {
|
||||
if vector := gradient.parseGradientText(value); vector != nil {
|
||||
gradient.properties[Gradient] = vector
|
||||
return true
|
||||
}
|
||||
return false
|
||||
} else if value[0] == '@' {
|
||||
gradient.properties[Gradient] = value
|
||||
return true
|
||||
}
|
||||
|
||||
ErrorLogF(`Invalid conic gradient: "%s"`, value)
|
||||
return false
|
||||
|
||||
case []BackgroundGradientAngle:
|
||||
count := len(value)
|
||||
if count < 2 {
|
||||
ErrorLog("The gradient must contain at least 2 points")
|
||||
return false
|
||||
}
|
||||
|
||||
for i, point := range value {
|
||||
if point.Color == nil {
|
||||
ErrorLogF("Invalid %d element of the conic gradient: Color is nil", i)
|
||||
return false
|
||||
}
|
||||
}
|
||||
gradient.properties[Gradient] = value
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (gradient *backgroundConicGradient) Get(tag string) any {
|
||||
return gradient.backgroundElement.Get(gradient.normalizeTag(tag))
|
||||
}
|
||||
|
||||
func (gradient *backgroundConicGradient) cssStyle(session Session) string {
|
||||
|
||||
points := []BackgroundGradientAngle{}
|
||||
|
@ -339,7 +328,7 @@ func (gradient *backgroundConicGradient) cssStyle(session Session) string {
|
|||
}
|
||||
|
||||
func (gradient *backgroundConicGradient) writeString(buffer *strings.Builder, indent string) {
|
||||
gradient.writeToBuffer(buffer, indent, gradient.Tag(), []string{
|
||||
gradient.writeToBuffer(buffer, indent, gradient.Tag(), []PropertyName{
|
||||
Gradient,
|
||||
CenterX,
|
||||
CenterY,
|
||||
|
|
|
@ -72,7 +72,7 @@ type backgroundRadialGradient struct {
|
|||
// NewBackgroundLinearGradient creates the new background linear gradient
|
||||
func NewBackgroundLinearGradient(params Params) BackgroundElement {
|
||||
result := new(backgroundLinearGradient)
|
||||
result.properties = map[string]any{}
|
||||
result.init()
|
||||
for tag, value := range params {
|
||||
result.Set(tag, value)
|
||||
}
|
||||
|
@ -82,14 +82,14 @@ func NewBackgroundLinearGradient(params Params) BackgroundElement {
|
|||
// NewBackgroundRadialGradient creates the new background radial gradient
|
||||
func NewBackgroundRadialGradient(params Params) BackgroundElement {
|
||||
result := new(backgroundRadialGradient)
|
||||
result.properties = map[string]any{}
|
||||
result.init()
|
||||
for tag, value := range params {
|
||||
result.Set(tag, value)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (gradient *backgroundGradient) parseGradientText(value string) []BackgroundGradientPoint {
|
||||
func parseGradientText(value string) []BackgroundGradientPoint {
|
||||
elements := strings.Split(value, ",")
|
||||
count := len(elements)
|
||||
if count < 2 {
|
||||
|
@ -107,31 +107,31 @@ func (gradient *backgroundGradient) parseGradientText(value string) []Background
|
|||
return points
|
||||
}
|
||||
|
||||
func (gradient *backgroundGradient) Set(tag string, value any) bool {
|
||||
func backgroundGradientSet(properties Properties, tag PropertyName, value any) []PropertyName {
|
||||
|
||||
switch tag = strings.ToLower(tag); tag {
|
||||
switch tag {
|
||||
case Repeating:
|
||||
return gradient.setBoolProperty(tag, value)
|
||||
return setBoolProperty(properties, tag, value)
|
||||
|
||||
case Gradient:
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
if value != "" {
|
||||
if strings.Contains(value, " ") || strings.Contains(value, ",") {
|
||||
if points := gradient.parseGradientText(value); len(points) >= 2 {
|
||||
gradient.properties[Gradient] = points
|
||||
return true
|
||||
if points := parseGradientText(value); len(points) >= 2 {
|
||||
properties.setRaw(Gradient, points)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
} else if value[0] == '@' {
|
||||
gradient.properties[Gradient] = value
|
||||
return true
|
||||
properties.setRaw(Gradient, value)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
}
|
||||
|
||||
case []BackgroundGradientPoint:
|
||||
if len(value) >= 2 {
|
||||
gradient.properties[Gradient] = value
|
||||
return true
|
||||
properties.setRaw(Gradient, value)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
case []Color:
|
||||
|
@ -141,8 +141,8 @@ func (gradient *backgroundGradient) Set(tag string, value any) bool {
|
|||
for i, color := range value {
|
||||
points[i].Color = color
|
||||
}
|
||||
gradient.properties[Gradient] = points
|
||||
return true
|
||||
properties.setRaw(Gradient, points)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
case []GradientPoint:
|
||||
|
@ -153,17 +153,17 @@ func (gradient *backgroundGradient) Set(tag string, value any) bool {
|
|||
points[i].Color = point.Color
|
||||
points[i].Pos = Percent(point.Offset * 100)
|
||||
}
|
||||
gradient.properties[Gradient] = points
|
||||
return true
|
||||
properties.setRaw(Gradient, points)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
}
|
||||
|
||||
ErrorLogF("Invalid gradient %v", value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
ErrorLogF("Property %s is not supported by a background gradient", tag)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
func (point *BackgroundGradientPoint) setValue(text string) bool {
|
||||
|
@ -266,7 +266,7 @@ func (gradient *backgroundGradient) writeGradient(session Session, buffer *strin
|
|||
case string:
|
||||
if value != "" && value[0] == '@' {
|
||||
if text, ok := session.Constant(value[1:]); ok {
|
||||
points = gradient.parseGradientText(text)
|
||||
points = parseGradientText(text)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -312,6 +312,13 @@ func (gradient *backgroundGradient) writeGradient(session Session, buffer *strin
|
|||
return false
|
||||
}
|
||||
|
||||
func (gradient *backgroundLinearGradient) init() {
|
||||
gradient.backgroundElement.init()
|
||||
gradient.set = backgroundLinearGradientSet
|
||||
gradient.supportedProperties = append(gradient.supportedProperties, Direction)
|
||||
|
||||
}
|
||||
|
||||
func (gradient *backgroundLinearGradient) Tag() string {
|
||||
return "linear-gradient"
|
||||
}
|
||||
|
@ -324,26 +331,26 @@ func (image *backgroundLinearGradient) Clone() BackgroundElement {
|
|||
return result
|
||||
}
|
||||
|
||||
func (gradient *backgroundLinearGradient) Set(tag string, value any) bool {
|
||||
if strings.ToLower(tag) == Direction {
|
||||
func backgroundLinearGradientSet(properties Properties, tag PropertyName, value any) []PropertyName {
|
||||
if tag == Direction {
|
||||
switch value := value.(type) {
|
||||
case AngleUnit:
|
||||
gradient.properties[Direction] = value
|
||||
return true
|
||||
properties.setRaw(Direction, value)
|
||||
return []PropertyName{tag}
|
||||
|
||||
case string:
|
||||
if gradient.setSimpleProperty(tag, value) {
|
||||
return true
|
||||
if setSimpleProperty(properties, tag, value) {
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
if angle, ok := StringToAngleUnit(value); ok {
|
||||
gradient.properties[Direction] = angle
|
||||
return true
|
||||
properties.setRaw(Direction, angle)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
}
|
||||
return gradient.setEnumProperty(tag, value, enumProperties[Direction].values)
|
||||
return setEnumProperty(properties, tag, value, enumProperties[Direction].values)
|
||||
}
|
||||
|
||||
return gradient.backgroundGradient.Set(tag, value)
|
||||
return backgroundGradientSet(properties, tag, value)
|
||||
}
|
||||
|
||||
func (gradient *backgroundLinearGradient) cssStyle(session Session) string {
|
||||
|
@ -400,7 +407,7 @@ func (gradient *backgroundLinearGradient) cssStyle(session Session) string {
|
|||
}
|
||||
|
||||
func (gradient *backgroundLinearGradient) writeString(buffer *strings.Builder, indent string) {
|
||||
gradient.writeToBuffer(buffer, indent, gradient.Tag(), []string{
|
||||
gradient.writeToBuffer(buffer, indent, gradient.Tag(), []PropertyName{
|
||||
Gradient,
|
||||
Repeating,
|
||||
Direction,
|
||||
|
@ -411,6 +418,15 @@ func (gradient *backgroundLinearGradient) String() string {
|
|||
return runStringWriter(gradient)
|
||||
}
|
||||
|
||||
func (gradient *backgroundRadialGradient) init() {
|
||||
gradient.backgroundElement.init()
|
||||
gradient.normalize = normalizeRadialGradientTag
|
||||
gradient.set = backgroundRadialGradientSet
|
||||
gradient.supportedProperties = append(gradient.supportedProperties, []PropertyName{
|
||||
RadialGradientRadius, RadialGradientShape, CenterX, CenterY,
|
||||
}...)
|
||||
}
|
||||
|
||||
func (gradient *backgroundRadialGradient) Tag() string {
|
||||
return "radial-gradient"
|
||||
}
|
||||
|
@ -423,8 +439,8 @@ func (image *backgroundRadialGradient) Clone() BackgroundElement {
|
|||
return result
|
||||
}
|
||||
|
||||
func (gradient *backgroundRadialGradient) normalizeTag(tag string) string {
|
||||
tag = strings.ToLower(tag)
|
||||
func normalizeRadialGradientTag(tag PropertyName) PropertyName {
|
||||
tag = defaultNormalize(tag)
|
||||
switch tag {
|
||||
case Radius:
|
||||
tag = RadialGradientRadius
|
||||
|
@ -442,83 +458,75 @@ func (gradient *backgroundRadialGradient) normalizeTag(tag string) string {
|
|||
return tag
|
||||
}
|
||||
|
||||
func (gradient *backgroundRadialGradient) Set(tag string, value any) bool {
|
||||
tag = gradient.normalizeTag(tag)
|
||||
func backgroundRadialGradientSet(properties Properties, tag PropertyName, value any) []PropertyName {
|
||||
switch tag {
|
||||
case RadialGradientRadius:
|
||||
switch value := value.(type) {
|
||||
case []SizeUnit:
|
||||
switch len(value) {
|
||||
case 0:
|
||||
delete(gradient.properties, RadialGradientRadius)
|
||||
return true
|
||||
properties.setRaw(RadialGradientRadius, nil)
|
||||
|
||||
case 1:
|
||||
if value[0].Type == Auto {
|
||||
delete(gradient.properties, RadialGradientRadius)
|
||||
properties.setRaw(RadialGradientRadius, nil)
|
||||
} else {
|
||||
gradient.properties[RadialGradientRadius] = value[0]
|
||||
properties.setRaw(RadialGradientRadius, value[0])
|
||||
}
|
||||
return true
|
||||
|
||||
default:
|
||||
gradient.properties[RadialGradientRadius] = value
|
||||
return true
|
||||
properties.setRaw(RadialGradientRadius, value)
|
||||
}
|
||||
return []PropertyName{tag}
|
||||
|
||||
case []any:
|
||||
switch len(value) {
|
||||
case 0:
|
||||
delete(gradient.properties, RadialGradientRadius)
|
||||
return true
|
||||
properties.setRaw(RadialGradientRadius, nil)
|
||||
return []PropertyName{tag}
|
||||
|
||||
case 1:
|
||||
return gradient.Set(RadialGradientRadius, value[0])
|
||||
return backgroundRadialGradientSet(properties, RadialGradientRadius, value[0])
|
||||
|
||||
default:
|
||||
gradient.properties[RadialGradientRadius] = value
|
||||
return true
|
||||
properties.setRaw(RadialGradientRadius, value)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
case string:
|
||||
if gradient.setSimpleProperty(RadialGradientRadius, value) {
|
||||
return true
|
||||
if setSimpleProperty(properties, RadialGradientRadius, value) {
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
if size, err := stringToSizeUnit(value); err == nil {
|
||||
if size.Type == Auto {
|
||||
delete(gradient.properties, RadialGradientRadius)
|
||||
properties.setRaw(RadialGradientRadius, nil)
|
||||
} else {
|
||||
gradient.properties[RadialGradientRadius] = size
|
||||
properties.setRaw(RadialGradientRadius, size)
|
||||
}
|
||||
return true
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
return gradient.setEnumProperty(RadialGradientRadius, value, enumProperties[RadialGradientRadius].values)
|
||||
return setEnumProperty(properties, RadialGradientRadius, value, enumProperties[RadialGradientRadius].values)
|
||||
|
||||
case SizeUnit:
|
||||
if value.Type == Auto {
|
||||
delete(gradient.properties, RadialGradientRadius)
|
||||
properties.setRaw(RadialGradientRadius, nil)
|
||||
} else {
|
||||
gradient.properties[RadialGradientRadius] = value
|
||||
properties.setRaw(RadialGradientRadius, value)
|
||||
}
|
||||
return true
|
||||
return []PropertyName{tag}
|
||||
|
||||
case int:
|
||||
n := value
|
||||
if n >= 0 && n < len(enumProperties[RadialGradientRadius].values) {
|
||||
return gradient.propertyList.Set(RadialGradientRadius, value)
|
||||
}
|
||||
return setEnumProperty(properties, RadialGradientRadius, value, enumProperties[RadialGradientRadius].values)
|
||||
}
|
||||
|
||||
ErrorLogF(`Invalid value of "%s" property: %v`, tag, value)
|
||||
return nil
|
||||
|
||||
case RadialGradientShape, CenterX, CenterY:
|
||||
return gradient.propertyList.Set(tag, value)
|
||||
return propertiesSet(properties, tag, value)
|
||||
}
|
||||
|
||||
return gradient.backgroundGradient.Set(tag, value)
|
||||
}
|
||||
|
||||
func (gradient *backgroundRadialGradient) Get(tag string) any {
|
||||
return gradient.backgroundGradient.Get(gradient.normalizeTag(tag))
|
||||
return backgroundGradientSet(properties, tag, value)
|
||||
}
|
||||
|
||||
func (gradient *backgroundRadialGradient) cssStyle(session Session) string {
|
||||
|
@ -652,7 +660,7 @@ func (gradient *backgroundRadialGradient) cssStyle(session Session) string {
|
|||
return buffer.String()
|
||||
}
|
||||
func (gradient *backgroundRadialGradient) writeString(buffer *strings.Builder, indent string) {
|
||||
gradient.writeToBuffer(buffer, indent, gradient.Tag(), []string{
|
||||
gradient.writeToBuffer(buffer, indent, gradient.Tag(), []PropertyName{
|
||||
Gradient,
|
||||
CenterX,
|
||||
CenterY,
|
||||
|
|
458
border.go
458
border.go
|
@ -38,7 +38,7 @@ const (
|
|||
// `2`(`DashedLine`) or "dashed" - Dashed line as a border.
|
||||
// `3`(`DottedLine`) or "dotted" - Dotted line as a border.
|
||||
// `4`(`DoubleLine`) or "double" - Double line as a border.
|
||||
LeftStyle = "left-style"
|
||||
LeftStyle PropertyName = "left-style"
|
||||
|
||||
// RightStyle is the constant for "right-style" property tag.
|
||||
//
|
||||
|
@ -53,7 +53,7 @@ const (
|
|||
// `2`(`DashedLine`) or "dashed" - Dashed line as a border.
|
||||
// `3`(`DottedLine`) or "dotted" - Dotted line as a border.
|
||||
// `4`(`DoubleLine`) or "double" - Double line as a border.
|
||||
RightStyle = "right-style"
|
||||
RightStyle PropertyName = "right-style"
|
||||
|
||||
// TopStyle is the constant for "top-style" property tag.
|
||||
//
|
||||
|
@ -68,7 +68,7 @@ const (
|
|||
// `2`(`DashedLine`) or "dashed" - Dashed line as a border.
|
||||
// `3`(`DottedLine`) or "dotted" - Dotted line as a border.
|
||||
// `4`(`DoubleLine`) or "double" - Double line as a border.
|
||||
TopStyle = "top-style"
|
||||
TopStyle PropertyName = "top-style"
|
||||
|
||||
// BottomStyle is the constant for "bottom-style" property tag.
|
||||
//
|
||||
|
@ -83,7 +83,7 @@ const (
|
|||
// `2`(`DashedLine`) or "dashed" - Dashed line as a border.
|
||||
// `3`(`DottedLine`) or "dotted" - Dotted line as a border.
|
||||
// `4`(`DoubleLine`) or "double" - Double line as a border.
|
||||
BottomStyle = "bottom-style"
|
||||
BottomStyle PropertyName = "bottom-style"
|
||||
|
||||
// LeftWidth is the constant for "left-width" property tag.
|
||||
//
|
||||
|
@ -94,7 +94,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
LeftWidth = "left-width"
|
||||
LeftWidth PropertyName = "left-width"
|
||||
|
||||
// RightWidth is the constant for "right-width" property tag.
|
||||
//
|
||||
|
@ -105,7 +105,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
RightWidth = "right-width"
|
||||
RightWidth PropertyName = "right-width"
|
||||
|
||||
// TopWidth is the constant for "top-width" property tag.
|
||||
//
|
||||
|
@ -116,7 +116,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
TopWidth = "top-width"
|
||||
TopWidth PropertyName = "top-width"
|
||||
|
||||
// BottomWidth is the constant for "bottom-width" property tag.
|
||||
//
|
||||
|
@ -127,7 +127,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
BottomWidth = "bottom-width"
|
||||
BottomWidth PropertyName = "bottom-width"
|
||||
|
||||
// LeftColor is the constant for "left-color" property tag.
|
||||
//
|
||||
|
@ -138,7 +138,7 @@ const (
|
|||
//
|
||||
// Internal type is `Color`, other types converted to it during assignment.
|
||||
// See `Color` description for more details.
|
||||
LeftColor = "left-color"
|
||||
LeftColor PropertyName = "left-color"
|
||||
|
||||
// RightColor is the constant for "right-color" property tag.
|
||||
//
|
||||
|
@ -149,7 +149,7 @@ const (
|
|||
//
|
||||
// Internal type is `Color`, other types converted to it during assignment.
|
||||
// See `Color` description for more details.
|
||||
RightColor = "right-color"
|
||||
RightColor PropertyName = "right-color"
|
||||
|
||||
// TopColor is the constant for "top-color" property tag.
|
||||
//
|
||||
|
@ -160,7 +160,7 @@ const (
|
|||
//
|
||||
// Internal type is `Color`, other types converted to it during assignment.
|
||||
// See `Color` description for more details.
|
||||
TopColor = "top-color"
|
||||
TopColor PropertyName = "top-color"
|
||||
|
||||
// BottomColor is the constant for "bottom-color" property tag.
|
||||
//
|
||||
|
@ -171,7 +171,7 @@ const (
|
|||
//
|
||||
// Internal type is `Color`, other types converted to it during assignment.
|
||||
// See `Color` description for more details.
|
||||
BottomColor = "bottom-color"
|
||||
BottomColor PropertyName = "bottom-color"
|
||||
)
|
||||
|
||||
// BorderProperty is the interface of a view border data
|
||||
|
@ -183,7 +183,7 @@ type BorderProperty interface {
|
|||
// ViewBorders returns top, right, bottom and left borders information all together
|
||||
ViewBorders(session Session) ViewBorders
|
||||
|
||||
delete(tag string)
|
||||
deleteTag(tag PropertyName) bool
|
||||
cssStyle(builder cssBuilder, session Session)
|
||||
cssWidth(builder cssBuilder, session Session)
|
||||
cssColor(builder cssBuilder, session Session)
|
||||
|
@ -193,12 +193,12 @@ type BorderProperty interface {
|
|||
}
|
||||
|
||||
type borderProperty struct {
|
||||
propertyList
|
||||
dataProperty
|
||||
}
|
||||
|
||||
func newBorderProperty(value any) BorderProperty {
|
||||
border := new(borderProperty)
|
||||
border.properties = map[string]any{}
|
||||
border.init()
|
||||
|
||||
if value != nil {
|
||||
switch value := value.(type) {
|
||||
|
@ -270,9 +270,10 @@ func newBorderProperty(value any) BorderProperty {
|
|||
// "width" (Width). Determines the line thickness (SizeUnit).
|
||||
func NewBorder(params Params) BorderProperty {
|
||||
border := new(borderProperty)
|
||||
border.properties = map[string]any{}
|
||||
border.init()
|
||||
|
||||
if params != nil {
|
||||
for _, tag := range []string{Style, Width, ColorTag, Left, Right, Top, Bottom,
|
||||
for _, tag := range []PropertyName{Style, Width, ColorTag, Left, Right, Top, Bottom,
|
||||
LeftStyle, RightStyle, TopStyle, BottomStyle,
|
||||
LeftWidth, RightWidth, TopWidth, BottomWidth,
|
||||
LeftColor, RightColor, TopColor, BottomColor} {
|
||||
|
@ -284,8 +285,37 @@ func NewBorder(params Params) BorderProperty {
|
|||
return border
|
||||
}
|
||||
|
||||
func (border *borderProperty) normalizeTag(tag string) string {
|
||||
tag = strings.ToLower(tag)
|
||||
func (border *borderProperty) init() {
|
||||
border.dataProperty.init()
|
||||
border.normalize = normalizeBorderTag
|
||||
border.get = borderGet
|
||||
border.set = borderSet
|
||||
border.remove = borderRemove
|
||||
border.supportedProperties = []PropertyName{
|
||||
Left,
|
||||
Right,
|
||||
Top,
|
||||
Bottom,
|
||||
Style,
|
||||
LeftStyle,
|
||||
RightStyle,
|
||||
TopStyle,
|
||||
BottomStyle,
|
||||
Width,
|
||||
LeftWidth,
|
||||
RightWidth,
|
||||
TopWidth,
|
||||
BottomWidth,
|
||||
ColorTag,
|
||||
LeftColor,
|
||||
RightColor,
|
||||
TopColor,
|
||||
BottomColor,
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeBorderTag(tag PropertyName) PropertyName {
|
||||
tag = defaultNormalize(tag)
|
||||
switch tag {
|
||||
case BorderLeft, CellBorderLeft:
|
||||
return Left
|
||||
|
@ -352,23 +382,23 @@ func (border *borderProperty) writeString(buffer *strings.Builder, indent string
|
|||
buffer.WriteString("_{ ")
|
||||
comma := false
|
||||
|
||||
write := func(tag string, value any) {
|
||||
write := func(tag PropertyName, value any) {
|
||||
if comma {
|
||||
buffer.WriteString(", ")
|
||||
}
|
||||
buffer.WriteString(tag)
|
||||
buffer.WriteString(string(tag))
|
||||
buffer.WriteString(" = ")
|
||||
writePropertyValue(buffer, BorderStyle, value, indent)
|
||||
comma = true
|
||||
}
|
||||
|
||||
for _, tag := range []string{Style, Width, ColorTag} {
|
||||
for _, tag := range []PropertyName{Style, Width, ColorTag} {
|
||||
if value, ok := border.properties[tag]; ok {
|
||||
write(tag, value)
|
||||
}
|
||||
}
|
||||
|
||||
for _, side := range []string{Top, Right, Bottom, Left} {
|
||||
for _, side := range []PropertyName{Top, Right, Bottom, Left} {
|
||||
style, okStyle := border.properties[side+"-"+Style]
|
||||
width, okWidth := border.properties[side+"-"+Width]
|
||||
color, okColor := border.properties[side+"-"+ColorTag]
|
||||
|
@ -378,7 +408,7 @@ func (border *borderProperty) writeString(buffer *strings.Builder, indent string
|
|||
comma = false
|
||||
}
|
||||
|
||||
buffer.WriteString(side)
|
||||
buffer.WriteString(string(side))
|
||||
buffer.WriteString(" = _{ ")
|
||||
if okStyle {
|
||||
write(Style, style)
|
||||
|
@ -401,164 +431,96 @@ func (border *borderProperty) String() string {
|
|||
return runStringWriter(border)
|
||||
}
|
||||
|
||||
func (border *borderProperty) setSingleBorderObject(prefix string, obj DataObject) bool {
|
||||
result := true
|
||||
if text, ok := obj.PropertyValue(Style); ok {
|
||||
if !border.setEnumProperty(prefix+"-style", text, enumProperties[BorderStyle].values) {
|
||||
result = false
|
||||
}
|
||||
}
|
||||
if text, ok := obj.PropertyValue(ColorTag); ok {
|
||||
if !border.setColorProperty(prefix+"-color", text) {
|
||||
result = false
|
||||
}
|
||||
}
|
||||
if text, ok := obj.PropertyValue("width"); ok {
|
||||
if !border.setSizeProperty(prefix+"-width", text) {
|
||||
result = false
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (border *borderProperty) setBorderObject(obj DataObject) bool {
|
||||
result := true
|
||||
|
||||
for _, side := range []string{Top, Right, Bottom, Left} {
|
||||
if node := obj.PropertyByTag(side); node != nil {
|
||||
if node.Type() == ObjectNode {
|
||||
if !border.setSingleBorderObject(side, node.Object()) {
|
||||
for i := 0; i < obj.PropertyCount(); i++ {
|
||||
if node := obj.Property(i); node != nil {
|
||||
tag := PropertyName(node.Tag())
|
||||
switch node.Type() {
|
||||
case TextNode:
|
||||
if borderSet(border, tag, node.Text()) == nil {
|
||||
result = false
|
||||
}
|
||||
} else {
|
||||
notCompatibleType(side, node)
|
||||
result = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if text, ok := obj.PropertyValue(Style); ok {
|
||||
values := split4Values(text)
|
||||
styles := enumProperties[BorderStyle].values
|
||||
switch len(values) {
|
||||
case 1:
|
||||
if !border.setEnumProperty(Style, values[0], styles) {
|
||||
result = false
|
||||
}
|
||||
|
||||
case 4:
|
||||
for n, tag := range [4]string{TopStyle, RightStyle, BottomStyle, LeftStyle} {
|
||||
if !border.setEnumProperty(tag, values[n], styles) {
|
||||
case ObjectNode:
|
||||
if borderSet(border, tag, node.Object()) == nil {
|
||||
result = false
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
notCompatibleType(Style, text)
|
||||
default:
|
||||
result = false
|
||||
}
|
||||
} else {
|
||||
result = false
|
||||
}
|
||||
}
|
||||
|
||||
if text, ok := obj.PropertyValue(ColorTag); ok {
|
||||
values := split4Values(text)
|
||||
switch len(values) {
|
||||
case 1:
|
||||
if !border.setColorProperty(ColorTag, values[0]) {
|
||||
return false
|
||||
}
|
||||
|
||||
case 4:
|
||||
for n, tag := range [4]string{TopColor, RightColor, BottomColor, LeftColor} {
|
||||
if !border.setColorProperty(tag, values[n]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
notCompatibleType(ColorTag, text)
|
||||
result = false
|
||||
}
|
||||
}
|
||||
|
||||
if text, ok := obj.PropertyValue(Width); ok {
|
||||
values := split4Values(text)
|
||||
switch len(values) {
|
||||
case 1:
|
||||
if !border.setSizeProperty(Width, values[0]) {
|
||||
result = false
|
||||
}
|
||||
|
||||
case 4:
|
||||
for n, tag := range [4]string{TopWidth, RightWidth, BottomWidth, LeftWidth} {
|
||||
if !border.setSizeProperty(tag, values[n]) {
|
||||
result = false
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
notCompatibleType(Width, text)
|
||||
result = false
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (border *borderProperty) Remove(tag string) {
|
||||
tag = border.normalizeTag(tag)
|
||||
func borderRemove(properties Properties, tag PropertyName) []PropertyName {
|
||||
result := []PropertyName{}
|
||||
removeTag := func(t PropertyName) {
|
||||
if properties.getRaw(t) != nil {
|
||||
properties.setRaw(t, nil)
|
||||
result = append(result, t)
|
||||
}
|
||||
}
|
||||
|
||||
switch tag {
|
||||
case Style:
|
||||
for _, t := range []string{tag, TopStyle, RightStyle, BottomStyle, LeftStyle} {
|
||||
delete(border.properties, t)
|
||||
for _, t := range []PropertyName{tag, TopStyle, RightStyle, BottomStyle, LeftStyle} {
|
||||
removeTag(t)
|
||||
}
|
||||
|
||||
case Width:
|
||||
for _, t := range []string{tag, TopWidth, RightWidth, BottomWidth, LeftWidth} {
|
||||
delete(border.properties, t)
|
||||
for _, t := range []PropertyName{tag, TopWidth, RightWidth, BottomWidth, LeftWidth} {
|
||||
removeTag(t)
|
||||
}
|
||||
|
||||
case ColorTag:
|
||||
for _, t := range []string{tag, TopColor, RightColor, BottomColor, LeftColor} {
|
||||
delete(border.properties, t)
|
||||
for _, t := range []PropertyName{tag, TopColor, RightColor, BottomColor, LeftColor} {
|
||||
removeTag(t)
|
||||
}
|
||||
|
||||
case Left, Right, Top, Bottom:
|
||||
border.Remove(tag + "-style")
|
||||
border.Remove(tag + "-width")
|
||||
border.Remove(tag + "-color")
|
||||
removeTag(tag + "-style")
|
||||
removeTag(tag + "-width")
|
||||
removeTag(tag + "-color")
|
||||
|
||||
case LeftStyle, RightStyle, TopStyle, BottomStyle:
|
||||
delete(border.properties, tag)
|
||||
if style, ok := border.properties[Style]; ok && style != nil {
|
||||
for _, t := range []string{TopStyle, RightStyle, BottomStyle, LeftStyle} {
|
||||
removeTag(tag)
|
||||
if style := properties.getRaw(Style); style != nil {
|
||||
for _, t := range []PropertyName{TopStyle, RightStyle, BottomStyle, LeftStyle} {
|
||||
if t != tag {
|
||||
if _, ok := border.properties[t]; !ok {
|
||||
border.properties[t] = style
|
||||
if properties.getRaw(t) == nil {
|
||||
properties.setRaw(t, style)
|
||||
result = append(result, t)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case LeftWidth, RightWidth, TopWidth, BottomWidth:
|
||||
delete(border.properties, tag)
|
||||
if width, ok := border.properties[Width]; ok && width != nil {
|
||||
for _, t := range []string{TopWidth, RightWidth, BottomWidth, LeftWidth} {
|
||||
removeTag(tag)
|
||||
if width := properties.getRaw(Width); width != nil {
|
||||
for _, t := range []PropertyName{TopWidth, RightWidth, BottomWidth, LeftWidth} {
|
||||
if t != tag {
|
||||
if _, ok := border.properties[t]; !ok {
|
||||
border.properties[t] = width
|
||||
if properties.getRaw(t) == nil {
|
||||
properties.setRaw(t, width)
|
||||
result = append(result, t)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case LeftColor, RightColor, TopColor, BottomColor:
|
||||
delete(border.properties, tag)
|
||||
if color, ok := border.properties[ColorTag]; ok && color != nil {
|
||||
for _, t := range []string{TopColor, RightColor, BottomColor, LeftColor} {
|
||||
removeTag(tag)
|
||||
if color := properties.getRaw(ColorTag); color != nil {
|
||||
for _, t := range []PropertyName{TopColor, RightColor, BottomColor, LeftColor} {
|
||||
if t != tag {
|
||||
if _, ok := border.properties[t]; !ok {
|
||||
border.properties[t] = color
|
||||
if properties.getRaw(t) == nil {
|
||||
properties.setRaw(t, color)
|
||||
result = append(result, t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -567,80 +529,118 @@ func (border *borderProperty) Remove(tag string) {
|
|||
default:
|
||||
ErrorLogF(`"%s" property is not compatible with the BorderProperty`, tag)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (border *borderProperty) Set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
border.Remove(tag)
|
||||
return true
|
||||
}
|
||||
func borderSet(properties Properties, tag PropertyName, value any) []PropertyName {
|
||||
|
||||
tag = border.normalizeTag(tag)
|
||||
setSingleBorderObject := func(prefix PropertyName, obj DataObject) []PropertyName {
|
||||
result := []PropertyName{}
|
||||
if text, ok := obj.PropertyValue(string(Style)); ok {
|
||||
props := setEnumProperty(properties, prefix+"-style", text, enumProperties[BorderStyle].values)
|
||||
if props == nil {
|
||||
return nil
|
||||
}
|
||||
result = append(result, props...)
|
||||
}
|
||||
if text, ok := obj.PropertyValue(string(ColorTag)); ok {
|
||||
props := setColorProperty(properties, prefix+"-color", text)
|
||||
if props == nil && len(result) == 0 {
|
||||
return nil
|
||||
}
|
||||
result = append(result, props...)
|
||||
}
|
||||
if text, ok := obj.PropertyValue("width"); ok {
|
||||
props := setSizeProperty(properties, prefix+"-width", text)
|
||||
if props == nil && len(result) == 0 {
|
||||
return nil
|
||||
}
|
||||
result = append(result, props...)
|
||||
}
|
||||
if len(result) > 0 {
|
||||
result = append(result, prefix)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
switch tag {
|
||||
case Style:
|
||||
if border.setEnumProperty(Style, value, enumProperties[BorderStyle].values) {
|
||||
for _, side := range []string{TopStyle, RightStyle, BottomStyle, LeftStyle} {
|
||||
delete(border.properties, side)
|
||||
if result := setEnumProperty(properties, Style, value, enumProperties[BorderStyle].values); result != nil {
|
||||
for _, side := range []PropertyName{TopStyle, RightStyle, BottomStyle, LeftStyle} {
|
||||
if value := properties.getRaw(side); value != nil {
|
||||
properties.setRaw(side, nil)
|
||||
result = append(result, side)
|
||||
}
|
||||
}
|
||||
return true
|
||||
return result
|
||||
}
|
||||
|
||||
case Width:
|
||||
if border.setSizeProperty(Width, value) {
|
||||
for _, side := range []string{TopWidth, RightWidth, BottomWidth, LeftWidth} {
|
||||
delete(border.properties, side)
|
||||
if result := setSizeProperty(properties, Width, value); result != nil {
|
||||
for _, side := range []PropertyName{TopWidth, RightWidth, BottomWidth, LeftWidth} {
|
||||
if value := properties.getRaw(side); value != nil {
|
||||
properties.setRaw(side, nil)
|
||||
result = append(result, side)
|
||||
}
|
||||
}
|
||||
return true
|
||||
return result
|
||||
}
|
||||
|
||||
case ColorTag:
|
||||
if border.setColorProperty(ColorTag, value) {
|
||||
for _, side := range []string{TopColor, RightColor, BottomColor, LeftColor} {
|
||||
delete(border.properties, side)
|
||||
if result := setColorProperty(properties, ColorTag, value); result != nil {
|
||||
for _, side := range []PropertyName{TopColor, RightColor, BottomColor, LeftColor} {
|
||||
if value := properties.getRaw(side); value != nil {
|
||||
properties.setRaw(side, nil)
|
||||
result = append(result, side)
|
||||
}
|
||||
}
|
||||
return true
|
||||
return result
|
||||
}
|
||||
|
||||
case LeftStyle, RightStyle, TopStyle, BottomStyle:
|
||||
return border.setEnumProperty(tag, value, enumProperties[BorderStyle].values)
|
||||
return setEnumProperty(properties, tag, value, enumProperties[BorderStyle].values)
|
||||
|
||||
case LeftWidth, RightWidth, TopWidth, BottomWidth:
|
||||
return border.setSizeProperty(tag, value)
|
||||
return setSizeProperty(properties, tag, value)
|
||||
|
||||
case LeftColor, RightColor, TopColor, BottomColor:
|
||||
return border.setColorProperty(tag, value)
|
||||
return setColorProperty(properties, tag, value)
|
||||
|
||||
case Left, Right, Top, Bottom:
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
if obj := ParseDataText(value); obj != nil {
|
||||
return border.setSingleBorderObject(tag, obj)
|
||||
return setSingleBorderObject(tag, obj)
|
||||
}
|
||||
|
||||
case DataObject:
|
||||
return border.setSingleBorderObject(tag, value)
|
||||
return setSingleBorderObject(tag, value)
|
||||
|
||||
case BorderProperty:
|
||||
result := []PropertyName{}
|
||||
styleTag := tag + "-" + Style
|
||||
if style := value.Get(styleTag); value != nil {
|
||||
border.properties[styleTag] = style
|
||||
properties.setRaw(styleTag, style)
|
||||
result = append(result, styleTag)
|
||||
}
|
||||
colorTag := tag + "-" + ColorTag
|
||||
if color := value.Get(colorTag); value != nil {
|
||||
border.properties[colorTag] = color
|
||||
properties.setRaw(colorTag, color)
|
||||
result = append(result, colorTag)
|
||||
}
|
||||
widthTag := tag + "-" + Width
|
||||
if width := value.Get(widthTag); value != nil {
|
||||
border.properties[widthTag] = width
|
||||
properties.setRaw(widthTag, width)
|
||||
result = append(result, widthTag)
|
||||
}
|
||||
return true
|
||||
return result
|
||||
|
||||
case ViewBorder:
|
||||
border.properties[tag+"-"+Style] = value.Style
|
||||
border.properties[tag+"-"+Width] = value.Width
|
||||
border.properties[tag+"-"+ColorTag] = value.Color
|
||||
return true
|
||||
properties.setRaw(tag+"-"+Style, value.Style)
|
||||
properties.setRaw(tag+"-"+Width, value.Width)
|
||||
properties.setRaw(tag+"-"+ColorTag, value.Color)
|
||||
return []PropertyName{tag + "-" + Style, tag + "-" + Width, tag + "-" + ColorTag}
|
||||
}
|
||||
fallthrough
|
||||
|
||||
|
@ -648,105 +648,119 @@ func (border *borderProperty) Set(tag string, value any) bool {
|
|||
ErrorLogF(`"%s" property is not compatible with the BorderProperty`, tag)
|
||||
}
|
||||
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
func (border *borderProperty) Get(tag string) any {
|
||||
tag = border.normalizeTag(tag)
|
||||
|
||||
if result, ok := border.properties[tag]; ok {
|
||||
func borderGet(properties Properties, tag PropertyName) any {
|
||||
if result := properties.getRaw(tag); result != nil {
|
||||
return result
|
||||
}
|
||||
|
||||
switch tag {
|
||||
case Left, Right, Top, Bottom:
|
||||
result := newBorderProperty(nil)
|
||||
if style, ok := border.properties[tag+"-"+Style]; ok {
|
||||
if style := properties.getRaw(tag + "-" + Style); style != nil {
|
||||
result.Set(Style, style)
|
||||
} else if style, ok := border.properties[Style]; ok {
|
||||
} else if style := properties.getRaw(Style); style != nil {
|
||||
result.Set(Style, style)
|
||||
}
|
||||
if width, ok := border.properties[tag+"-"+Width]; ok {
|
||||
if width := properties.getRaw(tag + "-" + Width); width != nil {
|
||||
result.Set(Width, width)
|
||||
} else if width, ok := border.properties[Width]; ok {
|
||||
} else if width := properties.getRaw(Width); width != nil {
|
||||
result.Set(Width, width)
|
||||
}
|
||||
if color, ok := border.properties[tag+"-"+ColorTag]; ok {
|
||||
if color := properties.getRaw(tag + "-" + ColorTag); color != nil {
|
||||
result.Set(ColorTag, color)
|
||||
} else if color, ok := border.properties[ColorTag]; ok {
|
||||
} else if color := properties.getRaw(ColorTag); color != nil {
|
||||
result.Set(ColorTag, color)
|
||||
}
|
||||
return result
|
||||
|
||||
case LeftStyle, RightStyle, TopStyle, BottomStyle:
|
||||
if style, ok := border.properties[tag]; ok {
|
||||
if style := properties.getRaw(tag); style != nil {
|
||||
return style
|
||||
}
|
||||
return border.properties[Style]
|
||||
return properties.getRaw(Style)
|
||||
|
||||
case LeftWidth, RightWidth, TopWidth, BottomWidth:
|
||||
if width, ok := border.properties[tag]; ok {
|
||||
if width := properties.getRaw(tag); width != nil {
|
||||
return width
|
||||
}
|
||||
return border.properties[Width]
|
||||
return properties.getRaw(Width)
|
||||
|
||||
case LeftColor, RightColor, TopColor, BottomColor:
|
||||
if color, ok := border.properties[tag]; ok {
|
||||
if color := properties.getRaw(tag); color != nil {
|
||||
return color
|
||||
}
|
||||
return border.properties[ColorTag]
|
||||
return properties.getRaw(ColorTag)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (border *borderProperty) delete(tag string) {
|
||||
tag = border.normalizeTag(tag)
|
||||
remove := []string{}
|
||||
func (border *borderProperty) deleteTag(tag PropertyName) bool {
|
||||
|
||||
result := false
|
||||
removeTags := func(tags []PropertyName) {
|
||||
for _, tag := range tags {
|
||||
if border.getRaw(tag) != nil {
|
||||
border.setRaw(tag, nil)
|
||||
result = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch tag {
|
||||
case Style:
|
||||
remove = []string{Style, LeftStyle, RightStyle, TopStyle, BottomStyle}
|
||||
removeTags([]PropertyName{Style, LeftStyle, RightStyle, TopStyle, BottomStyle})
|
||||
|
||||
case Width:
|
||||
remove = []string{Width, LeftWidth, RightWidth, TopWidth, BottomWidth}
|
||||
removeTags([]PropertyName{Width, LeftWidth, RightWidth, TopWidth, BottomWidth})
|
||||
|
||||
case ColorTag:
|
||||
remove = []string{ColorTag, LeftColor, RightColor, TopColor, BottomColor}
|
||||
removeTags([]PropertyName{ColorTag, LeftColor, RightColor, TopColor, BottomColor})
|
||||
|
||||
case Left, Right, Top, Bottom:
|
||||
if border.Get(Style) != nil {
|
||||
border.properties[tag+"-"+Style] = 0
|
||||
remove = []string{tag + "-" + ColorTag, tag + "-" + Width}
|
||||
result = true
|
||||
removeTags([]PropertyName{tag + "-" + ColorTag, tag + "-" + Width})
|
||||
} else {
|
||||
remove = []string{tag + "-" + Style, tag + "-" + ColorTag, tag + "-" + Width}
|
||||
removeTags([]PropertyName{tag + "-" + Style, tag + "-" + ColorTag, tag + "-" + Width})
|
||||
}
|
||||
|
||||
case LeftStyle, RightStyle, TopStyle, BottomStyle:
|
||||
if border.Get(Style) != nil {
|
||||
border.properties[tag] = 0
|
||||
} else {
|
||||
remove = []string{tag}
|
||||
if border.getRaw(tag) != nil {
|
||||
if border.Get(Style) != nil {
|
||||
border.properties[tag] = 0
|
||||
result = true
|
||||
} else {
|
||||
removeTags([]PropertyName{tag})
|
||||
}
|
||||
}
|
||||
|
||||
case LeftWidth, RightWidth, TopWidth, BottomWidth:
|
||||
if border.Get(Width) != nil {
|
||||
border.properties[tag] = AutoSize()
|
||||
} else {
|
||||
remove = []string{tag}
|
||||
if border.getRaw(tag) != nil {
|
||||
if border.Get(Width) != nil {
|
||||
border.properties[tag] = AutoSize()
|
||||
result = true
|
||||
} else {
|
||||
removeTags([]PropertyName{tag})
|
||||
}
|
||||
}
|
||||
|
||||
case LeftColor, RightColor, TopColor, BottomColor:
|
||||
if border.Get(ColorTag) != nil {
|
||||
border.properties[tag] = 0
|
||||
} else {
|
||||
remove = []string{tag}
|
||||
if border.getRaw(tag) != nil {
|
||||
if border.Get(ColorTag) != nil {
|
||||
border.properties[tag] = 0
|
||||
result = true
|
||||
} else {
|
||||
removeTags([]PropertyName{tag})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, tag := range remove {
|
||||
delete(border.properties, tag)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (border *borderProperty) ViewBorders(session Session) ViewBorders {
|
||||
|
@ -755,7 +769,7 @@ func (border *borderProperty) ViewBorders(session Session) ViewBorders {
|
|||
defWidth, _ := sizeProperty(border, Width, session)
|
||||
defColor, _ := colorProperty(border, ColorTag, session)
|
||||
|
||||
getBorder := func(prefix string) ViewBorder {
|
||||
getBorder := func(prefix PropertyName) ViewBorder {
|
||||
var result ViewBorder
|
||||
var ok bool
|
||||
if result.Style, ok = valueToEnum(border.getRaw(prefix+Style), BorderStyle, session, NoneLine); !ok {
|
||||
|
@ -784,9 +798,9 @@ func (border *borderProperty) cssStyle(builder cssBuilder, session Session) {
|
|||
if borders.Top.Style == borders.Right.Style &&
|
||||
borders.Top.Style == borders.Left.Style &&
|
||||
borders.Top.Style == borders.Bottom.Style {
|
||||
builder.add(BorderStyle, values[borders.Top.Style])
|
||||
builder.add(string(BorderStyle), values[borders.Top.Style])
|
||||
} else {
|
||||
builder.addValues(BorderStyle, " ", values[borders.Top.Style],
|
||||
builder.addValues(string(BorderStyle), " ", values[borders.Top.Style],
|
||||
values[borders.Right.Style], values[borders.Bottom.Style], values[borders.Left.Style])
|
||||
}
|
||||
}
|
||||
|
@ -870,11 +884,25 @@ func (border *ViewBorders) AllTheSame() bool {
|
|||
border.Top.Width.Equal(border.Bottom.Width)
|
||||
}
|
||||
|
||||
func getBorder(style Properties, tag string) BorderProperty {
|
||||
if value := style.Get(tag); value != nil {
|
||||
func getBorderProperty(properties Properties, tag PropertyName) BorderProperty {
|
||||
if value := properties.getRaw(tag); value != nil {
|
||||
if border, ok := value.(BorderProperty); ok {
|
||||
return border
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setBorderPropertyElement(properties Properties, mainTag, tag PropertyName, value any) []PropertyName {
|
||||
border := getBorderProperty(properties, mainTag)
|
||||
if border == nil {
|
||||
border = NewBorder(nil)
|
||||
if border.Set(tag, value) {
|
||||
properties.setRaw(mainTag, border)
|
||||
return []PropertyName{mainTag, tag}
|
||||
}
|
||||
} else if border.Set(tag, value) {
|
||||
return []PropertyName{mainTag, tag}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
177
bounds.go
177
bounds.go
|
@ -16,26 +16,33 @@ type BoundsProperty interface {
|
|||
}
|
||||
|
||||
type boundsPropertyData struct {
|
||||
propertyList
|
||||
dataProperty
|
||||
}
|
||||
|
||||
// NewBoundsProperty creates the new BoundsProperty object.
|
||||
// The following SizeUnit properties can be used: "left" (Left), "right" (Right), "top" (Top), and "bottom" (Bottom).
|
||||
func NewBoundsProperty(params Params) BoundsProperty {
|
||||
bounds := new(boundsPropertyData)
|
||||
bounds.properties = map[string]any{}
|
||||
bounds.init()
|
||||
|
||||
if params != nil {
|
||||
for _, tag := range []string{Top, Right, Bottom, Left} {
|
||||
if value, ok := params[tag]; ok {
|
||||
bounds.Set(tag, value)
|
||||
for _, tag := range bounds.supportedProperties {
|
||||
if value, ok := params[tag]; ok && value != nil {
|
||||
bounds.set(bounds, tag, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
return bounds
|
||||
}
|
||||
|
||||
func (bounds *boundsPropertyData) normalizeTag(tag string) string {
|
||||
tag = strings.ToLower(tag)
|
||||
func (bounds *boundsPropertyData) init() {
|
||||
bounds.dataProperty.init()
|
||||
bounds.normalize = normalizeBoundsTag
|
||||
bounds.supportedProperties = []PropertyName{Top, Right, Bottom, Left}
|
||||
}
|
||||
|
||||
func normalizeBoundsTag(tag PropertyName) PropertyName {
|
||||
tag = defaultNormalize(tag)
|
||||
switch tag {
|
||||
case MarginTop, PaddingTop, CellPaddingTop,
|
||||
"top-margin", "top-padding", "top-cell-padding":
|
||||
|
@ -64,12 +71,12 @@ func (bounds *boundsPropertyData) String() string {
|
|||
func (bounds *boundsPropertyData) writeString(buffer *strings.Builder, indent string) {
|
||||
buffer.WriteString("_{ ")
|
||||
comma := false
|
||||
for _, tag := range []string{Top, Right, Bottom, Left} {
|
||||
for _, tag := range []PropertyName{Top, Right, Bottom, Left} {
|
||||
if value, ok := bounds.properties[tag]; ok {
|
||||
if comma {
|
||||
buffer.WriteString(", ")
|
||||
}
|
||||
buffer.WriteString(tag)
|
||||
buffer.WriteString(string(tag))
|
||||
buffer.WriteString(" = ")
|
||||
writePropertyValue(buffer, tag, value, indent)
|
||||
comma = true
|
||||
|
@ -78,38 +85,6 @@ func (bounds *boundsPropertyData) writeString(buffer *strings.Builder, indent st
|
|||
buffer.WriteString(" }")
|
||||
}
|
||||
|
||||
func (bounds *boundsPropertyData) Remove(tag string) {
|
||||
bounds.propertyList.Remove(bounds.normalizeTag(tag))
|
||||
}
|
||||
|
||||
func (bounds *boundsPropertyData) Set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
bounds.Remove(tag)
|
||||
return true
|
||||
}
|
||||
|
||||
tag = bounds.normalizeTag(tag)
|
||||
|
||||
switch tag {
|
||||
case Top, Right, Bottom, Left:
|
||||
return bounds.setSizeProperty(tag, value)
|
||||
|
||||
default:
|
||||
ErrorLogF(`"%s" property is not compatible with the BoundsProperty`, tag)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (bounds *boundsPropertyData) Get(tag string) any {
|
||||
tag = bounds.normalizeTag(tag)
|
||||
if value, ok := bounds.properties[tag]; ok {
|
||||
return value
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bounds *boundsPropertyData) Bounds(session Session) Bounds {
|
||||
top, _ := sizeProperty(bounds, Top, session)
|
||||
right, _ := sizeProperty(bounds, Right, session)
|
||||
|
@ -141,7 +116,7 @@ func (bounds *Bounds) SetAll(value SizeUnit) {
|
|||
bounds.Left = value
|
||||
}
|
||||
|
||||
func (bounds *Bounds) setFromProperties(tag, topTag, rightTag, bottomTag, leftTag string, properties Properties, session Session) {
|
||||
func (bounds *Bounds) setFromProperties(tag, topTag, rightTag, bottomTag, leftTag PropertyName, properties Properties, session Session) {
|
||||
bounds.Top = AutoSize()
|
||||
if size, ok := sizeProperty(properties, tag, session); ok {
|
||||
bounds.Top = size
|
||||
|
@ -216,11 +191,11 @@ func (bounds *Bounds) String() string {
|
|||
bounds.Bottom.String() + "," + bounds.Left.String()
|
||||
}
|
||||
|
||||
func (bounds *Bounds) cssValue(tag string, builder cssBuilder, session Session) {
|
||||
func (bounds *Bounds) cssValue(tag PropertyName, builder cssBuilder, session Session) {
|
||||
if bounds.allFieldsEqual() {
|
||||
builder.add(tag, bounds.Top.cssString("0", session))
|
||||
builder.add(string(tag), bounds.Top.cssString("0", session))
|
||||
} else {
|
||||
builder.addValues(tag, " ",
|
||||
builder.addValues(string(tag), " ",
|
||||
bounds.Top.cssString("0", session),
|
||||
bounds.Right.cssString("0", session),
|
||||
bounds.Bottom.cssString("0", session),
|
||||
|
@ -234,8 +209,8 @@ func (bounds *Bounds) cssString(session Session) string {
|
|||
return builder.finish()
|
||||
}
|
||||
|
||||
func (properties *propertyList) setBounds(tag string, value any) bool {
|
||||
if !properties.setSimpleProperty(tag, value) {
|
||||
func setBoundsProperty(properties Properties, tag PropertyName, value any) []PropertyName {
|
||||
if !setSimpleProperty(properties, tag, value) {
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
if strings.Contains(value, ",") {
|
||||
|
@ -247,88 +222,119 @@ func (properties *propertyList) setBounds(tag string, value any) bool {
|
|||
|
||||
case 4:
|
||||
bounds := NewBoundsProperty(nil)
|
||||
for i, tag := range []string{Top, Right, Bottom, Left} {
|
||||
for i, tag := range []PropertyName{Top, Right, Bottom, Left} {
|
||||
if !bounds.Set(tag, values[i]) {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
}
|
||||
properties.properties[tag] = bounds
|
||||
return true
|
||||
properties.setRaw(tag, bounds)
|
||||
return []PropertyName{tag}
|
||||
|
||||
default:
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return properties.setSizeProperty(tag, value)
|
||||
return setSizeProperty(properties, tag, value)
|
||||
|
||||
case SizeUnit:
|
||||
properties.properties[tag] = value
|
||||
properties.setRaw(tag, value)
|
||||
|
||||
case float32:
|
||||
properties.properties[tag] = Px(float64(value))
|
||||
properties.setRaw(tag, Px(float64(value)))
|
||||
|
||||
case float64:
|
||||
properties.properties[tag] = Px(value)
|
||||
properties.setRaw(tag, Px(value))
|
||||
|
||||
case Bounds:
|
||||
bounds := NewBoundsProperty(nil)
|
||||
if value.Top.Type != Auto {
|
||||
bounds.Set(Top, value.Top)
|
||||
bounds.setRaw(Top, value.Top)
|
||||
}
|
||||
if value.Right.Type != Auto {
|
||||
bounds.Set(Right, value.Right)
|
||||
bounds.setRaw(Right, value.Right)
|
||||
}
|
||||
if value.Bottom.Type != Auto {
|
||||
bounds.Set(Bottom, value.Bottom)
|
||||
bounds.setRaw(Bottom, value.Bottom)
|
||||
}
|
||||
if value.Left.Type != Auto {
|
||||
bounds.Set(Left, value.Left)
|
||||
bounds.setRaw(Left, value.Left)
|
||||
}
|
||||
properties.properties[tag] = bounds
|
||||
properties.setRaw(tag, bounds)
|
||||
|
||||
case BoundsProperty:
|
||||
properties.properties[tag] = value
|
||||
properties.setRaw(tag, value)
|
||||
|
||||
case DataObject:
|
||||
bounds := NewBoundsProperty(nil)
|
||||
for _, tag := range []string{Top, Right, Bottom, Left} {
|
||||
if text, ok := value.PropertyValue(tag); ok {
|
||||
for _, tag := range []PropertyName{Top, Right, Bottom, Left} {
|
||||
if text, ok := value.PropertyValue(string(tag)); ok {
|
||||
if !bounds.Set(tag, text) {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
properties.properties[tag] = bounds
|
||||
properties.setRaw(tag, bounds)
|
||||
|
||||
default:
|
||||
if n, ok := isInt(value); ok {
|
||||
properties.properties[tag] = Px(float64(n))
|
||||
properties.setRaw(tag, Px(float64(n)))
|
||||
} else {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
func (properties *propertyList) boundsProperty(tag string) BoundsProperty {
|
||||
if value, ok := properties.properties[tag]; ok {
|
||||
func removeBoundsPropertySide(properties Properties, mainTag, sideTag PropertyName) []PropertyName {
|
||||
if bounds := getBoundsProperty(properties, mainTag); bounds != nil {
|
||||
if bounds.getRaw(sideTag) != nil {
|
||||
bounds.Remove(sideTag)
|
||||
if bounds.empty() {
|
||||
bounds = nil
|
||||
}
|
||||
properties.setRaw(mainTag, bounds)
|
||||
return []PropertyName{mainTag, sideTag}
|
||||
}
|
||||
}
|
||||
return []PropertyName{}
|
||||
}
|
||||
|
||||
func setBoundsPropertySide(properties Properties, mainTag, sideTag PropertyName, value any) []PropertyName {
|
||||
if value == nil {
|
||||
return removeBoundsPropertySide(properties, mainTag, sideTag)
|
||||
}
|
||||
|
||||
bounds := getBoundsProperty(properties, mainTag)
|
||||
if bounds == nil {
|
||||
bounds = NewBoundsProperty(nil)
|
||||
}
|
||||
if bounds.Set(sideTag, value) {
|
||||
properties.setRaw(mainTag, bounds)
|
||||
return []PropertyName{mainTag, sideTag}
|
||||
}
|
||||
|
||||
notCompatibleType(sideTag, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getBoundsProperty(properties Properties, tag PropertyName) BoundsProperty {
|
||||
if value := properties.getRaw(tag); value != nil {
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
bounds := NewBoundsProperty(nil)
|
||||
for _, t := range []string{Top, Right, Bottom, Left} {
|
||||
for _, t := range []PropertyName{Top, Right, Bottom, Left} {
|
||||
bounds.Set(t, value)
|
||||
}
|
||||
return bounds
|
||||
|
||||
case SizeUnit:
|
||||
bounds := NewBoundsProperty(nil)
|
||||
for _, t := range []string{Top, Right, Bottom, Left} {
|
||||
for _, t := range []PropertyName{Top, Right, Bottom, Left} {
|
||||
bounds.Set(t, value)
|
||||
}
|
||||
return bounds
|
||||
|
@ -345,29 +351,10 @@ func (properties *propertyList) boundsProperty(tag string) BoundsProperty {
|
|||
}
|
||||
}
|
||||
|
||||
return NewBoundsProperty(nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (properties *propertyList) removeBoundsSide(mainTag, sideTag string) {
|
||||
bounds := properties.boundsProperty(mainTag)
|
||||
if bounds.Get(sideTag) != nil {
|
||||
bounds.Remove(sideTag)
|
||||
properties.properties[mainTag] = bounds
|
||||
}
|
||||
}
|
||||
|
||||
func (properties *propertyList) setBoundsSide(mainTag, sideTag string, value any) bool {
|
||||
bounds := properties.boundsProperty(mainTag)
|
||||
if bounds.Set(sideTag, value) {
|
||||
properties.properties[mainTag] = bounds
|
||||
return true
|
||||
}
|
||||
|
||||
notCompatibleType(sideTag, value)
|
||||
return false
|
||||
}
|
||||
|
||||
func boundsProperty(properties Properties, tag string, session Session) (Bounds, bool) {
|
||||
func getBounds(properties Properties, tag PropertyName, session Session) (Bounds, bool) {
|
||||
if value := properties.Get(tag); value != nil {
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
|
|
|
@ -18,6 +18,7 @@ func NewButton(session Session, params Params) Button {
|
|||
|
||||
func newButton(session Session) View {
|
||||
return NewButton(session, nil)
|
||||
//return new(buttonData)
|
||||
}
|
||||
|
||||
func (button *buttonData) CreateSuperView(session Session) View {
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
package rui
|
||||
|
||||
import "strings"
|
||||
|
||||
// DrawFunction is the constant for "draw-function" property tag.
|
||||
//
|
||||
// Used by `CanvasView`.
|
||||
// Property sets the draw function of `CanvasView`.
|
||||
//
|
||||
// Supported types: `func(Canvas)`.
|
||||
const DrawFunction = "draw-function"
|
||||
const DrawFunction PropertyName = "draw-function"
|
||||
|
||||
// CanvasView interface of a custom draw view
|
||||
type CanvasView interface {
|
||||
|
@ -20,7 +18,6 @@ type CanvasView interface {
|
|||
|
||||
type canvasViewData struct {
|
||||
viewData
|
||||
drawer func(Canvas)
|
||||
}
|
||||
|
||||
// NewCanvasView creates the new custom draw view
|
||||
|
@ -32,21 +29,21 @@ func NewCanvasView(session Session, params Params) CanvasView {
|
|||
}
|
||||
|
||||
func newCanvasView(session Session) View {
|
||||
return NewCanvasView(session, nil)
|
||||
return new(canvasViewData)
|
||||
}
|
||||
|
||||
// Init initialize fields of ViewsContainer by default values
|
||||
func (canvasView *canvasViewData) init(session Session) {
|
||||
canvasView.viewData.init(session)
|
||||
canvasView.tag = "CanvasView"
|
||||
canvasView.normalize = normalizeCanvasViewTag
|
||||
canvasView.set = canvasViewSet
|
||||
canvasView.remove = canvasViewRemove
|
||||
|
||||
}
|
||||
|
||||
func (canvasView *canvasViewData) String() string {
|
||||
return getViewString(canvasView, nil)
|
||||
}
|
||||
|
||||
func (canvasView *canvasViewData) normalizeTag(tag string) string {
|
||||
tag = strings.ToLower(tag)
|
||||
func normalizeCanvasViewTag(tag PropertyName) PropertyName {
|
||||
tag = defaultNormalize(tag)
|
||||
switch tag {
|
||||
case "draw-func":
|
||||
tag = DrawFunction
|
||||
|
@ -54,51 +51,36 @@ func (canvasView *canvasViewData) normalizeTag(tag string) string {
|
|||
return tag
|
||||
}
|
||||
|
||||
func (canvasView *canvasViewData) Remove(tag string) {
|
||||
canvasView.remove(canvasView.normalizeTag(tag))
|
||||
}
|
||||
|
||||
func (canvasView *canvasViewData) remove(tag string) {
|
||||
func canvasViewRemove(view View, tag PropertyName) []PropertyName {
|
||||
if tag == DrawFunction {
|
||||
canvasView.drawer = nil
|
||||
canvasView.Redraw()
|
||||
canvasView.propertyChangedEvent(tag)
|
||||
} else {
|
||||
canvasView.viewData.remove(tag)
|
||||
if view.getRaw(DrawFunction) != nil {
|
||||
view.setRaw(DrawFunction, nil)
|
||||
if canvasView, ok := view.(CanvasView); ok {
|
||||
canvasView.Redraw()
|
||||
}
|
||||
return []PropertyName{DrawFunction}
|
||||
}
|
||||
return []PropertyName{}
|
||||
}
|
||||
|
||||
return viewRemove(view, tag)
|
||||
}
|
||||
|
||||
func (canvasView *canvasViewData) Set(tag string, value any) bool {
|
||||
return canvasView.set(canvasView.normalizeTag(tag), value)
|
||||
}
|
||||
|
||||
func (canvasView *canvasViewData) set(tag string, value any) bool {
|
||||
func canvasViewSet(view View, tag PropertyName, value any) []PropertyName {
|
||||
if tag == DrawFunction {
|
||||
if value == nil {
|
||||
canvasView.drawer = nil
|
||||
} else if fn, ok := value.(func(Canvas)); ok {
|
||||
canvasView.drawer = fn
|
||||
if fn, ok := value.(func(Canvas)); ok {
|
||||
view.setRaw(DrawFunction, fn)
|
||||
} else {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
canvasView.Redraw()
|
||||
canvasView.propertyChangedEvent(tag)
|
||||
return true
|
||||
if canvasView, ok := view.(CanvasView); ok {
|
||||
canvasView.Redraw()
|
||||
}
|
||||
return []PropertyName{DrawFunction}
|
||||
}
|
||||
|
||||
return canvasView.viewData.set(tag, value)
|
||||
}
|
||||
|
||||
func (canvasView *canvasViewData) Get(tag string) any {
|
||||
return canvasView.get(canvasView.normalizeTag(tag))
|
||||
}
|
||||
|
||||
func (canvasView *canvasViewData) get(tag string) any {
|
||||
if tag == DrawFunction {
|
||||
return canvasView.drawer
|
||||
}
|
||||
return canvasView.viewData.get(tag)
|
||||
return viewSet(view, tag, value)
|
||||
}
|
||||
|
||||
func (canvasView *canvasViewData) htmlTag() string {
|
||||
|
@ -106,14 +88,14 @@ func (canvasView *canvasViewData) htmlTag() string {
|
|||
}
|
||||
|
||||
func (canvasView *canvasViewData) Redraw() {
|
||||
if canvasView.drawer != nil {
|
||||
canvas := newCanvas(canvasView)
|
||||
canvas.ClearRect(0, 0, canvasView.frame.Width, canvasView.frame.Height)
|
||||
if canvasView.drawer != nil {
|
||||
canvasView.drawer(canvas)
|
||||
canvas := newCanvas(canvasView)
|
||||
canvas.ClearRect(0, 0, canvasView.frame.Width, canvasView.frame.Height)
|
||||
if value := canvasView.getRaw(DrawFunction); value != nil {
|
||||
if drawer, ok := value.(func(Canvas)); ok {
|
||||
drawer(canvas)
|
||||
}
|
||||
canvas.finishDraw()
|
||||
}
|
||||
canvas.finishDraw()
|
||||
}
|
||||
|
||||
func (canvasView *canvasViewData) onResize(self View, x, y, width, height float64) {
|
||||
|
|
219
checkbox.go
219
checkbox.go
|
@ -20,7 +20,7 @@ import (
|
|||
// `func(checkbox rui.Checkbox)`,
|
||||
// `func(checked bool)`,
|
||||
// `func()`.
|
||||
const CheckboxChangedEvent = "checkbox-event"
|
||||
const CheckboxChangedEvent PropertyName = "checkbox-event"
|
||||
|
||||
// Checkbox represent a Checkbox view
|
||||
type Checkbox interface {
|
||||
|
@ -29,171 +29,132 @@ type Checkbox interface {
|
|||
|
||||
type checkboxData struct {
|
||||
viewsContainerData
|
||||
checkedListeners []func(Checkbox, bool)
|
||||
}
|
||||
|
||||
// NewCheckbox create new Checkbox object and return it
|
||||
func NewCheckbox(session Session, params Params) Checkbox {
|
||||
view := new(checkboxData)
|
||||
view.init(session)
|
||||
setInitParams(view, Params{
|
||||
ClickEvent: checkboxClickListener,
|
||||
KeyDownEvent: checkboxKeyListener,
|
||||
})
|
||||
setInitParams(view, params)
|
||||
return view
|
||||
}
|
||||
|
||||
func newCheckbox(session Session) View {
|
||||
return NewCheckbox(session, nil)
|
||||
return new(checkboxData)
|
||||
}
|
||||
|
||||
func (button *checkboxData) init(session Session) {
|
||||
button.viewsContainerData.init(session)
|
||||
button.tag = "Checkbox"
|
||||
button.systemClass = "ruiGridLayout ruiCheckbox"
|
||||
button.checkedListeners = []func(Checkbox, bool){}
|
||||
}
|
||||
button.set = button.setFunc
|
||||
button.remove = button.removeFunc
|
||||
button.changed = checkboxPropertyChanged
|
||||
|
||||
func (button *checkboxData) String() string {
|
||||
return getViewString(button, nil)
|
||||
button.setRaw(ClickEvent, checkboxClickListener)
|
||||
button.setRaw(KeyDownEvent, checkboxKeyListener)
|
||||
}
|
||||
|
||||
func (button *checkboxData) Focusable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (button *checkboxData) Get(tag string) any {
|
||||
switch strings.ToLower(tag) {
|
||||
case CheckboxChangedEvent:
|
||||
return button.checkedListeners
|
||||
}
|
||||
|
||||
return button.viewsContainerData.Get(tag)
|
||||
}
|
||||
|
||||
func (button *checkboxData) Set(tag string, value any) bool {
|
||||
return button.set(tag, value)
|
||||
}
|
||||
|
||||
func (button *checkboxData) set(tag string, value any) bool {
|
||||
func checkboxPropertyChanged(view View, tag PropertyName) {
|
||||
switch tag {
|
||||
case CheckboxChangedEvent:
|
||||
if !button.setChangedListener(value) {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
|
||||
case Checked:
|
||||
oldChecked := button.checked()
|
||||
if !button.setBoolProperty(Checked, value) {
|
||||
return false
|
||||
}
|
||||
if button.created {
|
||||
checked := button.checked()
|
||||
if checked != oldChecked {
|
||||
button.changedCheckboxState(checked)
|
||||
session := view.Session()
|
||||
checked := IsCheckboxChecked(view)
|
||||
if listeners := GetCheckboxChangedListeners(view); len(listeners) > 0 {
|
||||
if checkbox, ok := view.(Checkbox); ok {
|
||||
for _, listener := range listeners {
|
||||
listener(checkbox, checked)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buffer := allocStringBuilder()
|
||||
defer freeStringBuilder(buffer)
|
||||
|
||||
checkboxHtml(view, buffer, checked)
|
||||
session.updateInnerHTML(view.htmlID()+"checkbox", buffer.String())
|
||||
|
||||
case CheckboxHorizontalAlign, CheckboxVerticalAlign:
|
||||
if !button.setEnumProperty(tag, value, enumProperties[tag].values) {
|
||||
return false
|
||||
}
|
||||
if button.created {
|
||||
htmlID := button.htmlID()
|
||||
updateCSSStyle(htmlID, button.session)
|
||||
updateInnerHTML(htmlID, button.session)
|
||||
}
|
||||
htmlID := view.htmlID()
|
||||
session := view.Session()
|
||||
updateCSSStyle(htmlID, session)
|
||||
updateInnerHTML(htmlID, session)
|
||||
|
||||
case VerticalAlign:
|
||||
if !button.setEnumProperty(tag, value, enumProperties[tag].values) {
|
||||
return false
|
||||
}
|
||||
if button.created {
|
||||
button.session.updateCSSProperty(button.htmlID()+"content", "align-items", button.cssVerticalAlign())
|
||||
}
|
||||
view.Session().updateCSSProperty(view.htmlID()+"content", "align-items", checkboxVerticalAlignCSS(view))
|
||||
|
||||
case HorizontalAlign:
|
||||
if !button.setEnumProperty(tag, value, enumProperties[tag].values) {
|
||||
return false
|
||||
}
|
||||
if button.created {
|
||||
button.session.updateCSSProperty(button.htmlID()+"content", "justify-items", button.cssHorizontalAlign())
|
||||
}
|
||||
|
||||
case CellVerticalAlign, CellHorizontalAlign, CellWidth, CellHeight:
|
||||
return false
|
||||
view.Session().updateCSSProperty(view.htmlID()+"content", "justify-items", checkboxHorizontalAlignCSS(view))
|
||||
|
||||
case AccentColor:
|
||||
if !button.setColorProperty(AccentColor, value) {
|
||||
return false
|
||||
}
|
||||
if button.created {
|
||||
updateInnerHTML(button.htmlID(), button.session)
|
||||
}
|
||||
updateInnerHTML(view.htmlID(), view.Session())
|
||||
|
||||
default:
|
||||
return button.viewsContainerData.set(tag, value)
|
||||
viewsContainerPropertyChanged(view, tag)
|
||||
}
|
||||
|
||||
button.propertyChangedEvent(tag)
|
||||
return true
|
||||
}
|
||||
|
||||
func (button *checkboxData) Remove(tag string) {
|
||||
button.remove(strings.ToLower(tag))
|
||||
}
|
||||
|
||||
func (button *checkboxData) remove(tag string) {
|
||||
func (button *checkboxData) setFunc(view View, tag PropertyName, value any) []PropertyName {
|
||||
switch tag {
|
||||
case ClickEvent:
|
||||
if !button.viewsContainerData.set(ClickEvent, checkboxClickListener) {
|
||||
delete(button.properties, tag)
|
||||
if button.viewsContainerData.setFunc(view, ClickEvent, value) != nil {
|
||||
if value := view.getRaw(ClickEvent); value != nil {
|
||||
if listeners, ok := value.([]func(View, MouseEvent)); ok {
|
||||
listeners = append(listeners, checkboxClickListener)
|
||||
view.setRaw(ClickEvent, listeners)
|
||||
return []PropertyName{ClickEvent}
|
||||
}
|
||||
}
|
||||
|
||||
return button.viewsContainerData.setFunc(view, ClickEvent, checkboxClickListener)
|
||||
}
|
||||
return nil
|
||||
|
||||
case KeyDownEvent:
|
||||
if !button.viewsContainerData.set(KeyDownEvent, checkboxKeyListener) {
|
||||
delete(button.properties, tag)
|
||||
if button.viewsContainerData.setFunc(view, KeyDownEvent, value) != nil {
|
||||
if value := view.getRaw(KeyDownEvent); value != nil {
|
||||
if listeners, ok := value.([]func(View, KeyEvent)); ok {
|
||||
listeners = append(listeners, checkboxKeyListener)
|
||||
view.setRaw(KeyDownEvent, listeners)
|
||||
return []PropertyName{KeyDownEvent}
|
||||
}
|
||||
}
|
||||
|
||||
return button.viewsContainerData.setFunc(view, KeyDownEvent, checkboxKeyListener)
|
||||
}
|
||||
return nil
|
||||
|
||||
case CheckboxChangedEvent:
|
||||
if len(button.checkedListeners) > 0 {
|
||||
button.checkedListeners = []func(Checkbox, bool){}
|
||||
}
|
||||
return setViewEventListener[Checkbox, bool](view, tag, value)
|
||||
|
||||
case Checked:
|
||||
oldChecked := button.checked()
|
||||
delete(button.properties, tag)
|
||||
if button.created && oldChecked {
|
||||
button.changedCheckboxState(false)
|
||||
}
|
||||
return setBoolProperty(view, Checked, value)
|
||||
|
||||
case CheckboxHorizontalAlign, CheckboxVerticalAlign:
|
||||
delete(button.properties, tag)
|
||||
if button.created {
|
||||
htmlID := button.htmlID()
|
||||
updateCSSStyle(htmlID, button.session)
|
||||
updateInnerHTML(htmlID, button.session)
|
||||
}
|
||||
|
||||
case VerticalAlign:
|
||||
delete(button.properties, tag)
|
||||
if button.created {
|
||||
button.session.updateCSSProperty(button.htmlID()+"content", "align-items", button.cssVerticalAlign())
|
||||
}
|
||||
|
||||
case HorizontalAlign:
|
||||
delete(button.properties, tag)
|
||||
if button.created {
|
||||
button.session.updateCSSProperty(button.htmlID()+"content", "justify-items", button.cssHorizontalAlign())
|
||||
}
|
||||
|
||||
default:
|
||||
button.viewsContainerData.remove(tag)
|
||||
return
|
||||
case CellVerticalAlign, CellHorizontalAlign, CellWidth, CellHeight:
|
||||
ErrorLogF(`"%s" property is not compatible with the BoundsProperty`, string(tag))
|
||||
return nil
|
||||
}
|
||||
button.propertyChangedEvent(tag)
|
||||
|
||||
return button.viewsContainerData.setFunc(view, tag, value)
|
||||
}
|
||||
|
||||
func (button *checkboxData) removeFunc(view View, tag PropertyName) []PropertyName {
|
||||
switch tag {
|
||||
case ClickEvent:
|
||||
button.setRaw(ClickEvent, checkboxClickListener)
|
||||
return []PropertyName{ClickEvent}
|
||||
|
||||
case KeyDownEvent:
|
||||
button.setRaw(KeyDownEvent, checkboxKeyListener)
|
||||
return []PropertyName{ClickEvent}
|
||||
}
|
||||
|
||||
return button.viewsContainerData.removeFunc(view, tag)
|
||||
}
|
||||
|
||||
func (button *checkboxData) checked() bool {
|
||||
|
@ -201,8 +162,9 @@ func (button *checkboxData) checked() bool {
|
|||
return checked
|
||||
}
|
||||
|
||||
/*
|
||||
func (button *checkboxData) changedCheckboxState(state bool) {
|
||||
for _, listener := range button.checkedListeners {
|
||||
for _, listener := range GetCheckboxChangedListeners(button) {
|
||||
listener(button, state)
|
||||
}
|
||||
|
||||
|
@ -212,8 +174,9 @@ func (button *checkboxData) changedCheckboxState(state bool) {
|
|||
button.htmlCheckbox(buffer, state)
|
||||
button.Session().updateInnerHTML(button.htmlID()+"checkbox", buffer.String())
|
||||
}
|
||||
*/
|
||||
|
||||
func checkboxClickListener(view View) {
|
||||
func checkboxClickListener(view View, _ MouseEvent) {
|
||||
view.Set(Checked, !IsCheckboxChecked(view))
|
||||
BlurView(view)
|
||||
}
|
||||
|
@ -225,17 +188,6 @@ func checkboxKeyListener(view View, event KeyEvent) {
|
|||
}
|
||||
}
|
||||
|
||||
func (button *checkboxData) setChangedListener(value any) bool {
|
||||
listeners, ok := valueToEventListeners[Checkbox, bool](value)
|
||||
if !ok {
|
||||
return false
|
||||
} else if listeners == nil {
|
||||
listeners = []func(Checkbox, bool){}
|
||||
}
|
||||
button.checkedListeners = listeners
|
||||
return true
|
||||
}
|
||||
|
||||
func (button *checkboxData) cssStyle(self View, builder cssBuilder) {
|
||||
session := button.Session()
|
||||
vAlign := GetCheckboxVerticalAlign(button)
|
||||
|
@ -265,7 +217,8 @@ func (button *checkboxData) cssStyle(self View, builder cssBuilder) {
|
|||
button.viewsContainerData.cssStyle(self, builder)
|
||||
}
|
||||
|
||||
func (button *checkboxData) htmlCheckbox(buffer *strings.Builder, checked bool) (int, int) {
|
||||
func checkboxHtml(button View, buffer *strings.Builder, checked bool) (int, int) {
|
||||
//func (button *checkboxData) htmlCheckbox(buffer *strings.Builder, checked bool) (int, int) {
|
||||
vAlign := GetCheckboxVerticalAlign(button)
|
||||
hAlign := GetCheckboxHorizontalAlign(button)
|
||||
|
||||
|
@ -317,7 +270,7 @@ func (button *checkboxData) htmlCheckbox(buffer *strings.Builder, checked bool)
|
|||
|
||||
func (button *checkboxData) htmlSubviews(self View, buffer *strings.Builder) {
|
||||
|
||||
vCheckboxAlign, hCheckboxAlign := button.htmlCheckbox(buffer, IsCheckboxChecked(button))
|
||||
vCheckboxAlign, hCheckboxAlign := checkboxHtml(button, buffer, IsCheckboxChecked(button))
|
||||
|
||||
buffer.WriteString(`<div id="`)
|
||||
buffer.WriteString(button.htmlID())
|
||||
|
@ -335,11 +288,11 @@ func (button *checkboxData) htmlSubviews(self View, buffer *strings.Builder) {
|
|||
}
|
||||
|
||||
buffer.WriteString(" align-items: ")
|
||||
buffer.WriteString(button.cssVerticalAlign())
|
||||
buffer.WriteString(checkboxVerticalAlignCSS(button))
|
||||
buffer.WriteRune(';')
|
||||
|
||||
buffer.WriteString(" justify-items: ")
|
||||
buffer.WriteString(button.cssHorizontalAlign())
|
||||
buffer.WriteString(checkboxHorizontalAlignCSS(button))
|
||||
buffer.WriteRune(';')
|
||||
|
||||
buffer.WriteString(`">`)
|
||||
|
@ -347,8 +300,8 @@ func (button *checkboxData) htmlSubviews(self View, buffer *strings.Builder) {
|
|||
buffer.WriteString(`</div>`)
|
||||
}
|
||||
|
||||
func (button *checkboxData) cssHorizontalAlign() string {
|
||||
align := GetHorizontalAlign(button)
|
||||
func checkboxHorizontalAlignCSS(view View) string {
|
||||
align := GetHorizontalAlign(view)
|
||||
values := enumProperties[CellHorizontalAlign].cssValues
|
||||
if align >= 0 && align < len(values) {
|
||||
return values[align]
|
||||
|
@ -356,8 +309,8 @@ func (button *checkboxData) cssHorizontalAlign() string {
|
|||
return values[0]
|
||||
}
|
||||
|
||||
func (button *checkboxData) cssVerticalAlign() string {
|
||||
align := GetVerticalAlign(button)
|
||||
func checkboxVerticalAlignCSS(view View) string {
|
||||
align := GetVerticalAlign(view)
|
||||
values := enumProperties[CellVerticalAlign].cssValues
|
||||
if align >= 0 && align < len(values) {
|
||||
return values[align]
|
||||
|
|
144
colorPicker.go
144
colorPicker.go
|
@ -25,7 +25,7 @@ const (
|
|||
// `func(newColor rui.Color)`,
|
||||
// `func(picker rui.ColorPicker)`,
|
||||
// `func()`.
|
||||
ColorChangedEvent = "color-changed"
|
||||
ColorChangedEvent PropertyName = "color-changed"
|
||||
|
||||
// ColorPickerValue is the constant for "color-picker-value" property tag.
|
||||
//
|
||||
|
@ -36,7 +36,7 @@ const (
|
|||
//
|
||||
// Internal type is `Color`, other types converted to it during assignment.
|
||||
// See `Color` description for more details.
|
||||
ColorPickerValue = "color-picker-value"
|
||||
ColorPickerValue PropertyName = "color-picker-value"
|
||||
)
|
||||
|
||||
// ColorPicker represent a ColorPicker view
|
||||
|
@ -46,8 +46,6 @@ type ColorPicker interface {
|
|||
|
||||
type colorPickerData struct {
|
||||
viewData
|
||||
dataList
|
||||
colorChangedListeners []func(ColorPicker, Color, Color)
|
||||
}
|
||||
|
||||
// NewColorPicker create new ColorPicker object and return it
|
||||
|
@ -59,125 +57,69 @@ func NewColorPicker(session Session, params Params) ColorPicker {
|
|||
}
|
||||
|
||||
func newColorPicker(session Session) View {
|
||||
return NewColorPicker(session, nil)
|
||||
return new(colorPickerData)
|
||||
}
|
||||
|
||||
func (picker *colorPickerData) init(session Session) {
|
||||
picker.viewData.init(session)
|
||||
picker.tag = "ColorPicker"
|
||||
picker.hasHtmlDisabled = true
|
||||
picker.colorChangedListeners = []func(ColorPicker, Color, Color){}
|
||||
picker.properties[Padding] = Px(0)
|
||||
picker.dataListInit()
|
||||
picker.normalize = normalizeColorPickerTag
|
||||
picker.set = colorPickerSet
|
||||
picker.changed = colorPickerPropertyChanged
|
||||
}
|
||||
|
||||
func (picker *colorPickerData) String() string {
|
||||
return getViewString(picker, nil)
|
||||
}
|
||||
|
||||
func (picker *colorPickerData) normalizeTag(tag string) string {
|
||||
tag = strings.ToLower(tag)
|
||||
func normalizeColorPickerTag(tag PropertyName) PropertyName {
|
||||
tag = defaultNormalize(tag)
|
||||
switch tag {
|
||||
case Value, ColorTag:
|
||||
return ColorPickerValue
|
||||
}
|
||||
|
||||
return picker.normalizeDataListTag(tag)
|
||||
return normalizeDataListTag(tag)
|
||||
}
|
||||
|
||||
func (picker *colorPickerData) Remove(tag string) {
|
||||
picker.remove(picker.normalizeTag(tag))
|
||||
}
|
||||
|
||||
func (picker *colorPickerData) remove(tag string) {
|
||||
func colorPickerSet(view View, tag PropertyName, value any) []PropertyName {
|
||||
switch tag {
|
||||
case ColorChangedEvent:
|
||||
if len(picker.colorChangedListeners) > 0 {
|
||||
picker.colorChangedListeners = []func(ColorPicker, Color, Color){}
|
||||
picker.propertyChangedEvent(tag)
|
||||
}
|
||||
return setEventWithOldListener[ColorPicker, Color](view, tag, value)
|
||||
|
||||
case ColorPickerValue:
|
||||
oldColor := GetColorPickerValue(picker)
|
||||
delete(picker.properties, ColorPickerValue)
|
||||
picker.colorChanged(oldColor)
|
||||
oldColor := GetColorPickerValue(view)
|
||||
result := setColorProperty(view, ColorPickerValue, value)
|
||||
if result != nil {
|
||||
view.setRaw("old-color", oldColor)
|
||||
}
|
||||
return result
|
||||
|
||||
case DataList:
|
||||
if len(picker.dataList.dataList) > 0 {
|
||||
picker.setDataList(picker, []string{}, true)
|
||||
}
|
||||
|
||||
default:
|
||||
picker.viewData.remove(tag)
|
||||
}
|
||||
}
|
||||
|
||||
func (picker *colorPickerData) Set(tag string, value any) bool {
|
||||
return picker.set(picker.normalizeTag(tag), value)
|
||||
}
|
||||
|
||||
func (picker *colorPickerData) set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
picker.remove(tag)
|
||||
return true
|
||||
return setDataList(view, value, "")
|
||||
}
|
||||
|
||||
return viewSet(view, tag, value)
|
||||
}
|
||||
|
||||
func colorPickerPropertyChanged(view View, tag PropertyName) {
|
||||
switch tag {
|
||||
case ColorChangedEvent:
|
||||
listeners, ok := valueToEventWithOldListeners[ColorPicker, Color](value)
|
||||
if !ok {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
} else if listeners == nil {
|
||||
listeners = []func(ColorPicker, Color, Color){}
|
||||
}
|
||||
picker.colorChangedListeners = listeners
|
||||
picker.propertyChangedEvent(tag)
|
||||
return true
|
||||
|
||||
case ColorPickerValue:
|
||||
oldColor := GetColorPickerValue(picker)
|
||||
if picker.setColorProperty(ColorPickerValue, value) {
|
||||
picker.colorChanged(oldColor)
|
||||
return true
|
||||
}
|
||||
color := GetColorPickerValue(view)
|
||||
view.Session().callFunc("setInputValue", view.htmlID(), color.rgbString())
|
||||
|
||||
case DataList:
|
||||
return picker.setDataList(picker, value, picker.created)
|
||||
if listeners := GetColorChangedListeners(view); len(listeners) > 0 {
|
||||
oldColor := Color(0)
|
||||
if value := view.getRaw("old-color"); value != nil {
|
||||
oldColor = value.(Color)
|
||||
}
|
||||
for _, listener := range listeners {
|
||||
listener(view, color, oldColor)
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return picker.viewData.set(tag, value)
|
||||
viewPropertyChanged(view, tag)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (picker *colorPickerData) colorChanged(oldColor Color) {
|
||||
if newColor := GetColorPickerValue(picker); oldColor != newColor {
|
||||
if picker.created {
|
||||
picker.session.callFunc("setInputValue", picker.htmlID(), newColor.rgbString())
|
||||
}
|
||||
for _, listener := range picker.colorChangedListeners {
|
||||
listener(picker, newColor, oldColor)
|
||||
}
|
||||
picker.propertyChangedEvent(ColorTag)
|
||||
}
|
||||
}
|
||||
|
||||
func (picker *colorPickerData) Get(tag string) any {
|
||||
return picker.get(picker.normalizeTag(tag))
|
||||
}
|
||||
|
||||
func (picker *colorPickerData) get(tag string) any {
|
||||
switch tag {
|
||||
case ColorChangedEvent:
|
||||
return picker.colorChangedListeners
|
||||
|
||||
case DataList:
|
||||
return picker.dataList.dataList
|
||||
|
||||
default:
|
||||
return picker.viewData.get(tag)
|
||||
}
|
||||
}
|
||||
|
||||
func (picker *colorPickerData) htmlTag() string {
|
||||
|
@ -185,7 +127,10 @@ func (picker *colorPickerData) htmlTag() string {
|
|||
}
|
||||
|
||||
func (picker *colorPickerData) htmlSubviews(self View, buffer *strings.Builder) {
|
||||
picker.dataListHtmlSubviews(self, buffer)
|
||||
dataListHtmlSubviews(self, buffer, func(text string, session Session) string {
|
||||
text, _ = session.resolveConstants(text)
|
||||
return text
|
||||
})
|
||||
}
|
||||
|
||||
func (picker *colorPickerData) htmlProperties(self View, buffer *strings.Builder) {
|
||||
|
@ -200,20 +145,23 @@ func (picker *colorPickerData) htmlProperties(self View, buffer *strings.Builder
|
|||
buffer.WriteString(` onclick="stopEventPropagation(this, event)"`)
|
||||
}
|
||||
|
||||
picker.dataListHtmlProperties(picker, buffer)
|
||||
dataListHtmlProperties(picker, buffer)
|
||||
}
|
||||
|
||||
func (picker *colorPickerData) handleCommand(self View, command string, data DataObject) bool {
|
||||
func (picker *colorPickerData) handleCommand(self View, command PropertyName, data DataObject) bool {
|
||||
switch command {
|
||||
case "textChanged":
|
||||
if text, ok := data.PropertyValue("text"); ok {
|
||||
oldColor := GetColorPickerValue(picker)
|
||||
if color, ok := StringToColor(text); ok {
|
||||
oldColor := GetColorPickerValue(picker)
|
||||
picker.properties[ColorPickerValue] = color
|
||||
if color != oldColor {
|
||||
for _, listener := range picker.colorChangedListeners {
|
||||
for _, listener := range GetColorChangedListeners(picker) {
|
||||
listener(picker, color, oldColor)
|
||||
}
|
||||
if listener, ok := picker.changeListener[ColorPickerValue]; ok {
|
||||
listener(picker, ColorPickerValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -233,7 +181,7 @@ func GetColorPickerValue(view View, subviewID ...string) Color {
|
|||
if value, ok := colorProperty(view, ColorPickerValue, view.Session()); ok {
|
||||
return value
|
||||
}
|
||||
for _, tag := range []string{ColorPickerValue, Value, ColorTag} {
|
||||
for _, tag := range []PropertyName{ColorPickerValue, Value, ColorTag} {
|
||||
if value := valueFromStyle(view, tag); value != nil {
|
||||
if result, ok := valueToColor(value, view.Session()); ok {
|
||||
return result
|
||||
|
|
113
columnLayout.go
113
columnLayout.go
|
@ -2,7 +2,6 @@ package rui
|
|||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Constants for [ColumnLayout] specific properties and events
|
||||
|
@ -10,7 +9,7 @@ const (
|
|||
// ColumnCount is the constant for "column-count" property tag.
|
||||
//
|
||||
// Used by `ColumnLayout`.
|
||||
// Specifies number of columns into which the content is break. Values less than zero are not valid. If this property
|
||||
// Specifies number of columns into which the content is break. Values less than zero are not valid. If this property
|
||||
// value is 0 then the number of columns is calculated based on the "column-width" property.
|
||||
//
|
||||
// Supported types: `int`, `string`.
|
||||
|
@ -18,7 +17,7 @@ const (
|
|||
// Values:
|
||||
// `0` or "0" - Use "column-width" to control how many columns will be created.
|
||||
// >= `0` or >= "0" - Тhe number of columns into which the content is divided.
|
||||
ColumnCount = "column-count"
|
||||
ColumnCount PropertyName = "column-count"
|
||||
|
||||
// ColumnWidth is the constant for "column-width" property tag.
|
||||
//
|
||||
|
@ -29,7 +28,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
ColumnWidth = "column-width"
|
||||
ColumnWidth PropertyName = "column-width"
|
||||
|
||||
// ColumnGap is the constant for "column-gap" property tag.
|
||||
//
|
||||
|
@ -40,7 +39,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
ColumnGap = "column-gap"
|
||||
ColumnGap PropertyName = "column-gap"
|
||||
|
||||
// ColumnSeparator is the constant for "column-separator" property tag.
|
||||
//
|
||||
|
@ -51,7 +50,7 @@ const (
|
|||
//
|
||||
// Internal type is `ColumnSeparatorProperty`, other types converted to it during assignment.
|
||||
// See `ColumnSeparatorProperty` and `ViewBorder` description for more details.
|
||||
ColumnSeparator = "column-separator"
|
||||
ColumnSeparator PropertyName = "column-separator"
|
||||
|
||||
// ColumnSeparatorStyle is the constant for "column-separator-style" property tag.
|
||||
//
|
||||
|
@ -66,7 +65,7 @@ const (
|
|||
// `2`(`DashedLine`) or "dashed" - Dashed line as a separator.
|
||||
// `3`(`DottedLine`) or "dotted" - Dotted line as a separator.
|
||||
// `4`(`DoubleLine`) or "double" - Double line as a separator.
|
||||
ColumnSeparatorStyle = "column-separator-style"
|
||||
ColumnSeparatorStyle PropertyName = "column-separator-style"
|
||||
|
||||
// ColumnSeparatorWidth is the constant for "column-separator-width" property tag.
|
||||
//
|
||||
|
@ -77,7 +76,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
ColumnSeparatorWidth = "column-separator-width"
|
||||
ColumnSeparatorWidth PropertyName = "column-separator-width"
|
||||
|
||||
// ColumnSeparatorColor is the constant for "column-separator-color" property tag.
|
||||
//
|
||||
|
@ -88,7 +87,7 @@ const (
|
|||
//
|
||||
// Internal type is `Color`, other types converted to it during assignment.
|
||||
// See `Color` description for more details.
|
||||
ColumnSeparatorColor = "column-separator-color"
|
||||
ColumnSeparatorColor PropertyName = "column-separator-color"
|
||||
|
||||
// ColumnFill is the constant for "column-fill" property tag.
|
||||
//
|
||||
|
@ -100,12 +99,12 @@ const (
|
|||
// Values:
|
||||
// `0`(`ColumnFillBalance`) or "balance" - Content is equally divided between columns.
|
||||
// `1`(`ColumnFillAuto`) or "auto" - Columns are filled sequentially. Content takes up only the room it needs, possibly resulting in some columns remaining empty.
|
||||
ColumnFill = "column-fill"
|
||||
ColumnFill PropertyName = "column-fill"
|
||||
|
||||
// ColumnSpanAll is the constant for "column-span-all" property tag.
|
||||
//
|
||||
// Used by `ColumnLayout`.
|
||||
// Property used in views placed inside the column layout container. Makes it possible for a view to span across all
|
||||
// Property used in views placed inside the column layout container. Makes it possible for a view to span across all
|
||||
// columns. Default value is `false`.
|
||||
//
|
||||
// Supported types: `bool`, `int`, `string`.
|
||||
|
@ -113,7 +112,7 @@ const (
|
|||
// Values:
|
||||
// `true` or `1` or "true", "yes", "on", "1" - View will span across all columns.
|
||||
// `false` or `0` or "false", "no", "off", "0" - View will be a part of a column.
|
||||
ColumnSpanAll = "column-span-all"
|
||||
ColumnSpanAll PropertyName = "column-span-all"
|
||||
)
|
||||
|
||||
// ColumnLayout represent a ColumnLayout view
|
||||
|
@ -134,22 +133,20 @@ func NewColumnLayout(session Session, params Params) ColumnLayout {
|
|||
}
|
||||
|
||||
func newColumnLayout(session Session) View {
|
||||
return NewColumnLayout(session, nil)
|
||||
return new(columnLayoutData)
|
||||
}
|
||||
|
||||
// Init initialize fields of ColumnLayout by default values
|
||||
func (ColumnLayout *columnLayoutData) init(session Session) {
|
||||
ColumnLayout.viewsContainerData.init(session)
|
||||
ColumnLayout.tag = "ColumnLayout"
|
||||
//ColumnLayout.systemClass = "ruiColumnLayout"
|
||||
func (columnLayout *columnLayoutData) init(session Session) {
|
||||
columnLayout.viewsContainerData.init(session)
|
||||
columnLayout.tag = "ColumnLayout"
|
||||
columnLayout.normalize = normalizeColumnLayoutTag
|
||||
columnLayout.changed = columnLayoutPropertyChanged
|
||||
//columnLayout.systemClass = "ruiColumnLayout"
|
||||
}
|
||||
|
||||
func (columnLayout *columnLayoutData) String() string {
|
||||
return getViewString(columnLayout, nil)
|
||||
}
|
||||
|
||||
func (columnLayout *columnLayoutData) normalizeTag(tag string) string {
|
||||
tag = strings.ToLower(tag)
|
||||
func normalizeColumnLayoutTag(tag PropertyName) PropertyName {
|
||||
tag = defaultNormalize(tag)
|
||||
switch tag {
|
||||
case Gap:
|
||||
return ColumnGap
|
||||
|
@ -157,62 +154,28 @@ func (columnLayout *columnLayoutData) normalizeTag(tag string) string {
|
|||
return tag
|
||||
}
|
||||
|
||||
func (columnLayout *columnLayoutData) Get(tag string) any {
|
||||
return columnLayout.get(columnLayout.normalizeTag(tag))
|
||||
}
|
||||
|
||||
func (columnLayout *columnLayoutData) Remove(tag string) {
|
||||
columnLayout.remove(columnLayout.normalizeTag(tag))
|
||||
}
|
||||
|
||||
func (columnLayout *columnLayoutData) remove(tag string) {
|
||||
columnLayout.viewsContainerData.remove(tag)
|
||||
if columnLayout.created {
|
||||
switch tag {
|
||||
case ColumnCount, ColumnWidth, ColumnGap:
|
||||
columnLayout.session.updateCSSProperty(columnLayout.htmlID(), tag, "")
|
||||
|
||||
case ColumnSeparator:
|
||||
columnLayout.session.updateCSSProperty(columnLayout.htmlID(), "column-rule", "")
|
||||
func columnLayoutPropertyChanged(view View, tag PropertyName) {
|
||||
switch tag {
|
||||
case ColumnSeparator:
|
||||
css := ""
|
||||
session := view.Session()
|
||||
if value := view.getRaw(ColumnSeparator); value != nil {
|
||||
separator := value.(ColumnSeparatorProperty)
|
||||
css = separator.cssValue(view.Session())
|
||||
}
|
||||
}
|
||||
}
|
||||
session.updateCSSProperty(view.htmlID(), "column-rule", css)
|
||||
|
||||
func (columnLayout *columnLayoutData) Set(tag string, value any) bool {
|
||||
return columnLayout.set(columnLayout.normalizeTag(tag), value)
|
||||
}
|
||||
|
||||
func (columnLayout *columnLayoutData) set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
columnLayout.remove(tag)
|
||||
return true
|
||||
}
|
||||
|
||||
if !columnLayout.viewsContainerData.set(tag, value) {
|
||||
return false
|
||||
}
|
||||
|
||||
if columnLayout.created {
|
||||
switch tag {
|
||||
case ColumnSeparator:
|
||||
css := ""
|
||||
session := columnLayout.Session()
|
||||
if val, ok := columnLayout.properties[ColumnSeparator]; ok {
|
||||
separator := val.(ColumnSeparatorProperty)
|
||||
css = separator.cssValue(columnLayout.Session())
|
||||
}
|
||||
session.updateCSSProperty(columnLayout.htmlID(), "column-rule", css)
|
||||
|
||||
case ColumnCount:
|
||||
session := columnLayout.Session()
|
||||
if count, ok := intProperty(columnLayout, tag, session, 0); ok && count > 0 {
|
||||
session.updateCSSProperty(columnLayout.htmlID(), tag, strconv.Itoa(count))
|
||||
} else {
|
||||
session.updateCSSProperty(columnLayout.htmlID(), tag, "auto")
|
||||
}
|
||||
case ColumnCount:
|
||||
session := view.Session()
|
||||
if count, ok := intProperty(view, tag, session, 0); ok && count > 0 {
|
||||
session.updateCSSProperty(view.htmlID(), string(ColumnCount), strconv.Itoa(count))
|
||||
} else {
|
||||
session.updateCSSProperty(view.htmlID(), string(ColumnCount), "auto")
|
||||
}
|
||||
|
||||
default:
|
||||
viewsContainerPropertyChanged(view, tag)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// GetColumnCount returns int value which specifies number of columns into which the content of
|
||||
|
|
|
@ -18,14 +18,14 @@ type ColumnSeparatorProperty interface {
|
|||
}
|
||||
|
||||
type columnSeparatorProperty struct {
|
||||
propertyList
|
||||
dataProperty
|
||||
}
|
||||
|
||||
func newColumnSeparatorProperty(value any) ColumnSeparatorProperty {
|
||||
|
||||
if value == nil {
|
||||
separator := new(columnSeparatorProperty)
|
||||
separator.properties = map[string]any{}
|
||||
separator.init()
|
||||
return separator
|
||||
}
|
||||
|
||||
|
@ -35,17 +35,18 @@ func newColumnSeparatorProperty(value any) ColumnSeparatorProperty {
|
|||
|
||||
case DataObject:
|
||||
separator := new(columnSeparatorProperty)
|
||||
separator.properties = map[string]any{}
|
||||
for _, tag := range []string{Style, Width, ColorTag} {
|
||||
if val, ok := value.PropertyValue(tag); ok && val != "" {
|
||||
separator.set(tag, value)
|
||||
separator.init()
|
||||
for _, tag := range []PropertyName{Style, Width, ColorTag} {
|
||||
if val, ok := value.PropertyValue(string(tag)); ok && val != "" {
|
||||
propertiesSet(separator, tag, value)
|
||||
}
|
||||
}
|
||||
return separator
|
||||
|
||||
case ViewBorder:
|
||||
separator := new(columnSeparatorProperty)
|
||||
separator.properties = map[string]any{
|
||||
separator.init()
|
||||
separator.properties = map[PropertyName]any{
|
||||
Style: value.Style,
|
||||
Width: value.Width,
|
||||
ColorTag: value.Color,
|
||||
|
@ -67,9 +68,9 @@ func newColumnSeparatorProperty(value any) ColumnSeparatorProperty {
|
|||
// "width" (Width). Determines the line thickness (SizeUnit).
|
||||
func NewColumnSeparator(params Params) ColumnSeparatorProperty {
|
||||
separator := new(columnSeparatorProperty)
|
||||
separator.properties = map[string]any{}
|
||||
separator.init()
|
||||
if params != nil {
|
||||
for _, tag := range []string{Style, Width, ColorTag} {
|
||||
for _, tag := range []PropertyName{Style, Width, ColorTag} {
|
||||
if value, ok := params[tag]; ok && value != nil {
|
||||
separator.Set(tag, value)
|
||||
}
|
||||
|
@ -78,8 +79,14 @@ func NewColumnSeparator(params Params) ColumnSeparatorProperty {
|
|||
return separator
|
||||
}
|
||||
|
||||
func (separator *columnSeparatorProperty) normalizeTag(tag string) string {
|
||||
tag = strings.ToLower(tag)
|
||||
func (separator *columnSeparatorProperty) init() {
|
||||
separator.dataProperty.init()
|
||||
separator.normalize = normalizeVolumnSeparatorTag
|
||||
separator.supportedProperties = []PropertyName{Style, Width, ColorTag}
|
||||
}
|
||||
|
||||
func normalizeVolumnSeparatorTag(tag PropertyName) PropertyName {
|
||||
tag = defaultNormalize(tag)
|
||||
switch tag {
|
||||
case ColumnSeparatorStyle, "separator-style":
|
||||
return Style
|
||||
|
@ -97,12 +104,12 @@ func (separator *columnSeparatorProperty) normalizeTag(tag string) string {
|
|||
func (separator *columnSeparatorProperty) writeString(buffer *strings.Builder, indent string) {
|
||||
buffer.WriteString("_{ ")
|
||||
comma := false
|
||||
for _, tag := range []string{Style, Width, ColorTag} {
|
||||
for _, tag := range []PropertyName{Style, Width, ColorTag} {
|
||||
if value, ok := separator.properties[tag]; ok {
|
||||
if comma {
|
||||
buffer.WriteString(", ")
|
||||
}
|
||||
buffer.WriteString(tag)
|
||||
buffer.WriteString(string(tag))
|
||||
buffer.WriteString(" = ")
|
||||
writePropertyValue(buffer, BorderStyle, value, indent)
|
||||
comma = true
|
||||
|
@ -116,47 +123,12 @@ func (separator *columnSeparatorProperty) String() string {
|
|||
return runStringWriter(separator)
|
||||
}
|
||||
|
||||
func (separator *columnSeparatorProperty) Remove(tag string) {
|
||||
|
||||
switch tag = separator.normalizeTag(tag); tag {
|
||||
case Style, Width, ColorTag:
|
||||
delete(separator.properties, tag)
|
||||
|
||||
default:
|
||||
ErrorLogF(`"%s" property is not compatible with the ColumnSeparatorProperty`, tag)
|
||||
func getColumnSeparatorProperty(properties Properties) ColumnSeparatorProperty {
|
||||
if val := properties.getRaw(ColumnSeparator); val != nil {
|
||||
if separator, ok := val.(ColumnSeparatorProperty); ok {
|
||||
return separator
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (separator *columnSeparatorProperty) Set(tag string, value any) bool {
|
||||
tag = separator.normalizeTag(tag)
|
||||
|
||||
if value == nil {
|
||||
separator.remove(tag)
|
||||
return true
|
||||
}
|
||||
|
||||
switch tag {
|
||||
case Style:
|
||||
return separator.setEnumProperty(Style, value, enumProperties[BorderStyle].values)
|
||||
|
||||
case Width:
|
||||
return separator.setSizeProperty(Width, value)
|
||||
|
||||
case ColorTag:
|
||||
return separator.setColorProperty(ColorTag, value)
|
||||
}
|
||||
|
||||
ErrorLogF(`"%s" property is not compatible with the ColumnSeparatorProperty`, tag)
|
||||
return false
|
||||
}
|
||||
|
||||
func (separator *columnSeparatorProperty) Get(tag string) any {
|
||||
tag = separator.normalizeTag(tag)
|
||||
|
||||
if result, ok := separator.properties[tag]; ok {
|
||||
return result
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,9 @@ func InitCustomView(customView CustomView, tag string, session Session, params P
|
|||
return true
|
||||
}
|
||||
|
||||
func (customView *CustomViewData) init(session Session) {
|
||||
}
|
||||
|
||||
// SuperView returns a super view
|
||||
func (customView *CustomViewData) SuperView() View {
|
||||
return customView.superView
|
||||
|
@ -57,29 +60,36 @@ func (customView *CustomViewData) setTag(tag string) {
|
|||
|
||||
// Get returns a value of the property with name defined by the argument.
|
||||
// The type of return value depends on the property. If the property is not set then nil is returned.
|
||||
func (customView *CustomViewData) Get(tag string) any {
|
||||
func (customView *CustomViewData) Get(tag PropertyName) any {
|
||||
return customView.superView.Get(tag)
|
||||
}
|
||||
|
||||
func (customView *CustomViewData) getRaw(tag string) any {
|
||||
func (customView *CustomViewData) getRaw(tag PropertyName) any {
|
||||
return customView.superView.getRaw(tag)
|
||||
}
|
||||
|
||||
func (customView *CustomViewData) setRaw(tag string, value any) {
|
||||
func (customView *CustomViewData) setRaw(tag PropertyName, value any) {
|
||||
customView.superView.setRaw(tag, value)
|
||||
}
|
||||
|
||||
func (customView *CustomViewData) setContent(value any) bool {
|
||||
if container, ok := customView.superView.(ViewsContainer); ok {
|
||||
return container.setContent(value)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Set 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
|
||||
func (customView *CustomViewData) Set(tag string, value any) bool {
|
||||
func (customView *CustomViewData) Set(tag PropertyName, value any) bool {
|
||||
return customView.superView.Set(tag, value)
|
||||
}
|
||||
|
||||
// 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
|
||||
func (customView *CustomViewData) SetAnimated(tag string, value any, animation Animation) bool {
|
||||
func (customView *CustomViewData) SetAnimated(tag PropertyName, value any, animation Animation) bool {
|
||||
return customView.superView.SetAnimated(tag, value, animation)
|
||||
}
|
||||
|
||||
|
@ -88,20 +98,24 @@ func (customView *CustomViewData) SetParams(params Params) bool {
|
|||
}
|
||||
|
||||
// SetChangeListener set the function to track the change of the View property
|
||||
func (customView *CustomViewData) SetChangeListener(tag string, listener func(View, string)) {
|
||||
func (customView *CustomViewData) SetChangeListener(tag PropertyName, listener func(View, PropertyName)) {
|
||||
customView.superView.SetChangeListener(tag, listener)
|
||||
}
|
||||
|
||||
// Remove removes the property with name defined by the argument
|
||||
func (customView *CustomViewData) Remove(tag string) {
|
||||
func (customView *CustomViewData) Remove(tag PropertyName) {
|
||||
customView.superView.Remove(tag)
|
||||
}
|
||||
|
||||
// AllTags returns an array of the set properties
|
||||
func (customView *CustomViewData) AllTags() []string {
|
||||
func (customView *CustomViewData) AllTags() []PropertyName {
|
||||
return customView.superView.AllTags()
|
||||
}
|
||||
|
||||
func (customView *CustomViewData) empty() bool {
|
||||
return customView.superView.empty()
|
||||
}
|
||||
|
||||
// Clear removes all properties
|
||||
func (customView *CustomViewData) Clear() {
|
||||
customView.superView.Clear()
|
||||
|
@ -182,7 +196,7 @@ func (customView *CustomViewData) onItemResize(self View, index string, x, y, wi
|
|||
customView.superView.onItemResize(customView.superView, index, x, y, width, height)
|
||||
}
|
||||
|
||||
func (customView *CustomViewData) handleCommand(self View, command string, data DataObject) bool {
|
||||
func (customView *CustomViewData) handleCommand(self View, command PropertyName, data DataObject) bool {
|
||||
return customView.superView.handleCommand(customView.superView, command, data)
|
||||
}
|
||||
|
||||
|
@ -210,6 +224,10 @@ func (customView *CustomViewData) htmlProperties(self View, buffer *strings.Buil
|
|||
customView.superView.htmlProperties(customView.superView, buffer)
|
||||
}
|
||||
|
||||
func (customView *CustomViewData) htmlDisabledProperty() bool {
|
||||
return customView.superView.htmlDisabledProperty()
|
||||
}
|
||||
|
||||
func (customView *CustomViewData) cssStyle(self View, builder cssBuilder) {
|
||||
customView.superView.cssStyle(customView.superView, builder)
|
||||
}
|
||||
|
@ -274,9 +292,9 @@ func (customView *CustomViewData) ViewIndex(view View) int {
|
|||
return -1
|
||||
}
|
||||
|
||||
func (customView *CustomViewData) exscludeTags() []string {
|
||||
func (customView *CustomViewData) exscludeTags() []PropertyName {
|
||||
if customView.superView != nil {
|
||||
exsclude := []string{}
|
||||
exsclude := []PropertyName{}
|
||||
for tag, value := range customView.defaultParams {
|
||||
if value == customView.superView.getRaw(tag) {
|
||||
exsclude = append(exsclude, tag)
|
||||
|
@ -290,7 +308,10 @@ func (customView *CustomViewData) exscludeTags() []string {
|
|||
// String convert internal representation of a [CustomViewData] into a string.
|
||||
func (customView *CustomViewData) String() string {
|
||||
if customView.superView != nil {
|
||||
return getViewString(customView, customView.exscludeTags())
|
||||
buffer := allocStringBuilder()
|
||||
defer freeStringBuilder(buffer)
|
||||
writeViewStyle(customView.tag, customView, buffer, "", customView.exscludeTags())
|
||||
return buffer.String()
|
||||
}
|
||||
return customView.tag + " { }"
|
||||
}
|
||||
|
@ -302,7 +323,7 @@ func (customView *CustomViewData) setScroll(x, y, width, height float64) {
|
|||
}
|
||||
|
||||
// Transition returns the transition animation of the property(tag). Returns nil is there is no transition animation.
|
||||
func (customView *CustomViewData) Transition(tag string) Animation {
|
||||
func (customView *CustomViewData) Transition(tag PropertyName) Animation {
|
||||
if customView.superView != nil {
|
||||
return customView.superView.Transition(tag)
|
||||
}
|
||||
|
@ -310,17 +331,17 @@ func (customView *CustomViewData) Transition(tag string) Animation {
|
|||
}
|
||||
|
||||
// Transitions returns a map of transition animations. The result is always non-nil.
|
||||
func (customView *CustomViewData) Transitions() map[string]Animation {
|
||||
func (customView *CustomViewData) Transitions() map[PropertyName]Animation {
|
||||
if customView.superView != nil {
|
||||
return customView.superView.Transitions()
|
||||
}
|
||||
return map[string]Animation{}
|
||||
return map[PropertyName]Animation{}
|
||||
}
|
||||
|
||||
// SetTransition sets the transition animation for the property if "animation" argument is not nil, and
|
||||
// removes the transition animation of the property if "animation" argument is nil.
|
||||
// The "tag" argument is the property name.
|
||||
func (customView *CustomViewData) SetTransition(tag string, animation Animation) {
|
||||
func (customView *CustomViewData) SetTransition(tag PropertyName, animation Animation) {
|
||||
if customView.superView != nil {
|
||||
customView.superView.SetTransition(tag, animation)
|
||||
}
|
||||
|
|
6
data.go
6
data.go
|
@ -212,12 +212,12 @@ func (object *dataObject) ToParams() Params {
|
|||
switch node.Type() {
|
||||
case TextNode:
|
||||
if text := node.Text(); text != "" {
|
||||
params[node.Tag()] = text
|
||||
params[PropertyName(node.Tag())] = text
|
||||
}
|
||||
|
||||
case ObjectNode:
|
||||
if obj := node.Object(); obj != nil {
|
||||
params[node.Tag()] = node.Object()
|
||||
params[PropertyName(node.Tag())] = node.Object()
|
||||
}
|
||||
|
||||
case ArrayNode:
|
||||
|
@ -234,7 +234,7 @@ func (object *dataObject) ToParams() Params {
|
|||
}
|
||||
}
|
||||
if len(array) > 0 {
|
||||
params[node.Tag()] = array
|
||||
params[PropertyName(node.Tag())] = array
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
265
dataList.go
265
dataList.go
|
@ -1,6 +1,11 @@
|
|||
package rui
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// DataList is the constant for "data-list" property tag.
|
||||
|
@ -10,8 +15,8 @@ const (
|
|||
// Usage in `ColorPicker`:
|
||||
// List of pre-defined colors.
|
||||
//
|
||||
// Supported types: `[]string`, `string`, `[]fmt.Stringer`, `[]Color`, `[]SizeUnit`, `[]AngleUnit`, `[]any` containing
|
||||
// elements of `string`, `fmt.Stringer`, `bool`, `rune`, `float32`, `float64`, `int`, `int8` … `int64`, `uint`, `uint8` …
|
||||
// Supported types: `[]string`, `string`, `[]fmt.Stringer`, `[]Color`, `[]SizeUnit`, `[]AngleUnit`, `[]any` containing
|
||||
// elements of `string`, `fmt.Stringer`, `bool`, `rune`, `float32`, `float64`, `int`, `int8` … `int64`, `uint`, `uint8` …
|
||||
// `uint64`.
|
||||
//
|
||||
// Internal type is `[]string`, other types converted to it during assignment.
|
||||
|
@ -25,12 +30,12 @@ const (
|
|||
// `[]any` - this array must contain only types which were listed in Types section.
|
||||
//
|
||||
// Usage in `DatePicker`:
|
||||
// List of predefined dates. If we set this property, date picker may have a drop-down menu with a list of these values.
|
||||
// Some browsers may ignore this property, such as Safari for macOS. The value of this property must be an array of
|
||||
// List of predefined dates. If we set this property, date picker may have a drop-down menu with a list of these values.
|
||||
// Some browsers may ignore this property, such as Safari for macOS. The value of this property must be an array of
|
||||
// strings in the format "YYYY-MM-DD".
|
||||
//
|
||||
// Supported types: `[]string`, `string`, `[]fmt.Stringer`, `[]Color`, `[]SizeUnit`, `[]AngleUnit`, `[]any` containing
|
||||
// elements of `string`, `fmt.Stringer`, `bool`, `rune`, `float32`, `float64`, `int`, `int8` … `int64`, `uint`, `uint8` …
|
||||
// Supported types: `[]string`, `string`, `[]fmt.Stringer`, `[]Color`, `[]SizeUnit`, `[]AngleUnit`, `[]any` containing
|
||||
// elements of `string`, `fmt.Stringer`, `bool`, `rune`, `float32`, `float64`, `int`, `int8` … `int64`, `uint`, `uint8` …
|
||||
// `uint64`.
|
||||
//
|
||||
// Internal type is `[]string`, other types converted to it during assignment.
|
||||
|
@ -46,8 +51,8 @@ const (
|
|||
// Usage in `EditView`:
|
||||
// Array of recommended values.
|
||||
//
|
||||
// Supported types: `[]string`, `string`, `[]fmt.Stringer`, `[]Color`, `[]SizeUnit`, `[]AngleUnit`, `[]any` containing
|
||||
// elements of `string`, `fmt.Stringer`, `bool`, `rune`, `float32`, `float64`, `int`, `int8` … `int64`, `uint`, `uint8` …
|
||||
// Supported types: `[]string`, `string`, `[]fmt.Stringer`, `[]Color`, `[]SizeUnit`, `[]AngleUnit`, `[]any` containing
|
||||
// elements of `string`, `fmt.Stringer`, `bool`, `rune`, `float32`, `float64`, `int`, `int8` … `int64`, `uint`, `uint8` …
|
||||
// `uint64`.
|
||||
//
|
||||
// Internal type is `[]string`, other types converted to it during assignment.
|
||||
|
@ -63,8 +68,8 @@ const (
|
|||
// Usage in `NumberPicker`:
|
||||
// Specify an array of recommended values.
|
||||
//
|
||||
// Supported types: `[]string`, `string`, `[]fmt.Stringer`, `[]Color`, `[]SizeUnit`, `[]AngleUnit`, `[]float`, `[]int`,
|
||||
// `[]bool`, `[]any` containing elements of `string`, `fmt.Stringer`, `bool`, `rune`, `float32`, `float64`, `int`, `int8`
|
||||
// Supported types: `[]string`, `string`, `[]fmt.Stringer`, `[]Color`, `[]SizeUnit`, `[]AngleUnit`, `[]float`, `[]int`,
|
||||
// `[]bool`, `[]any` containing elements of `string`, `fmt.Stringer`, `bool`, `rune`, `float32`, `float64`, `int`, `int8`
|
||||
// … `int64`, `uint`, `uint8` … `uint64`.
|
||||
//
|
||||
// Internal type is `[]string`, other types converted to it during assignment.
|
||||
|
@ -82,11 +87,11 @@ const (
|
|||
// `[]any` - an array which may contain types listed in Types section above, each value will be converted to a `string` and wrapped to array.
|
||||
//
|
||||
// Usage in `TimePicker`:
|
||||
// An array of recommended values. The value of this property must be an array of strings in the format "HH:MM:SS" or
|
||||
// An array of recommended values. The value of this property must be an array of strings in the format "HH:MM:SS" or
|
||||
// "HH:MM".
|
||||
//
|
||||
// Supported types: `[]string`, `string`, `[]fmt.Stringer`, `[]Color`, `[]SizeUnit`, `[]AngleUnit`, `[]any` containing
|
||||
// elements of `string`, `fmt.Stringer`, `bool`, `rune`, `float32`, `float64`, `int`, `int8` … `int64`, `uint`, `uint8` …
|
||||
// Supported types: `[]string`, `string`, `[]fmt.Stringer`, `[]Color`, `[]SizeUnit`, `[]AngleUnit`, `[]any` containing
|
||||
// elements of `string`, `fmt.Stringer`, `bool`, `rune`, `float32`, `float64`, `int`, `int8` … `int64`, `uint`, `uint8` …
|
||||
// `uint64`.
|
||||
//
|
||||
// Internal type is `[]string`, other types converted to it during assignment.
|
||||
|
@ -98,23 +103,14 @@ const (
|
|||
// `[]Color` - An array of color values which will be converted to a string array.
|
||||
// `[]SizeUnit` - an array of size unit values which will be converted to a string array.
|
||||
// `[]any` - this array must contain only types which were listed in Types section.
|
||||
DataList = "data-list"
|
||||
DataList PropertyName = "data-list"
|
||||
)
|
||||
|
||||
type dataList struct {
|
||||
dataList []string
|
||||
dataListHtml bool
|
||||
}
|
||||
|
||||
func (list *dataList) dataListInit() {
|
||||
list.dataList = []string{}
|
||||
}
|
||||
|
||||
func (list *dataList) dataListID(view View) string {
|
||||
func dataListID(view View) string {
|
||||
return view.htmlID() + "-datalist"
|
||||
}
|
||||
|
||||
func (list *dataList) normalizeDataListTag(tag string) string {
|
||||
func normalizeDataListTag(tag PropertyName) PropertyName {
|
||||
switch tag {
|
||||
case "datalist":
|
||||
return DataList
|
||||
|
@ -123,6 +119,210 @@ func (list *dataList) normalizeDataListTag(tag string) string {
|
|||
return tag
|
||||
}
|
||||
|
||||
func setDataList(properties Properties, value any, dateTimeFormat string) []PropertyName {
|
||||
if items, ok := anyToStringArray(value, timeFormat); ok {
|
||||
properties.setRaw(DataList, items)
|
||||
return []PropertyName{DataList}
|
||||
}
|
||||
|
||||
notCompatibleType(DataList, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func anyToStringArray(value any, dateTimeFormat string) ([]string, bool) {
|
||||
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
return []string{value}, true
|
||||
|
||||
case []string:
|
||||
return value, true
|
||||
|
||||
case []DataValue:
|
||||
items := make([]string, 0, len(value))
|
||||
for _, val := range value {
|
||||
if !val.IsObject() {
|
||||
items = append(items, val.Value())
|
||||
}
|
||||
}
|
||||
return items, true
|
||||
|
||||
case []fmt.Stringer:
|
||||
items := make([]string, len(value))
|
||||
for i, str := range value {
|
||||
items[i] = str.String()
|
||||
}
|
||||
return items, true
|
||||
|
||||
case []Color:
|
||||
items := make([]string, len(value))
|
||||
for i, str := range value {
|
||||
items[i] = str.String()
|
||||
}
|
||||
return items, true
|
||||
|
||||
case []SizeUnit:
|
||||
items := make([]string, len(value))
|
||||
for i, str := range value {
|
||||
items[i] = str.String()
|
||||
}
|
||||
return items, true
|
||||
|
||||
case []AngleUnit:
|
||||
items := make([]string, len(value))
|
||||
for i, str := range value {
|
||||
items[i] = str.String()
|
||||
}
|
||||
return items, true
|
||||
|
||||
case []float32:
|
||||
items := make([]string, len(value))
|
||||
for i, val := range value {
|
||||
items[i] = fmt.Sprintf("%g", float64(val))
|
||||
}
|
||||
return items, true
|
||||
|
||||
case []float64:
|
||||
items := make([]string, len(value))
|
||||
for i, val := range value {
|
||||
items[i] = fmt.Sprintf("%g", val)
|
||||
}
|
||||
return items, true
|
||||
|
||||
case []int:
|
||||
return intArrayToStringArray(value), true
|
||||
|
||||
case []uint:
|
||||
return intArrayToStringArray(value), true
|
||||
|
||||
case []int8:
|
||||
return intArrayToStringArray(value), true
|
||||
|
||||
case []uint8:
|
||||
return intArrayToStringArray(value), true
|
||||
|
||||
case []int16:
|
||||
return intArrayToStringArray(value), true
|
||||
|
||||
case []uint16:
|
||||
return intArrayToStringArray(value), true
|
||||
|
||||
case []int32:
|
||||
return intArrayToStringArray(value), true
|
||||
|
||||
case []uint32:
|
||||
return intArrayToStringArray(value), true
|
||||
|
||||
case []int64:
|
||||
return intArrayToStringArray(value), true
|
||||
|
||||
case []uint64:
|
||||
return intArrayToStringArray(value), true
|
||||
|
||||
case []bool:
|
||||
items := make([]string, len(value))
|
||||
for i, val := range value {
|
||||
if val {
|
||||
items[i] = "true"
|
||||
} else {
|
||||
items[i] = "false"
|
||||
}
|
||||
}
|
||||
return items, true
|
||||
|
||||
case []time.Time:
|
||||
if dateTimeFormat == "" {
|
||||
dateTimeFormat = dateFormat + " " + timeFormat
|
||||
}
|
||||
|
||||
items := make([]string, len(value))
|
||||
for i, val := range value {
|
||||
items[i] = val.Format(dateTimeFormat)
|
||||
}
|
||||
return items, true
|
||||
|
||||
case []any:
|
||||
items := make([]string, 0, len(value))
|
||||
for _, v := range value {
|
||||
switch val := v.(type) {
|
||||
case string:
|
||||
items = append(items, val)
|
||||
|
||||
case fmt.Stringer:
|
||||
items = append(items, val.String())
|
||||
|
||||
case bool:
|
||||
if val {
|
||||
items = append(items, "true")
|
||||
} else {
|
||||
items = append(items, "false")
|
||||
}
|
||||
|
||||
case float32:
|
||||
items = append(items, fmt.Sprintf("%g", float64(val)))
|
||||
|
||||
case float64:
|
||||
items = append(items, fmt.Sprintf("%g", val))
|
||||
|
||||
case rune:
|
||||
items = append(items, string(val))
|
||||
|
||||
default:
|
||||
if n, ok := isInt(v); ok {
|
||||
items = append(items, strconv.Itoa(n))
|
||||
} else {
|
||||
return []string{}, false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return items, true
|
||||
}
|
||||
|
||||
return []string{}, false
|
||||
}
|
||||
|
||||
func getDataListProperty(properties Properties) []string {
|
||||
if value := properties.getRaw(DataList); value != nil {
|
||||
if items, ok := value.([]string); ok {
|
||||
return items
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func dataListHtmlSubviews(view View, buffer *strings.Builder, normalizeItem func(text string, session Session) string) {
|
||||
if items := getDataListProperty(view); len(items) > 0 {
|
||||
session := view.Session()
|
||||
buffer.WriteString(`<datalist id="`)
|
||||
buffer.WriteString(dataListID(view))
|
||||
buffer.WriteString(`">`)
|
||||
for _, text := range items {
|
||||
text = normalizeItem(text, session)
|
||||
|
||||
if strings.ContainsRune(text, '"') {
|
||||
text = strings.ReplaceAll(text, `"`, `"`)
|
||||
}
|
||||
if strings.ContainsRune(text, '\n') {
|
||||
text = strings.ReplaceAll(text, "\n", `\n`)
|
||||
}
|
||||
buffer.WriteString(`<option value="`)
|
||||
buffer.WriteString(text)
|
||||
buffer.WriteString(`"></option>`)
|
||||
}
|
||||
buffer.WriteString(`</datalist>`)
|
||||
}
|
||||
}
|
||||
|
||||
func dataListHtmlProperties(view View, buffer *strings.Builder) {
|
||||
if len(getDataListProperty(view)) > 0 {
|
||||
buffer.WriteString(` list="`)
|
||||
buffer.WriteString(dataListID(view))
|
||||
buffer.WriteString(`"`)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func (list *dataList) setDataList(view View, value any, created bool) bool {
|
||||
items, ok := anyToStringArray(value)
|
||||
if !ok {
|
||||
|
@ -133,7 +333,7 @@ func (list *dataList) setDataList(view View, value any, created bool) bool {
|
|||
list.dataList = items
|
||||
if created {
|
||||
session := view.Session()
|
||||
dataListID := list.dataListID(view)
|
||||
dataListID := dataListID(view)
|
||||
buffer := allocStringBuilder()
|
||||
defer freeStringBuilder(buffer)
|
||||
|
||||
|
@ -162,7 +362,7 @@ func (list *dataList) dataListHtmlSubviews(view View, buffer *strings.Builder) {
|
|||
|
||||
func (list *dataList) dataListHtmlCode(view View, buffer *strings.Builder) {
|
||||
buffer.WriteString(`<datalist id="`)
|
||||
buffer.WriteString(list.dataListID(view))
|
||||
buffer.WriteString(dataListID(view))
|
||||
buffer.WriteString(`">`)
|
||||
list.dataListItemsHtml(buffer)
|
||||
buffer.WriteString(`</datalist>`)
|
||||
|
@ -185,10 +385,11 @@ func (list *dataList) dataListItemsHtml(buffer *strings.Builder) {
|
|||
func (list *dataList) dataListHtmlProperties(view View, buffer *strings.Builder) {
|
||||
if len(list.dataList) > 0 {
|
||||
buffer.WriteString(` list="`)
|
||||
buffer.WriteString(list.dataListID(view))
|
||||
buffer.WriteString(dataListID(view))
|
||||
buffer.WriteString(`"`)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// GetDataList returns the data list of an editor.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
|
@ -198,11 +399,7 @@ func GetDataList(view View, subviewID ...string) []string {
|
|||
}
|
||||
|
||||
if view != nil {
|
||||
if value := view.Get(DataList); value != nil {
|
||||
if list, ok := value.([]string); ok {
|
||||
return list
|
||||
}
|
||||
}
|
||||
return getDataListProperty(view)
|
||||
}
|
||||
|
||||
return []string{}
|
||||
|
|
337
datePicker.go
337
datePicker.go
|
@ -27,7 +27,7 @@ const (
|
|||
// `func(newDate time.Time)`,
|
||||
// `func(picker rui.DatePicker)`,
|
||||
// `func()`.
|
||||
DateChangedEvent = "date-changed"
|
||||
DateChangedEvent PropertyName = "date-changed"
|
||||
|
||||
// DatePickerMin is the constant for "date-picker-min" property tag.
|
||||
//
|
||||
|
@ -50,7 +50,7 @@ const (
|
|||
// "MM/DD/YYYY" - "01/02/2024".
|
||||
// "MM/DD/YY" - "01/02/24".
|
||||
// "MMDDYY" - "010224".
|
||||
DatePickerMin = "date-picker-min"
|
||||
DatePickerMin PropertyName = "date-picker-min"
|
||||
|
||||
// DatePickerMax is the constant for "date-picker-max" property tag.
|
||||
//
|
||||
|
@ -73,7 +73,7 @@ const (
|
|||
// "MM/DD/YYYY" - "01/02/2024".
|
||||
// "MM/DD/YY" - "01/02/24".
|
||||
// "MMDDYY" - "010224".
|
||||
DatePickerMax = "date-picker-max"
|
||||
DatePickerMax PropertyName = "date-picker-max"
|
||||
|
||||
// DatePickerStep is the constant for "date-picker-step" property tag.
|
||||
//
|
||||
|
@ -84,7 +84,7 @@ const (
|
|||
//
|
||||
// Values:
|
||||
// >= `0` or >= "0" - Step value in days used to increment or decrement date.
|
||||
DatePickerStep = "date-picker-step"
|
||||
DatePickerStep PropertyName = "date-picker-step"
|
||||
|
||||
// DatePickerValue is the constant for "date-picker-value" property tag.
|
||||
//
|
||||
|
@ -107,7 +107,7 @@ const (
|
|||
// "MM/DD/YYYY" - "01/02/2024".
|
||||
// "MM/DD/YY" - "01/02/24".
|
||||
// "MMDDYY" - "010224".
|
||||
DatePickerValue = "date-picker-value"
|
||||
DatePickerValue PropertyName = "date-picker-value"
|
||||
|
||||
dateFormat = "2006-01-02"
|
||||
)
|
||||
|
@ -119,8 +119,6 @@ type DatePicker interface {
|
|||
|
||||
type datePickerData struct {
|
||||
viewData
|
||||
dataList
|
||||
dateChangedListeners []func(DatePicker, time.Time, time.Time)
|
||||
}
|
||||
|
||||
// NewDatePicker create new DatePicker object and return it
|
||||
|
@ -132,248 +130,164 @@ func NewDatePicker(session Session, params Params) DatePicker {
|
|||
}
|
||||
|
||||
func newDatePicker(session Session) View {
|
||||
return NewDatePicker(session, nil)
|
||||
return new(datePickerData) // NewDatePicker(session, nil)
|
||||
}
|
||||
|
||||
func (picker *datePickerData) init(session Session) {
|
||||
picker.viewData.init(session)
|
||||
picker.tag = "DatePicker"
|
||||
picker.hasHtmlDisabled = true
|
||||
picker.dateChangedListeners = []func(DatePicker, time.Time, time.Time){}
|
||||
picker.dataListInit()
|
||||
}
|
||||
|
||||
func (picker *datePickerData) String() string {
|
||||
return getViewString(picker, nil)
|
||||
picker.normalize = normalizeDatePickerTag
|
||||
picker.set = datePickerSet
|
||||
picker.changed = datePickerPropertyChanged
|
||||
}
|
||||
|
||||
func (picker *datePickerData) Focusable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (picker *datePickerData) normalizeTag(tag string) string {
|
||||
tag = strings.ToLower(tag)
|
||||
func normalizeDatePickerTag(tag PropertyName) PropertyName {
|
||||
tag = defaultNormalize(tag)
|
||||
switch tag {
|
||||
case Type, Min, Max, Step, Value:
|
||||
return "date-picker-" + tag
|
||||
}
|
||||
|
||||
return tag
|
||||
return normalizeDataListTag(tag)
|
||||
}
|
||||
|
||||
func (picker *datePickerData) Remove(tag string) {
|
||||
picker.remove(picker.normalizeTag(tag))
|
||||
}
|
||||
|
||||
func (picker *datePickerData) remove(tag string) {
|
||||
switch tag {
|
||||
case DateChangedEvent:
|
||||
if len(picker.dateChangedListeners) > 0 {
|
||||
picker.dateChangedListeners = []func(DatePicker, time.Time, time.Time){}
|
||||
picker.propertyChangedEvent(tag)
|
||||
}
|
||||
return
|
||||
|
||||
case DatePickerMin:
|
||||
delete(picker.properties, DatePickerMin)
|
||||
if picker.created {
|
||||
picker.session.removeProperty(picker.htmlID(), Min)
|
||||
}
|
||||
|
||||
case DatePickerMax:
|
||||
delete(picker.properties, DatePickerMax)
|
||||
if picker.created {
|
||||
picker.session.removeProperty(picker.htmlID(), Max)
|
||||
}
|
||||
|
||||
case DatePickerStep:
|
||||
delete(picker.properties, DatePickerStep)
|
||||
if picker.created {
|
||||
picker.session.removeProperty(picker.htmlID(), Step)
|
||||
}
|
||||
|
||||
case DatePickerValue:
|
||||
if _, ok := picker.properties[DatePickerValue]; ok {
|
||||
oldDate := GetDatePickerValue(picker)
|
||||
delete(picker.properties, DatePickerValue)
|
||||
date := GetDatePickerValue(picker)
|
||||
if picker.created {
|
||||
picker.session.callFunc("setInputValue", picker.htmlID(), date.Format(dateFormat))
|
||||
func stringToDate(value string) (time.Time, bool) {
|
||||
format := "20060102"
|
||||
if strings.ContainsRune(value, '-') {
|
||||
if part := strings.Split(value, "-"); len(part) == 3 {
|
||||
if part[0] != "" && part[0][0] > '9' {
|
||||
if len(part[2]) == 2 {
|
||||
format = "Jan-02-06"
|
||||
} else {
|
||||
format = "Jan-02-2006"
|
||||
}
|
||||
} else if part[1] != "" && part[1][0] > '9' {
|
||||
format = "02-Jan-2006"
|
||||
} else {
|
||||
format = "2006-01-02"
|
||||
}
|
||||
for _, listener := range picker.dateChangedListeners {
|
||||
listener(picker, date, oldDate)
|
||||
}
|
||||
} else if strings.ContainsRune(value, ' ') {
|
||||
if part := strings.Split(value, " "); len(part) == 3 {
|
||||
if part[0] != "" && part[0][0] > '9' {
|
||||
format = "January 02, 2006"
|
||||
} else {
|
||||
format = "02 January 2006"
|
||||
}
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
case DataList:
|
||||
if len(picker.dataList.dataList) > 0 {
|
||||
picker.setDataList(picker, []string{}, true)
|
||||
} else if strings.ContainsRune(value, '/') {
|
||||
if part := strings.Split(value, "/"); len(part) == 3 {
|
||||
if len(part[2]) == 2 {
|
||||
format = "01/02/06"
|
||||
} else {
|
||||
format = "01/02/2006"
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
picker.viewData.remove(tag)
|
||||
return
|
||||
}
|
||||
picker.propertyChangedEvent(tag)
|
||||
}
|
||||
|
||||
func (picker *datePickerData) Set(tag string, value any) bool {
|
||||
return picker.set(picker.normalizeTag(tag), value)
|
||||
}
|
||||
|
||||
func (picker *datePickerData) set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
picker.remove(tag)
|
||||
return true
|
||||
} else if len(value) == 6 {
|
||||
format = "010206"
|
||||
}
|
||||
|
||||
setTimeValue := func(tag string) (time.Time, bool) {
|
||||
if date, err := time.Parse(format, value); err == nil {
|
||||
return date, true
|
||||
}
|
||||
return time.Now(), false
|
||||
}
|
||||
|
||||
func datePickerSet(view View, tag PropertyName, value any) []PropertyName {
|
||||
|
||||
setDateValue := func(tag PropertyName) []PropertyName {
|
||||
switch value := value.(type) {
|
||||
case time.Time:
|
||||
picker.properties[tag] = value
|
||||
return value, true
|
||||
view.setRaw(tag, value)
|
||||
return []PropertyName{tag}
|
||||
|
||||
case string:
|
||||
if text, ok := picker.Session().resolveConstants(value); ok {
|
||||
format := "20060102"
|
||||
if strings.ContainsRune(text, '-') {
|
||||
if part := strings.Split(text, "-"); len(part) == 3 {
|
||||
if part[0] != "" && part[0][0] > '9' {
|
||||
if len(part[2]) == 2 {
|
||||
format = "Jan-02-06"
|
||||
} else {
|
||||
format = "Jan-02-2006"
|
||||
}
|
||||
} else if part[1] != "" && part[1][0] > '9' {
|
||||
format = "02-Jan-2006"
|
||||
} else {
|
||||
format = "2006-01-02"
|
||||
}
|
||||
}
|
||||
} else if strings.ContainsRune(text, ' ') {
|
||||
if part := strings.Split(text, " "); len(part) == 3 {
|
||||
if part[0] != "" && part[0][0] > '9' {
|
||||
format = "January 02, 2006"
|
||||
} else {
|
||||
format = "02 January 2006"
|
||||
}
|
||||
}
|
||||
} else if strings.ContainsRune(text, '/') {
|
||||
if part := strings.Split(text, "/"); len(part) == 3 {
|
||||
if len(part[2]) == 2 {
|
||||
format = "01/02/06"
|
||||
} else {
|
||||
format = "01/02/2006"
|
||||
}
|
||||
}
|
||||
} else if len(text) == 6 {
|
||||
format = "010206"
|
||||
}
|
||||
if isConstantName(value) {
|
||||
view.setRaw(tag, value)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
if date, err := time.Parse(format, text); err == nil {
|
||||
picker.properties[tag] = value
|
||||
return date, true
|
||||
}
|
||||
if date, ok := stringToDate(value); ok {
|
||||
view.setRaw(tag, date)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
}
|
||||
|
||||
notCompatibleType(tag, value)
|
||||
return time.Now(), false
|
||||
return nil
|
||||
}
|
||||
|
||||
switch tag {
|
||||
case DatePickerMin, DatePickerMax:
|
||||
return setDateValue(tag)
|
||||
|
||||
case DatePickerStep:
|
||||
return setIntProperty(view, DatePickerStep, value)
|
||||
|
||||
case DatePickerValue:
|
||||
view.setRaw("old-date", GetDatePickerValue(view))
|
||||
return setDateValue(tag)
|
||||
|
||||
case DateChangedEvent:
|
||||
return setEventWithOldListener[DatePicker, time.Time](view, tag, value)
|
||||
|
||||
case DataList:
|
||||
return setDataList(view, value, dateFormat)
|
||||
}
|
||||
|
||||
return viewSet(view, tag, value)
|
||||
}
|
||||
|
||||
func datePickerPropertyChanged(view View, tag PropertyName) {
|
||||
|
||||
session := view.Session()
|
||||
|
||||
switch tag {
|
||||
|
||||
case DatePickerMin:
|
||||
old, oldOK := getDateProperty(picker, DatePickerMin, Min)
|
||||
if date, ok := setTimeValue(DatePickerMin); ok {
|
||||
if !oldOK || date != old {
|
||||
if picker.created {
|
||||
picker.session.updateProperty(picker.htmlID(), Min, date.Format(dateFormat))
|
||||
}
|
||||
picker.propertyChangedEvent(tag)
|
||||
}
|
||||
return true
|
||||
if date, ok := GetDatePickerMin(view); ok {
|
||||
session.updateProperty(view.htmlID(), "min", date.Format(dateFormat))
|
||||
} else {
|
||||
session.removeProperty(view.htmlID(), "min")
|
||||
}
|
||||
|
||||
case DatePickerMax:
|
||||
old, oldOK := getDateProperty(picker, DatePickerMax, Max)
|
||||
if date, ok := setTimeValue(DatePickerMax); ok {
|
||||
if !oldOK || date != old {
|
||||
if picker.created {
|
||||
picker.session.updateProperty(picker.htmlID(), Max, date.Format(dateFormat))
|
||||
}
|
||||
picker.propertyChangedEvent(tag)
|
||||
}
|
||||
return true
|
||||
if date, ok := GetDatePickerMax(view); ok {
|
||||
session.updateProperty(view.htmlID(), "max", date.Format(dateFormat))
|
||||
} else {
|
||||
session.removeProperty(view.htmlID(), "max")
|
||||
}
|
||||
|
||||
case DatePickerStep:
|
||||
oldStep := GetDatePickerStep(picker)
|
||||
if picker.setIntProperty(DatePickerStep, value) {
|
||||
if step := GetDatePickerStep(picker); oldStep != step {
|
||||
if picker.created {
|
||||
if step > 0 {
|
||||
picker.session.updateProperty(picker.htmlID(), Step, strconv.Itoa(step))
|
||||
} else {
|
||||
picker.session.removeProperty(picker.htmlID(), Step)
|
||||
}
|
||||
}
|
||||
picker.propertyChangedEvent(tag)
|
||||
}
|
||||
return true
|
||||
if step := GetDatePickerStep(view); step > 0 {
|
||||
session.updateProperty(view.htmlID(), "step", strconv.Itoa(step))
|
||||
} else {
|
||||
session.removeProperty(view.htmlID(), "step")
|
||||
}
|
||||
|
||||
case DatePickerValue:
|
||||
oldDate := GetDatePickerValue(picker)
|
||||
if date, ok := setTimeValue(DatePickerValue); ok {
|
||||
if date != oldDate {
|
||||
if picker.created {
|
||||
picker.session.callFunc("setInputValue", picker.htmlID(), date.Format(dateFormat))
|
||||
date := GetDatePickerValue(view)
|
||||
session.callFunc("setInputValue", view.htmlID(), date.Format(dateFormat))
|
||||
|
||||
if listeners := GetDateChangedListeners(view); len(listeners) > 0 {
|
||||
oldDate := time.Now()
|
||||
if value := view.getRaw("old-date"); value != nil {
|
||||
if date, ok := value.(time.Time); ok {
|
||||
oldDate = date
|
||||
}
|
||||
for _, listener := range picker.dateChangedListeners {
|
||||
listener(picker, date, oldDate)
|
||||
}
|
||||
picker.propertyChangedEvent(tag)
|
||||
}
|
||||
return true
|
||||
for _, listener := range listeners {
|
||||
listener(view, date, oldDate)
|
||||
}
|
||||
}
|
||||
|
||||
case DateChangedEvent:
|
||||
listeners, ok := valueToEventWithOldListeners[DatePicker, time.Time](value)
|
||||
if !ok {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
} else if listeners == nil {
|
||||
listeners = []func(DatePicker, time.Time, time.Time){}
|
||||
}
|
||||
picker.dateChangedListeners = listeners
|
||||
picker.propertyChangedEvent(tag)
|
||||
return true
|
||||
|
||||
case DataList:
|
||||
return picker.setDataList(picker, value, picker.created)
|
||||
|
||||
default:
|
||||
return picker.viewData.set(tag, value)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (picker *datePickerData) Get(tag string) any {
|
||||
return picker.get(picker.normalizeTag(tag))
|
||||
}
|
||||
|
||||
func (picker *datePickerData) get(tag string) any {
|
||||
switch tag {
|
||||
case DateChangedEvent:
|
||||
return picker.dateChangedListeners
|
||||
|
||||
case DataList:
|
||||
return picker.dataList.dataList
|
||||
|
||||
default:
|
||||
return picker.viewData.get(tag)
|
||||
viewPropertyChanged(view, tag)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -382,7 +296,13 @@ func (picker *datePickerData) htmlTag() string {
|
|||
}
|
||||
|
||||
func (picker *datePickerData) htmlSubviews(self View, buffer *strings.Builder) {
|
||||
picker.dataListHtmlSubviews(self, buffer)
|
||||
dataListHtmlSubviews(self, buffer, func(text string, session Session) string {
|
||||
text, _ = session.resolveConstants(text)
|
||||
if date, ok := stringToDate(text); ok {
|
||||
return date.Format(dateFormat)
|
||||
}
|
||||
return text
|
||||
})
|
||||
}
|
||||
|
||||
func (picker *datePickerData) htmlProperties(self View, buffer *strings.Builder) {
|
||||
|
@ -417,10 +337,10 @@ func (picker *datePickerData) htmlProperties(self View, buffer *strings.Builder)
|
|||
buffer.WriteString(` onclick="stopEventPropagation(this, event)"`)
|
||||
}
|
||||
|
||||
picker.dataListHtmlProperties(picker, buffer)
|
||||
dataListHtmlProperties(picker, buffer)
|
||||
}
|
||||
|
||||
func (picker *datePickerData) handleCommand(self View, command string, data DataObject) bool {
|
||||
func (picker *datePickerData) handleCommand(self View, command PropertyName, data DataObject) bool {
|
||||
switch command {
|
||||
case "textChanged":
|
||||
if text, ok := data.PropertyValue("text"); ok {
|
||||
|
@ -428,9 +348,12 @@ func (picker *datePickerData) handleCommand(self View, command string, data Data
|
|||
oldValue := GetDatePickerValue(picker)
|
||||
picker.properties[DatePickerValue] = value
|
||||
if value != oldValue {
|
||||
for _, listener := range picker.dateChangedListeners {
|
||||
for _, listener := range GetDateChangedListeners(picker) {
|
||||
listener(picker, value, oldValue)
|
||||
}
|
||||
if listener, ok := picker.changeListener[DatePickerValue]; ok {
|
||||
listener(picker, DatePickerValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -440,7 +363,7 @@ func (picker *datePickerData) handleCommand(self View, command string, data Data
|
|||
return picker.viewData.handleCommand(self, command, data)
|
||||
}
|
||||
|
||||
func getDateProperty(view View, mainTag, shortTag string) (time.Time, bool) {
|
||||
func getDateProperty(view View, mainTag, shortTag PropertyName) (time.Time, bool) {
|
||||
valueToTime := func(value any) (time.Time, bool) {
|
||||
if value != nil {
|
||||
switch value := value.(type) {
|
||||
|
@ -449,7 +372,7 @@ func getDateProperty(view View, mainTag, shortTag string) (time.Time, bool) {
|
|||
|
||||
case string:
|
||||
if text, ok := view.Session().resolveConstants(value); ok {
|
||||
if result, err := time.Parse(dateFormat, text); err == nil {
|
||||
if result, ok := stringToDate(text); ok {
|
||||
return result, true
|
||||
}
|
||||
}
|
||||
|
@ -463,9 +386,11 @@ func getDateProperty(view View, mainTag, shortTag string) (time.Time, bool) {
|
|||
return result, true
|
||||
}
|
||||
|
||||
if value := valueFromStyle(view, shortTag); value != nil {
|
||||
if result, ok := valueToTime(value); ok {
|
||||
return result, true
|
||||
for _, tag := range []PropertyName{mainTag, shortTag} {
|
||||
if value := valueFromStyle(view, tag); value != nil {
|
||||
if result, ok := valueToTime(value); ok {
|
||||
return result, true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
101
detailsView.go
101
detailsView.go
|
@ -13,7 +13,7 @@ const (
|
|||
//
|
||||
// `string` - Summary as a text.
|
||||
// `View` - Summary as a view, in this case it can be quite complex if needed.
|
||||
Summary = "summary"
|
||||
Summary PropertyName = "summary"
|
||||
|
||||
// Expanded is the constant for "expanded" property tag.
|
||||
//
|
||||
|
@ -25,7 +25,7 @@ const (
|
|||
// Values:
|
||||
// `true` or `1` or "true", "yes", "on", "1" - Content is visible.
|
||||
// `false` or `0` or "false", "no", "off", "0" - Content is collapsed(hidden).
|
||||
Expanded = "expanded"
|
||||
Expanded PropertyName = "expanded"
|
||||
)
|
||||
|
||||
// DetailsView represent a DetailsView view, which is a collapsible container of views
|
||||
|
@ -46,19 +46,21 @@ func NewDetailsView(session Session, params Params) DetailsView {
|
|||
}
|
||||
|
||||
func newDetailsView(session Session) View {
|
||||
return NewDetailsView(session, nil)
|
||||
return new(detailsViewData)
|
||||
}
|
||||
|
||||
// Init initialize fields of DetailsView by default values
|
||||
func (detailsView *detailsViewData) init(session Session) {
|
||||
detailsView.viewsContainerData.init(session)
|
||||
detailsView.tag = "DetailsView"
|
||||
detailsView.set = detailsView.setFunc
|
||||
detailsView.changed = detailsViewPropertyChanged
|
||||
//detailsView.systemClass = "ruiDetailsView"
|
||||
}
|
||||
|
||||
func (detailsView *detailsViewData) Views() []View {
|
||||
views := detailsView.viewsContainerData.Views()
|
||||
if summary := detailsView.get(Summary); summary != nil {
|
||||
if summary := detailsView.Get(Summary); summary != nil {
|
||||
switch summary := summary.(type) {
|
||||
case View:
|
||||
return append([]View{summary}, views...)
|
||||
|
@ -67,94 +69,53 @@ func (detailsView *detailsViewData) Views() []View {
|
|||
return views
|
||||
}
|
||||
|
||||
func (detailsView *detailsViewData) Remove(tag string) {
|
||||
detailsView.remove(strings.ToLower(tag))
|
||||
}
|
||||
|
||||
func (detailsView *detailsViewData) remove(tag string) {
|
||||
detailsView.viewsContainerData.remove(tag)
|
||||
if detailsView.created {
|
||||
switch tag {
|
||||
case Summary:
|
||||
updateInnerHTML(detailsView.htmlID(), detailsView.Session())
|
||||
|
||||
case Expanded:
|
||||
detailsView.session.removeProperty(detailsView.htmlID(), "open")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (detailsView *detailsViewData) Set(tag string, value any) bool {
|
||||
return detailsView.set(strings.ToLower(tag), value)
|
||||
}
|
||||
|
||||
func (detailsView *detailsViewData) set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
detailsView.remove(tag)
|
||||
return true
|
||||
}
|
||||
|
||||
func (detailsView *detailsViewData) setFunc(self View, tag PropertyName, value any) []PropertyName {
|
||||
switch tag {
|
||||
case Summary:
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
detailsView.properties[Summary] = value
|
||||
detailsView.setRaw(Summary, value)
|
||||
|
||||
case View:
|
||||
detailsView.properties[Summary] = value
|
||||
detailsView.setRaw(Summary, value)
|
||||
value.setParentID(detailsView.htmlID())
|
||||
|
||||
case DataObject:
|
||||
if view := CreateViewFromObject(detailsView.Session(), value); view != nil {
|
||||
detailsView.properties[Summary] = view
|
||||
detailsView.setRaw(Summary, view)
|
||||
view.setParentID(detailsView.htmlID())
|
||||
} else {
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
default:
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
if detailsView.created {
|
||||
updateInnerHTML(detailsView.htmlID(), detailsView.Session())
|
||||
return nil
|
||||
}
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
return detailsView.viewsContainerData.setFunc(detailsView, tag, value)
|
||||
}
|
||||
|
||||
func detailsViewPropertyChanged(view View, tag PropertyName) {
|
||||
switch tag {
|
||||
case Summary:
|
||||
updateInnerHTML(view.htmlID(), view.Session())
|
||||
|
||||
case Expanded:
|
||||
if !detailsView.setBoolProperty(tag, value) {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
if detailsView.created {
|
||||
if IsDetailsExpanded(detailsView) {
|
||||
detailsView.session.updateProperty(detailsView.htmlID(), "open", "")
|
||||
} else {
|
||||
detailsView.session.removeProperty(detailsView.htmlID(), "open")
|
||||
}
|
||||
if IsDetailsExpanded(view) {
|
||||
view.Session().updateProperty(view.htmlID(), "open", "")
|
||||
} else {
|
||||
view.Session().removeProperty(view.htmlID(), "open")
|
||||
}
|
||||
|
||||
case NotTranslate:
|
||||
if !detailsView.viewData.set(tag, value) {
|
||||
return false
|
||||
}
|
||||
if detailsView.created {
|
||||
updateInnerHTML(detailsView.htmlID(), detailsView.Session())
|
||||
}
|
||||
updateInnerHTML(view.htmlID(), view.Session())
|
||||
|
||||
default:
|
||||
return detailsView.viewsContainerData.Set(tag, value)
|
||||
viewsContainerPropertyChanged(view, tag)
|
||||
}
|
||||
|
||||
detailsView.propertyChangedEvent(tag)
|
||||
return true
|
||||
}
|
||||
|
||||
func (detailsView *detailsViewData) Get(tag string) any {
|
||||
return detailsView.get(strings.ToLower(tag))
|
||||
}
|
||||
|
||||
func (detailsView *detailsViewData) get(tag string) any {
|
||||
return detailsView.viewsContainerData.get(tag)
|
||||
}
|
||||
|
||||
func (detailsView *detailsViewData) htmlTag() string {
|
||||
|
@ -190,11 +151,13 @@ func (detailsView *detailsViewData) htmlSubviews(self View, buffer *strings.Buil
|
|||
detailsView.viewsContainerData.htmlSubviews(self, buffer)
|
||||
}
|
||||
|
||||
func (detailsView *detailsViewData) handleCommand(self View, command string, data DataObject) bool {
|
||||
func (detailsView *detailsViewData) handleCommand(self View, command PropertyName, data DataObject) bool {
|
||||
if command == "details-open" {
|
||||
if n, ok := dataIntProperty(data, "open"); ok {
|
||||
detailsView.properties[Expanded] = (n != 0)
|
||||
detailsView.propertyChangedEvent(Expanded)
|
||||
if listener, ok := detailsView.changeListener[Current]; ok {
|
||||
listener(detailsView, Current)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
462
dropDownList.go
462
dropDownList.go
|
@ -1,7 +1,6 @@
|
|||
package rui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
@ -19,20 +18,15 @@ import (
|
|||
// index - Index of a newly selected item.
|
||||
//
|
||||
// Allowed listener formats:
|
||||
const DropDownEvent = "drop-down-event"
|
||||
const DropDownEvent PropertyName = "drop-down-event"
|
||||
|
||||
// DropDownList represent a DropDownList view
|
||||
type DropDownList interface {
|
||||
View
|
||||
getItems() []string
|
||||
}
|
||||
|
||||
type dropDownListData struct {
|
||||
viewData
|
||||
items []string
|
||||
disabledItems []any
|
||||
itemSeparators []any
|
||||
dropDownListener []func(DropDownList, int, int)
|
||||
}
|
||||
|
||||
// NewDropDownList create new DropDownList object and return it
|
||||
|
@ -44,167 +38,86 @@ func NewDropDownList(session Session, params Params) DropDownList {
|
|||
}
|
||||
|
||||
func newDropDownList(session Session) View {
|
||||
return NewDropDownList(session, nil)
|
||||
return new(dropDownListData)
|
||||
}
|
||||
|
||||
func (list *dropDownListData) init(session Session) {
|
||||
list.viewData.init(session)
|
||||
list.tag = "DropDownList"
|
||||
list.hasHtmlDisabled = true
|
||||
list.items = []string{}
|
||||
list.disabledItems = []any{}
|
||||
list.itemSeparators = []any{}
|
||||
list.dropDownListener = []func(DropDownList, int, int){}
|
||||
}
|
||||
|
||||
func (list *dropDownListData) String() string {
|
||||
return getViewString(list, nil)
|
||||
list.normalize = normalizeDropDownListTag
|
||||
list.set = dropDownListSet
|
||||
list.changed = dropDownListPropertyChanged
|
||||
}
|
||||
|
||||
func (list *dropDownListData) Focusable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (list *dropDownListData) Remove(tag string) {
|
||||
list.remove(strings.ToLower(tag))
|
||||
func normalizeDropDownListTag(tag PropertyName) PropertyName {
|
||||
tag = defaultNormalize(tag)
|
||||
if tag == "separators" {
|
||||
return ItemSeparators
|
||||
}
|
||||
return tag
|
||||
}
|
||||
|
||||
func (list *dropDownListData) remove(tag string) {
|
||||
func dropDownListSet(view View, tag PropertyName, value any) []PropertyName {
|
||||
switch tag {
|
||||
case Items:
|
||||
if len(list.items) > 0 {
|
||||
list.items = []string{}
|
||||
if list.created {
|
||||
updateInnerHTML(list.htmlID(), list.session)
|
||||
}
|
||||
list.propertyChangedEvent(tag)
|
||||
if items, ok := anyToStringArray(value, ""); ok {
|
||||
return setArrayPropertyValue(view, tag, items)
|
||||
}
|
||||
notCompatibleType(Items, value)
|
||||
return nil
|
||||
|
||||
case DisabledItems:
|
||||
if len(list.disabledItems) > 0 {
|
||||
list.disabledItems = []any{}
|
||||
if list.created {
|
||||
updateInnerHTML(list.htmlID(), list.session)
|
||||
}
|
||||
list.propertyChangedEvent(tag)
|
||||
}
|
||||
|
||||
case ItemSeparators, "separators":
|
||||
if len(list.itemSeparators) > 0 {
|
||||
list.itemSeparators = []any{}
|
||||
if list.created {
|
||||
updateInnerHTML(list.htmlID(), list.session)
|
||||
}
|
||||
list.propertyChangedEvent(ItemSeparators)
|
||||
case DisabledItems, ItemSeparators:
|
||||
if items, ok := parseIndicesArray(value); ok {
|
||||
return setArrayPropertyValue(view, tag, items)
|
||||
}
|
||||
notCompatibleType(tag, value)
|
||||
return nil
|
||||
|
||||
case DropDownEvent:
|
||||
if len(list.dropDownListener) > 0 {
|
||||
list.dropDownListener = []func(DropDownList, int, int){}
|
||||
list.propertyChangedEvent(tag)
|
||||
}
|
||||
return setEventWithOldListener[DropDownList, int](view, tag, value)
|
||||
|
||||
case Current:
|
||||
oldCurrent := GetCurrent(list)
|
||||
delete(list.properties, Current)
|
||||
if oldCurrent != 0 {
|
||||
if list.created {
|
||||
list.session.callFunc("selectDropDownListItem", list.htmlID(), 0)
|
||||
if view, ok := view.(View); ok {
|
||||
view.setRaw("old-current", GetCurrent(view))
|
||||
}
|
||||
return setIntProperty(view, Current, value)
|
||||
}
|
||||
|
||||
return viewSet(view, tag, value)
|
||||
}
|
||||
|
||||
func dropDownListPropertyChanged(view View, tag PropertyName) {
|
||||
switch tag {
|
||||
case Items, DisabledItems, ItemSeparators:
|
||||
updateInnerHTML(view.htmlID(), view.Session())
|
||||
|
||||
case Current:
|
||||
current := GetCurrent(view)
|
||||
view.Session().callFunc("selectDropDownListItem", view.htmlID(), current)
|
||||
|
||||
if list, ok := view.(DropDownList); ok {
|
||||
oldCurrent := -1
|
||||
if value := view.getRaw("old-current"); value != nil {
|
||||
if n, ok := value.(int); ok {
|
||||
oldCurrent = n
|
||||
}
|
||||
}
|
||||
|
||||
for _, listener := range GetDropDownListeners(view) {
|
||||
listener(list, current, oldCurrent)
|
||||
}
|
||||
list.onSelectedItemChanged(0, oldCurrent)
|
||||
}
|
||||
|
||||
default:
|
||||
list.viewData.remove(tag)
|
||||
viewPropertyChanged(view, tag)
|
||||
}
|
||||
}
|
||||
|
||||
func (list *dropDownListData) Set(tag string, value any) bool {
|
||||
return list.set(strings.ToLower(tag), value)
|
||||
}
|
||||
|
||||
func (list *dropDownListData) set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
list.remove(tag)
|
||||
return true
|
||||
}
|
||||
|
||||
switch tag {
|
||||
case Items:
|
||||
return list.setItems(value)
|
||||
|
||||
case DisabledItems:
|
||||
items, ok := list.parseIndicesArray(value)
|
||||
if !ok {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
list.disabledItems = items
|
||||
if list.created {
|
||||
updateInnerHTML(list.htmlID(), list.session)
|
||||
}
|
||||
list.propertyChangedEvent(tag)
|
||||
return true
|
||||
|
||||
case ItemSeparators, "separators":
|
||||
items, ok := list.parseIndicesArray(value)
|
||||
if !ok {
|
||||
notCompatibleType(ItemSeparators, value)
|
||||
return false
|
||||
}
|
||||
list.itemSeparators = items
|
||||
if list.created {
|
||||
updateInnerHTML(list.htmlID(), list.session)
|
||||
}
|
||||
list.propertyChangedEvent(ItemSeparators)
|
||||
return true
|
||||
|
||||
case DropDownEvent:
|
||||
listeners, ok := valueToEventWithOldListeners[DropDownList, int](value)
|
||||
if !ok {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
} else if listeners == nil {
|
||||
listeners = []func(DropDownList, int, int){}
|
||||
}
|
||||
list.dropDownListener = listeners
|
||||
list.propertyChangedEvent(tag)
|
||||
return true
|
||||
|
||||
case Current:
|
||||
oldCurrent := GetCurrent(list)
|
||||
if !list.setIntProperty(Current, value) {
|
||||
return false
|
||||
}
|
||||
|
||||
if current := GetCurrent(list); oldCurrent != current {
|
||||
if list.created {
|
||||
list.session.callFunc("selectDropDownListItem", list.htmlID(), current)
|
||||
}
|
||||
list.onSelectedItemChanged(current, oldCurrent)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return list.viewData.set(tag, value)
|
||||
}
|
||||
|
||||
func (list *dropDownListData) setItems(value any) bool {
|
||||
items, ok := anyToStringArray(value)
|
||||
if !ok {
|
||||
notCompatibleType(Items, value)
|
||||
return false
|
||||
}
|
||||
|
||||
list.items = items
|
||||
if list.created {
|
||||
updateInnerHTML(list.htmlID(), list.session)
|
||||
}
|
||||
|
||||
list.propertyChangedEvent(Items)
|
||||
return true
|
||||
}
|
||||
|
||||
func intArrayToStringArray[T int | uint | int8 | uint8 | int16 | uint16 | int32 | uint32 | int64 | uint64](array []T) []string {
|
||||
items := make([]string, len(array))
|
||||
for i, val := range array {
|
||||
|
@ -213,150 +126,11 @@ func intArrayToStringArray[T int | uint | int8 | uint8 | int16 | uint16 | int32
|
|||
return items
|
||||
}
|
||||
|
||||
func anyToStringArray(value any) ([]string, bool) {
|
||||
|
||||
func parseIndicesArray(value any) ([]any, bool) {
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
return []string{value}, true
|
||||
case int:
|
||||
return []any{value}, true
|
||||
|
||||
case []string:
|
||||
return value, true
|
||||
|
||||
case []DataValue:
|
||||
items := make([]string, 0, len(value))
|
||||
for _, val := range value {
|
||||
if !val.IsObject() {
|
||||
items = append(items, val.Value())
|
||||
}
|
||||
}
|
||||
return items, true
|
||||
|
||||
case []fmt.Stringer:
|
||||
items := make([]string, len(value))
|
||||
for i, str := range value {
|
||||
items[i] = str.String()
|
||||
}
|
||||
return items, true
|
||||
|
||||
case []Color:
|
||||
items := make([]string, len(value))
|
||||
for i, str := range value {
|
||||
items[i] = str.String()
|
||||
}
|
||||
return items, true
|
||||
|
||||
case []SizeUnit:
|
||||
items := make([]string, len(value))
|
||||
for i, str := range value {
|
||||
items[i] = str.String()
|
||||
}
|
||||
return items, true
|
||||
|
||||
case []AngleUnit:
|
||||
items := make([]string, len(value))
|
||||
for i, str := range value {
|
||||
items[i] = str.String()
|
||||
}
|
||||
return items, true
|
||||
|
||||
case []float32:
|
||||
items := make([]string, len(value))
|
||||
for i, val := range value {
|
||||
items[i] = fmt.Sprintf("%g", float64(val))
|
||||
}
|
||||
return items, true
|
||||
|
||||
case []float64:
|
||||
items := make([]string, len(value))
|
||||
for i, val := range value {
|
||||
items[i] = fmt.Sprintf("%g", val)
|
||||
}
|
||||
return items, true
|
||||
|
||||
case []int:
|
||||
return intArrayToStringArray(value), true
|
||||
|
||||
case []uint:
|
||||
return intArrayToStringArray(value), true
|
||||
|
||||
case []int8:
|
||||
return intArrayToStringArray(value), true
|
||||
|
||||
case []uint8:
|
||||
return intArrayToStringArray(value), true
|
||||
|
||||
case []int16:
|
||||
return intArrayToStringArray(value), true
|
||||
|
||||
case []uint16:
|
||||
return intArrayToStringArray(value), true
|
||||
|
||||
case []int32:
|
||||
return intArrayToStringArray(value), true
|
||||
|
||||
case []uint32:
|
||||
return intArrayToStringArray(value), true
|
||||
|
||||
case []int64:
|
||||
return intArrayToStringArray(value), true
|
||||
|
||||
case []uint64:
|
||||
return intArrayToStringArray(value), true
|
||||
|
||||
case []bool:
|
||||
items := make([]string, len(value))
|
||||
for i, val := range value {
|
||||
if val {
|
||||
items[i] = "true"
|
||||
} else {
|
||||
items[i] = "false"
|
||||
}
|
||||
}
|
||||
return items, true
|
||||
|
||||
case []any:
|
||||
items := make([]string, 0, len(value))
|
||||
for _, v := range value {
|
||||
switch val := v.(type) {
|
||||
case string:
|
||||
items = append(items, val)
|
||||
|
||||
case fmt.Stringer:
|
||||
items = append(items, val.String())
|
||||
|
||||
case bool:
|
||||
if val {
|
||||
items = append(items, "true")
|
||||
} else {
|
||||
items = append(items, "false")
|
||||
}
|
||||
|
||||
case float32:
|
||||
items = append(items, fmt.Sprintf("%g", float64(val)))
|
||||
|
||||
case float64:
|
||||
items = append(items, fmt.Sprintf("%g", val))
|
||||
|
||||
case rune:
|
||||
items = append(items, string(val))
|
||||
|
||||
default:
|
||||
if n, ok := isInt(v); ok {
|
||||
items = append(items, strconv.Itoa(n))
|
||||
} else {
|
||||
return []string{}, false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return items, true
|
||||
}
|
||||
|
||||
return []string{}, false
|
||||
}
|
||||
|
||||
func (list *dropDownListData) parseIndicesArray(value any) ([]any, bool) {
|
||||
switch value := value.(type) {
|
||||
case []int:
|
||||
items := make([]any, len(value))
|
||||
for i, n := range value {
|
||||
|
@ -365,108 +139,72 @@ func (list *dropDownListData) parseIndicesArray(value any) ([]any, bool) {
|
|||
return items, true
|
||||
|
||||
case []any:
|
||||
items := make([]any, len(value))
|
||||
for i, val := range value {
|
||||
if val == nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
switch val := val.(type) {
|
||||
case string:
|
||||
if isConstantName(val) {
|
||||
items[i] = val
|
||||
} else {
|
||||
n, err := strconv.Atoi(val)
|
||||
if err != nil {
|
||||
items := make([]any, 0, len(value))
|
||||
for _, val := range value {
|
||||
if val != nil {
|
||||
switch val := val.(type) {
|
||||
case string:
|
||||
if isConstantName(val) {
|
||||
items = append(items, val)
|
||||
} else if n, err := strconv.Atoi(val); err == nil {
|
||||
items = append(items, n)
|
||||
} else {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
default:
|
||||
if n, ok := isInt(val); ok {
|
||||
items = append(items, n)
|
||||
} else {
|
||||
return nil, false
|
||||
}
|
||||
items[i] = n
|
||||
}
|
||||
default:
|
||||
if n, ok := isInt(val); ok {
|
||||
items[i] = n
|
||||
}
|
||||
}
|
||||
return items, true
|
||||
|
||||
case []string:
|
||||
items := make([]any, 0, len(value))
|
||||
for _, str := range value {
|
||||
if str = strings.Trim(str, " \t"); str != "" {
|
||||
if isConstantName(str) {
|
||||
items = append(items, str)
|
||||
} else if n, err := strconv.Atoi(str); err == nil {
|
||||
items = append(items, n)
|
||||
} else {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return items, true
|
||||
|
||||
case string:
|
||||
values := strings.Split(value, ",")
|
||||
items := make([]any, len(values))
|
||||
for i, str := range values {
|
||||
str = strings.Trim(str, " ")
|
||||
if str == "" {
|
||||
return nil, false
|
||||
}
|
||||
if isConstantName(str) {
|
||||
items[i] = str
|
||||
} else {
|
||||
n, err := strconv.Atoi(str)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
items[i] = n
|
||||
}
|
||||
}
|
||||
return items, true
|
||||
return parseIndicesArray(strings.Split(value, ","))
|
||||
|
||||
case []DataValue:
|
||||
items := make([]any, 0, len(value))
|
||||
items := make([]string, 0, len(value))
|
||||
for _, val := range value {
|
||||
if !val.IsObject() {
|
||||
items = append(items, val.Value())
|
||||
}
|
||||
}
|
||||
return list.parseIndicesArray(items)
|
||||
return parseIndicesArray(items)
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (list *dropDownListData) Get(tag string) any {
|
||||
return list.get(strings.ToLower(tag))
|
||||
}
|
||||
|
||||
func (list *dropDownListData) get(tag string) any {
|
||||
switch tag {
|
||||
case Items:
|
||||
return list.items
|
||||
|
||||
case DisabledItems:
|
||||
return list.disabledItems
|
||||
|
||||
case ItemSeparators:
|
||||
return list.itemSeparators
|
||||
|
||||
case Current:
|
||||
result, _ := intProperty(list, Current, list.session, 0)
|
||||
return result
|
||||
|
||||
case DropDownEvent:
|
||||
return list.dropDownListener
|
||||
}
|
||||
|
||||
return list.viewData.get(tag)
|
||||
}
|
||||
|
||||
func (list *dropDownListData) getItems() []string {
|
||||
return list.items
|
||||
}
|
||||
|
||||
func (list *dropDownListData) htmlTag() string {
|
||||
return "select"
|
||||
}
|
||||
|
||||
func (list *dropDownListData) htmlSubviews(self View, buffer *strings.Builder) {
|
||||
if list.items != nil {
|
||||
if items := GetDropDownItems(list); len(items) > 0 {
|
||||
current := GetCurrent(list)
|
||||
notTranslate := GetNotTranslate(list)
|
||||
disabledItems := GetDropDownDisabledItems(list)
|
||||
separators := GetDropDownItemSeparators(list)
|
||||
for i, item := range list.items {
|
||||
for i, item := range items {
|
||||
disabled := false
|
||||
for _, index := range disabledItems {
|
||||
if i == index {
|
||||
|
@ -503,22 +241,18 @@ func (list *dropDownListData) htmlProperties(self View, buffer *strings.Builder)
|
|||
buffer.WriteString(` size="1" onchange="dropDownListEvent(this, event)"`)
|
||||
}
|
||||
|
||||
func (list *dropDownListData) onSelectedItemChanged(number, old int) {
|
||||
for _, listener := range list.dropDownListener {
|
||||
listener(list, number, old)
|
||||
}
|
||||
list.propertyChangedEvent(Current)
|
||||
}
|
||||
|
||||
func (list *dropDownListData) handleCommand(self View, command string, data DataObject) bool {
|
||||
func (list *dropDownListData) handleCommand(self View, command PropertyName, data DataObject) bool {
|
||||
switch command {
|
||||
case "itemSelected":
|
||||
if text, ok := data.PropertyValue("number"); ok {
|
||||
if number, err := strconv.Atoi(text); err == nil {
|
||||
if GetCurrent(list) != number && number >= 0 && number < len(list.items) {
|
||||
items := GetDropDownItems(list)
|
||||
if GetCurrent(list) != number && number >= 0 && number < len(items) {
|
||||
old := GetCurrent(list)
|
||||
list.properties[Current] = number
|
||||
list.onSelectedItemChanged(number, old)
|
||||
for _, listener := range GetDropDownListeners(list) {
|
||||
listener(list, number, old)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ErrorLog(err.Error())
|
||||
|
@ -544,14 +278,16 @@ func GetDropDownItems(view View, subviewID ...string) []string {
|
|||
view = ViewByID(view, subviewID[0])
|
||||
}
|
||||
if view != nil {
|
||||
if list, ok := view.(DropDownList); ok {
|
||||
return list.getItems()
|
||||
if value := view.Get(Items); value != nil {
|
||||
if items, ok := value.([]string); ok {
|
||||
return items
|
||||
}
|
||||
}
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func getIndicesArray(view View, tag string) []int {
|
||||
func getIndicesArray(view View, tag PropertyName) []int {
|
||||
if view != nil {
|
||||
if value := view.Get(tag); value != nil {
|
||||
if values, ok := value.([]any); ok {
|
||||
|
|
337
editView.go
337
editView.go
|
@ -26,7 +26,7 @@ const (
|
|||
// `func(newText string)`,
|
||||
// `func(editView rui.EditView)`,
|
||||
// `func()`.
|
||||
EditTextChangedEvent = "edit-text-changed"
|
||||
EditTextChangedEvent PropertyName = "edit-text-changed"
|
||||
|
||||
// EditViewType is the constant for "edit-view-type" property tag.
|
||||
//
|
||||
|
@ -43,7 +43,7 @@ const (
|
|||
// `4`(`URLText`) or "url" - Internet address input editor.
|
||||
// `5`(`PhoneText`) or "phone" - Phone number editor.
|
||||
// `6`(`MultiLineText`) or "multiline" - Multi-line text editor.
|
||||
EditViewType = "edit-view-type"
|
||||
EditViewType PropertyName = "edit-view-type"
|
||||
|
||||
// EditViewPattern is the constant for "edit-view-pattern" property tag.
|
||||
//
|
||||
|
@ -51,12 +51,12 @@ const (
|
|||
// Regular expression to limit editing of a text.
|
||||
//
|
||||
// Supported types: `string`.
|
||||
EditViewPattern = "edit-view-pattern"
|
||||
EditViewPattern PropertyName = "edit-view-pattern"
|
||||
|
||||
// Spellcheck is the constant for "spellcheck" property tag.
|
||||
//
|
||||
// Used by `EditView`.
|
||||
// Enable or disable spell checker. Available in `SingleLineText` and `MultiLineText` types of edit view. Default value is
|
||||
// Enable or disable spell checker. Available in `SingleLineText` and `MultiLineText` types of edit view. Default value is
|
||||
// `false`.
|
||||
//
|
||||
// Supported types: `bool`, `int`, `string`.
|
||||
|
@ -64,7 +64,7 @@ const (
|
|||
// Values:
|
||||
// `true` or `1` or "true", "yes", "on", "1" - Enable spell checker for text.
|
||||
// `false` or `0` or "false", "no", "off", "0" - Disable spell checker for text.
|
||||
Spellcheck = "spellcheck"
|
||||
Spellcheck PropertyName = "spellcheck"
|
||||
)
|
||||
|
||||
// Constants for the values of an [EditView] "edit-view-type" property
|
||||
|
@ -97,12 +97,11 @@ type EditView interface {
|
|||
|
||||
// AppendText appends text to the current text of an EditView view
|
||||
AppendText(text string)
|
||||
textChanged(newText, oldText string)
|
||||
}
|
||||
|
||||
type editViewData struct {
|
||||
viewData
|
||||
dataList
|
||||
textChangeListeners []func(EditView, string, string)
|
||||
}
|
||||
|
||||
// NewEditView create new EditView object and return it
|
||||
|
@ -114,27 +113,24 @@ func NewEditView(session Session, params Params) EditView {
|
|||
}
|
||||
|
||||
func newEditView(session Session) View {
|
||||
return NewEditView(session, nil)
|
||||
return new(editViewData) // NewEditView(session, nil)
|
||||
}
|
||||
|
||||
func (edit *editViewData) init(session Session) {
|
||||
edit.viewData.init(session)
|
||||
edit.hasHtmlDisabled = true
|
||||
edit.textChangeListeners = []func(EditView, string, string){}
|
||||
edit.tag = "EditView"
|
||||
edit.dataListInit()
|
||||
}
|
||||
|
||||
func (edit *editViewData) String() string {
|
||||
return getViewString(edit, nil)
|
||||
edit.normalize = normalizeEditViewTag
|
||||
edit.set = editViewSet
|
||||
edit.changed = editViewPropertyChanged
|
||||
}
|
||||
|
||||
func (edit *editViewData) Focusable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (edit *editViewData) normalizeTag(tag string) string {
|
||||
tag = strings.ToLower(tag)
|
||||
func normalizeEditViewTag(tag PropertyName) PropertyName {
|
||||
tag = defaultNormalize(tag)
|
||||
switch tag {
|
||||
case Type, "edit-type":
|
||||
return EditViewType
|
||||
|
@ -149,279 +145,109 @@ func (edit *editViewData) normalizeTag(tag string) string {
|
|||
return EditWrap
|
||||
}
|
||||
|
||||
return edit.normalizeDataListTag(tag)
|
||||
return normalizeDataListTag(tag)
|
||||
}
|
||||
|
||||
func (edit *editViewData) Remove(tag string) {
|
||||
edit.remove(edit.normalizeTag(tag))
|
||||
}
|
||||
|
||||
func (edit *editViewData) remove(tag string) {
|
||||
_, exists := edit.properties[tag]
|
||||
func editViewSet(view View, tag PropertyName, value any) []PropertyName {
|
||||
switch tag {
|
||||
case Hint:
|
||||
if exists {
|
||||
delete(edit.properties, Hint)
|
||||
if edit.created {
|
||||
edit.session.removeProperty(edit.htmlID(), "placeholder")
|
||||
}
|
||||
edit.propertyChangedEvent(tag)
|
||||
}
|
||||
|
||||
case MaxLength:
|
||||
if exists {
|
||||
delete(edit.properties, MaxLength)
|
||||
if edit.created {
|
||||
edit.session.removeProperty(edit.htmlID(), "maxlength")
|
||||
}
|
||||
edit.propertyChangedEvent(tag)
|
||||
}
|
||||
|
||||
case ReadOnly, Spellcheck:
|
||||
if exists {
|
||||
delete(edit.properties, tag)
|
||||
if edit.created {
|
||||
edit.session.updateProperty(edit.htmlID(), tag, false)
|
||||
}
|
||||
edit.propertyChangedEvent(tag)
|
||||
}
|
||||
|
||||
case EditTextChangedEvent:
|
||||
if len(edit.textChangeListeners) > 0 {
|
||||
edit.textChangeListeners = []func(EditView, string, string){}
|
||||
edit.propertyChangedEvent(tag)
|
||||
}
|
||||
|
||||
case Text:
|
||||
if exists {
|
||||
oldText := GetText(edit)
|
||||
delete(edit.properties, tag)
|
||||
if oldText != "" {
|
||||
edit.textChanged("", oldText)
|
||||
if edit.created {
|
||||
edit.session.callFunc("setInputValue", edit.htmlID(), "")
|
||||
if text, ok := value.(string); ok {
|
||||
old := ""
|
||||
if val := view.getRaw(Text); val != nil {
|
||||
if txt, ok := val.(string); ok {
|
||||
old = txt
|
||||
}
|
||||
}
|
||||
view.setRaw("old-text", old)
|
||||
view.setRaw(tag, text)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
case EditViewPattern:
|
||||
if exists {
|
||||
oldText := GetEditViewPattern(edit)
|
||||
delete(edit.properties, tag)
|
||||
if oldText != "" {
|
||||
if edit.created {
|
||||
edit.session.removeProperty(edit.htmlID(), Pattern)
|
||||
}
|
||||
edit.propertyChangedEvent(tag)
|
||||
}
|
||||
}
|
||||
notCompatibleType(tag, value)
|
||||
return nil
|
||||
|
||||
case EditViewType:
|
||||
if exists {
|
||||
oldType := GetEditViewType(edit)
|
||||
delete(edit.properties, tag)
|
||||
if oldType != 0 {
|
||||
if edit.created {
|
||||
updateInnerHTML(edit.parentHTMLID(), edit.session)
|
||||
}
|
||||
edit.propertyChangedEvent(tag)
|
||||
}
|
||||
}
|
||||
|
||||
case EditWrap:
|
||||
if exists {
|
||||
oldWrap := IsEditViewWrap(edit)
|
||||
delete(edit.properties, tag)
|
||||
if GetEditViewType(edit) == MultiLineText {
|
||||
if wrap := IsEditViewWrap(edit); wrap != oldWrap {
|
||||
if edit.created {
|
||||
if wrap {
|
||||
edit.session.updateProperty(edit.htmlID(), "wrap", "soft")
|
||||
} else {
|
||||
edit.session.updateProperty(edit.htmlID(), "wrap", "off")
|
||||
}
|
||||
}
|
||||
edit.propertyChangedEvent(tag)
|
||||
}
|
||||
}
|
||||
case Hint:
|
||||
if text, ok := value.(string); ok {
|
||||
return setStringPropertyValue(view, tag, strings.Trim(text, " \t\n"))
|
||||
}
|
||||
notCompatibleType(tag, value)
|
||||
return nil
|
||||
|
||||
case DataList:
|
||||
if len(edit.dataList.dataList) > 0 {
|
||||
edit.setDataList(edit, []string{}, true)
|
||||
}
|
||||
setDataList(view, value, "")
|
||||
|
||||
default:
|
||||
edit.viewData.remove(tag)
|
||||
case EditTextChangedEvent:
|
||||
return setEventWithOldListener[EditView, string](view, tag, value)
|
||||
}
|
||||
|
||||
return viewSet(view, tag, value)
|
||||
}
|
||||
|
||||
func (edit *editViewData) Set(tag string, value any) bool {
|
||||
return edit.set(edit.normalizeTag(tag), value)
|
||||
}
|
||||
|
||||
func (edit *editViewData) set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
edit.remove(tag)
|
||||
return true
|
||||
}
|
||||
func editViewPropertyChanged(view View, tag PropertyName) {
|
||||
session := view.Session()
|
||||
|
||||
switch tag {
|
||||
case Text:
|
||||
if text, ok := value.(string); ok {
|
||||
oldText := GetText(edit)
|
||||
edit.properties[Text] = text
|
||||
if text = GetText(edit); oldText != text {
|
||||
edit.textChanged(text, oldText)
|
||||
if edit.created {
|
||||
edit.session.callFunc("setInputValue", edit.htmlID(), text)
|
||||
text := GetText(view)
|
||||
session.callFunc("setInputValue", view.htmlID(), text)
|
||||
|
||||
if edit, ok := view.(EditView); ok {
|
||||
old := ""
|
||||
if val := view.getRaw("old-text"); val != nil {
|
||||
if txt, ok := val.(string); ok {
|
||||
old = txt
|
||||
}
|
||||
}
|
||||
return true
|
||||
edit.textChanged(text, old)
|
||||
}
|
||||
return false
|
||||
|
||||
case Hint:
|
||||
if text, ok := value.(string); ok {
|
||||
oldText := GetHint(edit)
|
||||
edit.properties[Hint] = text
|
||||
if text = GetHint(edit); oldText != text {
|
||||
if edit.created {
|
||||
if text != "" {
|
||||
edit.session.updateProperty(edit.htmlID(), "placeholder", text)
|
||||
} else {
|
||||
edit.session.removeProperty(edit.htmlID(), "placeholder")
|
||||
}
|
||||
}
|
||||
edit.propertyChangedEvent(tag)
|
||||
}
|
||||
return true
|
||||
if text := GetHint(view); text != "" {
|
||||
session.updateProperty(view.htmlID(), "placeholder", text)
|
||||
} else {
|
||||
session.removeProperty(view.htmlID(), "placeholder")
|
||||
}
|
||||
return false
|
||||
|
||||
case MaxLength:
|
||||
oldMaxLength := GetMaxLength(edit)
|
||||
if edit.setIntProperty(MaxLength, value) {
|
||||
if maxLength := GetMaxLength(edit); maxLength != oldMaxLength {
|
||||
if edit.created {
|
||||
if maxLength > 0 {
|
||||
edit.session.updateProperty(edit.htmlID(), "maxlength", strconv.Itoa(maxLength))
|
||||
} else {
|
||||
edit.session.removeProperty(edit.htmlID(), "maxlength")
|
||||
}
|
||||
}
|
||||
edit.propertyChangedEvent(tag)
|
||||
}
|
||||
return true
|
||||
if maxLength := GetMaxLength(view); maxLength > 0 {
|
||||
session.updateProperty(view.htmlID(), "maxlength", strconv.Itoa(maxLength))
|
||||
} else {
|
||||
session.removeProperty(view.htmlID(), "maxlength")
|
||||
}
|
||||
return false
|
||||
|
||||
case ReadOnly:
|
||||
if edit.setBoolProperty(ReadOnly, value) {
|
||||
if edit.created {
|
||||
if IsReadOnly(edit) {
|
||||
edit.session.updateProperty(edit.htmlID(), ReadOnly, "")
|
||||
} else {
|
||||
edit.session.removeProperty(edit.htmlID(), ReadOnly)
|
||||
}
|
||||
}
|
||||
edit.propertyChangedEvent(tag)
|
||||
return true
|
||||
if IsReadOnly(view) {
|
||||
session.updateProperty(view.htmlID(), "readonly", "")
|
||||
} else {
|
||||
session.removeProperty(view.htmlID(), "readonly")
|
||||
}
|
||||
return false
|
||||
|
||||
case Spellcheck:
|
||||
if edit.setBoolProperty(Spellcheck, value) {
|
||||
if edit.created {
|
||||
edit.session.updateProperty(edit.htmlID(), Spellcheck, IsSpellcheck(edit))
|
||||
}
|
||||
edit.propertyChangedEvent(tag)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
session.updateProperty(view.htmlID(), "spellcheck", IsSpellcheck(view))
|
||||
|
||||
case EditViewPattern:
|
||||
oldText := GetEditViewPattern(edit)
|
||||
if text, ok := value.(string); ok {
|
||||
edit.properties[EditViewPattern] = text
|
||||
if text = GetEditViewPattern(edit); oldText != text {
|
||||
if edit.created {
|
||||
if text != "" {
|
||||
edit.session.updateProperty(edit.htmlID(), Pattern, text)
|
||||
} else {
|
||||
edit.session.removeProperty(edit.htmlID(), Pattern)
|
||||
}
|
||||
}
|
||||
edit.propertyChangedEvent(tag)
|
||||
}
|
||||
return true
|
||||
if text := GetEditViewPattern(view); text != "" {
|
||||
session.updateProperty(view.htmlID(), "pattern", text)
|
||||
} else {
|
||||
session.removeProperty(view.htmlID(), "pattern")
|
||||
}
|
||||
return false
|
||||
|
||||
case EditViewType:
|
||||
oldType := GetEditViewType(edit)
|
||||
if edit.setEnumProperty(EditViewType, value, enumProperties[EditViewType].values) {
|
||||
if GetEditViewType(edit) != oldType {
|
||||
if edit.created {
|
||||
updateInnerHTML(edit.parentHTMLID(), edit.session)
|
||||
}
|
||||
edit.propertyChangedEvent(tag)
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
updateInnerHTML(view.parentHTMLID(), session)
|
||||
|
||||
case EditWrap:
|
||||
oldWrap := IsEditViewWrap(edit)
|
||||
if edit.setBoolProperty(EditWrap, value) {
|
||||
if GetEditViewType(edit) == MultiLineText {
|
||||
if wrap := IsEditViewWrap(edit); wrap != oldWrap {
|
||||
if edit.created {
|
||||
if wrap {
|
||||
edit.session.updateProperty(edit.htmlID(), "wrap", "soft")
|
||||
} else {
|
||||
edit.session.updateProperty(edit.htmlID(), "wrap", "off")
|
||||
}
|
||||
}
|
||||
edit.propertyChangedEvent(tag)
|
||||
}
|
||||
}
|
||||
return true
|
||||
if wrap := IsEditViewWrap(view); wrap {
|
||||
session.updateProperty(view.htmlID(), "wrap", "soft")
|
||||
} else {
|
||||
session.updateProperty(view.htmlID(), "wrap", "off")
|
||||
}
|
||||
return false
|
||||
|
||||
case DataList:
|
||||
return edit.setDataList(edit, value, edit.created)
|
||||
updateInnerHTML(view.htmlID(), session)
|
||||
|
||||
case EditTextChangedEvent:
|
||||
listeners, ok := valueToEventWithOldListeners[EditView, string](value)
|
||||
if !ok {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
} else if listeners == nil {
|
||||
listeners = []func(EditView, string, string){}
|
||||
}
|
||||
edit.textChangeListeners = listeners
|
||||
edit.propertyChangedEvent(tag)
|
||||
return true
|
||||
default:
|
||||
viewPropertyChanged(view, tag)
|
||||
}
|
||||
|
||||
return edit.viewData.set(tag, value)
|
||||
}
|
||||
|
||||
func (edit *editViewData) Get(tag string) any {
|
||||
return edit.get(edit.normalizeTag(tag))
|
||||
}
|
||||
|
||||
func (edit *editViewData) get(tag string) any {
|
||||
switch tag {
|
||||
case EditTextChangedEvent:
|
||||
return edit.textChangeListeners
|
||||
|
||||
case DataList:
|
||||
return edit.dataList.dataList
|
||||
}
|
||||
return edit.viewData.get(tag)
|
||||
}
|
||||
|
||||
func (edit *editViewData) AppendText(text string) {
|
||||
|
@ -432,21 +258,24 @@ func (edit *editViewData) AppendText(text string) {
|
|||
textValue += text
|
||||
edit.properties[Text] = textValue
|
||||
edit.session.callFunc("appendToInnerHTML", edit.htmlID(), text)
|
||||
edit.session.callFunc("appendToInputValue", edit.htmlID(), text)
|
||||
edit.textChanged(textValue, oldText)
|
||||
return
|
||||
}
|
||||
}
|
||||
edit.set(Text, text)
|
||||
edit.setRaw(Text, text)
|
||||
} else {
|
||||
edit.set(Text, GetText(edit)+text)
|
||||
edit.setRaw(Text, GetText(edit)+text)
|
||||
}
|
||||
}
|
||||
|
||||
func (edit *editViewData) textChanged(newText, oldText string) {
|
||||
for _, listener := range edit.textChangeListeners {
|
||||
for _, listener := range GetTextChangedListeners(edit) {
|
||||
listener(edit, newText, oldText)
|
||||
}
|
||||
edit.propertyChangedEvent(Text)
|
||||
if listener, ok := edit.changeListener[Text]; ok {
|
||||
listener(edit, Text)
|
||||
}
|
||||
}
|
||||
|
||||
func (edit *editViewData) htmlTag() string {
|
||||
|
@ -462,7 +291,9 @@ func (edit *editViewData) htmlSubviews(self View, buffer *strings.Builder) {
|
|||
buffer.WriteString(text)
|
||||
}
|
||||
}
|
||||
edit.dataListHtmlSubviews(self, buffer)
|
||||
dataListHtmlSubviews(self, buffer, func(text string, session Session) string {
|
||||
return text
|
||||
})
|
||||
}
|
||||
|
||||
func (edit *editViewData) htmlProperties(self View, buffer *strings.Builder) {
|
||||
|
@ -547,16 +378,16 @@ func (edit *editViewData) htmlProperties(self View, buffer *strings.Builder) {
|
|||
}
|
||||
}
|
||||
|
||||
edit.dataListHtmlProperties(edit, buffer)
|
||||
dataListHtmlProperties(edit, buffer)
|
||||
}
|
||||
|
||||
func (edit *editViewData) handleCommand(self View, command string, data DataObject) bool {
|
||||
func (edit *editViewData) handleCommand(self View, command PropertyName, data DataObject) bool {
|
||||
switch command {
|
||||
case "textChanged":
|
||||
oldText := GetText(edit)
|
||||
if text, ok := data.PropertyValue("text"); ok {
|
||||
edit.properties[Text] = text
|
||||
if text := GetText(edit); text != oldText {
|
||||
edit.setRaw(Text, text)
|
||||
if text != oldText {
|
||||
edit.textChanged(text, oldText)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,526 @@
|
|||
package rui
|
||||
|
||||
import "strings"
|
||||
|
||||
var eventJsFunc = map[PropertyName]struct{ jsEvent, jsFunc string }{
|
||||
FocusEvent: {jsEvent: "onfocus", jsFunc: "focusEvent"},
|
||||
LostFocusEvent: {jsEvent: "onblur", jsFunc: "blurEvent"},
|
||||
KeyDownEvent: {jsEvent: "onkeydown", jsFunc: "keyDownEvent"},
|
||||
KeyUpEvent: {jsEvent: "onkeyup", jsFunc: "keyUpEvent"},
|
||||
ClickEvent: {jsEvent: "onclick", jsFunc: "clickEvent"},
|
||||
DoubleClickEvent: {jsEvent: "ondblclick", jsFunc: "doubleClickEvent"},
|
||||
MouseDown: {jsEvent: "onmousedown", jsFunc: "mouseDownEvent"},
|
||||
MouseUp: {jsEvent: "onmouseup", jsFunc: "mouseUpEvent"},
|
||||
MouseMove: {jsEvent: "onmousemove", jsFunc: "mouseMoveEvent"},
|
||||
MouseOut: {jsEvent: "onmouseout", jsFunc: "mouseOutEvent"},
|
||||
MouseOver: {jsEvent: "onmouseover", jsFunc: "mouseOverEvent"},
|
||||
ContextMenuEvent: {jsEvent: "oncontextmenu", jsFunc: "contextMenuEvent"},
|
||||
PointerDown: {jsEvent: "onpointerdown", jsFunc: "pointerDownEvent"},
|
||||
PointerUp: {jsEvent: "onpointerup", jsFunc: "pointerUpEvent"},
|
||||
PointerMove: {jsEvent: "onpointermove", jsFunc: "pointerMoveEvent"},
|
||||
PointerCancel: {jsEvent: "onpointercancel", jsFunc: "pointerCancelEvent"},
|
||||
PointerOut: {jsEvent: "onpointerout", jsFunc: "pointerOutEvent"},
|
||||
PointerOver: {jsEvent: "onpointerover", jsFunc: "pointerOverEvent"},
|
||||
TouchStart: {jsEvent: "ontouchstart", jsFunc: "touchStartEvent"},
|
||||
TouchEnd: {jsEvent: "ontouchend", jsFunc: "touchEndEvent"},
|
||||
TouchMove: {jsEvent: "ontouchmove", jsFunc: "touchMoveEvent"},
|
||||
TouchCancel: {jsEvent: "ontouchcancel", jsFunc: "touchCancelEvent"},
|
||||
TransitionRunEvent: {jsEvent: "ontransitionrun", jsFunc: "transitionRunEvent"},
|
||||
TransitionStartEvent: {jsEvent: "ontransitionstart", jsFunc: "transitionStartEvent"},
|
||||
TransitionEndEvent: {jsEvent: "ontransitionend", jsFunc: "transitionEndEvent"},
|
||||
TransitionCancelEvent: {jsEvent: "ontransitioncancel", jsFunc: "transitionCancelEvent"},
|
||||
AnimationStartEvent: {jsEvent: "onanimationstart", jsFunc: "animationStartEvent"},
|
||||
AnimationEndEvent: {jsEvent: "onanimationend", jsFunc: "animationEndEvent"},
|
||||
AnimationIterationEvent: {jsEvent: "onanimationiteration", jsFunc: "animationIterationEvent"},
|
||||
AnimationCancelEvent: {jsEvent: "onanimationcancel", jsFunc: "animationCancelEvent"},
|
||||
}
|
||||
|
||||
func valueToNoParamListeners[V any](value any) ([]func(V), bool) {
|
||||
if value == nil {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case func(V):
|
||||
return []func(V){value}, true
|
||||
|
||||
case func():
|
||||
fn := func(V) {
|
||||
value()
|
||||
}
|
||||
return []func(V){fn}, true
|
||||
|
||||
case []func(V):
|
||||
if len(value) == 0 {
|
||||
return nil, true
|
||||
}
|
||||
for _, fn := range value {
|
||||
if fn == nil {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return value, true
|
||||
|
||||
case []func():
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V), count)
|
||||
for i, v := range value {
|
||||
if v == nil {
|
||||
return nil, false
|
||||
}
|
||||
listeners[i] = func(V) {
|
||||
v()
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
|
||||
case []any:
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V), count)
|
||||
for i, v := range value {
|
||||
if v == nil {
|
||||
return nil, false
|
||||
}
|
||||
switch v := v.(type) {
|
||||
case func(V):
|
||||
listeners[i] = v
|
||||
|
||||
case func():
|
||||
listeners[i] = func(V) {
|
||||
v()
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func valueToEventListeners[V View, E any](value any) ([]func(V, E), bool) {
|
||||
if value == nil {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case func(V, E):
|
||||
return []func(V, E){value}, true
|
||||
|
||||
case func(E):
|
||||
fn := func(_ V, event E) {
|
||||
value(event)
|
||||
}
|
||||
return []func(V, E){fn}, true
|
||||
|
||||
case func(V):
|
||||
fn := func(view V, _ E) {
|
||||
value(view)
|
||||
}
|
||||
return []func(V, E){fn}, true
|
||||
|
||||
case func():
|
||||
fn := func(V, E) {
|
||||
value()
|
||||
}
|
||||
return []func(V, E){fn}, true
|
||||
|
||||
case []func(V, E):
|
||||
if len(value) == 0 {
|
||||
return nil, true
|
||||
}
|
||||
for _, fn := range value {
|
||||
if fn == nil {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return value, true
|
||||
|
||||
case []func(E):
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V, E), count)
|
||||
for i, v := range value {
|
||||
if v == nil {
|
||||
return nil, false
|
||||
}
|
||||
listeners[i] = func(_ V, event E) {
|
||||
v(event)
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
|
||||
case []func(V):
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V, E), count)
|
||||
for i, v := range value {
|
||||
if v == nil {
|
||||
return nil, false
|
||||
}
|
||||
listeners[i] = func(view V, _ E) {
|
||||
v(view)
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
|
||||
case []func():
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V, E), count)
|
||||
for i, v := range value {
|
||||
if v == nil {
|
||||
return nil, false
|
||||
}
|
||||
listeners[i] = func(V, E) {
|
||||
v()
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
|
||||
case []any:
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V, E), count)
|
||||
for i, v := range value {
|
||||
if v == nil {
|
||||
return nil, false
|
||||
}
|
||||
switch v := v.(type) {
|
||||
case func(V, E):
|
||||
listeners[i] = v
|
||||
|
||||
case func(E):
|
||||
listeners[i] = func(_ V, event E) {
|
||||
v(event)
|
||||
}
|
||||
|
||||
case func(V):
|
||||
listeners[i] = func(view V, _ E) {
|
||||
v(view)
|
||||
}
|
||||
|
||||
case func():
|
||||
listeners[i] = func(V, E) {
|
||||
v()
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func valueToEventWithOldListeners[V View, E any](value any) ([]func(V, E, E), bool) {
|
||||
if value == nil {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case func(V, E, E):
|
||||
return []func(V, E, E){value}, true
|
||||
|
||||
case func(V, E):
|
||||
fn := func(v V, val, _ E) {
|
||||
value(v, val)
|
||||
}
|
||||
return []func(V, E, E){fn}, true
|
||||
|
||||
case func(E, E):
|
||||
fn := func(_ V, val, old E) {
|
||||
value(val, old)
|
||||
}
|
||||
return []func(V, E, E){fn}, true
|
||||
|
||||
case func(E):
|
||||
fn := func(_ V, val, _ E) {
|
||||
value(val)
|
||||
}
|
||||
return []func(V, E, E){fn}, true
|
||||
|
||||
case func(V):
|
||||
fn := func(v V, _, _ E) {
|
||||
value(v)
|
||||
}
|
||||
return []func(V, E, E){fn}, true
|
||||
|
||||
case func():
|
||||
fn := func(V, E, E) {
|
||||
value()
|
||||
}
|
||||
return []func(V, E, E){fn}, true
|
||||
|
||||
case []func(V, E, E):
|
||||
if len(value) == 0 {
|
||||
return nil, true
|
||||
}
|
||||
for _, fn := range value {
|
||||
if fn == nil {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return value, true
|
||||
|
||||
case []func(V, E):
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V, E, E), count)
|
||||
for i, fn := range value {
|
||||
if fn == nil {
|
||||
return nil, false
|
||||
}
|
||||
listeners[i] = func(view V, val, _ E) {
|
||||
fn(view, val)
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
|
||||
case []func(E):
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V, E, E), count)
|
||||
for i, fn := range value {
|
||||
if fn == nil {
|
||||
return nil, false
|
||||
}
|
||||
listeners[i] = func(_ V, val, _ E) {
|
||||
fn(val)
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
|
||||
case []func(E, E):
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V, E, E), count)
|
||||
for i, fn := range value {
|
||||
if fn == nil {
|
||||
return nil, false
|
||||
}
|
||||
listeners[i] = func(_ V, val, old E) {
|
||||
fn(val, old)
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
|
||||
case []func(V):
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V, E, E), count)
|
||||
for i, fn := range value {
|
||||
if fn == nil {
|
||||
return nil, false
|
||||
}
|
||||
listeners[i] = func(view V, _, _ E) {
|
||||
fn(view)
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
|
||||
case []func():
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V, E, E), count)
|
||||
for i, fn := range value {
|
||||
if fn == nil {
|
||||
return nil, false
|
||||
}
|
||||
listeners[i] = func(V, E, E) {
|
||||
fn()
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
|
||||
case []any:
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V, E, E), count)
|
||||
for i, v := range value {
|
||||
if v == nil {
|
||||
return nil, false
|
||||
}
|
||||
switch fn := v.(type) {
|
||||
case func(V, E, E):
|
||||
listeners[i] = fn
|
||||
|
||||
case func(V, E):
|
||||
listeners[i] = func(view V, val, _ E) {
|
||||
fn(view, val)
|
||||
}
|
||||
|
||||
case func(E, E):
|
||||
listeners[i] = func(_ V, val, old E) {
|
||||
fn(val, old)
|
||||
}
|
||||
|
||||
case func(E):
|
||||
listeners[i] = func(_ V, val, _ E) {
|
||||
fn(val)
|
||||
}
|
||||
|
||||
case func(V):
|
||||
listeners[i] = func(view V, _, _ E) {
|
||||
fn(view)
|
||||
}
|
||||
|
||||
case func():
|
||||
listeners[i] = func(V, E, E) {
|
||||
fn()
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func getNoParamEventListeners[V View](view View, subviewID []string, tag PropertyName) []func(V) {
|
||||
if len(subviewID) > 0 && subviewID[0] != "" {
|
||||
view = ViewByID(view, subviewID[0])
|
||||
}
|
||||
if view != nil {
|
||||
if value := view.Get(tag); value != nil {
|
||||
if result, ok := value.([]func(V)); ok {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
return []func(V){}
|
||||
}
|
||||
|
||||
func getEventListeners[V View, E any](view View, subviewID []string, tag PropertyName) []func(V, E) {
|
||||
if len(subviewID) > 0 && subviewID[0] != "" {
|
||||
view = ViewByID(view, subviewID[0])
|
||||
}
|
||||
if view != nil {
|
||||
if value := view.Get(tag); value != nil {
|
||||
if result, ok := value.([]func(V, E)); ok {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
return []func(V, E){}
|
||||
}
|
||||
|
||||
func getEventWithOldListeners[V View, E any](view View, subviewID []string, tag PropertyName) []func(V, E, E) {
|
||||
if len(subviewID) > 0 && subviewID[0] != "" {
|
||||
view = ViewByID(view, subviewID[0])
|
||||
}
|
||||
if view != nil {
|
||||
if value := view.Get(tag); value != nil {
|
||||
if result, ok := value.([]func(V, E, E)); ok {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
return []func(V, E, E){}
|
||||
}
|
||||
|
||||
func setNoParamEventListener[V View](properties Properties, tag PropertyName, value any) []PropertyName {
|
||||
if listeners, ok := valueToNoParamListeners[V](value); ok {
|
||||
if len(listeners) > 0 {
|
||||
properties.setRaw(tag, listeners)
|
||||
} else if properties.getRaw(tag) != nil {
|
||||
properties.setRaw(tag, nil)
|
||||
} else {
|
||||
return []PropertyName{}
|
||||
}
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
notCompatibleType(tag, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func setViewEventListener[V View, T any](properties Properties, tag PropertyName, value any) []PropertyName {
|
||||
if listeners, ok := valueToEventListeners[V, T](value); ok {
|
||||
if len(listeners) > 0 {
|
||||
properties.setRaw(tag, listeners)
|
||||
} else if properties.getRaw(tag) != nil {
|
||||
properties.setRaw(tag, nil)
|
||||
} else {
|
||||
return []PropertyName{}
|
||||
}
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
notCompatibleType(tag, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func setEventWithOldListener[V View, T any](properties Properties, tag PropertyName, value any) []PropertyName {
|
||||
listeners, ok := valueToEventWithOldListeners[V, T](value)
|
||||
if !ok {
|
||||
notCompatibleType(tag, value)
|
||||
return nil
|
||||
} else if len(listeners) > 0 {
|
||||
properties.setRaw(tag, listeners)
|
||||
} else if properties.getRaw(tag) != nil {
|
||||
properties.setRaw(tag, nil)
|
||||
} else {
|
||||
return []PropertyName{}
|
||||
}
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
func viewEventsHtml[T any](view View, events []PropertyName, buffer *strings.Builder) {
|
||||
for _, tag := range []PropertyName{AnimationStartEvent, AnimationEndEvent, AnimationIterationEvent, AnimationCancelEvent} {
|
||||
if value := view.getRaw(tag); value != nil {
|
||||
if js, ok := eventJsFunc[tag]; ok {
|
||||
if listeners, ok := value.([]func(View, T)); ok && len(listeners) > 0 {
|
||||
buffer.WriteString(js.jsEvent)
|
||||
buffer.WriteString(`="`)
|
||||
buffer.WriteString(js.jsFunc)
|
||||
buffer.WriteString(`(this, event)" `)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateEventListenerHtml(view View, tag PropertyName) {
|
||||
if js, ok := eventJsFunc[tag]; ok {
|
||||
value := view.getRaw(tag)
|
||||
session := view.Session()
|
||||
htmlID := view.htmlID()
|
||||
if value == nil {
|
||||
session.removeProperty(view.htmlID(), js.jsEvent)
|
||||
} else {
|
||||
session.updateProperty(htmlID, js.jsEvent, js.jsFunc+"(this, event)")
|
||||
}
|
||||
}
|
||||
}
|
122
filePicker.go
122
filePicker.go
|
@ -25,7 +25,7 @@ const (
|
|||
// `func(picker rui.FilePicker)`,
|
||||
// `func(files []rui.FileInfo)`,
|
||||
// `func()`.
|
||||
FileSelectedEvent = "file-selected-event"
|
||||
FileSelectedEvent PropertyName = "file-selected-event"
|
||||
|
||||
// Accept is the constant for "accept" property tag.
|
||||
//
|
||||
|
@ -39,7 +39,7 @@ const (
|
|||
// Conversion rules:
|
||||
// `string` - may contain single value of multiple separated by comma(`,`).
|
||||
// `[]string` - an array of acceptable file extensions or MIME types.
|
||||
Accept = "accept"
|
||||
Accept PropertyName = "accept"
|
||||
|
||||
// Multiple is the constant for "multiple" property tag.
|
||||
//
|
||||
|
@ -51,7 +51,7 @@ const (
|
|||
// Values:
|
||||
// `true` or `1` or "true", "yes", "on", "1" - Several files can be selected.
|
||||
// `false` or `0` or "false", "no", "off", "0" - Only one file can be selected.
|
||||
Multiple = "multiple"
|
||||
Multiple PropertyName = "multiple"
|
||||
)
|
||||
|
||||
// FileInfo describes a file which selected in the FilePicker view
|
||||
|
@ -82,9 +82,8 @@ type FilePicker interface {
|
|||
|
||||
type filePickerData struct {
|
||||
viewData
|
||||
files []FileInfo
|
||||
fileSelectedListeners []func(FilePicker, []FileInfo)
|
||||
loader map[int]func(FileInfo, []byte)
|
||||
files []FileInfo
|
||||
loader map[int]func(FileInfo, []byte)
|
||||
}
|
||||
|
||||
func (file *FileInfo) initBy(node DataValue) {
|
||||
|
@ -115,7 +114,7 @@ func NewFilePicker(session Session, params Params) FilePicker {
|
|||
}
|
||||
|
||||
func newFilePicker(session Session) View {
|
||||
return NewFilePicker(session, nil)
|
||||
return new(filePickerData) // NewFilePicker(session, nil)
|
||||
}
|
||||
|
||||
func (picker *filePickerData) init(session Session) {
|
||||
|
@ -124,11 +123,9 @@ func (picker *filePickerData) init(session Session) {
|
|||
picker.hasHtmlDisabled = true
|
||||
picker.files = []FileInfo{}
|
||||
picker.loader = map[int]func(FileInfo, []byte){}
|
||||
picker.fileSelectedListeners = []func(FilePicker, []FileInfo){}
|
||||
}
|
||||
picker.set = filePickerSet
|
||||
picker.changed = filePickerPropertyChanged
|
||||
|
||||
func (picker *filePickerData) String() string {
|
||||
return getViewString(picker, nil)
|
||||
}
|
||||
|
||||
func (picker *filePickerData) Focusable() bool {
|
||||
|
@ -153,62 +150,27 @@ func (picker *filePickerData) LoadFile(file FileInfo, result func(FileInfo, []by
|
|||
}
|
||||
}
|
||||
|
||||
func (picker *filePickerData) Remove(tag string) {
|
||||
picker.remove(strings.ToLower(tag))
|
||||
}
|
||||
func filePickerSet(view View, tag PropertyName, value any) []PropertyName {
|
||||
|
||||
func (picker *filePickerData) remove(tag string) {
|
||||
switch tag {
|
||||
case FileSelectedEvent:
|
||||
if len(picker.fileSelectedListeners) > 0 {
|
||||
picker.fileSelectedListeners = []func(FilePicker, []FileInfo){}
|
||||
picker.propertyChangedEvent(tag)
|
||||
setAccept := func(value string) []PropertyName {
|
||||
if value != "" {
|
||||
view.setRaw(tag, value)
|
||||
} else if view.getRaw(tag) != nil {
|
||||
view.setRaw(tag, nil)
|
||||
} else {
|
||||
return []PropertyName{}
|
||||
}
|
||||
|
||||
case Accept:
|
||||
delete(picker.properties, tag)
|
||||
if picker.created {
|
||||
picker.session.removeProperty(picker.htmlID(), "accept")
|
||||
}
|
||||
picker.propertyChangedEvent(tag)
|
||||
|
||||
default:
|
||||
picker.viewData.remove(tag)
|
||||
}
|
||||
}
|
||||
|
||||
func (picker *filePickerData) Set(tag string, value any) bool {
|
||||
return picker.set(strings.ToLower(tag), value)
|
||||
}
|
||||
|
||||
func (picker *filePickerData) set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
picker.remove(tag)
|
||||
return true
|
||||
return []PropertyName{Accept}
|
||||
}
|
||||
|
||||
switch tag {
|
||||
case FileSelectedEvent:
|
||||
listeners, ok := valueToEventListeners[FilePicker, []FileInfo](value)
|
||||
if !ok {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
} else if listeners == nil {
|
||||
listeners = []func(FilePicker, []FileInfo){}
|
||||
}
|
||||
picker.fileSelectedListeners = listeners
|
||||
picker.propertyChangedEvent(tag)
|
||||
return true
|
||||
return setViewEventListener[FilePicker, []FileInfo](view, tag, value)
|
||||
|
||||
case Accept:
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
value = strings.Trim(value, " \t\n")
|
||||
if value == "" {
|
||||
picker.remove(Accept)
|
||||
} else {
|
||||
picker.properties[Accept] = value
|
||||
}
|
||||
return setAccept(strings.Trim(value, " \t\n"))
|
||||
|
||||
case []string:
|
||||
buffer := allocStringBuilder()
|
||||
|
@ -222,29 +184,27 @@ func (picker *filePickerData) set(tag string, value any) bool {
|
|||
buffer.WriteString(val)
|
||||
}
|
||||
}
|
||||
if buffer.Len() == 0 {
|
||||
picker.remove(Accept)
|
||||
} else {
|
||||
picker.properties[Accept] = buffer.String()
|
||||
}
|
||||
|
||||
default:
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
return setAccept(buffer.String())
|
||||
}
|
||||
notCompatibleType(tag, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
if picker.created {
|
||||
if css := picker.acceptCSS(); css != "" {
|
||||
picker.session.updateProperty(picker.htmlID(), "accept", css)
|
||||
} else {
|
||||
picker.session.removeProperty(picker.htmlID(), "accept")
|
||||
}
|
||||
return viewSet(view, tag, value)
|
||||
}
|
||||
|
||||
func filePickerPropertyChanged(view View, tag PropertyName) {
|
||||
switch tag {
|
||||
case Accept:
|
||||
session := view.Session()
|
||||
if css := acceptPropertyCSS(view); css != "" {
|
||||
session.updateProperty(view.htmlID(), "accept", css)
|
||||
} else {
|
||||
session.removeProperty(view.htmlID(), "accept")
|
||||
}
|
||||
picker.propertyChangedEvent(tag)
|
||||
return true
|
||||
|
||||
default:
|
||||
return picker.viewData.set(tag, value)
|
||||
viewPropertyChanged(view, tag)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -252,10 +212,10 @@ func (picker *filePickerData) htmlTag() string {
|
|||
return "input"
|
||||
}
|
||||
|
||||
func (picker *filePickerData) acceptCSS() string {
|
||||
accept, ok := stringProperty(picker, Accept, picker.Session())
|
||||
func acceptPropertyCSS(view View) string {
|
||||
accept, ok := stringProperty(view, Accept, view.Session())
|
||||
if !ok {
|
||||
if value := valueFromStyle(picker, Accept); value != nil {
|
||||
if value := valueFromStyle(view, Accept); value != nil {
|
||||
accept, ok = value.(string)
|
||||
}
|
||||
}
|
||||
|
@ -282,7 +242,7 @@ func (picker *filePickerData) acceptCSS() string {
|
|||
func (picker *filePickerData) htmlProperties(self View, buffer *strings.Builder) {
|
||||
picker.viewData.htmlProperties(self, buffer)
|
||||
|
||||
if accept := picker.acceptCSS(); accept != "" {
|
||||
if accept := acceptPropertyCSS(picker); accept != "" {
|
||||
buffer.WriteString(` accept="`)
|
||||
buffer.WriteString(accept)
|
||||
buffer.WriteRune('"')
|
||||
|
@ -299,7 +259,7 @@ func (picker *filePickerData) htmlProperties(self View, buffer *strings.Builder)
|
|||
}
|
||||
}
|
||||
|
||||
func (picker *filePickerData) handleCommand(self View, command string, data DataObject) bool {
|
||||
func (picker *filePickerData) handleCommand(self View, command PropertyName, data DataObject) bool {
|
||||
switch command {
|
||||
case "fileSelected":
|
||||
if node := data.PropertyByTag("files"); node != nil && node.Type() == ArrayNode {
|
||||
|
@ -312,7 +272,7 @@ func (picker *filePickerData) handleCommand(self View, command string, data Data
|
|||
}
|
||||
picker.files = files
|
||||
|
||||
for _, listener := range picker.fileSelectedListeners {
|
||||
for _, listener := range GetFileSelectedListeners(picker) {
|
||||
listener(picker, files)
|
||||
}
|
||||
}
|
||||
|
|
140
focusEvents.go
140
focusEvents.go
|
@ -17,7 +17,7 @@ const (
|
|||
//
|
||||
// Allowed listener formats:
|
||||
// `func()`.
|
||||
FocusEvent = "focus-event"
|
||||
FocusEvent PropertyName = "focus-event"
|
||||
|
||||
// LostFocusEvent is the constant for "lost-focus-event" property tag.
|
||||
//
|
||||
|
@ -32,136 +32,18 @@ const (
|
|||
//
|
||||
// Allowed listener formats:
|
||||
// `func()`.
|
||||
LostFocusEvent = "lost-focus-event"
|
||||
LostFocusEvent PropertyName = "lost-focus-event"
|
||||
)
|
||||
|
||||
func valueToNoParamListeners[V any](value any) ([]func(V), bool) {
|
||||
if value == nil {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case func(V):
|
||||
return []func(V){value}, true
|
||||
|
||||
case func():
|
||||
fn := func(V) {
|
||||
value()
|
||||
}
|
||||
return []func(V){fn}, true
|
||||
|
||||
case []func(V):
|
||||
if len(value) == 0 {
|
||||
return nil, true
|
||||
}
|
||||
for _, fn := range value {
|
||||
if fn == nil {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return value, true
|
||||
|
||||
case []func():
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V), count)
|
||||
for i, v := range value {
|
||||
if v == nil {
|
||||
return nil, false
|
||||
}
|
||||
listeners[i] = func(V) {
|
||||
v()
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
|
||||
case []any:
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V), count)
|
||||
for i, v := range value {
|
||||
if v == nil {
|
||||
return nil, false
|
||||
}
|
||||
switch v := v.(type) {
|
||||
case func(V):
|
||||
listeners[i] = v
|
||||
|
||||
case func():
|
||||
listeners[i] = func(V) {
|
||||
v()
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
var focusEvents = map[string]struct{ jsEvent, jsFunc string }{
|
||||
FocusEvent: {jsEvent: "onfocus", jsFunc: "focusEvent"},
|
||||
LostFocusEvent: {jsEvent: "onblur", jsFunc: "blurEvent"},
|
||||
}
|
||||
|
||||
func (view *viewData) setFocusListener(tag string, value any) bool {
|
||||
listeners, ok := valueToNoParamListeners[View](value)
|
||||
if !ok {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
|
||||
if listeners == nil {
|
||||
view.removeFocusListener(tag)
|
||||
} else if js, ok := focusEvents[tag]; ok {
|
||||
view.properties[tag] = listeners
|
||||
if view.created {
|
||||
view.session.updateProperty(view.htmlID(), js.jsEvent, js.jsFunc+"(this, event)")
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (view *viewData) removeFocusListener(tag string) {
|
||||
delete(view.properties, tag)
|
||||
if view.created {
|
||||
if js, ok := focusEvents[tag]; ok {
|
||||
view.session.removeProperty(view.htmlID(), js.jsEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getFocusListeners(view View, subviewID []string, tag string) []func(View) {
|
||||
if len(subviewID) > 0 && subviewID[0] != "" {
|
||||
view = ViewByID(view, subviewID[0])
|
||||
}
|
||||
|
||||
if view != nil {
|
||||
if value := view.Get(tag); value != nil {
|
||||
if result, ok := value.([]func(View)); ok {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
return []func(View){}
|
||||
}
|
||||
|
||||
func focusEventsHtml(view View, buffer *strings.Builder) {
|
||||
if view.Focusable() {
|
||||
for _, js := range focusEvents {
|
||||
buffer.WriteString(js.jsEvent)
|
||||
buffer.WriteString(`="`)
|
||||
buffer.WriteString(js.jsFunc)
|
||||
buffer.WriteString(`(this, event)" `)
|
||||
for _, tag := range []PropertyName{FocusEvent, LostFocusEvent} {
|
||||
if js, ok := eventJsFunc[tag]; ok {
|
||||
buffer.WriteString(js.jsEvent)
|
||||
buffer.WriteString(`="`)
|
||||
buffer.WriteString(js.jsFunc)
|
||||
buffer.WriteString(`(this, event)" `)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -169,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
|
||||
// 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) {
|
||||
return getFocusListeners(view, subviewID, FocusEvent)
|
||||
return getNoParamEventListeners[View](view, subviewID, FocusEvent)
|
||||
}
|
||||
|
||||
// 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.
|
||||
func GetLostFocusListeners(view View, subviewID ...string) []func(View) {
|
||||
return getFocusListeners(view, subviewID, LostFocusEvent)
|
||||
return getNoParamEventListeners[View](view, subviewID, LostFocusEvent)
|
||||
}
|
||||
|
|
311
gridLayout.go
311
gridLayout.go
|
@ -24,7 +24,7 @@ const (
|
|||
//
|
||||
// Usage in `SvgImageView`:
|
||||
// Same as "vertical-align".
|
||||
CellVerticalAlign = "cell-vertical-align"
|
||||
CellVerticalAlign PropertyName = "cell-vertical-align"
|
||||
|
||||
// CellHorizontalAlign is the constant for "cell-horizontal-align" property tag.
|
||||
//
|
||||
|
@ -43,12 +43,12 @@ const (
|
|||
//
|
||||
// Usage in `SvgImageView`:
|
||||
// Same as "horizontal-align".
|
||||
CellHorizontalAlign = "cell-horizontal-align"
|
||||
CellHorizontalAlign PropertyName = "cell-horizontal-align"
|
||||
|
||||
// CellVerticalSelfAlign is the constant for "cell-vertical-self-align" property tag.
|
||||
//
|
||||
// Used by `GridLayout`.
|
||||
// Sets the vertical alignment of `GridLayout` children within the cell they are occupying. The property is set for the
|
||||
// Sets the vertical alignment of `GridLayout` children within the cell they are occupying. The property is set for the
|
||||
// child view of `GridLayout`.
|
||||
//
|
||||
// Supported types: `int`, `string`.
|
||||
|
@ -58,12 +58,12 @@ const (
|
|||
// `1`(`BottomAlign`) or "bottom" - Bottom alignment.
|
||||
// `2`(`CenterAlign`) or "center" - Center alignment.
|
||||
// `3`(`StretchAlign`) or "stretch" - Full height stretch.
|
||||
CellVerticalSelfAlign = "cell-vertical-self-align"
|
||||
CellVerticalSelfAlign PropertyName = "cell-vertical-self-align"
|
||||
|
||||
// CellHorizontalSelfAlign is the constant for "cell-horizontal-self-align" property tag.
|
||||
//
|
||||
// Used by `GridLayout`.
|
||||
// Sets the horizontal alignment of `GridLayout` children within the occupied cell. The property is set for the child view
|
||||
// Sets the horizontal alignment of `GridLayout` children within the occupied cell. The property is set for the child view
|
||||
// of `GridLayout`.
|
||||
//
|
||||
// Supported types: `int`, `string`.
|
||||
|
@ -73,7 +73,7 @@ const (
|
|||
// `1`(`RightAlign`) or "right" - Right alignment.
|
||||
// `2`(`CenterAlign`) or "center" - Center alignment.
|
||||
// `3`(`StretchAlign`) or "stretch" - Full width stretch.
|
||||
CellHorizontalSelfAlign = "cell-horizontal-self-align"
|
||||
CellHorizontalSelfAlign PropertyName = "cell-horizontal-self-align"
|
||||
)
|
||||
|
||||
// GridAdapter is an interface to define [GridLayout] content. [GridLayout] will query interface functions to populate
|
||||
|
@ -126,7 +126,8 @@ func NewGridLayout(session Session, params Params) GridLayout {
|
|||
}
|
||||
|
||||
func newGridLayout(session Session) View {
|
||||
return NewGridLayout(session, nil)
|
||||
//return NewGridLayout(session, nil)
|
||||
return new(gridLayoutData)
|
||||
}
|
||||
|
||||
// Init initialize fields of GridLayout by default values
|
||||
|
@ -135,13 +136,14 @@ func (gridLayout *gridLayoutData) init(session Session) {
|
|||
gridLayout.tag = "GridLayout"
|
||||
gridLayout.systemClass = "ruiGridLayout"
|
||||
gridLayout.adapter = nil
|
||||
gridLayout.normalize = normalizeGridLayoutTag
|
||||
gridLayout.getFunc = gridLayout.get
|
||||
gridLayout.set = gridLayout.setFunc
|
||||
gridLayout.remove = gridLayout.removeFunc
|
||||
|
||||
}
|
||||
|
||||
func (gridLayout *gridLayoutData) String() string {
|
||||
return getViewString(gridLayout, nil)
|
||||
}
|
||||
|
||||
func (style *viewStyle) setGridCellSize(tag string, value any) bool {
|
||||
func setGridCellSize(properties Properties, tag PropertyName, value any) []PropertyName {
|
||||
setValues := func(values []string) bool {
|
||||
count := len(values)
|
||||
if count > 1 {
|
||||
|
@ -159,11 +161,11 @@ func (style *viewStyle) setGridCellSize(tag string, value any) bool {
|
|||
return false
|
||||
}
|
||||
}
|
||||
style.properties[tag] = sizes
|
||||
properties.setRaw(tag, sizes)
|
||||
} else if isConstantName(values[0]) {
|
||||
style.properties[tag] = values[0]
|
||||
properties.setRaw(tag, values[0])
|
||||
} else if size, err := stringToSizeUnit(values[0]); err == nil {
|
||||
style.properties[tag] = size
|
||||
properties.setRaw(tag, size)
|
||||
} else {
|
||||
invalidPropertyValue(tag, value)
|
||||
return false
|
||||
|
@ -175,41 +177,41 @@ func (style *viewStyle) setGridCellSize(tag string, value any) bool {
|
|||
case CellWidth, CellHeight:
|
||||
switch value := value.(type) {
|
||||
case SizeUnit, []SizeUnit:
|
||||
style.properties[tag] = value
|
||||
properties.setRaw(tag, value)
|
||||
|
||||
case string:
|
||||
if !setValues(strings.Split(value, ",")) {
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
case []string:
|
||||
if !setValues(value) {
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
case []DataValue:
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
invalidPropertyValue(tag, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
values := make([]string, count)
|
||||
for i, val := range value {
|
||||
if val.IsObject() {
|
||||
invalidPropertyValue(tag, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
values[i] = val.Value()
|
||||
}
|
||||
if !setValues(values) {
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
case []any:
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
invalidPropertyValue(tag, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
sizes := make([]any, count)
|
||||
for i, val := range value {
|
||||
|
@ -224,29 +226,29 @@ func (style *viewStyle) setGridCellSize(tag string, value any) bool {
|
|||
sizes[i] = size
|
||||
} else {
|
||||
invalidPropertyValue(tag, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
default:
|
||||
invalidPropertyValue(tag, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
}
|
||||
style.properties[tag] = sizes
|
||||
properties.setRaw(tag, sizes)
|
||||
|
||||
default:
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
return true
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
func (style *viewStyle) gridCellSizesCSS(tag string, session Session) string {
|
||||
switch cellSize := gridCellSizes(style, tag, session); len(cellSize) {
|
||||
func gridCellSizesCSS(properties Properties, tag PropertyName, session Session) string {
|
||||
switch cellSize := gridCellSizes(properties, tag, session); len(cellSize) {
|
||||
case 0:
|
||||
|
||||
case 1:
|
||||
|
@ -283,8 +285,8 @@ func (style *viewStyle) gridCellSizesCSS(tag string, session Session) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
func (gridLayout *gridLayoutData) normalizeTag(tag string) string {
|
||||
tag = strings.ToLower(tag)
|
||||
func normalizeGridLayoutTag(tag PropertyName) PropertyName {
|
||||
tag = defaultNormalize(tag)
|
||||
switch tag {
|
||||
case VerticalAlign:
|
||||
return CellVerticalAlign
|
||||
|
@ -301,162 +303,167 @@ func (gridLayout *gridLayoutData) normalizeTag(tag string) string {
|
|||
return tag
|
||||
}
|
||||
|
||||
func (gridLayout *gridLayoutData) Get(tag string) any {
|
||||
return gridLayout.get(gridLayout.normalizeTag(tag))
|
||||
}
|
||||
|
||||
func (gridLayout *gridLayoutData) get(tag string) any {
|
||||
if tag == Gap {
|
||||
func (gridLayout *gridLayoutData) get(self View, tag PropertyName) any {
|
||||
switch tag {
|
||||
case Gap:
|
||||
rowGap := GetGridRowGap(gridLayout)
|
||||
columnGap := GetGridColumnGap(gridLayout)
|
||||
if rowGap.Equal(columnGap) {
|
||||
return rowGap
|
||||
}
|
||||
return AutoSize()
|
||||
}
|
||||
|
||||
return gridLayout.viewsContainerData.get(tag)
|
||||
}
|
||||
|
||||
func (gridLayout *gridLayoutData) Remove(tag string) {
|
||||
gridLayout.remove(gridLayout.normalizeTag(tag))
|
||||
}
|
||||
|
||||
func (gridLayout *gridLayoutData) remove(tag string) {
|
||||
switch tag {
|
||||
case Gap:
|
||||
gridLayout.remove(GridRowGap)
|
||||
gridLayout.remove(GridColumnGap)
|
||||
gridLayout.propertyChangedEvent(Gap)
|
||||
return
|
||||
|
||||
case Content:
|
||||
gridLayout.adapter = nil
|
||||
}
|
||||
|
||||
gridLayout.viewsContainerData.remove(tag)
|
||||
|
||||
if gridLayout.created {
|
||||
switch tag {
|
||||
case CellWidth:
|
||||
gridLayout.session.updateCSSProperty(gridLayout.htmlID(), `grid-template-columns`,
|
||||
gridLayout.gridCellSizesCSS(CellWidth, gridLayout.session))
|
||||
|
||||
case CellHeight:
|
||||
gridLayout.session.updateCSSProperty(gridLayout.htmlID(), `grid-template-rows`,
|
||||
gridLayout.gridCellSizesCSS(CellHeight, gridLayout.session))
|
||||
|
||||
if gridLayout.adapter != nil {
|
||||
return gridLayout.adapter
|
||||
}
|
||||
}
|
||||
|
||||
return gridLayout.viewsContainerData.get(gridLayout, tag)
|
||||
}
|
||||
|
||||
func (gridLayout *gridLayoutData) Set(tag string, value any) bool {
|
||||
return gridLayout.set(gridLayout.normalizeTag(tag), value)
|
||||
}
|
||||
|
||||
func (gridLayout *gridLayoutData) set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
gridLayout.remove(tag)
|
||||
return true
|
||||
}
|
||||
|
||||
func (gridLayout *gridLayoutData) removeFunc(self View, tag PropertyName) []PropertyName {
|
||||
switch tag {
|
||||
case Gap:
|
||||
return gridLayout.set(GridRowGap, value) && gridLayout.set(GridColumnGap, value)
|
||||
result := []PropertyName{}
|
||||
for _, tag := range []PropertyName{GridRowGap, GridColumnGap} {
|
||||
if gridLayout.getRaw(tag) != nil {
|
||||
gridLayout.setRaw(tag, nil)
|
||||
result = append(result, tag)
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
||||
case Content:
|
||||
if len(gridLayout.views) > 0 || gridLayout.adapter != nil {
|
||||
gridLayout.views = []View{}
|
||||
gridLayout.adapter = nil
|
||||
return []PropertyName{Content}
|
||||
}
|
||||
return []PropertyName{}
|
||||
}
|
||||
|
||||
return gridLayout.viewsContainerData.removeFunc(gridLayout, tag)
|
||||
}
|
||||
|
||||
func (gridLayout *gridLayoutData) setFunc(self View, tag PropertyName, value any) []PropertyName {
|
||||
switch tag {
|
||||
case Gap:
|
||||
result := gridLayout.setFunc(gridLayout, GridRowGap, value)
|
||||
if result != nil {
|
||||
if gap := gridLayout.getRaw(GridRowGap); gap != nil {
|
||||
gridLayout.setRaw(GridColumnGap, gap)
|
||||
result = append(result, GridColumnGap)
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
||||
case Content:
|
||||
if adapter, ok := value.(GridAdapter); ok {
|
||||
gridLayout.adapter = adapter
|
||||
gridLayout.UpdateGridContent()
|
||||
return true
|
||||
gridLayout.createGridContent()
|
||||
} else if gridLayout.setContent(value) {
|
||||
gridLayout.adapter = nil
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
gridLayout.adapter = nil
|
||||
return []PropertyName{Content}
|
||||
}
|
||||
|
||||
if gridLayout.viewsContainerData.set(tag, value) {
|
||||
if gridLayout.created {
|
||||
switch tag {
|
||||
case CellWidth:
|
||||
gridLayout.session.updateCSSProperty(gridLayout.htmlID(), `grid-template-columns`,
|
||||
gridLayout.gridCellSizesCSS(CellWidth, gridLayout.session))
|
||||
return gridLayout.viewsContainerData.setFunc(gridLayout, tag, value)
|
||||
}
|
||||
|
||||
case CellHeight:
|
||||
gridLayout.session.updateCSSProperty(gridLayout.htmlID(), `grid-template-rows`,
|
||||
gridLayout.gridCellSizesCSS(CellHeight, gridLayout.session))
|
||||
func gridLayoutPropertyChanged(view View, tag PropertyName) {
|
||||
switch tag {
|
||||
case CellWidth:
|
||||
view.Session().updateCSSProperty(view.htmlID(), `grid-template-columns`,
|
||||
gridCellSizesCSS(view, CellWidth, view.Session()))
|
||||
|
||||
case CellHeight:
|
||||
view.Session().updateCSSProperty(view.htmlID(), `grid-template-rows`,
|
||||
gridCellSizesCSS(view, CellHeight, view.Session()))
|
||||
|
||||
default:
|
||||
viewsContainerPropertyChanged(view, tag)
|
||||
}
|
||||
}
|
||||
|
||||
func (gridLayout *gridLayoutData) createGridContent() bool {
|
||||
if gridLayout.adapter == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
adapter := gridLayout.adapter
|
||||
gridLayout.views = []View{}
|
||||
|
||||
session := gridLayout.session
|
||||
htmlID := gridLayout.htmlID()
|
||||
isDisabled := IsDisabled(gridLayout)
|
||||
|
||||
var columnSpan GridCellColumnSpanAdapter = nil
|
||||
if span, ok := adapter.(GridCellColumnSpanAdapter); ok {
|
||||
columnSpan = span
|
||||
}
|
||||
|
||||
var rowSpan GridCellRowSpanAdapter = nil
|
||||
if span, ok := adapter.(GridCellRowSpanAdapter); ok {
|
||||
rowSpan = span
|
||||
}
|
||||
|
||||
width := adapter.GridColumnCount()
|
||||
height := adapter.GridRowCount()
|
||||
for column := 0; column < width; column++ {
|
||||
for row := 0; row < height; row++ {
|
||||
if view := adapter.GridCellContent(row, column, session); view != nil {
|
||||
view.setParentID(htmlID)
|
||||
|
||||
columnCount := 1
|
||||
if columnSpan != nil {
|
||||
columnCount = columnSpan.GridCellColumnSpan(row, column)
|
||||
}
|
||||
|
||||
if columnCount > 1 {
|
||||
view.Set(Column, Range{First: column, Last: column + columnCount - 1})
|
||||
} else {
|
||||
view.Set(Column, column)
|
||||
}
|
||||
|
||||
rowCount := 1
|
||||
if rowSpan != nil {
|
||||
rowCount = rowSpan.GridCellRowSpan(row, column)
|
||||
}
|
||||
|
||||
if rowCount > 1 {
|
||||
view.Set(Row, Range{First: row, Last: row + rowCount - 1})
|
||||
} else {
|
||||
view.Set(Row, row)
|
||||
}
|
||||
|
||||
if isDisabled {
|
||||
view.Set(Disabled, true)
|
||||
}
|
||||
|
||||
gridLayout.views = append(gridLayout.views, view)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
func (gridLayout *gridLayoutData) UpdateGridContent() {
|
||||
if adapter := gridLayout.adapter; adapter != nil {
|
||||
gridLayout.views = []View{}
|
||||
|
||||
session := gridLayout.session
|
||||
htmlID := gridLayout.htmlID()
|
||||
isDisabled := IsDisabled(gridLayout)
|
||||
|
||||
var columnSpan GridCellColumnSpanAdapter = nil
|
||||
if span, ok := adapter.(GridCellColumnSpanAdapter); ok {
|
||||
columnSpan = span
|
||||
}
|
||||
|
||||
var rowSpan GridCellRowSpanAdapter = nil
|
||||
if span, ok := adapter.(GridCellRowSpanAdapter); ok {
|
||||
rowSpan = span
|
||||
}
|
||||
|
||||
width := adapter.GridColumnCount()
|
||||
height := adapter.GridRowCount()
|
||||
for column := 0; column < width; column++ {
|
||||
for row := 0; row < height; row++ {
|
||||
if view := adapter.GridCellContent(row, column, session); view != nil {
|
||||
view.setParentID(htmlID)
|
||||
|
||||
columnCount := 1
|
||||
if columnSpan != nil {
|
||||
columnCount = columnSpan.GridCellColumnSpan(row, column)
|
||||
}
|
||||
|
||||
if columnCount > 1 {
|
||||
view.Set(Column, Range{First: column, Last: column + columnCount - 1})
|
||||
} else {
|
||||
view.Set(Column, column)
|
||||
}
|
||||
|
||||
rowCount := 1
|
||||
if rowSpan != nil {
|
||||
rowCount = rowSpan.GridCellRowSpan(row, column)
|
||||
}
|
||||
|
||||
if rowCount > 1 {
|
||||
view.Set(Row, Range{First: row, Last: row + rowCount - 1})
|
||||
} else {
|
||||
view.Set(Row, row)
|
||||
}
|
||||
|
||||
if isDisabled {
|
||||
view.Set(Disabled, true)
|
||||
}
|
||||
|
||||
gridLayout.views = append(gridLayout.views, view)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if gridLayout.createGridContent() {
|
||||
if gridLayout.created {
|
||||
updateInnerHTML(htmlID, session)
|
||||
updateInnerHTML(gridLayout.htmlID(), gridLayout.session)
|
||||
}
|
||||
|
||||
gridLayout.propertyChangedEvent(Content)
|
||||
if listener, ok := gridLayout.changeListener[Content]; ok {
|
||||
listener(gridLayout, Content)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func gridCellSizes(properties Properties, tag string, session Session) []SizeUnit {
|
||||
func gridCellSizes(properties Properties, tag PropertyName, session Session) []SizeUnit {
|
||||
if value := properties.Get(tag); value != nil {
|
||||
switch value := value.(type) {
|
||||
case []SizeUnit:
|
||||
|
|
166
imageView.go
166
imageView.go
|
@ -20,7 +20,7 @@ const (
|
|||
//
|
||||
// Allowed listener formats:
|
||||
// `func()`.
|
||||
LoadedEvent = "loaded-event"
|
||||
LoadedEvent PropertyName = "loaded-event"
|
||||
|
||||
// ErrorEvent is the constant for "error-event" property tag.
|
||||
//
|
||||
|
@ -35,7 +35,7 @@ const (
|
|||
//
|
||||
// Allowed listener formats:
|
||||
// `func()`.
|
||||
ErrorEvent = "error-event"
|
||||
ErrorEvent PropertyName = "error-event"
|
||||
|
||||
// NoneFit - value of the "object-fit" property of an ImageView. The replaced content is not resized
|
||||
NoneFit = 0
|
||||
|
@ -88,7 +88,7 @@ func NewImageView(session Session, params Params) ImageView {
|
|||
}
|
||||
|
||||
func newImageView(session Session) View {
|
||||
return NewImageView(session, nil)
|
||||
return new(imageViewData)
|
||||
}
|
||||
|
||||
// Init initialize fields of imageView by default values
|
||||
|
@ -96,14 +96,13 @@ func (imageView *imageViewData) init(session Session) {
|
|||
imageView.viewData.init(session)
|
||||
imageView.tag = "ImageView"
|
||||
imageView.systemClass = "ruiImageView"
|
||||
imageView.normalize = normalizeImageViewTag
|
||||
imageView.set = imageViewSet
|
||||
imageView.changed = imageViewPropertyChanged
|
||||
}
|
||||
|
||||
func (imageView *imageViewData) String() string {
|
||||
return getViewString(imageView, nil)
|
||||
}
|
||||
|
||||
func (imageView *imageViewData) normalizeTag(tag string) string {
|
||||
tag = strings.ToLower(tag)
|
||||
func normalizeImageViewTag(tag PropertyName) PropertyName {
|
||||
tag = defaultNormalize(tag)
|
||||
switch tag {
|
||||
case "source":
|
||||
tag = Source
|
||||
|
@ -123,127 +122,58 @@ func (imageView *imageViewData) normalizeTag(tag string) string {
|
|||
return tag
|
||||
}
|
||||
|
||||
func (imageView *imageViewData) Remove(tag string) {
|
||||
imageView.remove(imageView.normalizeTag(tag))
|
||||
}
|
||||
func imageViewSet(view View, tag PropertyName, value any) []PropertyName {
|
||||
|
||||
func (imageView *imageViewData) remove(tag string) {
|
||||
imageView.viewData.remove(tag)
|
||||
if imageView.created {
|
||||
switch tag {
|
||||
case Source:
|
||||
imageView.session.updateProperty(imageView.htmlID(), "src", "")
|
||||
imageView.session.removeProperty(imageView.htmlID(), "srcset")
|
||||
|
||||
case AltText:
|
||||
updateInnerHTML(imageView.htmlID(), imageView.session)
|
||||
|
||||
case ImageVerticalAlign, ImageHorizontalAlign:
|
||||
updateCSSStyle(imageView.htmlID(), imageView.session)
|
||||
switch tag {
|
||||
case Source, SrcSet, AltText:
|
||||
if text, ok := value.(string); ok {
|
||||
return setStringPropertyValue(view, tag, text)
|
||||
}
|
||||
notCompatibleType(tag, value)
|
||||
return nil
|
||||
|
||||
case LoadedEvent, ErrorEvent:
|
||||
return setNoParamEventListener[ImageView](view, tag, value)
|
||||
}
|
||||
|
||||
return viewSet(view, tag, value)
|
||||
}
|
||||
|
||||
func (imageView *imageViewData) Set(tag string, value any) bool {
|
||||
return imageView.set(imageView.normalizeTag(tag), value)
|
||||
}
|
||||
|
||||
func (imageView *imageViewData) set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
imageView.remove(tag)
|
||||
return true
|
||||
}
|
||||
func imageViewPropertyChanged(view View, tag PropertyName) {
|
||||
session := view.Session()
|
||||
htmlID := view.htmlID()
|
||||
|
||||
switch tag {
|
||||
case Source:
|
||||
if text, ok := value.(string); ok {
|
||||
imageView.properties[tag] = text
|
||||
if imageView.created {
|
||||
src, srcset := imageView.src(text)
|
||||
imageView.session.updateProperty(imageView.htmlID(), "src", src)
|
||||
|
||||
if srcset != "" {
|
||||
imageView.session.updateProperty(imageView.htmlID(), "srcset", srcset)
|
||||
} else {
|
||||
imageView.session.removeProperty(imageView.htmlID(), "srcset")
|
||||
}
|
||||
}
|
||||
imageView.propertyChangedEvent(Source)
|
||||
return true
|
||||
src, srcset := imageViewSrc(view, GetImageViewSource(view))
|
||||
session.updateProperty(htmlID, "src", src)
|
||||
if srcset != "" {
|
||||
session.updateProperty(htmlID, "srcset", srcset)
|
||||
} else {
|
||||
session.removeProperty(htmlID, "srcset")
|
||||
}
|
||||
notCompatibleType(Source, value)
|
||||
|
||||
case SrcSet:
|
||||
if text, ok := value.(string); ok {
|
||||
if text == "" {
|
||||
delete(imageView.properties, tag)
|
||||
} else {
|
||||
imageView.properties[tag] = text
|
||||
}
|
||||
if imageView.created {
|
||||
_, srcset := imageView.src(text)
|
||||
if srcset != "" {
|
||||
imageView.session.updateProperty(imageView.htmlID(), "srcset", srcset)
|
||||
} else {
|
||||
imageView.session.removeProperty(imageView.htmlID(), "srcset")
|
||||
}
|
||||
}
|
||||
imageView.propertyChangedEvent(Source)
|
||||
return true
|
||||
_, srcset := imageViewSrc(view, GetImageViewSource(view))
|
||||
if srcset != "" {
|
||||
session.updateProperty(htmlID, "srcset", srcset)
|
||||
} else {
|
||||
session.removeProperty(htmlID, "srcset")
|
||||
}
|
||||
notCompatibleType(Source, value)
|
||||
|
||||
case AltText:
|
||||
if text, ok := value.(string); ok {
|
||||
imageView.properties[AltText] = text
|
||||
if imageView.created {
|
||||
updateInnerHTML(imageView.htmlID(), imageView.session)
|
||||
}
|
||||
imageView.propertyChangedEvent(Source)
|
||||
return true
|
||||
}
|
||||
notCompatibleType(tag, value)
|
||||
updateInnerHTML(htmlID, session)
|
||||
|
||||
case LoadedEvent, ErrorEvent:
|
||||
if listeners, ok := valueToNoParamListeners[ImageView](value); ok {
|
||||
if listeners == nil {
|
||||
delete(imageView.properties, tag)
|
||||
} else {
|
||||
imageView.properties[tag] = listeners
|
||||
}
|
||||
return true
|
||||
}
|
||||
case ImageVerticalAlign, ImageHorizontalAlign:
|
||||
updateCSSStyle(htmlID, session)
|
||||
|
||||
default:
|
||||
if imageView.viewData.set(tag, value) {
|
||||
if imageView.created {
|
||||
switch tag {
|
||||
case ImageVerticalAlign, ImageHorizontalAlign:
|
||||
updateCSSStyle(imageView.htmlID(), imageView.session)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
viewPropertyChanged(view, tag)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (imageView *imageViewData) Get(tag string) any {
|
||||
return imageView.viewData.get(imageView.normalizeTag(tag))
|
||||
}
|
||||
|
||||
func (imageView *imageViewData) imageListeners(tag string) []func(ImageView) {
|
||||
if value := imageView.getRaw(tag); value != nil {
|
||||
if listeners, ok := value.([]func(ImageView)); ok {
|
||||
return listeners
|
||||
}
|
||||
}
|
||||
return []func(ImageView){}
|
||||
}
|
||||
|
||||
func (imageView *imageViewData) srcSet(path string) string {
|
||||
if value := imageView.getRaw(SrcSet); value != nil {
|
||||
func imageViewSrcSet(view View, path string) string {
|
||||
if value := view.getRaw(SrcSet); value != nil {
|
||||
if text, ok := value.(string); ok {
|
||||
srcset := strings.Split(text, ",")
|
||||
buffer := allocStringBuilder()
|
||||
|
@ -286,9 +216,9 @@ func (imageView *imageViewData) htmlTag() string {
|
|||
return "img"
|
||||
}
|
||||
|
||||
func (imageView *imageViewData) src(src string) (string, string) {
|
||||
func imageViewSrc(view View, src string) (string, string) {
|
||||
if src != "" && src[0] == '@' {
|
||||
if image, ok := imageView.Session().ImageConstant(src[1:]); ok {
|
||||
if image, ok := view.Session().ImageConstant(src[1:]); ok {
|
||||
src = image
|
||||
} else {
|
||||
src = ""
|
||||
|
@ -296,7 +226,7 @@ func (imageView *imageViewData) src(src string) (string, string) {
|
|||
}
|
||||
|
||||
if src != "" {
|
||||
return src, imageView.srcSet(src)
|
||||
return src, imageViewSrcSet(view, src)
|
||||
}
|
||||
return "", ""
|
||||
}
|
||||
|
@ -306,7 +236,7 @@ func (imageView *imageViewData) htmlProperties(self View, buffer *strings.Builde
|
|||
imageView.viewData.htmlProperties(self, buffer)
|
||||
|
||||
if imageResource, ok := imageProperty(imageView, Source, imageView.Session()); ok && imageResource != "" {
|
||||
if src, srcset := imageView.src(imageResource); src != "" {
|
||||
if src, srcset := imageViewSrc(imageView, imageResource); src != "" {
|
||||
buffer.WriteString(` src="`)
|
||||
buffer.WriteString(src)
|
||||
buffer.WriteString(`"`)
|
||||
|
@ -326,7 +256,7 @@ func (imageView *imageViewData) htmlProperties(self View, buffer *strings.Builde
|
|||
|
||||
buffer.WriteString(` onload="imageLoaded(this, event)"`)
|
||||
|
||||
if len(imageView.imageListeners(ErrorEvent)) > 0 {
|
||||
if len(getNoParamEventListeners[ImageView](imageView, nil, ErrorEvent)) > 0 {
|
||||
buffer.WriteString(` onerror="imageError(this, event)"`)
|
||||
}
|
||||
}
|
||||
|
@ -366,10 +296,10 @@ func (imageView *imageViewData) cssStyle(self View, builder cssBuilder) {
|
|||
}
|
||||
}
|
||||
|
||||
func (imageView *imageViewData) handleCommand(self View, command string, data DataObject) bool {
|
||||
func (imageView *imageViewData) handleCommand(self View, command PropertyName, data DataObject) bool {
|
||||
switch command {
|
||||
case "imageViewError":
|
||||
for _, listener := range imageView.imageListeners(ErrorEvent) {
|
||||
for _, listener := range getNoParamEventListeners[ImageView](imageView, nil, ErrorEvent) {
|
||||
listener(imageView)
|
||||
}
|
||||
|
||||
|
@ -378,7 +308,7 @@ func (imageView *imageViewData) handleCommand(self View, command string, data Da
|
|||
imageView.naturalHeight = dataFloatProperty(data, "natural-height")
|
||||
imageView.currentSrc, _ = data.PropertyValue("current-src")
|
||||
|
||||
for _, listener := range imageView.imageListeners(LoadedEvent) {
|
||||
for _, listener := range getNoParamEventListeners[ImageView](imageView, nil, LoadedEvent) {
|
||||
listener(imageView)
|
||||
}
|
||||
|
||||
|
|
380
keyEvents.go
380
keyEvents.go
|
@ -20,7 +20,7 @@ const (
|
|||
// `func(view rui.View)`,
|
||||
// `func(event rui.KeyEvent)`,
|
||||
// `func()`.
|
||||
KeyDownEvent = "key-down-event"
|
||||
KeyDownEvent PropertyName = "key-down-event"
|
||||
|
||||
// KeyUpEvent is the constant for "key-up-event" property tag.
|
||||
//
|
||||
|
@ -38,7 +38,7 @@ const (
|
|||
// `func(view rui.View)`,
|
||||
// `func(event rui.KeyEvent)`,
|
||||
// `func()`.
|
||||
KeyUpEvent = "key-up-event"
|
||||
KeyUpEvent PropertyName = "key-up-event"
|
||||
)
|
||||
|
||||
// ControlKeyMask represent ORed state of keyboard's control keys like [AltKey], [CtrlKey], [ShiftKey] and [MetaKey]
|
||||
|
@ -429,342 +429,21 @@ func (event *KeyEvent) init(data DataObject) {
|
|||
event.MetaKey = getBool("metaKey")
|
||||
}
|
||||
|
||||
func valueToEventListeners[V View, E any](value any) ([]func(V, E), bool) {
|
||||
if value == nil {
|
||||
return nil, true
|
||||
/*
|
||||
func setKeyListener(properties Properties, tag PropertyName, value any) bool {
|
||||
if listeners, ok := valueToEventListeners[View, KeyEvent](value); ok {
|
||||
if len(listeners) == 0 {
|
||||
properties.setRaw(tag, nil)
|
||||
} else {
|
||||
properties.setRaw(tag, listeners)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case func(V, E):
|
||||
return []func(V, E){value}, true
|
||||
|
||||
case func(E):
|
||||
fn := func(_ V, event E) {
|
||||
value(event)
|
||||
}
|
||||
return []func(V, E){fn}, true
|
||||
|
||||
case func(V):
|
||||
fn := func(view V, _ E) {
|
||||
value(view)
|
||||
}
|
||||
return []func(V, E){fn}, true
|
||||
|
||||
case func():
|
||||
fn := func(V, E) {
|
||||
value()
|
||||
}
|
||||
return []func(V, E){fn}, true
|
||||
|
||||
case []func(V, E):
|
||||
if len(value) == 0 {
|
||||
return nil, true
|
||||
}
|
||||
for _, fn := range value {
|
||||
if fn == nil {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return value, true
|
||||
|
||||
case []func(E):
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V, E), count)
|
||||
for i, v := range value {
|
||||
if v == nil {
|
||||
return nil, false
|
||||
}
|
||||
listeners[i] = func(_ V, event E) {
|
||||
v(event)
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
|
||||
case []func(V):
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V, E), count)
|
||||
for i, v := range value {
|
||||
if v == nil {
|
||||
return nil, false
|
||||
}
|
||||
listeners[i] = func(view V, _ E) {
|
||||
v(view)
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
|
||||
case []func():
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V, E), count)
|
||||
for i, v := range value {
|
||||
if v == nil {
|
||||
return nil, false
|
||||
}
|
||||
listeners[i] = func(V, E) {
|
||||
v()
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
|
||||
case []any:
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V, E), count)
|
||||
for i, v := range value {
|
||||
if v == nil {
|
||||
return nil, false
|
||||
}
|
||||
switch v := v.(type) {
|
||||
case func(V, E):
|
||||
listeners[i] = v
|
||||
|
||||
case func(E):
|
||||
listeners[i] = func(_ V, event E) {
|
||||
v(event)
|
||||
}
|
||||
|
||||
case func(V):
|
||||
listeners[i] = func(view V, _ E) {
|
||||
v(view)
|
||||
}
|
||||
|
||||
case func():
|
||||
listeners[i] = func(V, E) {
|
||||
v()
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
|
||||
func valueToEventWithOldListeners[V View, E any](value any) ([]func(V, E, E), bool) {
|
||||
if value == nil {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case func(V, E, E):
|
||||
return []func(V, E, E){value}, true
|
||||
|
||||
case func(V, E):
|
||||
fn := func(v V, val, _ E) {
|
||||
value(v, val)
|
||||
}
|
||||
return []func(V, E, E){fn}, true
|
||||
|
||||
case func(E, E):
|
||||
fn := func(_ V, val, old E) {
|
||||
value(val, old)
|
||||
}
|
||||
return []func(V, E, E){fn}, true
|
||||
|
||||
case func(E):
|
||||
fn := func(_ V, val, _ E) {
|
||||
value(val)
|
||||
}
|
||||
return []func(V, E, E){fn}, true
|
||||
|
||||
case func(V):
|
||||
fn := func(v V, _, _ E) {
|
||||
value(v)
|
||||
}
|
||||
return []func(V, E, E){fn}, true
|
||||
|
||||
case func():
|
||||
fn := func(V, E, E) {
|
||||
value()
|
||||
}
|
||||
return []func(V, E, E){fn}, true
|
||||
|
||||
case []func(V, E, E):
|
||||
if len(value) == 0 {
|
||||
return nil, true
|
||||
}
|
||||
for _, fn := range value {
|
||||
if fn == nil {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return value, true
|
||||
|
||||
case []func(V, E):
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V, E, E), count)
|
||||
for i, fn := range value {
|
||||
if fn == nil {
|
||||
return nil, false
|
||||
}
|
||||
listeners[i] = func(view V, val, _ E) {
|
||||
fn(view, val)
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
|
||||
case []func(E):
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V, E, E), count)
|
||||
for i, fn := range value {
|
||||
if fn == nil {
|
||||
return nil, false
|
||||
}
|
||||
listeners[i] = func(_ V, val, _ E) {
|
||||
fn(val)
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
|
||||
case []func(E, E):
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V, E, E), count)
|
||||
for i, fn := range value {
|
||||
if fn == nil {
|
||||
return nil, false
|
||||
}
|
||||
listeners[i] = func(_ V, val, old E) {
|
||||
fn(val, old)
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
|
||||
case []func(V):
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V, E, E), count)
|
||||
for i, fn := range value {
|
||||
if fn == nil {
|
||||
return nil, false
|
||||
}
|
||||
listeners[i] = func(view V, _, _ E) {
|
||||
fn(view)
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
|
||||
case []func():
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V, E, E), count)
|
||||
for i, fn := range value {
|
||||
if fn == nil {
|
||||
return nil, false
|
||||
}
|
||||
listeners[i] = func(V, E, E) {
|
||||
fn()
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
|
||||
case []any:
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V, E, E), count)
|
||||
for i, v := range value {
|
||||
if v == nil {
|
||||
return nil, false
|
||||
}
|
||||
switch fn := v.(type) {
|
||||
case func(V, E, E):
|
||||
listeners[i] = fn
|
||||
|
||||
case func(V, E):
|
||||
listeners[i] = func(view V, val, _ E) {
|
||||
fn(view, val)
|
||||
}
|
||||
|
||||
case func(E, E):
|
||||
listeners[i] = func(_ V, val, old E) {
|
||||
fn(val, old)
|
||||
}
|
||||
|
||||
case func(E):
|
||||
listeners[i] = func(_ V, val, _ E) {
|
||||
fn(val)
|
||||
}
|
||||
|
||||
case func(V):
|
||||
listeners[i] = func(view V, _, _ E) {
|
||||
fn(view)
|
||||
}
|
||||
|
||||
case func():
|
||||
listeners[i] = func(V, E, E) {
|
||||
fn()
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (view *viewData) setKeyListener(tag string, value any) bool {
|
||||
listeners, ok := valueToEventListeners[View, KeyEvent](value)
|
||||
if !ok {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
|
||||
if listeners == nil {
|
||||
view.removeKeyListener(tag)
|
||||
} else {
|
||||
switch tag {
|
||||
case KeyDownEvent:
|
||||
view.properties[tag] = listeners
|
||||
if view.created {
|
||||
view.session.updateProperty(view.htmlID(), "onkeydown", "keyDownEvent(this, event)")
|
||||
}
|
||||
|
||||
case KeyUpEvent:
|
||||
view.properties[tag] = listeners
|
||||
if view.created {
|
||||
view.session.updateProperty(view.htmlID(), "onkeyup", "keyUpEvent(this, event)")
|
||||
}
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (view *viewData) removeKeyListener(tag string) {
|
||||
func (view *viewData) removeKeyListener(tag PropertyName) {
|
||||
delete(view.properties, tag)
|
||||
if view.created {
|
||||
switch tag {
|
||||
|
@ -778,34 +457,7 @@ func (view *viewData) removeKeyListener(tag string) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getEventWithOldListeners[V View, E any](view View, subviewID []string, tag string) []func(V, E, E) {
|
||||
if len(subviewID) > 0 && subviewID[0] != "" {
|
||||
view = ViewByID(view, subviewID[0])
|
||||
}
|
||||
if view != nil {
|
||||
if value := view.Get(tag); value != nil {
|
||||
if result, ok := value.([]func(V, E, E)); ok {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
return []func(V, E, E){}
|
||||
}
|
||||
|
||||
func getEventListeners[V View, E any](view View, subviewID []string, tag string) []func(V, E) {
|
||||
if len(subviewID) > 0 && subviewID[0] != "" {
|
||||
view = ViewByID(view, subviewID[0])
|
||||
}
|
||||
if view != nil {
|
||||
if value := view.Get(tag); value != nil {
|
||||
if result, ok := value.([]func(V, E)); ok {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
return []func(V, E){}
|
||||
}
|
||||
*/
|
||||
|
||||
func keyEventsHtml(view View, buffer *strings.Builder) {
|
||||
if len(getEventListeners[View, KeyEvent](view, nil, KeyDownEvent)) > 0 {
|
||||
|
@ -821,7 +473,7 @@ func keyEventsHtml(view View, buffer *strings.Builder) {
|
|||
}
|
||||
}
|
||||
|
||||
func handleKeyEvents(view View, tag string, data DataObject) {
|
||||
func handleKeyEvents(view View, tag PropertyName, data DataObject) {
|
||||
var event KeyEvent
|
||||
event.init(data)
|
||||
listeners := getEventListeners[View, KeyEvent](view, nil, tag)
|
||||
|
|
145
listLayout.go
145
listLayout.go
|
@ -37,6 +37,7 @@ type ListLayout interface {
|
|||
// UpdateContent updates child Views if the "content" property value is set to ListAdapter,
|
||||
// otherwise does nothing
|
||||
UpdateContent()
|
||||
setAdapter(ListAdapter)
|
||||
}
|
||||
|
||||
type listLayoutData struct {
|
||||
|
@ -53,7 +54,8 @@ func NewListLayout(session Session, params Params) ListLayout {
|
|||
}
|
||||
|
||||
func newListLayout(session Session) View {
|
||||
return NewListLayout(session, nil)
|
||||
//return NewListLayout(session, nil)
|
||||
return new(listLayoutData)
|
||||
}
|
||||
|
||||
// Init initialize fields of ViewsAlignContainer by default values
|
||||
|
@ -61,14 +63,16 @@ func (listLayout *listLayoutData) init(session Session) {
|
|||
listLayout.viewsContainerData.init(session)
|
||||
listLayout.tag = "ListLayout"
|
||||
listLayout.systemClass = "ruiListLayout"
|
||||
listLayout.normalize = normalizeListLayoutTag
|
||||
listLayout.getFunc = listLayout.get
|
||||
listLayout.set = listLayout.setFunc
|
||||
listLayout.remove = listLayout.removeFunc
|
||||
listLayout.changed = listLayoutPropertyChanged
|
||||
|
||||
}
|
||||
|
||||
func (listLayout *listLayoutData) String() string {
|
||||
return getViewString(listLayout, nil)
|
||||
}
|
||||
|
||||
func (listLayout *listLayoutData) normalizeTag(tag string) string {
|
||||
tag = strings.ToLower(tag)
|
||||
func normalizeListLayoutTag(tag PropertyName) PropertyName {
|
||||
tag = defaultNormalize(tag)
|
||||
switch tag {
|
||||
case "wrap":
|
||||
tag = ListWrap
|
||||
|
@ -82,79 +86,78 @@ func (listLayout *listLayoutData) normalizeTag(tag string) string {
|
|||
return tag
|
||||
}
|
||||
|
||||
func (listLayout *listLayoutData) Get(tag string) any {
|
||||
return listLayout.get(listLayout.normalizeTag(tag))
|
||||
}
|
||||
|
||||
func (listLayout *listLayoutData) get(tag string) any {
|
||||
if tag == Gap {
|
||||
func (listLayout *listLayoutData) get(self View, tag PropertyName) any {
|
||||
switch tag {
|
||||
case Gap:
|
||||
if rowGap := GetListRowGap(listLayout); rowGap.Equal(GetListColumnGap(listLayout)) {
|
||||
return rowGap
|
||||
}
|
||||
return AutoSize()
|
||||
}
|
||||
|
||||
return listLayout.viewsContainerData.get(tag)
|
||||
}
|
||||
|
||||
func (listLayout *listLayoutData) Remove(tag string) {
|
||||
listLayout.remove(listLayout.normalizeTag(tag))
|
||||
}
|
||||
|
||||
func (listLayout *listLayoutData) remove(tag string) {
|
||||
switch tag {
|
||||
case Gap:
|
||||
listLayout.remove(ListRowGap)
|
||||
listLayout.remove(ListColumnGap)
|
||||
return
|
||||
|
||||
case Content:
|
||||
listLayout.adapter = nil
|
||||
}
|
||||
|
||||
listLayout.viewsContainerData.remove(tag)
|
||||
if listLayout.created {
|
||||
switch tag {
|
||||
case Orientation, ListWrap, HorizontalAlign, VerticalAlign:
|
||||
updateCSSStyle(listLayout.htmlID(), listLayout.session)
|
||||
if listLayout.adapter != nil {
|
||||
return listLayout.adapter
|
||||
}
|
||||
}
|
||||
|
||||
return listLayout.viewsContainerData.get(listLayout, tag)
|
||||
}
|
||||
|
||||
func (listLayout *listLayoutData) Set(tag string, value any) bool {
|
||||
return listLayout.set(listLayout.normalizeTag(tag), value)
|
||||
}
|
||||
|
||||
func (listLayout *listLayoutData) set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
listLayout.remove(tag)
|
||||
return true
|
||||
}
|
||||
|
||||
func (listLayout *listLayoutData) removeFunc(self View, tag PropertyName) []PropertyName {
|
||||
switch tag {
|
||||
case Gap:
|
||||
return listLayout.set(ListRowGap, value) && listLayout.set(ListColumnGap, value)
|
||||
result := []PropertyName{}
|
||||
for _, tag := range []PropertyName{ListRowGap, ListColumnGap} {
|
||||
if listLayout.getRaw(tag) != nil {
|
||||
listLayout.setRaw(tag, nil)
|
||||
result = append(result, tag)
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
||||
case Content:
|
||||
listLayout.viewsContainerData.removeFunc(listLayout, Content)
|
||||
listLayout.adapter = nil
|
||||
return []PropertyName{Content}
|
||||
}
|
||||
|
||||
return listLayout.viewsContainerData.removeFunc(listLayout, tag)
|
||||
}
|
||||
|
||||
func (listLayout *listLayoutData) setFunc(self View, tag PropertyName, value any) []PropertyName {
|
||||
switch tag {
|
||||
case Gap:
|
||||
result := listLayout.setFunc(listLayout, ListRowGap, value)
|
||||
if result != nil {
|
||||
if gap := listLayout.getRaw(ListRowGap); gap != nil {
|
||||
listLayout.setRaw(ListColumnGap, gap)
|
||||
result = append(result, ListColumnGap)
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
||||
case Content:
|
||||
if adapter, ok := value.(ListAdapter); ok {
|
||||
listLayout.adapter = adapter
|
||||
listLayout.UpdateContent()
|
||||
// TODO
|
||||
return true
|
||||
listLayout.createContent()
|
||||
} else if listLayout.setContent(value) {
|
||||
listLayout.adapter = nil
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
listLayout.adapter = nil
|
||||
return []PropertyName{Content}
|
||||
}
|
||||
return listLayout.viewsContainerData.setFunc(listLayout, tag, value)
|
||||
}
|
||||
|
||||
if listLayout.viewsContainerData.set(tag, value) {
|
||||
if listLayout.created {
|
||||
switch tag {
|
||||
case Orientation, ListWrap, HorizontalAlign, VerticalAlign:
|
||||
updateCSSStyle(listLayout.htmlID(), listLayout.session)
|
||||
}
|
||||
}
|
||||
return true
|
||||
func listLayoutPropertyChanged(view View, tag PropertyName) {
|
||||
switch tag {
|
||||
case Orientation, ListWrap, HorizontalAlign, VerticalAlign:
|
||||
updateCSSStyle(view.htmlID(), view.Session())
|
||||
|
||||
default:
|
||||
viewsContainerPropertyChanged(view, tag)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (listLayout *listLayoutData) htmlSubviews(self View, buffer *strings.Builder) {
|
||||
|
@ -166,7 +169,14 @@ func (listLayout *listLayoutData) htmlSubviews(self View, buffer *strings.Builde
|
|||
}
|
||||
}
|
||||
|
||||
func (listLayout *listLayoutData) UpdateContent() {
|
||||
func (listLayout *listLayoutData) setAdapter(adapter ListAdapter) {
|
||||
listLayout.adapter = adapter
|
||||
if adapter != nil {
|
||||
listLayout.createContent()
|
||||
}
|
||||
}
|
||||
|
||||
func (listLayout *listLayoutData) createContent() bool {
|
||||
if adapter := listLayout.adapter; adapter != nil {
|
||||
listLayout.views = []View{}
|
||||
|
||||
|
@ -185,11 +195,20 @@ func (listLayout *listLayoutData) UpdateContent() {
|
|||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (listLayout *listLayoutData) UpdateContent() {
|
||||
if listLayout.createContent() {
|
||||
if listLayout.created {
|
||||
updateInnerHTML(htmlID, session)
|
||||
updateInnerHTML(listLayout.htmlID(), listLayout.session)
|
||||
}
|
||||
|
||||
listLayout.propertyChangedEvent(Content)
|
||||
if listener, ok := listLayout.changeListener[Content]; ok {
|
||||
listener(listLayout, Content)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
728
listView.go
728
listView.go
File diff suppressed because it is too large
Load Diff
389
mediaPlayer.go
389
mediaPlayer.go
|
@ -14,7 +14,7 @@ const (
|
|||
// Used by `AudioPlayer`, `VideoPlayer`.
|
||||
//
|
||||
// Usage in `AudioPlayer`:
|
||||
// Controls whether the browser need to provide controls to allow user to control audio playback, volume, seeking and
|
||||
// Controls whether the browser need to provide controls to allow user to control audio playback, volume, seeking and
|
||||
// pause/resume playback. Default value is `false`.
|
||||
//
|
||||
// Supported types: `bool`, `int`, `string`.
|
||||
|
@ -24,7 +24,7 @@ const (
|
|||
// `false` or `0` or "false", "no", "off", "0" - No controls will be visible to the end user.
|
||||
//
|
||||
// Usage in `VideoPlayer`:
|
||||
// Whether the browser need to provide controls to allow user to control video playback, volume, seeking and pause/resume
|
||||
// Whether the browser need to provide controls to allow user to control video playback, volume, seeking and pause/resume
|
||||
// playback. Default value is `false`.
|
||||
//
|
||||
// Supported types: `bool`, `int`, `string`.
|
||||
|
@ -32,7 +32,7 @@ const (
|
|||
// Values:
|
||||
// `true` or `1` or "true", "yes", "on", "1" - The browser will offer controls to allow the user to control video playback, volume, seeking and pause/resume playback.
|
||||
// `false` or `0` or "false", "no", "off", "0" - No controls will be visible to the end user.
|
||||
Controls = "controls"
|
||||
Controls PropertyName = "controls"
|
||||
|
||||
// Loop is the constant for "loop" property tag.
|
||||
//
|
||||
|
@ -55,7 +55,7 @@ const (
|
|||
// Values:
|
||||
// `true` or `1` or "true", "yes", "on", "1" - The video player will automatically seek back to the start upon reaching the end of the video.
|
||||
// `false` or `0` or "false", "no", "off", "0" - Video player will stop playing when the end of the media file has been reached.
|
||||
Loop = "loop"
|
||||
Loop PropertyName = "loop"
|
||||
|
||||
// Muted is the constant for "muted" property tag.
|
||||
//
|
||||
|
@ -78,14 +78,14 @@ const (
|
|||
// Values:
|
||||
// `true` or `1` or "true", "yes", "on", "1" - Video will be muted.
|
||||
// `false` or `0` or "false", "no", "off", "0" - Video playing normally.
|
||||
Muted = "muted"
|
||||
Muted PropertyName = "muted"
|
||||
|
||||
// Preload is the constant for "preload" property tag.
|
||||
//
|
||||
// Used by `AudioPlayer`, `VideoPlayer`.
|
||||
//
|
||||
// Usage in `AudioPlayer`:
|
||||
// Property is intended to provide a hint to the browser about what the author thinks will lead to the best user
|
||||
// Property is intended to provide a hint to the browser about what the author thinks will lead to the best user
|
||||
// experience. Default value is different for each browser.
|
||||
//
|
||||
// Supported types: `int`, `string`.
|
||||
|
@ -96,7 +96,7 @@ const (
|
|||
// `2`(`PreloadAuto`) or "auto" - The entire media file can be downloaded even if the user doesn't have to use it.
|
||||
//
|
||||
// Usage in `VideoPlayer`:
|
||||
// Property is intended to provide a hint to the browser about what the author thinks will lead to the best user
|
||||
// Property is intended to provide a hint to the browser about what the author thinks will lead to the best user
|
||||
// experience. Default value is different for each browser.
|
||||
//
|
||||
// Supported types: `int`, `string`.
|
||||
|
@ -105,7 +105,7 @@ const (
|
|||
// `0`(`PreloadNone`) or "none" - Media file must not be pre-loaded.
|
||||
// `1`(`PreloadMetadata`) or "metadata" - Only metadata is preloaded.
|
||||
// `2`(`PreloadAuto`) or "auto" - The entire media file can be downloaded even if the user doesn't have to use it.
|
||||
Preload = "preload"
|
||||
Preload PropertyName = "preload"
|
||||
|
||||
// AbortEvent is the constant for "abort-event" property tag.
|
||||
//
|
||||
|
@ -134,14 +134,14 @@ const (
|
|||
//
|
||||
// Allowed listener formats:
|
||||
// `func()`.
|
||||
AbortEvent = "abort-event"
|
||||
AbortEvent PropertyName = "abort-event"
|
||||
|
||||
// CanPlayEvent is the constant for "can-play-event" property tag.
|
||||
//
|
||||
// Used by `AudioPlayer`, `VideoPlayer`.
|
||||
//
|
||||
// Usage in `AudioPlayer`:
|
||||
// Occur when the browser can play the media, but estimates that not enough data has been loaded to play the media up to
|
||||
// Occur when the browser can play the media, but estimates that not enough data has been loaded to play the media up to
|
||||
// its end without having to stop for further buffering of content.
|
||||
//
|
||||
// General listener format:
|
||||
|
@ -154,7 +154,7 @@ const (
|
|||
// `func()`.
|
||||
//
|
||||
// Usage in `VideoPlayer`:
|
||||
// Occur when the browser can play the media, but estimates that not enough data has been loaded to play the media up to
|
||||
// Occur when the browser can play the media, but estimates that not enough data has been loaded to play the media up to
|
||||
// its end without having to stop for further buffering of content.
|
||||
//
|
||||
// General listener format:
|
||||
|
@ -165,7 +165,7 @@ const (
|
|||
//
|
||||
// Allowed listener formats:
|
||||
// `func()`.
|
||||
CanPlayEvent = "can-play-event"
|
||||
CanPlayEvent PropertyName = "can-play-event"
|
||||
|
||||
// CanPlayThroughEvent is the constant for "can-play-through-event" property tag.
|
||||
//
|
||||
|
@ -194,7 +194,7 @@ const (
|
|||
//
|
||||
// Allowed listener formats:
|
||||
// `func()`.
|
||||
CanPlayThroughEvent = "can-play-through-event"
|
||||
CanPlayThroughEvent PropertyName = "can-play-through-event"
|
||||
|
||||
// CompleteEvent is the constant for "complete-event" property tag.
|
||||
//
|
||||
|
@ -223,7 +223,7 @@ const (
|
|||
//
|
||||
// Allowed listener formats:
|
||||
// `func()`.
|
||||
CompleteEvent = "complete-event"
|
||||
CompleteEvent PropertyName = "complete-event"
|
||||
|
||||
// DurationChangedEvent is the constant for "duration-changed-event" property tag.
|
||||
//
|
||||
|
@ -258,14 +258,14 @@ const (
|
|||
// `func(player rui.MediaPlayer)`,
|
||||
// `func(duration float64)`,
|
||||
// `func()`.
|
||||
DurationChangedEvent = "duration-changed-event"
|
||||
DurationChangedEvent PropertyName = "duration-changed-event"
|
||||
|
||||
// EmptiedEvent is the constant for "emptied-event" property tag.
|
||||
//
|
||||
// Used by `AudioPlayer`, `VideoPlayer`.
|
||||
//
|
||||
// Usage in `AudioPlayer`:
|
||||
// Occur when the media has become empty; for example, this event is sent if the media has already been loaded(or
|
||||
// Occur when the media has become empty; for example, this event is sent if the media has already been loaded(or
|
||||
// partially loaded), and the HTMLMediaElement.load method is called to reload it.
|
||||
//
|
||||
// General listener format:
|
||||
|
@ -278,7 +278,7 @@ const (
|
|||
// `func()`.
|
||||
//
|
||||
// Usage in `VideoPlayer`:
|
||||
// Occur when the media has become empty; for example, this event is sent if the media has already been loaded(or
|
||||
// Occur when the media has become empty; for example, this event is sent if the media has already been loaded(or
|
||||
// partially loaded), and the HTMLMediaElement.load method is called to reload it.
|
||||
//
|
||||
// General listener format:
|
||||
|
@ -289,7 +289,7 @@ const (
|
|||
//
|
||||
// Allowed listener formats:
|
||||
// `func()`.
|
||||
EmptiedEvent = "emptied-event"
|
||||
EmptiedEvent PropertyName = "emptied-event"
|
||||
|
||||
// EndedEvent is the constant for "ended-event" property tag.
|
||||
//
|
||||
|
@ -318,7 +318,7 @@ const (
|
|||
//
|
||||
// Allowed listener formats:
|
||||
// `func()`.
|
||||
EndedEvent = "ended-event"
|
||||
EndedEvent PropertyName = "ended-event"
|
||||
|
||||
// LoadedDataEvent is the constant for "loaded-data-event" property tag.
|
||||
//
|
||||
|
@ -347,7 +347,7 @@ const (
|
|||
//
|
||||
// Allowed listener formats:
|
||||
// `func()`.
|
||||
LoadedDataEvent = "loaded-data-event"
|
||||
LoadedDataEvent PropertyName = "loaded-data-event"
|
||||
|
||||
// LoadedMetadataEvent is the constant for "loaded-metadata-event" property tag.
|
||||
//
|
||||
|
@ -376,7 +376,7 @@ const (
|
|||
//
|
||||
// Allowed listener formats:
|
||||
// `func()`.
|
||||
LoadedMetadataEvent = "loaded-metadata-event"
|
||||
LoadedMetadataEvent PropertyName = "loaded-metadata-event"
|
||||
|
||||
// LoadStartEvent is the constant for "load-start-event" property tag.
|
||||
//
|
||||
|
@ -405,7 +405,7 @@ const (
|
|||
//
|
||||
// Allowed listener formats:
|
||||
// `func()`.
|
||||
LoadStartEvent = "load-start-event"
|
||||
LoadStartEvent PropertyName = "load-start-event"
|
||||
|
||||
// PauseEvent is the constant for "pause-event" property tag.
|
||||
//
|
||||
|
@ -434,7 +434,7 @@ const (
|
|||
//
|
||||
// Allowed listener formats:
|
||||
// `func()`.
|
||||
PauseEvent = "pause-event"
|
||||
PauseEvent PropertyName = "pause-event"
|
||||
|
||||
// PlayEvent is the constant for "play-event" property tag.
|
||||
//
|
||||
|
@ -463,7 +463,7 @@ const (
|
|||
//
|
||||
// Allowed listener formats:
|
||||
// `func()`.
|
||||
PlayEvent = "play-event"
|
||||
PlayEvent PropertyName = "play-event"
|
||||
|
||||
// PlayingEvent is the constant for "playing-event" property tag.
|
||||
//
|
||||
|
@ -492,7 +492,7 @@ const (
|
|||
//
|
||||
// Allowed listener formats:
|
||||
// `func()`.
|
||||
PlayingEvent = "playing-event"
|
||||
PlayingEvent PropertyName = "playing-event"
|
||||
|
||||
// ProgressEvent is the constant for "progress-event" property tag.
|
||||
//
|
||||
|
@ -521,7 +521,7 @@ const (
|
|||
//
|
||||
// Allowed listener formats:
|
||||
// `func()`.
|
||||
ProgressEvent = "progress-event"
|
||||
ProgressEvent PropertyName = "progress-event"
|
||||
|
||||
// RateChangedEvent is the constant for "rate-changed-event" property tag.
|
||||
//
|
||||
|
@ -556,7 +556,7 @@ const (
|
|||
// `func(player rui.MediaPlayer)`,
|
||||
// `func(rate float64)`,
|
||||
// `func()`.
|
||||
RateChangedEvent = "rate-changed-event"
|
||||
RateChangedEvent PropertyName = "rate-changed-event"
|
||||
|
||||
// SeekedEvent is the constant for "seeked-event" property tag.
|
||||
//
|
||||
|
@ -585,7 +585,7 @@ const (
|
|||
//
|
||||
// Allowed listener formats:
|
||||
// `func()`.
|
||||
SeekedEvent = "seeked-event"
|
||||
SeekedEvent PropertyName = "seeked-event"
|
||||
|
||||
// SeekingEvent is the constant for "seeking-event" property tag.
|
||||
//
|
||||
|
@ -614,7 +614,7 @@ const (
|
|||
//
|
||||
// Allowed listener formats:
|
||||
// `func()`.
|
||||
SeekingEvent = "seeking-event"
|
||||
SeekingEvent PropertyName = "seeking-event"
|
||||
|
||||
// StalledEvent is the constant for "stalled-event" property tag.
|
||||
//
|
||||
|
@ -643,7 +643,7 @@ const (
|
|||
//
|
||||
// Allowed listener formats:
|
||||
// `func()`.
|
||||
StalledEvent = "stalled-event"
|
||||
StalledEvent PropertyName = "stalled-event"
|
||||
|
||||
// SuspendEvent is the constant for "suspend-event" property tag.
|
||||
//
|
||||
|
@ -672,7 +672,7 @@ const (
|
|||
//
|
||||
// Allowed listener formats:
|
||||
// `func()`.
|
||||
SuspendEvent = "suspend-event"
|
||||
SuspendEvent PropertyName = "suspend-event"
|
||||
|
||||
// TimeUpdateEvent is the constant for "time-update-event" property tag.
|
||||
//
|
||||
|
@ -707,7 +707,7 @@ const (
|
|||
// `func(player rui.MediaPlayer)`,
|
||||
// `func(time float64)`,
|
||||
// `func()`.
|
||||
TimeUpdateEvent = "time-update-event"
|
||||
TimeUpdateEvent PropertyName = "time-update-event"
|
||||
|
||||
// VolumeChangedEvent is the constant for "volume-changed-event" property tag.
|
||||
//
|
||||
|
@ -742,7 +742,7 @@ const (
|
|||
// `func(player rui.MediaPlayer)`,
|
||||
// `func(volume float64)`,
|
||||
// `func()`.
|
||||
VolumeChangedEvent = "volume-changed-event"
|
||||
VolumeChangedEvent PropertyName = "volume-changed-event"
|
||||
|
||||
// WaitingEvent is the constant for "waiting-event" property tag.
|
||||
//
|
||||
|
@ -771,7 +771,7 @@ const (
|
|||
//
|
||||
// Allowed listener formats:
|
||||
// `func()`.
|
||||
WaitingEvent = "waiting-event"
|
||||
WaitingEvent PropertyName = "waiting-event"
|
||||
|
||||
// PlayerErrorEvent is the constant for "player-error-event" property tag.
|
||||
//
|
||||
|
@ -820,7 +820,7 @@ const (
|
|||
// `func(code int, message string)`,
|
||||
// `func(player rui.MediaPlayer)`,
|
||||
// `func()`.
|
||||
PlayerErrorEvent = "player-error-event"
|
||||
PlayerErrorEvent PropertyName = "player-error-event"
|
||||
|
||||
// PreloadNone - value of the view "preload" property: indicates that the audio/video should not be preloaded.
|
||||
PreloadNone = 0
|
||||
|
@ -907,119 +907,70 @@ type MediaSource struct {
|
|||
func (player *mediaPlayerData) init(session Session) {
|
||||
player.viewData.init(session)
|
||||
player.tag = "MediaPlayer"
|
||||
}
|
||||
|
||||
func (player *mediaPlayerData) String() string {
|
||||
return getViewString(player, nil)
|
||||
player.set = mediaPlayerSet
|
||||
player.changed = mediaPlayerPropertyChanged
|
||||
}
|
||||
|
||||
func (player *mediaPlayerData) Focusable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (player *mediaPlayerData) Remove(tag string) {
|
||||
player.remove(strings.ToLower(tag))
|
||||
}
|
||||
|
||||
func (player *mediaPlayerData) remove(tag string) {
|
||||
player.viewData.remove(tag)
|
||||
player.propertyChanged(tag)
|
||||
}
|
||||
|
||||
func (player *mediaPlayerData) Set(tag string, value any) bool {
|
||||
return player.set(strings.ToLower(tag), value)
|
||||
}
|
||||
|
||||
func (player *mediaPlayerData) set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
player.remove(tag)
|
||||
return true
|
||||
}
|
||||
|
||||
func mediaPlayerSet(view View, tag PropertyName, value any) []PropertyName {
|
||||
switch tag {
|
||||
case Controls, Loop, Muted, Preload:
|
||||
if player.viewData.set(tag, value) {
|
||||
player.propertyChanged(tag)
|
||||
return true
|
||||
}
|
||||
|
||||
case AbortEvent, CanPlayEvent, CanPlayThroughEvent, CompleteEvent, EmptiedEvent, LoadStartEvent,
|
||||
EndedEvent, LoadedDataEvent, LoadedMetadataEvent, PauseEvent, PlayEvent, PlayingEvent,
|
||||
ProgressEvent, SeekedEvent, SeekingEvent, StalledEvent, SuspendEvent, WaitingEvent:
|
||||
if listeners, ok := valueToNoParamListeners[MediaPlayer](value); ok {
|
||||
if listeners == nil {
|
||||
delete(player.properties, tag)
|
||||
} else {
|
||||
player.properties[tag] = listeners
|
||||
}
|
||||
player.propertyChanged(tag)
|
||||
player.propertyChangedEvent(tag)
|
||||
return true
|
||||
}
|
||||
notCompatibleType(tag, value)
|
||||
|
||||
return setNoParamEventListener[MediaPlayer](view, tag, value)
|
||||
|
||||
case DurationChangedEvent, RateChangedEvent, TimeUpdateEvent, VolumeChangedEvent:
|
||||
if listeners, ok := valueToEventListeners[MediaPlayer, float64](value); ok {
|
||||
if listeners == nil {
|
||||
delete(player.properties, tag)
|
||||
} else {
|
||||
player.properties[tag] = listeners
|
||||
}
|
||||
player.propertyChanged(tag)
|
||||
player.propertyChangedEvent(tag)
|
||||
return true
|
||||
}
|
||||
notCompatibleType(tag, value)
|
||||
|
||||
return setViewEventListener[MediaPlayer, float64](view, tag, value)
|
||||
|
||||
case PlayerErrorEvent:
|
||||
if listeners, ok := valueToPlayerErrorListeners(value); ok {
|
||||
if listeners == nil {
|
||||
delete(player.properties, tag)
|
||||
if len(listeners) > 0 {
|
||||
view.setRaw(tag, listeners)
|
||||
} else if view.getRaw(tag) != nil {
|
||||
view.setRaw(tag, nil)
|
||||
} else {
|
||||
player.properties[tag] = listeners
|
||||
return []PropertyName{}
|
||||
}
|
||||
player.propertyChanged(tag)
|
||||
player.propertyChangedEvent(tag)
|
||||
return true
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
notCompatibleType(tag, value)
|
||||
return nil
|
||||
|
||||
case Source:
|
||||
if player.setSource(value) {
|
||||
player.propertyChanged(tag)
|
||||
player.propertyChangedEvent(tag)
|
||||
return true
|
||||
}
|
||||
|
||||
default:
|
||||
return player.viewData.set(tag, value)
|
||||
return setMediaPlayerSource(view, value)
|
||||
}
|
||||
|
||||
return false
|
||||
return viewSet(view, tag, value)
|
||||
}
|
||||
|
||||
func (player *mediaPlayerData) setSource(value any) bool {
|
||||
func setMediaPlayerSource(properties Properties, value any) []PropertyName {
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
src := MediaSource{Url: value, MimeType: ""}
|
||||
player.properties[Source] = []MediaSource{src}
|
||||
properties.setRaw(Source, []MediaSource{src})
|
||||
|
||||
case MediaSource:
|
||||
player.properties[Source] = []MediaSource{value}
|
||||
properties.setRaw(Source, []MediaSource{value})
|
||||
|
||||
case []MediaSource:
|
||||
player.properties[Source] = value
|
||||
properties.setRaw(Source, value)
|
||||
|
||||
case DataObject:
|
||||
url, ok := value.PropertyValue("src")
|
||||
if !ok || url == "" {
|
||||
invalidPropertyValue(Source, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
mimeType, _ := value.PropertyValue("mime-type")
|
||||
src := MediaSource{Url: url, MimeType: mimeType}
|
||||
player.properties[Source] = []MediaSource{src}
|
||||
properties.setRaw(Source, []MediaSource{src})
|
||||
|
||||
case []DataValue:
|
||||
src := []MediaSource{}
|
||||
|
@ -1031,7 +982,7 @@ func (player *mediaPlayerData) setSource(value any) bool {
|
|||
src = append(src, MediaSource{Url: url, MimeType: mimeType})
|
||||
} else {
|
||||
invalidPropertyValue(Source, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
src = append(src, MediaSource{Url: val.Value(), MimeType: ""})
|
||||
|
@ -1040,16 +991,16 @@ func (player *mediaPlayerData) setSource(value any) bool {
|
|||
|
||||
if len(src) == 0 {
|
||||
invalidPropertyValue(Source, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
player.properties[Source] = src
|
||||
properties.setRaw(Source, src)
|
||||
|
||||
default:
|
||||
notCompatibleType(Source, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
return true
|
||||
return []PropertyName{Source}
|
||||
}
|
||||
|
||||
func valueToPlayerErrorListeners(value any) ([]func(MediaPlayer, int, string), bool) {
|
||||
|
@ -1177,109 +1128,106 @@ func valueToPlayerErrorListeners(value any) ([]func(MediaPlayer, int, string), b
|
|||
return nil, false
|
||||
}
|
||||
|
||||
func playerEvents() []struct{ tag, cssTag string } {
|
||||
return []struct{ tag, cssTag string }{
|
||||
{AbortEvent, "onabort"},
|
||||
{CanPlayEvent, "oncanplay"},
|
||||
{CanPlayThroughEvent, "oncanplaythrough"},
|
||||
{CompleteEvent, "oncomplete"},
|
||||
{EmptiedEvent, "onemptied"},
|
||||
{EndedEvent, "ended"},
|
||||
{LoadedDataEvent, "onloadeddata"},
|
||||
{LoadedMetadataEvent, "onloadedmetadata"},
|
||||
{LoadStartEvent, "onloadstart"},
|
||||
{PauseEvent, "onpause"},
|
||||
{PlayEvent, "onplay"},
|
||||
{PlayingEvent, "onplaying"},
|
||||
{ProgressEvent, "onprogress"},
|
||||
{SeekedEvent, "onseeked"},
|
||||
{SeekingEvent, "onseeking"},
|
||||
{StalledEvent, "onstalled"},
|
||||
{SuspendEvent, "onsuspend"},
|
||||
{WaitingEvent, "onwaiting"},
|
||||
func mediaPlayerEvents() map[PropertyName]string {
|
||||
return map[PropertyName]string{
|
||||
AbortEvent: "onabort",
|
||||
CanPlayEvent: "oncanplay",
|
||||
CanPlayThroughEvent: "oncanplaythrough",
|
||||
CompleteEvent: "oncomplete",
|
||||
EmptiedEvent: "onemptied",
|
||||
EndedEvent: "ended",
|
||||
LoadedDataEvent: "onloadeddata",
|
||||
LoadedMetadataEvent: "onloadedmetadata",
|
||||
LoadStartEvent: "onloadstart",
|
||||
PauseEvent: "onpause",
|
||||
PlayEvent: "onplay",
|
||||
PlayingEvent: "onplaying",
|
||||
ProgressEvent: "onprogress",
|
||||
SeekedEvent: "onseeked",
|
||||
SeekingEvent: "onseeking",
|
||||
StalledEvent: "onstalled",
|
||||
SuspendEvent: "onsuspend",
|
||||
WaitingEvent: "onwaiting",
|
||||
}
|
||||
}
|
||||
|
||||
func (player *mediaPlayerData) propertyChanged(tag string) {
|
||||
if player.created {
|
||||
switch tag {
|
||||
case Controls, Loop:
|
||||
value, _ := boolProperty(player, tag, player.session)
|
||||
if value {
|
||||
player.session.updateProperty(player.htmlID(), tag, value)
|
||||
} else {
|
||||
player.session.removeProperty(player.htmlID(), tag)
|
||||
}
|
||||
func mediaPlayerPropertyChanged(view View, tag PropertyName) {
|
||||
session := view.Session()
|
||||
|
||||
case Muted:
|
||||
value, _ := boolProperty(player, tag, player.session)
|
||||
player.session.callFunc("setMediaMuted", player.htmlID(), value)
|
||||
|
||||
case Preload:
|
||||
value, _ := enumProperty(player, tag, player.session, 0)
|
||||
values := enumProperties[Preload].values
|
||||
player.session.updateProperty(player.htmlID(), tag, values[value])
|
||||
|
||||
case AbortEvent, CanPlayEvent, CanPlayThroughEvent, CompleteEvent, EmptiedEvent,
|
||||
EndedEvent, LoadedDataEvent, LoadedMetadataEvent, PauseEvent, PlayEvent, PlayingEvent, ProgressEvent,
|
||||
LoadStartEvent, SeekedEvent, SeekingEvent, StalledEvent, SuspendEvent, WaitingEvent:
|
||||
|
||||
for _, event := range playerEvents() {
|
||||
if event.tag == tag {
|
||||
if value := player.getRaw(event.tag); value != nil {
|
||||
switch value := value.(type) {
|
||||
case []func(MediaPlayer):
|
||||
if len(value) > 0 {
|
||||
fn := fmt.Sprintf(`playerEvent(this, "%s")`, event.tag)
|
||||
player.session.updateProperty(player.htmlID(), event.cssTag, fn)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
player.session.updateProperty(player.htmlID(), tag, "")
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
case TimeUpdateEvent:
|
||||
if value := player.getRaw(tag); value != nil {
|
||||
player.session.updateProperty(player.htmlID(), "ontimeupdate", "playerTimeUpdatedEvent(this)")
|
||||
} else {
|
||||
player.session.updateProperty(player.htmlID(), "ontimeupdate", "")
|
||||
}
|
||||
|
||||
case VolumeChangedEvent:
|
||||
if value := player.getRaw(tag); value != nil {
|
||||
player.session.updateProperty(player.htmlID(), "onvolumechange", "playerVolumeChangedEvent(this)")
|
||||
} else {
|
||||
player.session.updateProperty(player.htmlID(), "onvolumechange", "")
|
||||
}
|
||||
|
||||
case DurationChangedEvent:
|
||||
if value := player.getRaw(tag); value != nil {
|
||||
player.session.updateProperty(player.htmlID(), "ondurationchange", "playerDurationChangedEvent(this)")
|
||||
} else {
|
||||
player.session.updateProperty(player.htmlID(), "ondurationchange", "")
|
||||
}
|
||||
|
||||
case RateChangedEvent:
|
||||
if value := player.getRaw(tag); value != nil {
|
||||
player.session.updateProperty(player.htmlID(), "onratechange", "playerRateChangedEvent(this)")
|
||||
} else {
|
||||
player.session.updateProperty(player.htmlID(), "onratechange", "")
|
||||
}
|
||||
|
||||
case PlayerErrorEvent:
|
||||
if value := player.getRaw(tag); value != nil {
|
||||
player.session.updateProperty(player.htmlID(), "onerror", "playerErrorEvent(this)")
|
||||
} else {
|
||||
player.session.updateProperty(player.htmlID(), "onerror", "")
|
||||
}
|
||||
|
||||
case Source:
|
||||
updateInnerHTML(player.htmlID(), player.session)
|
||||
switch tag {
|
||||
case Controls, Loop:
|
||||
value, _ := boolProperty(view, tag, session)
|
||||
if value {
|
||||
session.updateProperty(view.htmlID(), string(tag), value)
|
||||
} else {
|
||||
session.removeProperty(view.htmlID(), string(tag))
|
||||
}
|
||||
|
||||
case Muted:
|
||||
value, _ := boolProperty(view, Muted, session)
|
||||
session.callFunc("setMediaMuted", view.htmlID(), value)
|
||||
|
||||
case Preload:
|
||||
value, _ := enumProperty(view, Preload, session, 0)
|
||||
values := enumProperties[Preload].values
|
||||
session.updateProperty(view.htmlID(), string(Preload), values[value])
|
||||
|
||||
case AbortEvent, CanPlayEvent, CanPlayThroughEvent, CompleteEvent, EmptiedEvent,
|
||||
EndedEvent, LoadedDataEvent, LoadedMetadataEvent, PauseEvent, PlayEvent, PlayingEvent, ProgressEvent,
|
||||
LoadStartEvent, SeekedEvent, SeekingEvent, StalledEvent, SuspendEvent, WaitingEvent:
|
||||
|
||||
if cssTag, ok := mediaPlayerEvents()[tag]; ok {
|
||||
fn := ""
|
||||
if value := view.getRaw(tag); value != nil {
|
||||
if listeners, ok := value.([]func(MediaPlayer)); ok && len(listeners) > 0 {
|
||||
fn = fmt.Sprintf(`viewEvent(this, "%s")`, string(tag))
|
||||
}
|
||||
}
|
||||
session.updateProperty(view.htmlID(), cssTag, fn)
|
||||
}
|
||||
|
||||
case TimeUpdateEvent:
|
||||
if value := view.getRaw(tag); value != nil {
|
||||
session.updateProperty(view.htmlID(), "ontimeupdate", "viewTimeUpdatedEvent(this)")
|
||||
} else {
|
||||
session.updateProperty(view.htmlID(), "ontimeupdate", "")
|
||||
}
|
||||
|
||||
case VolumeChangedEvent:
|
||||
if value := view.getRaw(tag); value != nil {
|
||||
session.updateProperty(view.htmlID(), "onvolumechange", "viewVolumeChangedEvent(this)")
|
||||
} else {
|
||||
session.updateProperty(view.htmlID(), "onvolumechange", "")
|
||||
}
|
||||
|
||||
case DurationChangedEvent:
|
||||
if value := view.getRaw(tag); value != nil {
|
||||
session.updateProperty(view.htmlID(), "ondurationchange", "viewDurationChangedEvent(this)")
|
||||
} else {
|
||||
session.updateProperty(view.htmlID(), "ondurationchange", "")
|
||||
}
|
||||
|
||||
case RateChangedEvent:
|
||||
if value := view.getRaw(tag); value != nil {
|
||||
session.updateProperty(view.htmlID(), "onratechange", "viewRateChangedEvent(this)")
|
||||
} else {
|
||||
session.updateProperty(view.htmlID(), "onratechange", "")
|
||||
}
|
||||
|
||||
case PlayerErrorEvent:
|
||||
if value := view.getRaw(tag); value != nil {
|
||||
session.updateProperty(view.htmlID(), "onerror", "viewErrorEvent(this)")
|
||||
} else {
|
||||
session.updateProperty(view.htmlID(), "onerror", "")
|
||||
}
|
||||
|
||||
case Source:
|
||||
updateInnerHTML(view.htmlID(), session)
|
||||
|
||||
default:
|
||||
viewPropertyChanged(view, tag)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (player *mediaPlayerData) htmlSubviews(self View, buffer *strings.Builder) {
|
||||
|
@ -1305,10 +1253,10 @@ func (player *mediaPlayerData) htmlSubviews(self View, buffer *strings.Builder)
|
|||
|
||||
func (player *mediaPlayerData) htmlProperties(self View, buffer *strings.Builder) {
|
||||
player.viewData.htmlProperties(self, buffer)
|
||||
for _, tag := range []string{Controls, Loop, Muted, Preload} {
|
||||
for _, tag := range []PropertyName{Controls, Loop, Muted, Preload} {
|
||||
if value, _ := boolProperty(player, tag, player.session); value {
|
||||
buffer.WriteRune(' ')
|
||||
buffer.WriteString(tag)
|
||||
buffer.WriteString(string(tag))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1319,17 +1267,14 @@ func (player *mediaPlayerData) htmlProperties(self View, buffer *strings.Builder
|
|||
buffer.WriteRune('"')
|
||||
}
|
||||
|
||||
for _, event := range playerEvents() {
|
||||
if value := player.getRaw(event.tag); value != nil {
|
||||
switch value := value.(type) {
|
||||
case []func(MediaPlayer):
|
||||
if len(value) > 0 {
|
||||
buffer.WriteString(` `)
|
||||
buffer.WriteString(event.cssTag)
|
||||
buffer.WriteString(`="playerEvent(this, '`)
|
||||
buffer.WriteString(event.tag)
|
||||
buffer.WriteString(`')"`)
|
||||
}
|
||||
for tag, cssTag := range mediaPlayerEvents() {
|
||||
if value := player.getRaw(tag); value != nil {
|
||||
if listeners, ok := value.([]func(MediaPlayer)); ok && len(listeners) > 0 {
|
||||
buffer.WriteString(` `)
|
||||
buffer.WriteString(cssTag)
|
||||
buffer.WriteString(`="playerEvent(this, '`)
|
||||
buffer.WriteString(string(tag))
|
||||
buffer.WriteString(`')"`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1355,7 +1300,7 @@ func (player *mediaPlayerData) htmlProperties(self View, buffer *strings.Builder
|
|||
}
|
||||
}
|
||||
|
||||
func (player *mediaPlayerData) handleCommand(self View, command string, data DataObject) bool {
|
||||
func (player *mediaPlayerData) handleCommand(self View, command PropertyName, data DataObject) bool {
|
||||
switch command {
|
||||
case AbortEvent, CanPlayEvent, CanPlayThroughEvent, CompleteEvent, LoadStartEvent,
|
||||
EmptiedEvent, EndedEvent, LoadedDataEvent, LoadedMetadataEvent, PauseEvent, PlayEvent,
|
||||
|
|
|
@ -23,7 +23,7 @@ const (
|
|||
// `func(view rui.View)`,
|
||||
// `func(event rui.MouseEvent)`,
|
||||
// `func()`.
|
||||
ClickEvent = "click-event"
|
||||
ClickEvent PropertyName = "click-event"
|
||||
|
||||
// DoubleClickEvent is the constant for "double-click-event" property tag.
|
||||
//
|
||||
|
@ -41,7 +41,7 @@ const (
|
|||
// `func(view rui.View)`,
|
||||
// `func(event rui.MouseEvent)`,
|
||||
// `func()`.
|
||||
DoubleClickEvent = "double-click-event"
|
||||
DoubleClickEvent PropertyName = "double-click-event"
|
||||
|
||||
// MouseDown is the constant for "mouse-down" property tag.
|
||||
//
|
||||
|
@ -59,12 +59,12 @@ const (
|
|||
// `func(view rui.View)`,
|
||||
// `func(event rui.MouseEvent)`,
|
||||
// `func()`.
|
||||
MouseDown = "mouse-down"
|
||||
MouseDown PropertyName = "mouse-down"
|
||||
|
||||
// MouseUp is the constant for "mouse-up" property tag.
|
||||
//
|
||||
// Used by `View`.
|
||||
// Is fired at a View when a button on a pointing device (such as a mouse or trackpad) is released while the pointer is
|
||||
// Is fired at a View when a button on a pointing device (such as a mouse or trackpad) is released while the pointer is
|
||||
// located inside it. "mouse-up" events are the counterpoint to "mouse-down" events.
|
||||
//
|
||||
// General listener format:
|
||||
|
@ -78,7 +78,7 @@ const (
|
|||
// `func(view rui.View)`,
|
||||
// `func(event rui.MouseEvent)`,
|
||||
// `func()`.
|
||||
MouseUp = "mouse-up"
|
||||
MouseUp PropertyName = "mouse-up"
|
||||
|
||||
// MouseMove is the constant for "mouse-move" property tag.
|
||||
//
|
||||
|
@ -96,13 +96,13 @@ const (
|
|||
// `func(view rui.View)`,
|
||||
// `func(event rui.MouseEvent)`,
|
||||
// `func()`.
|
||||
MouseMove = "mouse-move"
|
||||
MouseMove PropertyName = "mouse-move"
|
||||
|
||||
// MouseOut is the constant for "mouse-out" property tag.
|
||||
//
|
||||
// Used by `View`.
|
||||
// Is fired at a View when a pointing device (usually a mouse) is used to move the cursor so that it is no longer
|
||||
// contained within the view or one of its children. "mouse-out" is also delivered to a view if the cursor enters a child
|
||||
// Is fired at a View when a pointing device (usually a mouse) is used to move the cursor so that it is no longer
|
||||
// contained within the view or one of its children. "mouse-out" is also delivered to a view if the cursor enters a child
|
||||
// view, because the child view obscures the visible area of the view.
|
||||
//
|
||||
// General listener format:
|
||||
|
@ -116,12 +116,12 @@ const (
|
|||
// `func(view rui.View)`,
|
||||
// `func(event rui.MouseEvent)`,
|
||||
// `func()`.
|
||||
MouseOut = "mouse-out"
|
||||
MouseOut PropertyName = "mouse-out"
|
||||
|
||||
// MouseOver is the constant for "mouse-over" property tag.
|
||||
//
|
||||
// Used by `View`.
|
||||
// Is fired at a View when a pointing device (such as a mouse or trackpad) is used to move the cursor onto the view or one
|
||||
// Is fired at a View when a pointing device (such as a mouse or trackpad) is used to move the cursor onto the view or one
|
||||
// of its child views.
|
||||
//
|
||||
// General listener format:
|
||||
|
@ -135,7 +135,7 @@ const (
|
|||
// `func(view rui.View)`,
|
||||
// `func(event rui.MouseEvent)`,
|
||||
// `func()`.
|
||||
MouseOver = "mouse-over"
|
||||
MouseOver PropertyName = "mouse-over"
|
||||
|
||||
// ContextMenuEvent is the constant for "context-menu-event" property tag.
|
||||
//
|
||||
|
@ -153,7 +153,7 @@ const (
|
|||
// `func(view rui.View)`,
|
||||
// `func(event rui.MouseEvent)`,
|
||||
// `func()`.
|
||||
ContextMenuEvent = "context-menu-event"
|
||||
ContextMenuEvent PropertyName = "context-menu-event"
|
||||
|
||||
// PrimaryMouseButton is a number of the main pressed button, usually the left button or the un-initialized state
|
||||
PrimaryMouseButton = 0
|
||||
|
@ -228,54 +228,39 @@ type MouseEvent struct {
|
|||
MetaKey bool
|
||||
}
|
||||
|
||||
var mouseEvents = map[string]struct{ jsEvent, jsFunc string }{
|
||||
ClickEvent: {jsEvent: "onclick", jsFunc: "clickEvent"},
|
||||
DoubleClickEvent: {jsEvent: "ondblclick", jsFunc: "doubleClickEvent"},
|
||||
MouseDown: {jsEvent: "onmousedown", jsFunc: "mouseDownEvent"},
|
||||
MouseUp: {jsEvent: "onmouseup", jsFunc: "mouseUpEvent"},
|
||||
MouseMove: {jsEvent: "onmousemove", jsFunc: "mouseMoveEvent"},
|
||||
MouseOut: {jsEvent: "onmouseout", jsFunc: "mouseOutEvent"},
|
||||
MouseOver: {jsEvent: "onmouseover", jsFunc: "mouseOverEvent"},
|
||||
ContextMenuEvent: {jsEvent: "oncontextmenu", jsFunc: "contextMenuEvent"},
|
||||
}
|
||||
|
||||
func (view *viewData) setMouseListener(tag string, value any) bool {
|
||||
listeners, ok := valueToEventListeners[View, MouseEvent](value)
|
||||
if !ok {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
|
||||
if listeners == nil {
|
||||
view.removeMouseListener(tag)
|
||||
} else if js, ok := mouseEvents[tag]; ok {
|
||||
view.properties[tag] = listeners
|
||||
if view.created {
|
||||
view.session.updateProperty(view.htmlID(), js.jsEvent, js.jsFunc+"(this, event)")
|
||||
/*
|
||||
func setMouseListener(properties Properties, tag PropertyName, value any) bool {
|
||||
if listeners, ok := valueToEventListeners[View, MouseEvent](value); ok {
|
||||
if len(listeners) == 0 {
|
||||
properties.setRaw(tag, nil)
|
||||
} else {
|
||||
properties.setRaw(tag, listeners)
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
return true
|
||||
}
|
||||
return true
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
|
||||
func (view *viewData) removeMouseListener(tag string) {
|
||||
func (view *viewData) removeMouseListener(tag PropertyName) {
|
||||
delete(view.properties, tag)
|
||||
if view.created {
|
||||
if js, ok := mouseEvents[tag]; ok {
|
||||
if js, ok := eventJsFunc[tag]; ok {
|
||||
view.session.removeProperty(view.htmlID(), js.jsEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func mouseEventsHtml(view View, buffer *strings.Builder, hasTooltip bool) {
|
||||
for tag, js := range mouseEvents {
|
||||
for _, tag := range []PropertyName{ClickEvent, DoubleClickEvent, MouseDown, MouseUp, MouseMove, MouseOut, MouseOver, ContextMenuEvent} {
|
||||
if value := view.getRaw(tag); value != nil {
|
||||
if listeners, ok := value.([]func(View, MouseEvent)); ok && len(listeners) > 0 {
|
||||
buffer.WriteString(js.jsEvent)
|
||||
buffer.WriteString(`="`)
|
||||
buffer.WriteString(js.jsFunc)
|
||||
buffer.WriteString(`(this, event)" `)
|
||||
if js, ok := eventJsFunc[tag]; ok {
|
||||
if listeners, ok := value.([]func(View, MouseEvent)); ok && len(listeners) > 0 {
|
||||
buffer.WriteString(js.jsEvent)
|
||||
buffer.WriteString(`="`)
|
||||
buffer.WriteString(js.jsFunc)
|
||||
buffer.WriteString(`(this, event)" `)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -285,6 +270,7 @@ func mouseEventsHtml(view View, buffer *strings.Builder, hasTooltip bool) {
|
|||
buffer.WriteString(`onmouseleave="mouseLeaveEvent(this, event)" `)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func getTimeStamp(data DataObject) uint64 {
|
||||
if value, ok := data.PropertyValue("timeStamp"); ok {
|
||||
|
@ -315,7 +301,7 @@ func (event *MouseEvent) init(data DataObject) {
|
|||
event.MetaKey = dataBoolProperty(data, "metaKey")
|
||||
}
|
||||
|
||||
func handleMouseEvents(view View, tag string, data DataObject) {
|
||||
func handleMouseEvents(view View, tag PropertyName, data DataObject) {
|
||||
listeners := getEventListeners[View, MouseEvent](view, nil, tag)
|
||||
if len(listeners) > 0 {
|
||||
var event MouseEvent
|
||||
|
|
203
numberPicker.go
203
numberPicker.go
|
@ -26,7 +26,7 @@ const (
|
|||
// `func(newValue, oldValue float64)`,
|
||||
// `func(newValue float64)`,
|
||||
// `func()`.
|
||||
NumberChangedEvent = "number-changed"
|
||||
NumberChangedEvent PropertyName = "number-changed"
|
||||
|
||||
// NumberPickerType is the constant for "number-picker-type" property tag.
|
||||
//
|
||||
|
@ -38,7 +38,7 @@ const (
|
|||
// Values:
|
||||
// `0`(`NumberEditor`) or "editor" - Displayed as an editor.
|
||||
// `1`(`NumberSlider`) or "slider" - Displayed as a slider.
|
||||
NumberPickerType = "number-picker-type"
|
||||
NumberPickerType PropertyName = "number-picker-type"
|
||||
|
||||
// NumberPickerMin is the constant for "number-picker-min" property tag.
|
||||
//
|
||||
|
@ -48,7 +48,7 @@ const (
|
|||
// Supported types: `float`, `int`, `string`.
|
||||
//
|
||||
// Internal type is `float`, other types converted to it during assignment.
|
||||
NumberPickerMin = "number-picker-min"
|
||||
NumberPickerMin PropertyName = "number-picker-min"
|
||||
|
||||
// NumberPickerMax is the constant for "number-picker-max" property tag.
|
||||
//
|
||||
|
@ -58,7 +58,7 @@ const (
|
|||
// Supported types: `float`, `int`, `string`.
|
||||
//
|
||||
// Internal type is `float`, other types converted to it during assignment.
|
||||
NumberPickerMax = "number-picker-max"
|
||||
NumberPickerMax PropertyName = "number-picker-max"
|
||||
|
||||
// NumberPickerStep is the constant for "number-picker-step" property tag.
|
||||
//
|
||||
|
@ -68,7 +68,7 @@ const (
|
|||
// Supported types: `float`, `int`, `string`.
|
||||
//
|
||||
// Internal type is `float`, other types converted to it during assignment.
|
||||
NumberPickerStep = "number-picker-step"
|
||||
NumberPickerStep PropertyName = "number-picker-step"
|
||||
|
||||
// NumberPickerValue is the constant for "number-picker-value" property tag.
|
||||
//
|
||||
|
@ -78,7 +78,7 @@ const (
|
|||
// Supported types: `float`, `int`, `string`.
|
||||
//
|
||||
// Internal type is `float`, other types converted to it during assignment.
|
||||
NumberPickerValue = "number-picker-value"
|
||||
NumberPickerValue PropertyName = "number-picker-value"
|
||||
)
|
||||
|
||||
// Constants which describe values of the "number-picker-type" property of a [NumberPicker]
|
||||
|
@ -97,8 +97,6 @@ type NumberPicker interface {
|
|||
|
||||
type numberPickerData struct {
|
||||
viewData
|
||||
dataList
|
||||
numberChangedListeners []func(NumberPicker, float64, float64)
|
||||
}
|
||||
|
||||
// NewNumberPicker create new NumberPicker object and return it
|
||||
|
@ -110,165 +108,92 @@ func NewNumberPicker(session Session, params Params) NumberPicker {
|
|||
}
|
||||
|
||||
func newNumberPicker(session Session) View {
|
||||
return NewNumberPicker(session, nil)
|
||||
return new(numberPickerData)
|
||||
}
|
||||
|
||||
func (picker *numberPickerData) init(session Session) {
|
||||
picker.viewData.init(session)
|
||||
picker.tag = "NumberPicker"
|
||||
picker.hasHtmlDisabled = true
|
||||
picker.numberChangedListeners = []func(NumberPicker, float64, float64){}
|
||||
picker.dataListInit()
|
||||
}
|
||||
|
||||
func (picker *numberPickerData) String() string {
|
||||
return getViewString(picker, nil)
|
||||
picker.normalize = normalizeNumberPickerTag
|
||||
picker.set = numberPickerSet
|
||||
picker.changed = numberPickerPropertyChanged
|
||||
}
|
||||
|
||||
func (picker *numberPickerData) Focusable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (picker *numberPickerData) normalizeTag(tag string) string {
|
||||
tag = strings.ToLower(tag)
|
||||
func normalizeNumberPickerTag(tag PropertyName) PropertyName {
|
||||
tag = defaultNormalize(tag)
|
||||
switch tag {
|
||||
case Type, Min, Max, Step, Value:
|
||||
return "number-picker-" + tag
|
||||
}
|
||||
|
||||
return picker.normalizeDataListTag(tag)
|
||||
return normalizeDataListTag(tag)
|
||||
}
|
||||
|
||||
func (picker *numberPickerData) Remove(tag string) {
|
||||
picker.remove(picker.normalizeTag(tag))
|
||||
}
|
||||
|
||||
func (picker *numberPickerData) remove(tag string) {
|
||||
func numberPickerSet(view View, tag PropertyName, value any) []PropertyName {
|
||||
switch tag {
|
||||
case NumberChangedEvent:
|
||||
if len(picker.numberChangedListeners) > 0 {
|
||||
picker.numberChangedListeners = []func(NumberPicker, float64, float64){}
|
||||
picker.propertyChangedEvent(tag)
|
||||
}
|
||||
return setEventWithOldListener[NumberPicker, float64](view, tag, value)
|
||||
|
||||
case NumberPickerValue:
|
||||
oldValue := GetNumberPickerValue(picker)
|
||||
picker.viewData.remove(tag)
|
||||
if oldValue != 0 {
|
||||
if picker.created {
|
||||
picker.session.callFunc("setInputValue", picker.htmlID(), 0)
|
||||
}
|
||||
for _, listener := range picker.numberChangedListeners {
|
||||
listener(picker, 0, oldValue)
|
||||
}
|
||||
picker.propertyChangedEvent(tag)
|
||||
}
|
||||
view.setRaw("old-number", GetNumberPickerValue(view))
|
||||
min, max := GetNumberPickerMinMax(view)
|
||||
|
||||
return setFloatProperty(view, NumberPickerValue, value, min, max)
|
||||
|
||||
case DataList:
|
||||
if len(picker.dataList.dataList) > 0 {
|
||||
picker.setDataList(picker, []string{}, true)
|
||||
}
|
||||
|
||||
default:
|
||||
picker.viewData.remove(tag)
|
||||
picker.propertyChanged(tag)
|
||||
}
|
||||
}
|
||||
|
||||
func (picker *numberPickerData) Set(tag string, value any) bool {
|
||||
return picker.set(picker.normalizeTag(tag), value)
|
||||
}
|
||||
|
||||
func (picker *numberPickerData) set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
picker.remove(tag)
|
||||
return true
|
||||
return setDataList(view, value, "")
|
||||
}
|
||||
|
||||
return viewSet(view, tag, value)
|
||||
}
|
||||
|
||||
func numberPickerPropertyChanged(view View, tag PropertyName) {
|
||||
switch tag {
|
||||
case NumberChangedEvent:
|
||||
listeners, ok := valueToEventWithOldListeners[NumberPicker, float64](value)
|
||||
if !ok {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
} else if listeners == nil {
|
||||
listeners = []func(NumberPicker, float64, float64){}
|
||||
case NumberPickerType:
|
||||
if GetNumberPickerType(view) == NumberSlider {
|
||||
view.Session().updateProperty(view.htmlID(), "type", "range")
|
||||
} else {
|
||||
view.Session().updateProperty(view.htmlID(), "type", "number")
|
||||
}
|
||||
picker.numberChangedListeners = listeners
|
||||
picker.propertyChangedEvent(tag)
|
||||
return true
|
||||
|
||||
case NumberPickerValue:
|
||||
oldValue := GetNumberPickerValue(picker)
|
||||
min, max := GetNumberPickerMinMax(picker)
|
||||
if picker.setFloatProperty(NumberPickerValue, value, min, max) {
|
||||
if f, ok := floatProperty(picker, NumberPickerValue, picker.Session(), min); ok && f != oldValue {
|
||||
newValue, _ := floatTextProperty(picker, NumberPickerValue, picker.Session(), min)
|
||||
if picker.created {
|
||||
picker.session.callFunc("setInputValue", picker.htmlID(), newValue)
|
||||
case NumberPickerMin:
|
||||
min, _ := GetNumberPickerMinMax(view)
|
||||
view.Session().updateProperty(view.htmlID(), "min", strconv.FormatFloat(min, 'f', -1, 32))
|
||||
|
||||
case NumberPickerMax:
|
||||
_, max := GetNumberPickerMinMax(view)
|
||||
view.Session().updateProperty(view.htmlID(), "max", strconv.FormatFloat(max, 'f', -1, 32))
|
||||
|
||||
case NumberPickerStep:
|
||||
if step := GetNumberPickerStep(view); step > 0 {
|
||||
view.Session().updateProperty(view.htmlID(), "step", strconv.FormatFloat(step, 'f', -1, 32))
|
||||
} else {
|
||||
view.Session().updateProperty(view.htmlID(), "step", "any")
|
||||
}
|
||||
|
||||
case TimePickerValue:
|
||||
value := GetNumberPickerValue(view)
|
||||
view.Session().callFunc("setInputValue", view.htmlID(), value)
|
||||
|
||||
if listeners := GetNumberChangedListeners(view); len(listeners) > 0 {
|
||||
old := 0.0
|
||||
if val := view.getRaw("old-number"); val != nil {
|
||||
if n, ok := val.(float64); ok {
|
||||
old = n
|
||||
}
|
||||
for _, listener := range picker.numberChangedListeners {
|
||||
listener(picker, f, oldValue)
|
||||
}
|
||||
picker.propertyChangedEvent(tag)
|
||||
}
|
||||
return true
|
||||
for _, listener := range listeners {
|
||||
listener(view, value, old)
|
||||
}
|
||||
}
|
||||
|
||||
case DataList:
|
||||
return picker.setDataList(picker, value, picker.created)
|
||||
|
||||
default:
|
||||
if picker.viewData.set(tag, value) {
|
||||
picker.propertyChanged(tag)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (picker *numberPickerData) propertyChanged(tag string) {
|
||||
if picker.created {
|
||||
switch tag {
|
||||
case NumberPickerType:
|
||||
if GetNumberPickerType(picker) == NumberSlider {
|
||||
picker.session.updateProperty(picker.htmlID(), "type", "range")
|
||||
} else {
|
||||
picker.session.updateProperty(picker.htmlID(), "type", "number")
|
||||
}
|
||||
|
||||
case NumberPickerMin:
|
||||
min, _ := GetNumberPickerMinMax(picker)
|
||||
picker.session.updateProperty(picker.htmlID(), Min, strconv.FormatFloat(min, 'f', -1, 32))
|
||||
|
||||
case NumberPickerMax:
|
||||
_, max := GetNumberPickerMinMax(picker)
|
||||
picker.session.updateProperty(picker.htmlID(), Max, strconv.FormatFloat(max, 'f', -1, 32))
|
||||
|
||||
case NumberPickerStep:
|
||||
if step := GetNumberPickerStep(picker); step > 0 {
|
||||
picker.session.updateProperty(picker.htmlID(), Step, strconv.FormatFloat(step, 'f', -1, 32))
|
||||
} else {
|
||||
picker.session.updateProperty(picker.htmlID(), Step, "any")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (picker *numberPickerData) Get(tag string) any {
|
||||
return picker.get(picker.normalizeTag(tag))
|
||||
}
|
||||
|
||||
func (picker *numberPickerData) get(tag string) any {
|
||||
switch tag {
|
||||
case NumberChangedEvent:
|
||||
return picker.numberChangedListeners
|
||||
|
||||
case DataList:
|
||||
return picker.dataList.dataList
|
||||
|
||||
default:
|
||||
return picker.viewData.get(tag)
|
||||
viewPropertyChanged(view, tag)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -277,7 +202,10 @@ func (picker *numberPickerData) htmlTag() string {
|
|||
}
|
||||
|
||||
func (picker *numberPickerData) htmlSubviews(self View, buffer *strings.Builder) {
|
||||
picker.dataListHtmlSubviews(self, buffer)
|
||||
dataListHtmlSubviews(self, buffer, func(text string, session Session) string {
|
||||
text, _ = session.resolveConstants(text)
|
||||
return text
|
||||
})
|
||||
}
|
||||
|
||||
func (picker *numberPickerData) htmlProperties(self View, buffer *strings.Builder) {
|
||||
|
@ -317,10 +245,10 @@ func (picker *numberPickerData) htmlProperties(self View, buffer *strings.Builde
|
|||
|
||||
buffer.WriteString(` oninput="editViewInputEvent(this)"`)
|
||||
|
||||
picker.dataListHtmlProperties(picker, buffer)
|
||||
dataListHtmlProperties(picker, buffer)
|
||||
}
|
||||
|
||||
func (picker *numberPickerData) handleCommand(self View, command string, data DataObject) bool {
|
||||
func (picker *numberPickerData) handleCommand(self View, command PropertyName, data DataObject) bool {
|
||||
switch command {
|
||||
case "textChanged":
|
||||
if text, ok := data.PropertyValue("text"); ok {
|
||||
|
@ -328,9 +256,12 @@ func (picker *numberPickerData) handleCommand(self View, command string, data Da
|
|||
oldValue := GetNumberPickerValue(picker)
|
||||
picker.properties[NumberPickerValue] = text
|
||||
if value != oldValue {
|
||||
for _, listener := range picker.numberChangedListeners {
|
||||
for _, listener := range GetNumberChangedListeners(picker) {
|
||||
listener(picker, value, oldValue)
|
||||
}
|
||||
if listener, ok := picker.changeListener[NumberPickerValue]; ok {
|
||||
listener(picker, NumberPickerValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
66
outline.go
66
outline.go
|
@ -16,7 +16,7 @@ type OutlineProperty interface {
|
|||
}
|
||||
|
||||
type outlinePropertyData struct {
|
||||
propertyList
|
||||
dataProperty
|
||||
}
|
||||
|
||||
// NewOutlineProperty creates the new OutlineProperty.
|
||||
|
@ -27,22 +27,29 @@ type outlinePropertyData struct {
|
|||
// "width" (Width). Determines the line thickness (SizeUnit).
|
||||
func NewOutlineProperty(params Params) OutlineProperty {
|
||||
outline := new(outlinePropertyData)
|
||||
outline.properties = map[string]any{}
|
||||
outline.init()
|
||||
for tag, value := range params {
|
||||
outline.Set(tag, value)
|
||||
}
|
||||
return outline
|
||||
}
|
||||
|
||||
func (outline *outlinePropertyData) init() {
|
||||
outline.propertyList.init()
|
||||
outline.normalize = normalizeOutlineTag
|
||||
outline.set = outlineSet
|
||||
outline.supportedProperties = []PropertyName{Style, Width, ColorTag}
|
||||
}
|
||||
|
||||
func (outline *outlinePropertyData) writeString(buffer *strings.Builder, indent string) {
|
||||
buffer.WriteString("_{ ")
|
||||
comma := false
|
||||
for _, tag := range []string{Style, Width, ColorTag} {
|
||||
for _, tag := range []PropertyName{Style, Width, ColorTag} {
|
||||
if value, ok := outline.properties[tag]; ok {
|
||||
if comma {
|
||||
buffer.WriteString(", ")
|
||||
}
|
||||
buffer.WriteString(tag)
|
||||
buffer.WriteString(string(tag))
|
||||
buffer.WriteString(" = ")
|
||||
writePropertyValue(buffer, BorderStyle, value, indent)
|
||||
comma = true
|
||||
|
@ -56,46 +63,33 @@ func (outline *outlinePropertyData) String() string {
|
|||
return runStringWriter(outline)
|
||||
}
|
||||
|
||||
func (outline *outlinePropertyData) normalizeTag(tag string) string {
|
||||
return strings.TrimPrefix(strings.ToLower(tag), "outline-")
|
||||
func normalizeOutlineTag(tag PropertyName) PropertyName {
|
||||
tag = defaultNormalize(tag)
|
||||
return PropertyName(strings.TrimPrefix(string(tag), "outline-"))
|
||||
}
|
||||
|
||||
func (outline *outlinePropertyData) Remove(tag string) {
|
||||
delete(outline.properties, outline.normalizeTag(tag))
|
||||
}
|
||||
|
||||
func (outline *outlinePropertyData) Set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
outline.Remove(tag)
|
||||
return true
|
||||
}
|
||||
|
||||
tag = outline.normalizeTag(tag)
|
||||
func outlineSet(properties Properties, tag PropertyName, value any) []PropertyName {
|
||||
switch tag {
|
||||
case Style:
|
||||
return outline.setEnumProperty(Style, value, enumProperties[BorderStyle].values)
|
||||
return setEnumProperty(properties, Style, value, enumProperties[BorderStyle].values)
|
||||
|
||||
case Width:
|
||||
if width, ok := value.(SizeUnit); ok {
|
||||
switch width.Type {
|
||||
case SizeInFraction, SizeInPercent:
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return outline.setSizeProperty(Width, value)
|
||||
return setSizeProperty(properties, Width, value)
|
||||
|
||||
case ColorTag:
|
||||
return outline.setColorProperty(ColorTag, value)
|
||||
return setColorProperty(properties, ColorTag, value)
|
||||
|
||||
default:
|
||||
ErrorLogF(`"%s" property is not compatible with the OutlineProperty`, tag)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (outline *outlinePropertyData) Get(tag string) any {
|
||||
return outline.propertyList.Get(outline.normalizeTag(tag))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (outline *outlinePropertyData) ViewOutline(session Session) ViewOutline {
|
||||
|
@ -132,7 +126,7 @@ func (outline ViewOutline) cssString(session Session) string {
|
|||
return builder.finish()
|
||||
}
|
||||
|
||||
func getOutline(properties Properties) OutlineProperty {
|
||||
func getOutlineProperty(properties Properties) OutlineProperty {
|
||||
if value := properties.Get(Outline); value != nil {
|
||||
if outline, ok := value.(OutlineProperty); ok {
|
||||
return outline
|
||||
|
@ -142,30 +136,30 @@ func getOutline(properties Properties) OutlineProperty {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (style *viewStyle) setOutline(value any) bool {
|
||||
func setOutlineProperty(properties Properties, value any) []PropertyName {
|
||||
switch value := value.(type) {
|
||||
case OutlineProperty:
|
||||
style.properties[Outline] = value
|
||||
properties.setRaw(Outline, value)
|
||||
|
||||
case ViewOutline:
|
||||
style.properties[Outline] = NewOutlineProperty(Params{Style: value.Style, Width: value.Width, ColorTag: value.Color})
|
||||
properties.setRaw(Outline, NewOutlineProperty(Params{Style: value.Style, Width: value.Width, ColorTag: value.Color}))
|
||||
|
||||
case ViewBorder:
|
||||
style.properties[Outline] = NewOutlineProperty(Params{Style: value.Style, Width: value.Width, ColorTag: value.Color})
|
||||
properties.setRaw(Outline, NewOutlineProperty(Params{Style: value.Style, Width: value.Width, ColorTag: value.Color}))
|
||||
|
||||
case DataObject:
|
||||
outline := NewOutlineProperty(nil)
|
||||
for _, tag := range []string{Style, Width, ColorTag} {
|
||||
if text, ok := value.PropertyValue(tag); ok && text != "" {
|
||||
for _, tag := range []PropertyName{Style, Width, ColorTag} {
|
||||
if text, ok := value.PropertyValue(string(tag)); ok && text != "" {
|
||||
outline.Set(tag, text)
|
||||
}
|
||||
}
|
||||
style.properties[Outline] = outline
|
||||
properties.setRaw(Outline, outline)
|
||||
|
||||
default:
|
||||
notCompatibleType(Outline, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
return true
|
||||
return []PropertyName{Outline}
|
||||
}
|
||||
|
|
24
params.go
24
params.go
|
@ -3,15 +3,15 @@ package rui
|
|||
import "sort"
|
||||
|
||||
// Params defines a type of a parameters list
|
||||
type Params map[string]any
|
||||
type Params map[PropertyName]any
|
||||
|
||||
// Get returns a value of the property with name defined by the argument. The type of return value depends
|
||||
// on the property. If the property is not set then nil is returned.
|
||||
func (params Params) Get(tag string) any {
|
||||
func (params Params) Get(tag PropertyName) any {
|
||||
return params.getRaw(tag)
|
||||
}
|
||||
|
||||
func (params Params) getRaw(tag string) any {
|
||||
func (params Params) getRaw(tag PropertyName) any {
|
||||
if value, ok := params[tag]; ok {
|
||||
return value
|
||||
}
|
||||
|
@ -20,12 +20,12 @@ func (params Params) getRaw(tag string) any {
|
|||
|
||||
// Set 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" is returned and a description of an error is written to the log
|
||||
func (params Params) Set(tag string, value any) bool {
|
||||
func (params Params) Set(tag PropertyName, value any) bool {
|
||||
params.setRaw(tag, value)
|
||||
return true
|
||||
}
|
||||
|
||||
func (params Params) setRaw(tag string, value any) {
|
||||
func (params Params) setRaw(tag PropertyName, value any) {
|
||||
if value != nil {
|
||||
params[tag] = value
|
||||
} else {
|
||||
|
@ -34,7 +34,7 @@ func (params Params) setRaw(tag string, value any) {
|
|||
}
|
||||
|
||||
// Remove removes the property with name defined by the argument from a map.
|
||||
func (params Params) Remove(tag string) {
|
||||
func (params Params) Remove(tag PropertyName) {
|
||||
delete(params, tag)
|
||||
}
|
||||
|
||||
|
@ -46,11 +46,17 @@ func (params Params) Clear() {
|
|||
}
|
||||
|
||||
// AllTags returns a sorted slice of all properties.
|
||||
func (params Params) AllTags() []string {
|
||||
tags := make([]string, 0, len(params))
|
||||
func (params Params) AllTags() []PropertyName {
|
||||
tags := make([]PropertyName, 0, len(params))
|
||||
for t := range params {
|
||||
tags = append(tags, t)
|
||||
}
|
||||
sort.Strings(tags)
|
||||
sort.Slice(tags, func(i, j int) bool {
|
||||
return tags[i] < tags[j]
|
||||
})
|
||||
return tags
|
||||
}
|
||||
|
||||
func (params Params) empty() bool {
|
||||
return len(params) == 0
|
||||
}
|
||||
|
|
1
path.go
1
path.go
|
@ -55,7 +55,6 @@ type Path interface {
|
|||
// If the shape has already been closed or has only one point, this function does nothing.
|
||||
Close()
|
||||
|
||||
//create(session Session)
|
||||
obj() any
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
package rui
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Constants for [View] specific pointer events properties
|
||||
const (
|
||||
// PointerDown is the constant for "pointer-down" property tag.
|
||||
//
|
||||
// Used by `View`.
|
||||
// Fired when a pointer becomes active. For mouse, it is fired when the device transitions from no buttons depressed to at
|
||||
// least one button depressed. For touch, it is fired when physical contact is made with the digitizer. For pen, it is
|
||||
// Fired when a pointer becomes active. For mouse, it is fired when the device transitions from no buttons depressed to at
|
||||
// least one button depressed. For touch, it is fired when physical contact is made with the digitizer. For pen, it is
|
||||
// fired when the stylus makes physical contact with the digitizer.
|
||||
//
|
||||
// General listener format:
|
||||
|
@ -24,7 +20,7 @@ const (
|
|||
// `func(event rui.PointerEvent)`,
|
||||
// `func(view rui.View)`,
|
||||
// `func()`.
|
||||
PointerDown = "pointer-down"
|
||||
PointerDown PropertyName = "pointer-down"
|
||||
|
||||
// PointerUp is the constant for "pointer-up" property tag.
|
||||
//
|
||||
|
@ -42,7 +38,7 @@ const (
|
|||
// `func(event rui.PointerEvent)`,
|
||||
// `func(view rui.View)`,
|
||||
// `func()`.
|
||||
PointerUp = "pointer-up"
|
||||
PointerUp PropertyName = "pointer-up"
|
||||
|
||||
// PointerMove is the constant for "pointer-move" property tag.
|
||||
//
|
||||
|
@ -60,7 +56,7 @@ const (
|
|||
// `func(event rui.PointerEvent)`,
|
||||
// `func(view rui.View)`,
|
||||
// `func()`.
|
||||
PointerMove = "pointer-move"
|
||||
PointerMove PropertyName = "pointer-move"
|
||||
|
||||
// PointerCancel is the constant for "pointer-cancel" property tag.
|
||||
//
|
||||
|
@ -78,13 +74,13 @@ const (
|
|||
// `func(event rui.PointerEvent)`,
|
||||
// `func(view rui.View)`,
|
||||
// `func()`.
|
||||
PointerCancel = "pointer-cancel"
|
||||
PointerCancel PropertyName = "pointer-cancel"
|
||||
|
||||
// PointerOut is the constant for "pointer-out" property tag.
|
||||
//
|
||||
// Used by `View`.
|
||||
// Is fired for several reasons including: pointing device is moved out of the hit test boundaries of an element; firing
|
||||
// the "pointer-up" event for a device that does not support hover (see "pointer-up"); after firing the "pointer-cancel"
|
||||
// Is fired for several reasons including: pointing device is moved out of the hit test boundaries of an element; firing
|
||||
// the "pointer-up" event for a device that does not support hover (see "pointer-up"); after firing the "pointer-cancel"
|
||||
// event (see "pointer-cancel"); when a pen stylus leaves the hover range detectable by the digitizer.
|
||||
//
|
||||
// General listener format:
|
||||
|
@ -98,7 +94,7 @@ const (
|
|||
// `func(event rui.PointerEvent)`,
|
||||
// `func(view rui.View)`,
|
||||
// `func()`.
|
||||
PointerOut = "pointer-out"
|
||||
PointerOut PropertyName = "pointer-out"
|
||||
|
||||
// PointerOver is the constant for "pointer-over" property tag.
|
||||
//
|
||||
|
@ -116,7 +112,7 @@ const (
|
|||
// `func(event rui.PointerEvent)`,
|
||||
// `func(view rui.View)`,
|
||||
// `func()`.
|
||||
PointerOver = "pointer-over"
|
||||
PointerOver PropertyName = "pointer-over"
|
||||
)
|
||||
|
||||
// PointerEvent represent a stylus events. Also inherit [MouseEvent] attributes
|
||||
|
@ -158,56 +154,44 @@ type PointerEvent struct {
|
|||
IsPrimary bool
|
||||
}
|
||||
|
||||
var pointerEvents = map[string]struct{ jsEvent, jsFunc string }{
|
||||
PointerDown: {jsEvent: "onpointerdown", jsFunc: "pointerDownEvent"},
|
||||
PointerUp: {jsEvent: "onpointerup", jsFunc: "pointerUpEvent"},
|
||||
PointerMove: {jsEvent: "onpointermove", jsFunc: "pointerMoveEvent"},
|
||||
PointerCancel: {jsEvent: "onpointercancel", jsFunc: "pointerCancelEvent"},
|
||||
PointerOut: {jsEvent: "onpointerout", jsFunc: "pointerOutEvent"},
|
||||
PointerOver: {jsEvent: "onpointerover", jsFunc: "pointerOverEvent"},
|
||||
}
|
||||
|
||||
func (view *viewData) setPointerListener(tag string, value any) bool {
|
||||
listeners, ok := valueToEventListeners[View, PointerEvent](value)
|
||||
if !ok {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
|
||||
if listeners == nil {
|
||||
view.removePointerListener(tag)
|
||||
} else if js, ok := pointerEvents[tag]; ok {
|
||||
view.properties[tag] = listeners
|
||||
if view.created {
|
||||
view.session.updateProperty(view.htmlID(), js.jsEvent, js.jsFunc+"(this, event)")
|
||||
/*
|
||||
func setPointerListener(properties Properties, tag PropertyName, value any) bool {
|
||||
if listeners, ok := valueToEventListeners[View, PointerEvent](value); ok {
|
||||
if len(listeners) == 0 {
|
||||
properties.setRaw(tag, nil)
|
||||
} else {
|
||||
properties.setRaw(tag, listeners)
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
return true
|
||||
}
|
||||
return true
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
|
||||
func (view *viewData) removePointerListener(tag string) {
|
||||
func (view *viewData) removePointerListener(tag PropertyName) {
|
||||
delete(view.properties, tag)
|
||||
if view.created {
|
||||
if js, ok := pointerEvents[tag]; ok {
|
||||
if js, ok := eventJsFunc[tag]; ok {
|
||||
view.session.removeProperty(view.htmlID(), js.jsEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func pointerEventsHtml(view View, buffer *strings.Builder) {
|
||||
for tag, js := range pointerEvents {
|
||||
for _, tag := range []PropertyName{PointerDown, PointerUp, PointerMove, PointerOut, PointerOver, PointerCancel} {
|
||||
if value := view.getRaw(tag); value != nil {
|
||||
if listeners, ok := value.([]func(View, PointerEvent)); ok && len(listeners) > 0 {
|
||||
buffer.WriteString(js.jsEvent)
|
||||
buffer.WriteString(`="`)
|
||||
buffer.WriteString(js.jsFunc)
|
||||
buffer.WriteString(`(this, event)" `)
|
||||
if js, ok := eventJsFunc[tag]; ok {
|
||||
if listeners, ok := value.([]func(View, PointerEvent)); ok && len(listeners) > 0 {
|
||||
buffer.WriteString(js.jsEvent)
|
||||
buffer.WriteString(`="`)
|
||||
buffer.WriteString(js.jsFunc)
|
||||
buffer.WriteString(`(this, event)" `)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func (event *PointerEvent) init(data DataObject) {
|
||||
event.MouseEvent.init(data)
|
||||
|
@ -225,7 +209,7 @@ func (event *PointerEvent) init(data DataObject) {
|
|||
event.IsPrimary = dataBoolProperty(data, "isPrimary")
|
||||
}
|
||||
|
||||
func handlePointerEvents(view View, tag string, data DataObject) {
|
||||
func handlePointerEvents(view View, tag PropertyName, data DataObject) {
|
||||
listeners := getEventListeners[View, PointerEvent](view, nil, tag)
|
||||
if len(listeners) == 0 {
|
||||
return
|
||||
|
|
22
popup.go
22
popup.go
|
@ -27,7 +27,7 @@ const (
|
|||
// Set popup title style. Default title style is "ruiPopupTitle".
|
||||
//
|
||||
// Supported types: `string`.
|
||||
TitleStyle = "title-style"
|
||||
TitleStyle PropertyName = "title-style"
|
||||
|
||||
// CloseButton is the constant for "close-button" property tag.
|
||||
//
|
||||
|
@ -39,7 +39,7 @@ const (
|
|||
// Values:
|
||||
// `true` or `1` or "true", "yes", "on", "1" - Close button will be added to a title bar of a window.
|
||||
// `false` or `0` or "false", "no", "off", "0" - Popup without a close button.
|
||||
CloseButton = "close-button"
|
||||
CloseButton PropertyName = "close-button"
|
||||
|
||||
// OutsideClose is the constant for "outside-close" property tag.
|
||||
//
|
||||
|
@ -51,7 +51,7 @@ const (
|
|||
// Values:
|
||||
// `true` or `1` or "true", "yes", "on", "1" - Clicking outside the popup window will automatically call the `Dismiss()` method.
|
||||
// `false` or `0` or "false", "no", "off", "0" - Clicking outside the popup window has no effect.
|
||||
OutsideClose = "outside-close"
|
||||
OutsideClose PropertyName = "outside-close"
|
||||
|
||||
// Buttons is the constant for "buttons" property tag.
|
||||
//
|
||||
|
@ -62,7 +62,7 @@ const (
|
|||
//
|
||||
// Internal type is `[]PopupButton`, other types converted to it during assignment.
|
||||
// See `PopupButton` description for more details.
|
||||
Buttons = "buttons"
|
||||
Buttons PropertyName = "buttons"
|
||||
|
||||
// ButtonsAlign is the constant for "buttons-align" property tag.
|
||||
//
|
||||
|
@ -76,7 +76,7 @@ const (
|
|||
// `1`(`RightAlign`) or "right" - Right alignment.
|
||||
// `2`(`CenterAlign`) or "center" - Center alignment.
|
||||
// `3`(`StretchAlign`) or "stretch" - Width alignment.
|
||||
ButtonsAlign = "buttons-align"
|
||||
ButtonsAlign PropertyName = "buttons-align"
|
||||
|
||||
// DismissEvent is the constant for "dismiss-event" property tag.
|
||||
//
|
||||
|
@ -91,7 +91,7 @@ const (
|
|||
//
|
||||
// Allowed listener formats:
|
||||
// `func()`.
|
||||
DismissEvent = "dismiss-event"
|
||||
DismissEvent PropertyName = "dismiss-event"
|
||||
|
||||
// Arrow is the constant for "arrow" property tag.
|
||||
//
|
||||
|
@ -106,7 +106,7 @@ const (
|
|||
// `2`(`RightArrow`) or "right" - Arrow on the right side of the pop-up window.
|
||||
// `3`(`BottomArrow`) or "bottom" - Arrow at the bottom of the pop-up window.
|
||||
// `4`(`LeftArrow`) or "left" - Arrow on the left side of the pop-up window.
|
||||
Arrow = "arrow"
|
||||
Arrow PropertyName = "arrow"
|
||||
|
||||
// ArrowAlign is the constant for "arrow-align" property tag.
|
||||
//
|
||||
|
@ -119,7 +119,7 @@ const (
|
|||
// `0`(`TopAlign`/`LeftAlign`) or "top" - Top/left alignment.
|
||||
// `1`(`BottomAlign`/`RightAlign`) or "bottom" - Bottom/right alignment.
|
||||
// `2`(`CenterAlign`) or "center" - Center alignment.
|
||||
ArrowAlign = "arrow-align"
|
||||
ArrowAlign PropertyName = "arrow-align"
|
||||
|
||||
// ArrowSize is the constant for "arrow-size" property tag.
|
||||
//
|
||||
|
@ -130,7 +130,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
ArrowSize = "arrow-size"
|
||||
ArrowSize PropertyName = "arrow-size"
|
||||
|
||||
// ArrowWidth is the constant for "arrow-width" property tag.
|
||||
//
|
||||
|
@ -141,7 +141,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
ArrowWidth = "arrow-width"
|
||||
ArrowWidth PropertyName = "arrow-width"
|
||||
|
||||
// ArrowOffset is the constant for "arrow-offset" property tag.
|
||||
//
|
||||
|
@ -152,7 +152,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
ArrowOffset = "arrow-offset"
|
||||
ArrowOffset PropertyName = "arrow-offset"
|
||||
|
||||
// NoneArrow is value of the popup "arrow" property: no arrow
|
||||
NoneArrow = 0
|
||||
|
|
|
@ -15,7 +15,7 @@ const (
|
|||
// Supported types: `float`, `int`, `string`.
|
||||
//
|
||||
// Internal type is `float`, other types converted to it during assignment.
|
||||
ProgressBarMax = "progress-max"
|
||||
ProgressBarMax PropertyName = "progress-max"
|
||||
|
||||
// ProgressBarValue is the constant for "progress-value" property tag.
|
||||
//
|
||||
|
@ -25,7 +25,7 @@ const (
|
|||
// Supported types: `float`, `int`, `string`.
|
||||
//
|
||||
// Internal type is `float`, other types converted to it during assignment.
|
||||
ProgressBarValue = "progress-value"
|
||||
ProgressBarValue PropertyName = "progress-value"
|
||||
)
|
||||
|
||||
// ProgressBar represents a ProgressBar view
|
||||
|
@ -46,20 +46,18 @@ func NewProgressBar(session Session, params Params) ProgressBar {
|
|||
}
|
||||
|
||||
func newProgressBar(session Session) View {
|
||||
return NewProgressBar(session, nil)
|
||||
return new(progressBarData)
|
||||
}
|
||||
|
||||
func (progress *progressBarData) init(session Session) {
|
||||
progress.viewData.init(session)
|
||||
progress.tag = "ProgressBar"
|
||||
progress.normalize = normalizeProgressBarTag
|
||||
progress.changed = progressBarPropertyChanged
|
||||
}
|
||||
|
||||
func (progress *progressBarData) String() string {
|
||||
return getViewString(progress, nil)
|
||||
}
|
||||
|
||||
func (progress *progressBarData) normalizeTag(tag string) string {
|
||||
tag = strings.ToLower(tag)
|
||||
func normalizeProgressBarTag(tag PropertyName) PropertyName {
|
||||
tag = defaultNormalize(tag)
|
||||
switch tag {
|
||||
case Max, "progress-bar-max", "progressbar-max":
|
||||
return ProgressBarMax
|
||||
|
@ -70,45 +68,22 @@ func (progress *progressBarData) normalizeTag(tag string) string {
|
|||
return tag
|
||||
}
|
||||
|
||||
func (progress *progressBarData) Remove(tag string) {
|
||||
progress.remove(progress.normalizeTag(tag))
|
||||
}
|
||||
func progressBarPropertyChanged(view View, tag PropertyName) {
|
||||
|
||||
func (progress *progressBarData) remove(tag string) {
|
||||
progress.viewData.remove(tag)
|
||||
progress.propertyChanged(tag)
|
||||
}
|
||||
switch tag {
|
||||
case ProgressBarMax:
|
||||
view.Session().updateProperty(view.htmlID(), "max",
|
||||
strconv.FormatFloat(GetProgressBarMax(view), 'f', -1, 32))
|
||||
|
||||
func (progress *progressBarData) propertyChanged(tag string) {
|
||||
if progress.created {
|
||||
switch tag {
|
||||
case ProgressBarMax:
|
||||
progress.session.updateProperty(progress.htmlID(), Max,
|
||||
strconv.FormatFloat(GetProgressBarMax(progress), 'f', -1, 32))
|
||||
case ProgressBarValue:
|
||||
view.Session().updateProperty(view.htmlID(), "value",
|
||||
strconv.FormatFloat(GetProgressBarValue(view), 'f', -1, 32))
|
||||
|
||||
case ProgressBarValue:
|
||||
progress.session.updateProperty(progress.htmlID(), Value,
|
||||
strconv.FormatFloat(GetProgressBarValue(progress), 'f', -1, 32))
|
||||
}
|
||||
default:
|
||||
viewPropertyChanged(view, tag)
|
||||
}
|
||||
}
|
||||
|
||||
func (progress *progressBarData) Set(tag string, value any) bool {
|
||||
return progress.set(progress.normalizeTag(tag), value)
|
||||
}
|
||||
|
||||
func (progress *progressBarData) set(tag string, value any) bool {
|
||||
if progress.viewData.set(tag, value) {
|
||||
progress.propertyChanged(tag)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (progress *progressBarData) Get(tag string) any {
|
||||
return progress.get(progress.normalizeTag(tag))
|
||||
}
|
||||
|
||||
func (progress *progressBarData) htmlTag() string {
|
||||
return "progress"
|
||||
}
|
||||
|
|
126
properties.go
126
properties.go
|
@ -9,71 +9,96 @@ import (
|
|||
type Properties interface {
|
||||
// Get returns a value of the property with name defined by the argument.
|
||||
// The type of return value depends on the property. If the property is not set then nil is returned.
|
||||
Get(tag string) any
|
||||
getRaw(tag string) any
|
||||
Get(tag PropertyName) any
|
||||
getRaw(tag PropertyName) any
|
||||
|
||||
// Set 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
|
||||
Set(tag string, value any) bool
|
||||
setRaw(tag string, value any)
|
||||
Set(tag PropertyName, value any) bool
|
||||
setRaw(tag PropertyName, value any)
|
||||
|
||||
// Remove removes the property with name defined by the argument
|
||||
Remove(tag string)
|
||||
Remove(tag PropertyName)
|
||||
|
||||
// Clear removes all properties
|
||||
Clear()
|
||||
|
||||
// AllTags returns an array of the set properties
|
||||
AllTags() []string
|
||||
AllTags() []PropertyName
|
||||
|
||||
empty() bool
|
||||
}
|
||||
|
||||
type propertyList struct {
|
||||
properties map[string]any
|
||||
properties map[PropertyName]any
|
||||
normalize func(PropertyName) PropertyName
|
||||
//getFunc func(PropertyName) any
|
||||
//set func(Properties, PropertyName, any) []PropertyName
|
||||
//remove func(Properties, PropertyName) []PropertyName
|
||||
}
|
||||
|
||||
type dataProperty struct {
|
||||
propertyList
|
||||
supportedProperties []PropertyName
|
||||
get func(Properties, PropertyName) any
|
||||
set func(Properties, PropertyName, any) []PropertyName
|
||||
remove func(Properties, PropertyName) []PropertyName
|
||||
}
|
||||
|
||||
func defaultNormalize(tag PropertyName) PropertyName {
|
||||
return PropertyName(strings.ToLower(strings.Trim(string(tag), " \t")))
|
||||
}
|
||||
|
||||
func (properties *propertyList) init() {
|
||||
properties.properties = map[string]any{}
|
||||
properties.properties = map[PropertyName]any{}
|
||||
properties.normalize = defaultNormalize
|
||||
//properties.getFunc = properties.getRaw
|
||||
//properties.set = propertiesSet
|
||||
//properties.remove = propertiesRemove
|
||||
}
|
||||
|
||||
func (properties *propertyList) Get(tag string) any {
|
||||
return properties.getRaw(strings.ToLower(tag))
|
||||
func (properties *propertyList) empty() bool {
|
||||
return len(properties.properties) == 0
|
||||
}
|
||||
|
||||
func (properties *propertyList) getRaw(tag string) any {
|
||||
func (properties *propertyList) getRaw(tag PropertyName) any {
|
||||
if value, ok := properties.properties[tag]; ok {
|
||||
return value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (properties *propertyList) setRaw(tag string, value any) {
|
||||
properties.properties[tag] = value
|
||||
}
|
||||
|
||||
func (properties *propertyList) Remove(tag string) {
|
||||
delete(properties.properties, strings.ToLower(tag))
|
||||
}
|
||||
|
||||
func (properties *propertyList) remove(tag string) {
|
||||
delete(properties.properties, tag)
|
||||
}
|
||||
|
||||
func (properties *propertyList) Clear() {
|
||||
properties.properties = map[string]any{}
|
||||
}
|
||||
|
||||
func (properties *propertyList) AllTags() []string {
|
||||
tags := make([]string, 0, len(properties.properties))
|
||||
for t := range properties.properties {
|
||||
tags = append(tags, t)
|
||||
func (properties *propertyList) setRaw(tag PropertyName, value any) {
|
||||
if value == nil {
|
||||
delete(properties.properties, tag)
|
||||
} else {
|
||||
properties.properties[tag] = value
|
||||
}
|
||||
sort.Strings(tags)
|
||||
}
|
||||
|
||||
/*
|
||||
func (properties *propertyList) Remove(tag PropertyName) {
|
||||
properties.remove(properties, properties.normalize(tag))
|
||||
}
|
||||
*/
|
||||
func (properties *propertyList) Clear() {
|
||||
properties.properties = map[PropertyName]any{}
|
||||
}
|
||||
|
||||
func (properties *propertyList) AllTags() []PropertyName {
|
||||
tags := make([]PropertyName, 0, len(properties.properties))
|
||||
for tag := range properties.properties {
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
sort.Slice(tags, func(i, j int) bool {
|
||||
return tags[i] < tags[j]
|
||||
})
|
||||
return tags
|
||||
}
|
||||
|
||||
func (properties *propertyList) writeToBuffer(buffer *strings.Builder,
|
||||
indent string, objectTag string, tags []string) {
|
||||
indent string, objectTag string, tags []PropertyName) {
|
||||
|
||||
buffer.WriteString(objectTag)
|
||||
buffer.WriteString(" {\n")
|
||||
|
@ -83,7 +108,7 @@ func (properties *propertyList) writeToBuffer(buffer *strings.Builder,
|
|||
for _, tag := range tags {
|
||||
if value, ok := properties.properties[tag]; ok {
|
||||
buffer.WriteString(indent2)
|
||||
buffer.WriteString(tag)
|
||||
buffer.WriteString(string(tag))
|
||||
buffer.WriteString(" = ")
|
||||
writePropertyValue(buffer, tag, value, indent2)
|
||||
buffer.WriteString(",\n")
|
||||
|
@ -100,14 +125,41 @@ func parseProperties(properties Properties, object DataObject) {
|
|||
if node := object.Property(i); node != nil {
|
||||
switch node.Type() {
|
||||
case TextNode:
|
||||
properties.Set(node.Tag(), node.Text())
|
||||
properties.Set(PropertyName(node.Tag()), node.Text())
|
||||
|
||||
case ObjectNode:
|
||||
properties.Set(node.Tag(), node.Object())
|
||||
properties.Set(PropertyName(node.Tag()), node.Object())
|
||||
|
||||
case ArrayNode:
|
||||
properties.Set(node.Tag(), node.ArrayElements())
|
||||
properties.Set(PropertyName(node.Tag()), node.ArrayElements())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func propertiesGet(properties Properties, tag PropertyName) any {
|
||||
return properties.getRaw(tag)
|
||||
}
|
||||
|
||||
func propertiesRemove(properties Properties, tag PropertyName) []PropertyName {
|
||||
if properties.getRaw(tag) == nil {
|
||||
return []PropertyName{}
|
||||
}
|
||||
properties.setRaw(tag, nil)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
func (data *dataProperty) init() {
|
||||
data.propertyList.init()
|
||||
data.get = propertiesGet
|
||||
data.set = propertiesSet
|
||||
data.remove = propertiesRemove
|
||||
}
|
||||
|
||||
func (data *dataProperty) Get(tag PropertyName) any {
|
||||
return propertiesGet(data, data.normalize(tag))
|
||||
}
|
||||
|
||||
func (data *dataProperty) Remove(tag PropertyName) {
|
||||
data.remove(data, data.normalize(tag))
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
func stringProperty(properties Properties, tag string, session Session) (string, bool) {
|
||||
func stringProperty(properties Properties, tag PropertyName, session Session) (string, bool) {
|
||||
if value := properties.getRaw(tag); value != nil {
|
||||
if text, ok := value.(string); ok {
|
||||
return session.resolveConstants(text)
|
||||
|
@ -15,7 +15,7 @@ func stringProperty(properties Properties, tag string, session Session) (string,
|
|||
return "", false
|
||||
}
|
||||
|
||||
func imageProperty(properties Properties, tag string, session Session) (string, bool) {
|
||||
func imageProperty(properties Properties, tag PropertyName, session Session) (string, bool) {
|
||||
if value := properties.getRaw(tag); value != nil {
|
||||
if text, ok := value.(string); ok {
|
||||
if text != "" && text[0] == '@' {
|
||||
|
@ -61,11 +61,11 @@ func valueToSizeUnit(value any, session Session) (SizeUnit, bool) {
|
|||
return AutoSize(), false
|
||||
}
|
||||
|
||||
func sizeProperty(properties Properties, tag string, session Session) (SizeUnit, bool) {
|
||||
func sizeProperty(properties Properties, tag PropertyName, session Session) (SizeUnit, bool) {
|
||||
return valueToSizeUnit(properties.getRaw(tag), session)
|
||||
}
|
||||
|
||||
func angleProperty(properties Properties, tag string, session Session) (AngleUnit, bool) {
|
||||
func angleProperty(properties Properties, tag PropertyName, session Session) (AngleUnit, bool) {
|
||||
if value := properties.getRaw(tag); value != nil {
|
||||
switch value := value.(type) {
|
||||
case AngleUnit:
|
||||
|
@ -98,11 +98,11 @@ func valueToColor(value any, session Session) (Color, bool) {
|
|||
return Color(0), false
|
||||
}
|
||||
|
||||
func colorProperty(properties Properties, tag string, session Session) (Color, bool) {
|
||||
func colorProperty(properties Properties, tag PropertyName, session Session) (Color, bool) {
|
||||
return valueToColor(properties.getRaw(tag), session)
|
||||
}
|
||||
|
||||
func valueToEnum(value any, tag string, session Session, defaultValue int) (int, bool) {
|
||||
func valueToEnum(value any, tag PropertyName, session Session, defaultValue int) (int, bool) {
|
||||
if value != nil {
|
||||
values := enumProperties[tag].values
|
||||
switch value := value.(type) {
|
||||
|
@ -165,7 +165,7 @@ func enumStringToInt(value string, enumValues []string, logError bool) (int, boo
|
|||
return 0, false
|
||||
}
|
||||
|
||||
func enumProperty(properties Properties, tag string, session Session, defaultValue int) (int, bool) {
|
||||
func enumProperty(properties Properties, tag PropertyName, session Session, defaultValue int) (int, bool) {
|
||||
return valueToEnum(properties.getRaw(tag), tag, session, defaultValue)
|
||||
}
|
||||
|
||||
|
@ -194,7 +194,7 @@ func valueToBool(value any, session Session) (bool, bool) {
|
|||
return false, false
|
||||
}
|
||||
|
||||
func boolProperty(properties Properties, tag string, session Session) (bool, bool) {
|
||||
func boolProperty(properties Properties, tag PropertyName, session Session) (bool, bool) {
|
||||
return valueToBool(properties.getRaw(tag), session)
|
||||
}
|
||||
|
||||
|
@ -224,7 +224,7 @@ func valueToInt(value any, session Session, defaultValue int) (int, bool) {
|
|||
return defaultValue, false
|
||||
}
|
||||
|
||||
func intProperty(properties Properties, tag string, session Session, defaultValue int) (int, bool) {
|
||||
func intProperty(properties Properties, tag PropertyName, session Session, defaultValue int) (int, bool) {
|
||||
return valueToInt(properties.getRaw(tag), session, defaultValue)
|
||||
}
|
||||
|
||||
|
@ -248,7 +248,7 @@ func valueToFloat(value any, session Session, defaultValue float64) (float64, bo
|
|||
return defaultValue, false
|
||||
}
|
||||
|
||||
func floatProperty(properties Properties, tag string, session Session, defaultValue float64) (float64, bool) {
|
||||
func floatProperty(properties Properties, tag PropertyName, session Session, defaultValue float64) (float64, bool) {
|
||||
return valueToFloat(properties.getRaw(tag), session, defaultValue)
|
||||
}
|
||||
|
||||
|
@ -272,7 +272,7 @@ func valueToFloatText(value any, session Session, defaultValue float64) (string,
|
|||
return fmt.Sprintf("%g", defaultValue), false
|
||||
}
|
||||
|
||||
func floatTextProperty(properties Properties, tag string, session Session, defaultValue float64) (string, bool) {
|
||||
func floatTextProperty(properties Properties, tag PropertyName, session Session, defaultValue float64) (string, bool) {
|
||||
return valueToFloatText(properties.getRaw(tag), session, defaultValue)
|
||||
}
|
||||
|
||||
|
@ -297,6 +297,6 @@ func valueToRange(value any, session Session) (Range, bool) {
|
|||
return Range{}, false
|
||||
}
|
||||
|
||||
func rangeProperty(properties Properties, tag string, session Session) (Range, bool) {
|
||||
func rangeProperty(properties Properties, tag PropertyName, session Session) (Range, bool) {
|
||||
return valueToRange(properties.getRaw(tag), session)
|
||||
}
|
||||
|
|
346
propertyNames.go
346
propertyNames.go
File diff suppressed because it is too large
Load Diff
385
propertySet.go
385
propertySet.go
|
@ -6,7 +6,7 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
var colorProperties = []string{
|
||||
var colorProperties = []PropertyName{
|
||||
ColorTag,
|
||||
BackgroundColor,
|
||||
TextColor,
|
||||
|
@ -21,7 +21,7 @@ var colorProperties = []string{
|
|||
ColorPickerValue,
|
||||
}
|
||||
|
||||
func isPropertyInList(tag string, list []string) bool {
|
||||
func isPropertyInList(tag PropertyName, list []PropertyName) bool {
|
||||
for _, prop := range list {
|
||||
if prop == tag {
|
||||
return true
|
||||
|
@ -30,11 +30,11 @@ func isPropertyInList(tag string, list []string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
var angleProperties = []string{
|
||||
var angleProperties = []PropertyName{
|
||||
From,
|
||||
}
|
||||
|
||||
var boolProperties = []string{
|
||||
var boolProperties = []PropertyName{
|
||||
Disabled,
|
||||
Focusable,
|
||||
Inset,
|
||||
|
@ -63,7 +63,7 @@ var boolProperties = []string{
|
|||
ColumnSpanAll,
|
||||
}
|
||||
|
||||
var intProperties = []string{
|
||||
var intProperties = []PropertyName{
|
||||
ZIndex,
|
||||
TabSize,
|
||||
HeadHeight,
|
||||
|
@ -73,9 +73,10 @@ var intProperties = []string{
|
|||
ColumnCount,
|
||||
Order,
|
||||
TabIndex,
|
||||
MaxLength,
|
||||
}
|
||||
|
||||
var floatProperties = map[string]struct{ min, max float64 }{
|
||||
var floatProperties = map[PropertyName]struct{ min, max float64 }{
|
||||
Opacity: {min: 0, max: 1},
|
||||
NumberPickerMax: {min: -math.MaxFloat64, max: math.MaxFloat64},
|
||||
NumberPickerMin: {min: -math.MaxFloat64, max: math.MaxFloat64},
|
||||
|
@ -87,79 +88,79 @@ var floatProperties = map[string]struct{ min, max float64 }{
|
|||
VideoHeight: {min: 0, max: 10000},
|
||||
}
|
||||
|
||||
var sizeProperties = map[string]string{
|
||||
Width: Width,
|
||||
Height: Height,
|
||||
MinWidth: MinWidth,
|
||||
MinHeight: MinHeight,
|
||||
MaxWidth: MaxWidth,
|
||||
MaxHeight: MaxHeight,
|
||||
Left: Left,
|
||||
Right: Right,
|
||||
Top: Top,
|
||||
Bottom: Bottom,
|
||||
var sizeProperties = map[PropertyName]string{
|
||||
Width: string(Width),
|
||||
Height: string(Height),
|
||||
MinWidth: string(MinWidth),
|
||||
MinHeight: string(MinHeight),
|
||||
MaxWidth: string(MaxWidth),
|
||||
MaxHeight: string(MaxHeight),
|
||||
Left: string(Left),
|
||||
Right: string(Right),
|
||||
Top: string(Top),
|
||||
Bottom: string(Bottom),
|
||||
TextSize: "font-size",
|
||||
TextIndent: TextIndent,
|
||||
LetterSpacing: LetterSpacing,
|
||||
WordSpacing: WordSpacing,
|
||||
LineHeight: LineHeight,
|
||||
TextIndent: string(TextIndent),
|
||||
LetterSpacing: string(LetterSpacing),
|
||||
WordSpacing: string(WordSpacing),
|
||||
LineHeight: string(LineHeight),
|
||||
TextLineThickness: "text-decoration-thickness",
|
||||
ListRowGap: "row-gap",
|
||||
ListColumnGap: "column-gap",
|
||||
GridRowGap: GridRowGap,
|
||||
GridColumnGap: GridColumnGap,
|
||||
ColumnWidth: ColumnWidth,
|
||||
ColumnGap: ColumnGap,
|
||||
Gap: Gap,
|
||||
Margin: Margin,
|
||||
MarginLeft: MarginLeft,
|
||||
MarginRight: MarginRight,
|
||||
MarginTop: MarginTop,
|
||||
MarginBottom: MarginBottom,
|
||||
Padding: Padding,
|
||||
PaddingLeft: PaddingLeft,
|
||||
PaddingRight: PaddingRight,
|
||||
PaddingTop: PaddingTop,
|
||||
PaddingBottom: PaddingBottom,
|
||||
BorderWidth: BorderWidth,
|
||||
BorderLeftWidth: BorderLeftWidth,
|
||||
BorderRightWidth: BorderRightWidth,
|
||||
BorderTopWidth: BorderTopWidth,
|
||||
BorderBottomWidth: BorderBottomWidth,
|
||||
OutlineWidth: OutlineWidth,
|
||||
OutlineOffset: OutlineOffset,
|
||||
XOffset: XOffset,
|
||||
YOffset: YOffset,
|
||||
BlurRadius: BlurRadius,
|
||||
SpreadRadius: SpreadRadius,
|
||||
Perspective: Perspective,
|
||||
PerspectiveOriginX: PerspectiveOriginX,
|
||||
PerspectiveOriginY: PerspectiveOriginY,
|
||||
OriginX: OriginX,
|
||||
OriginY: OriginY,
|
||||
OriginZ: OriginZ,
|
||||
Radius: Radius,
|
||||
RadiusX: RadiusX,
|
||||
RadiusY: RadiusY,
|
||||
RadiusTopLeft: RadiusTopLeft,
|
||||
RadiusTopLeftX: RadiusTopLeftX,
|
||||
RadiusTopLeftY: RadiusTopLeftY,
|
||||
RadiusTopRight: RadiusTopRight,
|
||||
RadiusTopRightX: RadiusTopRightX,
|
||||
RadiusTopRightY: RadiusTopRightY,
|
||||
RadiusBottomLeft: RadiusBottomLeft,
|
||||
RadiusBottomLeftX: RadiusBottomLeftX,
|
||||
RadiusBottomLeftY: RadiusBottomLeftY,
|
||||
RadiusBottomRight: RadiusBottomRight,
|
||||
RadiusBottomRightX: RadiusBottomRightX,
|
||||
RadiusBottomRightY: RadiusBottomRightY,
|
||||
ItemWidth: ItemWidth,
|
||||
ItemHeight: ItemHeight,
|
||||
CenterX: CenterX,
|
||||
CenterY: CenterX,
|
||||
GridRowGap: string(GridRowGap),
|
||||
GridColumnGap: string(GridColumnGap),
|
||||
ColumnWidth: string(ColumnWidth),
|
||||
ColumnGap: string(ColumnGap),
|
||||
Gap: string(Gap),
|
||||
Margin: string(Margin),
|
||||
MarginLeft: string(MarginLeft),
|
||||
MarginRight: string(MarginRight),
|
||||
MarginTop: string(MarginTop),
|
||||
MarginBottom: string(MarginBottom),
|
||||
Padding: string(Padding),
|
||||
PaddingLeft: string(PaddingLeft),
|
||||
PaddingRight: string(PaddingRight),
|
||||
PaddingTop: string(PaddingTop),
|
||||
PaddingBottom: string(PaddingBottom),
|
||||
BorderWidth: string(BorderWidth),
|
||||
BorderLeftWidth: string(BorderLeftWidth),
|
||||
BorderRightWidth: string(BorderRightWidth),
|
||||
BorderTopWidth: string(BorderTopWidth),
|
||||
BorderBottomWidth: string(BorderBottomWidth),
|
||||
OutlineWidth: string(OutlineWidth),
|
||||
OutlineOffset: string(OutlineOffset),
|
||||
XOffset: string(XOffset),
|
||||
YOffset: string(YOffset),
|
||||
BlurRadius: string(BlurRadius),
|
||||
SpreadRadius: string(SpreadRadius),
|
||||
Perspective: string(Perspective),
|
||||
PerspectiveOriginX: string(PerspectiveOriginX),
|
||||
PerspectiveOriginY: string(PerspectiveOriginY),
|
||||
OriginX: string(OriginX),
|
||||
OriginY: string(OriginY),
|
||||
OriginZ: string(OriginZ),
|
||||
Radius: string(Radius),
|
||||
RadiusX: string(RadiusX),
|
||||
RadiusY: string(RadiusY),
|
||||
RadiusTopLeft: string(RadiusTopLeft),
|
||||
RadiusTopLeftX: string(RadiusTopLeftX),
|
||||
RadiusTopLeftY: string(RadiusTopLeftY),
|
||||
RadiusTopRight: string(RadiusTopRight),
|
||||
RadiusTopRightX: string(RadiusTopRightX),
|
||||
RadiusTopRightY: string(RadiusTopRightY),
|
||||
RadiusBottomLeft: string(RadiusBottomLeft),
|
||||
RadiusBottomLeftX: string(RadiusBottomLeftX),
|
||||
RadiusBottomLeftY: string(RadiusBottomLeftY),
|
||||
RadiusBottomRight: string(RadiusBottomRight),
|
||||
RadiusBottomRightX: string(RadiusBottomRightX),
|
||||
RadiusBottomRightY: string(RadiusBottomRightY),
|
||||
ItemWidth: string(ItemWidth),
|
||||
ItemHeight: string(ItemHeight),
|
||||
CenterX: string(CenterX),
|
||||
CenterY: string(CenterX),
|
||||
}
|
||||
|
||||
var enumProperties = map[string]struct {
|
||||
var enumProperties = map[PropertyName]struct {
|
||||
values []string
|
||||
cssTag string
|
||||
cssValues []string
|
||||
|
@ -176,17 +177,17 @@ var enumProperties = map[string]struct {
|
|||
},
|
||||
Overflow: {
|
||||
[]string{"hidden", "visible", "scroll", "auto"},
|
||||
Overflow,
|
||||
string(Overflow),
|
||||
[]string{"hidden", "visible", "scroll", "auto"},
|
||||
},
|
||||
TextAlign: {
|
||||
[]string{"left", "right", "center", "justify"},
|
||||
TextAlign,
|
||||
string(TextAlign),
|
||||
[]string{"left", "right", "center", "justify"},
|
||||
},
|
||||
TextTransform: {
|
||||
[]string{"none", "capitalize", "lowercase", "uppercase"},
|
||||
TextTransform,
|
||||
string(TextTransform),
|
||||
[]string{"none", "capitalize", "lowercase", "uppercase"},
|
||||
},
|
||||
TextWeight: {
|
||||
|
@ -196,27 +197,27 @@ var enumProperties = map[string]struct {
|
|||
},
|
||||
WhiteSpace: {
|
||||
[]string{"normal", "nowrap", "pre", "pre-wrap", "pre-line", "break-spaces"},
|
||||
WhiteSpace,
|
||||
string(WhiteSpace),
|
||||
[]string{"normal", "nowrap", "pre", "pre-wrap", "pre-line", "break-spaces"},
|
||||
},
|
||||
WordBreak: {
|
||||
[]string{"normal", "break-all", "keep-all", "break-word"},
|
||||
WordBreak,
|
||||
string(WordBreak),
|
||||
[]string{"normal", "break-all", "keep-all", "break-word"},
|
||||
},
|
||||
TextOverflow: {
|
||||
[]string{"clip", "ellipsis"},
|
||||
TextOverflow,
|
||||
string(TextOverflow),
|
||||
[]string{"clip", "ellipsis"},
|
||||
},
|
||||
TextWrap: {
|
||||
[]string{"wrap", "nowrap", "balance"},
|
||||
TextWrap,
|
||||
string(TextWrap),
|
||||
[]string{"wrap", "nowrap", "balance"},
|
||||
},
|
||||
WritingMode: {
|
||||
[]string{"horizontal-top-to-bottom", "horizontal-bottom-to-top", "vertical-right-to-left", "vertical-left-to-right"},
|
||||
WritingMode,
|
||||
string(WritingMode),
|
||||
[]string{"horizontal-tb", "horizontal-bt", "vertical-rl", "vertical-lr"},
|
||||
},
|
||||
TextDirection: {
|
||||
|
@ -236,7 +237,7 @@ var enumProperties = map[string]struct {
|
|||
},
|
||||
BorderStyle: {
|
||||
[]string{"none", "solid", "dashed", "dotted", "double"},
|
||||
BorderStyle,
|
||||
string(BorderStyle),
|
||||
[]string{"none", "solid", "dashed", "dotted", "double"},
|
||||
},
|
||||
TopStyle: {
|
||||
|
@ -261,7 +262,7 @@ var enumProperties = map[string]struct {
|
|||
},
|
||||
OutlineStyle: {
|
||||
[]string{"none", "solid", "dashed", "dotted", "double"},
|
||||
OutlineStyle,
|
||||
string(OutlineStyle),
|
||||
[]string{"none", "solid", "dashed", "dotted", "double"},
|
||||
},
|
||||
Tabs: {
|
||||
|
@ -336,7 +337,7 @@ var enumProperties = map[string]struct {
|
|||
},
|
||||
GridAutoFlow: {
|
||||
[]string{"row", "column", "row-dense", "column-dense"},
|
||||
GridAutoFlow,
|
||||
string(GridAutoFlow),
|
||||
[]string{"row", "column", "row dense", "column dense"},
|
||||
},
|
||||
ImageVerticalAlign: {
|
||||
|
@ -376,7 +377,7 @@ var enumProperties = map[string]struct {
|
|||
},
|
||||
Cursor: {
|
||||
[]string{"auto", "default", "none", "context-menu", "help", "pointer", "progress", "wait", "cell", "crosshair", "text", "vertical-text", "alias", "copy", "move", "no-drop", "not-allowed", "e-resize", "n-resize", "ne-resize", "nw-resize", "s-resize", "se-resize", "sw-resize", "w-resize", "ew-resize", "ns-resize", "nesw-resize", "nwse-resize", "col-resize", "row-resize", "all-scroll", "zoom-in", "zoom-out", "grab", "grabbing"},
|
||||
Cursor,
|
||||
string(Cursor),
|
||||
[]string{"auto", "default", "none", "context-menu", "help", "pointer", "progress", "wait", "cell", "crosshair", "text", "vertical-text", "alias", "copy", "move", "no-drop", "not-allowed", "e-resize", "n-resize", "ne-resize", "nw-resize", "s-resize", "se-resize", "sw-resize", "w-resize", "ew-resize", "ns-resize", "nesw-resize", "nwse-resize", "col-resize", "row-resize", "all-scroll", "zoom-in", "zoom-out", "grab", "grabbing"},
|
||||
},
|
||||
Fit: {
|
||||
|
@ -456,27 +457,27 @@ var enumProperties = map[string]struct {
|
|||
},
|
||||
MixBlendMode: {
|
||||
[]string{"normal", "multiply", "screen", "overlay", "darken", "lighten", "color-dodge", "color-burn", "hard-light", "soft-light", "difference", "exclusion", "hue", "saturation", "color", "luminosity"},
|
||||
MixBlendMode,
|
||||
string(MixBlendMode),
|
||||
[]string{"normal", "multiply", "screen", "overlay", "darken", "lighten", "color-dodge", "color-burn", "hard-light", "soft-light", "difference", "exclusion", "hue", "saturation", "color", "luminosity"},
|
||||
},
|
||||
BackgroundBlendMode: {
|
||||
[]string{"normal", "multiply", "screen", "overlay", "darken", "lighten", "color-dodge", "color-burn", "hard-light", "soft-light", "difference", "exclusion", "hue", "saturation", "color", "luminosity"},
|
||||
BackgroundBlendMode,
|
||||
string(BackgroundBlendMode),
|
||||
[]string{"normal", "multiply", "screen", "overlay", "darken", "lighten", "color-dodge", "color-burn", "hard-light", "soft-light", "difference", "exclusion", "hue", "saturation", "color", "luminosity"},
|
||||
},
|
||||
ColumnFill: {
|
||||
[]string{"balance", "auto"},
|
||||
ColumnFill,
|
||||
string(ColumnFill),
|
||||
[]string{"balance", "auto"},
|
||||
},
|
||||
}
|
||||
|
||||
func notCompatibleType(tag string, value any) {
|
||||
ErrorLogF(`"%T" type not compatible with "%s" property`, value, tag)
|
||||
func notCompatibleType(tag PropertyName, value any) {
|
||||
ErrorLogF(`"%T" type not compatible with "%s" property`, value, string(tag))
|
||||
}
|
||||
|
||||
func invalidPropertyValue(tag string, value any) {
|
||||
ErrorLogF(`Invalid value "%v" of "%s" property`, value, tag)
|
||||
func invalidPropertyValue(tag PropertyName, value any) {
|
||||
ErrorLogF(`Invalid value "%v" of "%s" property`, value, string(tag))
|
||||
}
|
||||
|
||||
func isConstantName(text string) bool {
|
||||
|
@ -537,26 +538,48 @@ func isInt(value any) (int, bool) {
|
|||
return n, true
|
||||
}
|
||||
|
||||
func (properties *propertyList) setSimpleProperty(tag string, value any) bool {
|
||||
func setSimpleProperty(properties Properties, tag PropertyName, value any) bool {
|
||||
if value == nil {
|
||||
delete(properties.properties, tag)
|
||||
properties.setRaw(tag, nil)
|
||||
return true
|
||||
} else if text, ok := value.(string); ok {
|
||||
text = strings.Trim(text, " \t\n\r")
|
||||
if text == "" {
|
||||
delete(properties.properties, tag)
|
||||
properties.setRaw(tag, nil)
|
||||
return true
|
||||
}
|
||||
if isConstantName(text) {
|
||||
properties.properties[tag] = text
|
||||
properties.setRaw(tag, text)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (properties *propertyList) setSizeProperty(tag string, value any) bool {
|
||||
if !properties.setSimpleProperty(tag, value) {
|
||||
func setStringPropertyValue(properties Properties, tag PropertyName, text any) []PropertyName {
|
||||
if text != "" {
|
||||
properties.setRaw(tag, text)
|
||||
} else if properties.getRaw(tag) != nil {
|
||||
properties.setRaw(tag, nil)
|
||||
} else {
|
||||
return []PropertyName{}
|
||||
}
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
func setArrayPropertyValue[T any](properties Properties, tag PropertyName, value []T) []PropertyName {
|
||||
if len(value) > 0 {
|
||||
properties.setRaw(tag, value)
|
||||
} else if properties.getRaw(tag) != nil {
|
||||
properties.setRaw(tag, nil)
|
||||
} else {
|
||||
return []PropertyName{}
|
||||
}
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
func setSizeProperty(properties Properties, tag PropertyName, value any) []PropertyName {
|
||||
if !setSimpleProperty(properties, tag, value) {
|
||||
var size SizeUnit
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
|
@ -566,7 +589,7 @@ func (properties *propertyList) setSizeProperty(tag string, value any) bool {
|
|||
size.Function = fn
|
||||
} else if size, ok = StringToSizeUnit(value); !ok {
|
||||
invalidPropertyValue(tag, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
case SizeUnit:
|
||||
size = value
|
||||
|
@ -589,29 +612,29 @@ func (properties *propertyList) setSizeProperty(tag string, value any) bool {
|
|||
size.Value = float64(n)
|
||||
} else {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if size.Type == Auto {
|
||||
delete(properties.properties, tag)
|
||||
properties.setRaw(tag, nil)
|
||||
} else {
|
||||
properties.properties[tag] = size
|
||||
properties.setRaw(tag, size)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
func (properties *propertyList) setAngleProperty(tag string, value any) bool {
|
||||
if !properties.setSimpleProperty(tag, value) {
|
||||
func setAngleProperty(properties Properties, tag PropertyName, value any) []PropertyName {
|
||||
if !setSimpleProperty(properties, tag, value) {
|
||||
var angle AngleUnit
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
var ok bool
|
||||
if angle, ok = StringToAngleUnit(value); !ok {
|
||||
invalidPropertyValue(tag, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
case AngleUnit:
|
||||
angle = value
|
||||
|
@ -627,24 +650,24 @@ func (properties *propertyList) setAngleProperty(tag string, value any) bool {
|
|||
angle = Rad(float64(n))
|
||||
} else {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
}
|
||||
properties.properties[tag] = angle
|
||||
properties.setRaw(tag, angle)
|
||||
}
|
||||
|
||||
return true
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
func (properties *propertyList) setColorProperty(tag string, value any) bool {
|
||||
if !properties.setSimpleProperty(tag, value) {
|
||||
func setColorProperty(properties Properties, tag PropertyName, value any) []PropertyName {
|
||||
if !setSimpleProperty(properties, tag, value) {
|
||||
var result Color
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
var err error
|
||||
if result, err = stringToColor(value); err != nil {
|
||||
invalidPropertyValue(tag, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
case Color:
|
||||
result = value
|
||||
|
@ -654,105 +677,101 @@ func (properties *propertyList) setColorProperty(tag string, value any) bool {
|
|||
result = Color(color)
|
||||
} else {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if result == 0 {
|
||||
delete(properties.properties, tag)
|
||||
} else {
|
||||
properties.properties[tag] = result
|
||||
}
|
||||
properties.setRaw(tag, result)
|
||||
}
|
||||
|
||||
return true
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
func (properties *propertyList) setEnumProperty(tag string, value any, values []string) bool {
|
||||
if !properties.setSimpleProperty(tag, value) {
|
||||
func setEnumProperty(properties Properties, tag PropertyName, value any, values []string) []PropertyName {
|
||||
if !setSimpleProperty(properties, tag, value) {
|
||||
var n int
|
||||
if text, ok := value.(string); ok {
|
||||
if n, ok = enumStringToInt(text, values, false); !ok {
|
||||
invalidPropertyValue(tag, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
} else if i, ok := isInt(value); ok {
|
||||
if i < 0 || i >= len(values) {
|
||||
invalidPropertyValue(tag, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
n = i
|
||||
} else {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
properties.properties[tag] = n
|
||||
properties.setRaw(tag, n)
|
||||
}
|
||||
|
||||
return true
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
func (properties *propertyList) setBoolProperty(tag string, value any) bool {
|
||||
if !properties.setSimpleProperty(tag, value) {
|
||||
func setBoolProperty(properties Properties, tag PropertyName, value any) []PropertyName {
|
||||
if !setSimpleProperty(properties, tag, value) {
|
||||
if text, ok := value.(string); ok {
|
||||
switch strings.ToLower(strings.Trim(text, " \t")) {
|
||||
case "true", "yes", "on", "1":
|
||||
properties.properties[tag] = true
|
||||
properties.setRaw(tag, true)
|
||||
|
||||
case "false", "no", "off", "0":
|
||||
properties.properties[tag] = false
|
||||
properties.setRaw(tag, false)
|
||||
|
||||
default:
|
||||
invalidPropertyValue(tag, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
} else if n, ok := isInt(value); ok {
|
||||
switch n {
|
||||
case 1:
|
||||
properties.properties[tag] = true
|
||||
properties.setRaw(tag, true)
|
||||
|
||||
case 0:
|
||||
properties.properties[tag] = false
|
||||
properties.setRaw(tag, false)
|
||||
|
||||
default:
|
||||
invalidPropertyValue(tag, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
} else if b, ok := value.(bool); ok {
|
||||
properties.properties[tag] = b
|
||||
properties.setRaw(tag, b)
|
||||
} else {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
func (properties *propertyList) setIntProperty(tag string, value any) bool {
|
||||
if !properties.setSimpleProperty(tag, value) {
|
||||
func setIntProperty(properties Properties, tag PropertyName, value any) []PropertyName {
|
||||
if !setSimpleProperty(properties, tag, value) {
|
||||
if text, ok := value.(string); ok {
|
||||
n, err := strconv.Atoi(strings.Trim(text, " \t"))
|
||||
if err != nil {
|
||||
invalidPropertyValue(tag, value)
|
||||
ErrorLog(err.Error())
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
properties.properties[tag] = n
|
||||
properties.setRaw(tag, n)
|
||||
} else if n, ok := isInt(value); ok {
|
||||
properties.properties[tag] = n
|
||||
properties.setRaw(tag, n)
|
||||
} else {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
func (properties *propertyList) setFloatProperty(tag string, value any, min, max float64) bool {
|
||||
if !properties.setSimpleProperty(tag, value) {
|
||||
func setFloatProperty(properties Properties, tag PropertyName, value any, min, max float64) []PropertyName {
|
||||
if !setSimpleProperty(properties, tag, value) {
|
||||
f := float64(0)
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
|
@ -760,14 +779,14 @@ func (properties *propertyList) setFloatProperty(tag string, value any, min, max
|
|||
if f, err = strconv.ParseFloat(strings.Trim(value, " \t"), 64); err != nil {
|
||||
invalidPropertyValue(tag, value)
|
||||
ErrorLog(err.Error())
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
if f < min || f > max {
|
||||
ErrorLogF(`"%T" out of range of "%s" property`, value, tag)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
properties.properties[tag] = value
|
||||
return true
|
||||
properties.setRaw(tag, value)
|
||||
return nil
|
||||
|
||||
case float32:
|
||||
f = float64(value)
|
||||
|
@ -780,64 +799,84 @@ func (properties *propertyList) setFloatProperty(tag string, value any, min, max
|
|||
f = float64(n)
|
||||
} else {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if f >= min && f <= max {
|
||||
properties.properties[tag] = f
|
||||
properties.setRaw(tag, f)
|
||||
} else {
|
||||
ErrorLogF(`"%T" out of range of "%s" property`, value, tag)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
func (properties *propertyList) Set(tag string, value any) bool {
|
||||
return properties.set(strings.ToLower(tag), value)
|
||||
}
|
||||
|
||||
func (properties *propertyList) set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
delete(properties.properties, tag)
|
||||
return true
|
||||
}
|
||||
|
||||
func propertiesSet(properties Properties, tag PropertyName, value any) []PropertyName {
|
||||
if _, ok := sizeProperties[tag]; ok {
|
||||
return properties.setSizeProperty(tag, value)
|
||||
return setSizeProperty(properties, tag, value)
|
||||
}
|
||||
|
||||
if valuesData, ok := enumProperties[tag]; ok {
|
||||
return properties.setEnumProperty(tag, value, valuesData.values)
|
||||
return setEnumProperty(properties, tag, value, valuesData.values)
|
||||
}
|
||||
|
||||
if limits, ok := floatProperties[tag]; ok {
|
||||
return properties.setFloatProperty(tag, value, limits.min, limits.max)
|
||||
return setFloatProperty(properties, tag, value, limits.min, limits.max)
|
||||
}
|
||||
|
||||
if isPropertyInList(tag, colorProperties) {
|
||||
return properties.setColorProperty(tag, value)
|
||||
return setColorProperty(properties, tag, value)
|
||||
}
|
||||
|
||||
if isPropertyInList(tag, angleProperties) {
|
||||
return properties.setAngleProperty(tag, value)
|
||||
return setAngleProperty(properties, tag, value)
|
||||
}
|
||||
|
||||
if isPropertyInList(tag, boolProperties) {
|
||||
return properties.setBoolProperty(tag, value)
|
||||
return setBoolProperty(properties, tag, value)
|
||||
}
|
||||
|
||||
if isPropertyInList(tag, intProperties) {
|
||||
return properties.setIntProperty(tag, value)
|
||||
return setIntProperty(properties, tag, value)
|
||||
}
|
||||
|
||||
if text, ok := value.(string); ok {
|
||||
properties.properties[tag] = text
|
||||
return true
|
||||
properties.setRaw(tag, text)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
notCompatibleType(tag, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
func (properties *propertyList) Set(tag PropertyName, value any) bool {
|
||||
tag = properties.normalize(tag)
|
||||
if value == nil {
|
||||
properties.remove(properties, tag)
|
||||
return true
|
||||
}
|
||||
|
||||
return properties.set(properties, tag, value) != nil
|
||||
}
|
||||
*/
|
||||
|
||||
func (data *dataProperty) Set(tag PropertyName, value any) bool {
|
||||
if value == nil {
|
||||
data.Remove(tag)
|
||||
return true
|
||||
}
|
||||
|
||||
tag = data.normalize(tag)
|
||||
for _, supported := range data.supportedProperties {
|
||||
if tag == supported {
|
||||
return data.set(data, tag, value) != nil
|
||||
}
|
||||
}
|
||||
|
||||
ErrorLogF(`"%s" property is not supported`, string(tag))
|
||||
return false
|
||||
}
|
||||
|
|
383
radius.go
383
radius.go
|
@ -37,7 +37,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
Radius = "radius"
|
||||
Radius PropertyName = "radius"
|
||||
|
||||
// RadiusX is the constant for "radius-x" property tag.
|
||||
//
|
||||
|
@ -58,7 +58,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
RadiusX = "radius-x"
|
||||
RadiusX PropertyName = "radius-x"
|
||||
|
||||
// RadiusY is the constant for "radius-y" property tag.
|
||||
//
|
||||
|
@ -79,7 +79,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
RadiusY = "radius-y"
|
||||
RadiusY PropertyName = "radius-y"
|
||||
|
||||
// RadiusTopLeft is the constant for "radius-top-left" property tag.
|
||||
//
|
||||
|
@ -90,7 +90,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
RadiusTopLeft = "radius-top-left"
|
||||
RadiusTopLeft PropertyName = "radius-top-left"
|
||||
|
||||
// RadiusTopLeftX is the constant for "radius-top-left-x" property tag.
|
||||
//
|
||||
|
@ -101,7 +101,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
RadiusTopLeftX = "radius-top-left-x"
|
||||
RadiusTopLeftX PropertyName = "radius-top-left-x"
|
||||
|
||||
// RadiusTopLeftY is the constant for "radius-top-left-y" property tag.
|
||||
//
|
||||
|
@ -112,7 +112,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
RadiusTopLeftY = "radius-top-left-y"
|
||||
RadiusTopLeftY PropertyName = "radius-top-left-y"
|
||||
|
||||
// RadiusTopRight is the constant for "radius-top-right" property tag.
|
||||
//
|
||||
|
@ -123,7 +123,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
RadiusTopRight = "radius-top-right"
|
||||
RadiusTopRight PropertyName = "radius-top-right"
|
||||
|
||||
// RadiusTopRightX is the constant for "radius-top-right-x" property tag.
|
||||
//
|
||||
|
@ -134,7 +134,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
RadiusTopRightX = "radius-top-right-x"
|
||||
RadiusTopRightX PropertyName = "radius-top-right-x"
|
||||
|
||||
// RadiusTopRightY is the constant for "radius-top-right-y" property tag.
|
||||
//
|
||||
|
@ -145,7 +145,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
RadiusTopRightY = "radius-top-right-y"
|
||||
RadiusTopRightY PropertyName = "radius-top-right-y"
|
||||
|
||||
// RadiusBottomLeft is the constant for "radius-bottom-left" property tag.
|
||||
//
|
||||
|
@ -156,7 +156,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
RadiusBottomLeft = "radius-bottom-left"
|
||||
RadiusBottomLeft PropertyName = "radius-bottom-left"
|
||||
|
||||
// RadiusBottomLeftX is the constant for "radius-bottom-left-x" property tag.
|
||||
//
|
||||
|
@ -167,7 +167,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
RadiusBottomLeftX = "radius-bottom-left-x"
|
||||
RadiusBottomLeftX PropertyName = "radius-bottom-left-x"
|
||||
|
||||
// RadiusBottomLeftY is the constant for "radius-bottom-left-y" property tag.
|
||||
//
|
||||
|
@ -178,7 +178,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
RadiusBottomLeftY = "radius-bottom-left-y"
|
||||
RadiusBottomLeftY PropertyName = "radius-bottom-left-y"
|
||||
|
||||
// RadiusBottomRight is the constant for "radius-bottom-right" property tag.
|
||||
//
|
||||
|
@ -189,7 +189,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
RadiusBottomRight = "radius-bottom-right"
|
||||
RadiusBottomRight PropertyName = "radius-bottom-right"
|
||||
|
||||
// RadiusBottomRightX is the constant for "radius-bottom-right-x" property tag.
|
||||
//
|
||||
|
@ -200,7 +200,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
RadiusBottomRightX = "radius-bottom-right-x"
|
||||
RadiusBottomRightX PropertyName = "radius-bottom-right-x"
|
||||
|
||||
// RadiusBottomRightY is the constant for "radius-bottom-right-y" property tag.
|
||||
//
|
||||
|
@ -211,7 +211,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
RadiusBottomRightY = "radius-bottom-right-y"
|
||||
RadiusBottomRightY PropertyName = "radius-bottom-right-y"
|
||||
|
||||
// X is the constant for "x" property tag.
|
||||
//
|
||||
|
@ -232,7 +232,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
X = "x"
|
||||
X PropertyName = "x"
|
||||
|
||||
// Y is the constant for "y" property tag.
|
||||
//
|
||||
|
@ -253,7 +253,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
Y = "y"
|
||||
Y PropertyName = "y"
|
||||
|
||||
// TopLeft is the constant for "top-left" property tag.
|
||||
//
|
||||
|
@ -264,7 +264,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
TopLeft = "top-left"
|
||||
TopLeft PropertyName = "top-left"
|
||||
|
||||
// TopLeftX is the constant for "top-left-x" property tag.
|
||||
//
|
||||
|
@ -275,7 +275,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
TopLeftX = "top-left-x"
|
||||
TopLeftX PropertyName = "top-left-x"
|
||||
|
||||
// TopLeftY is the constant for "top-left-y" property tag.
|
||||
//
|
||||
|
@ -286,7 +286,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
TopLeftY = "top-left-y"
|
||||
TopLeftY PropertyName = "top-left-y"
|
||||
|
||||
// TopRight is the constant for "top-right" property tag.
|
||||
//
|
||||
|
@ -297,7 +297,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
TopRight = "top-right"
|
||||
TopRight PropertyName = "top-right"
|
||||
|
||||
// TopRightX is the constant for "top-right-x" property tag.
|
||||
//
|
||||
|
@ -308,7 +308,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
TopRightX = "top-right-x"
|
||||
TopRightX PropertyName = "top-right-x"
|
||||
|
||||
// TopRightY is the constant for "top-right-y" property tag.
|
||||
//
|
||||
|
@ -319,7 +319,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
TopRightY = "top-right-y"
|
||||
TopRightY PropertyName = "top-right-y"
|
||||
|
||||
// BottomLeft is the constant for "bottom-left" property tag.
|
||||
//
|
||||
|
@ -330,7 +330,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
BottomLeft = "bottom-left"
|
||||
BottomLeft PropertyName = "bottom-left"
|
||||
|
||||
// BottomLeftX is the constant for "bottom-left-x" property tag.
|
||||
//
|
||||
|
@ -341,7 +341,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
BottomLeftX = "bottom-left-x"
|
||||
BottomLeftX PropertyName = "bottom-left-x"
|
||||
|
||||
// BottomLeftY is the constant for "bottom-left-y" property tag.
|
||||
//
|
||||
|
@ -352,7 +352,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
BottomLeftY = "bottom-left-y"
|
||||
BottomLeftY PropertyName = "bottom-left-y"
|
||||
|
||||
// BottomRight is the constant for "bottom-right" property tag.
|
||||
//
|
||||
|
@ -363,7 +363,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
BottomRight = "bottom-right"
|
||||
BottomRight PropertyName = "bottom-right"
|
||||
|
||||
// BottomRightX is the constant for "bottom-right-x" property tag.
|
||||
//
|
||||
|
@ -374,7 +374,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
BottomRightX = "bottom-right-x"
|
||||
BottomRightX PropertyName = "bottom-right-x"
|
||||
|
||||
// BottomRightY is the constant for "bottom-right-y" property tag.
|
||||
//
|
||||
|
@ -385,7 +385,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
BottomRightY = "bottom-right-y"
|
||||
BottomRightY PropertyName = "bottom-right-y"
|
||||
)
|
||||
|
||||
// RadiusProperty is a description of the [View] (shape) elliptical corner radius.
|
||||
|
@ -399,38 +399,46 @@ type RadiusProperty interface {
|
|||
}
|
||||
|
||||
type radiusPropertyData struct {
|
||||
propertyList
|
||||
dataProperty
|
||||
}
|
||||
|
||||
// NewRadiusProperty creates the new RadiusProperty
|
||||
func NewRadiusProperty(params Params) RadiusProperty {
|
||||
result := new(radiusPropertyData)
|
||||
result.properties = map[string]any{}
|
||||
result.dataProperty.init()
|
||||
result.normalize = radiusPropertyNormalize
|
||||
result.get = radiusPropertyGet
|
||||
result.remove = radiusPropertyRemove
|
||||
result.set = radiusPropertySet
|
||||
result.supportedProperties = []PropertyName{
|
||||
X, Y, TopLeft, TopRight, BottomLeft, BottomRight, TopLeftX, TopLeftY,
|
||||
TopRightX, TopRightY, BottomLeftX, BottomLeftY, BottomRightX, BottomRightY,
|
||||
}
|
||||
|
||||
if params != nil {
|
||||
for _, tag := range []string{X, Y, TopLeft, TopRight, BottomLeft, BottomRight, TopLeftX, TopLeftY,
|
||||
TopRightX, TopRightY, BottomLeftX, BottomLeftY, BottomRightX, BottomRightY} {
|
||||
for _, tag := range result.supportedProperties {
|
||||
if value, ok := params[tag]; ok {
|
||||
result.Set(tag, value)
|
||||
radiusPropertySet(result, tag, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (radius *radiusPropertyData) normalizeTag(tag string) string {
|
||||
return strings.TrimPrefix(strings.ToLower(tag), "radius-")
|
||||
func radiusPropertyNormalize(tag PropertyName) PropertyName {
|
||||
name := strings.TrimPrefix(strings.ToLower(string(tag)), "radius-")
|
||||
return PropertyName(name)
|
||||
}
|
||||
|
||||
func (radius *radiusPropertyData) writeString(buffer *strings.Builder, indent string) {
|
||||
buffer.WriteString("_{ ")
|
||||
comma := false
|
||||
for _, tag := range []string{X, Y, TopLeft, TopLeftX, TopLeftY, TopRight, TopRightX, TopRightY,
|
||||
BottomLeft, BottomLeftX, BottomLeftY, BottomRight, BottomRightX, BottomRightY} {
|
||||
for _, tag := range radius.supportedProperties {
|
||||
if value, ok := radius.properties[tag]; ok {
|
||||
if comma {
|
||||
buffer.WriteString(", ")
|
||||
}
|
||||
buffer.WriteString(tag)
|
||||
buffer.WriteString(string(tag))
|
||||
buffer.WriteString(" = ")
|
||||
writePropertyValue(buffer, tag, value, indent)
|
||||
comma = true
|
||||
|
@ -444,26 +452,54 @@ func (radius *radiusPropertyData) String() string {
|
|||
return runStringWriter(radius)
|
||||
}
|
||||
|
||||
func (radius *radiusPropertyData) delete(tags []string) {
|
||||
for _, tag := range tags {
|
||||
delete(radius.properties, tag)
|
||||
func radiusPropertyRemove(properties Properties, tag PropertyName) []PropertyName {
|
||||
result := []PropertyName{}
|
||||
removeTag := func(tag PropertyName) {
|
||||
if properties.getRaw(tag) != nil {
|
||||
properties.setRaw(tag, nil)
|
||||
result = append(result, tag)
|
||||
}
|
||||
}
|
||||
|
||||
switch tag {
|
||||
case X, Y:
|
||||
if properties.getRaw(tag) == nil {
|
||||
for _, prefix := range []PropertyName{TopLeft, TopRight, BottomLeft, BottomRight} {
|
||||
removeTag(prefix + "-" + tag)
|
||||
}
|
||||
} else {
|
||||
removeTag(tag)
|
||||
}
|
||||
|
||||
case TopLeftX, TopLeftY, TopRightX, TopRightY, BottomLeftX, BottomLeftY, BottomRightX, BottomRightY:
|
||||
removeTag(tag)
|
||||
|
||||
case TopLeft, TopRight, BottomLeft, BottomRight:
|
||||
for _, tag := range []PropertyName{tag, tag + "-x", tag + "-y"} {
|
||||
removeTag(tag)
|
||||
}
|
||||
|
||||
default:
|
||||
ErrorLogF(`"%s" property is not compatible with the RadiusProperty`, tag)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (radius *radiusPropertyData) deleteUnusedTags() {
|
||||
for _, tag := range []string{X, Y} {
|
||||
if _, ok := radius.properties[tag]; ok {
|
||||
func deleteRadiusUnusedTags(radius Properties, result []PropertyName) {
|
||||
|
||||
for _, tag := range []PropertyName{X, Y} {
|
||||
if radius.getRaw(tag) != nil {
|
||||
unused := true
|
||||
for _, t := range []string{TopLeft, TopRight, BottomLeft, BottomRight} {
|
||||
if _, ok := radius.properties[t+"-"+tag]; !ok {
|
||||
if _, ok := radius.properties[t]; !ok {
|
||||
unused = false
|
||||
break
|
||||
}
|
||||
for _, t := range []PropertyName{TopLeft, TopRight, BottomLeft, BottomRight} {
|
||||
if radius.getRaw(t+"-"+tag) == nil && radius.getRaw(t) == nil {
|
||||
unused = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if unused {
|
||||
delete(radius.properties, tag)
|
||||
radius.setRaw(tag, nil)
|
||||
result = append(result, tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -485,124 +521,122 @@ func (radius *radiusPropertyData) deleteUnusedTags() {
|
|||
return false
|
||||
}
|
||||
|
||||
for _, tag := range []string{TopLeft, TopRight, BottomLeft, BottomRight} {
|
||||
for _, tag := range []PropertyName{TopLeft, TopRight, BottomLeft, BottomRight} {
|
||||
tagX := tag + "-x"
|
||||
tagY := tag + "-y"
|
||||
valueX, okX := radius.properties[tagX]
|
||||
valueY, okY := radius.properties[tagY]
|
||||
valueX := radius.getRaw(tagX)
|
||||
valueY := radius.getRaw(tagY)
|
||||
|
||||
if value, ok := radius.properties[tag]; ok {
|
||||
if okX && okY {
|
||||
delete(radius.properties, tag)
|
||||
} else if okX && !okY {
|
||||
if value := radius.getRaw(tag); value != nil {
|
||||
if valueX != nil && valueY != nil {
|
||||
radius.setRaw(tag, nil)
|
||||
result = append(result, tag)
|
||||
} else if valueX != nil && valueY == nil {
|
||||
if equalValue(value, valueX) {
|
||||
delete(radius.properties, tagX)
|
||||
radius.setRaw(tagX, nil)
|
||||
result = append(result, tagX)
|
||||
} else {
|
||||
radius.properties[tagY] = value
|
||||
delete(radius.properties, tag)
|
||||
radius.setRaw(tagY, value)
|
||||
result = append(result, tagY)
|
||||
radius.setRaw(tag, nil)
|
||||
result = append(result, tag)
|
||||
}
|
||||
} else if !okX && okY {
|
||||
} else if valueX == nil && valueY != nil {
|
||||
if equalValue(value, valueY) {
|
||||
delete(radius.properties, tagY)
|
||||
radius.setRaw(tagY, nil)
|
||||
result = append(result, tagY)
|
||||
} else {
|
||||
radius.properties[tagX] = value
|
||||
delete(radius.properties, tag)
|
||||
radius.setRaw(tagX, value)
|
||||
result = append(result, tagX)
|
||||
radius.setRaw(tag, nil)
|
||||
result = append(result, tag)
|
||||
}
|
||||
}
|
||||
} else if okX && okY && equalValue(valueX, valueY) {
|
||||
radius.properties[tag] = valueX
|
||||
delete(radius.properties, tagX)
|
||||
delete(radius.properties, tagY)
|
||||
} else if valueX != nil && valueY != nil && equalValue(valueX, valueY) {
|
||||
radius.setRaw(tag, valueX)
|
||||
result = append(result, tag)
|
||||
radius.setRaw(tagX, nil)
|
||||
result = append(result, tagX)
|
||||
radius.setRaw(tagY, nil)
|
||||
result = append(result, tagY)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (radius *radiusPropertyData) Remove(tag string) {
|
||||
tag = radius.normalizeTag(tag)
|
||||
func radiusPropertySet(radius Properties, tag PropertyName, value any) []PropertyName {
|
||||
var result []PropertyName = nil
|
||||
|
||||
switch tag {
|
||||
case X, Y:
|
||||
if _, ok := radius.properties[tag]; ok {
|
||||
radius.Set(tag, AutoSize())
|
||||
delete(radius.properties, tag)
|
||||
deleteTags := func(tags []PropertyName) {
|
||||
for _, tag := range tags {
|
||||
if radius.getRaw(tag) != nil {
|
||||
radius.setRaw(tag, nil)
|
||||
result = append(result, tag)
|
||||
}
|
||||
}
|
||||
|
||||
case TopLeftX, TopLeftY, TopRightX, TopRightY, BottomLeftX, BottomLeftY, BottomRightX, BottomRightY:
|
||||
delete(radius.properties, tag)
|
||||
|
||||
case TopLeft, TopRight, BottomLeft, BottomRight:
|
||||
radius.delete([]string{tag, tag + "-x", tag + "-y"})
|
||||
|
||||
default:
|
||||
ErrorLogF(`"%s" property is not compatible with the RadiusProperty`, tag)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (radius *radiusPropertyData) Set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
radius.Remove(tag)
|
||||
return true
|
||||
}
|
||||
|
||||
tag = radius.normalizeTag(tag)
|
||||
switch tag {
|
||||
case X:
|
||||
if radius.setSizeProperty(tag, value) {
|
||||
radius.delete([]string{TopLeftX, TopRightX, BottomLeftX, BottomRightX})
|
||||
for _, t := range []string{TopLeft, TopRight, BottomLeft, BottomRight} {
|
||||
if val, ok := radius.properties[t]; ok {
|
||||
if _, ok := radius.properties[t+"-y"]; !ok {
|
||||
radius.properties[t+"-y"] = val
|
||||
if result = setSizeProperty(radius, tag, value); result != nil {
|
||||
deleteTags([]PropertyName{TopLeftX, TopRightX, BottomLeftX, BottomRightX})
|
||||
for _, t := range []PropertyName{TopLeft, TopRight, BottomLeft, BottomRight} {
|
||||
if val := radius.getRaw(t); val != nil {
|
||||
t2 := t + "-y"
|
||||
if radius.getRaw(t2) != nil {
|
||||
radius.setRaw(t2, val)
|
||||
result = append(result, t2)
|
||||
}
|
||||
delete(radius.properties, t)
|
||||
radius.setRaw(t, nil)
|
||||
result = append(result, t)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
case Y:
|
||||
if radius.setSizeProperty(tag, value) {
|
||||
radius.delete([]string{TopLeftY, TopRightY, BottomLeftY, BottomRightY})
|
||||
for _, t := range []string{TopLeft, TopRight, BottomLeft, BottomRight} {
|
||||
if val, ok := radius.properties[t]; ok {
|
||||
if _, ok := radius.properties[t+"-x"]; !ok {
|
||||
radius.properties[t+"-x"] = val
|
||||
if result = setSizeProperty(radius, tag, value); result != nil {
|
||||
deleteTags([]PropertyName{TopLeftY, TopRightY, BottomLeftY, BottomRightY})
|
||||
for _, t := range []PropertyName{TopLeft, TopRight, BottomLeft, BottomRight} {
|
||||
if val := radius.getRaw(t); val != nil {
|
||||
t2 := t + "-x"
|
||||
if radius.getRaw(t2) != nil {
|
||||
radius.setRaw(t2, val)
|
||||
result = append(result, t2)
|
||||
}
|
||||
delete(radius.properties, t)
|
||||
radius.setRaw(t, nil)
|
||||
result = append(result, t)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
case TopLeftX, TopLeftY, TopRightX, TopRightY, BottomLeftX, BottomLeftY, BottomRightX, BottomRightY:
|
||||
if radius.setSizeProperty(tag, value) {
|
||||
radius.deleteUnusedTags()
|
||||
return true
|
||||
if result = setSizeProperty(radius, tag, value); result != nil {
|
||||
deleteRadiusUnusedTags(radius, result)
|
||||
}
|
||||
|
||||
case TopLeft, TopRight, BottomLeft, BottomRight:
|
||||
switch value := value.(type) {
|
||||
case SizeUnit:
|
||||
radius.properties[tag] = value
|
||||
radius.delete([]string{tag + "-x", tag + "-y"})
|
||||
radius.deleteUnusedTags()
|
||||
return true
|
||||
radius.setRaw(tag, value)
|
||||
result = []PropertyName{tag}
|
||||
deleteTags([]PropertyName{tag + "-x", tag + "-y"})
|
||||
deleteRadiusUnusedTags(radius, result)
|
||||
|
||||
case string:
|
||||
if strings.Contains(value, "/") {
|
||||
if values := strings.Split(value, "/"); len(values) == 2 {
|
||||
xOK := radius.Set(tag+"-x", value[0])
|
||||
yOK := radius.Set(tag+"-y", value[1])
|
||||
return xOK && yOK
|
||||
if result = radiusPropertySet(radius, tag+"-x", value[0]); result != nil {
|
||||
if resultY := radiusPropertySet(radius, tag+"-y", value[1]); resultY != nil {
|
||||
result = append(result, resultY...)
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
notCompatibleType(tag, value)
|
||||
}
|
||||
} else {
|
||||
if radius.setSizeProperty(tag, value) {
|
||||
radius.delete([]string{tag + "-x", tag + "-y"})
|
||||
radius.deleteUnusedTags()
|
||||
return true
|
||||
if result = setSizeProperty(radius, tag, value); result != nil {
|
||||
deleteTags([]PropertyName{tag + "-x", tag + "-y"})
|
||||
deleteRadiusUnusedTags(radius, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -611,33 +645,32 @@ func (radius *radiusPropertyData) Set(tag string, value any) bool {
|
|||
ErrorLogF(`"%s" property is not compatible with the RadiusProperty`, tag)
|
||||
}
|
||||
|
||||
return false
|
||||
return result
|
||||
}
|
||||
|
||||
func (radius *radiusPropertyData) Get(tag string) any {
|
||||
tag = radius.normalizeTag(tag)
|
||||
if value, ok := radius.properties[tag]; ok {
|
||||
func radiusPropertyGet(properties Properties, tag PropertyName) any {
|
||||
if value := properties.getRaw(tag); value != nil {
|
||||
return value
|
||||
}
|
||||
|
||||
switch tag {
|
||||
case TopLeftX, TopLeftY, TopRightX, TopRightY, BottomLeftX, BottomLeftY, BottomRightX, BottomRightY:
|
||||
tagLen := len(tag)
|
||||
if value, ok := radius.properties[tag[:tagLen-2]]; ok {
|
||||
if value := properties.getRaw(tag[:tagLen-2]); value != nil {
|
||||
return value
|
||||
}
|
||||
if value, ok := radius.properties[tag[tagLen-1:]]; ok {
|
||||
if value := properties.getRaw(tag[tagLen-1:]); value != nil {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
switch tag {
|
||||
case TopLeftX, TopRightX, BottomLeftX, BottomRightX:
|
||||
if value, ok := radius.properties[X]; ok {
|
||||
if value := properties.getRaw(X); value != nil {
|
||||
return value
|
||||
}
|
||||
case TopLeftY, TopRightY, BottomLeftY, BottomRightY:
|
||||
if value, ok := radius.properties[Y]; ok {
|
||||
if value := properties.getRaw(Y); value != nil {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
@ -649,7 +682,7 @@ func (radius *radiusPropertyData) BoxRadius(session Session) BoxRadius {
|
|||
x, _ := sizeProperty(radius, X, session)
|
||||
y, _ := sizeProperty(radius, Y, session)
|
||||
|
||||
getRadius := func(tag string) (SizeUnit, SizeUnit) {
|
||||
getRadius := func(tag PropertyName) (SizeUnit, SizeUnit) {
|
||||
rx := x
|
||||
ry := y
|
||||
if r, ok := sizeProperty(radius, tag, session); ok {
|
||||
|
@ -866,21 +899,18 @@ func getRadiusProperty(style Properties) RadiusProperty {
|
|||
return NewRadiusProperty(nil)
|
||||
}
|
||||
|
||||
func (properties *propertyList) setRadius(value any) bool {
|
||||
func setRadiusProperty(properties Properties, value any) []PropertyName {
|
||||
|
||||
if value == nil {
|
||||
delete(properties.properties, Radius)
|
||||
return true
|
||||
return propertiesRemove(properties, Radius)
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case RadiusProperty:
|
||||
properties.properties[Radius] = value
|
||||
return true
|
||||
properties.setRaw(Radius, value)
|
||||
|
||||
case SizeUnit:
|
||||
properties.properties[Radius] = value
|
||||
return true
|
||||
properties.setRaw(Radius, value)
|
||||
|
||||
case BoxRadius:
|
||||
radius := NewRadiusProperty(nil)
|
||||
|
@ -913,78 +943,85 @@ func (properties *propertyList) setRadius(value any) bool {
|
|||
radius.Set(BottomRightY, value.BottomRightY)
|
||||
}
|
||||
}
|
||||
properties.properties[Radius] = radius
|
||||
return true
|
||||
properties.setRaw(Radius, radius)
|
||||
|
||||
case string:
|
||||
if strings.Contains(value, "/") {
|
||||
values := strings.Split(value, "/")
|
||||
if len(values) == 2 {
|
||||
okX := properties.setRadiusElement(RadiusX, values[0])
|
||||
okY := properties.setRadiusElement(RadiusY, values[1])
|
||||
return okX && okY
|
||||
} else {
|
||||
notCompatibleType(Radius, value)
|
||||
if setRadiusPropertyElement(properties, RadiusX, values[0]) {
|
||||
result := []PropertyName{Radius, RadiusX}
|
||||
if setRadiusPropertyElement(properties, RadiusY, values[1]) {
|
||||
result = append(result, RadiusY)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
notCompatibleType(Radius, value)
|
||||
return nil
|
||||
|
||||
} else {
|
||||
return properties.setSizeProperty(Radius, value)
|
||||
return setSizeProperty(properties, Radius, value)
|
||||
}
|
||||
|
||||
case DataObject:
|
||||
radius := NewRadiusProperty(nil)
|
||||
for _, tag := range []string{X, Y, TopLeft, TopRight, BottomLeft, BottomRight, TopLeftX, TopLeftY,
|
||||
for _, tag := range []PropertyName{X, Y, TopLeft, TopRight, BottomLeft, BottomRight, TopLeftX, TopLeftY,
|
||||
TopRightX, TopRightY, BottomLeftX, BottomLeftY, BottomRightX, BottomRightY} {
|
||||
if value, ok := value.PropertyValue(tag); ok {
|
||||
if value, ok := value.PropertyValue(string(tag)); ok {
|
||||
radius.Set(tag, value)
|
||||
}
|
||||
}
|
||||
properties.properties[Radius] = radius
|
||||
return true
|
||||
properties.setRaw(Radius, radius)
|
||||
|
||||
case float32:
|
||||
return properties.setRadius(Px(float64(value)))
|
||||
properties.setRaw(Radius, Px(float64(value)))
|
||||
|
||||
case float64:
|
||||
return properties.setRadius(Px(value))
|
||||
properties.setRaw(Radius, Px(value))
|
||||
|
||||
default:
|
||||
if n, ok := isInt(value); ok {
|
||||
return properties.setRadius(Px(float64(n)))
|
||||
properties.setRaw(Radius, Px(float64(n)))
|
||||
} else {
|
||||
notCompatibleType(Radius, value)
|
||||
return nil
|
||||
}
|
||||
notCompatibleType(Radius, value)
|
||||
}
|
||||
|
||||
return []PropertyName{Radius}
|
||||
}
|
||||
|
||||
func removeRadiusPropertyElement(properties Properties, tag PropertyName) bool {
|
||||
if value := properties.getRaw(Radius); value != nil {
|
||||
radius := getRadiusProperty(properties)
|
||||
radius.Remove(tag)
|
||||
if radius.empty() {
|
||||
properties.setRaw(Radius, nil)
|
||||
} else {
|
||||
properties.setRaw(Radius, radius)
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (properties *propertyList) removeRadiusElement(tag string) {
|
||||
if value, ok := properties.properties[Radius]; ok && value != nil {
|
||||
radius := getRadiusProperty(properties)
|
||||
radius.Remove(tag)
|
||||
if len(radius.AllTags()) == 0 {
|
||||
delete(properties.properties, Radius)
|
||||
} else {
|
||||
properties.properties[Radius] = radius
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (properties *propertyList) setRadiusElement(tag string, value any) bool {
|
||||
func setRadiusPropertyElement(properties Properties, tag PropertyName, value any) bool {
|
||||
if value == nil {
|
||||
properties.removeRadiusElement(tag)
|
||||
removeRadiusPropertyElement(properties, tag)
|
||||
return true
|
||||
}
|
||||
|
||||
radius := getRadiusProperty(properties)
|
||||
if radius.Set(tag, value) {
|
||||
properties.properties[Radius] = radius
|
||||
properties.setRaw(Radius, radius)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func getRadiusElement(style Properties, tag string) any {
|
||||
func getRadiusElement(style Properties, tag PropertyName) any {
|
||||
value := style.Get(Radius)
|
||||
if value != nil {
|
||||
switch value := value.(type) {
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
package rui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Range defines range limits. The First and Last value are included in the range
|
||||
type Range struct {
|
||||
First, Last int
|
||||
}
|
||||
|
||||
// String returns a string representation of the Range struct
|
||||
func (r Range) String() string {
|
||||
if r.First == r.Last {
|
||||
return fmt.Sprintf("%d", r.First)
|
||||
}
|
||||
return fmt.Sprintf("%d:%d", r.First, r.Last)
|
||||
}
|
||||
|
||||
func (r *Range) setValue(value string) bool {
|
||||
var err error
|
||||
if strings.Contains(value, ":") {
|
||||
values := strings.Split(value, ":")
|
||||
if len(values) != 2 {
|
||||
ErrorLog("Invalid range value: " + value)
|
||||
return false
|
||||
}
|
||||
if r.First, err = strconv.Atoi(strings.Trim(values[0], " \t\n\r")); err != nil {
|
||||
ErrorLog(`Invalid first range value "` + value + `" (` + err.Error() + ")")
|
||||
return false
|
||||
}
|
||||
if r.Last, err = strconv.Atoi(strings.Trim(values[1], " \t\n\r")); err != nil {
|
||||
ErrorLog(`Invalid last range value "` + value + `" (` + err.Error() + ")")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if r.First, err = strconv.Atoi(value); err != nil {
|
||||
ErrorLog(`Invalid range value "` + value + `" (` + err.Error() + ")")
|
||||
return false
|
||||
}
|
||||
r.Last = r.First
|
||||
return true
|
||||
}
|
||||
|
||||
func setRangeProperty(properties Properties, tag PropertyName, value any) []PropertyName {
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
if setSimpleProperty(properties, tag, value) {
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
var r Range
|
||||
if !r.setValue(value) {
|
||||
invalidPropertyValue(tag, value)
|
||||
return nil
|
||||
}
|
||||
properties.setRaw(tag, r)
|
||||
|
||||
case Range:
|
||||
properties.setRaw(tag, value)
|
||||
|
||||
default:
|
||||
if n, ok := isInt(value); ok {
|
||||
properties.setRaw(tag, Range{First: n, Last: n})
|
||||
} else {
|
||||
notCompatibleType(tag, value)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return []PropertyName{tag}
|
||||
}
|
223
resizable.go
223
resizable.go
|
@ -11,7 +11,7 @@ const (
|
|||
// Side is the constant for "side" property tag.
|
||||
//
|
||||
// Used by `Resizable`.
|
||||
// Determines which side of the container is used to resize. The value of property is an or-combination of values listed.
|
||||
// Determines which side of the container is used to resize. The value of property is an or-combination of values listed.
|
||||
// Default value is "all".
|
||||
//
|
||||
// Supported types: `int`, `string`.
|
||||
|
@ -62,7 +62,6 @@ type Resizable interface {
|
|||
|
||||
type resizableData struct {
|
||||
viewData
|
||||
content []View
|
||||
}
|
||||
|
||||
// NewResizable create new Resizable object and return it
|
||||
|
@ -74,147 +73,99 @@ func NewResizable(session Session, params Params) Resizable {
|
|||
}
|
||||
|
||||
func newResizable(session Session) View {
|
||||
return NewResizable(session, nil)
|
||||
return new(resizableData)
|
||||
}
|
||||
|
||||
func (resizable *resizableData) init(session Session) {
|
||||
resizable.viewData.init(session)
|
||||
resizable.tag = "Resizable"
|
||||
resizable.systemClass = "ruiGridLayout"
|
||||
resizable.content = []View{}
|
||||
}
|
||||
|
||||
func (resizable *resizableData) String() string {
|
||||
return getViewString(resizable, nil)
|
||||
resizable.set = resizableSet
|
||||
resizable.changed = resizablePropertyChanged
|
||||
}
|
||||
|
||||
func (resizable *resizableData) Views() []View {
|
||||
return resizable.content
|
||||
if view := resizable.content(); view != nil {
|
||||
return []View{view}
|
||||
}
|
||||
return []View{}
|
||||
}
|
||||
|
||||
func (resizable *resizableData) Remove(tag string) {
|
||||
resizable.remove(strings.ToLower(tag))
|
||||
func (resizable *resizableData) content() View {
|
||||
if value := resizable.getRaw(Content); value != nil {
|
||||
if content, ok := value.(View); ok {
|
||||
return content
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (resizable *resizableData) remove(tag string) {
|
||||
func resizableSet(view View, tag PropertyName, value any) []PropertyName {
|
||||
switch tag {
|
||||
case Side:
|
||||
oldSide := resizable.getSide()
|
||||
delete(resizable.properties, Side)
|
||||
if oldSide != resizable.getSide() {
|
||||
if resizable.created {
|
||||
updateInnerHTML(resizable.htmlID(), resizable.Session())
|
||||
resizable.updateResizeBorderWidth()
|
||||
}
|
||||
resizable.propertyChangedEvent(tag)
|
||||
}
|
||||
return resizableSetSide(view, value)
|
||||
|
||||
case ResizeBorderWidth:
|
||||
w := resizable.resizeBorderWidth()
|
||||
delete(resizable.properties, ResizeBorderWidth)
|
||||
if !w.Equal(resizable.resizeBorderWidth()) {
|
||||
resizable.updateResizeBorderWidth()
|
||||
resizable.propertyChangedEvent(tag)
|
||||
}
|
||||
|
||||
case Content:
|
||||
if len(resizable.content) > 0 {
|
||||
resizable.content = []View{}
|
||||
if resizable.created {
|
||||
updateInnerHTML(resizable.htmlID(), resizable.Session())
|
||||
}
|
||||
resizable.propertyChangedEvent(tag)
|
||||
}
|
||||
|
||||
default:
|
||||
resizable.viewData.remove(tag)
|
||||
}
|
||||
}
|
||||
|
||||
func (resizable *resizableData) Set(tag string, value any) bool {
|
||||
return resizable.set(strings.ToLower(tag), value)
|
||||
}
|
||||
|
||||
func (resizable *resizableData) set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
resizable.remove(tag)
|
||||
return true
|
||||
}
|
||||
|
||||
switch tag {
|
||||
case Side:
|
||||
oldSide := resizable.getSide()
|
||||
if !resizable.setSide(value) {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
if oldSide != resizable.getSide() {
|
||||
if resizable.created {
|
||||
updateInnerHTML(resizable.htmlID(), resizable.Session())
|
||||
resizable.updateResizeBorderWidth()
|
||||
}
|
||||
resizable.propertyChangedEvent(tag)
|
||||
}
|
||||
return true
|
||||
|
||||
case ResizeBorderWidth:
|
||||
w := resizable.resizeBorderWidth()
|
||||
ok := resizable.setSizeProperty(tag, value)
|
||||
if ok && !w.Equal(resizable.resizeBorderWidth()) {
|
||||
resizable.updateResizeBorderWidth()
|
||||
resizable.propertyChangedEvent(tag)
|
||||
}
|
||||
return ok
|
||||
return setSizeProperty(view, tag, value)
|
||||
|
||||
case Content:
|
||||
var newContent View = nil
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
newContent = NewTextView(resizable.Session(), Params{Text: value})
|
||||
newContent = NewTextView(view.Session(), Params{Text: value})
|
||||
|
||||
case View:
|
||||
newContent = value
|
||||
|
||||
case DataObject:
|
||||
if view := CreateViewFromObject(resizable.Session(), value); view != nil {
|
||||
newContent = view
|
||||
} else {
|
||||
return false
|
||||
if newContent = CreateViewFromObject(view.Session(), value); newContent == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
default:
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(resizable.content) == 0 {
|
||||
resizable.content = []View{newContent}
|
||||
} else {
|
||||
resizable.content[0] = newContent
|
||||
}
|
||||
if resizable.created {
|
||||
updateInnerHTML(resizable.htmlID(), resizable.Session())
|
||||
}
|
||||
resizable.propertyChangedEvent(tag)
|
||||
return true
|
||||
view.setRaw(Content, newContent)
|
||||
return []PropertyName{}
|
||||
|
||||
case CellWidth, CellHeight, GridRowGap, GridColumnGap, CellVerticalAlign, CellHorizontalAlign:
|
||||
ErrorLogF(`Not supported "%s" property`, tag)
|
||||
return false
|
||||
ErrorLogF(`Not supported "%s" property`, string(tag))
|
||||
return nil
|
||||
}
|
||||
|
||||
return resizable.viewData.set(tag, value)
|
||||
return viewSet(view, tag, value)
|
||||
}
|
||||
|
||||
func (resizable *resizableData) Get(tag string) any {
|
||||
return resizable.get(strings.ToLower(tag))
|
||||
func resizablePropertyChanged(view View, tag PropertyName) {
|
||||
switch tag {
|
||||
case Side:
|
||||
updateInnerHTML(view.htmlID(), view.Session())
|
||||
fallthrough
|
||||
|
||||
case ResizeBorderWidth:
|
||||
htmlID := view.htmlID()
|
||||
session := view.Session()
|
||||
column, row := resizableCellSizeCSS(view)
|
||||
|
||||
session.updateCSSProperty(htmlID, "grid-template-columns", column)
|
||||
session.updateCSSProperty(htmlID, "grid-template-rows", row)
|
||||
|
||||
case Content:
|
||||
updateInnerHTML(view.htmlID(), view.Session())
|
||||
|
||||
default:
|
||||
viewPropertyChanged(view, tag)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (resizable *resizableData) getSide() int {
|
||||
if value := resizable.getRaw(Side); value != nil {
|
||||
func resizableSide(view View) int {
|
||||
if value := view.getRaw(Side); value != nil {
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
if value, ok := resizable.session.resolveConstants(value); ok {
|
||||
if value, ok := view.Session().resolveConstants(value); ok {
|
||||
validValues := map[string]int{
|
||||
"top": TopSide,
|
||||
"right": RightSide,
|
||||
|
@ -258,15 +209,15 @@ func (resizable *resizableData) getSide() int {
|
|||
return AllSides
|
||||
}
|
||||
|
||||
func (resizable *resizableData) setSide(value any) bool {
|
||||
func resizableSetSide(properties Properties, value any) []PropertyName {
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
if n, err := strconv.Atoi(value); err == nil {
|
||||
if n >= 1 && n <= AllSides {
|
||||
resizable.properties[Side] = n
|
||||
return true
|
||||
properties.setRaw(Side, n)
|
||||
return []PropertyName{Side}
|
||||
}
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
validValues := map[string]int{
|
||||
"top": TopSide,
|
||||
|
@ -287,13 +238,13 @@ func (resizable *resizableData) setSide(value any) bool {
|
|||
hasConst = true
|
||||
} else if n, err := strconv.Atoi(val); err == nil {
|
||||
if n < 1 || n > AllSides {
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
sides |= n
|
||||
} else if n, ok := validValues[val]; ok {
|
||||
sides |= n
|
||||
} else {
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -302,69 +253,58 @@ func (resizable *resizableData) setSide(value any) bool {
|
|||
for i := 1; i < len(values); i++ {
|
||||
value += "|" + values[i]
|
||||
}
|
||||
resizable.properties[Side] = value
|
||||
return true
|
||||
properties.setRaw(Side, value)
|
||||
return []PropertyName{Side}
|
||||
}
|
||||
|
||||
if sides >= 1 && sides <= AllSides {
|
||||
resizable.properties[Side] = sides
|
||||
return true
|
||||
properties.setRaw(Side, sides)
|
||||
return []PropertyName{Side}
|
||||
}
|
||||
|
||||
} else if value[0] == '@' {
|
||||
resizable.properties[Side] = value
|
||||
return true
|
||||
properties.setRaw(Side, value)
|
||||
return []PropertyName{Side}
|
||||
} else if n, ok := validValues[value]; ok {
|
||||
resizable.properties[Side] = n
|
||||
return true
|
||||
properties.setRaw(Side, n)
|
||||
return []PropertyName{Side}
|
||||
}
|
||||
|
||||
case int:
|
||||
if value >= 1 && value <= AllSides {
|
||||
resizable.properties[Side] = value
|
||||
return true
|
||||
properties.setRaw(Side, value)
|
||||
return []PropertyName{Side}
|
||||
} else {
|
||||
ErrorLogF(`Invalid value %d of "side" property`, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
default:
|
||||
if n, ok := isInt(value); ok {
|
||||
if n >= 1 && n <= AllSides {
|
||||
resizable.properties[Side] = n
|
||||
return true
|
||||
properties.setRaw(Side, n)
|
||||
return []PropertyName{Side}
|
||||
} else {
|
||||
ErrorLogF(`Invalid value %d of "side" property`, n)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
func (resizable *resizableData) resizeBorderWidth() SizeUnit {
|
||||
result, _ := sizeProperty(resizable, ResizeBorderWidth, resizable.Session())
|
||||
func resizableBorderWidth(view View) SizeUnit {
|
||||
result, _ := sizeProperty(view, ResizeBorderWidth, view.Session())
|
||||
if result.Type == Auto || result.Value == 0 {
|
||||
return Px(4)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (resizable *resizableData) updateResizeBorderWidth() {
|
||||
if resizable.created {
|
||||
htmlID := resizable.htmlID()
|
||||
session := resizable.Session()
|
||||
column, row := resizable.cellSizeCSS()
|
||||
|
||||
session.updateCSSProperty(htmlID, "grid-template-columns", column)
|
||||
session.updateCSSProperty(htmlID, "grid-template-rows", row)
|
||||
}
|
||||
}
|
||||
|
||||
func (resizable *resizableData) cellSizeCSS() (string, string) {
|
||||
w := resizable.resizeBorderWidth().cssString("4px", resizable.Session())
|
||||
side := resizable.getSide()
|
||||
func resizableCellSizeCSS(view View) (string, string) {
|
||||
w := resizableBorderWidth(view).cssString("4px", view.Session())
|
||||
side := resizableSide(view)
|
||||
column := "1fr"
|
||||
row := "1fr"
|
||||
|
||||
|
@ -392,7 +332,7 @@ func (resizable *resizableData) cellSizeCSS() (string, string) {
|
|||
}
|
||||
|
||||
func (resizable *resizableData) cssStyle(self View, builder cssBuilder) {
|
||||
column, row := resizable.cellSizeCSS()
|
||||
column, row := resizableCellSizeCSS(resizable)
|
||||
|
||||
builder.add("grid-template-columns", column)
|
||||
builder.add("grid-template-rows", row)
|
||||
|
@ -402,12 +342,12 @@ func (resizable *resizableData) cssStyle(self View, builder cssBuilder) {
|
|||
|
||||
func (resizable *resizableData) htmlSubviews(self View, buffer *strings.Builder) {
|
||||
|
||||
side := resizable.getSide()
|
||||
side := resizableSide(resizable)
|
||||
left := 1
|
||||
top := 1
|
||||
leftSide := (side & LeftSide) != 0
|
||||
rightSide := (side & RightSide) != 0
|
||||
w := resizable.resizeBorderWidth().cssString("4px", resizable.Session())
|
||||
w := resizableBorderWidth(resizable).cssString("4px", resizable.Session())
|
||||
|
||||
if leftSide {
|
||||
left = 2
|
||||
|
@ -484,8 +424,7 @@ func (resizable *resizableData) htmlSubviews(self View, buffer *strings.Builder)
|
|||
}
|
||||
}
|
||||
|
||||
if len(resizable.content) > 0 {
|
||||
view := resizable.content[0]
|
||||
if view := resizable.content(); view != nil {
|
||||
view.addToCSSStyle(map[string]string{
|
||||
"grid-column-start": strconv.Itoa(left),
|
||||
"grid-column-end": strconv.Itoa(left + 1),
|
||||
|
|
|
@ -16,7 +16,7 @@ package rui
|
|||
// `func(frame rui.Frame)`,
|
||||
// `func(view rui.View)`,
|
||||
// `func()`.
|
||||
const ResizeEvent = "resize-event"
|
||||
const ResizeEvent PropertyName = "resize-event"
|
||||
|
||||
func (view *viewData) onResize(self View, x, y, width, height float64) {
|
||||
view.frame.Left = x
|
||||
|
@ -31,21 +31,20 @@ func (view *viewData) onResize(self View, x, y, width, height float64) {
|
|||
func (view *viewData) onItemResize(self View, index string, x, y, width, height float64) {
|
||||
}
|
||||
|
||||
func (view *viewData) setFrameListener(tag string, value any) bool {
|
||||
listeners, ok := valueToEventListeners[View, Frame](value)
|
||||
if !ok {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
/*
|
||||
func setFrameListener(properties Properties, tag PropertyName, value any) bool {
|
||||
if listeners, ok := valueToEventListeners[View, Frame](value); ok {
|
||||
if len(listeners) == 0 {
|
||||
properties.setRaw(tag, nil)
|
||||
} else {
|
||||
properties.setRaw(tag, listeners)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if listeners == nil {
|
||||
delete(view.properties, tag)
|
||||
} else {
|
||||
view.properties[tag] = listeners
|
||||
}
|
||||
view.propertyChangedEvent(tag)
|
||||
return true
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
*/
|
||||
|
||||
func (view *viewData) setNoResizeEvent() {
|
||||
view.noResizeEvent = true
|
||||
|
|
|
@ -16,7 +16,7 @@ package rui
|
|||
// `func(frame rui.Frame)`,
|
||||
// `func(view rui.View)`,
|
||||
// `func()`.
|
||||
const ScrollEvent = "scroll-event"
|
||||
const ScrollEvent PropertyName = "scroll-event"
|
||||
|
||||
func (view *viewData) onScroll(self View, x, y, width, height float64) {
|
||||
view.scroll.Left = x
|
||||
|
|
16
session.go
16
session.go
|
@ -89,11 +89,11 @@ type Session interface {
|
|||
RootView() View
|
||||
// Get returns a value of the view (with id defined by the first argument) property with name defined by the second argument.
|
||||
// The type of return value depends on the property. If the property is not set then nil is returned.
|
||||
Get(viewID, tag string) any
|
||||
Get(viewID string, tag PropertyName) any
|
||||
// Set sets the value (third argument) of the property (second argument) of the view with id 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
|
||||
Set(viewID, tag string, value any) bool
|
||||
Set(viewID string, tag PropertyName, value any) bool
|
||||
|
||||
// DownloadFile downloads (saves) on the client side the file located at the specified path on the server.
|
||||
DownloadFile(path string)
|
||||
|
@ -134,7 +134,7 @@ type Session interface {
|
|||
|
||||
viewByHTMLID(id string) View
|
||||
nextViewID() string
|
||||
styleProperty(styleTag, property string) any
|
||||
styleProperty(styleTag string, propertyTag PropertyName) any
|
||||
|
||||
setBridge(events chan DataObject, bridge bridge)
|
||||
writeInitScript(writer *strings.Builder)
|
||||
|
@ -270,7 +270,7 @@ func (session *sessionData) close() {
|
|||
}
|
||||
}
|
||||
|
||||
func (session *sessionData) styleProperty(styleTag, propertyTag string) any {
|
||||
func (session *sessionData) styleProperty(styleTag string, propertyTag PropertyName) any {
|
||||
if style := session.getCurrentTheme().style(styleTag); style != nil {
|
||||
return style.getRaw(propertyTag)
|
||||
}
|
||||
|
@ -376,14 +376,14 @@ func (session *sessionData) setIgnoreViewUpdates(ignore bool) {
|
|||
session.ignoreUpdates = ignore
|
||||
}
|
||||
|
||||
func (session *sessionData) Get(viewID, tag string) any {
|
||||
func (session *sessionData) Get(viewID string, tag PropertyName) any {
|
||||
if view := ViewByID(session.RootView(), viewID); view != nil {
|
||||
return view.Get(tag)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (session *sessionData) Set(viewID, tag string, value any) bool {
|
||||
func (session *sessionData) Set(viewID string, tag PropertyName, value any) bool {
|
||||
if view := ViewByID(session.RootView(), viewID); view != nil {
|
||||
return view.Set(tag, value)
|
||||
}
|
||||
|
@ -785,10 +785,10 @@ func (session *sessionData) handleEvent(command string, data DataObject) {
|
|||
if viewID, ok := data.PropertyValue("id"); ok {
|
||||
if viewID != "body" {
|
||||
if view := session.viewByHTMLID(viewID); view != nil {
|
||||
view.handleCommand(view, command, data)
|
||||
view.handleCommand(view, PropertyName(command), data)
|
||||
}
|
||||
}
|
||||
if command == KeyDownEvent {
|
||||
if command == string(KeyDownEvent) {
|
||||
var event KeyEvent
|
||||
event.init(data)
|
||||
session.hotKey(event)
|
||||
|
|
70
shadow.go
70
shadow.go
|
@ -42,7 +42,7 @@ const (
|
|||
//
|
||||
// Internal type is `Color`, other types converted to it during assignment.
|
||||
// See `Color` description for more details.
|
||||
ColorTag = "color"
|
||||
ColorTag PropertyName = "color"
|
||||
|
||||
// Inset is the constant for "inset" property tag.
|
||||
//
|
||||
|
@ -55,7 +55,7 @@ const (
|
|||
// Values:
|
||||
// `true` or `1` or "true", "yes", "on", "1" - Drop shadow inside the frame(as if the content was depressed inside the box).
|
||||
// `false` or `0` or "false", "no", "off", "0" - Shadow is assumed to be a drop shadow(as if the box were raised above the content).
|
||||
Inset = "inset"
|
||||
Inset PropertyName = "inset"
|
||||
|
||||
// XOffset is the constant for "x-offset" property tag.
|
||||
//
|
||||
|
@ -66,7 +66,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
XOffset = "x-offset"
|
||||
XOffset PropertyName = "x-offset"
|
||||
|
||||
// YOffset is the constant for "y-offset" property tag.
|
||||
//
|
||||
|
@ -77,7 +77,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
YOffset = "y-offset"
|
||||
YOffset PropertyName = "y-offset"
|
||||
|
||||
// BlurRadius is the constant for "blur" property tag.
|
||||
//
|
||||
|
@ -89,7 +89,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
BlurRadius = "blur"
|
||||
BlurRadius PropertyName = "blur"
|
||||
|
||||
// SpreadRadius is the constant for "spread-radius" property tag.
|
||||
//
|
||||
|
@ -100,7 +100,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
SpreadRadius = "spread-radius"
|
||||
SpreadRadius PropertyName = "spread-radius"
|
||||
)
|
||||
|
||||
// ViewShadow contains attributes of the view shadow
|
||||
|
@ -114,7 +114,7 @@ type ViewShadow interface {
|
|||
}
|
||||
|
||||
type viewShadowData struct {
|
||||
propertyList
|
||||
dataProperty
|
||||
}
|
||||
|
||||
// NewViewShadow create the new shadow for a view. Arguments:
|
||||
|
@ -188,11 +188,12 @@ func NewTextShadow(offsetX, offsetY, blurRadius SizeUnit, color Color) ViewShado
|
|||
// "inset" (Inset). Controls (bool) whether to draw shadow inside the frame or outside.
|
||||
func NewShadowWithParams(params Params) ViewShadow {
|
||||
shadow := new(viewShadowData)
|
||||
shadow.propertyList.init()
|
||||
shadow.init()
|
||||
|
||||
if params != nil {
|
||||
for _, tag := range []string{ColorTag, Inset, XOffset, YOffset, BlurRadius, SpreadRadius} {
|
||||
for _, tag := range []PropertyName{ColorTag, Inset, XOffset, YOffset, BlurRadius, SpreadRadius} {
|
||||
if value, ok := params[tag]; ok && value != nil {
|
||||
shadow.Set(tag, value)
|
||||
shadow.set(shadow, tag, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -202,33 +203,14 @@ func NewShadowWithParams(params Params) ViewShadow {
|
|||
// parseViewShadow parse DataObject and create ViewShadow object
|
||||
func parseViewShadow(object DataObject) ViewShadow {
|
||||
shadow := new(viewShadowData)
|
||||
shadow.propertyList.init()
|
||||
shadow.init()
|
||||
parseProperties(shadow, object)
|
||||
return shadow
|
||||
}
|
||||
|
||||
func (shadow *viewShadowData) Remove(tag string) {
|
||||
delete(shadow.properties, strings.ToLower(tag))
|
||||
}
|
||||
|
||||
func (shadow *viewShadowData) Set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
shadow.Remove(tag)
|
||||
return true
|
||||
}
|
||||
|
||||
tag = strings.ToLower(tag)
|
||||
switch tag {
|
||||
case ColorTag, Inset, XOffset, YOffset, BlurRadius, SpreadRadius:
|
||||
return shadow.propertyList.Set(tag, value)
|
||||
}
|
||||
|
||||
ErrorLogF(`"%s" property is not supported by Shadow`, tag)
|
||||
return false
|
||||
}
|
||||
|
||||
func (shadow *viewShadowData) Get(tag string) any {
|
||||
return shadow.propertyList.Get(strings.ToLower(tag))
|
||||
func (shadow *viewShadowData) init() {
|
||||
shadow.dataProperty.init()
|
||||
shadow.supportedProperties = []PropertyName{ColorTag, Inset, XOffset, YOffset, BlurRadius, SpreadRadius}
|
||||
}
|
||||
|
||||
func (shadow *viewShadowData) cssStyle(buffer *strings.Builder, session Session, lead string) bool {
|
||||
|
@ -316,7 +298,7 @@ func (shadow *viewShadowData) writeString(buffer *strings.Builder, indent string
|
|||
if comma {
|
||||
buffer.WriteString(", ")
|
||||
}
|
||||
buffer.WriteString(tag)
|
||||
buffer.WriteString(string(tag))
|
||||
buffer.WriteString(" = ")
|
||||
writePropertyValue(buffer, tag, value, indent)
|
||||
comma = true
|
||||
|
@ -325,29 +307,29 @@ func (shadow *viewShadowData) writeString(buffer *strings.Builder, indent string
|
|||
buffer.WriteString(" }")
|
||||
}
|
||||
|
||||
func (properties *propertyList) setShadow(tag string, value any) bool {
|
||||
func setShadowProperty(properties Properties, tag PropertyName, value any) bool {
|
||||
|
||||
if value == nil {
|
||||
delete(properties.properties, tag)
|
||||
properties.setRaw(tag, nil)
|
||||
return true
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case ViewShadow:
|
||||
properties.properties[tag] = []ViewShadow{value}
|
||||
properties.setRaw(tag, []ViewShadow{value})
|
||||
|
||||
case []ViewShadow:
|
||||
if len(value) == 0 {
|
||||
delete(properties.properties, tag)
|
||||
properties.setRaw(tag, nil)
|
||||
} else {
|
||||
properties.properties[tag] = value
|
||||
properties.setRaw(tag, value)
|
||||
}
|
||||
|
||||
case DataValue:
|
||||
if !value.IsObject() {
|
||||
return false
|
||||
}
|
||||
properties.properties[tag] = []ViewShadow{parseViewShadow(value.Object())}
|
||||
properties.setRaw(tag, []ViewShadow{parseViewShadow(value.Object())})
|
||||
|
||||
case []DataValue:
|
||||
shadows := []ViewShadow{}
|
||||
|
@ -359,7 +341,7 @@ func (properties *propertyList) setShadow(tag string, value any) bool {
|
|||
if len(shadows) == 0 {
|
||||
return false
|
||||
}
|
||||
properties.properties[tag] = shadows
|
||||
properties.setRaw(tag, shadows)
|
||||
|
||||
case string:
|
||||
obj := NewDataObject(value)
|
||||
|
@ -367,7 +349,7 @@ func (properties *propertyList) setShadow(tag string, value any) bool {
|
|||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
properties.properties[tag] = []ViewShadow{parseViewShadow(obj)}
|
||||
properties.setRaw(tag, []ViewShadow{parseViewShadow(obj)})
|
||||
|
||||
default:
|
||||
notCompatibleType(tag, value)
|
||||
|
@ -377,7 +359,7 @@ func (properties *propertyList) setShadow(tag string, value any) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func getShadows(properties Properties, tag string) []ViewShadow {
|
||||
func getShadows(properties Properties, tag PropertyName) []ViewShadow {
|
||||
if value := properties.Get(tag); value != nil {
|
||||
switch value := value.(type) {
|
||||
case []ViewShadow:
|
||||
|
@ -390,7 +372,7 @@ func getShadows(properties Properties, tag string) []ViewShadow {
|
|||
return []ViewShadow{}
|
||||
}
|
||||
|
||||
func shadowCSS(properties Properties, tag string, session Session) string {
|
||||
func shadowCSS(properties Properties, tag PropertyName, session Session) string {
|
||||
shadows := getShadows(properties, tag)
|
||||
if len(shadows) == 0 {
|
||||
return ""
|
||||
|
|
123
stackLayout.go
123
stackLayout.go
|
@ -55,7 +55,7 @@ type StackLayout interface {
|
|||
|
||||
type stackLayoutData struct {
|
||||
viewsContainerData
|
||||
peek int
|
||||
peek, prevPeek int
|
||||
pushView, popView View
|
||||
animationType int
|
||||
onPushFinished func()
|
||||
|
@ -71,7 +71,8 @@ func NewStackLayout(session Session, params Params) StackLayout {
|
|||
}
|
||||
|
||||
func newStackLayout(session Session) View {
|
||||
return NewStackLayout(session, nil)
|
||||
//return NewStackLayout(session, nil)
|
||||
return new(stackLayoutData)
|
||||
}
|
||||
|
||||
// Init initialize fields of ViewsContainer by default values
|
||||
|
@ -80,10 +81,9 @@ func (layout *stackLayoutData) init(session Session) {
|
|||
layout.tag = "StackLayout"
|
||||
layout.systemClass = "ruiStackLayout"
|
||||
layout.properties[TransitionEndEvent] = []func(View, string){layout.pushFinished, layout.popFinished}
|
||||
}
|
||||
|
||||
func (layout *stackLayoutData) String() string {
|
||||
return getViewString(layout, nil)
|
||||
layout.getFunc = layout.get
|
||||
layout.set = layout.setFunc
|
||||
layout.remove = layout.removeFunc
|
||||
}
|
||||
|
||||
func (layout *stackLayoutData) pushFinished(view View, tag string) {
|
||||
|
@ -97,7 +97,7 @@ func (layout *stackLayoutData) pushFinished(view View, tag string) {
|
|||
layout.peek = 0
|
||||
}
|
||||
updateInnerHTML(layout.htmlID(), layout.session)
|
||||
layout.propertyChangedEvent(Current)
|
||||
layout.currentChanged()
|
||||
}
|
||||
|
||||
if layout.onPushFinished != nil {
|
||||
|
@ -121,97 +121,91 @@ func (layout *stackLayoutData) popFinished(view View, tag string) {
|
|||
}
|
||||
}
|
||||
|
||||
func (layout *stackLayoutData) Set(tag string, value any) bool {
|
||||
return layout.set(strings.ToLower(tag), value)
|
||||
}
|
||||
|
||||
func (layout *stackLayoutData) set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
layout.remove(tag)
|
||||
return true
|
||||
}
|
||||
|
||||
func (layout *stackLayoutData) setFunc(view View, tag PropertyName, value any) []PropertyName {
|
||||
switch tag {
|
||||
case TransitionEndEvent:
|
||||
listeners, ok := valueToEventListeners[View, string](value)
|
||||
if ok && listeners != nil {
|
||||
listeners = append(listeners, layout.pushFinished)
|
||||
listeners = append(listeners, layout.popFinished)
|
||||
layout.properties[TransitionEndEvent] = listeners
|
||||
layout.propertyChangedEvent(TransitionEndEvent)
|
||||
view.setRaw(TransitionEndEvent, listeners)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
return ok
|
||||
return nil
|
||||
|
||||
case Current:
|
||||
setCurrent := func(index int) {
|
||||
if index != layout.peek {
|
||||
if layout.peek < len(layout.views) {
|
||||
layout.Session().updateCSSProperty(layout.htmlID()+"page"+strconv.Itoa(layout.peek), "visibility", "hidden")
|
||||
}
|
||||
|
||||
layout.peek = index
|
||||
layout.Session().updateCSSProperty(layout.htmlID()+"page"+strconv.Itoa(index), "visibility", "visible")
|
||||
layout.propertyChangedEvent(Current)
|
||||
}
|
||||
}
|
||||
newCurrent := 0
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
text, ok := layout.session.resolveConstants(value)
|
||||
if !ok {
|
||||
invalidPropertyValue(tag, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
n, err := strconv.Atoi(strings.Trim(text, " \t"))
|
||||
if err != nil {
|
||||
invalidPropertyValue(tag, value)
|
||||
ErrorLog(err.Error())
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
setCurrent(n)
|
||||
newCurrent = n
|
||||
|
||||
default:
|
||||
n, ok := isInt(value)
|
||||
if !ok {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
return nil
|
||||
} else if n < 0 || n >= len(layout.views) {
|
||||
ErrorLogF(`The view index "%d" of "%s" property is out of range`, n, tag)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
setCurrent(n)
|
||||
newCurrent = n
|
||||
}
|
||||
return true
|
||||
|
||||
layout.prevPeek = layout.peek
|
||||
if newCurrent == layout.peek {
|
||||
return []PropertyName{}
|
||||
}
|
||||
|
||||
layout.peek = newCurrent
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
return layout.viewsContainerData.set(tag, value)
|
||||
return layout.viewsContainerData.setFunc(view, tag, value)
|
||||
}
|
||||
|
||||
func (layout *stackLayoutData) Remove(tag string) {
|
||||
layout.remove(strings.ToLower(tag))
|
||||
func (layout *stackLayoutData) propertyChanged(view View, tag PropertyName) {
|
||||
switch tag {
|
||||
case Current:
|
||||
if layout.prevPeek != layout.peek {
|
||||
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", "visible")
|
||||
layout.prevPeek = layout.peek
|
||||
}
|
||||
default:
|
||||
viewsContainerPropertyChanged(view, tag)
|
||||
}
|
||||
}
|
||||
|
||||
func (layout *stackLayoutData) remove(tag string) {
|
||||
func (layout *stackLayoutData) removeFunc(view View, tag PropertyName) []PropertyName {
|
||||
switch tag {
|
||||
case TransitionEndEvent:
|
||||
layout.properties[TransitionEndEvent] = []func(View, string){layout.pushFinished, layout.popFinished}
|
||||
layout.propertyChangedEvent(TransitionEndEvent)
|
||||
view.setRaw(TransitionEndEvent, []func(View, string){layout.pushFinished, layout.popFinished})
|
||||
return []PropertyName{tag}
|
||||
|
||||
case Current:
|
||||
layout.set(Current, 0)
|
||||
|
||||
default:
|
||||
layout.viewsContainerData.remove(tag)
|
||||
view.setRaw(Current, 0)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
return layout.viewsContainerData.removeFunc(view, tag)
|
||||
}
|
||||
|
||||
func (layout *stackLayoutData) Get(tag string) any {
|
||||
return layout.get(strings.ToLower(tag))
|
||||
}
|
||||
|
||||
func (layout *stackLayoutData) get(tag string) any {
|
||||
func (layout *stackLayoutData) get(view View, tag PropertyName) any {
|
||||
if tag == Current {
|
||||
return layout.peek
|
||||
}
|
||||
return layout.viewsContainerData.get(tag)
|
||||
return layout.viewsContainerData.get(view, tag)
|
||||
}
|
||||
|
||||
func (layout *stackLayoutData) Peek() View {
|
||||
|
@ -233,7 +227,7 @@ func (layout *stackLayoutData) MoveToFront(view View) bool {
|
|||
|
||||
layout.peek = i
|
||||
layout.Session().updateCSSProperty(layout.htmlID()+"page"+strconv.Itoa(i), "visibility", "visible")
|
||||
layout.propertyChangedEvent(Current)
|
||||
layout.currentChanged()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -243,6 +237,12 @@ func (layout *stackLayoutData) MoveToFront(view View) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (layout *stackLayoutData) currentChanged() {
|
||||
if listener, ok := layout.changeListener[Current]; ok {
|
||||
listener(layout, Current)
|
||||
}
|
||||
}
|
||||
|
||||
func (layout *stackLayoutData) MoveToFrontByID(viewID string) bool {
|
||||
peek := int(layout.peek)
|
||||
for i, view := range layout.views {
|
||||
|
@ -254,7 +254,7 @@ func (layout *stackLayoutData) MoveToFrontByID(viewID string) bool {
|
|||
|
||||
layout.peek = i
|
||||
layout.Session().updateCSSProperty(layout.htmlID()+"page"+strconv.Itoa(i), "visibility", "visible")
|
||||
layout.propertyChangedEvent(Current)
|
||||
layout.currentChanged()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -268,7 +268,7 @@ func (layout *stackLayoutData) Append(view View) {
|
|||
if view != nil {
|
||||
layout.peek = len(layout.views)
|
||||
layout.viewsContainerData.Append(view)
|
||||
layout.propertyChangedEvent(Current)
|
||||
layout.currentChanged()
|
||||
} else {
|
||||
ErrorLog("StackLayout.Append(nil, ....) is forbidden")
|
||||
}
|
||||
|
@ -283,7 +283,7 @@ func (layout *stackLayoutData) Insert(view View, index int) {
|
|||
layout.peek = count
|
||||
}
|
||||
layout.viewsContainerData.Insert(view, index)
|
||||
layout.propertyChangedEvent(Current)
|
||||
layout.currentChanged()
|
||||
} else {
|
||||
ErrorLog("StackLayout.Insert(nil, ....) is forbidden")
|
||||
}
|
||||
|
@ -297,7 +297,7 @@ func (layout *stackLayoutData) RemoveView(index int) View {
|
|||
if layout.peek > 0 {
|
||||
layout.peek--
|
||||
}
|
||||
defer layout.propertyChangedEvent(Current)
|
||||
defer layout.currentChanged()
|
||||
return layout.viewsContainerData.RemoveView(index)
|
||||
}
|
||||
|
||||
|
@ -352,7 +352,10 @@ func (layout *stackLayoutData) Push(view View, animation int, onPushFinished fun
|
|||
|
||||
layout.views = append(layout.views, view)
|
||||
view.setParentID(htmlID)
|
||||
layout.propertyChangedEvent(Content)
|
||||
|
||||
if listener, ok := layout.changeListener[Content]; ok {
|
||||
listener(layout, Content)
|
||||
}
|
||||
}
|
||||
|
||||
func (layout *stackLayoutData) Pop(animation int, onPopFinished func(View)) bool {
|
||||
|
|
|
@ -25,7 +25,7 @@ func NewSvgImageView(session Session, params Params) SvgImageView {
|
|||
}
|
||||
|
||||
func newSvgImageView(session Session) View {
|
||||
return NewSvgImageView(session, nil)
|
||||
return new(svgImageViewData) // NewSvgImageView(session, nil)
|
||||
}
|
||||
|
||||
// Init initialize fields of imageView by default values
|
||||
|
@ -33,14 +33,14 @@ func (imageView *svgImageViewData) init(session Session) {
|
|||
imageView.viewData.init(session)
|
||||
imageView.tag = "SvgImageView"
|
||||
imageView.systemClass = "ruiSvgImageView"
|
||||
imageView.normalize = normalizeSvgImageViewTag
|
||||
imageView.set = svgImageViewSet
|
||||
imageView.changed = svgImageViewPropertyChanged
|
||||
|
||||
}
|
||||
|
||||
func (imageView *svgImageViewData) String() string {
|
||||
return getViewString(imageView, nil)
|
||||
}
|
||||
|
||||
func (imageView *svgImageViewData) normalizeTag(tag string) string {
|
||||
tag = strings.ToLower(tag)
|
||||
func normalizeSvgImageViewTag(tag PropertyName) PropertyName {
|
||||
tag = defaultNormalize(tag)
|
||||
switch tag {
|
||||
case Source, "source":
|
||||
tag = Content
|
||||
|
@ -54,51 +54,29 @@ func (imageView *svgImageViewData) normalizeTag(tag string) string {
|
|||
return tag
|
||||
}
|
||||
|
||||
func (imageView *svgImageViewData) Remove(tag string) {
|
||||
imageView.remove(imageView.normalizeTag(tag))
|
||||
}
|
||||
|
||||
func (imageView *svgImageViewData) remove(tag string) {
|
||||
imageView.viewData.remove(tag)
|
||||
|
||||
if imageView.created {
|
||||
switch tag {
|
||||
case Content:
|
||||
updateInnerHTML(imageView.htmlID(), imageView.session)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (imageView *svgImageViewData) Set(tag string, value any) bool {
|
||||
return imageView.set(imageView.normalizeTag(tag), value)
|
||||
}
|
||||
|
||||
func (imageView *svgImageViewData) set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
imageView.remove(tag)
|
||||
return true
|
||||
}
|
||||
|
||||
func svgImageViewSet(view View, tag PropertyName, value any) []PropertyName {
|
||||
switch tag {
|
||||
case Content:
|
||||
if text, ok := value.(string); ok {
|
||||
imageView.properties[Content] = text
|
||||
if imageView.created {
|
||||
updateInnerHTML(imageView.htmlID(), imageView.session)
|
||||
}
|
||||
imageView.propertyChangedEvent(Content)
|
||||
return true
|
||||
view.setRaw(Content, text)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
notCompatibleType(Source, value)
|
||||
return false
|
||||
return nil
|
||||
|
||||
default:
|
||||
return imageView.viewData.set(tag, value)
|
||||
return viewSet(view, tag, value)
|
||||
}
|
||||
}
|
||||
|
||||
func (imageView *svgImageViewData) Get(tag string) any {
|
||||
return imageView.viewData.get(imageView.normalizeTag(tag))
|
||||
func svgImageViewPropertyChanged(view View, tag PropertyName) {
|
||||
switch tag {
|
||||
case Content:
|
||||
updateInnerHTML(view.htmlID(), view.Session())
|
||||
|
||||
default:
|
||||
viewPropertyChanged(view, tag)
|
||||
}
|
||||
}
|
||||
|
||||
func (imageView *svgImageViewData) htmlTag() string {
|
||||
|
|
|
@ -256,78 +256,3 @@ func (style *simpleTableLineStyle) RowStyle(row int) Params {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (table *tableViewData) setLineStyle(tag string, value any) bool {
|
||||
switch value := value.(type) {
|
||||
case []Params:
|
||||
if len(value) > 0 {
|
||||
style := new(simpleTableLineStyle)
|
||||
style.params = value
|
||||
table.properties[tag] = style
|
||||
} else {
|
||||
delete(table.properties, tag)
|
||||
}
|
||||
|
||||
case DataNode:
|
||||
if params := value.ArrayAsParams(); len(params) > 0 {
|
||||
style := new(simpleTableLineStyle)
|
||||
style.params = params
|
||||
table.properties[tag] = style
|
||||
} else {
|
||||
delete(table.properties, tag)
|
||||
}
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (table *tableViewData) setRowStyle(value any) bool {
|
||||
switch value := value.(type) {
|
||||
case TableRowStyle:
|
||||
table.properties[RowStyle] = value
|
||||
}
|
||||
return table.setLineStyle(RowStyle, value)
|
||||
}
|
||||
|
||||
func (table *tableViewData) getRowStyle() TableRowStyle {
|
||||
for _, tag := range []string{RowStyle, Content} {
|
||||
if value := table.getRaw(tag); value != nil {
|
||||
if style, ok := value.(TableRowStyle); ok {
|
||||
return style
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (table *tableViewData) setColumnStyle(value any) bool {
|
||||
switch value := value.(type) {
|
||||
case TableColumnStyle:
|
||||
table.properties[ColumnStyle] = value
|
||||
}
|
||||
return table.setLineStyle(ColumnStyle, value)
|
||||
}
|
||||
|
||||
func (table *tableViewData) getColumnStyle() TableColumnStyle {
|
||||
for _, tag := range []string{ColumnStyle, Content} {
|
||||
if value := table.getRaw(tag); value != nil {
|
||||
if style, ok := value.(TableColumnStyle); ok {
|
||||
return style
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (table *tableViewData) getCellStyle() TableCellStyle {
|
||||
for _, tag := range []string{CellStyle, Content} {
|
||||
if value := table.getRaw(tag); value != nil {
|
||||
if style, ok := value.(TableCellStyle); ok {
|
||||
return style
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
721
tableView.go
721
tableView.go
File diff suppressed because it is too large
Load Diff
|
@ -1,17 +1,19 @@
|
|||
package rui
|
||||
|
||||
import "strings"
|
||||
|
||||
func (cell *tableCellView) Set(tag string, value any) bool {
|
||||
return cell.set(strings.ToLower(tag), value)
|
||||
func newTableCellView(session Session) *tableCellView {
|
||||
view := new(tableCellView)
|
||||
view.init(session)
|
||||
return view
|
||||
}
|
||||
|
||||
func (cell *tableCellView) set(tag string, value any) bool {
|
||||
switch tag {
|
||||
case VerticalAlign:
|
||||
tag = TableVerticalAlign
|
||||
func (cell *tableCellView) init(session Session) {
|
||||
cell.viewData.init(session)
|
||||
cell.normalize = func(tag PropertyName) PropertyName {
|
||||
if tag == VerticalAlign {
|
||||
return TableVerticalAlign
|
||||
}
|
||||
return tag
|
||||
}
|
||||
return cell.viewData.set(tag, value)
|
||||
}
|
||||
|
||||
func (cell *tableCellView) cssStyle(self View, builder cssBuilder) {
|
||||
|
@ -31,8 +33,10 @@ func GetTableContent(view View, subviewID ...string) TableAdapter {
|
|||
}
|
||||
|
||||
if view != nil {
|
||||
if tableView, ok := view.(TableView); ok {
|
||||
return tableView.content()
|
||||
if content := view.getRaw(Content); content != nil {
|
||||
if adapter, ok := content.(TableAdapter); ok {
|
||||
return adapter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,8 +51,12 @@ func GetTableRowStyle(view View, subviewID ...string) TableRowStyle {
|
|||
}
|
||||
|
||||
if view != nil {
|
||||
if tableView, ok := view.(TableView); ok {
|
||||
return tableView.getRowStyle()
|
||||
for _, tag := range []PropertyName{RowStyle, Content} {
|
||||
if value := view.getRaw(tag); value != nil {
|
||||
if style, ok := value.(TableRowStyle); ok {
|
||||
return style
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,8 +71,12 @@ func GetTableColumnStyle(view View, subviewID ...string) TableColumnStyle {
|
|||
}
|
||||
|
||||
if view != nil {
|
||||
if tableView, ok := view.(TableView); ok {
|
||||
return tableView.getColumnStyle()
|
||||
for _, tag := range []PropertyName{ColumnStyle, Content} {
|
||||
if value := view.getRaw(tag); value != nil {
|
||||
if style, ok := value.(TableColumnStyle); ok {
|
||||
return style
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,9 +91,14 @@ func GetTableCellStyle(view View, subviewID ...string) TableCellStyle {
|
|||
}
|
||||
|
||||
if view != nil {
|
||||
if tableView, ok := view.(TableView); ok {
|
||||
return tableView.getCellStyle()
|
||||
for _, tag := range []PropertyName{CellStyle, Content} {
|
||||
if value := view.getRaw(tag); value != nil {
|
||||
if style, ok := value.(TableCellStyle); ok {
|
||||
return style
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -125,9 +142,7 @@ func GetTableCurrent(view View, subviewID ...string) CellIndex {
|
|||
|
||||
if view != nil {
|
||||
if selectionMode := GetTableSelectionMode(view); selectionMode != NoneSelection {
|
||||
if tableView, ok := view.(TableView); ok {
|
||||
return tableView.getCurrent()
|
||||
}
|
||||
return tableViewCurrent(view)
|
||||
}
|
||||
}
|
||||
return CellIndex{Row: -1, Column: -1}
|
||||
|
@ -137,34 +152,14 @@ func GetTableCurrent(view View, subviewID ...string) CellIndex {
|
|||
// If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetTableCellClickedListeners(view View, subviewID ...string) []func(TableView, int, int) {
|
||||
if len(subviewID) > 0 && subviewID[0] != "" {
|
||||
view = ViewByID(view, subviewID[0])
|
||||
}
|
||||
if view != nil {
|
||||
if value := view.Get(TableCellClickedEvent); value != nil {
|
||||
if result, ok := value.([]func(TableView, int, int)); ok {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
return []func(TableView, int, int){}
|
||||
return getEventWithOldListeners[TableView, int](view, subviewID, TableCellClickedEvent)
|
||||
}
|
||||
|
||||
// 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 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) {
|
||||
if len(subviewID) > 0 && subviewID[0] != "" {
|
||||
view = ViewByID(view, subviewID[0])
|
||||
}
|
||||
if view != nil {
|
||||
if value := view.Get(TableCellSelectedEvent); value != nil {
|
||||
if result, ok := value.([]func(TableView, int, int)); ok {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
return []func(TableView, int, int){}
|
||||
return getEventWithOldListeners[TableView, int](view, subviewID, TableCellSelectedEvent)
|
||||
}
|
||||
|
||||
// GetTableRowClickedListeners returns listeners of event which occurs when the user clicks on a table row.
|
||||
|
|
610
tabsLayout.go
610
tabsLayout.go
|
@ -5,7 +5,7 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// Constants for [TabsLayout] specific properties and events
|
||||
// Constants for [TabsLayout] specific view and events
|
||||
const (
|
||||
// CurrentTabChangedEvent is the constant for "current-tab-changed" property tag.
|
||||
//
|
||||
|
@ -25,7 +25,7 @@ const (
|
|||
// `func(newTab, oldTab int)`,
|
||||
// `func(newTab int)`,
|
||||
// `func()`.
|
||||
CurrentTabChangedEvent = "current-tab-changed"
|
||||
CurrentTabChangedEvent PropertyName = "current-tab-changed"
|
||||
|
||||
// Icon is the constant for "icon" property tag.
|
||||
//
|
||||
|
@ -47,7 +47,7 @@ const (
|
|||
// Values:
|
||||
// `true` or `1` or "true", "yes", "on", "1" - Tab(s) has close button.
|
||||
// `false` or `0` or "false", "no", "off", "0" - No close button in tab(s).
|
||||
TabCloseButton = "tab-close-button"
|
||||
TabCloseButton PropertyName = "tab-close-button"
|
||||
|
||||
// TabCloseEvent is the constant for "tab-close-event" property tag.
|
||||
//
|
||||
|
@ -65,7 +65,7 @@ const (
|
|||
// `func(tab int)`,
|
||||
// `func(tabsLayout rui.TabsLayout)`,
|
||||
// `func()`.
|
||||
TabCloseEvent = "tab-close-event"
|
||||
TabCloseEvent PropertyName = "tab-close-event"
|
||||
|
||||
// Tabs is the constant for "tabs" property tag.
|
||||
//
|
||||
|
@ -82,7 +82,7 @@ const (
|
|||
// `4`(`LeftListTabs`) or "left-list" - Tabs on the left. The tabs are displayed as a list.
|
||||
// `5`(`RightListTabs`) or "right-list" - Tabs on the right. The tabs are displayed as a list.
|
||||
// `6`(`HiddenTabs`) or "hidden" - Tabs are hidden.
|
||||
Tabs = "tabs"
|
||||
Tabs PropertyName = "tabs"
|
||||
|
||||
// TabBarStyle is the constant for "tab-bar-style" property tag.
|
||||
//
|
||||
|
@ -90,7 +90,7 @@ const (
|
|||
// Set the style for the display of the tab bar. The default value is "ruiTabBar".
|
||||
//
|
||||
// Supported types: `string`.
|
||||
TabBarStyle = "tab-bar-style"
|
||||
TabBarStyle PropertyName = "tab-bar-style"
|
||||
|
||||
// TabStyle is the constant for "tab-style" property tag.
|
||||
//
|
||||
|
@ -98,7 +98,7 @@ const (
|
|||
// Set the style for the display of the tab. The default value is "ruiTab" or "ruiVerticalTab".
|
||||
//
|
||||
// Supported types: `string`.
|
||||
TabStyle = "tab-style"
|
||||
TabStyle PropertyName = "tab-style"
|
||||
|
||||
// CurrentTabStyle is the constant for "current-tab-style" property tag.
|
||||
//
|
||||
|
@ -107,7 +107,7 @@ const (
|
|||
// "ruiCurrentVerticalTab".
|
||||
//
|
||||
// Supported types: `string`.
|
||||
CurrentTabStyle = "current-tab-style"
|
||||
CurrentTabStyle PropertyName = "current-tab-style"
|
||||
|
||||
inactiveTabStyle = "data-inactiveTabStyle"
|
||||
activeTabStyle = "data-activeTabStyle"
|
||||
|
@ -139,8 +139,6 @@ type TabsLayout interface {
|
|||
|
||||
type tabsLayoutData struct {
|
||||
viewsContainerData
|
||||
tabListener []func(TabsLayout, int, int)
|
||||
tabCloseListener []func(TabsLayout, int)
|
||||
}
|
||||
|
||||
// NewTabsLayout create new TabsLayout object and return it
|
||||
|
@ -152,7 +150,8 @@ func NewTabsLayout(session Session, params Params) TabsLayout {
|
|||
}
|
||||
|
||||
func newTabsLayout(session Session) View {
|
||||
return NewTabsLayout(session, nil)
|
||||
//return NewTabsLayout(session, nil)
|
||||
return new(tabsLayoutData)
|
||||
}
|
||||
|
||||
// Init initialize fields of ViewsContainer by default values
|
||||
|
@ -160,345 +159,229 @@ func (tabsLayout *tabsLayoutData) init(session Session) {
|
|||
tabsLayout.viewsContainerData.init(session)
|
||||
tabsLayout.tag = "TabsLayout"
|
||||
tabsLayout.systemClass = "ruiTabsLayout"
|
||||
tabsLayout.tabListener = []func(TabsLayout, int, int){}
|
||||
tabsLayout.tabCloseListener = []func(TabsLayout, int){}
|
||||
tabsLayout.set = tabsLayout.setFunc
|
||||
tabsLayout.changed = tabsLayout.propertyChanged
|
||||
}
|
||||
|
||||
func (tabsLayout *tabsLayoutData) String() string {
|
||||
return getViewString(tabsLayout, nil)
|
||||
}
|
||||
|
||||
func (tabsLayout *tabsLayoutData) currentItem(defaultValue int) int {
|
||||
result, _ := intProperty(tabsLayout, Current, tabsLayout.session, defaultValue)
|
||||
func tabsLayoutCurrent(view View, defaultValue int) int {
|
||||
result, _ := intProperty(view, Current, view.Session(), defaultValue)
|
||||
return result
|
||||
}
|
||||
|
||||
func (tabsLayout *tabsLayoutData) Get(tag string) any {
|
||||
return tabsLayout.get(strings.ToLower(tag))
|
||||
}
|
||||
|
||||
func (tabsLayout *tabsLayoutData) get(tag string) any {
|
||||
func (tabsLayout *tabsLayoutData) setFunc(view View, tag PropertyName, value any) []PropertyName {
|
||||
switch tag {
|
||||
case CurrentTabChangedEvent:
|
||||
return tabsLayout.tabListener
|
||||
return setEventWithOldListener[TabsLayout, int](view, tag, value)
|
||||
|
||||
case TabCloseEvent:
|
||||
return tabsLayout.tabCloseListener
|
||||
}
|
||||
|
||||
return tabsLayout.viewsContainerData.get(tag)
|
||||
}
|
||||
|
||||
func (tabsLayout *tabsLayoutData) Remove(tag string) {
|
||||
tabsLayout.remove(strings.ToLower(tag))
|
||||
}
|
||||
|
||||
func (tabsLayout *tabsLayoutData) remove(tag string) {
|
||||
switch tag {
|
||||
case CurrentTabChangedEvent:
|
||||
if len(tabsLayout.tabListener) > 0 {
|
||||
tabsLayout.tabListener = []func(TabsLayout, int, int){}
|
||||
tabsLayout.propertyChangedEvent(tag)
|
||||
}
|
||||
return
|
||||
|
||||
case TabCloseEvent:
|
||||
if len(tabsLayout.tabCloseListener) > 0 {
|
||||
tabsLayout.tabCloseListener = []func(TabsLayout, int){}
|
||||
tabsLayout.propertyChangedEvent(tag)
|
||||
}
|
||||
return
|
||||
return setViewEventListener[TabsLayout, int](view, tag, value)
|
||||
|
||||
case Current:
|
||||
oldCurrent := tabsLayout.currentItem(0)
|
||||
delete(tabsLayout.properties, Current)
|
||||
if oldCurrent == 0 {
|
||||
return
|
||||
}
|
||||
if tabsLayout.created {
|
||||
tabsLayout.session.callFunc("activateTab", tabsLayout.htmlID(), 0)
|
||||
for _, listener := range tabsLayout.tabListener {
|
||||
listener(tabsLayout, 0, oldCurrent)
|
||||
}
|
||||
}
|
||||
view.setRaw("old-current", tabsLayoutCurrent(view, -1))
|
||||
|
||||
case Tabs:
|
||||
delete(tabsLayout.properties, Tabs)
|
||||
if tabsLayout.created {
|
||||
htmlID := tabsLayout.htmlID()
|
||||
tabsLayout.session.updateProperty(htmlID, inactiveTabStyle, tabsLayout.inactiveTabStyle())
|
||||
tabsLayout.session.updateProperty(htmlID, activeTabStyle, tabsLayout.activeTabStyle())
|
||||
updateCSSStyle(htmlID, tabsLayout.session)
|
||||
updateInnerHTML(htmlID, tabsLayout.session)
|
||||
}
|
||||
|
||||
case TabStyle, CurrentTabStyle:
|
||||
delete(tabsLayout.properties, tag)
|
||||
if tabsLayout.created {
|
||||
htmlID := tabsLayout.htmlID()
|
||||
tabsLayout.session.updateProperty(htmlID, inactiveTabStyle, tabsLayout.inactiveTabStyle())
|
||||
tabsLayout.session.updateProperty(htmlID, activeTabStyle, tabsLayout.activeTabStyle())
|
||||
updateInnerHTML(htmlID, tabsLayout.session)
|
||||
}
|
||||
|
||||
case TabCloseButton:
|
||||
delete(tabsLayout.properties, tag)
|
||||
if tabsLayout.created {
|
||||
updateInnerHTML(tabsLayout.htmlID(), tabsLayout.session)
|
||||
}
|
||||
|
||||
default:
|
||||
tabsLayout.viewsContainerData.remove(tag)
|
||||
return
|
||||
}
|
||||
|
||||
tabsLayout.propertyChangedEvent(tag)
|
||||
}
|
||||
|
||||
func (tabsLayout *tabsLayoutData) Set(tag string, value any) bool {
|
||||
return tabsLayout.set(strings.ToLower(tag), value)
|
||||
}
|
||||
|
||||
func (tabsLayout *tabsLayoutData) set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
tabsLayout.remove(tag)
|
||||
return true
|
||||
}
|
||||
|
||||
switch tag {
|
||||
case CurrentTabChangedEvent:
|
||||
listeners := tabsLayout.valueToTabListeners(value)
|
||||
if listeners == nil {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
tabsLayout.tabListener = listeners
|
||||
|
||||
case TabCloseEvent:
|
||||
listeners, ok := valueToEventListeners[TabsLayout, int](value)
|
||||
if !ok {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
} else if listeners == nil {
|
||||
listeners = []func(TabsLayout, int){}
|
||||
}
|
||||
tabsLayout.tabCloseListener = listeners
|
||||
|
||||
case Current:
|
||||
if current, ok := value.(int); ok && current < 0 {
|
||||
tabsLayout.remove(Current)
|
||||
return true
|
||||
view.setRaw(Current, nil)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
oldCurrent := tabsLayout.currentItem(-1)
|
||||
if !tabsLayout.setIntProperty(Current, value) {
|
||||
return false
|
||||
}
|
||||
return setIntProperty(view, Current, value)
|
||||
|
||||
current := tabsLayout.currentItem(0)
|
||||
if oldCurrent == current {
|
||||
return true
|
||||
case TabStyle, CurrentTabStyle, TabBarStyle:
|
||||
if text, ok := value.(string); ok {
|
||||
return setStringPropertyValue(view, tag, text)
|
||||
}
|
||||
if tabsLayout.created {
|
||||
tabsLayout.session.callFunc("activateTab", tabsLayout.htmlID(), current)
|
||||
for _, listener := range tabsLayout.tabListener {
|
||||
notCompatibleType(tag, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
return tabsLayout.viewsContainerData.setFunc(tabsLayout, tag, value)
|
||||
}
|
||||
|
||||
func (tabsLayout *tabsLayoutData) propertyChanged(view View, tag PropertyName) {
|
||||
switch tag {
|
||||
case Current:
|
||||
session := view.Session()
|
||||
current := GetCurrent(view)
|
||||
session.callFunc("activateTab", view.htmlID(), current)
|
||||
|
||||
if listeners := getEventWithOldListeners[TabsLayout, int](view, nil, CurrentTabChangedEvent); len(listeners) > 0 {
|
||||
oldCurrent, _ := intProperty(view, "old-current", session, -1)
|
||||
for _, listener := range listeners {
|
||||
listener(tabsLayout, current, oldCurrent)
|
||||
}
|
||||
}
|
||||
|
||||
case Tabs:
|
||||
if !tabsLayout.setEnumProperty(Tabs, value, enumProperties[Tabs].values) {
|
||||
return false
|
||||
}
|
||||
if tabsLayout.created {
|
||||
htmlID := tabsLayout.htmlID()
|
||||
tabsLayout.session.updateProperty(htmlID, inactiveTabStyle, tabsLayout.inactiveTabStyle())
|
||||
tabsLayout.session.updateProperty(htmlID, activeTabStyle, tabsLayout.activeTabStyle())
|
||||
updateCSSStyle(htmlID, tabsLayout.session)
|
||||
updateInnerHTML(htmlID, tabsLayout.session)
|
||||
}
|
||||
htmlID := view.htmlID()
|
||||
session := view.Session()
|
||||
session.updateProperty(htmlID, inactiveTabStyle, tabsLayoutInactiveTabStyle(view))
|
||||
session.updateProperty(htmlID, activeTabStyle, tabsLayoutActiveTabStyle(view))
|
||||
updateCSSStyle(htmlID, session)
|
||||
updateInnerHTML(htmlID, session)
|
||||
|
||||
case TabStyle, CurrentTabStyle, TabBarStyle:
|
||||
if text, ok := value.(string); ok {
|
||||
if text == "" {
|
||||
delete(tabsLayout.properties, tag)
|
||||
} else {
|
||||
tabsLayout.properties[tag] = text
|
||||
}
|
||||
} else {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
|
||||
if tabsLayout.created {
|
||||
htmlID := tabsLayout.htmlID()
|
||||
tabsLayout.session.updateProperty(htmlID, inactiveTabStyle, tabsLayout.inactiveTabStyle())
|
||||
tabsLayout.session.updateProperty(htmlID, activeTabStyle, tabsLayout.activeTabStyle())
|
||||
updateInnerHTML(htmlID, tabsLayout.session)
|
||||
}
|
||||
htmlID := view.htmlID()
|
||||
session := view.Session()
|
||||
session.updateProperty(htmlID, inactiveTabStyle, tabsLayoutInactiveTabStyle(view))
|
||||
session.updateProperty(htmlID, activeTabStyle, tabsLayoutActiveTabStyle(view))
|
||||
updateInnerHTML(htmlID, session)
|
||||
|
||||
case TabCloseButton:
|
||||
if !tabsLayout.setBoolProperty(tag, value) {
|
||||
return false
|
||||
}
|
||||
if tabsLayout.created {
|
||||
updateInnerHTML(tabsLayout.htmlID(), tabsLayout.session)
|
||||
}
|
||||
updateInnerHTML(view.htmlID(), view.Session())
|
||||
|
||||
default:
|
||||
return tabsLayout.viewsContainerData.set(tag, value)
|
||||
viewsContainerPropertyChanged(view, tag)
|
||||
}
|
||||
|
||||
tabsLayout.propertyChangedEvent(tag)
|
||||
return true
|
||||
}
|
||||
|
||||
func (tabsLayout *tabsLayoutData) valueToTabListeners(value any) []func(TabsLayout, int, int) {
|
||||
if value == nil {
|
||||
return []func(TabsLayout, int, int){}
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case func(TabsLayout, int, int):
|
||||
return []func(TabsLayout, int, int){value}
|
||||
|
||||
case func(TabsLayout, int):
|
||||
fn := func(view TabsLayout, current, _ int) {
|
||||
value(view, current)
|
||||
/*
|
||||
func (tabsLayout *tabsLayoutData) valueToTabListeners(value any) []func(TabsLayout, int, int) {
|
||||
if value == nil {
|
||||
return []func(TabsLayout, int, int){}
|
||||
}
|
||||
return []func(TabsLayout, int, int){fn}
|
||||
|
||||
case func(TabsLayout):
|
||||
fn := func(view TabsLayout, _, _ int) {
|
||||
value(view)
|
||||
}
|
||||
return []func(TabsLayout, int, int){fn}
|
||||
switch value := value.(type) {
|
||||
case func(TabsLayout, int, int):
|
||||
return []func(TabsLayout, int, int){value}
|
||||
|
||||
case func(int, int):
|
||||
fn := func(_ TabsLayout, current, old int) {
|
||||
value(current, old)
|
||||
}
|
||||
return []func(TabsLayout, int, int){fn}
|
||||
|
||||
case func(int):
|
||||
fn := func(_ TabsLayout, current, _ int) {
|
||||
value(current)
|
||||
}
|
||||
return []func(TabsLayout, int, int){fn}
|
||||
|
||||
case func():
|
||||
fn := func(TabsLayout, int, int) {
|
||||
value()
|
||||
}
|
||||
return []func(TabsLayout, int, int){fn}
|
||||
|
||||
case []func(TabsLayout, int, int):
|
||||
return value
|
||||
|
||||
case []func(TabsLayout, int):
|
||||
listeners := make([]func(TabsLayout, int, int), len(value))
|
||||
for i, val := range value {
|
||||
if val == nil {
|
||||
return nil
|
||||
case func(TabsLayout, int):
|
||||
fn := func(view TabsLayout, current, _ int) {
|
||||
value(view, current)
|
||||
}
|
||||
listeners[i] = func(view TabsLayout, current, _ int) {
|
||||
val(view, current)
|
||||
}
|
||||
}
|
||||
return listeners
|
||||
return []func(TabsLayout, int, int){fn}
|
||||
|
||||
case []func(TabsLayout):
|
||||
listeners := make([]func(TabsLayout, int, int), len(value))
|
||||
for i, val := range value {
|
||||
if val == nil {
|
||||
return nil
|
||||
case func(TabsLayout):
|
||||
fn := func(view TabsLayout, _, _ int) {
|
||||
value(view)
|
||||
}
|
||||
listeners[i] = func(view TabsLayout, _, _ int) {
|
||||
val(view)
|
||||
}
|
||||
}
|
||||
return listeners
|
||||
return []func(TabsLayout, int, int){fn}
|
||||
|
||||
case []func(int, int):
|
||||
listeners := make([]func(TabsLayout, int, int), len(value))
|
||||
for i, val := range value {
|
||||
if val == nil {
|
||||
return nil
|
||||
case func(int, int):
|
||||
fn := func(_ TabsLayout, current, old int) {
|
||||
value(current, old)
|
||||
}
|
||||
listeners[i] = func(_ TabsLayout, current, old int) {
|
||||
val(current, old)
|
||||
}
|
||||
}
|
||||
return listeners
|
||||
return []func(TabsLayout, int, int){fn}
|
||||
|
||||
case []func(int):
|
||||
listeners := make([]func(TabsLayout, int, int), len(value))
|
||||
for i, val := range value {
|
||||
if val == nil {
|
||||
return nil
|
||||
case func(int):
|
||||
fn := func(_ TabsLayout, current, _ int) {
|
||||
value(current)
|
||||
}
|
||||
listeners[i] = func(_ TabsLayout, current, _ int) {
|
||||
val(current)
|
||||
}
|
||||
}
|
||||
return listeners
|
||||
return []func(TabsLayout, int, int){fn}
|
||||
|
||||
case []func():
|
||||
listeners := make([]func(TabsLayout, int, int), len(value))
|
||||
for i, val := range value {
|
||||
if val == nil {
|
||||
return nil
|
||||
case func():
|
||||
fn := func(TabsLayout, int, int) {
|
||||
value()
|
||||
}
|
||||
listeners[i] = func(TabsLayout, int, int) {
|
||||
val()
|
||||
}
|
||||
}
|
||||
return listeners
|
||||
return []func(TabsLayout, int, int){fn}
|
||||
|
||||
case []any:
|
||||
listeners := make([]func(TabsLayout, int, int), len(value))
|
||||
for i, val := range value {
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
switch val := val.(type) {
|
||||
case func(TabsLayout, int, int):
|
||||
listeners[i] = val
|
||||
case []func(TabsLayout, int, int):
|
||||
return value
|
||||
|
||||
case func(TabsLayout, int):
|
||||
case []func(TabsLayout, int):
|
||||
listeners := make([]func(TabsLayout, int, int), len(value))
|
||||
for i, val := range value {
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
listeners[i] = func(view TabsLayout, current, _ int) {
|
||||
val(view, current)
|
||||
}
|
||||
}
|
||||
return listeners
|
||||
|
||||
case func(TabsLayout):
|
||||
case []func(TabsLayout):
|
||||
listeners := make([]func(TabsLayout, int, int), len(value))
|
||||
for i, val := range value {
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
listeners[i] = func(view TabsLayout, _, _ int) {
|
||||
val(view)
|
||||
}
|
||||
}
|
||||
return listeners
|
||||
|
||||
case func(int, int):
|
||||
case []func(int, int):
|
||||
listeners := make([]func(TabsLayout, int, int), len(value))
|
||||
for i, val := range value {
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
listeners[i] = func(_ TabsLayout, current, old int) {
|
||||
val(current, old)
|
||||
}
|
||||
}
|
||||
return listeners
|
||||
|
||||
case func(int):
|
||||
case []func(int):
|
||||
listeners := make([]func(TabsLayout, int, int), len(value))
|
||||
for i, val := range value {
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
listeners[i] = func(_ TabsLayout, current, _ int) {
|
||||
val(current)
|
||||
}
|
||||
}
|
||||
return listeners
|
||||
|
||||
case func():
|
||||
case []func():
|
||||
listeners := make([]func(TabsLayout, int, int), len(value))
|
||||
for i, val := range value {
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
listeners[i] = func(TabsLayout, int, int) {
|
||||
val()
|
||||
}
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return listeners
|
||||
}
|
||||
return listeners
|
||||
|
||||
return nil
|
||||
}
|
||||
case []any:
|
||||
listeners := make([]func(TabsLayout, int, int), len(value))
|
||||
for i, val := range value {
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
switch val := val.(type) {
|
||||
case func(TabsLayout, int, int):
|
||||
listeners[i] = val
|
||||
|
||||
case func(TabsLayout, int):
|
||||
listeners[i] = func(view TabsLayout, current, _ int) {
|
||||
val(view, current)
|
||||
}
|
||||
|
||||
case func(TabsLayout):
|
||||
listeners[i] = func(view TabsLayout, _, _ int) {
|
||||
val(view)
|
||||
}
|
||||
|
||||
case func(int, int):
|
||||
listeners[i] = func(_ TabsLayout, current, old int) {
|
||||
val(current, old)
|
||||
}
|
||||
|
||||
case func(int):
|
||||
listeners[i] = func(_ TabsLayout, current, _ int) {
|
||||
val(current)
|
||||
}
|
||||
|
||||
case func():
|
||||
listeners[i] = func(TabsLayout, int, int) {
|
||||
val()
|
||||
}
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return listeners
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
*/
|
||||
|
||||
func (tabsLayout *tabsLayoutData) tabsLocation() int {
|
||||
tabs, _ := enumProperty(tabsLayout, Tabs, tabsLayout.session, 0)
|
||||
|
@ -519,36 +402,42 @@ func (tabsLayout *tabsLayoutData) tabBarStyle() string {
|
|||
return "ruiTabBar"
|
||||
}
|
||||
|
||||
func (tabsLayout *tabsLayoutData) inactiveTabStyle() string {
|
||||
if style, ok := stringProperty(tabsLayout, TabStyle, tabsLayout.session); ok {
|
||||
func tabsLayoutInactiveTabStyle(view View) string {
|
||||
session := view.Session()
|
||||
if style, ok := stringProperty(view, TabStyle, session); ok {
|
||||
return style
|
||||
}
|
||||
if value := valueFromStyle(tabsLayout, TabStyle); value != nil {
|
||||
if value := valueFromStyle(view, TabStyle); value != nil {
|
||||
if style, ok := value.(string); ok {
|
||||
if style, ok = tabsLayout.session.resolveConstants(style); ok {
|
||||
if style, ok = session.resolveConstants(style); ok {
|
||||
return style
|
||||
}
|
||||
}
|
||||
}
|
||||
switch tabsLayout.tabsLocation() {
|
||||
|
||||
tabs, _ := enumProperty(view, Tabs, session, 0)
|
||||
switch tabs {
|
||||
case LeftTabs, RightTabs:
|
||||
return "ruiVerticalTab"
|
||||
}
|
||||
return "ruiTab"
|
||||
}
|
||||
|
||||
func (tabsLayout *tabsLayoutData) activeTabStyle() string {
|
||||
if style, ok := stringProperty(tabsLayout, CurrentTabStyle, tabsLayout.session); ok {
|
||||
func tabsLayoutActiveTabStyle(view View) string {
|
||||
session := view.Session()
|
||||
if style, ok := stringProperty(view, CurrentTabStyle, session); ok {
|
||||
return style
|
||||
}
|
||||
if value := valueFromStyle(tabsLayout, CurrentTabStyle); value != nil {
|
||||
if value := valueFromStyle(view, CurrentTabStyle); value != nil {
|
||||
if style, ok := value.(string); ok {
|
||||
if style, ok = tabsLayout.session.resolveConstants(style); ok {
|
||||
if style, ok = session.resolveConstants(style); ok {
|
||||
return style
|
||||
}
|
||||
}
|
||||
}
|
||||
switch tabsLayout.tabsLocation() {
|
||||
|
||||
tabs, _ := enumProperty(view, Tabs, session, 0)
|
||||
switch tabs {
|
||||
case LeftTabs, RightTabs:
|
||||
return "ruiCurrentVerticalTab"
|
||||
}
|
||||
|
@ -610,7 +499,7 @@ func (tabsLayout *tabsLayoutData) ListItem(index int, session Session) View {
|
|||
Column: 2,
|
||||
Content: "✕",
|
||||
ClickEvent: func() {
|
||||
for _, listener := range tabsLayout.tabCloseListener {
|
||||
for _, listener := range getEventListeners[TabsLayout, int](tabsLayout, nil, TabCloseEvent) {
|
||||
listener(tabsLayout, index)
|
||||
}
|
||||
},
|
||||
|
@ -632,7 +521,7 @@ func (tabsLayout *tabsLayoutData) IsListItemEnabled(index int) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func (tabsLayout *tabsLayoutData) updateTitle(view View, tag string) {
|
||||
func (tabsLayout *tabsLayoutData) updateTitle(view View, tag PropertyName) {
|
||||
session := tabsLayout.session
|
||||
title, _ := stringProperty(view, Title, session)
|
||||
if !GetNotTranslate(tabsLayout) {
|
||||
|
@ -641,13 +530,13 @@ func (tabsLayout *tabsLayoutData) updateTitle(view View, tag string) {
|
|||
session.updateInnerHTML(view.htmlID()+"-title", title)
|
||||
}
|
||||
|
||||
func (tabsLayout *tabsLayoutData) updateIcon(view View, tag string) {
|
||||
func (tabsLayout *tabsLayoutData) updateIcon(view View, tag PropertyName) {
|
||||
session := tabsLayout.session
|
||||
icon, _ := stringProperty(view, Icon, session)
|
||||
session.updateProperty(view.htmlID()+"-icon", "src", icon)
|
||||
}
|
||||
|
||||
func (tabsLayout *tabsLayoutData) updateTabCloseButton(view View, tag string) {
|
||||
func (tabsLayout *tabsLayoutData) updateTabCloseButton(view View, tag PropertyName) {
|
||||
updateInnerHTML(tabsLayout.htmlID(), tabsLayout.session)
|
||||
}
|
||||
|
||||
|
@ -662,11 +551,8 @@ func (tabsLayout *tabsLayoutData) Append(view View) {
|
|||
view.SetChangeListener(Icon, tabsLayout.updateIcon)
|
||||
view.SetChangeListener(TabCloseButton, tabsLayout.updateTabCloseButton)
|
||||
if len(tabsLayout.views) == 1 {
|
||||
tabsLayout.properties[Current] = 0
|
||||
for _, listener := range tabsLayout.tabListener {
|
||||
listener(tabsLayout, 0, -1)
|
||||
}
|
||||
defer tabsLayout.propertyChangedEvent(Current)
|
||||
tabsLayout.setRaw(Current, nil)
|
||||
tabsLayout.Set(Current, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -677,9 +563,9 @@ func (tabsLayout *tabsLayoutData) Insert(view View, index int) {
|
|||
tabsLayout.views = []View{}
|
||||
}
|
||||
if view != nil {
|
||||
if current := tabsLayout.currentItem(0); current >= index {
|
||||
tabsLayout.properties[Current] = current + 1
|
||||
defer tabsLayout.propertyChangedEvent(Current)
|
||||
if current := GetCurrent(tabsLayout); current >= index {
|
||||
tabsLayout.setRaw(Current, current+1)
|
||||
defer tabsLayout.currentChanged()
|
||||
}
|
||||
tabsLayout.viewsContainerData.Insert(view, index)
|
||||
view.SetChangeListener(Title, tabsLayout.updateTitle)
|
||||
|
@ -688,56 +574,87 @@ func (tabsLayout *tabsLayoutData) Insert(view View, index int) {
|
|||
}
|
||||
}
|
||||
|
||||
func (tabsLayout *tabsLayoutData) currentChanged() {
|
||||
if listener, ok := tabsLayout.changeListener[Current]; ok {
|
||||
listener(tabsLayout, Current)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove removes view from list and return it
|
||||
func (tabsLayout *tabsLayoutData) RemoveView(index int) View {
|
||||
if tabsLayout.views == nil {
|
||||
tabsLayout.views = []View{}
|
||||
|
||||
if index < 0 || index >= len(tabsLayout.views) {
|
||||
return nil
|
||||
}
|
||||
|
||||
count := len(tabsLayout.views)
|
||||
if index < 0 || index >= count {
|
||||
return nil
|
||||
oldCurrent := GetCurrent(tabsLayout)
|
||||
newCurrent := oldCurrent
|
||||
if index < oldCurrent || (index == oldCurrent && oldCurrent > 0) {
|
||||
newCurrent--
|
||||
}
|
||||
|
||||
view := tabsLayout.views[index]
|
||||
view.setParentID("")
|
||||
view.SetChangeListener(Title, nil)
|
||||
view.SetChangeListener(Icon, nil)
|
||||
view.SetChangeListener(TabCloseButton, nil)
|
||||
if view := tabsLayout.viewsContainerData.RemoveView(index); view != nil {
|
||||
view.SetChangeListener(Title, nil)
|
||||
view.SetChangeListener(Icon, nil)
|
||||
view.SetChangeListener(TabCloseButton, nil)
|
||||
|
||||
current := tabsLayout.currentItem(0)
|
||||
if index < current || (index == current && current > 0) {
|
||||
current--
|
||||
if newCurrent != oldCurrent {
|
||||
tabsLayout.setRaw(Current, newCurrent)
|
||||
tabsLayout.currentChanged()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
if len(tabsLayout.views) == 1 {
|
||||
tabsLayout.views = []View{}
|
||||
current = -1
|
||||
} else if index == 0 {
|
||||
tabsLayout.views = tabsLayout.views[1:]
|
||||
} else if index == count-1 {
|
||||
tabsLayout.views = tabsLayout.views[:index]
|
||||
} else {
|
||||
tabsLayout.views = append(tabsLayout.views[:index], tabsLayout.views[index+1:]...)
|
||||
}
|
||||
/*
|
||||
if tabsLayout.views == nil {
|
||||
tabsLayout.views = []View{}
|
||||
return nil
|
||||
}
|
||||
|
||||
updateInnerHTML(tabsLayout.parentHTMLID(), tabsLayout.session)
|
||||
tabsLayout.propertyChangedEvent(Content)
|
||||
count := len(tabsLayout.views)
|
||||
if index < 0 || index >= count {
|
||||
return nil
|
||||
}
|
||||
|
||||
delete(tabsLayout.properties, Current)
|
||||
tabsLayout.set(Current, current)
|
||||
return view
|
||||
view := tabsLayout.views[index]
|
||||
view.setParentID("")
|
||||
view.SetChangeListener(Title, nil)
|
||||
view.SetChangeListener(Icon, nil)
|
||||
view.SetChangeListener(TabCloseButton, nil)
|
||||
|
||||
current := GetCurrent(tabsLayout)
|
||||
if index < current || (index == current && current > 0) {
|
||||
current--
|
||||
}
|
||||
|
||||
if len(tabsLayout.views) == 1 {
|
||||
tabsLayout.views = []View{}
|
||||
current = -1
|
||||
} else if index == 0 {
|
||||
tabsLayout.views = tabsLayout.views[1:]
|
||||
} else if index == count-1 {
|
||||
tabsLayout.views = tabsLayout.views[:index]
|
||||
} else {
|
||||
tabsLayout.views = append(tabsLayout.views[:index], tabsLayout.views[index+1:]...)
|
||||
}
|
||||
|
||||
updateInnerHTML(tabsLayout.parentHTMLID(), tabsLayout.session)
|
||||
tabsLayout.propertyChangedEvent(Content)
|
||||
|
||||
delete(tabsLayout.view, Current)
|
||||
tabsLayout.Set(Current, current)
|
||||
return view
|
||||
*/
|
||||
}
|
||||
|
||||
func (tabsLayout *tabsLayoutData) htmlProperties(self View, buffer *strings.Builder) {
|
||||
tabsLayout.viewsContainerData.htmlProperties(self, buffer)
|
||||
buffer.WriteString(` data-inactiveTabStyle="`)
|
||||
buffer.WriteString(tabsLayout.inactiveTabStyle())
|
||||
buffer.WriteString(tabsLayoutInactiveTabStyle(tabsLayout))
|
||||
buffer.WriteString(`" data-activeTabStyle="`)
|
||||
buffer.WriteString(tabsLayout.activeTabStyle())
|
||||
buffer.WriteString(tabsLayoutActiveTabStyle(tabsLayout))
|
||||
buffer.WriteString(`" data-current="`)
|
||||
buffer.WriteString(strconv.Itoa(tabsLayout.currentItem(0)))
|
||||
buffer.WriteString(strconv.Itoa(GetCurrent(tabsLayout)))
|
||||
buffer.WriteRune('"')
|
||||
}
|
||||
|
||||
|
@ -763,8 +680,7 @@ func (tabsLayout *tabsLayoutData) htmlSubviews(self View, buffer *strings.Builde
|
|||
return
|
||||
}
|
||||
|
||||
//viewCount := len(tabsLayout.views)
|
||||
current := tabsLayout.currentItem(0)
|
||||
current := GetCurrent(tabsLayout)
|
||||
location := tabsLayout.tabsLocation()
|
||||
tabsLayoutID := tabsLayout.htmlID()
|
||||
|
||||
|
@ -796,8 +712,8 @@ func (tabsLayout *tabsLayoutData) htmlSubviews(self View, buffer *strings.Builde
|
|||
|
||||
buffer.WriteString(`">`)
|
||||
|
||||
inactiveStyle := tabsLayout.inactiveTabStyle()
|
||||
activeStyle := tabsLayout.activeTabStyle()
|
||||
inactiveStyle := tabsLayoutInactiveTabStyle(tabsLayout)
|
||||
activeStyle := tabsLayoutActiveTabStyle(tabsLayout)
|
||||
|
||||
notTranslate := GetNotTranslate(tabsLayout)
|
||||
closeButton, _ := boolProperty(tabsLayout, TabCloseButton, tabsLayout.session)
|
||||
|
@ -947,18 +863,18 @@ func (tabsLayout *tabsLayoutData) htmlSubviews(self View, buffer *strings.Builde
|
|||
}
|
||||
}
|
||||
|
||||
func (tabsLayout *tabsLayoutData) handleCommand(self View, command string, data DataObject) bool {
|
||||
func (tabsLayout *tabsLayoutData) handleCommand(self View, command PropertyName, data DataObject) bool {
|
||||
switch command {
|
||||
case "tabClick":
|
||||
if numberText, ok := data.PropertyValue("number"); ok {
|
||||
if number, err := strconv.Atoi(numberText); err == nil {
|
||||
current := tabsLayout.currentItem(0)
|
||||
current := GetCurrent(tabsLayout)
|
||||
if current != number {
|
||||
tabsLayout.properties[Current] = number
|
||||
for _, listener := range tabsLayout.tabListener {
|
||||
tabsLayout.setRaw(Current, number)
|
||||
for _, listener := range getEventWithOldListeners[TabsLayout, int](tabsLayout, nil, CurrentTabChangedEvent) {
|
||||
listener(tabsLayout, number, current)
|
||||
}
|
||||
tabsLayout.propertyChangedEvent(Current)
|
||||
tabsLayout.currentChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -967,7 +883,7 @@ func (tabsLayout *tabsLayoutData) handleCommand(self View, command string, data
|
|||
case "tabCloseClick":
|
||||
if numberText, ok := data.PropertyValue("number"); ok {
|
||||
if number, err := strconv.Atoi(numberText); err == nil {
|
||||
for _, listener := range tabsLayout.tabCloseListener {
|
||||
for _, listener := range getEventListeners[TabsLayout, int](tabsLayout, nil, TabCloseEvent) {
|
||||
listener(tabsLayout, number)
|
||||
}
|
||||
}
|
||||
|
|
105
textView.go
105
textView.go
|
@ -23,116 +23,79 @@ func NewTextView(session Session, params Params) TextView {
|
|||
}
|
||||
|
||||
func newTextView(session Session) View {
|
||||
return NewTextView(session, nil)
|
||||
return new(textViewData)
|
||||
}
|
||||
|
||||
// Init initialize fields of TextView by default values
|
||||
func (textView *textViewData) init(session Session) {
|
||||
textView.viewData.init(session)
|
||||
textView.tag = "TextView"
|
||||
textView.set = textViewSet
|
||||
textView.changed = textViewPropertyChanged
|
||||
}
|
||||
|
||||
func (textView *textViewData) String() string {
|
||||
return getViewString(textView, nil)
|
||||
}
|
||||
func textViewPropertyChanged(view View, tag PropertyName) {
|
||||
switch tag {
|
||||
case Text:
|
||||
updateInnerHTML(view.htmlID(), view.Session())
|
||||
|
||||
func (textView *textViewData) Get(tag string) any {
|
||||
return textView.get(strings.ToLower(tag))
|
||||
}
|
||||
|
||||
func (textView *textViewData) Remove(tag string) {
|
||||
textView.remove(strings.ToLower(tag))
|
||||
}
|
||||
|
||||
func (textView *textViewData) remove(tag string) {
|
||||
textView.viewData.remove(tag)
|
||||
if textView.created {
|
||||
switch tag {
|
||||
case Text:
|
||||
updateInnerHTML(textView.htmlID(), textView.session)
|
||||
|
||||
case TextOverflow:
|
||||
textView.textOverflowUpdated()
|
||||
case TextOverflow:
|
||||
session := view.Session()
|
||||
if n, ok := enumProperty(view, TextOverflow, session, 0); ok {
|
||||
values := enumProperties[TextOverflow].cssValues
|
||||
if n >= 0 && n < len(values) {
|
||||
session.updateCSSProperty(view.htmlID(), string(TextOverflow), values[n])
|
||||
return
|
||||
}
|
||||
}
|
||||
session.updateCSSProperty(view.htmlID(), string(TextOverflow), "")
|
||||
|
||||
case NotTranslate:
|
||||
updateInnerHTML(view.htmlID(), view.Session())
|
||||
|
||||
default:
|
||||
viewPropertyChanged(view, tag)
|
||||
}
|
||||
}
|
||||
|
||||
func (textView *textViewData) Set(tag string, value any) bool {
|
||||
return textView.set(strings.ToLower(tag), value)
|
||||
}
|
||||
|
||||
func (textView *textViewData) set(tag string, value any) bool {
|
||||
func textViewSet(view View, tag PropertyName, value any) []PropertyName {
|
||||
switch tag {
|
||||
case Text:
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
textView.properties[Text] = value
|
||||
view.setRaw(Text, value)
|
||||
|
||||
case fmt.Stringer:
|
||||
textView.properties[Text] = value.String()
|
||||
view.setRaw(Text, value.String())
|
||||
|
||||
case float32:
|
||||
textView.properties[Text] = fmt.Sprintf("%g", float64(value))
|
||||
view.setRaw(Text, fmt.Sprintf("%g", float64(value)))
|
||||
|
||||
case float64:
|
||||
textView.properties[Text] = fmt.Sprintf("%g", value)
|
||||
view.setRaw(Text, fmt.Sprintf("%g", value))
|
||||
|
||||
case []rune:
|
||||
textView.properties[Text] = string(value)
|
||||
view.setRaw(Text, string(value))
|
||||
|
||||
case bool:
|
||||
if value {
|
||||
textView.properties[Text] = "true"
|
||||
view.setRaw(Text, "true")
|
||||
} else {
|
||||
textView.properties[Text] = "false"
|
||||
view.setRaw(Text, "false")
|
||||
}
|
||||
|
||||
default:
|
||||
if n, ok := isInt(value); ok {
|
||||
textView.properties[Text] = fmt.Sprintf("%d", n)
|
||||
view.setRaw(Text, fmt.Sprintf("%d", n))
|
||||
} else {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if textView.created {
|
||||
updateInnerHTML(textView.htmlID(), textView.session)
|
||||
}
|
||||
|
||||
case TextOverflow:
|
||||
if !textView.viewData.set(tag, value) {
|
||||
return false
|
||||
}
|
||||
if textView.created {
|
||||
textView.textOverflowUpdated()
|
||||
}
|
||||
|
||||
case NotTranslate:
|
||||
if !textView.viewData.set(tag, value) {
|
||||
return false
|
||||
}
|
||||
if textView.created {
|
||||
updateInnerHTML(textView.htmlID(), textView.Session())
|
||||
}
|
||||
|
||||
default:
|
||||
return textView.viewData.set(tag, value)
|
||||
return []PropertyName{Text}
|
||||
}
|
||||
|
||||
textView.propertyChangedEvent(tag)
|
||||
return true
|
||||
}
|
||||
|
||||
func (textView *textViewData) textOverflowUpdated() {
|
||||
session := textView.Session()
|
||||
if n, ok := enumProperty(textView, TextOverflow, session, 0); ok {
|
||||
values := enumProperties[TextOverflow].cssValues
|
||||
if n >= 0 && n < len(values) {
|
||||
session.updateCSSProperty(textView.htmlID(), TextOverflow, values[n])
|
||||
return
|
||||
}
|
||||
}
|
||||
session.updateCSSProperty(textView.htmlID(), TextOverflow, "")
|
||||
return viewSet(view, tag, value)
|
||||
}
|
||||
|
||||
func (textView *textViewData) htmlSubviews(self View, buffer *strings.Builder) {
|
||||
|
|
6
theme.go
6
theme.go
|
@ -699,13 +699,13 @@ func (theme *theme) addText(themeText string) bool {
|
|||
if node := obj.Property(i); node != nil {
|
||||
switch node.Type() {
|
||||
case ArrayNode:
|
||||
params[node.Tag()] = node.ArrayElements()
|
||||
params[PropertyName(node.Tag())] = node.ArrayElements()
|
||||
|
||||
case ObjectNode:
|
||||
params[node.Tag()] = node.Object()
|
||||
params[PropertyName(node.Tag())] = node.Object()
|
||||
|
||||
default:
|
||||
params[node.Tag()] = node.Text()
|
||||
params[PropertyName(node.Tag())] = node.Text()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
314
timePicker.go
314
timePicker.go
|
@ -27,7 +27,7 @@ const (
|
|||
// `func(newTime time.Time)`,
|
||||
// `func(picker rui.TimePicker)`,
|
||||
// `func()`.
|
||||
TimeChangedEvent = "time-changed"
|
||||
TimeChangedEvent PropertyName = "time-changed"
|
||||
|
||||
// TimePickerMin is the constant for "time-picker-min" property tag.
|
||||
//
|
||||
|
@ -44,7 +44,7 @@ const (
|
|||
// "HH:MM:SS PM" - "08:15:00 AM".
|
||||
// "HH:MM" - "08:15".
|
||||
// "HH:MM PM" - "08:15 AM".
|
||||
TimePickerMin = "time-picker-min"
|
||||
TimePickerMin PropertyName = "time-picker-min"
|
||||
|
||||
// TimePickerMax is the constant for "time-picker-max" property tag.
|
||||
//
|
||||
|
@ -61,7 +61,7 @@ const (
|
|||
// "HH:MM:SS PM" - "08:15:00 AM".
|
||||
// "HH:MM" - "08:15".
|
||||
// "HH:MM PM" - "08:15 AM".
|
||||
TimePickerMax = "time-picker-max"
|
||||
TimePickerMax PropertyName = "time-picker-max"
|
||||
|
||||
// TimePickerStep is the constant for "time-picker-step" property tag.
|
||||
//
|
||||
|
@ -72,7 +72,7 @@ const (
|
|||
//
|
||||
// Values:
|
||||
// >= `0` or >= "0" - Step value in seconds used to increment or decrement time.
|
||||
TimePickerStep = "time-picker-step"
|
||||
TimePickerStep PropertyName = "time-picker-step"
|
||||
|
||||
// TimePickerValue is the constant for "time-picker-value" property tag.
|
||||
//
|
||||
|
@ -89,7 +89,7 @@ const (
|
|||
// "HH:MM:SS PM" - "08:15:00 AM".
|
||||
// "HH:MM" - "08:15".
|
||||
// "HH:MM PM" - "08:15 AM".
|
||||
TimePickerValue = "time-picker-value"
|
||||
TimePickerValue PropertyName = "time-picker-value"
|
||||
|
||||
timeFormat = "15:04:05"
|
||||
)
|
||||
|
@ -101,8 +101,6 @@ type TimePicker interface {
|
|||
|
||||
type timePickerData struct {
|
||||
viewData
|
||||
dataList
|
||||
timeChangedListeners []func(TimePicker, time.Time, time.Time)
|
||||
}
|
||||
|
||||
// NewTimePicker create new TimePicker object and return it
|
||||
|
@ -114,236 +112,154 @@ func NewTimePicker(session Session, params Params) TimePicker {
|
|||
}
|
||||
|
||||
func newTimePicker(session Session) View {
|
||||
return NewTimePicker(session, nil)
|
||||
return new(timePickerData)
|
||||
}
|
||||
|
||||
func (picker *timePickerData) init(session Session) {
|
||||
picker.viewData.init(session)
|
||||
picker.tag = "TimePicker"
|
||||
picker.hasHtmlDisabled = true
|
||||
picker.timeChangedListeners = []func(TimePicker, time.Time, time.Time){}
|
||||
picker.dataListInit()
|
||||
}
|
||||
|
||||
func (picker *timePickerData) String() string {
|
||||
return getViewString(picker, nil)
|
||||
picker.normalize = normalizeTimePickerTag
|
||||
picker.set = timePickerSet
|
||||
picker.changed = timePickerPropertyChanged
|
||||
}
|
||||
|
||||
func (picker *timePickerData) Focusable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (picker *timePickerData) normalizeTag(tag string) string {
|
||||
tag = strings.ToLower(tag)
|
||||
func normalizeTimePickerTag(tag PropertyName) PropertyName {
|
||||
tag = defaultNormalize(tag)
|
||||
switch tag {
|
||||
case Type, Min, Max, Step, Value:
|
||||
return "time-picker-" + tag
|
||||
}
|
||||
|
||||
return tag
|
||||
return normalizeDataListTag(tag)
|
||||
}
|
||||
|
||||
func (picker *timePickerData) Remove(tag string) {
|
||||
picker.remove(picker.normalizeTag(tag))
|
||||
}
|
||||
func stringToTime(value string) (time.Time, bool) {
|
||||
lowText := strings.ToUpper(value)
|
||||
pm := strings.HasSuffix(lowText, "PM") || strings.HasSuffix(lowText, "AM")
|
||||
|
||||
func (picker *timePickerData) remove(tag string) {
|
||||
switch tag {
|
||||
case TimeChangedEvent:
|
||||
if len(picker.timeChangedListeners) > 0 {
|
||||
picker.timeChangedListeners = []func(TimePicker, time.Time, time.Time){}
|
||||
picker.propertyChangedEvent(tag)
|
||||
}
|
||||
return
|
||||
|
||||
case TimePickerMin:
|
||||
delete(picker.properties, TimePickerMin)
|
||||
if picker.created {
|
||||
picker.session.removeProperty(picker.htmlID(), Min)
|
||||
}
|
||||
|
||||
case TimePickerMax:
|
||||
delete(picker.properties, TimePickerMax)
|
||||
if picker.created {
|
||||
picker.session.removeProperty(picker.htmlID(), Max)
|
||||
}
|
||||
|
||||
case TimePickerStep:
|
||||
delete(picker.properties, TimePickerStep)
|
||||
if picker.created {
|
||||
picker.session.removeProperty(picker.htmlID(), Step)
|
||||
}
|
||||
|
||||
case TimePickerValue:
|
||||
if _, ok := picker.properties[TimePickerValue]; ok {
|
||||
oldTime := GetTimePickerValue(picker)
|
||||
delete(picker.properties, TimePickerValue)
|
||||
time := GetTimePickerValue(picker)
|
||||
if picker.created {
|
||||
picker.session.callFunc("setInputValue", picker.htmlID(), time.Format(timeFormat))
|
||||
}
|
||||
for _, listener := range picker.timeChangedListeners {
|
||||
listener(picker, time, oldTime)
|
||||
}
|
||||
var format string
|
||||
switch len(strings.Split(value, ":")) {
|
||||
case 2:
|
||||
if pm {
|
||||
format = "3:04 PM"
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
case DataList:
|
||||
if len(picker.dataList.dataList) > 0 {
|
||||
picker.setDataList(picker, []string{}, true)
|
||||
format = "15:04"
|
||||
}
|
||||
|
||||
default:
|
||||
picker.viewData.remove(tag)
|
||||
return
|
||||
}
|
||||
picker.propertyChangedEvent(tag)
|
||||
}
|
||||
|
||||
func (picker *timePickerData) Set(tag string, value any) bool {
|
||||
return picker.set(picker.normalizeTag(tag), value)
|
||||
}
|
||||
|
||||
func (picker *timePickerData) set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
picker.remove(tag)
|
||||
return true
|
||||
if pm {
|
||||
format = "03:04:05 PM"
|
||||
} else {
|
||||
format = "15:04:05"
|
||||
}
|
||||
}
|
||||
|
||||
setTimeValue := func(tag string) (time.Time, bool) {
|
||||
result, err := time.Parse(format, value)
|
||||
if err != nil {
|
||||
ErrorLog(err.Error())
|
||||
return time.Now(), false
|
||||
}
|
||||
return result, true
|
||||
}
|
||||
|
||||
func timePickerSet(view View, tag PropertyName, value any) []PropertyName {
|
||||
|
||||
setTimeValue := func(tag PropertyName) []PropertyName {
|
||||
switch value := value.(type) {
|
||||
case time.Time:
|
||||
picker.properties[tag] = value
|
||||
return value, true
|
||||
view.setRaw(tag, value)
|
||||
return []PropertyName{tag}
|
||||
|
||||
case string:
|
||||
if text, ok := picker.Session().resolveConstants(value); ok {
|
||||
lowText := strings.ToLower(text)
|
||||
pm := strings.HasSuffix(lowText, "pm") || strings.HasSuffix(lowText, "am")
|
||||
if isConstantName(value) {
|
||||
view.setRaw(tag, value)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
var format string
|
||||
switch len(strings.Split(text, ":")) {
|
||||
case 2:
|
||||
if pm {
|
||||
format = "3:04 PM"
|
||||
} else {
|
||||
format = "15:04"
|
||||
}
|
||||
|
||||
default:
|
||||
if pm {
|
||||
format = "03:04:05 PM"
|
||||
} else {
|
||||
format = "15:04:05"
|
||||
}
|
||||
}
|
||||
|
||||
if time, err := time.Parse(format, text); err == nil {
|
||||
picker.properties[tag] = value
|
||||
return time, true
|
||||
} else {
|
||||
ErrorLog(err.Error())
|
||||
}
|
||||
return time.Now(), false
|
||||
if time, ok := stringToTime(value); ok {
|
||||
view.setRaw(tag, time)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
}
|
||||
|
||||
notCompatibleType(tag, value)
|
||||
return time.Now(), false
|
||||
return nil
|
||||
}
|
||||
|
||||
switch tag {
|
||||
case TimePickerMin:
|
||||
old, oldOK := getTimeProperty(picker, TimePickerMin, Min)
|
||||
if time, ok := setTimeValue(TimePickerMin); ok {
|
||||
if !oldOK || time != old {
|
||||
if picker.created {
|
||||
picker.session.updateProperty(picker.htmlID(), Min, time.Format(timeFormat))
|
||||
}
|
||||
picker.propertyChangedEvent(tag)
|
||||
}
|
||||
return true
|
||||
return setTimeValue(TimePickerMin)
|
||||
|
||||
case TimePickerMax:
|
||||
return setTimeValue(TimePickerMax)
|
||||
|
||||
case TimePickerStep:
|
||||
return setIntProperty(view, TimePickerStep, value)
|
||||
|
||||
case TimePickerValue:
|
||||
view.setRaw("old-time", GetTimePickerValue(view))
|
||||
return setTimeValue(tag)
|
||||
|
||||
case TimeChangedEvent:
|
||||
return setEventWithOldListener[TimePicker, time.Time](view, tag, value)
|
||||
|
||||
case DataList:
|
||||
return setDataList(view, value, timeFormat)
|
||||
}
|
||||
|
||||
return viewSet(view, tag, value)
|
||||
}
|
||||
|
||||
func timePickerPropertyChanged(view View, tag PropertyName) {
|
||||
|
||||
session := view.Session()
|
||||
|
||||
switch tag {
|
||||
|
||||
case TimePickerMin:
|
||||
if time, ok := GetTimePickerMin(view); ok {
|
||||
session.updateProperty(view.htmlID(), "min", time.Format(timeFormat))
|
||||
} else {
|
||||
session.removeProperty(view.htmlID(), "min")
|
||||
}
|
||||
|
||||
case TimePickerMax:
|
||||
old, oldOK := getTimeProperty(picker, TimePickerMax, Max)
|
||||
if time, ok := setTimeValue(TimePickerMax); ok {
|
||||
if !oldOK || time != old {
|
||||
if picker.created {
|
||||
picker.session.updateProperty(picker.htmlID(), Max, time.Format(timeFormat))
|
||||
}
|
||||
picker.propertyChangedEvent(tag)
|
||||
}
|
||||
return true
|
||||
if time, ok := GetTimePickerMax(view); ok {
|
||||
session.updateProperty(view.htmlID(), "max", time.Format(timeFormat))
|
||||
} else {
|
||||
session.removeProperty(view.htmlID(), "max")
|
||||
}
|
||||
|
||||
case TimePickerStep:
|
||||
oldStep := GetTimePickerStep(picker)
|
||||
if picker.setIntProperty(TimePickerStep, value) {
|
||||
if step := GetTimePickerStep(picker); oldStep != step {
|
||||
if picker.created {
|
||||
if step > 0 {
|
||||
picker.session.updateProperty(picker.htmlID(), Step, strconv.Itoa(step))
|
||||
} else {
|
||||
picker.session.removeProperty(picker.htmlID(), Step)
|
||||
}
|
||||
}
|
||||
picker.propertyChangedEvent(tag)
|
||||
}
|
||||
return true
|
||||
if step := GetTimePickerStep(view); step > 0 {
|
||||
session.updateProperty(view.htmlID(), "step", strconv.Itoa(step))
|
||||
} else {
|
||||
session.removeProperty(view.htmlID(), "step")
|
||||
}
|
||||
|
||||
case TimePickerValue:
|
||||
oldTime := GetTimePickerValue(picker)
|
||||
if time, ok := setTimeValue(TimePickerValue); ok {
|
||||
if time != oldTime {
|
||||
if picker.created {
|
||||
picker.session.callFunc("setInputValue", picker.htmlID(), time.Format(timeFormat))
|
||||
value := GetTimePickerValue(view)
|
||||
session.callFunc("setInputValue", view.htmlID(), value.Format(timeFormat))
|
||||
|
||||
if listeners := GetTimeChangedListeners(view); len(listeners) > 0 {
|
||||
oldTime := time.Now()
|
||||
if val := view.getRaw("old-time"); val != nil {
|
||||
if time, ok := val.(time.Time); ok {
|
||||
oldTime = time
|
||||
}
|
||||
for _, listener := range picker.timeChangedListeners {
|
||||
listener(picker, time, oldTime)
|
||||
}
|
||||
picker.propertyChangedEvent(tag)
|
||||
}
|
||||
return true
|
||||
for _, listener := range listeners {
|
||||
listener(view, value, oldTime)
|
||||
}
|
||||
}
|
||||
|
||||
case TimeChangedEvent:
|
||||
listeners, ok := valueToEventWithOldListeners[TimePicker, time.Time](value)
|
||||
if !ok {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
} else if listeners == nil {
|
||||
listeners = []func(TimePicker, time.Time, time.Time){}
|
||||
}
|
||||
picker.timeChangedListeners = listeners
|
||||
picker.propertyChangedEvent(tag)
|
||||
return true
|
||||
|
||||
case DataList:
|
||||
return picker.setDataList(picker, value, picker.created)
|
||||
|
||||
default:
|
||||
return picker.viewData.set(tag, value)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (picker *timePickerData) Get(tag string) any {
|
||||
return picker.get(picker.normalizeTag(tag))
|
||||
}
|
||||
|
||||
func (picker *timePickerData) get(tag string) any {
|
||||
switch tag {
|
||||
case TimeChangedEvent:
|
||||
return picker.timeChangedListeners
|
||||
|
||||
case DataList:
|
||||
return picker.dataList.dataList
|
||||
|
||||
default:
|
||||
return picker.viewData.get(tag)
|
||||
viewPropertyChanged(view, tag)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -352,7 +268,13 @@ func (picker *timePickerData) htmlTag() string {
|
|||
}
|
||||
|
||||
func (picker *timePickerData) htmlSubviews(self View, buffer *strings.Builder) {
|
||||
picker.dataListHtmlSubviews(self, buffer)
|
||||
dataListHtmlSubviews(self, buffer, func(text string, session Session) string {
|
||||
text, _ = session.resolveConstants(text)
|
||||
if time, ok := stringToTime(text); ok {
|
||||
return time.Format(timeFormat)
|
||||
}
|
||||
return text
|
||||
})
|
||||
}
|
||||
|
||||
func (picker *timePickerData) htmlProperties(self View, buffer *strings.Builder) {
|
||||
|
@ -387,10 +309,10 @@ func (picker *timePickerData) htmlProperties(self View, buffer *strings.Builder)
|
|||
buffer.WriteString(` onclick="stopEventPropagation(this, event)"`)
|
||||
}
|
||||
|
||||
picker.dataListHtmlProperties(picker, buffer)
|
||||
dataListHtmlProperties(picker, buffer)
|
||||
}
|
||||
|
||||
func (picker *timePickerData) handleCommand(self View, command string, data DataObject) bool {
|
||||
func (picker *timePickerData) handleCommand(self View, command PropertyName, data DataObject) bool {
|
||||
switch command {
|
||||
case "textChanged":
|
||||
if text, ok := data.PropertyValue("text"); ok {
|
||||
|
@ -398,9 +320,13 @@ func (picker *timePickerData) handleCommand(self View, command string, data Data
|
|||
oldValue := GetTimePickerValue(picker)
|
||||
picker.properties[TimePickerValue] = value
|
||||
if value != oldValue {
|
||||
for _, listener := range picker.timeChangedListeners {
|
||||
for _, listener := range GetTimeChangedListeners(picker) {
|
||||
listener(picker, value, oldValue)
|
||||
}
|
||||
if listener, ok := picker.changeListener[TimePickerValue]; ok {
|
||||
listener(picker, TimePickerValue)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -410,7 +336,7 @@ func (picker *timePickerData) handleCommand(self View, command string, data Data
|
|||
return picker.viewData.handleCommand(self, command, data)
|
||||
}
|
||||
|
||||
func getTimeProperty(view View, mainTag, shortTag string) (time.Time, bool) {
|
||||
func getTimeProperty(view View, mainTag, shortTag PropertyName) (time.Time, bool) {
|
||||
valueToTime := func(value any) (time.Time, bool) {
|
||||
if value != nil {
|
||||
switch value := value.(type) {
|
||||
|
@ -419,7 +345,7 @@ func getTimeProperty(view View, mainTag, shortTag string) (time.Time, bool) {
|
|||
|
||||
case string:
|
||||
if text, ok := view.Session().resolveConstants(value); ok {
|
||||
if result, err := time.Parse(timeFormat, text); err == nil {
|
||||
if result, ok := stringToTime(text); ok {
|
||||
return result, true
|
||||
}
|
||||
}
|
||||
|
@ -433,9 +359,11 @@ func getTimeProperty(view View, mainTag, shortTag string) (time.Time, bool) {
|
|||
return result, true
|
||||
}
|
||||
|
||||
if value := valueFromStyle(view, shortTag); value != nil {
|
||||
if result, ok := valueToTime(value); ok {
|
||||
return result, true
|
||||
for _, tag := range []PropertyName{mainTag, shortTag} {
|
||||
if value := valueFromStyle(view, tag); value != nil {
|
||||
if result, ok := valueToTime(value); ok {
|
||||
return result, true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package rui
|
|||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Constants which represent [View] specific touch events properties
|
||||
|
@ -23,7 +22,7 @@ const (
|
|||
// `func(event rui.TouchEvent)`,
|
||||
// `func(view rui.View)`,
|
||||
// `func()`.
|
||||
TouchStart = "touch-start"
|
||||
TouchStart PropertyName = "touch-start"
|
||||
|
||||
// TouchEnd is the constant for "touch-end" property tag.
|
||||
//
|
||||
|
@ -41,7 +40,7 @@ const (
|
|||
// `func(event rui.TouchEvent)`,
|
||||
// `func(view rui.View)`,
|
||||
// `func()`.
|
||||
TouchEnd = "touch-end"
|
||||
TouchEnd PropertyName = "touch-end"
|
||||
|
||||
// TouchMove is the constant for "touch-move" property tag.
|
||||
//
|
||||
|
@ -59,12 +58,12 @@ const (
|
|||
// `func(event rui.TouchEvent)`,
|
||||
// `func(view rui.View)`,
|
||||
// `func()`.
|
||||
TouchMove = "touch-move"
|
||||
TouchMove PropertyName = "touch-move"
|
||||
|
||||
// TouchCancel is the constant for "touch-cancel" property tag.
|
||||
//
|
||||
// Used by `View`.
|
||||
// Is fired when one or more touch points have been disrupted in an implementation-specific manner (for example, too many
|
||||
// Is fired when one or more touch points have been disrupted in an implementation-specific manner (for example, too many
|
||||
// touch points are created).
|
||||
//
|
||||
// General listener format:
|
||||
|
@ -78,7 +77,7 @@ const (
|
|||
// `func(event rui.TouchEvent)`,
|
||||
// `func(view rui.View)`,
|
||||
// `func()`.
|
||||
TouchCancel = "touch-cancel"
|
||||
TouchCancel PropertyName = "touch-cancel"
|
||||
)
|
||||
|
||||
// Touch contains parameters of a single touch of a touch event
|
||||
|
@ -143,54 +142,44 @@ type TouchEvent struct {
|
|||
MetaKey bool
|
||||
}
|
||||
|
||||
var touchEvents = map[string]struct{ jsEvent, jsFunc string }{
|
||||
TouchStart: {jsEvent: "ontouchstart", jsFunc: "touchStartEvent"},
|
||||
TouchEnd: {jsEvent: "ontouchend", jsFunc: "touchEndEvent"},
|
||||
TouchMove: {jsEvent: "ontouchmove", jsFunc: "touchMoveEvent"},
|
||||
TouchCancel: {jsEvent: "ontouchcancel", jsFunc: "touchCancelEvent"},
|
||||
}
|
||||
|
||||
func (view *viewData) setTouchListener(tag string, value any) bool {
|
||||
listeners, ok := valueToEventListeners[View, TouchEvent](value)
|
||||
if !ok {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
|
||||
if listeners == nil {
|
||||
view.removeTouchListener(tag)
|
||||
} else if js, ok := touchEvents[tag]; ok {
|
||||
view.properties[tag] = listeners
|
||||
if view.created {
|
||||
view.session.updateProperty(view.htmlID(), js.jsEvent, js.jsFunc+"(this, event)")
|
||||
/*
|
||||
func setTouchListener(properties Properties, tag PropertyName, value any) bool {
|
||||
if listeners, ok := valueToEventListeners[View, TouchEvent](value); ok {
|
||||
if len(listeners) == 0 {
|
||||
properties.setRaw(tag, nil)
|
||||
} else {
|
||||
properties.setRaw(tag, listeners)
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
return true
|
||||
}
|
||||
return true
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
|
||||
func (view *viewData) removeTouchListener(tag string) {
|
||||
func (view *viewData) removeTouchListener(tag PropertyName) {
|
||||
delete(view.properties, tag)
|
||||
if view.created {
|
||||
if js, ok := touchEvents[tag]; ok {
|
||||
if js, ok := eventJsFunc[tag]; ok {
|
||||
view.session.removeProperty(view.htmlID(), js.jsEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func touchEventsHtml(view View, buffer *strings.Builder) {
|
||||
for tag, js := range touchEvents {
|
||||
for _, tag := range []PropertyName{TouchStart, TouchEnd, TouchMove, TouchCancel} {
|
||||
if value := view.getRaw(tag); value != nil {
|
||||
if listeners, ok := value.([]func(View, TouchEvent)); ok && len(listeners) > 0 {
|
||||
buffer.WriteString(js.jsEvent)
|
||||
buffer.WriteString(`="`)
|
||||
buffer.WriteString(js.jsFunc)
|
||||
buffer.WriteString(`(this, event)" `)
|
||||
if js, ok := eventJsFunc[tag]; ok {
|
||||
if listeners, ok := value.([]func(View, TouchEvent)); ok && len(listeners) > 0 {
|
||||
buffer.WriteString(js.jsEvent)
|
||||
buffer.WriteString(`="`)
|
||||
buffer.WriteString(js.jsFunc)
|
||||
buffer.WriteString(`(this, event)" `)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func (event *TouchEvent) init(data DataObject) {
|
||||
|
||||
|
@ -225,7 +214,7 @@ func (event *TouchEvent) init(data DataObject) {
|
|||
event.MetaKey = dataBoolProperty(data, "metaKey")
|
||||
}
|
||||
|
||||
func handleTouchEvents(view View, tag string, data DataObject) {
|
||||
func handleTouchEvents(view View, tag PropertyName, data DataObject) {
|
||||
listeners := getEventListeners[View, TouchEvent](view, nil, tag)
|
||||
if len(listeners) == 0 {
|
||||
return
|
||||
|
|
|
@ -15,7 +15,7 @@ const (
|
|||
//
|
||||
// Values:
|
||||
// Internal type is `float`, other types converted to it during assignment.
|
||||
VideoWidth = "video-width"
|
||||
VideoWidth PropertyName = "video-width"
|
||||
|
||||
// VideoHeight is the constant for "video-height" property tag.
|
||||
//
|
||||
|
@ -25,16 +25,16 @@ const (
|
|||
// Supported types: `float`, `int`, `string`.
|
||||
//
|
||||
// Internal type is `float`, other types converted to it during assignment.
|
||||
VideoHeight = "video-height"
|
||||
VideoHeight PropertyName = "video-height"
|
||||
|
||||
// Poster is the constant for "poster" property tag.
|
||||
//
|
||||
// Used by `VideoPlayer`.
|
||||
// Defines an URL for an image to be shown while the video is downloading. If this attribute isn't specified, nothing is
|
||||
// Defines an URL for an image to be shown while the video is downloading. If this attribute isn't specified, nothing is
|
||||
// displayed until the first frame is available, then the first frame is shown as the poster frame.
|
||||
//
|
||||
// Supported types: `string`.
|
||||
Poster = "poster"
|
||||
Poster PropertyName = "poster"
|
||||
)
|
||||
|
||||
// VideoPlayer is a type of a [View] which can play video files
|
||||
|
@ -50,92 +50,56 @@ type videoPlayerData struct {
|
|||
func NewVideoPlayer(session Session, params Params) VideoPlayer {
|
||||
view := new(videoPlayerData)
|
||||
view.init(session)
|
||||
view.tag = "VideoPlayer"
|
||||
setInitParams(view, params)
|
||||
return view
|
||||
}
|
||||
|
||||
func newVideoPlayer(session Session) View {
|
||||
return NewVideoPlayer(session, nil)
|
||||
return new(videoPlayerData) // NewVideoPlayer(session, nil)
|
||||
}
|
||||
|
||||
func (player *videoPlayerData) init(session Session) {
|
||||
player.mediaPlayerData.init(session)
|
||||
player.tag = "VideoPlayer"
|
||||
}
|
||||
|
||||
func (player *videoPlayerData) String() string {
|
||||
return getViewString(player, nil)
|
||||
player.changed = videoPlayerPropertyChanged
|
||||
}
|
||||
|
||||
func (player *videoPlayerData) htmlTag() string {
|
||||
return "video"
|
||||
}
|
||||
|
||||
func (player *videoPlayerData) Remove(tag string) {
|
||||
player.remove(strings.ToLower(tag))
|
||||
}
|
||||
func videoPlayerPropertyChanged(view View, tag PropertyName) {
|
||||
|
||||
session := view.Session()
|
||||
updateSize := func(cssTag string) {
|
||||
if size, ok := floatTextProperty(view, tag, session, 0); ok {
|
||||
if size != "0" {
|
||||
session.updateProperty(view.htmlID(), cssTag, size)
|
||||
} else {
|
||||
session.removeProperty(view.htmlID(), cssTag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (player *videoPlayerData) remove(tag string) {
|
||||
switch tag {
|
||||
|
||||
case VideoWidth:
|
||||
delete(player.properties, tag)
|
||||
player.session.removeProperty(player.htmlID(), "width")
|
||||
updateSize("width")
|
||||
|
||||
case VideoHeight:
|
||||
delete(player.properties, tag)
|
||||
player.session.removeProperty(player.htmlID(), "height")
|
||||
updateSize("height")
|
||||
|
||||
case Poster:
|
||||
delete(player.properties, tag)
|
||||
player.session.removeProperty(player.htmlID(), Poster)
|
||||
if url, ok := stringProperty(view, Poster, session); ok {
|
||||
session.updateProperty(view.htmlID(), string(Poster), url)
|
||||
} else {
|
||||
session.removeProperty(view.htmlID(), string(Poster))
|
||||
}
|
||||
|
||||
default:
|
||||
player.mediaPlayerData.remove(tag)
|
||||
mediaPlayerPropertyChanged(view, tag)
|
||||
}
|
||||
}
|
||||
|
||||
func (player *videoPlayerData) Set(tag string, value any) bool {
|
||||
return player.set(strings.ToLower(tag), value)
|
||||
}
|
||||
|
||||
func (player *videoPlayerData) set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
player.remove(tag)
|
||||
return true
|
||||
}
|
||||
|
||||
if player.mediaPlayerData.set(tag, value) {
|
||||
session := player.Session()
|
||||
updateSize := func(cssTag string) {
|
||||
if size, ok := floatTextProperty(player, tag, session, 0); ok {
|
||||
if size != "0" {
|
||||
session.updateProperty(player.htmlID(), cssTag, size)
|
||||
} else {
|
||||
session.removeProperty(player.htmlID(), cssTag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch tag {
|
||||
case VideoWidth:
|
||||
updateSize("width")
|
||||
|
||||
case VideoHeight:
|
||||
updateSize("height")
|
||||
|
||||
case Poster:
|
||||
if url, ok := stringProperty(player, Poster, session); ok {
|
||||
session.updateProperty(player.htmlID(), Poster, url)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (player *videoPlayerData) htmlProperties(self View, buffer *strings.Builder) {
|
||||
player.mediaPlayerData.htmlProperties(self, buffer)
|
||||
|
||||
|
|
608
view.go
608
view.go
|
@ -62,15 +62,16 @@ type View interface {
|
|||
// 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 string, value any, animation Animation) bool
|
||||
SetAnimated(tag PropertyName, value any, animation Animation) bool
|
||||
|
||||
// SetChangeListener set the function to track the change of the View property
|
||||
SetChangeListener(tag string, listener func(View, string))
|
||||
SetChangeListener(tag PropertyName, listener func(View, PropertyName))
|
||||
|
||||
// HasFocus returns 'true' if the view has focus
|
||||
HasFocus() bool
|
||||
|
||||
handleCommand(self View, command string, data DataObject) bool
|
||||
init(session Session)
|
||||
handleCommand(self View, command PropertyName, data DataObject) bool
|
||||
htmlClass(disabled bool) string
|
||||
htmlTag() string
|
||||
closeHTMLTag() bool
|
||||
|
@ -81,7 +82,8 @@ type View interface {
|
|||
htmlProperties(self View, buffer *strings.Builder)
|
||||
cssStyle(self View, builder cssBuilder)
|
||||
addToCSSStyle(addCSS map[string]string)
|
||||
exscludeTags() []string
|
||||
exscludeTags() []PropertyName
|
||||
htmlDisabledProperty() bool
|
||||
|
||||
onResize(self View, x, y, width, height float64)
|
||||
onItemResize(self View, index string, x, y, width, height float64)
|
||||
|
@ -99,8 +101,8 @@ type viewData struct {
|
|||
_htmlID string
|
||||
parentID string
|
||||
systemClass string
|
||||
changeListener map[string]func(View, string)
|
||||
singleTransition map[string]Animation
|
||||
changeListener map[PropertyName]func(View, PropertyName)
|
||||
singleTransition map[PropertyName]Animation
|
||||
addCSS map[string]string
|
||||
frame Frame
|
||||
scroll Frame
|
||||
|
@ -108,12 +110,21 @@ type viewData struct {
|
|||
created bool
|
||||
hasFocus bool
|
||||
hasHtmlDisabled bool
|
||||
//animation map[string]AnimationEndListener
|
||||
getFunc func(view View, tag PropertyName) any
|
||||
set func(view View, tag PropertyName, value any) []PropertyName
|
||||
remove func(view View, tag PropertyName) []PropertyName
|
||||
changed func(view View, 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
|
||||
}
|
||||
|
||||
|
@ -132,22 +143,18 @@ func setInitParams(view View, params Params) {
|
|||
}
|
||||
}
|
||||
|
||||
// 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 (view *viewData) init(session Session) {
|
||||
view.viewStyle.init()
|
||||
view.getFunc = viewGet
|
||||
view.set = viewSet
|
||||
view.normalize = normalizeViewTag
|
||||
view.changed = viewPropertyChanged
|
||||
view.tag = "View"
|
||||
view.session = session
|
||||
view.changeListener = map[string]func(View, string){}
|
||||
view.changeListener = map[PropertyName]func(View, PropertyName){}
|
||||
view.addCSS = map[string]string{}
|
||||
//view.animation = map[string]AnimationEndListener{}
|
||||
view.singleTransition = map[string]Animation{}
|
||||
view.singleTransition = map[PropertyName]Animation{}
|
||||
view.noResizeEvent = false
|
||||
view.created = false
|
||||
view.hasHtmlDisabled = false
|
||||
|
@ -205,71 +212,112 @@ func (view *viewData) Focusable() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (view *viewData) Remove(tag string) {
|
||||
view.remove(strings.ToLower(tag))
|
||||
}
|
||||
func (view *viewData) Remove(tag PropertyName) {
|
||||
tag = view.normalize(tag)
|
||||
var changedTags []PropertyName = nil
|
||||
|
||||
func (view *viewData) remove(tag string) {
|
||||
switch tag {
|
||||
case ID:
|
||||
view.viewID = ""
|
||||
|
||||
case TabIndex, "tab-index":
|
||||
delete(view.properties, tag)
|
||||
if view.Focusable() {
|
||||
view.session.updateProperty(view.htmlID(), "tabindex", "0")
|
||||
} else {
|
||||
view.session.updateProperty(view.htmlID(), "tabindex", "-1")
|
||||
if view.viewID != "" {
|
||||
view.viewID = ""
|
||||
changedTags = []PropertyName{ID}
|
||||
}
|
||||
|
||||
case UserData:
|
||||
delete(view.properties, tag)
|
||||
case AnimationTag:
|
||||
if val := view.getRaw(AnimationTag); val != nil {
|
||||
if animations, ok := val.([]Animation); ok {
|
||||
for _, animation := range animations {
|
||||
animation.unused(view.session)
|
||||
}
|
||||
}
|
||||
|
||||
case Style, StyleDisabled:
|
||||
if _, ok := view.properties[tag]; ok {
|
||||
delete(view.properties, tag)
|
||||
view.session.updateProperty(view.htmlID(), "class", view.htmlClass(IsDisabled(view)))
|
||||
}
|
||||
|
||||
case FocusEvent, LostFocusEvent:
|
||||
view.removeFocusListener(tag)
|
||||
|
||||
case KeyDownEvent, KeyUpEvent:
|
||||
view.removeKeyListener(tag)
|
||||
|
||||
case ClickEvent, DoubleClickEvent, MouseDown, MouseUp, MouseMove, MouseOut, MouseOver, ContextMenuEvent:
|
||||
view.removeMouseListener(tag)
|
||||
|
||||
case PointerDown, PointerUp, PointerMove, PointerOut, PointerOver, PointerCancel:
|
||||
view.removePointerListener(tag)
|
||||
|
||||
case TouchStart, TouchEnd, TouchMove, TouchCancel:
|
||||
view.removeTouchListener(tag)
|
||||
|
||||
case TransitionRunEvent, TransitionStartEvent, TransitionEndEvent, TransitionCancelEvent:
|
||||
view.removeTransitionListener(tag)
|
||||
|
||||
case AnimationStartEvent, AnimationEndEvent, AnimationIterationEvent, AnimationCancelEvent:
|
||||
view.removeAnimationListener(tag)
|
||||
|
||||
case ResizeEvent, ScrollEvent:
|
||||
delete(view.properties, tag)
|
||||
|
||||
case Content:
|
||||
if _, ok := view.properties[Content]; ok {
|
||||
delete(view.properties, Content)
|
||||
updateInnerHTML(view.htmlID(), view.session)
|
||||
view.setRaw(AnimationTag, nil)
|
||||
changedTags = []PropertyName{AnimationTag}
|
||||
}
|
||||
|
||||
default:
|
||||
view.viewStyle.remove(tag)
|
||||
viewPropertyChanged(view, tag)
|
||||
changedTags = view.remove(view, tag)
|
||||
}
|
||||
|
||||
view.propertyChangedEvent(tag)
|
||||
if view.created && len(changedTags) > 0 {
|
||||
for _, tag := range changedTags {
|
||||
view.changed(view, tag)
|
||||
}
|
||||
|
||||
for _, tag := range changedTags {
|
||||
if listener, ok := view.changeListener[tag]; ok {
|
||||
listener(view, tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (view *viewData) propertyChangedEvent(tag string) {
|
||||
func (view *viewData) Set(tag PropertyName, value any) bool {
|
||||
if value == nil {
|
||||
view.Remove(tag)
|
||||
return true
|
||||
}
|
||||
|
||||
tag = view.normalize(tag)
|
||||
var changedTags []PropertyName = nil
|
||||
|
||||
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 {
|
||||
for _, tag := range changedTags {
|
||||
view.changed(view, 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) propertyChangedEvent(tag PropertyName) {
|
||||
if listener, ok := view.changeListener[tag]; ok {
|
||||
listener(view, tag)
|
||||
}
|
||||
|
@ -319,113 +367,58 @@ func (view *viewData) propertyChangedEvent(tag string) {
|
|||
listener(view, tag)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func (view *viewData) Set(tag string, value any) bool {
|
||||
return view.set(strings.ToLower(tag), value)
|
||||
func viewRemove(properties Properties, tag PropertyName) []PropertyName {
|
||||
return viewStyleRemove(properties, tag)
|
||||
}
|
||||
|
||||
func (view *viewData) set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
view.remove(tag)
|
||||
return true
|
||||
}
|
||||
|
||||
result := func(res bool) bool {
|
||||
if res {
|
||||
view.propertyChangedEvent(tag)
|
||||
}
|
||||
return res
|
||||
}
|
||||
func viewSet(view View, tag PropertyName, value any) []PropertyName {
|
||||
|
||||
switch tag {
|
||||
case ID:
|
||||
text, ok := value.(string)
|
||||
if !ok {
|
||||
notCompatibleType(ID, value)
|
||||
return false
|
||||
}
|
||||
view.viewID = text
|
||||
|
||||
case AnimationTag:
|
||||
oldAnimations := []Animation{}
|
||||
if val, ok := view.properties[AnimationTag]; ok && val != nil {
|
||||
if animation, ok := val.([]Animation); ok {
|
||||
oldAnimations = animation
|
||||
}
|
||||
}
|
||||
|
||||
if !view.setAnimation(tag, value) {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, animation := range oldAnimations {
|
||||
animation.unused(view.session)
|
||||
}
|
||||
if view.created {
|
||||
viewPropertyChanged(view, tag)
|
||||
}
|
||||
|
||||
case TabIndex, "tab-index":
|
||||
if !view.setIntProperty(tag, value) {
|
||||
return false
|
||||
}
|
||||
if value, ok := intProperty(view, TabIndex, view.Session(), 0); ok {
|
||||
view.session.updateProperty(view.htmlID(), "tabindex", strconv.Itoa(value))
|
||||
} else if view.Focusable() {
|
||||
view.session.updateProperty(view.htmlID(), "tabindex", "0")
|
||||
} else {
|
||||
view.session.updateProperty(view.htmlID(), "tabindex", "-1")
|
||||
}
|
||||
return setIntProperty(view, TabIndex, value)
|
||||
|
||||
case UserData:
|
||||
view.properties[tag] = value
|
||||
view.setRaw(tag, value)
|
||||
return []PropertyName{UserData}
|
||||
|
||||
case Style, StyleDisabled:
|
||||
text, ok := value.(string)
|
||||
if !ok {
|
||||
notCompatibleType(ID, value)
|
||||
return false
|
||||
}
|
||||
view.properties[tag] = text
|
||||
if view.created {
|
||||
view.session.updateProperty(view.htmlID(), "class", view.htmlClass(IsDisabled(view)))
|
||||
if text, ok := value.(string); ok {
|
||||
view.setRaw(tag, text)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
notCompatibleType(ID, value)
|
||||
return nil
|
||||
|
||||
case FocusEvent, LostFocusEvent:
|
||||
return result(view.setFocusListener(tag, value))
|
||||
return setNoParamEventListener[View](view, tag, value)
|
||||
|
||||
case KeyDownEvent, KeyUpEvent:
|
||||
return result(view.setKeyListener(tag, value))
|
||||
return setViewEventListener[View, KeyEvent](view, tag, value)
|
||||
|
||||
case ClickEvent, DoubleClickEvent, MouseDown, MouseUp, MouseMove, MouseOut, MouseOver, ContextMenuEvent:
|
||||
return result(view.setMouseListener(tag, value))
|
||||
return setViewEventListener[View, MouseEvent](view, tag, value)
|
||||
|
||||
case PointerDown, PointerUp, PointerMove, PointerOut, PointerOver, PointerCancel:
|
||||
return result(view.setPointerListener(tag, value))
|
||||
return setViewEventListener[View, PointerEvent](view, tag, value)
|
||||
|
||||
case TouchStart, TouchEnd, TouchMove, TouchCancel:
|
||||
return result(view.setTouchListener(tag, value))
|
||||
return setViewEventListener[View, TouchEvent](view, tag, value)
|
||||
|
||||
case TransitionRunEvent, TransitionStartEvent, TransitionEndEvent, TransitionCancelEvent:
|
||||
return result(view.setTransitionListener(tag, value))
|
||||
|
||||
case AnimationStartEvent, AnimationEndEvent, AnimationIterationEvent, AnimationCancelEvent:
|
||||
return result(view.setAnimationListener(tag, value))
|
||||
case TransitionRunEvent, TransitionStartEvent, TransitionEndEvent, TransitionCancelEvent,
|
||||
AnimationStartEvent, AnimationEndEvent, AnimationIterationEvent, AnimationCancelEvent:
|
||||
return setViewEventListener[View, string](view, tag, value)
|
||||
//return setTransitionListener(view, tag, value), tag
|
||||
//return setAnimationListener(view, tag, value), tag
|
||||
|
||||
case ResizeEvent, ScrollEvent:
|
||||
return result(view.setFrameListener(tag, value))
|
||||
|
||||
default:
|
||||
if !view.viewStyle.set(tag, value) {
|
||||
return false
|
||||
}
|
||||
if view.created {
|
||||
viewPropertyChanged(view, tag)
|
||||
}
|
||||
return setViewEventListener[View, Frame](view, tag, value)
|
||||
//return setFrameListener(view, tag, value), tag
|
||||
}
|
||||
|
||||
view.propertyChangedEvent(tag)
|
||||
return true
|
||||
return viewStyleSet(view, tag, value)
|
||||
}
|
||||
|
||||
func (view *viewData) SetParams(params Params) bool {
|
||||
|
@ -446,15 +439,29 @@ func (view *viewData) SetParams(params Params) bool {
|
|||
return result
|
||||
}
|
||||
|
||||
func viewPropertyChanged(view *viewData, tag string) {
|
||||
if view.updateTransformProperty(tag) {
|
||||
return
|
||||
}
|
||||
func viewPropertyChanged(view View, tag PropertyName) {
|
||||
/*
|
||||
if view.updateTransformProperty(tag) {
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
htmlID := view.htmlID()
|
||||
session := view.session
|
||||
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)
|
||||
|
@ -462,7 +469,7 @@ func viewPropertyChanged(view *viewData, tag string) {
|
|||
session.startUpdateScript(htmlID)
|
||||
if IsDisabled(view) {
|
||||
session.updateProperty(htmlID, "data-disabled", "1")
|
||||
if view.hasHtmlDisabled {
|
||||
if view.htmlDisabledProperty() {
|
||||
session.updateProperty(htmlID, "disabled", true)
|
||||
}
|
||||
if tabIndex >= 0 {
|
||||
|
@ -473,7 +480,7 @@ func viewPropertyChanged(view *viewData, tag string) {
|
|||
}
|
||||
} else {
|
||||
session.updateProperty(htmlID, "data-disabled", "0")
|
||||
if view.hasHtmlDisabled {
|
||||
if view.htmlDisabledProperty() {
|
||||
session.removeProperty(htmlID, "disabled")
|
||||
}
|
||||
if tabIndex >= 0 {
|
||||
|
@ -485,82 +492,65 @@ func viewPropertyChanged(view *viewData, tag string) {
|
|||
}
|
||||
session.finishUpdateScript(htmlID)
|
||||
updateInnerHTML(htmlID, session)
|
||||
return
|
||||
|
||||
case Visibility:
|
||||
switch GetVisibility(view) {
|
||||
case Invisible:
|
||||
session.updateCSSProperty(htmlID, Visibility, "hidden")
|
||||
session.updateCSSProperty(htmlID, string(Visibility), "hidden")
|
||||
session.updateCSSProperty(htmlID, "display", "")
|
||||
session.callFunc("hideTooltip")
|
||||
|
||||
case Gone:
|
||||
session.updateCSSProperty(htmlID, Visibility, "hidden")
|
||||
session.updateCSSProperty(htmlID, string(Visibility), "hidden")
|
||||
session.updateCSSProperty(htmlID, "display", "none")
|
||||
session.callFunc("hideTooltip")
|
||||
|
||||
default:
|
||||
session.updateCSSProperty(htmlID, Visibility, "visible")
|
||||
session.updateCSSProperty(htmlID, string(Visibility), "visible")
|
||||
session.updateCSSProperty(htmlID, "display", "")
|
||||
}
|
||||
return
|
||||
|
||||
case Background:
|
||||
session.updateCSSProperty(htmlID, Background, view.backgroundCSS(session))
|
||||
return
|
||||
session.updateCSSProperty(htmlID, string(Background), backgroundCSS(view, session))
|
||||
|
||||
case Border:
|
||||
if getBorder(view, Border) == nil {
|
||||
if session.startUpdateScript(htmlID) {
|
||||
defer session.finishUpdateScript(htmlID)
|
||||
}
|
||||
session.updateCSSProperty(htmlID, BorderWidth, "")
|
||||
session.updateCSSProperty(htmlID, BorderColor, "")
|
||||
session.updateCSSProperty(htmlID, BorderStyle, "none")
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case Border, BorderLeft, BorderRight, BorderTop, BorderBottom:
|
||||
cssWidth := ""
|
||||
cssColor := ""
|
||||
cssStyle := "none"
|
||||
|
||||
case BorderLeft, BorderRight, BorderTop, BorderBottom:
|
||||
if border := getBorder(view, Border); border != nil {
|
||||
if session.startUpdateScript(htmlID) {
|
||||
defer session.finishUpdateScript(htmlID)
|
||||
}
|
||||
session.updateCSSProperty(htmlID, BorderWidth, border.cssWidthValue(session))
|
||||
session.updateCSSProperty(htmlID, BorderColor, border.cssColorValue(session))
|
||||
session.updateCSSProperty(htmlID, BorderStyle, border.cssStyleValue(session))
|
||||
if border := getBorderProperty(view, Border); border != nil {
|
||||
cssWidth = border.cssWidthValue(session)
|
||||
cssColor = border.cssColorValue(session)
|
||||
cssStyle = border.cssStyleValue(session)
|
||||
}
|
||||
return
|
||||
|
||||
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 := getBorder(view, Border); border != nil {
|
||||
session.updateCSSProperty(htmlID, BorderStyle, border.cssStyleValue(session))
|
||||
if border := getBorderProperty(view, Border); border != nil {
|
||||
session.updateCSSProperty(htmlID, string(BorderStyle), border.cssStyleValue(session))
|
||||
}
|
||||
return
|
||||
|
||||
case BorderColor, BorderLeftColor, BorderRightColor, BorderTopColor, BorderBottomColor:
|
||||
if border := getBorder(view, Border); border != nil {
|
||||
session.updateCSSProperty(htmlID, BorderColor, border.cssColorValue(session))
|
||||
if border := getBorderProperty(view, Border); border != nil {
|
||||
session.updateCSSProperty(htmlID, string(BorderColor), border.cssColorValue(session))
|
||||
}
|
||||
return
|
||||
|
||||
case BorderWidth, BorderLeftWidth, BorderRightWidth, BorderTopWidth, BorderBottomWidth:
|
||||
if border := getBorder(view, Border); border != nil {
|
||||
session.updateCSSProperty(htmlID, BorderWidth, border.cssWidthValue(session))
|
||||
if border := getBorderProperty(view, Border); border != nil {
|
||||
session.updateCSSProperty(htmlID, string(BorderWidth), border.cssWidthValue(session))
|
||||
}
|
||||
return
|
||||
|
||||
case Outline, OutlineColor, OutlineStyle, OutlineWidth:
|
||||
session.updateCSSProperty(htmlID, Outline, GetOutline(view).cssString(session))
|
||||
return
|
||||
session.updateCSSProperty(htmlID, string(Outline), GetOutline(view).cssString(session))
|
||||
|
||||
case Shadow:
|
||||
session.updateCSSProperty(htmlID, "box-shadow", shadowCSS(view, Shadow, session))
|
||||
return
|
||||
|
||||
case TextShadow:
|
||||
session.updateCSSProperty(htmlID, "text-shadow", shadowCSS(view, TextShadow, session))
|
||||
return
|
||||
|
||||
case Radius, RadiusX, RadiusY, RadiusTopLeft, RadiusTopLeftX, RadiusTopLeftY,
|
||||
RadiusTopRight, RadiusTopRightX, RadiusTopRightY,
|
||||
|
@ -568,19 +558,16 @@ func viewPropertyChanged(view *viewData, tag string) {
|
|||
RadiusBottomRight, RadiusBottomRightX, RadiusBottomRightY:
|
||||
radius := GetRadius(view)
|
||||
session.updateCSSProperty(htmlID, "border-radius", radius.cssString(session))
|
||||
return
|
||||
|
||||
case Margin, MarginTop, MarginRight, MarginBottom, MarginLeft,
|
||||
"top-margin", "right-margin", "bottom-margin", "left-margin":
|
||||
margin := GetMargin(view)
|
||||
session.updateCSSProperty(htmlID, Margin, margin.cssString(session))
|
||||
return
|
||||
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, Padding, padding.cssString(session))
|
||||
return
|
||||
session.updateCSSProperty(htmlID, string(Padding), padding.cssString(session))
|
||||
|
||||
case AvoidBreak:
|
||||
if avoid, ok := boolProperty(view, AvoidBreak, session); ok {
|
||||
|
@ -590,7 +577,6 @@ func viewPropertyChanged(view *viewData, tag string) {
|
|||
session.updateCSSProperty(htmlID, "break-inside", "auto")
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
case Clip:
|
||||
if clip := getClipShape(view, Clip, session); clip != nil && clip.valid(session) {
|
||||
|
@ -598,29 +584,26 @@ func viewPropertyChanged(view *viewData, tag string) {
|
|||
} else {
|
||||
session.updateCSSProperty(htmlID, `clip-path`, "none")
|
||||
}
|
||||
return
|
||||
|
||||
case ShapeOutside:
|
||||
if clip := getClipShape(view, ShapeOutside, session); clip != nil && clip.valid(session) {
|
||||
session.updateCSSProperty(htmlID, ShapeOutside, clip.cssStyle(session))
|
||||
session.updateCSSProperty(htmlID, string(ShapeOutside), clip.cssStyle(session))
|
||||
} else {
|
||||
session.updateCSSProperty(htmlID, ShapeOutside, "none")
|
||||
session.updateCSSProperty(htmlID, string(ShapeOutside), "none")
|
||||
}
|
||||
return
|
||||
|
||||
case Filter:
|
||||
text := ""
|
||||
if value := view.getRaw(tag); value != nil {
|
||||
if value := view.getRaw(Filter); value != nil {
|
||||
if filter, ok := value.(ViewFilter); ok {
|
||||
text = filter.cssStyle(session)
|
||||
}
|
||||
}
|
||||
session.updateCSSProperty(htmlID, tag, text)
|
||||
return
|
||||
session.updateCSSProperty(htmlID, string(Filter), text)
|
||||
|
||||
case BackdropFilter:
|
||||
text := ""
|
||||
if value := view.getRaw(tag); value != nil {
|
||||
if value := view.getRaw(BackdropFilter); value != nil {
|
||||
if filter, ok := value.(ViewFilter); ok {
|
||||
text = filter.cssStyle(session)
|
||||
}
|
||||
|
@ -629,8 +612,7 @@ func viewPropertyChanged(view *viewData, tag string) {
|
|||
defer session.finishUpdateScript(htmlID)
|
||||
}
|
||||
session.updateCSSProperty(htmlID, "-webkit-backdrop-filter", text)
|
||||
session.updateCSSProperty(htmlID, tag, text)
|
||||
return
|
||||
session.updateCSSProperty(htmlID, string(BackdropFilter), text)
|
||||
|
||||
case FontName:
|
||||
if font, ok := stringProperty(view, FontName, session); ok {
|
||||
|
@ -638,7 +620,6 @@ func viewPropertyChanged(view *viewData, tag string) {
|
|||
} else {
|
||||
session.updateCSSProperty(htmlID, "font-family", "")
|
||||
}
|
||||
return
|
||||
|
||||
case Italic:
|
||||
if state, ok := boolProperty(view, tag, session); ok {
|
||||
|
@ -650,7 +631,6 @@ func viewPropertyChanged(view *viewData, tag string) {
|
|||
} else {
|
||||
session.updateCSSProperty(htmlID, "font-style", "")
|
||||
}
|
||||
return
|
||||
|
||||
case SmallCaps:
|
||||
if state, ok := boolProperty(view, tag, session); ok {
|
||||
|
@ -662,22 +642,18 @@ func viewPropertyChanged(view *viewData, tag string) {
|
|||
} else {
|
||||
session.updateCSSProperty(htmlID, "font-variant", "")
|
||||
}
|
||||
return
|
||||
|
||||
case Strikethrough, Overline, Underline:
|
||||
session.updateCSSProperty(htmlID, "text-decoration", view.cssTextDecoration(session))
|
||||
for _, tag2 := range []string{TextLineColor, TextLineStyle, TextLineThickness} {
|
||||
session.updateCSSProperty(htmlID, "text-decoration", textDecorationCSS(view, session))
|
||||
for _, tag2 := range []PropertyName{TextLineColor, TextLineStyle, TextLineThickness} {
|
||||
viewPropertyChanged(view, tag2)
|
||||
}
|
||||
return
|
||||
|
||||
case Transition:
|
||||
view.updateTransitionCSS()
|
||||
return
|
||||
session.updateCSSProperty(htmlID, "transition", transitionCSS(view, session))
|
||||
|
||||
case AnimationTag:
|
||||
session.updateCSSProperty(htmlID, AnimationTag, view.animationCSS(session))
|
||||
return
|
||||
session.updateCSSProperty(htmlID, "animation", animationCSS(view, session))
|
||||
|
||||
case AnimationPaused:
|
||||
paused, ok := boolProperty(view, AnimationPaused, session)
|
||||
|
@ -688,21 +664,18 @@ func viewPropertyChanged(view *viewData, tag string) {
|
|||
} else {
|
||||
session.updateCSSProperty(htmlID, `animation-play-state`, `running`)
|
||||
}
|
||||
return
|
||||
|
||||
case ZIndex, Order, TabSize:
|
||||
if i, ok := intProperty(view, tag, session, 0); ok {
|
||||
session.updateCSSProperty(htmlID, tag, strconv.Itoa(i))
|
||||
session.updateCSSProperty(htmlID, string(tag), strconv.Itoa(i))
|
||||
} else {
|
||||
session.updateCSSProperty(htmlID, tag, "")
|
||||
session.updateCSSProperty(htmlID, string(tag), "")
|
||||
}
|
||||
return
|
||||
|
||||
case Row, Column:
|
||||
if parentID := view.parentHTMLID(); parentID != "" {
|
||||
updateInnerHTML(parentID, session)
|
||||
}
|
||||
return
|
||||
|
||||
case UserSelect:
|
||||
if session.startUpdateScript(htmlID) {
|
||||
|
@ -720,7 +693,6 @@ func viewPropertyChanged(view *viewData, tag string) {
|
|||
session.updateCSSProperty(htmlID, "-webkit-user-select", "")
|
||||
session.updateCSSProperty(htmlID, "user-select", "")
|
||||
}
|
||||
return
|
||||
|
||||
case ColumnSpanAll:
|
||||
if spanAll, ok := boolProperty(view, ColumnSpanAll, session); ok && spanAll {
|
||||
|
@ -728,7 +700,6 @@ func viewPropertyChanged(view *viewData, tag string) {
|
|||
} else {
|
||||
session.updateCSSProperty(htmlID, `column-span`, `none`)
|
||||
}
|
||||
return
|
||||
|
||||
case Tooltip:
|
||||
if tooltip := GetTooltip(view); tooltip == "" {
|
||||
|
@ -738,68 +709,109 @@ func viewPropertyChanged(view *viewData, tag string) {
|
|||
session.updateProperty(htmlID, "onmouseenter", "mouseEnterEvent(this, event)")
|
||||
session.updateProperty(htmlID, "onmouseleave", "mouseLeaveEvent(this, event)")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
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, "")
|
||||
case PerspectiveOriginX, PerspectiveOriginY:
|
||||
if getTransform3D(view, session) {
|
||||
x, y := GetPerspectiveOrigin(view)
|
||||
value := ""
|
||||
if x.Type != Auto || y.Type != Auto {
|
||||
value = x.cssString("50%", session) + " " + y.cssString("50%", session)
|
||||
}
|
||||
session.updateCSSProperty(htmlID, "perspective-origin", value)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
colorTags := map[string]string{
|
||||
BackgroundColor: BackgroundColor,
|
||||
TextColor: "color",
|
||||
TextLineColor: "text-decoration-color",
|
||||
CaretColor: CaretColor,
|
||||
AccentColor: 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
|
||||
}
|
||||
|
||||
for _, floatTag := range []string{Opacity, ScaleX, ScaleY, ScaleZ, RotateX, RotateY, RotateZ} {
|
||||
if tag == floatTag {
|
||||
if f, ok := floatTextProperty(view, floatTag, session, 0); ok {
|
||||
session.updateCSSProperty(htmlID, floatTag, f)
|
||||
case BackfaceVisible:
|
||||
if getTransform3D(view, session) {
|
||||
if GetBackfaceVisible(view) {
|
||||
session.updateCSSProperty(htmlID, string(BackfaceVisible), "visible")
|
||||
} else {
|
||||
session.updateCSSProperty(htmlID, floatTag, "")
|
||||
session.updateCSSProperty(htmlID, string(BackfaceVisible), "hidden")
|
||||
}
|
||||
}
|
||||
|
||||
case OriginX, OriginY, OriginZ:
|
||||
x, y, z := getOrigin(view, session)
|
||||
value := ""
|
||||
|
||||
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,
|
||||
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())
|
||||
|
||||
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) Get(tag string) any {
|
||||
return view.get(strings.ToLower(tag))
|
||||
}
|
||||
|
||||
func (view *viewData) get(tag string) any {
|
||||
func viewGet(view View, tag PropertyName) any {
|
||||
if tag == ID {
|
||||
if view.viewID != "" {
|
||||
return view.viewID
|
||||
if id := view.ID(); id != "" {
|
||||
return id
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return view.viewStyle.get(tag)
|
||||
return viewStyleGet(view, tag)
|
||||
}
|
||||
|
||||
func (view *viewData) htmlTag() string {
|
||||
|
@ -848,6 +860,10 @@ func (view *viewData) cssStyle(self View, builder cssBuilder) {
|
|||
}
|
||||
}
|
||||
|
||||
func (view *viewData) htmlDisabledProperty() bool {
|
||||
return view.hasHtmlDisabled
|
||||
}
|
||||
|
||||
func (view *viewData) htmlProperties(self View, buffer *strings.Builder) {
|
||||
view.created = true
|
||||
|
||||
|
@ -908,23 +924,30 @@ func viewHTML(view View, buffer *strings.Builder) {
|
|||
}
|
||||
}
|
||||
|
||||
hasTooltip := false
|
||||
if tooltip := GetTooltip(view); tooltip != "" {
|
||||
buffer.WriteString(`data-tooltip=" `)
|
||||
buffer.WriteString(tooltip)
|
||||
buffer.WriteString(`" `)
|
||||
hasTooltip = true
|
||||
buffer.WriteString(`" onmouseenter="mouseEnterEvent(this, event)" onmouseleave="mouseLeaveEvent(this, event)" `)
|
||||
}
|
||||
|
||||
buffer.WriteString(`onscroll="scrollEvent(this, event)" `)
|
||||
|
||||
keyEventsHtml(view, buffer)
|
||||
mouseEventsHtml(view, buffer, hasTooltip)
|
||||
pointerEventsHtml(view, buffer)
|
||||
touchEventsHtml(view, buffer)
|
||||
focusEventsHtml(view, buffer)
|
||||
transitionEventsHtml(view, buffer)
|
||||
animationEventsHtml(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)
|
||||
|
@ -957,7 +980,7 @@ func (view *viewData) htmlClass(disabled bool) string {
|
|||
return cls
|
||||
}
|
||||
|
||||
func (view *viewData) handleCommand(self View, command string, data DataObject) bool {
|
||||
func (view *viewData) handleCommand(self View, command PropertyName, data DataObject) bool {
|
||||
switch command {
|
||||
|
||||
case KeyDownEvent, KeyUpEvent:
|
||||
|
@ -976,13 +999,13 @@ func (view *viewData) handleCommand(self View, command string, data DataObject)
|
|||
|
||||
case FocusEvent:
|
||||
view.hasFocus = true
|
||||
for _, listener := range getFocusListeners(view, nil, command) {
|
||||
for _, listener := range getNoParamEventListeners[View](view, nil, command) {
|
||||
listener(self)
|
||||
}
|
||||
|
||||
case LostFocusEvent:
|
||||
view.hasFocus = false
|
||||
for _, listener := range getFocusListeners(view, nil, command) {
|
||||
for _, listener := range getNoParamEventListeners[View](view, nil, command) {
|
||||
listener(self)
|
||||
}
|
||||
|
||||
|
@ -1030,7 +1053,7 @@ func (view *viewData) handleCommand(self View, command string, data DataObject)
|
|||
|
||||
}
|
||||
|
||||
func (view *viewData) SetChangeListener(tag string, listener func(View, string)) {
|
||||
func (view *viewData) SetChangeListener(tag PropertyName, listener func(View, PropertyName)) {
|
||||
if listener == nil {
|
||||
delete(view.changeListener, tag)
|
||||
} else {
|
||||
|
@ -1043,9 +1066,12 @@ func (view *viewData) HasFocus() bool {
|
|||
}
|
||||
|
||||
func (view *viewData) String() string {
|
||||
return getViewString(view, nil)
|
||||
buffer := allocStringBuilder()
|
||||
defer freeStringBuilder(buffer)
|
||||
writeViewStyle(view.tag, view, buffer, "", nil)
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
func (view *viewData) exscludeTags() []string {
|
||||
func (view *viewData) exscludeTags() []PropertyName {
|
||||
return nil
|
||||
}
|
||||
|
|
277
viewClip.go
277
viewClip.go
|
@ -15,19 +15,19 @@ type ClipShape interface {
|
|||
}
|
||||
|
||||
type insetClip struct {
|
||||
propertyList
|
||||
dataProperty
|
||||
}
|
||||
|
||||
type ellipseClip struct {
|
||||
propertyList
|
||||
dataProperty
|
||||
}
|
||||
|
||||
type circleClip struct {
|
||||
propertyList
|
||||
dataProperty
|
||||
}
|
||||
|
||||
type polygonClip struct {
|
||||
points []any
|
||||
dataProperty
|
||||
}
|
||||
|
||||
// InsetClip creates a rectangle View clipping area.
|
||||
|
@ -39,12 +39,12 @@ type polygonClip struct {
|
|||
func InsetClip(top, right, bottom, left SizeUnit, radius RadiusProperty) ClipShape {
|
||||
clip := new(insetClip)
|
||||
clip.init()
|
||||
clip.Set(Top, top)
|
||||
clip.Set(Right, right)
|
||||
clip.Set(Bottom, bottom)
|
||||
clip.Set(Left, left)
|
||||
clip.setRaw(Top, top)
|
||||
clip.setRaw(Right, right)
|
||||
clip.setRaw(Bottom, bottom)
|
||||
clip.setRaw(Left, left)
|
||||
if radius != nil {
|
||||
clip.Set(Radius, radius)
|
||||
clip.setRaw(Radius, radius)
|
||||
}
|
||||
return clip
|
||||
}
|
||||
|
@ -53,9 +53,9 @@ func InsetClip(top, right, bottom, left SizeUnit, radius RadiusProperty) ClipSha
|
|||
func CircleClip(x, y, radius SizeUnit) ClipShape {
|
||||
clip := new(circleClip)
|
||||
clip.init()
|
||||
clip.Set(X, x)
|
||||
clip.Set(Y, y)
|
||||
clip.Set(Radius, radius)
|
||||
clip.setRaw(X, x)
|
||||
clip.setRaw(Y, y)
|
||||
clip.setRaw(Radius, radius)
|
||||
return clip
|
||||
}
|
||||
|
||||
|
@ -63,10 +63,10 @@ func CircleClip(x, y, radius SizeUnit) ClipShape {
|
|||
func EllipseClip(x, y, rx, ry SizeUnit) ClipShape {
|
||||
clip := new(ellipseClip)
|
||||
clip.init()
|
||||
clip.Set(X, x)
|
||||
clip.Set(Y, y)
|
||||
clip.Set(RadiusX, rx)
|
||||
clip.Set(RadiusY, ry)
|
||||
clip.setRaw(X, x)
|
||||
clip.setRaw(Y, y)
|
||||
clip.setRaw(RadiusX, rx)
|
||||
clip.setRaw(RadiusY, ry)
|
||||
return clip
|
||||
}
|
||||
|
||||
|
@ -75,8 +75,8 @@ func EllipseClip(x, y, rx, ry SizeUnit) ClipShape {
|
|||
// or the text representation of SizeUnit, or elements of SizeUnit type.
|
||||
func PolygonClip(points []any) ClipShape {
|
||||
clip := new(polygonClip)
|
||||
clip.points = []any{}
|
||||
if clip.Set(Points, points) {
|
||||
clip.init()
|
||||
if polygonClipSet(clip, Points, points) != nil {
|
||||
return clip
|
||||
}
|
||||
return nil
|
||||
|
@ -85,34 +85,45 @@ func PolygonClip(points []any) ClipShape {
|
|||
// PolygonPointsClip creates a polygon View clipping area.
|
||||
func PolygonPointsClip(points []SizeUnit) ClipShape {
|
||||
clip := new(polygonClip)
|
||||
clip.points = []any{}
|
||||
if clip.Set(Points, points) {
|
||||
clip.init()
|
||||
if polygonClipSet(clip, Points, points) != nil {
|
||||
return clip
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (clip *insetClip) Set(tag string, value any) bool {
|
||||
switch strings.ToLower(tag) {
|
||||
func (clip *insetClip) init() {
|
||||
clip.dataProperty.init()
|
||||
clip.set = insetClipSet
|
||||
clip.supportedProperties = []PropertyName{
|
||||
Top, Right, Bottom, Left, Radius,
|
||||
RadiusX, RadiusY, RadiusTopLeft, RadiusTopLeftX, RadiusTopLeftY,
|
||||
RadiusTopRight, RadiusTopRightX, RadiusTopRightY,
|
||||
RadiusBottomLeft, RadiusBottomLeftX, RadiusBottomLeftY,
|
||||
RadiusBottomRight, RadiusBottomRightX, RadiusBottomRightY,
|
||||
}
|
||||
}
|
||||
|
||||
func insetClipSet(properties Properties, tag PropertyName, value any) []PropertyName {
|
||||
switch tag {
|
||||
case Top, Right, Bottom, Left:
|
||||
if value == nil {
|
||||
clip.Remove(tag)
|
||||
return true
|
||||
}
|
||||
return clip.setSizeProperty(tag, value)
|
||||
return setSizeProperty(properties, tag, value)
|
||||
|
||||
case Radius:
|
||||
return clip.setRadius(value)
|
||||
return setRadiusProperty(properties, value)
|
||||
|
||||
case RadiusX, RadiusY, RadiusTopLeft, RadiusTopLeftX, RadiusTopLeftY,
|
||||
RadiusTopRight, RadiusTopRightX, RadiusTopRightY,
|
||||
RadiusBottomLeft, RadiusBottomLeftX, RadiusBottomLeftY,
|
||||
RadiusBottomRight, RadiusBottomRightX, RadiusBottomRightY:
|
||||
return clip.setRadiusElement(tag, value)
|
||||
if setRadiusPropertyElement(properties, tag, value) {
|
||||
return []PropertyName{tag, Radius}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
ErrorLogF(`"%s" property is not supported by the inset clip shape`, tag)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
func (clip *insetClip) String() string {
|
||||
|
@ -122,12 +133,12 @@ func (clip *insetClip) String() string {
|
|||
func (clip *insetClip) writeString(buffer *strings.Builder, indent string) {
|
||||
buffer.WriteString("inset { ")
|
||||
comma := false
|
||||
for _, tag := range []string{Top, Right, Bottom, Left, Radius} {
|
||||
for _, tag := range []PropertyName{Top, Right, Bottom, Left, Radius} {
|
||||
if value, ok := clip.properties[tag]; ok {
|
||||
if comma {
|
||||
buffer.WriteString(", ")
|
||||
}
|
||||
buffer.WriteString(tag)
|
||||
buffer.WriteString(string(tag))
|
||||
buffer.WriteString(" = ")
|
||||
writePropertyValue(buffer, tag, value, indent)
|
||||
comma = true
|
||||
|
@ -143,7 +154,7 @@ func (clip *insetClip) cssStyle(session Session) string {
|
|||
defer freeStringBuilder(buffer)
|
||||
|
||||
leadText := "inset("
|
||||
for _, tag := range []string{Top, Right, Bottom, Left} {
|
||||
for _, tag := range []PropertyName{Top, Right, Bottom, Left} {
|
||||
value, _ := sizeProperty(clip, tag, session)
|
||||
buffer.WriteString(leadText)
|
||||
buffer.WriteString(value.cssString("0px", session))
|
||||
|
@ -160,7 +171,7 @@ func (clip *insetClip) cssStyle(session Session) string {
|
|||
}
|
||||
|
||||
func (clip *insetClip) valid(session Session) bool {
|
||||
for _, tag := range []string{Top, Right, Bottom, Left, Radius, RadiusX, RadiusY} {
|
||||
for _, tag := range []PropertyName{Top, Right, Bottom, Left, Radius, RadiusX, RadiusY} {
|
||||
if value, ok := sizeProperty(clip, tag, session); ok && value.Type != Auto && value.Value != 0 {
|
||||
return true
|
||||
}
|
||||
|
@ -168,18 +179,20 @@ func (clip *insetClip) valid(session Session) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (clip *circleClip) Set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
clip.Remove(tag)
|
||||
}
|
||||
func (clip *circleClip) init() {
|
||||
clip.dataProperty.init()
|
||||
clip.set = circleClipSet
|
||||
clip.supportedProperties = []PropertyName{X, Y, Radius}
|
||||
}
|
||||
|
||||
switch strings.ToLower(tag) {
|
||||
func circleClipSet(properties Properties, tag PropertyName, value any) []PropertyName {
|
||||
switch tag {
|
||||
case X, Y, Radius:
|
||||
return clip.setSizeProperty(tag, value)
|
||||
return setSizeProperty(properties, tag, value)
|
||||
}
|
||||
|
||||
ErrorLogF(`"%s" property is not supported by the circle clip shape`, tag)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
func (clip *circleClip) String() string {
|
||||
|
@ -189,12 +202,12 @@ func (clip *circleClip) String() string {
|
|||
func (clip *circleClip) writeString(buffer *strings.Builder, indent string) {
|
||||
buffer.WriteString("circle { ")
|
||||
comma := false
|
||||
for _, tag := range []string{Radius, X, Y} {
|
||||
for _, tag := range []PropertyName{Radius, X, Y} {
|
||||
if value, ok := clip.properties[tag]; ok {
|
||||
if comma {
|
||||
buffer.WriteString(", ")
|
||||
}
|
||||
buffer.WriteString(tag)
|
||||
buffer.WriteString(string(tag))
|
||||
buffer.WriteString(" = ")
|
||||
writePropertyValue(buffer, tag, value, indent)
|
||||
comma = true
|
||||
|
@ -232,22 +245,27 @@ func (clip *circleClip) valid(session Session) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func (clip *ellipseClip) Set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
clip.Remove(tag)
|
||||
}
|
||||
func (clip *ellipseClip) init() {
|
||||
clip.dataProperty.init()
|
||||
clip.set = ellipseClipSet
|
||||
clip.supportedProperties = []PropertyName{X, Y, Radius, RadiusX, RadiusY}
|
||||
}
|
||||
|
||||
switch strings.ToLower(tag) {
|
||||
func ellipseClipSet(properties Properties, tag PropertyName, value any) []PropertyName {
|
||||
switch tag {
|
||||
case X, Y, RadiusX, RadiusY:
|
||||
return clip.setSizeProperty(tag, value)
|
||||
return setSizeProperty(properties, tag, value)
|
||||
|
||||
case Radius:
|
||||
return clip.setSizeProperty(RadiusX, value) &&
|
||||
clip.setSizeProperty(RadiusY, value)
|
||||
if result := setSizeProperty(properties, RadiusX, value); result != nil {
|
||||
properties.setRaw(RadiusY, properties.getRaw(RadiusX))
|
||||
return append(result, RadiusY)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
ErrorLogF(`"%s" property is not supported by the ellipse clip shape`, tag)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
func (clip *ellipseClip) String() string {
|
||||
|
@ -257,12 +275,12 @@ func (clip *ellipseClip) String() string {
|
|||
func (clip *ellipseClip) writeString(buffer *strings.Builder, indent string) {
|
||||
buffer.WriteString("ellipse { ")
|
||||
comma := false
|
||||
for _, tag := range []string{RadiusX, RadiusY, X, Y} {
|
||||
for _, tag := range []PropertyName{RadiusX, RadiusY, X, Y} {
|
||||
if value, ok := clip.properties[tag]; ok {
|
||||
if comma {
|
||||
buffer.WriteString(", ")
|
||||
}
|
||||
buffer.WriteString(tag)
|
||||
buffer.WriteString(string(tag))
|
||||
buffer.WriteString(" = ")
|
||||
writePropertyValue(buffer, tag, value, indent)
|
||||
comma = true
|
||||
|
@ -302,104 +320,91 @@ func (clip *ellipseClip) valid(session Session) bool {
|
|||
return rx.Value != 0 && ry.Value != 0
|
||||
}
|
||||
|
||||
func (clip *polygonClip) Get(tag string) any {
|
||||
if Points == strings.ToLower(tag) {
|
||||
return clip.points
|
||||
}
|
||||
return nil
|
||||
func (clip *polygonClip) init() {
|
||||
clip.dataProperty.init()
|
||||
clip.set = polygonClipSet
|
||||
clip.supportedProperties = []PropertyName{Points}
|
||||
}
|
||||
|
||||
func (clip *polygonClip) getRaw(tag string) any {
|
||||
return clip.Get(tag)
|
||||
}
|
||||
|
||||
func (clip *polygonClip) Set(tag string, value any) bool {
|
||||
if Points == strings.ToLower(tag) {
|
||||
func polygonClipSet(properties Properties, tag PropertyName, value any) []PropertyName {
|
||||
if Points == tag {
|
||||
switch value := value.(type) {
|
||||
case []any:
|
||||
result := true
|
||||
clip.points = make([]any, len(value))
|
||||
points := make([]any, len(value))
|
||||
for i, val := range value {
|
||||
switch val := val.(type) {
|
||||
case string:
|
||||
if isConstantName(val) {
|
||||
clip.points[i] = val
|
||||
points[i] = val
|
||||
} else if size, ok := StringToSizeUnit(val); ok {
|
||||
clip.points[i] = size
|
||||
points[i] = size
|
||||
} else {
|
||||
notCompatibleType(tag, val)
|
||||
result = false
|
||||
return nil
|
||||
}
|
||||
|
||||
case SizeUnit:
|
||||
clip.points[i] = val
|
||||
points[i] = val
|
||||
|
||||
default:
|
||||
notCompatibleType(tag, val)
|
||||
clip.points[i] = AutoSize()
|
||||
result = false
|
||||
points[i] = AutoSize()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return result
|
||||
properties.setRaw(Points, points)
|
||||
return []PropertyName{tag}
|
||||
|
||||
case []SizeUnit:
|
||||
clip.points = make([]any, len(value))
|
||||
points := make([]any, len(value))
|
||||
for i, point := range value {
|
||||
clip.points[i] = point
|
||||
points[i] = point
|
||||
}
|
||||
return true
|
||||
properties.setRaw(Points, points)
|
||||
return []PropertyName{tag}
|
||||
|
||||
case string:
|
||||
result := true
|
||||
values := strings.Split(value, ",")
|
||||
clip.points = make([]any, len(values))
|
||||
points := make([]any, len(values))
|
||||
for i, val := range values {
|
||||
val = strings.Trim(val, " \t\n\r")
|
||||
if isConstantName(val) {
|
||||
clip.points[i] = val
|
||||
points[i] = val
|
||||
} else if size, ok := StringToSizeUnit(val); ok {
|
||||
clip.points[i] = size
|
||||
points[i] = size
|
||||
} else {
|
||||
notCompatibleType(tag, val)
|
||||
result = false
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return result
|
||||
properties.setRaw(Points, points)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (clip *polygonClip) setRaw(tag string, value any) {
|
||||
clip.Set(tag, value)
|
||||
}
|
||||
|
||||
func (clip *polygonClip) Remove(tag string) {
|
||||
if Points == strings.ToLower(tag) {
|
||||
clip.points = []any{}
|
||||
}
|
||||
}
|
||||
|
||||
func (clip *polygonClip) Clear() {
|
||||
clip.points = []any{}
|
||||
}
|
||||
|
||||
func (clip *polygonClip) AllTags() []string {
|
||||
return []string{Points}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (clip *polygonClip) String() string {
|
||||
return runStringWriter(clip)
|
||||
}
|
||||
|
||||
func (clip *polygonClip) points() []any {
|
||||
if value := clip.getRaw(Points); value != nil {
|
||||
if points, ok := value.([]any); ok {
|
||||
return points
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (clip *polygonClip) writeString(buffer *strings.Builder, indent string) {
|
||||
|
||||
buffer.WriteString("inset { ")
|
||||
|
||||
if clip.points != nil {
|
||||
buffer.WriteString(Points)
|
||||
if points := clip.points(); points != nil {
|
||||
buffer.WriteString(string(Points))
|
||||
buffer.WriteString(` = "`)
|
||||
for i, value := range clip.points {
|
||||
for i, value := range points {
|
||||
if i > 0 {
|
||||
buffer.WriteString(", ")
|
||||
}
|
||||
|
@ -408,13 +413,13 @@ func (clip *polygonClip) writeString(buffer *strings.Builder, indent string) {
|
|||
|
||||
buffer.WriteString(`" `)
|
||||
}
|
||||
|
||||
buffer.WriteRune('}')
|
||||
}
|
||||
|
||||
func (clip *polygonClip) cssStyle(session Session) string {
|
||||
|
||||
count := len(clip.points)
|
||||
points := clip.points()
|
||||
count := len(points)
|
||||
if count < 2 {
|
||||
return ""
|
||||
}
|
||||
|
@ -443,9 +448,9 @@ func (clip *polygonClip) cssStyle(session Session) string {
|
|||
leadText := "polygon("
|
||||
for i := 1; i < count; i += 2 {
|
||||
buffer.WriteString(leadText)
|
||||
writePoint(clip.points[i-1])
|
||||
writePoint(points[i-1])
|
||||
buffer.WriteRune(' ')
|
||||
writePoint(clip.points[i])
|
||||
writePoint(points[i])
|
||||
leadText = ", "
|
||||
}
|
||||
|
||||
|
@ -454,42 +459,46 @@ func (clip *polygonClip) cssStyle(session Session) string {
|
|||
}
|
||||
|
||||
func (clip *polygonClip) valid(session Session) bool {
|
||||
return len(clip.points) > 0
|
||||
return len(clip.points()) > 0
|
||||
}
|
||||
|
||||
func parseClipShape(obj DataObject) ClipShape {
|
||||
switch obj.Tag() {
|
||||
case "inset":
|
||||
clip := new(insetClip)
|
||||
for _, tag := range []string{Top, Right, Bottom, Left, Radius, RadiusX, RadiusY} {
|
||||
if value, ok := obj.PropertyValue(tag); ok {
|
||||
clip.Set(tag, value)
|
||||
clip.init()
|
||||
for _, tag := range []PropertyName{Top, Right, Bottom, Left, Radius, RadiusX, RadiusY} {
|
||||
if value, ok := obj.PropertyValue(string(tag)); ok {
|
||||
insetClipSet(clip, tag, value)
|
||||
}
|
||||
}
|
||||
return clip
|
||||
|
||||
case "circle":
|
||||
clip := new(ellipseClip)
|
||||
for _, tag := range []string{X, Y, Radius} {
|
||||
if value, ok := obj.PropertyValue(tag); ok {
|
||||
clip.Set(tag, value)
|
||||
clip.init()
|
||||
for _, tag := range []PropertyName{X, Y, Radius} {
|
||||
if value, ok := obj.PropertyValue(string(tag)); ok {
|
||||
circleClipSet(clip, tag, value)
|
||||
}
|
||||
}
|
||||
return clip
|
||||
|
||||
case "ellipse":
|
||||
clip := new(ellipseClip)
|
||||
for _, tag := range []string{X, Y, RadiusX, RadiusY} {
|
||||
if value, ok := obj.PropertyValue(tag); ok {
|
||||
clip.Set(tag, value)
|
||||
clip.init()
|
||||
for _, tag := range []PropertyName{X, Y, RadiusX, RadiusY} {
|
||||
if value, ok := obj.PropertyValue(string(tag)); ok {
|
||||
ellipseClipSet(clip, tag, value)
|
||||
}
|
||||
}
|
||||
return clip
|
||||
|
||||
case "polygon":
|
||||
clip := new(ellipseClip)
|
||||
if value, ok := obj.PropertyValue(Points); ok {
|
||||
clip.Set(Points, value)
|
||||
clip.init()
|
||||
if value, ok := obj.PropertyValue(string(Points)); ok {
|
||||
polygonClipSet(clip, Points, value)
|
||||
}
|
||||
return clip
|
||||
}
|
||||
|
@ -497,45 +506,45 @@ func parseClipShape(obj DataObject) ClipShape {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (style *viewStyle) setClipShape(tag string, value any) bool {
|
||||
func setClipShapeProperty(properties Properties, tag PropertyName, value any) []PropertyName {
|
||||
switch value := value.(type) {
|
||||
case ClipShape:
|
||||
style.properties[tag] = value
|
||||
return true
|
||||
properties.setRaw(tag, value)
|
||||
return []PropertyName{tag}
|
||||
|
||||
case string:
|
||||
if isConstantName(value) {
|
||||
style.properties[tag] = value
|
||||
return true
|
||||
properties.setRaw(tag, value)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
if obj := NewDataObject(value); obj == nil {
|
||||
if clip := parseClipShape(obj); clip != nil {
|
||||
style.properties[tag] = clip
|
||||
return true
|
||||
properties.setRaw(tag, clip)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
}
|
||||
|
||||
case DataObject:
|
||||
if clip := parseClipShape(value); clip != nil {
|
||||
style.properties[tag] = clip
|
||||
return true
|
||||
properties.setRaw(tag, clip)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
case DataValue:
|
||||
if value.IsObject() {
|
||||
if clip := parseClipShape(value.Object()); clip != nil {
|
||||
style.properties[tag] = clip
|
||||
return true
|
||||
properties.setRaw(tag, clip)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
func getClipShape(prop Properties, tag string, session Session) ClipShape {
|
||||
func getClipShape(prop Properties, tag PropertyName, session Session) ClipShape {
|
||||
if value := prop.getRaw(tag); value != nil {
|
||||
switch value := value.(type) {
|
||||
case ClipShape:
|
||||
|
|
|
@ -85,6 +85,7 @@ func CreateViewFromObject(session Session, object DataObject) View {
|
|||
defer session.setIgnoreViewUpdates(false)
|
||||
}
|
||||
view := creator(session)
|
||||
view.init(session)
|
||||
if customView, ok := view.(CustomView); ok {
|
||||
if !InitCustomView(customView, tag, session, nil) {
|
||||
return nil
|
||||
|
|
126
viewFilter.go
126
viewFilter.go
|
@ -10,43 +10,43 @@ const (
|
|||
// Blur is the constant for "blur" property tag.
|
||||
//
|
||||
// Used by `ViewFilter`.
|
||||
// Applies a Gaussian blur. The value of radius defines the value of the standard deviation to the Gaussian function, or
|
||||
// how many pixels on the screen blend into each other, so a larger value will create more blur. The lacuna value for
|
||||
// Applies a Gaussian blur. The value of radius defines the value of the standard deviation to the Gaussian function, or
|
||||
// how many pixels on the screen blend into each other, so a larger value will create more blur. The lacuna value for
|
||||
// interpolation is 0. The parameter is specified as a length in pixels.
|
||||
//
|
||||
// Supported types: `float`, `int`, `string`.
|
||||
//
|
||||
// Internal type is `float`, other types converted to it during assignment.
|
||||
Blur = "blur"
|
||||
Blur PropertyName = "blur"
|
||||
|
||||
// Brightness is the constant for "brightness" property tag.
|
||||
//
|
||||
// Used by `ViewFilter`.
|
||||
// Applies a linear multiplier to input image, making it appear more or less bright. A value of 0% will create an image
|
||||
// that is completely black. A value of 100% leaves the input unchanged. Other values are linear multipliers on the
|
||||
// Applies a linear multiplier to input image, making it appear more or less bright. A value of 0% will create an image
|
||||
// that is completely black. A value of 100% leaves the input unchanged. Other values are linear multipliers on the
|
||||
// effect. Values of an amount over 100% are allowed, providing brighter results.
|
||||
//
|
||||
// Supported types: `float`, `int`, `string`.
|
||||
//
|
||||
// Internal type is `float`, other types converted to it during assignment.
|
||||
Brightness = "brightness"
|
||||
Brightness PropertyName = "brightness"
|
||||
|
||||
// Contrast is the constant for "contrast" property tag.
|
||||
//
|
||||
// Used by `ViewFilter`.
|
||||
// Adjusts the contrast of the input. A value of 0% will create an image that is completely black. A value of 100% leaves
|
||||
// Adjusts the contrast of the input. A value of 0% will create an image that is completely black. A value of 100% leaves
|
||||
// the input unchanged. Values of amount over 100% are allowed, providing results with less contrast.
|
||||
//
|
||||
// Supported types: `float`, `int`, `string`.
|
||||
//
|
||||
// Internal type is `float`, other types converted to it during assignment.
|
||||
Contrast = "contrast"
|
||||
Contrast PropertyName = "contrast"
|
||||
|
||||
// DropShadow is the constant for "drop-shadow" property tag.
|
||||
//
|
||||
// Used by `ViewFilter`.
|
||||
// Applies a drop shadow effect to the input image. A drop shadow is effectively a blurred, offset version of the input
|
||||
// image's alpha mask drawn in a particular color, composited below the image. Shadow parameters are set using the
|
||||
// Applies a drop shadow effect to the input image. A drop shadow is effectively a blurred, offset version of the input
|
||||
// image's alpha mask drawn in a particular color, composited below the image. Shadow parameters are set using the
|
||||
// `ViewShadow` interface.
|
||||
//
|
||||
// Supported types: `[]ViewShadow`, `ViewShadow`, `string`.
|
||||
|
@ -58,25 +58,25 @@ const (
|
|||
// `[]ViewShadow` - stored as is, no conversion performed.
|
||||
// `ViewShadow` - converted to `[]ViewShadow`.
|
||||
// `string` - string representation of `ViewShadow`. Example: "_{blur = 1em, color = black, spread-radius = 0.5em}".
|
||||
DropShadow = "drop-shadow"
|
||||
DropShadow PropertyName = "drop-shadow"
|
||||
|
||||
// Grayscale is the constant for "grayscale" property tag.
|
||||
//
|
||||
// Used by `ViewFilter`.
|
||||
// Converts the input image to grayscale. The value of ‘amount’ defines the proportion of the conversion. A value of 100%
|
||||
// is completely grayscale. A value of 0% leaves the input unchanged. Values between 0% and 100% are linear multipliers on
|
||||
// Converts the input image to grayscale. The value of ‘amount’ defines the proportion of the conversion. A value of 100%
|
||||
// is completely grayscale. A value of 0% leaves the input unchanged. Values between 0% and 100% are linear multipliers on
|
||||
// the effect.
|
||||
//
|
||||
// Supported types: `float`, `int`, `string`.
|
||||
//
|
||||
// Internal type is `float`, other types converted to it during assignment.
|
||||
Grayscale = "grayscale"
|
||||
Grayscale PropertyName = "grayscale"
|
||||
|
||||
// HueRotate is the constant for "hue-rotate" property tag.
|
||||
//
|
||||
// Used by `ViewFilter`.
|
||||
// Applies a hue rotation on the input image. The value of ‘angle’ defines the number of degrees around the color circle
|
||||
// the input samples will be adjusted. A value of 0deg leaves the input unchanged. If the ‘angle’ parameter is missing, a
|
||||
// Applies a hue rotation on the input image. The value of ‘angle’ defines the number of degrees around the color circle
|
||||
// the input samples will be adjusted. A value of 0deg leaves the input unchanged. If the ‘angle’ parameter is missing, a
|
||||
// value of 0deg is used. Though there is no maximum value, the effect of values above 360deg wraps around.
|
||||
//
|
||||
// Supported types: `AngleUnit`, `string`, `float`, `int`.
|
||||
|
@ -89,45 +89,43 @@ const (
|
|||
// `string` - must contain string representation of `AngleUnit`. If numeric value will be provided without any suffix then `AngleUnit` with value and `Radian` value type will be created.
|
||||
// `float` - a new `AngleUnit` value will be created with `Radian` as a type.
|
||||
// `int` - a new `AngleUnit` value will be created with `Radian` as a type.
|
||||
HueRotate = "hue-rotate"
|
||||
HueRotate PropertyName = "hue-rotate"
|
||||
|
||||
// Invert is the constant for "invert" property tag.
|
||||
//
|
||||
// Used by `ViewFilter`.
|
||||
// Inverts the samples in the input image. The value of ‘amount’ defines the proportion of the conversion. A value of 100%
|
||||
// is completely inverted. A value of 0% leaves the input unchanged. Values between 0% and 100% are linear multipliers on
|
||||
// Inverts the samples in the input image. The value of ‘amount’ defines the proportion of the conversion. A value of 100%
|
||||
// is completely inverted. A value of 0% leaves the input unchanged. Values between 0% and 100% are linear multipliers on
|
||||
// the effect.
|
||||
//
|
||||
// Supported types: `float64`, `int`, `string`.
|
||||
//
|
||||
// Internal type is `float`, other types converted to it during assignment.
|
||||
Invert = "invert"
|
||||
Invert PropertyName = "invert"
|
||||
|
||||
// Saturate is the constant for "saturate" property tag.
|
||||
//
|
||||
// Used by `ViewFilter`.
|
||||
// Saturates the input image. The value of ‘amount’ defines the proportion of the conversion. A value of 0% is completely
|
||||
// un-saturated. A value of 100% leaves the input unchanged. Other values are linear multipliers on the effect. Values of
|
||||
// Saturates the input image. The value of ‘amount’ defines the proportion of the conversion. A value of 0% is completely
|
||||
// un-saturated. A value of 100% leaves the input unchanged. Other values are linear multipliers on the effect. Values of
|
||||
// amount over 100% are allowed, providing super-saturated results.
|
||||
//
|
||||
// Supported types: `float`, `int`, `string`.
|
||||
//
|
||||
// Internal type is `float`, other types converted to it during assignment.
|
||||
Saturate = "saturate"
|
||||
Saturate PropertyName = "saturate"
|
||||
|
||||
// Sepia is the constant for "sepia" property tag.
|
||||
//
|
||||
// Used by `ViewFilter`.
|
||||
// Converts the input image to sepia. The value of ‘amount’ defines the proportion of the conversion. A value of 100% is
|
||||
// completely sepia. A value of 0% leaves the input unchanged. Values between 0% and 100% are linear multipliers on the
|
||||
// Converts the input image to sepia. The value of ‘amount’ defines the proportion of the conversion. A value of 100% is
|
||||
// completely sepia. A value of 0% leaves the input unchanged. Values between 0% and 100% are linear multipliers on the
|
||||
// effect.
|
||||
//
|
||||
// Supported types: `float`, `int`, `string`.
|
||||
//
|
||||
// Internal type is `float`, other types converted to it during assignment.
|
||||
Sepia = "sepia"
|
||||
|
||||
//Opacity = "opacity"
|
||||
Sepia PropertyName = "sepia"
|
||||
)
|
||||
|
||||
// ViewFilter defines an applied to a View a graphical effects like blur or color shift.
|
||||
|
@ -140,20 +138,20 @@ type ViewFilter interface {
|
|||
}
|
||||
|
||||
type viewFilter struct {
|
||||
propertyList
|
||||
dataProperty
|
||||
}
|
||||
|
||||
// NewViewFilter creates the new ViewFilter
|
||||
func NewViewFilter(params Params) ViewFilter {
|
||||
if params != nil {
|
||||
if len(params) > 0 {
|
||||
filter := new(viewFilter)
|
||||
filter.init()
|
||||
for tag, value := range params {
|
||||
filter.Set(tag, value)
|
||||
}
|
||||
if len(filter.properties) > 0 {
|
||||
return filter
|
||||
if !filter.Set(tag, value) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return filter
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -166,10 +164,10 @@ func newViewFilter(obj DataObject) ViewFilter {
|
|||
tag := node.Tag()
|
||||
switch node.Type() {
|
||||
case TextNode:
|
||||
filter.Set(tag, node.Text())
|
||||
filter.Set(PropertyName(tag), node.Text())
|
||||
|
||||
case ObjectNode:
|
||||
if tag == HueRotate {
|
||||
if tag == string(HueRotate) {
|
||||
// TODO
|
||||
} else {
|
||||
ErrorLog(`Invalid value of "` + tag + `"`)
|
||||
|
@ -188,28 +186,31 @@ func newViewFilter(obj DataObject) ViewFilter {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (filter *viewFilter) Set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
filter.Remove(tag)
|
||||
return true
|
||||
}
|
||||
func (filter *viewFilter) init() {
|
||||
filter.dataProperty.init()
|
||||
filter.set = viewFilterSet
|
||||
filter.supportedProperties = []PropertyName{Blur, Brightness, Contrast, Saturate, Grayscale, Invert, Opacity, Sepia, HueRotate, DropShadow}
|
||||
}
|
||||
|
||||
switch strings.ToLower(tag) {
|
||||
func viewFilterSet(properties Properties, tag PropertyName, value any) []PropertyName {
|
||||
switch tag {
|
||||
case Blur, Brightness, Contrast, Saturate:
|
||||
return filter.setFloatProperty(tag, value, 0, 10000)
|
||||
return setFloatProperty(properties, tag, value, 0, 10000)
|
||||
|
||||
case Grayscale, Invert, Opacity, Sepia:
|
||||
return filter.setFloatProperty(tag, value, 0, 100)
|
||||
return setFloatProperty(properties, tag, value, 0, 100)
|
||||
|
||||
case HueRotate:
|
||||
return filter.setAngleProperty(tag, value)
|
||||
return setAngleProperty(properties, tag, value)
|
||||
|
||||
case DropShadow:
|
||||
return filter.setShadow(tag, value)
|
||||
if setShadowProperty(properties, tag, value) {
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
}
|
||||
|
||||
ErrorLogF(`"%s" property is not supported by the view filter`, tag)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
func (filter *viewFilter) String() string {
|
||||
|
@ -225,7 +226,7 @@ func (filter *viewFilter) writeString(buffer *strings.Builder, indent string) {
|
|||
if comma {
|
||||
buffer.WriteString(", ")
|
||||
}
|
||||
buffer.WriteString(tag)
|
||||
buffer.WriteString(string(tag))
|
||||
buffer.WriteString(" = ")
|
||||
writePropertyValue(buffer, tag, value, indent)
|
||||
comma = true
|
||||
|
@ -239,18 +240,18 @@ func (filter *viewFilter) cssStyle(session Session) string {
|
|||
defer freeStringBuilder(buffer)
|
||||
|
||||
if value, ok := floatTextProperty(filter, Blur, session, 0); ok {
|
||||
buffer.WriteString(Blur)
|
||||
buffer.WriteString(string(Blur))
|
||||
buffer.WriteRune('(')
|
||||
buffer.WriteString(value)
|
||||
buffer.WriteString("px)")
|
||||
}
|
||||
|
||||
for _, tag := range []string{Brightness, Contrast, Saturate, Grayscale, Invert, Opacity, Sepia} {
|
||||
for _, tag := range []PropertyName{Brightness, Contrast, Saturate, Grayscale, Invert, Opacity, Sepia} {
|
||||
if value, ok := floatTextProperty(filter, tag, session, 0); ok {
|
||||
if buffer.Len() > 0 {
|
||||
buffer.WriteRune(' ')
|
||||
}
|
||||
buffer.WriteString(tag)
|
||||
buffer.WriteString(string(tag))
|
||||
buffer.WriteRune('(')
|
||||
buffer.WriteString(value)
|
||||
buffer.WriteString("%)")
|
||||
|
@ -261,7 +262,7 @@ func (filter *viewFilter) cssStyle(session Session) string {
|
|||
if buffer.Len() > 0 {
|
||||
buffer.WriteRune(' ')
|
||||
}
|
||||
buffer.WriteString(HueRotate)
|
||||
buffer.WriteString(string(HueRotate))
|
||||
buffer.WriteRune('(')
|
||||
buffer.WriteString(value.cssString())
|
||||
buffer.WriteRune(')')
|
||||
|
@ -284,36 +285,37 @@ func (filter *viewFilter) cssStyle(session Session) string {
|
|||
return buffer.String()
|
||||
}
|
||||
|
||||
func (style *viewStyle) setFilter(tag string, value any) bool {
|
||||
func setFilterProperty(properties Properties, tag PropertyName, value any) []PropertyName {
|
||||
switch value := value.(type) {
|
||||
case ViewFilter:
|
||||
style.properties[tag] = value
|
||||
return true
|
||||
properties.setRaw(tag, value)
|
||||
return []PropertyName{tag}
|
||||
|
||||
case string:
|
||||
if obj := NewDataObject(value); obj == nil {
|
||||
if filter := newViewFilter(obj); filter != nil {
|
||||
style.properties[tag] = filter
|
||||
return true
|
||||
properties.setRaw(tag, filter)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
}
|
||||
|
||||
case DataObject:
|
||||
if filter := newViewFilter(value); filter != nil {
|
||||
style.properties[tag] = filter
|
||||
return true
|
||||
properties.setRaw(tag, filter)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
case DataValue:
|
||||
if value.IsObject() {
|
||||
if filter := newViewFilter(value.Object()); filter != nil {
|
||||
style.properties[tag] = filter
|
||||
return true
|
||||
properties.setRaw(tag, filter)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFilter returns a View graphical effects like blur or color shift.
|
||||
|
|
229
viewStyle.go
229
viewStyle.go
|
@ -12,72 +12,31 @@ type ViewStyle interface {
|
|||
Properties
|
||||
|
||||
// Transition returns the transition animation of the property. Returns nil is there is no transition animation.
|
||||
Transition(tag string) Animation
|
||||
Transition(tag PropertyName) Animation
|
||||
|
||||
// Transitions returns the map of transition animations. The result is always non-nil.
|
||||
Transitions() map[string]Animation
|
||||
Transitions() map[PropertyName]Animation
|
||||
|
||||
// SetTransition sets the transition animation for the property if "animation" argument is not nil, and
|
||||
// removes the transition animation of the property if "animation" argument is nil.
|
||||
// The "tag" argument is the property name.
|
||||
SetTransition(tag string, animation Animation)
|
||||
SetTransition(tag PropertyName, animation Animation)
|
||||
|
||||
cssViewStyle(buffer cssBuilder, session Session)
|
||||
}
|
||||
|
||||
type viewStyle struct {
|
||||
propertyList
|
||||
transitions map[string]Animation
|
||||
}
|
||||
|
||||
// Range defines range limits. The First and Last value are included in the range
|
||||
type Range struct {
|
||||
First, Last int
|
||||
//transitions map[PropertyName]Animation
|
||||
}
|
||||
|
||||
type stringWriter interface {
|
||||
writeString(buffer *strings.Builder, indent string)
|
||||
}
|
||||
|
||||
// String returns a string representation of the Range struct
|
||||
func (r Range) String() string {
|
||||
if r.First == r.Last {
|
||||
return fmt.Sprintf("%d", r.First)
|
||||
}
|
||||
return fmt.Sprintf("%d:%d", r.First, r.Last)
|
||||
}
|
||||
|
||||
func (r *Range) setValue(value string) bool {
|
||||
var err error
|
||||
if strings.Contains(value, ":") {
|
||||
values := strings.Split(value, ":")
|
||||
if len(values) != 2 {
|
||||
ErrorLog("Invalid range value: " + value)
|
||||
return false
|
||||
}
|
||||
if r.First, err = strconv.Atoi(strings.Trim(values[0], " \t\n\r")); err != nil {
|
||||
ErrorLog(`Invalid first range value "` + value + `" (` + err.Error() + ")")
|
||||
return false
|
||||
}
|
||||
if r.Last, err = strconv.Atoi(strings.Trim(values[1], " \t\n\r")); err != nil {
|
||||
ErrorLog(`Invalid last range value "` + value + `" (` + err.Error() + ")")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if r.First, err = strconv.Atoi(value); err != nil {
|
||||
ErrorLog(`Invalid range value "` + value + `" (` + err.Error() + ")")
|
||||
return false
|
||||
}
|
||||
r.Last = r.First
|
||||
return true
|
||||
}
|
||||
|
||||
func (style *viewStyle) init() {
|
||||
style.propertyList.init()
|
||||
//style.shadows = []ViewShadow{}
|
||||
style.transitions = map[string]Animation{}
|
||||
style.normalize = normalizeViewStyleTag
|
||||
}
|
||||
|
||||
// NewViewStyle create new ViewStyle object
|
||||
|
@ -90,19 +49,19 @@ func NewViewStyle(params Params) ViewStyle {
|
|||
return style
|
||||
}
|
||||
|
||||
func (style *viewStyle) cssTextDecoration(session Session) string {
|
||||
func textDecorationCSS(properties Properties, session Session) string {
|
||||
buffer := allocStringBuilder()
|
||||
defer freeStringBuilder(buffer)
|
||||
|
||||
noDecoration := false
|
||||
if strikethrough, ok := boolProperty(style, Strikethrough, session); ok {
|
||||
if strikethrough, ok := boolProperty(properties, Strikethrough, session); ok {
|
||||
if strikethrough {
|
||||
buffer.WriteString("line-through")
|
||||
}
|
||||
noDecoration = true
|
||||
}
|
||||
|
||||
if overline, ok := boolProperty(style, Overline, session); ok {
|
||||
if overline, ok := boolProperty(properties, Overline, session); ok {
|
||||
if overline {
|
||||
if buffer.Len() > 0 {
|
||||
buffer.WriteRune(' ')
|
||||
|
@ -112,7 +71,7 @@ func (style *viewStyle) cssTextDecoration(session Session) string {
|
|||
noDecoration = true
|
||||
}
|
||||
|
||||
if underline, ok := boolProperty(style, Underline, session); ok {
|
||||
if underline, ok := boolProperty(properties, Underline, session); ok {
|
||||
if underline {
|
||||
if buffer.Len() > 0 {
|
||||
buffer.WriteRune(' ')
|
||||
|
@ -149,8 +108,8 @@ func split4Values(text string) []string {
|
|||
return []string{}
|
||||
}
|
||||
|
||||
func (style *viewStyle) backgroundCSS(session Session) string {
|
||||
if value, ok := style.properties[Background]; ok {
|
||||
func backgroundCSS(properties Properties, session Session) string {
|
||||
if value := properties.getRaw(Background); value != nil {
|
||||
if backgrounds, ok := value.([]BackgroundElement); ok {
|
||||
buffer := allocStringBuilder()
|
||||
defer freeStringBuilder(buffer)
|
||||
|
@ -184,15 +143,15 @@ func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) {
|
|||
}
|
||||
}
|
||||
|
||||
if margin, ok := boundsProperty(style, Margin, session); ok {
|
||||
if margin, ok := getBounds(style, Margin, session); ok {
|
||||
margin.cssValue(Margin, builder, session)
|
||||
}
|
||||
|
||||
if padding, ok := boundsProperty(style, Padding, session); ok {
|
||||
if padding, ok := getBounds(style, Padding, session); ok {
|
||||
padding.cssValue(Padding, builder, session)
|
||||
}
|
||||
|
||||
if border := getBorder(style, Border); border != nil {
|
||||
if border := getBorderProperty(style, Border); border != nil {
|
||||
border.cssStyle(builder, session)
|
||||
border.cssWidth(builder, session)
|
||||
border.cssColor(builder, session)
|
||||
|
@ -201,27 +160,27 @@ func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) {
|
|||
radius := getRadius(style, session)
|
||||
radius.cssValue(builder, session)
|
||||
|
||||
if outline := getOutline(style); outline != nil {
|
||||
if outline := getOutlineProperty(style); outline != nil {
|
||||
outline.ViewOutline(session).cssValue(builder, session)
|
||||
}
|
||||
|
||||
for _, tag := range []string{ZIndex, Order} {
|
||||
for _, tag := range []PropertyName{ZIndex, Order} {
|
||||
if value, ok := intProperty(style, tag, session, 0); ok {
|
||||
builder.add(tag, strconv.Itoa(value))
|
||||
builder.add(string(tag), strconv.Itoa(value))
|
||||
}
|
||||
}
|
||||
|
||||
if opacity, ok := floatProperty(style, Opacity, session, 1.0); ok && opacity >= 0 && opacity <= 1 {
|
||||
builder.add(Opacity, strconv.FormatFloat(opacity, 'f', 3, 32))
|
||||
builder.add(string(Opacity), strconv.FormatFloat(opacity, 'f', 3, 32))
|
||||
}
|
||||
|
||||
for _, tag := range []string{ColumnCount, TabSize} {
|
||||
for _, tag := range []PropertyName{ColumnCount, TabSize} {
|
||||
if value, ok := intProperty(style, tag, session, 0); ok && value > 0 {
|
||||
builder.add(tag, strconv.Itoa(value))
|
||||
builder.add(string(tag), strconv.Itoa(value))
|
||||
}
|
||||
}
|
||||
|
||||
for _, tag := range []string{
|
||||
for _, tag := range []PropertyName{
|
||||
Width, Height, MinWidth, MinHeight, MaxWidth, MaxHeight, Left, Right, Top, Bottom,
|
||||
TextSize, TextIndent, LetterSpacing, WordSpacing, LineHeight, TextLineThickness,
|
||||
ListRowGap, ListColumnGap, GridRowGap, GridColumnGap, ColumnGap, ColumnWidth, OutlineOffset} {
|
||||
|
@ -229,18 +188,22 @@ func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) {
|
|||
if size, ok := sizeProperty(style, tag, session); ok && size.Type != Auto {
|
||||
cssTag, ok := sizeProperties[tag]
|
||||
if !ok {
|
||||
cssTag = tag
|
||||
cssTag = string(tag)
|
||||
}
|
||||
builder.add(cssTag, size.cssString("", session))
|
||||
}
|
||||
}
|
||||
|
||||
colorProperties := []struct{ property, cssTag string }{
|
||||
{BackgroundColor, BackgroundColor},
|
||||
type propertyCss struct {
|
||||
property PropertyName
|
||||
cssTag string
|
||||
}
|
||||
colorProperties := []propertyCss{
|
||||
{BackgroundColor, string(BackgroundColor)},
|
||||
{TextColor, "color"},
|
||||
{TextLineColor, "text-decoration-color"},
|
||||
{CaretColor, CaretColor},
|
||||
{AccentColor, AccentColor},
|
||||
{CaretColor, string(CaretColor)},
|
||||
{AccentColor, string(AccentColor)},
|
||||
}
|
||||
for _, p := range colorProperties {
|
||||
if color, ok := colorProperty(style, p.property, session); ok && color != 0 {
|
||||
|
@ -249,10 +212,10 @@ func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) {
|
|||
}
|
||||
|
||||
if value, ok := enumProperty(style, BackgroundClip, session, 0); ok {
|
||||
builder.add(BackgroundClip, enumProperties[BackgroundClip].values[value])
|
||||
builder.add(string(BackgroundClip), enumProperties[BackgroundClip].values[value])
|
||||
}
|
||||
|
||||
if background := style.backgroundCSS(session); background != "" {
|
||||
if background := backgroundCSS(style, session); background != "" {
|
||||
builder.add("background", background)
|
||||
}
|
||||
|
||||
|
@ -261,7 +224,7 @@ func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) {
|
|||
}
|
||||
|
||||
writingMode := 0
|
||||
for _, tag := range []string{
|
||||
for _, tag := range []PropertyName{
|
||||
Overflow, TextAlign, TextTransform, TextWeight, TextLineStyle, WritingMode, TextDirection,
|
||||
VerticalTextOrientation, CellVerticalAlign, CellHorizontalAlign, GridAutoFlow, Cursor,
|
||||
WhiteSpace, WordBreak, TextOverflow, Float, TableVerticalAlign, Resize, MixBlendMode, BackgroundBlendMode} {
|
||||
|
@ -282,7 +245,11 @@ func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) {
|
|||
}
|
||||
}
|
||||
|
||||
for _, prop := range []struct{ tag, cssTag, off, on string }{
|
||||
type boolPropertyCss struct {
|
||||
tag PropertyName
|
||||
cssTag, off, on string
|
||||
}
|
||||
for _, prop := range []boolPropertyCss{
|
||||
{tag: Italic, cssTag: "font-style", off: "normal", on: "italic"},
|
||||
{tag: SmallCaps, cssTag: "font-variant", off: "normal", on: "small-caps"},
|
||||
} {
|
||||
|
@ -295,7 +262,7 @@ func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) {
|
|||
}
|
||||
}
|
||||
|
||||
if text := style.cssTextDecoration(session); text != "" {
|
||||
if text := textDecorationCSS(style, session); text != "" {
|
||||
builder.add("text-decoration", text)
|
||||
}
|
||||
|
||||
|
@ -416,10 +383,10 @@ func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) {
|
|||
if r, ok := rangeProperty(style, Column, session); ok {
|
||||
builder.add("grid-column", fmt.Sprintf("%d / %d", r.First+1, r.Last+2))
|
||||
}
|
||||
if text := style.gridCellSizesCSS(CellWidth, session); text != "" {
|
||||
if text := gridCellSizesCSS(style, CellWidth, session); text != "" {
|
||||
builder.add(`grid-template-columns`, text)
|
||||
}
|
||||
if text := style.gridCellSizesCSS(CellHeight, session); text != "" {
|
||||
if text := gridCellSizesCSS(style, CellHeight, session); text != "" {
|
||||
builder.add(`grid-template-rows`, text)
|
||||
}
|
||||
|
||||
|
@ -436,7 +403,7 @@ func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) {
|
|||
if value := style.getRaw(Filter); value != nil {
|
||||
if filter, ok := value.(ViewFilter); ok {
|
||||
if text := filter.cssStyle(session); text != "" {
|
||||
builder.add(Filter, text)
|
||||
builder.add(string(Filter), text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -445,17 +412,17 @@ func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) {
|
|||
if filter, ok := value.(ViewFilter); ok {
|
||||
if text := filter.cssStyle(session); text != "" {
|
||||
builder.add(`-webkit-backdrop-filter`, text)
|
||||
builder.add(BackdropFilter, text)
|
||||
builder.add(string(BackdropFilter), text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if transition := style.transitionCSS(session); transition != "" {
|
||||
if transition := transitionCSS(style, session); transition != "" {
|
||||
builder.add(`transition`, transition)
|
||||
}
|
||||
|
||||
if animation := style.animationCSS(session); animation != "" {
|
||||
builder.add(AnimationTag, animation)
|
||||
if animation := animationCSS(style, session); animation != "" {
|
||||
builder.add(string(AnimationTag), animation)
|
||||
}
|
||||
|
||||
if pause, ok := boolProperty(style, AnimationPaused, session); ok {
|
||||
|
@ -504,20 +471,48 @@ func valueToOrientation(value any, session Session) (int, bool) {
|
|||
return 0, false
|
||||
}
|
||||
|
||||
func (style *viewStyle) Get(tag string) any {
|
||||
return style.get(strings.ToLower(tag))
|
||||
func normalizeViewStyleTag(tag PropertyName) PropertyName {
|
||||
tag = defaultNormalize(tag)
|
||||
switch tag {
|
||||
case "top-margin":
|
||||
return MarginTop
|
||||
|
||||
case "right-margin":
|
||||
return MarginRight
|
||||
|
||||
case "bottom-margin":
|
||||
return MarginBottom
|
||||
|
||||
case "left-margin":
|
||||
return MarginLeft
|
||||
|
||||
case "top-padding":
|
||||
return PaddingTop
|
||||
|
||||
case "right-padding":
|
||||
return PaddingRight
|
||||
|
||||
case "bottom-padding":
|
||||
return PaddingBottom
|
||||
|
||||
case "left-padding":
|
||||
return PaddingLeft
|
||||
}
|
||||
return tag
|
||||
}
|
||||
|
||||
func (style *viewStyle) get(tag string) any {
|
||||
func (style *viewStyle) Get(tag PropertyName) any {
|
||||
return viewStyleGet(style, normalizeViewStyleTag(tag))
|
||||
}
|
||||
|
||||
func viewStyleGet(style Properties, tag PropertyName) any {
|
||||
switch tag {
|
||||
case Border, CellBorder:
|
||||
return getBorder(&style.propertyList, tag)
|
||||
|
||||
case BorderLeft, BorderRight, BorderTop, BorderBottom,
|
||||
BorderStyle, BorderLeftStyle, BorderRightStyle, BorderTopStyle, BorderBottomStyle,
|
||||
BorderColor, BorderLeftColor, BorderRightColor, BorderTopColor, BorderBottomColor,
|
||||
BorderWidth, BorderLeftWidth, BorderRightWidth, BorderTopWidth, BorderBottomWidth:
|
||||
if border := getBorder(style, Border); border != nil {
|
||||
if border := getBorderProperty(style, Border); border != nil {
|
||||
return border.Get(tag)
|
||||
}
|
||||
return nil
|
||||
|
@ -526,7 +521,7 @@ func (style *viewStyle) get(tag string) any {
|
|||
CellBorderStyle, CellBorderLeftStyle, CellBorderRightStyle, CellBorderTopStyle, CellBorderBottomStyle,
|
||||
CellBorderColor, CellBorderLeftColor, CellBorderRightColor, CellBorderTopColor, CellBorderBottomColor,
|
||||
CellBorderWidth, CellBorderLeftWidth, CellBorderRightWidth, CellBorderTopWidth, CellBorderBottomWidth:
|
||||
if border := getBorder(style, CellBorder); border != nil {
|
||||
if border := getBorderProperty(style, CellBorder); border != nil {
|
||||
return border.Get(tag)
|
||||
}
|
||||
return nil
|
||||
|
@ -537,46 +532,22 @@ func (style *viewStyle) get(tag string) any {
|
|||
RadiusBottomRight, RadiusBottomRightX, RadiusBottomRightY:
|
||||
return getRadiusElement(style, tag)
|
||||
|
||||
case ColumnSeparator:
|
||||
if val, ok := style.properties[ColumnSeparator]; ok {
|
||||
return val.(ColumnSeparatorProperty)
|
||||
}
|
||||
return nil
|
||||
|
||||
case ColumnSeparatorStyle, ColumnSeparatorWidth, ColumnSeparatorColor:
|
||||
if val, ok := style.properties[ColumnSeparator]; ok {
|
||||
if val := style.getRaw(ColumnSeparator); val != nil {
|
||||
separator := val.(ColumnSeparatorProperty)
|
||||
return separator.Get(tag)
|
||||
}
|
||||
return nil
|
||||
|
||||
case Transition:
|
||||
if len(style.transitions) == 0 {
|
||||
return nil
|
||||
}
|
||||
result := map[string]Animation{}
|
||||
for tag, animation := range style.transitions {
|
||||
result[tag] = animation
|
||||
}
|
||||
return result
|
||||
|
||||
case RotateX, RotateY, RotateZ, Rotate, SkewX, SkewY, ScaleX, ScaleY, ScaleZ,
|
||||
TranslateX, TranslateY, TranslateZ:
|
||||
if transform := style.transformProperty(); transform != nil {
|
||||
if transform := getTransformProperty(style); transform != nil {
|
||||
return transform.Get(tag)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return style.propertyList.getRaw(tag)
|
||||
}
|
||||
|
||||
func (style *viewStyle) AllTags() []string {
|
||||
result := style.propertyList.AllTags()
|
||||
if len(style.transitions) > 0 {
|
||||
result = append(result, Transition)
|
||||
}
|
||||
return result
|
||||
return style.getRaw(tag)
|
||||
}
|
||||
|
||||
func supportedPropertyValue(value any) bool {
|
||||
|
@ -595,14 +566,14 @@ func supportedPropertyValue(value any) bool {
|
|||
case []BackgroundElement:
|
||||
case []BackgroundGradientPoint:
|
||||
case []BackgroundGradientAngle:
|
||||
case map[string]Animation:
|
||||
case map[PropertyName]Animation:
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func writePropertyValue(buffer *strings.Builder, tag string, value any, indent string) {
|
||||
func writePropertyValue(buffer *strings.Builder, tag PropertyName, value any, indent string) {
|
||||
|
||||
writeString := func(text string) {
|
||||
simple := (tag != Text && tag != Title && tag != Summary)
|
||||
|
@ -804,7 +775,7 @@ func writePropertyValue(buffer *strings.Builder, tag string, value any, indent s
|
|||
}
|
||||
buffer.WriteRune('"')
|
||||
|
||||
case map[string]Animation:
|
||||
case map[PropertyName]Animation:
|
||||
switch count := len(value); count {
|
||||
case 0:
|
||||
buffer.WriteString("[]")
|
||||
|
@ -816,11 +787,13 @@ func writePropertyValue(buffer *strings.Builder, tag string, value any, indent s
|
|||
}
|
||||
|
||||
default:
|
||||
tags := make([]string, 0, len(value))
|
||||
tags := make([]PropertyName, 0, len(value))
|
||||
for tag := range value {
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
sort.Strings(tags)
|
||||
sort.Slice(tags, func(i, j int) bool {
|
||||
return tags[i] < tags[j]
|
||||
})
|
||||
buffer.WriteString("[\n")
|
||||
indent2 := indent + "\t"
|
||||
for _, tag := range tags {
|
||||
|
@ -836,12 +809,12 @@ func writePropertyValue(buffer *strings.Builder, tag string, value any, indent s
|
|||
}
|
||||
}
|
||||
|
||||
func writeViewStyle(name string, view ViewStyle, buffer *strings.Builder, indent string, excludeTags []string) {
|
||||
func writeViewStyle(name string, view Properties, buffer *strings.Builder, indent string, excludeTags []PropertyName) {
|
||||
buffer.WriteString(name)
|
||||
buffer.WriteString(" {\n")
|
||||
indent += "\t"
|
||||
|
||||
writeProperty := func(tag string, value any) {
|
||||
writeProperty := func(tag PropertyName, value any) {
|
||||
for _, exclude := range excludeTags {
|
||||
if exclude == tag {
|
||||
return
|
||||
|
@ -850,7 +823,7 @@ func writeViewStyle(name string, view ViewStyle, buffer *strings.Builder, indent
|
|||
|
||||
if supportedPropertyValue(value) {
|
||||
buffer.WriteString(indent)
|
||||
buffer.WriteString(tag)
|
||||
buffer.WriteString(string(tag))
|
||||
buffer.WriteString(" = ")
|
||||
writePropertyValue(buffer, tag, value, indent)
|
||||
buffer.WriteString(",\n")
|
||||
|
@ -858,7 +831,7 @@ func writeViewStyle(name string, view ViewStyle, buffer *strings.Builder, indent
|
|||
}
|
||||
|
||||
tags := view.AllTags()
|
||||
removeTag := func(tag string) {
|
||||
removeTag := func(tag PropertyName) {
|
||||
for i, t := range tags {
|
||||
if t == tag {
|
||||
if i == 0 {
|
||||
|
@ -873,7 +846,7 @@ func writeViewStyle(name string, view ViewStyle, buffer *strings.Builder, indent
|
|||
}
|
||||
}
|
||||
|
||||
tagOrder := []string{
|
||||
tagOrder := []PropertyName{
|
||||
ID, Row, Column, Top, Right, Bottom, Left, Semantics, Cursor, Visibility,
|
||||
Opacity, ZIndex, Width, Height, MinWidth, MinHeight, MaxWidth, MaxHeight,
|
||||
Margin, Padding, BackgroundClip, BackgroundColor, Background, Border, Radius, Outline, Shadow,
|
||||
|
@ -894,7 +867,7 @@ func writeViewStyle(name string, view ViewStyle, buffer *strings.Builder, indent
|
|||
}
|
||||
}
|
||||
|
||||
finalTags := []string{
|
||||
finalTags := []PropertyName{
|
||||
Perspective, PerspectiveOriginX, PerspectiveOriginY, BackfaceVisible, OriginX, OriginY, OriginZ,
|
||||
TransformTag, Clip, Filter, BackdropFilter, Summary, Content, Transition}
|
||||
for _, tag := range finalTags {
|
||||
|
@ -918,14 +891,6 @@ func writeViewStyle(name string, view ViewStyle, buffer *strings.Builder, indent
|
|||
buffer.WriteString("}")
|
||||
}
|
||||
|
||||
func getViewString(view View, excludeTags []string) string {
|
||||
buffer := allocStringBuilder()
|
||||
defer freeStringBuilder(buffer)
|
||||
writeViewStyle(view.Tag(), view, buffer, "", excludeTags)
|
||||
return buffer.String()
|
||||
|
||||
}
|
||||
|
||||
func runStringWriter(writer stringWriter) string {
|
||||
buffer := allocStringBuilder()
|
||||
defer freeStringBuilder(buffer)
|
||||
|
|
511
viewStyleSet.go
511
viewStyleSet.go
|
@ -4,93 +4,19 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
func (style *viewStyle) setRange(tag string, value any) bool {
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
if strings.Contains(value, "@") {
|
||||
style.properties[tag] = value
|
||||
return true
|
||||
}
|
||||
var r Range
|
||||
if !r.setValue(value) {
|
||||
invalidPropertyValue(tag, value)
|
||||
return false
|
||||
}
|
||||
style.properties[tag] = r
|
||||
func setTransitionProperty(properties Properties, value any) bool {
|
||||
|
||||
case int:
|
||||
style.properties[tag] = Range{First: value, Last: value}
|
||||
transitions := map[PropertyName]Animation{}
|
||||
|
||||
case Range:
|
||||
style.properties[tag] = value
|
||||
|
||||
default:
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (style *viewStyle) setBackground(value any) bool {
|
||||
background := []BackgroundElement{}
|
||||
|
||||
switch value := value.(type) {
|
||||
case BackgroundElement:
|
||||
background = []BackgroundElement{value}
|
||||
|
||||
case []BackgroundElement:
|
||||
background = value
|
||||
|
||||
case []DataValue:
|
||||
for _, el := range value {
|
||||
if el.IsObject() {
|
||||
if element := createBackground(el.Object()); element != nil {
|
||||
background = append(background, element)
|
||||
}
|
||||
} else if obj := ParseDataText(el.Value()); obj != nil {
|
||||
if element := createBackground(obj); element != nil {
|
||||
background = append(background, element)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case DataObject:
|
||||
if element := createBackground(value); element != nil {
|
||||
background = []BackgroundElement{element}
|
||||
}
|
||||
|
||||
case []DataObject:
|
||||
for _, obj := range value {
|
||||
if element := createBackground(obj); element != nil {
|
||||
background = append(background, element)
|
||||
}
|
||||
}
|
||||
|
||||
case string:
|
||||
if obj := ParseDataText(value); obj != nil {
|
||||
if element := createBackground(obj); element != nil {
|
||||
background = []BackgroundElement{element}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(background) > 0 {
|
||||
style.properties[Background] = background
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (style *viewStyle) setTransition(tag string, value any) bool {
|
||||
setObject := func(obj DataObject) bool {
|
||||
if obj != nil {
|
||||
tag := strings.ToLower(tag)
|
||||
tag := strings.ToLower(obj.Tag())
|
||||
switch tag {
|
||||
case "", "_":
|
||||
ErrorLog("Invalid transition property name")
|
||||
|
||||
default:
|
||||
style.transitions[tag] = parseAnimation(obj)
|
||||
transitions[PropertyName(tag)] = parseAnimation(obj)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -99,111 +25,140 @@ func (style *viewStyle) setTransition(tag string, value any) bool {
|
|||
|
||||
switch value := value.(type) {
|
||||
case Params:
|
||||
result := true
|
||||
for tag, val := range value {
|
||||
tag = strings.ToLower(strings.Trim(tag, " \t"))
|
||||
tag = defaultNormalize(tag)
|
||||
if tag == "" {
|
||||
ErrorLog("Invalid transition property name")
|
||||
result = false
|
||||
} else if val == nil {
|
||||
delete(style.transitions, tag)
|
||||
} else if animation, ok := val.(Animation); ok {
|
||||
style.transitions[tag] = animation
|
||||
} else {
|
||||
notCompatibleType(Transition, val)
|
||||
result = false
|
||||
return false
|
||||
}
|
||||
|
||||
if val != nil {
|
||||
if animation, ok := val.(Animation); ok {
|
||||
transitions[PropertyName(tag)] = animation
|
||||
} else {
|
||||
notCompatibleType(Transition, val)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
if len(transitions) == 0 {
|
||||
transitions = nil
|
||||
}
|
||||
properties.setRaw(Transition, transitions)
|
||||
return true
|
||||
|
||||
case DataObject:
|
||||
return setObject(value)
|
||||
if setObject(value) {
|
||||
properties.setRaw(Transition, transitions)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
case DataNode:
|
||||
switch value.Type() {
|
||||
case ObjectNode:
|
||||
return setObject(value.Object())
|
||||
if setObject(value.Object()) {
|
||||
properties.setRaw(Transition, transitions)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
case ArrayNode:
|
||||
result := true
|
||||
for i := 0; i < value.ArraySize(); i++ {
|
||||
if obj := value.ArrayElement(i).Object(); obj != nil {
|
||||
result = setObject(obj) && result
|
||||
if !setObject(obj) {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
notCompatibleType(tag, value.ArrayElement(i))
|
||||
notCompatibleType(Transition, value.ArrayElement(i))
|
||||
return false
|
||||
}
|
||||
}
|
||||
if len(transitions) == 0 {
|
||||
transitions = nil
|
||||
}
|
||||
properties.setRaw(Transition, transitions)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
notCompatibleType(Transition, value)
|
||||
return false
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
func (style *viewStyle) setTransition(tag PropertyName, value any) bool {
|
||||
setObject := func(obj DataObject) bool {
|
||||
if obj != nil {
|
||||
tag := defaultNormalize(tag)
|
||||
switch tag {
|
||||
case "", "_":
|
||||
ErrorLog("Invalid transition property name")
|
||||
|
||||
default:
|
||||
style.transitions[tag] = parseAnimation(obj)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case Params:
|
||||
result := true
|
||||
for tag, val := range value {
|
||||
tag = defaultNormalize(tag)
|
||||
if tag == "" {
|
||||
ErrorLog("Invalid transition property name")
|
||||
result = false
|
||||
} else if val == nil {
|
||||
delete(style.transitions, tag)
|
||||
} else if animation, ok := val.(Animation); ok {
|
||||
style.transitions[tag] = animation
|
||||
} else {
|
||||
notCompatibleType(Transition, val)
|
||||
result = false
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
case DataObject:
|
||||
return setObject(value)
|
||||
|
||||
func (style *viewStyle) setAnimation(tag string, value any) bool {
|
||||
case DataNode:
|
||||
switch value.Type() {
|
||||
case ObjectNode:
|
||||
return setObject(value.Object())
|
||||
|
||||
set := func(animations []Animation) {
|
||||
style.properties[tag] = animations
|
||||
for _, animation := range animations {
|
||||
animation.used()
|
||||
}
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case Animation:
|
||||
set([]Animation{value})
|
||||
return true
|
||||
|
||||
case []Animation:
|
||||
set(value)
|
||||
return true
|
||||
|
||||
case DataObject:
|
||||
if animation := parseAnimation(value); animation.hasAnimatedProperty() {
|
||||
set([]Animation{animation})
|
||||
return true
|
||||
}
|
||||
|
||||
case DataNode:
|
||||
animations := []Animation{}
|
||||
result := true
|
||||
for i := 0; i < value.ArraySize(); i++ {
|
||||
if obj := value.ArrayElement(i).Object(); obj != nil {
|
||||
if anim := parseAnimation(obj); anim.hasAnimatedProperty() {
|
||||
animations = append(animations, anim)
|
||||
} else {
|
||||
result = false
|
||||
case ArrayNode:
|
||||
result := true
|
||||
for i := 0; i < value.ArraySize(); i++ {
|
||||
if obj := value.ArrayElement(i).Object(); obj != nil {
|
||||
result = setObject(obj) && result
|
||||
} else {
|
||||
notCompatibleType(tag, value.ArrayElement(i))
|
||||
result = false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
notCompatibleType(tag, value.ArrayElement(i))
|
||||
result = false
|
||||
return result
|
||||
}
|
||||
}
|
||||
if result && len(animations) > 0 {
|
||||
set(animations)
|
||||
}
|
||||
return result
|
||||
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
*/
|
||||
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
|
||||
func (style *viewStyle) Remove(tag string) {
|
||||
style.remove(strings.ToLower(tag))
|
||||
}
|
||||
|
||||
func (style *viewStyle) remove(tag string) {
|
||||
func viewStyleRemove(properties Properties, tag PropertyName) []PropertyName {
|
||||
switch tag {
|
||||
case BorderStyle, BorderColor, BorderWidth,
|
||||
BorderLeft, BorderLeftStyle, BorderLeftColor, BorderLeftWidth,
|
||||
BorderRight, BorderRightStyle, BorderRightColor, BorderRightWidth,
|
||||
BorderTop, BorderTopStyle, BorderTopColor, BorderTopWidth,
|
||||
BorderBottom, BorderBottomStyle, BorderBottomColor, BorderBottomWidth:
|
||||
if border := getBorder(style, Border); border != nil {
|
||||
border.delete(tag)
|
||||
if border := getBorderProperty(properties, Border); border != nil && border.deleteTag(tag) {
|
||||
return []PropertyName{Border}
|
||||
}
|
||||
|
||||
case CellBorderStyle, CellBorderColor, CellBorderWidth,
|
||||
|
@ -211,58 +166,59 @@ func (style *viewStyle) remove(tag string) {
|
|||
CellBorderRight, CellBorderRightStyle, CellBorderRightColor, CellBorderRightWidth,
|
||||
CellBorderTop, CellBorderTopStyle, CellBorderTopColor, CellBorderTopWidth,
|
||||
CellBorderBottom, CellBorderBottomStyle, CellBorderBottomColor, CellBorderBottomWidth:
|
||||
if border := getBorder(style, CellBorder); border != nil {
|
||||
border.delete(tag)
|
||||
if border := getBorderProperty(properties, CellBorder); border != nil && border.deleteTag(tag) {
|
||||
return []PropertyName{CellBorder}
|
||||
}
|
||||
|
||||
case MarginTop, MarginRight, MarginBottom, MarginLeft,
|
||||
"top-margin", "right-margin", "bottom-margin", "left-margin":
|
||||
style.removeBoundsSide(Margin, tag)
|
||||
case MarginTop, MarginRight, MarginBottom, MarginLeft:
|
||||
return removeBoundsPropertySide(properties, Margin, tag)
|
||||
|
||||
case PaddingTop, PaddingRight, PaddingBottom, PaddingLeft,
|
||||
"top-padding", "right-padding", "bottom-padding", "left-padding":
|
||||
style.removeBoundsSide(Padding, tag)
|
||||
case PaddingTop, PaddingRight, PaddingBottom, PaddingLeft:
|
||||
return removeBoundsPropertySide(properties, Padding, tag)
|
||||
|
||||
case CellPaddingTop, CellPaddingRight, CellPaddingBottom, CellPaddingLeft:
|
||||
style.removeBoundsSide(CellPadding, tag)
|
||||
return removeBoundsPropertySide(properties, CellPadding, tag)
|
||||
|
||||
case RadiusX, RadiusY, RadiusTopLeft, RadiusTopLeftX, RadiusTopLeftY,
|
||||
RadiusTopRight, RadiusTopRightX, RadiusTopRightY,
|
||||
RadiusBottomLeft, RadiusBottomLeftX, RadiusBottomLeftY,
|
||||
RadiusBottomRight, RadiusBottomRightX, RadiusBottomRightY:
|
||||
style.removeRadiusElement(tag)
|
||||
if removeRadiusPropertyElement(properties, tag) {
|
||||
return []PropertyName{Radius, tag}
|
||||
}
|
||||
|
||||
case OutlineStyle, OutlineWidth, OutlineColor:
|
||||
if outline := getOutline(style); outline != nil {
|
||||
if outline := getOutlineProperty(properties); outline != nil {
|
||||
outline.Remove(tag)
|
||||
if outline.empty() {
|
||||
properties.setRaw(Outline, nil)
|
||||
}
|
||||
return []PropertyName{Outline, tag}
|
||||
}
|
||||
|
||||
default:
|
||||
style.propertyList.remove(tag)
|
||||
}
|
||||
}
|
||||
|
||||
func (style *viewStyle) Set(tag string, value any) bool {
|
||||
return style.set(strings.ToLower(tag), value)
|
||||
}
|
||||
|
||||
func (style *viewStyle) set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
style.remove(tag)
|
||||
return true
|
||||
return propertiesRemove(properties, tag)
|
||||
}
|
||||
|
||||
return []PropertyName{}
|
||||
}
|
||||
|
||||
func viewStyleSet(style Properties, tag PropertyName, value any) []PropertyName {
|
||||
switch tag {
|
||||
case Shadow, TextShadow:
|
||||
return style.setShadow(tag, value)
|
||||
if setShadowProperty(style, tag, value) {
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
case Background:
|
||||
return style.setBackground(value)
|
||||
return setBackgroundProperty(style, value)
|
||||
|
||||
case Border, CellBorder:
|
||||
if border := newBorderProperty(value); border != nil {
|
||||
style.properties[tag] = border
|
||||
return true
|
||||
style.setRaw(tag, border)
|
||||
return []PropertyName{tag}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
case BorderStyle, BorderColor, BorderWidth,
|
||||
|
@ -271,16 +227,7 @@ func (style *viewStyle) set(tag string, value any) bool {
|
|||
BorderTop, BorderTopStyle, BorderTopColor, BorderTopWidth,
|
||||
BorderBottom, BorderBottomStyle, BorderBottomColor, BorderBottomWidth:
|
||||
|
||||
border := getBorder(style, Border)
|
||||
if border == nil {
|
||||
border = NewBorder(nil)
|
||||
if border.Set(tag, value) {
|
||||
style.properties[Border] = border
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
return border.Set(tag, value)
|
||||
return setBorderPropertyElement(style, Border, tag, value)
|
||||
|
||||
case CellBorderStyle, CellBorderColor, CellBorderWidth,
|
||||
CellBorderLeft, CellBorderLeftStyle, CellBorderLeftColor, CellBorderLeftWidth,
|
||||
|
@ -288,173 +235,211 @@ func (style *viewStyle) set(tag string, value any) bool {
|
|||
CellBorderTop, CellBorderTopStyle, CellBorderTopColor, CellBorderTopWidth,
|
||||
CellBorderBottom, CellBorderBottomStyle, CellBorderBottomColor, CellBorderBottomWidth:
|
||||
|
||||
border := getBorder(style, CellBorder)
|
||||
if border == nil {
|
||||
border = NewBorder(nil)
|
||||
if border.Set(tag, value) {
|
||||
style.properties[CellBorder] = border
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
return border.Set(tag, value)
|
||||
return setBorderPropertyElement(style, CellBorder, tag, value)
|
||||
|
||||
case Radius:
|
||||
return style.setRadius(value)
|
||||
return setRadiusProperty(style, value)
|
||||
|
||||
case RadiusX, RadiusY, RadiusTopLeft, RadiusTopLeftX, RadiusTopLeftY,
|
||||
RadiusTopRight, RadiusTopRightX, RadiusTopRightY,
|
||||
RadiusBottomLeft, RadiusBottomLeftX, RadiusBottomLeftY,
|
||||
RadiusBottomRight, RadiusBottomRightX, RadiusBottomRightY:
|
||||
return style.setRadiusElement(tag, value)
|
||||
if setRadiusPropertyElement(style, tag, value) {
|
||||
return []PropertyName{Radius, tag}
|
||||
}
|
||||
|
||||
case Margin, Padding, CellPadding:
|
||||
return style.setBounds(tag, value)
|
||||
return setBoundsProperty(style, tag, value)
|
||||
|
||||
case MarginTop, MarginRight, MarginBottom, MarginLeft,
|
||||
"top-margin", "right-margin", "bottom-margin", "left-margin":
|
||||
return style.setBoundsSide(Margin, tag, value)
|
||||
case MarginTop, MarginRight, MarginBottom, MarginLeft:
|
||||
return setBoundsPropertySide(style, Margin, tag, value)
|
||||
|
||||
case PaddingTop, PaddingRight, PaddingBottom, PaddingLeft,
|
||||
"top-padding", "right-padding", "bottom-padding", "left-padding":
|
||||
return style.setBoundsSide(Padding, tag, value)
|
||||
case PaddingTop, PaddingRight, PaddingBottom, PaddingLeft:
|
||||
return setBoundsPropertySide(style, Padding, tag, value)
|
||||
|
||||
case CellPaddingTop, CellPaddingRight, CellPaddingBottom, CellPaddingLeft:
|
||||
return style.setBoundsSide(CellPadding, tag, value)
|
||||
return setBoundsPropertySide(style, CellPadding, tag, value)
|
||||
|
||||
case HeadStyle, FootStyle:
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
style.properties[tag] = value
|
||||
return true
|
||||
style.setRaw(tag, value)
|
||||
|
||||
case Params:
|
||||
style.properties[tag] = value
|
||||
return true
|
||||
style.setRaw(tag, value)
|
||||
|
||||
case DataObject:
|
||||
if params := value.ToParams(); len(params) > 0 {
|
||||
style.properties[tag] = params
|
||||
style.setRaw(tag, params)
|
||||
} else {
|
||||
style.setRaw(tag, nil)
|
||||
}
|
||||
return true
|
||||
|
||||
default:
|
||||
notCompatibleType(tag, value)
|
||||
return nil
|
||||
}
|
||||
return []PropertyName{tag}
|
||||
|
||||
case CellStyle, ColumnStyle, RowStyle:
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
style.properties[tag] = value
|
||||
return true
|
||||
style.setRaw(tag, value)
|
||||
|
||||
case Params:
|
||||
style.properties[tag] = value
|
||||
return true
|
||||
style.setRaw(tag, value)
|
||||
|
||||
case DataObject:
|
||||
if params := value.ToParams(); len(params) > 0 {
|
||||
style.properties[tag] = params
|
||||
style.setRaw(tag, params)
|
||||
} else {
|
||||
style.setRaw(tag, nil)
|
||||
}
|
||||
return true
|
||||
|
||||
case DataNode:
|
||||
switch value.Type() {
|
||||
case TextNode:
|
||||
if text := value.Text(); text != "" {
|
||||
style.properties[tag] = text
|
||||
style.setRaw(tag, text)
|
||||
} else {
|
||||
style.setRaw(tag, nil)
|
||||
}
|
||||
return true
|
||||
|
||||
case ObjectNode:
|
||||
if obj := value.Object(); obj != nil {
|
||||
if params := obj.ToParams(); len(params) > 0 {
|
||||
style.properties[tag] = params
|
||||
style.setRaw(tag, params)
|
||||
} else {
|
||||
style.setRaw(tag, nil)
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
notCompatibleType(tag, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
case ArrayNode:
|
||||
// TODO
|
||||
default:
|
||||
notCompatibleType(tag, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
default:
|
||||
notCompatibleType(tag, value)
|
||||
return nil
|
||||
}
|
||||
return []PropertyName{tag}
|
||||
|
||||
case Outline:
|
||||
return style.setOutline(value)
|
||||
return setOutlineProperty(style, value)
|
||||
|
||||
case OutlineStyle, OutlineWidth, OutlineColor:
|
||||
if outline := getOutline(style); outline != nil {
|
||||
return outline.Set(tag, value)
|
||||
if outline := getOutlineProperty(style); outline != nil {
|
||||
if outline.Set(tag, value) {
|
||||
return []PropertyName{Outline, tag}
|
||||
}
|
||||
} else {
|
||||
outline := NewOutlineProperty(nil)
|
||||
if outline.Set(tag, value) {
|
||||
style.setRaw(Outline, outline)
|
||||
return []PropertyName{Outline, tag}
|
||||
}
|
||||
}
|
||||
style.properties[Outline] = NewOutlineProperty(Params{tag: value})
|
||||
return true
|
||||
return nil
|
||||
|
||||
case TransformTag:
|
||||
return style.setTransform(value)
|
||||
if setTransformProperty(style, value) {
|
||||
return []PropertyName{TransformTag}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
case RotateX, RotateY, RotateZ, Rotate, SkewX, SkewY, ScaleX, ScaleY, ScaleZ,
|
||||
case Perspective, RotateX, RotateY, RotateZ, Rotate, SkewX, SkewY, ScaleX, ScaleY, ScaleZ,
|
||||
TranslateX, TranslateY, TranslateZ:
|
||||
return style.setTransformProperty(tag, value)
|
||||
return setTransformPropertyElement(style, tag, value)
|
||||
|
||||
case Orientation:
|
||||
if text, ok := value.(string); ok {
|
||||
switch strings.ToLower(text) {
|
||||
case "vertical":
|
||||
style.properties[Orientation] = TopDownOrientation
|
||||
return true
|
||||
style.setRaw(Orientation, TopDownOrientation)
|
||||
return []PropertyName{Orientation}
|
||||
|
||||
case "horizontal":
|
||||
style.properties[Orientation] = StartToEndOrientation
|
||||
return true
|
||||
style.setRaw(Orientation, StartToEndOrientation)
|
||||
return []PropertyName{Orientation}
|
||||
}
|
||||
}
|
||||
|
||||
case TextWeight:
|
||||
if n, ok := value.(int); ok && n >= 100 && n%100 == 0 {
|
||||
n /= 100
|
||||
if n > 0 && n <= 9 {
|
||||
style.properties[TextWeight] = n
|
||||
return true
|
||||
if n, ok := value.(int); ok {
|
||||
if n >= 100 && n%100 == 0 {
|
||||
n /= 100
|
||||
if n > 0 && n <= 9 {
|
||||
style.setRaw(TextWeight, n)
|
||||
return []PropertyName{TextWeight}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case Row, Column:
|
||||
return style.setRange(tag, value)
|
||||
return setRangeProperty(style, tag, value)
|
||||
|
||||
case CellWidth, CellHeight:
|
||||
return style.setGridCellSize(tag, value)
|
||||
return setGridCellSize(style, tag, value)
|
||||
|
||||
case ColumnSeparator:
|
||||
if separator := newColumnSeparatorProperty(value); separator != nil {
|
||||
style.properties[ColumnSeparator] = separator
|
||||
return true
|
||||
style.setRaw(ColumnSeparator, separator)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
return false
|
||||
return nil
|
||||
|
||||
case ColumnSeparatorStyle, ColumnSeparatorWidth, ColumnSeparatorColor:
|
||||
var separator ColumnSeparatorProperty = nil
|
||||
if val, ok := style.properties[ColumnSeparator]; ok {
|
||||
separator = val.(ColumnSeparatorProperty)
|
||||
if separator := getColumnSeparatorProperty(style); separator != nil {
|
||||
if separator.Set(tag, value) {
|
||||
return []PropertyName{ColumnSeparator, tag}
|
||||
}
|
||||
} else {
|
||||
separator := newColumnSeparatorProperty(nil)
|
||||
if separator.Set(tag, value) {
|
||||
style.setRaw(ColumnSeparator, separator)
|
||||
return []PropertyName{ColumnSeparator, tag}
|
||||
}
|
||||
}
|
||||
if separator == nil {
|
||||
separator = newColumnSeparatorProperty(nil)
|
||||
}
|
||||
|
||||
if separator.Set(tag, value) {
|
||||
style.properties[ColumnSeparator] = separator
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return nil
|
||||
|
||||
case Clip, ShapeOutside:
|
||||
return style.setClipShape(tag, value)
|
||||
return setClipShapeProperty(style, tag, value)
|
||||
|
||||
case Filter, BackdropFilter:
|
||||
return style.setFilter(tag, value)
|
||||
return setFilterProperty(style, tag, value)
|
||||
|
||||
case Transition:
|
||||
return style.setTransition(tag, value)
|
||||
if setTransitionProperty(style, value) {
|
||||
return []PropertyName{tag}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
case AnimationTag:
|
||||
return style.setAnimation(tag, value)
|
||||
if setAnimationProperty(style, tag, value) {
|
||||
return []PropertyName{tag}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return style.propertyList.set(tag, value)
|
||||
return propertiesSet(style, tag, value)
|
||||
}
|
||||
|
||||
func (style *viewStyle) Set(tag PropertyName, value any) bool {
|
||||
if value == nil {
|
||||
style.Remove(tag)
|
||||
return true
|
||||
}
|
||||
|
||||
return viewStyleSet(style, normalizeViewStyleTag(tag), value) != nil
|
||||
}
|
||||
|
||||
func (style *viewStyle) Remove(tag PropertyName) {
|
||||
viewStyleRemove(style, normalizeViewStyleTag(tag))
|
||||
}
|
||||
|
|
556
viewTransform.go
556
viewTransform.go
|
@ -8,87 +8,6 @@ import (
|
|||
|
||||
// Constants for [Transform] specific properties
|
||||
const (
|
||||
// Perspective is the constant for "perspective" property tag.
|
||||
//
|
||||
// Used by `View`.
|
||||
// Distance between the z-plane and the user in order to give a 3D-positioned element some perspective. Each 3D element
|
||||
// with z > 0 becomes larger, each 3D-element with z < 0 becomes smaller. The default value is 0 (no 3D effects).
|
||||
//
|
||||
// Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`.
|
||||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
Perspective = "perspective"
|
||||
|
||||
// PerspectiveOriginX is the constant for "perspective-origin-x" property tag.
|
||||
//
|
||||
// Used by `View`.
|
||||
// x-coordinate of the position at which the viewer is looking. It is used as the vanishing point by the "perspective"
|
||||
// property. The default value is 50%.
|
||||
//
|
||||
// Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`.
|
||||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
PerspectiveOriginX = "perspective-origin-x"
|
||||
|
||||
// PerspectiveOriginY is the constant for "perspective-origin-y" property tag.
|
||||
//
|
||||
// Used by `View`.
|
||||
// y-coordinate of the position at which the viewer is looking. It is used as the vanishing point by the "perspective"
|
||||
// property. The default value is 50%.
|
||||
//
|
||||
// Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`.
|
||||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
PerspectiveOriginY = "perspective-origin-y"
|
||||
|
||||
// BackfaceVisible is the constant for "backface-visibility" property tag.
|
||||
//
|
||||
// Used by `View`.
|
||||
// Controls whether the back face of a view is visible when turned towards the user. Default value is `true`.
|
||||
//
|
||||
// Supported types: `bool`, `int`, `string`.
|
||||
//
|
||||
// Values:
|
||||
// `true` or `1` or "true", "yes", "on", "1" - Back face is visible when turned towards the user.
|
||||
// `false` or `0` or "false", "no", "off", "0" - Back face is hidden, effectively making the view invisible when turned away from the user.
|
||||
BackfaceVisible = "backface-visibility"
|
||||
|
||||
// OriginX is the constant for "origin-x" property tag.
|
||||
//
|
||||
// Used by `View`.
|
||||
// x-coordinate of the point around which a view transformation is applied. The default value is 50%.
|
||||
//
|
||||
// Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`.
|
||||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
OriginX = "origin-x"
|
||||
|
||||
// OriginY is the constant for "origin-y" property tag.
|
||||
//
|
||||
// Used by `View`.
|
||||
// y-coordinate of the point around which a view transformation is applied. The default value is 50%.
|
||||
//
|
||||
// Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`.
|
||||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
OriginY = "origin-y"
|
||||
|
||||
// OriginZ is the constant for "origin-z" property tag.
|
||||
//
|
||||
// Used by `View`.
|
||||
// z-coordinate of the point around which a view transformation is applied. The default value is 50%.
|
||||
//
|
||||
// Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`.
|
||||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
OriginZ = "origin-z"
|
||||
|
||||
// TransformTag is the constant for "transform" property tag.
|
||||
//
|
||||
// Used by `View`.
|
||||
|
@ -101,7 +20,88 @@ const (
|
|||
// Conversion rules:
|
||||
// `Transform` - stored as is, no conversion performed.
|
||||
// `string` - string representation of `Transform` interface. Example: "_{translate-x = 10px, scale-y = 1.1}".
|
||||
TransformTag = "transform"
|
||||
TransformTag PropertyName = "transform"
|
||||
|
||||
// Perspective is the constant for "perspective" property tag.
|
||||
//
|
||||
// Used by `View`.
|
||||
// Distance between the z-plane and the user in order to give a 3D-positioned element some perspective. Each 3D element
|
||||
// with z > 0 becomes larger, each 3D-element with z < 0 becomes smaller. The default value is 0 (no 3D effects).
|
||||
//
|
||||
// Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`.
|
||||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
Perspective PropertyName = "perspective"
|
||||
|
||||
// PerspectiveOriginX is the constant for "perspective-origin-x" property tag.
|
||||
//
|
||||
// Used by `View`.
|
||||
// x-coordinate of the position at which the viewer is looking. It is used as the vanishing point by the "perspective"
|
||||
// property. The default value is 50%.
|
||||
//
|
||||
// Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`.
|
||||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
PerspectiveOriginX PropertyName = "perspective-origin-x"
|
||||
|
||||
// PerspectiveOriginY is the constant for "perspective-origin-y" property tag.
|
||||
//
|
||||
// Used by `View`.
|
||||
// y-coordinate of the position at which the viewer is looking. It is used as the vanishing point by the "perspective"
|
||||
// property. The default value is 50%.
|
||||
//
|
||||
// Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`.
|
||||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
PerspectiveOriginY PropertyName = "perspective-origin-y"
|
||||
|
||||
// BackfaceVisible is the constant for "backface-visibility" property tag.
|
||||
//
|
||||
// Used by `View`.
|
||||
// Controls whether the back face of a view is visible when turned towards the user. Default value is `true`.
|
||||
//
|
||||
// Supported types: `bool`, `int`, `string`.
|
||||
//
|
||||
// Values:
|
||||
// `true` or `1` or "true", "yes", "on", "1" - Back face is visible when turned towards the user.
|
||||
// `false` or `0` or "false", "no", "off", "0" - Back face is hidden, effectively making the view invisible when turned away from the user.
|
||||
BackfaceVisible PropertyName = "backface-visibility"
|
||||
|
||||
// OriginX is the constant for "origin-x" property tag.
|
||||
//
|
||||
// Used by `View`.
|
||||
// x-coordinate of the point around which a view transformation is applied. The default value is 50%.
|
||||
//
|
||||
// Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`.
|
||||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
OriginX PropertyName = "origin-x"
|
||||
|
||||
// OriginY is the constant for "origin-y" property tag.
|
||||
//
|
||||
// Used by `View`.
|
||||
// y-coordinate of the point around which a view transformation is applied. The default value is 50%.
|
||||
//
|
||||
// Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`.
|
||||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
OriginY PropertyName = "origin-y"
|
||||
|
||||
// OriginZ is the constant for "origin-z" property tag.
|
||||
//
|
||||
// Used by `View`.
|
||||
// z-coordinate of the point around which a view transformation is applied. The default value is 50%.
|
||||
//
|
||||
// Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`.
|
||||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
OriginZ PropertyName = "origin-z"
|
||||
|
||||
// TranslateX is the constant for "translate-x" property tag.
|
||||
//
|
||||
|
@ -122,7 +122,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
TranslateX = "translate-x"
|
||||
TranslateX PropertyName = "translate-x"
|
||||
|
||||
// TranslateY is the constant for "translate-y" property tag.
|
||||
//
|
||||
|
@ -143,7 +143,7 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
TranslateY = "translate-y"
|
||||
TranslateY PropertyName = "translate-y"
|
||||
|
||||
// TranslateZ is the constant for "translate-z" property tag.
|
||||
//
|
||||
|
@ -164,14 +164,14 @@ const (
|
|||
//
|
||||
// Internal type is `SizeUnit`, other types converted to it during assignment.
|
||||
// See `SizeUnit` description for more details.
|
||||
TranslateZ = "translate-z"
|
||||
TranslateZ PropertyName = "translate-z"
|
||||
|
||||
// ScaleX is the constant for "scale-x" property tag.
|
||||
//
|
||||
// Used by `View`, `Transform`.
|
||||
//
|
||||
// Usage in `View`:
|
||||
// x-axis scaling value of a 2D/3D scale. The original scale is 1. Values between 0 and 1 are used to decrease original
|
||||
// x-axis scaling value of a 2D/3D scale. The original scale is 1. Values between 0 and 1 are used to decrease original
|
||||
// scale, more than 1 - to increase. The default value is 1.
|
||||
//
|
||||
// Supported types: `float`, `int`, `string`.
|
||||
|
@ -179,20 +179,20 @@ const (
|
|||
// Internal type is `float`, other types converted to it during assignment.
|
||||
//
|
||||
// Usage in `Transform`:
|
||||
// x-axis scaling value of a 2D/3D scale. The original scale is 1. Values between 0 and 1 are used to decrease original
|
||||
// x-axis scaling value of a 2D/3D scale. The original scale is 1. Values between 0 and 1 are used to decrease original
|
||||
// scale, more than 1 - to increase. The default value is 1.
|
||||
//
|
||||
// Supported types: `float`, `int`, `string`.
|
||||
//
|
||||
// Internal type is `float`, other types converted to it during assignment.
|
||||
ScaleX = "scale-x"
|
||||
ScaleX PropertyName = "scale-x"
|
||||
|
||||
// ScaleY is the constant for "scale-y" property tag.
|
||||
//
|
||||
// Used by `View`, `Transform`.
|
||||
//
|
||||
// Usage in `View`:
|
||||
// y-axis scaling value of a 2D/3D scale. The original scale is 1. Values between 0 and 1 are used to decrease original
|
||||
// y-axis scaling value of a 2D/3D scale. The original scale is 1. Values between 0 and 1 are used to decrease original
|
||||
// scale, more than 1 - to increase. The default value is 1.
|
||||
//
|
||||
// Supported types: `float`, `int`, `string`.
|
||||
|
@ -200,20 +200,20 @@ const (
|
|||
// Internal type is `float`, other types converted to it during assignment.
|
||||
//
|
||||
// Usage in `Transform`:
|
||||
// y-axis scaling value of a 2D/3D scale. The original scale is 1. Values between 0 and 1 are used to decrease original
|
||||
// y-axis scaling value of a 2D/3D scale. The original scale is 1. Values between 0 and 1 are used to decrease original
|
||||
// scale, more than 1 - to increase. The default value is 1.
|
||||
//
|
||||
// Supported types: `float`, `int`, `string`.
|
||||
//
|
||||
// Internal type is `float`, other types converted to it during assignment.
|
||||
ScaleY = "scale-y"
|
||||
ScaleY PropertyName = "scale-y"
|
||||
|
||||
// ScaleZ is the constant for "scale-z" property tag.
|
||||
//
|
||||
// Used by `View`, `Transform`.
|
||||
//
|
||||
// Usage in `View`:
|
||||
// z-axis scaling value of a 3D scale. The original scale is 1. Values between 0 and 1 are used to decrease original
|
||||
// z-axis scaling value of a 3D scale. The original scale is 1. Values between 0 and 1 are used to decrease original
|
||||
// scale, more than 1 - to increase. The default value is 1.
|
||||
//
|
||||
// Supported types: `float`, `int`, `string`.
|
||||
|
@ -221,13 +221,13 @@ const (
|
|||
// Internal type is `float`, other types converted to it during assignment.
|
||||
//
|
||||
// Usage in `Transform`:
|
||||
// z-axis scaling value of a 3D scale. The original scale is 1. Values between 0 and 1 are used to decrease original
|
||||
// z-axis scaling value of a 3D scale. The original scale is 1. Values between 0 and 1 are used to decrease original
|
||||
// scale, more than 1 - to increase. The default value is 1.
|
||||
//
|
||||
// Supported types: `float`, `int`, `string`.
|
||||
//
|
||||
// Internal type is `float`, other types converted to it during assignment.
|
||||
ScaleZ = "scale-z"
|
||||
ScaleZ PropertyName = "scale-z"
|
||||
|
||||
// Rotate is the constant for "rotate" property tag.
|
||||
//
|
||||
|
@ -260,7 +260,7 @@ const (
|
|||
// `string` - must contain string representation of `AngleUnit`. If numeric value will be provided without any suffix then `AngleUnit` with value and `Radian` value type will be created.
|
||||
// `float` - a new `AngleUnit` value will be created with `Radian` as a type.
|
||||
// `int` - a new `AngleUnit` value will be created with `Radian` as a type.
|
||||
Rotate = "rotate"
|
||||
Rotate PropertyName = "rotate"
|
||||
|
||||
// RotateX is the constant for "rotate-x" property tag.
|
||||
//
|
||||
|
@ -279,7 +279,7 @@ const (
|
|||
// Supported types: `float`, `int`, `string`.
|
||||
//
|
||||
// Internal type is `float`, other types converted to it during assignment.
|
||||
RotateX = "rotate-x"
|
||||
RotateX PropertyName = "rotate-x"
|
||||
|
||||
// RotateY is the constant for "rotate-y" property tag.
|
||||
//
|
||||
|
@ -298,7 +298,7 @@ const (
|
|||
// Supported types: `float`, `int`, `string`.
|
||||
//
|
||||
// Internal type is `float`, other types converted to it during assignment.
|
||||
RotateY = "rotate-y"
|
||||
RotateY PropertyName = "rotate-y"
|
||||
|
||||
// RotateZ is the constant for "rotate-z" property tag.
|
||||
//
|
||||
|
@ -317,7 +317,7 @@ const (
|
|||
// Supported types: `float`, `int`, `string`.
|
||||
//
|
||||
// Internal type is `float`, other types converted to it during assignment.
|
||||
RotateZ = "rotate-z"
|
||||
RotateZ PropertyName = "rotate-z"
|
||||
|
||||
// SkewX is the constant for "skew-x" property tag.
|
||||
//
|
||||
|
@ -350,7 +350,7 @@ const (
|
|||
// `string` - must contain string representation of `AngleUnit`. If numeric value will be provided without any suffix then `AngleUnit` with value and `Radian` value type will be created.
|
||||
// `float` - a new `AngleUnit` value will be created with `Radian` as a type.
|
||||
// `int` - a new `AngleUnit` value will be created with `Radian` as a type.
|
||||
SkewX = "skew-x"
|
||||
SkewX PropertyName = "skew-x"
|
||||
|
||||
// SkewY is the constant for "skew-y" property tag.
|
||||
//
|
||||
|
@ -383,44 +383,97 @@ const (
|
|||
// `string` - must contain string representation of `AngleUnit`. If numeric value will be provided without any suffix then `AngleUnit` with value and `Radian` value type will be created.
|
||||
// `float` - a new `AngleUnit` value will be created with `Radian` as a type.
|
||||
// `int` - a new `AngleUnit` value will be created with `Radian` as a type.
|
||||
SkewY = "skew-y"
|
||||
SkewY PropertyName = "skew-y"
|
||||
)
|
||||
|
||||
// Transform interface specifies view transformation parameters: the x-, y-, and z-axis translation values,
|
||||
// the x-, y-, and z-axis scaling values, the angle to use to distort the element along the abscissa and ordinate,
|
||||
// the angle of the view rotation.
|
||||
// Valid property tags: TranslateX ("translate-x"), TranslateY ("translate-y"), TranslateZ ("translate-z"),
|
||||
// Valid property tags: Perspective ("perspective"), TranslateX ("translate-x"), TranslateY ("translate-y"), TranslateZ ("translate-z"),
|
||||
// ScaleX ("scale-x"), ScaleY ("scale-y"), ScaleZ ("scale-z"), Rotate ("rotate"), RotateX ("rotate-x"),
|
||||
// RotateY ("rotate-y"), RotateZ ("rotate-z"), SkewX ("skew-x"), and SkewY ("skew-y")
|
||||
type Transform interface {
|
||||
Properties
|
||||
fmt.Stringer
|
||||
stringWriter
|
||||
transformCSS(session Session, transform3D bool) string
|
||||
transformCSS(session Session) string
|
||||
}
|
||||
|
||||
type transformData struct {
|
||||
propertyList
|
||||
dataProperty
|
||||
}
|
||||
|
||||
// NewTransform creates a new transform property data and return its interface
|
||||
func NewTransform(params Params) Transform {
|
||||
transform := new(transformData)
|
||||
transform.properties = map[string]any{}
|
||||
transform.init()
|
||||
|
||||
for tag, value := range params {
|
||||
transform.Set(tag, value)
|
||||
}
|
||||
return transform
|
||||
}
|
||||
|
||||
func (style *viewStyle) setTransform(value any) bool {
|
||||
func (transform *transformData) init() {
|
||||
transform.dataProperty.init()
|
||||
transform.set = transformSet
|
||||
transform.supportedProperties = []PropertyName{
|
||||
RotateX, RotateY, RotateZ, Rotate, SkewX, SkewY, ScaleX, ScaleY, ScaleZ,
|
||||
Perspective, TranslateX, TranslateY, TranslateZ,
|
||||
}
|
||||
}
|
||||
|
||||
func (transform *transformData) String() string {
|
||||
buffer := allocStringBuilder()
|
||||
defer freeStringBuilder(buffer)
|
||||
transform.writeString(buffer, "")
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
func (transform *transformData) writeString(buffer *strings.Builder, indent string) {
|
||||
buffer.WriteString("_{ ")
|
||||
comma := false
|
||||
for _, tag := range transform.supportedProperties {
|
||||
if value, ok := transform.properties[tag]; ok {
|
||||
if comma {
|
||||
buffer.WriteString(", ")
|
||||
}
|
||||
buffer.WriteString(string(tag))
|
||||
buffer.WriteString(" = ")
|
||||
writePropertyValue(buffer, tag, value, indent)
|
||||
comma = true
|
||||
}
|
||||
}
|
||||
buffer.WriteString(" }")
|
||||
}
|
||||
|
||||
func transformSet(properties Properties, tag PropertyName, value any) []PropertyName {
|
||||
switch tag {
|
||||
|
||||
case RotateX, RotateY, RotateZ:
|
||||
return setFloatProperty(properties, tag, value, 0, 1)
|
||||
|
||||
case Rotate, SkewX, SkewY:
|
||||
return setAngleProperty(properties, tag, value)
|
||||
|
||||
case ScaleX, ScaleY, ScaleZ:
|
||||
return setFloatProperty(properties, tag, value, -math.MaxFloat64, math.MaxFloat64)
|
||||
|
||||
case Perspective, TranslateX, TranslateY, TranslateZ:
|
||||
return setSizeProperty(properties, tag, value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setTransformProperty(properties Properties, value any) bool {
|
||||
|
||||
setObject := func(obj DataObject) bool {
|
||||
transform := NewTransform(nil)
|
||||
ok := true
|
||||
for i := 0; i < obj.PropertyCount(); i++ {
|
||||
if prop := obj.Property(i); prop.Type() == TextNode {
|
||||
if !transform.Set(prop.Tag(), prop.Text()) {
|
||||
if !transform.Set(PropertyName(prop.Tag()), prop.Text()) {
|
||||
ok = false
|
||||
}
|
||||
} else {
|
||||
|
@ -428,17 +481,17 @@ func (style *viewStyle) setTransform(value any) bool {
|
|||
}
|
||||
}
|
||||
|
||||
if !ok && len(transform.AllTags()) == 0 {
|
||||
if !ok && transform.empty() {
|
||||
return false
|
||||
}
|
||||
|
||||
style.properties[TransformTag] = transform
|
||||
properties.setRaw(TransformTag, transform)
|
||||
return true
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case Transform:
|
||||
style.properties[TransformTag] = value
|
||||
properties.setRaw(TransformTag, value)
|
||||
return true
|
||||
|
||||
case DataObject:
|
||||
|
@ -462,8 +515,8 @@ func (style *viewStyle) setTransform(value any) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (style *viewStyle) transformProperty() Transform {
|
||||
if val, ok := style.properties[TransformTag]; ok {
|
||||
func getTransformProperty(properties Properties) Transform {
|
||||
if val := properties.getRaw(TransformTag); val != nil {
|
||||
if transform, ok := val.(Transform); ok {
|
||||
return transform
|
||||
}
|
||||
|
@ -471,80 +524,26 @@ func (style *viewStyle) transformProperty() Transform {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (style *viewStyle) setTransformProperty(tag string, value any) bool {
|
||||
func setTransformPropertyElement(properties Properties, tag PropertyName, value any) []PropertyName {
|
||||
switch tag {
|
||||
case RotateX, RotateY, RotateZ, Rotate, SkewX, SkewY, ScaleX, ScaleY, ScaleZ, TranslateX, TranslateY, TranslateZ:
|
||||
if transform := style.transformProperty(); transform != nil {
|
||||
return transform.Set(tag, value)
|
||||
}
|
||||
|
||||
transform := NewTransform(nil)
|
||||
if !transform.Set(tag, value) {
|
||||
return false
|
||||
}
|
||||
|
||||
style.properties[TransformTag] = transform
|
||||
return true
|
||||
}
|
||||
|
||||
ErrorLogF(`"Transform" interface does not support the "%s" property`, tag)
|
||||
return false
|
||||
}
|
||||
|
||||
func (transform *transformData) String() string {
|
||||
buffer := allocStringBuilder()
|
||||
defer freeStringBuilder(buffer)
|
||||
transform.writeString(buffer, "")
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
func (transform *transformData) writeString(buffer *strings.Builder, indent string) {
|
||||
buffer.WriteString("_{ ")
|
||||
comma := false
|
||||
for _, tag := range []string{SkewX, SkewY, TranslateX, TranslateY, TranslateZ,
|
||||
ScaleX, ScaleY, ScaleZ, Rotate, RotateX, RotateY, RotateZ} {
|
||||
if value, ok := transform.properties[tag]; ok {
|
||||
if comma {
|
||||
buffer.WriteString(", ")
|
||||
case Perspective, RotateX, RotateY, RotateZ, Rotate, SkewX, SkewY, ScaleX, ScaleY, ScaleZ, TranslateX, TranslateY, TranslateZ:
|
||||
if transform := getTransformProperty(properties); transform != nil {
|
||||
if result := transformSet(transform, tag, value); result != nil {
|
||||
result = append(result, TransformTag)
|
||||
}
|
||||
} else {
|
||||
transform := NewTransform(nil)
|
||||
if result := transformSet(transform, tag, value); result != nil {
|
||||
properties.setRaw(TransformTag, transform)
|
||||
result = append(result, TransformTag)
|
||||
}
|
||||
buffer.WriteString(tag)
|
||||
buffer.WriteString(" = ")
|
||||
writePropertyValue(buffer, tag, value, indent)
|
||||
comma = true
|
||||
}
|
||||
}
|
||||
buffer.WriteString(" }")
|
||||
}
|
||||
|
||||
func (transform *transformData) Set(tag string, value any) bool {
|
||||
return transform.set(strings.ToLower(tag), value)
|
||||
}
|
||||
|
||||
func (transform *transformData) set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
_, exist := transform.properties[tag]
|
||||
if exist {
|
||||
delete(transform.properties, tag)
|
||||
}
|
||||
return exist
|
||||
default:
|
||||
ErrorLogF(`"Transform" interface does not support the "%s" property`, tag)
|
||||
}
|
||||
|
||||
switch tag {
|
||||
|
||||
case RotateX, RotateY, RotateZ:
|
||||
return transform.setFloatProperty(tag, value, 0, 1)
|
||||
|
||||
case Rotate, SkewX, SkewY:
|
||||
return transform.setAngleProperty(tag, value)
|
||||
|
||||
case ScaleX, ScaleY, ScaleZ:
|
||||
return transform.setFloatProperty(tag, value, -math.MaxFloat64, math.MaxFloat64)
|
||||
|
||||
case TranslateX, TranslateY, TranslateZ:
|
||||
return transform.setSizeProperty(tag, value)
|
||||
}
|
||||
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
func getTransform3D(style Properties, session Session) bool {
|
||||
|
@ -578,61 +577,75 @@ func (transform *transformData) getTranslate(session Session) (SizeUnit, SizeUni
|
|||
return x, y, z
|
||||
}
|
||||
|
||||
func (transform *transformData) transformCSS(session Session, transform3D bool) string {
|
||||
func (transform *transformData) transformCSS(session Session) string {
|
||||
|
||||
buffer := allocStringBuilder()
|
||||
defer freeStringBuilder(buffer)
|
||||
|
||||
if perspective, ok := sizeProperty(transform, Perspective, session); ok && perspective.Type != Auto && perspective.Value != 0 {
|
||||
buffer.WriteString(`perspective(`)
|
||||
buffer.WriteString(perspective.cssString("0", session))
|
||||
buffer.WriteString(") ")
|
||||
}
|
||||
|
||||
skewX, skewY, skewOK := transform.getSkew(session)
|
||||
if skewOK {
|
||||
buffer.WriteString(`skew(`)
|
||||
buffer.WriteString(skewX.cssString())
|
||||
buffer.WriteRune(',')
|
||||
buffer.WriteString(skewY.cssString())
|
||||
buffer.WriteRune(')')
|
||||
buffer.WriteString(") ")
|
||||
}
|
||||
|
||||
x, y, z := transform.getTranslate(session)
|
||||
if z.Type != Auto && z.Value != 0 {
|
||||
|
||||
buffer.WriteString(`translate3d(`)
|
||||
buffer.WriteString(x.cssString("0", session))
|
||||
buffer.WriteRune(',')
|
||||
buffer.WriteString(y.cssString("0", session))
|
||||
buffer.WriteRune(',')
|
||||
buffer.WriteString(z.cssString("0", session))
|
||||
buffer.WriteString(") ")
|
||||
|
||||
} else if (x.Type != Auto && x.Value != 0) || (y.Type != Auto && y.Value != 0) {
|
||||
|
||||
buffer.WriteString(`translate(`)
|
||||
buffer.WriteString(x.cssString("0", session))
|
||||
buffer.WriteRune(',')
|
||||
buffer.WriteString(y.cssString("0", session))
|
||||
buffer.WriteString(") ")
|
||||
}
|
||||
|
||||
scaleX, okScaleX := floatTextProperty(transform, ScaleX, session, 1)
|
||||
scaleY, okScaleY := floatTextProperty(transform, ScaleY, session, 1)
|
||||
scaleZ, okScaleZ := floatTextProperty(transform, ScaleZ, session, 1)
|
||||
if okScaleZ {
|
||||
|
||||
if transform3D {
|
||||
if x.Type != Auto || y.Type != Auto || z.Type != Auto {
|
||||
if buffer.Len() > 0 {
|
||||
buffer.WriteRune(' ')
|
||||
}
|
||||
buffer.WriteString(`translate3d(`)
|
||||
buffer.WriteString(x.cssString("0", session))
|
||||
buffer.WriteRune(',')
|
||||
buffer.WriteString(y.cssString("0", session))
|
||||
buffer.WriteRune(',')
|
||||
buffer.WriteString(z.cssString("0", session))
|
||||
buffer.WriteRune(')')
|
||||
}
|
||||
buffer.WriteString(`scale3d(`)
|
||||
buffer.WriteString(scaleX)
|
||||
buffer.WriteRune(',')
|
||||
buffer.WriteString(scaleY)
|
||||
buffer.WriteRune(',')
|
||||
buffer.WriteString(scaleZ)
|
||||
buffer.WriteString(") ")
|
||||
|
||||
scaleZ, okScaleZ := floatTextProperty(transform, ScaleZ, session, 1)
|
||||
if okScaleX || okScaleY || okScaleZ {
|
||||
if buffer.Len() > 0 {
|
||||
buffer.WriteRune(' ')
|
||||
}
|
||||
buffer.WriteString(`scale3d(`)
|
||||
buffer.WriteString(scaleX)
|
||||
buffer.WriteRune(',')
|
||||
buffer.WriteString(scaleY)
|
||||
buffer.WriteRune(',')
|
||||
buffer.WriteString(scaleZ)
|
||||
buffer.WriteRune(')')
|
||||
}
|
||||
} else if okScaleX || okScaleY {
|
||||
|
||||
if angle, ok := angleProperty(transform, Rotate, session); ok {
|
||||
rotateX, _ := floatTextProperty(transform, RotateX, session, 1)
|
||||
rotateY, _ := floatTextProperty(transform, RotateY, session, 1)
|
||||
rotateZ, _ := floatTextProperty(transform, RotateZ, session, 1)
|
||||
buffer.WriteString(`scale(`)
|
||||
buffer.WriteString(scaleX)
|
||||
buffer.WriteRune(',')
|
||||
buffer.WriteString(scaleY)
|
||||
buffer.WriteString(") ")
|
||||
}
|
||||
|
||||
if angle, ok := angleProperty(transform, Rotate, session); ok {
|
||||
rotateX, xOK := floatTextProperty(transform, RotateX, session, 1)
|
||||
rotateY, yOK := floatTextProperty(transform, RotateY, session, 1)
|
||||
rotateZ, zOK := floatTextProperty(transform, RotateZ, session, 1)
|
||||
|
||||
if xOK || yOK || zOK {
|
||||
|
||||
if buffer.Len() > 0 {
|
||||
buffer.WriteRune(' ')
|
||||
}
|
||||
buffer.WriteString(`rotate3d(`)
|
||||
buffer.WriteString(rotateX)
|
||||
buffer.WriteRune(',')
|
||||
|
@ -641,90 +654,56 @@ func (transform *transformData) transformCSS(session Session, transform3D bool)
|
|||
buffer.WriteString(rotateZ)
|
||||
buffer.WriteRune(',')
|
||||
buffer.WriteString(angle.cssString())
|
||||
buffer.WriteRune(')')
|
||||
}
|
||||
buffer.WriteString(") ")
|
||||
|
||||
} else {
|
||||
} else {
|
||||
|
||||
if x.Type != Auto || y.Type != Auto {
|
||||
if buffer.Len() > 0 {
|
||||
buffer.WriteRune(' ')
|
||||
}
|
||||
buffer.WriteString(`translate(`)
|
||||
buffer.WriteString(x.cssString("0", session))
|
||||
buffer.WriteRune(',')
|
||||
buffer.WriteString(y.cssString("0", session))
|
||||
buffer.WriteRune(')')
|
||||
}
|
||||
|
||||
if okScaleX || okScaleY {
|
||||
if buffer.Len() > 0 {
|
||||
buffer.WriteRune(' ')
|
||||
}
|
||||
buffer.WriteString(`scale(`)
|
||||
buffer.WriteString(scaleX)
|
||||
buffer.WriteRune(',')
|
||||
buffer.WriteString(scaleY)
|
||||
buffer.WriteRune(')')
|
||||
}
|
||||
|
||||
if angle, ok := angleProperty(transform, Rotate, session); ok {
|
||||
if buffer.Len() > 0 {
|
||||
buffer.WriteRune(' ')
|
||||
}
|
||||
buffer.WriteString(`rotate(`)
|
||||
buffer.WriteString(angle.cssString())
|
||||
buffer.WriteRune(')')
|
||||
buffer.WriteString(") ")
|
||||
}
|
||||
}
|
||||
|
||||
return buffer.String()
|
||||
length := buffer.Len()
|
||||
if length == 0 {
|
||||
return ""
|
||||
}
|
||||
result := buffer.String()
|
||||
return result[:length-1]
|
||||
}
|
||||
|
||||
func (style *viewStyle) writeViewTransformCSS(builder cssBuilder, session Session) {
|
||||
transform3D := getTransform3D(style, session)
|
||||
if transform3D {
|
||||
if perspective, ok := sizeProperty(style, Perspective, session); ok && perspective.Type != Auto && perspective.Value != 0 {
|
||||
builder.add(`perspective`, perspective.cssString("0", session))
|
||||
}
|
||||
x, y := getPerspectiveOrigin(style, session)
|
||||
if x.Type != Auto || y.Type != Auto {
|
||||
builder.addValues(`perspective-origin`, ` `, x.cssString("50%", session), y.cssString("50%", session))
|
||||
}
|
||||
|
||||
x, y := getPerspectiveOrigin(style, session)
|
||||
if x.Type != Auto || y.Type != Auto {
|
||||
builder.addValues(`perspective-origin`, ` `, x.cssString("50%", session), y.cssString("50%", session))
|
||||
}
|
||||
|
||||
if backfaceVisible, ok := boolProperty(style, BackfaceVisible, session); ok {
|
||||
if backfaceVisible {
|
||||
builder.add(`backface-visibility`, `visible`)
|
||||
} else {
|
||||
builder.add(`backface-visibility`, `hidden`)
|
||||
}
|
||||
}
|
||||
|
||||
x, y, z := getOrigin(style, session)
|
||||
if x.Type != Auto || y.Type != Auto || z.Type != Auto {
|
||||
builder.addValues(`transform-origin`, ` `, x.cssString("50%", session), y.cssString("50%", session), z.cssString("0", session))
|
||||
}
|
||||
} else {
|
||||
x, y, _ := getOrigin(style, session)
|
||||
if x.Type != Auto || y.Type != Auto {
|
||||
builder.addValues(`transform-origin`, ` `, x.cssString("50%", session), y.cssString("50%", session))
|
||||
if backfaceVisible, ok := boolProperty(style, BackfaceVisible, session); ok {
|
||||
if backfaceVisible {
|
||||
builder.add(`backface-visibility`, `visible`)
|
||||
} else {
|
||||
builder.add(`backface-visibility`, `hidden`)
|
||||
}
|
||||
}
|
||||
|
||||
if transform := style.transformProperty(); transform != nil {
|
||||
builder.add(`transform`, transform.transformCSS(session, transform3D))
|
||||
x, y, z := getOrigin(style, session)
|
||||
if z.Type != Auto && z.Value != 0 {
|
||||
builder.addValues(`transform-origin`, ` `, x.cssString("50%", session), y.cssString("50%", session), z.cssString("0", session))
|
||||
} 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 {
|
||||
builder.add(`transform`, transform.transformCSS(session))
|
||||
}
|
||||
}
|
||||
|
||||
func (view *viewData) updateTransformProperty(tag string) bool {
|
||||
/*
|
||||
func (view *viewData) updateTransformProperty(tag PropertyName) bool {
|
||||
htmlID := view.htmlID()
|
||||
session := view.session
|
||||
|
||||
switch tag {
|
||||
case Perspective:
|
||||
updateCSSStyle(htmlID, session)
|
||||
|
||||
case PerspectiveOriginX, PerspectiveOriginY:
|
||||
if getTransform3D(view, session) {
|
||||
x, y := GetPerspectiveOrigin(view)
|
||||
|
@ -738,31 +717,27 @@ func (view *viewData) updateTransformProperty(tag string) bool {
|
|||
case BackfaceVisible:
|
||||
if getTransform3D(view, session) {
|
||||
if GetBackfaceVisible(view) {
|
||||
session.updateCSSProperty(htmlID, BackfaceVisible, "visible")
|
||||
session.updateCSSProperty(htmlID, string(BackfaceVisible), "visible")
|
||||
} else {
|
||||
session.updateCSSProperty(htmlID, BackfaceVisible, "hidden")
|
||||
session.updateCSSProperty(htmlID, string(BackfaceVisible), "hidden")
|
||||
}
|
||||
}
|
||||
|
||||
case OriginX, OriginY, OriginZ:
|
||||
x, y, z := getOrigin(view, session)
|
||||
value := ""
|
||||
if getTransform3D(view, session) {
|
||||
if x.Type != Auto || y.Type != Auto || 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)
|
||||
}
|
||||
|
||||
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, SkewX, SkewY, TranslateX, TranslateY, TranslateZ,
|
||||
ScaleX, ScaleY, ScaleZ, Rotate, RotateX, RotateY, RotateZ:
|
||||
if transform := view.transformProperty(); transform != nil {
|
||||
transform3D := getTransform3D(view, session)
|
||||
session.updateCSSProperty(htmlID, "transform", transform.transformCSS(session, transform3D))
|
||||
if transform := getTransformProperty(view); transform != nil {
|
||||
session.updateCSSProperty(htmlID, "transform", transform.transformCSS(session))
|
||||
} else {
|
||||
session.updateCSSProperty(htmlID, "transform", "")
|
||||
}
|
||||
|
@ -773,3 +748,4 @@ func (view *viewData) updateTransformProperty(tag string) bool {
|
|||
|
||||
return true
|
||||
}
|
||||
*/
|
||||
|
|
26
viewUtils.go
26
viewUtils.go
|
@ -3,7 +3,7 @@ package rui
|
|||
// Get returns a value of the property with name "tag" of the "rootView" subview with "viewID" id value.
|
||||
// The type of return value depends on the property.
|
||||
// If the subview don't exists or the property is not set then nil is returned.
|
||||
func Get(rootView View, viewID, tag string) any {
|
||||
func Get(rootView View, viewID string, tag PropertyName) any {
|
||||
var view View
|
||||
if viewID != "" {
|
||||
view = ViewByID(rootView, viewID)
|
||||
|
@ -19,7 +19,7 @@ func Get(rootView View, viewID, tag string) any {
|
|||
// Set sets the property with name "tag" of the "rootView" subview with "viewID" id by value. Result:
|
||||
// true - success,
|
||||
// false - error (incompatible type or invalid format of a string value, see AppLog).
|
||||
func Set(rootView View, viewID, tag string, value any) bool {
|
||||
func Set(rootView View, viewID string, tag PropertyName, value any) bool {
|
||||
var view View
|
||||
if viewID != "" {
|
||||
view = ViewByID(rootView, viewID)
|
||||
|
@ -34,7 +34,7 @@ func Set(rootView View, viewID, tag string, value any) bool {
|
|||
|
||||
// SetChangeListener sets a listener for changing a subview property value.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a listener for the first argument (view) is set
|
||||
func SetChangeListener(view View, viewID, tag string, listener func(View, string)) {
|
||||
func SetChangeListener(view View, viewID string, tag PropertyName, listener func(View, PropertyName)) {
|
||||
if viewID != "" {
|
||||
view = ViewByID(view, viewID)
|
||||
}
|
||||
|
@ -294,7 +294,7 @@ func GetBorder(view View, subviewID ...string) ViewBorders {
|
|||
view = ViewByID(view, subviewID[0])
|
||||
}
|
||||
if view != nil {
|
||||
if border := getBorder(view, Border); border != nil {
|
||||
if border := getBorderProperty(view, Border); border != nil {
|
||||
return border.ViewBorders(view.Session())
|
||||
}
|
||||
}
|
||||
|
@ -320,7 +320,7 @@ func GetOutline(view View, subviewID ...string) ViewOutline {
|
|||
view = ViewByID(view, subviewID[0])
|
||||
}
|
||||
if view != nil {
|
||||
if outline := getOutline(view); outline != nil {
|
||||
if outline := getOutlineProperty(view); outline != nil {
|
||||
return outline.ViewOutline(view.Session())
|
||||
}
|
||||
}
|
||||
|
@ -706,9 +706,9 @@ func GetNotTranslate(view View, subviewID ...string) bool {
|
|||
return boolStyledProperty(view, subviewID, NotTranslate, true)
|
||||
}
|
||||
|
||||
func valueFromStyle(view View, tag string) any {
|
||||
func valueFromStyle(view View, tag PropertyName) any {
|
||||
session := view.Session()
|
||||
getValue := func(styleTag string) any {
|
||||
getValue := func(styleTag PropertyName) any {
|
||||
if style, ok := stringProperty(view, styleTag, session); ok {
|
||||
if style, ok := session.resolveConstants(style); ok {
|
||||
return session.styleProperty(style, tag)
|
||||
|
@ -725,7 +725,7 @@ func valueFromStyle(view View, tag string) any {
|
|||
return getValue(Style)
|
||||
}
|
||||
|
||||
func sizeStyledProperty(view View, subviewID []string, tag string, inherit bool) SizeUnit {
|
||||
func sizeStyledProperty(view View, subviewID []string, tag PropertyName, inherit bool) SizeUnit {
|
||||
if len(subviewID) > 0 && subviewID[0] != "" {
|
||||
view = ViewByID(view, subviewID[0])
|
||||
}
|
||||
|
@ -749,7 +749,7 @@ func sizeStyledProperty(view View, subviewID []string, tag string, inherit bool)
|
|||
return AutoSize()
|
||||
}
|
||||
|
||||
func enumStyledProperty(view View, subviewID []string, tag string, defaultValue int, inherit bool) int {
|
||||
func enumStyledProperty(view View, subviewID []string, tag PropertyName, defaultValue int, inherit bool) int {
|
||||
if len(subviewID) > 0 && subviewID[0] != "" {
|
||||
view = ViewByID(view, subviewID[0])
|
||||
}
|
||||
|
@ -773,7 +773,7 @@ func enumStyledProperty(view View, subviewID []string, tag string, defaultValue
|
|||
return defaultValue
|
||||
}
|
||||
|
||||
func boolStyledProperty(view View, subviewID []string, tag string, inherit bool) bool {
|
||||
func boolStyledProperty(view View, subviewID []string, tag PropertyName, inherit bool) bool {
|
||||
if len(subviewID) > 0 && subviewID[0] != "" {
|
||||
view = ViewByID(view, subviewID[0])
|
||||
}
|
||||
|
@ -798,7 +798,7 @@ func boolStyledProperty(view View, subviewID []string, tag string, inherit bool)
|
|||
return false
|
||||
}
|
||||
|
||||
func intStyledProperty(view View, subviewID []string, tag string, defaultValue int) int {
|
||||
func intStyledProperty(view View, subviewID []string, tag PropertyName, defaultValue int) int {
|
||||
if len(subviewID) > 0 && subviewID[0] != "" {
|
||||
view = ViewByID(view, subviewID[0])
|
||||
}
|
||||
|
@ -815,7 +815,7 @@ func intStyledProperty(view View, subviewID []string, tag string, defaultValue i
|
|||
return defaultValue
|
||||
}
|
||||
|
||||
func floatStyledProperty(view View, subviewID []string, tag string, defaultValue float64) float64 {
|
||||
func floatStyledProperty(view View, subviewID []string, tag PropertyName, defaultValue float64) float64 {
|
||||
if len(subviewID) > 0 && subviewID[0] != "" {
|
||||
view = ViewByID(view, subviewID[0])
|
||||
}
|
||||
|
@ -831,7 +831,7 @@ func floatStyledProperty(view View, subviewID []string, tag string, defaultValue
|
|||
return defaultValue
|
||||
}
|
||||
|
||||
func colorStyledProperty(view View, subviewID []string, tag string, inherit bool) Color {
|
||||
func colorStyledProperty(view View, subviewID []string, tag PropertyName, inherit bool) Color {
|
||||
if len(subviewID) > 0 && subviewID[0] != "" {
|
||||
view = ViewByID(view, subviewID[0])
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ type ViewsContainer interface {
|
|||
|
||||
// ViewIndex returns the index of view, -1 overwise
|
||||
ViewIndex(view View) int
|
||||
|
||||
setContent(value any) bool
|
||||
}
|
||||
|
||||
type viewsContainerData struct {
|
||||
|
@ -36,10 +38,10 @@ func (container *viewsContainerData) init(session Session) {
|
|||
container.viewData.init(session)
|
||||
container.tag = "ViewsContainer"
|
||||
container.views = []View{}
|
||||
}
|
||||
|
||||
func (container *viewsContainerData) String() string {
|
||||
return getViewString(container, nil)
|
||||
container.getFunc = container.get
|
||||
container.set = container.setFunc
|
||||
container.remove = container.removeFunc
|
||||
container.changed = viewsContainerPropertyChanged
|
||||
}
|
||||
|
||||
func (container *viewsContainerData) setParentID(parentID string) {
|
||||
|
@ -62,6 +64,13 @@ func (container *viewsContainerData) Views() []View {
|
|||
return []View{}
|
||||
}
|
||||
|
||||
func viewsContainerContentChanged(container *viewsContainerData) {
|
||||
updateInnerHTML(container.htmlID(), container.Session())
|
||||
if listener, ok := container.changeListener[Content]; ok {
|
||||
listener(container, Content)
|
||||
}
|
||||
}
|
||||
|
||||
// Append appends a view to the end of the list of a view children
|
||||
func (container *viewsContainerData) Append(view View) {
|
||||
if view != nil {
|
||||
|
@ -72,8 +81,7 @@ func (container *viewsContainerData) Append(view View) {
|
|||
} else {
|
||||
container.views = append(container.views, view)
|
||||
}
|
||||
updateInnerHTML(container.htmlID(), container.session)
|
||||
container.propertyChangedEvent(Content)
|
||||
viewsContainerContentChanged(container)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,13 +94,11 @@ func (container *viewsContainerData) Insert(view View, index int) {
|
|||
} else if index > 0 {
|
||||
view.setParentID(htmlID)
|
||||
container.views = append(container.views[:index], append([]View{view}, container.views[index:]...)...)
|
||||
updateInnerHTML(container.htmlID(), container.session)
|
||||
container.propertyChangedEvent(Content)
|
||||
viewsContainerContentChanged(container)
|
||||
} else {
|
||||
view.setParentID(htmlID)
|
||||
container.views = append([]View{view}, container.views...)
|
||||
updateInnerHTML(container.htmlID(), container.session)
|
||||
container.propertyChangedEvent(Content)
|
||||
viewsContainerContentChanged(container)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -119,8 +125,7 @@ func (container *viewsContainerData) RemoveView(index int) View {
|
|||
}
|
||||
|
||||
view.setParentID("")
|
||||
updateInnerHTML(container.htmlID(), container.session)
|
||||
container.propertyChangedEvent(Content)
|
||||
viewsContainerContentChanged(container)
|
||||
return view
|
||||
}
|
||||
|
||||
|
@ -158,67 +163,60 @@ func viewFromTextValue(text string, session Session) View {
|
|||
return NewTextView(session, Params{Text: text})
|
||||
}
|
||||
|
||||
func (container *viewsContainerData) Remove(tag string) {
|
||||
container.remove(strings.ToLower(tag))
|
||||
}
|
||||
|
||||
func (container *viewsContainerData) remove(tag string) {
|
||||
func (container *viewsContainerData) removeFunc(view View, tag PropertyName) []PropertyName {
|
||||
switch tag {
|
||||
case Content:
|
||||
if container.views == nil || len(container.views) > 0 {
|
||||
if len(container.views) > 0 {
|
||||
container.views = []View{}
|
||||
updateInnerHTML(container.htmlID(), container.Session())
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
container.propertyChangedEvent(Content)
|
||||
return []PropertyName{}
|
||||
|
||||
case Disabled:
|
||||
if _, ok := container.properties[Disabled]; ok {
|
||||
delete(container.properties, Disabled)
|
||||
if container.views != nil {
|
||||
for _, view := range container.views {
|
||||
view.Remove(Disabled)
|
||||
}
|
||||
if view.getRaw(Disabled) != nil {
|
||||
view.setRaw(Disabled, nil)
|
||||
for _, view := range container.views {
|
||||
view.Remove(Disabled)
|
||||
}
|
||||
container.propertyChangedEvent(tag)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
default:
|
||||
container.viewData.remove(tag)
|
||||
}
|
||||
return viewRemove(view, tag)
|
||||
}
|
||||
|
||||
func (container *viewsContainerData) Set(tag string, value any) bool {
|
||||
return container.set(strings.ToLower(tag), value)
|
||||
}
|
||||
|
||||
func (container *viewsContainerData) set(tag string, value any) bool {
|
||||
if value == nil {
|
||||
container.remove(tag)
|
||||
return true
|
||||
}
|
||||
|
||||
func (container *viewsContainerData) setFunc(self View, tag PropertyName, value any) []PropertyName {
|
||||
switch tag {
|
||||
case Content:
|
||||
return container.setContent(value)
|
||||
if container.setContent(value) {
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
return nil
|
||||
|
||||
case Disabled:
|
||||
oldDisabled := IsDisabled(container)
|
||||
if container.viewData.Set(Disabled, value) {
|
||||
result := viewSet(self, Disabled, value)
|
||||
if result != nil {
|
||||
disabled := IsDisabled(container)
|
||||
if oldDisabled != disabled {
|
||||
if container.views != nil {
|
||||
for _, view := range container.views {
|
||||
view.Set(Disabled, disabled)
|
||||
}
|
||||
for _, view := range container.views {
|
||||
view.Set(Disabled, disabled)
|
||||
}
|
||||
}
|
||||
container.propertyChangedEvent(tag)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return result
|
||||
}
|
||||
|
||||
return container.viewData.set(tag, value)
|
||||
return viewSet(self, tag, value)
|
||||
}
|
||||
|
||||
func viewsContainerPropertyChanged(view View, tag PropertyName) {
|
||||
switch tag {
|
||||
case Content:
|
||||
updateInnerHTML(view.htmlID(), view.Session())
|
||||
|
||||
default:
|
||||
viewPropertyChanged(view, tag)
|
||||
}
|
||||
}
|
||||
|
||||
func (container *viewsContainerData) setContent(value any) bool {
|
||||
|
@ -291,25 +289,16 @@ func (container *viewsContainerData) setContent(value any) bool {
|
|||
}
|
||||
}
|
||||
|
||||
if container.created {
|
||||
updateInnerHTML(htmlID, container.session)
|
||||
}
|
||||
|
||||
container.propertyChangedEvent(Content)
|
||||
return true
|
||||
}
|
||||
|
||||
func (container *viewsContainerData) Get(tag string) any {
|
||||
return container.get(strings.ToLower(tag))
|
||||
}
|
||||
|
||||
func (container *viewsContainerData) get(tag string) any {
|
||||
func (container *viewsContainerData) get(view View, tag PropertyName) any {
|
||||
switch tag {
|
||||
case Content:
|
||||
return container.views
|
||||
|
||||
default:
|
||||
return container.viewData.get(tag)
|
||||
return viewGet(view, tag)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue