package rui import ( "fmt" "strings" ) // Constants for [RadiusProperty] specific properties const ( // Radius is the constant for "radius" property tag. // // Used by `View`, `BackgroundElement`, `ClipShape`. // // Usage in `View`: // Specifies the corners rounding radius of an element's outer border edge. // // Supported types: `RadiusProperty`, `SizeUnit`, `SizeFunc`, `BoxRadius`, `string`, `float`, `int`. // // Internal type is either `RadiusProperty` or `SizeUnit`, other types converted to them during assignment. // See `RadiusProperty`, `SizeUnit`, `SizeFunc` and `BoxRadius` description for more details. // // Conversion rules: // `RadiusProperty` - stored as is, no conversion performed. // `SizeUnit` - stored as is and set all corners to have the same value. // `BoxRadius` - a new `RadiusProperty` will be created and all corresponding elliptical radius values will be set. // `string` - if one value will be provided then it will be set as a radius for all corners. If two values will be provided divided by (`/`) then x and y radius will be set for all corners. Examples: "1em", "1em/0.5em", "2/4". Values which doesn't have size prefix will use size in pixels by default. // `float` - values of this type will set radius for all corners in pixels. // `int` - values of this type will set radius for all corners in pixels. // // Usage in `BackgroundElement`: // Same as "radial-gradient-radius". // // Usage in `ClipShape`: // Specifies the radius of the corners or the radius of the cropping area. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. Radius PropertyName = "radius" // RadiusX is the constant for "radius-x" property tag. // // Used by `View`, `ClipShape`. // // Usage in `View`: // Specifies the x-axis corners elliptic rounding radius of an element's outer border edge. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. // // Usage in `ClipShape`: // Specifies the x-axis corners elliptic rounding radius of the elliptic clip shape. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. RadiusX PropertyName = "radius-x" // RadiusY is the constant for "radius-y" property tag. // // Used by `View`, `ClipShape`. // // Usage in `View`: // Specifies the y-axis corners elliptic rounding radius of an element's outer border edge. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. // // Usage in `ClipShape`: // Specifies the y-axis corners elliptic rounding radius of of the elliptic clip shape. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. RadiusY PropertyName = "radius-y" // RadiusTopLeft is the constant for "radius-top-left" property tag. // // Used by `View`. // Specifies the top-left corner rounding radius of an element's outer border edge. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. RadiusTopLeft PropertyName = "radius-top-left" // RadiusTopLeftX is the constant for "radius-top-left-x" property tag. // // Used by `View`. // Specifies the x-axis top-left corner elliptic rounding radius of an element's outer border edge. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. RadiusTopLeftX PropertyName = "radius-top-left-x" // RadiusTopLeftY is the constant for "radius-top-left-y" property tag. // // Used by `View`. // Specifies the y-axis top-left corner elliptic rounding radius of an element's outer border edge. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. RadiusTopLeftY PropertyName = "radius-top-left-y" // RadiusTopRight is the constant for "radius-top-right" property tag. // // Used by `View`. // Specifies the top-right corner rounding radius of an element's outer border edge. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. RadiusTopRight PropertyName = "radius-top-right" // RadiusTopRightX is the constant for "radius-top-right-x" property tag. // // Used by `View`. // Specifies the x-axis top-right corner elliptic rounding radius of an element's outer border edge. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. RadiusTopRightX PropertyName = "radius-top-right-x" // RadiusTopRightY is the constant for "radius-top-right-y" property tag. // // Used by `View`. // Specifies the y-axis top-right corner elliptic rounding radius of an element's outer border edge. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. RadiusTopRightY PropertyName = "radius-top-right-y" // RadiusBottomLeft is the constant for "radius-bottom-left" property tag. // // Used by `View`. // Specifies the bottom-left corner rounding radius of an element's outer border edge. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. RadiusBottomLeft PropertyName = "radius-bottom-left" // RadiusBottomLeftX is the constant for "radius-bottom-left-x" property tag. // // Used by `View`. // Specifies the x-axis bottom-left corner elliptic rounding radius of an element's outer border edge. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. RadiusBottomLeftX PropertyName = "radius-bottom-left-x" // RadiusBottomLeftY is the constant for "radius-bottom-left-y" property tag. // // Used by `View`. // Specifies the y-axis bottom-left corner elliptic rounding radius of an element's outer border edge. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. RadiusBottomLeftY PropertyName = "radius-bottom-left-y" // RadiusBottomRight is the constant for "radius-bottom-right" property tag. // // Used by `View`. // Specifies the bottom-right corner rounding radius of an element's outer border edge. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. RadiusBottomRight PropertyName = "radius-bottom-right" // RadiusBottomRightX is the constant for "radius-bottom-right-x" property tag. // // Used by `View`. // Specifies the x-axis bottom-right corner elliptic rounding radius of an element's outer border edge. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. RadiusBottomRightX PropertyName = "radius-bottom-right-x" // RadiusBottomRightY is the constant for "radius-bottom-right-y" property tag. // // Used by `View`. // Specifies the y-axis bottom-right corner elliptic rounding radius of an element's outer border edge. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. RadiusBottomRightY PropertyName = "radius-bottom-right-y" // X is the constant for "x" property tag. // // Used by `ClipShape`, `RadiusProperty`. // // Usage in `ClipShape`: // Specifies x-axis position of the clip shape. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. // // Usage in `RadiusProperty`: // Determines the x-axis top-right corner elliptic rounding radius of an element's outer border edge. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. X PropertyName = "x" // Y is the constant for "y" property tag. // // Used by `ClipShape`, `RadiusProperty`. // // Usage in `ClipShape`: // Specifies y-axis position of the clip shape. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. // // Usage in `RadiusProperty`: // Determines the y-axis top-right corner elliptic rounding radius of an element's outer border edge. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. Y PropertyName = "y" // TopLeft is the constant for "top-left" property tag. // // Used by `RadiusProperty`. // Determines the top-left corner rounding radius of an element's outer border edge. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. TopLeft PropertyName = "top-left" // TopLeftX is the constant for "top-left-x" property tag. // // Used by `RadiusProperty`. // Determines the x-axis top-left corner elliptic rounding radius of an element's outer border edge. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. TopLeftX PropertyName = "top-left-x" // TopLeftY is the constant for "top-left-y" property tag. // // Used by `RadiusProperty`. // Determines the y-axis top-left corner elliptic rounding radius of an element's outer border edge. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. TopLeftY PropertyName = "top-left-y" // TopRight is the constant for "top-right" property tag. // // Used by `RadiusProperty`. // Determines the top-right corner rounding radius of an element's outer border edge. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. TopRight PropertyName = "top-right" // TopRightX is the constant for "top-right-x" property tag. // // Used by `RadiusProperty`. // Determines the x-axis top-right corner elliptic rounding radius of an element's outer border edge. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. TopRightX PropertyName = "top-right-x" // TopRightY is the constant for "top-right-y" property tag. // // Used by `RadiusProperty`. // Determines the y-axis top-right corner elliptic rounding radius of an element's outer border edge. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. TopRightY PropertyName = "top-right-y" // BottomLeft is the constant for "bottom-left" property tag. // // Used by `RadiusProperty`. // Determines the bottom-left corner rounding radius of an element's outer border edge. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. BottomLeft PropertyName = "bottom-left" // BottomLeftX is the constant for "bottom-left-x" property tag. // // Used by `RadiusProperty`. // Determines the x-axis bottom-left corner elliptic rounding radius of an element's outer border edge. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. BottomLeftX PropertyName = "bottom-left-x" // BottomLeftY is the constant for "bottom-left-y" property tag. // // Used by `RadiusProperty`. // Determines the y-axis bottom-left corner elliptic rounding radius of an element's outer border edge. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. BottomLeftY PropertyName = "bottom-left-y" // BottomRight is the constant for "bottom-right" property tag. // // Used by `RadiusProperty`. // Determines the bottom-right corner rounding radius of an element's outer border edge. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. BottomRight PropertyName = "bottom-right" // BottomRightX is the constant for "bottom-right-x" property tag. // // Used by `RadiusProperty`. // Determines the x-axis bottom-right corner elliptic rounding radius of an element's outer border edge. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. BottomRightX PropertyName = "bottom-right-x" // BottomRightY is the constant for "bottom-right-y" property tag. // // Used by `RadiusProperty`. // Determines the y-axis bottom-right corner elliptic rounding radius of an element's outer border edge. // // Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`. // // Internal type is `SizeUnit`, other types converted to it during assignment. // See `SizeUnit` description for more details. BottomRightY PropertyName = "bottom-right-y" ) // RadiusProperty is a description of the [View] (shape) elliptical corner radius. type RadiusProperty interface { Properties stringWriter fmt.Stringer // BoxRadius returns x and y radius of the corners of the element BoxRadius(session Session) BoxRadius } type radiusPropertyData struct { dataProperty } // NewRadiusProperty creates the new RadiusProperty func NewRadiusProperty(params Params) RadiusProperty { result := new(radiusPropertyData) 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 result.supportedProperties { if value, ok := params[tag]; ok { radiusPropertySet(result, tag, value) } } } return result } 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 radius.supportedProperties { if value, ok := radius.properties[tag]; ok { if comma { buffer.WriteString(", ") } buffer.WriteString(string(tag)) buffer.WriteString(" = ") writePropertyValue(buffer, tag, value, indent) comma = true } } buffer.WriteString(" }") } func (radius *radiusPropertyData) String() string { return runStringWriter(radius) } 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 deleteRadiusUnusedTags(radius Properties, result []PropertyName) { for _, tag := range []PropertyName{X, Y} { if radius.getRaw(tag) != nil { unused := true for _, t := range []PropertyName{TopLeft, TopRight, BottomLeft, BottomRight} { if radius.getRaw(t+"-"+tag) == nil && radius.getRaw(t) == nil { unused = false break } } if unused { radius.setRaw(tag, nil) result = append(result, tag) } } } equalValue := func(value1, value2 any) bool { switch value1 := value1.(type) { case string: switch value2 := value2.(type) { case string: return value1 == value2 } case SizeUnit: switch value2 := value2.(type) { case SizeUnit: return value1.Equal(value2) } } return false } for _, tag := range []PropertyName{TopLeft, TopRight, BottomLeft, BottomRight} { tagX := tag + "-x" tagY := tag + "-y" valueX := radius.getRaw(tagX) valueY := radius.getRaw(tagY) 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) { radius.setRaw(tagX, nil) result = append(result, tagX) } else { radius.setRaw(tagY, value) result = append(result, tagY) radius.setRaw(tag, nil) result = append(result, tag) } } else if valueX == nil && valueY != nil { if equalValue(value, valueY) { radius.setRaw(tagY, nil) result = append(result, tagY) } else { radius.setRaw(tagX, value) result = append(result, tagX) radius.setRaw(tag, nil) result = append(result, tag) } } } 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 radiusPropertySet(radius Properties, tag PropertyName, value any) []PropertyName { var result []PropertyName = nil deleteTags := func(tags []PropertyName) { for _, tag := range tags { if radius.getRaw(tag) != nil { radius.setRaw(tag, nil) result = append(result, tag) } } } switch tag { case X: 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) } radius.setRaw(t, nil) result = append(result, t) } } } case Y: 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) } radius.setRaw(t, nil) result = append(result, t) } } } case TopLeftX, TopLeftY, TopRightX, TopRightY, BottomLeftX, BottomLeftY, BottomRightX, BottomRightY: if result = setSizeProperty(radius, tag, value); result != nil { deleteRadiusUnusedTags(radius, result) } case TopLeft, TopRight, BottomLeft, BottomRight: switch value := value.(type) { case SizeUnit: 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 { if result = radiusPropertySet(radius, tag+"-x", values[0]); result != nil { if resultY := radiusPropertySet(radius, tag+"-y", values[1]); resultY != nil { result = append(result, resultY...) } } } else { notCompatibleType(tag, value) } } else { if result = setSizeProperty(radius, tag, value); result != nil { deleteTags([]PropertyName{tag + "-x", tag + "-y"}) deleteRadiusUnusedTags(radius, result) } } } default: ErrorLogF(`"%s" property is not compatible with the RadiusProperty`, tag) } return result } 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 := properties.getRaw(tag[:tagLen-2]); value != nil { return value } if value := properties.getRaw(tag[tagLen-1:]); value != nil { return value } } switch tag { case TopLeftX, TopRightX, BottomLeftX, BottomRightX: if value := properties.getRaw(X); value != nil { return value } case TopLeftY, TopRightY, BottomLeftY, BottomRightY: if value := properties.getRaw(Y); value != nil { return value } } return nil } func (radius *radiusPropertyData) BoxRadius(session Session) BoxRadius { x, _ := sizeProperty(radius, X, session) y, _ := sizeProperty(radius, Y, session) getRadius := func(tag PropertyName) (SizeUnit, SizeUnit) { rx := x ry := y if r, ok := sizeProperty(radius, tag, session); ok { rx = r ry = r } if r, ok := sizeProperty(radius, tag+"-x", session); ok { rx = r } if r, ok := sizeProperty(radius, tag+"-y", session); ok { ry = r } return rx, ry } var result BoxRadius result.TopLeftX, result.TopLeftY = getRadius(TopLeft) result.TopRightX, result.TopRightY = getRadius(TopRight) result.BottomLeftX, result.BottomLeftY = getRadius(BottomLeft) result.BottomRightX, result.BottomRightY = getRadius(BottomRight) return result } // BoxRadius defines radii of rounds the corners of an element's outer border edge type BoxRadius struct { TopLeftX, TopLeftY, TopRightX, TopRightY, BottomLeftX, BottomLeftY, BottomRightX, BottomRightY SizeUnit } // AllAnglesIsEqual returns 'true' if all angles is equal, 'false' otherwise func (radius BoxRadius) AllAnglesIsEqual() bool { return radius.TopLeftX.Equal(radius.TopRightX) && radius.TopLeftY.Equal(radius.TopRightY) && radius.TopLeftX.Equal(radius.BottomLeftX) && radius.TopLeftY.Equal(radius.BottomLeftY) && radius.TopLeftX.Equal(radius.BottomRightX) && radius.TopLeftY.Equal(radius.BottomRightY) } // String returns a string representation of a BoxRadius struct func (radius BoxRadius) String() string { if radius.AllAnglesIsEqual() { if radius.TopLeftX.Equal(radius.TopLeftY) { return radius.TopLeftX.String() } else { return fmt.Sprintf("_{ x = %s, y = %s }", radius.TopLeftX.String(), radius.TopLeftY.String()) } } buffer := allocStringBuilder() defer freeStringBuilder(buffer) buffer.WriteString("_{ ") if radius.TopLeftX.Equal(radius.TopLeftY) { buffer.WriteString("top-left = ") buffer.WriteString(radius.TopLeftX.String()) } else { buffer.WriteString("top-left-x = ") buffer.WriteString(radius.TopLeftX.String()) buffer.WriteString("top-left-y = ") buffer.WriteString(radius.TopLeftY.String()) } if radius.TopRightX.Equal(radius.TopRightY) { buffer.WriteString(", top-right = ") buffer.WriteString(radius.TopRightX.String()) } else { buffer.WriteString(", top-right-x = ") buffer.WriteString(radius.TopRightX.String()) buffer.WriteString(", top-right-y = ") buffer.WriteString(radius.TopRightY.String()) } if radius.BottomLeftX.Equal(radius.BottomLeftY) { buffer.WriteString(", bottom-left = ") buffer.WriteString(radius.BottomLeftX.String()) } else { buffer.WriteString(", bottom-left-x = ") buffer.WriteString(radius.BottomLeftX.String()) buffer.WriteString(", bottom-left-y = ") buffer.WriteString(radius.BottomLeftY.String()) } if radius.BottomRightX.Equal(radius.BottomRightY) { buffer.WriteString(", bottom-right = ") buffer.WriteString(radius.BottomRightX.String()) } else { buffer.WriteString(", bottom-right-x = ") buffer.WriteString(radius.BottomRightX.String()) buffer.WriteString(", bottom-right-y = ") buffer.WriteString(radius.BottomRightY.String()) } buffer.WriteString(" }") return buffer.String() } func (radius BoxRadius) cssValue(builder cssBuilder, session Session) { if (radius.TopLeftX.Type == Auto || radius.TopLeftX.Value == 0) && (radius.TopLeftY.Type == Auto || radius.TopLeftY.Value == 0) && (radius.TopRightX.Type == Auto || radius.TopRightX.Value == 0) && (radius.TopRightY.Type == Auto || radius.TopRightY.Value == 0) && (radius.BottomRightX.Type == Auto || radius.BottomRightX.Value == 0) && (radius.BottomRightY.Type == Auto || radius.BottomRightY.Value == 0) && (radius.BottomLeftX.Type == Auto || radius.BottomLeftX.Value == 0) && (radius.BottomLeftY.Type == Auto || radius.BottomLeftY.Value == 0) { return } buffer := allocStringBuilder() defer freeStringBuilder(buffer) buffer.WriteString(radius.TopLeftX.cssString("0", session)) if radius.AllAnglesIsEqual() { if !radius.TopLeftX.Equal(radius.TopLeftY) { buffer.WriteString(" / ") buffer.WriteString(radius.TopLeftY.cssString("0", session)) } } else { buffer.WriteRune(' ') buffer.WriteString(radius.TopRightX.cssString("0", session)) buffer.WriteRune(' ') buffer.WriteString(radius.BottomRightX.cssString("0", session)) buffer.WriteRune(' ') buffer.WriteString(radius.BottomLeftX.cssString("0", session)) if !radius.TopLeftX.Equal(radius.TopLeftY) || !radius.TopRightX.Equal(radius.TopRightY) || !radius.BottomLeftX.Equal(radius.BottomLeftY) || !radius.BottomRightX.Equal(radius.BottomRightY) { buffer.WriteString(" / ") buffer.WriteString(radius.TopLeftY.cssString("0", session)) buffer.WriteRune(' ') buffer.WriteString(radius.TopRightY.cssString("0", session)) buffer.WriteRune(' ') buffer.WriteString(radius.BottomRightY.cssString("0", session)) buffer.WriteRune(' ') buffer.WriteString(radius.BottomLeftY.cssString("0", session)) } } builder.add("border-radius", buffer.String()) } func (radius BoxRadius) cssString(session Session) string { var builder cssValueBuilder radius.cssValue(&builder, session) return builder.finish() } func getRadiusProperty(style Properties) RadiusProperty { if value := style.Get(Radius); value != nil { switch value := value.(type) { case RadiusProperty: return value case BoxRadius: result := NewRadiusProperty(nil) if value.AllAnglesIsEqual() { result.Set(X, value.TopLeftX) result.Set(Y, value.TopLeftY) } else { if value.TopLeftX.Equal(value.TopLeftY) { result.Set(TopLeft, value.TopLeftX) } else { result.Set(TopLeftX, value.TopLeftX) result.Set(TopLeftY, value.TopLeftY) } if value.TopRightX.Equal(value.TopRightY) { result.Set(TopRight, value.TopRightX) } else { result.Set(TopRightX, value.TopRightX) result.Set(TopRightY, value.TopRightY) } if value.BottomLeftX.Equal(value.BottomLeftY) { result.Set(BottomLeft, value.BottomLeftX) } else { result.Set(BottomLeftX, value.BottomLeftX) result.Set(BottomLeftY, value.BottomLeftY) } if value.BottomRightX.Equal(value.BottomRightY) { result.Set(BottomRight, value.BottomRightX) } else { result.Set(BottomRightX, value.BottomRightX) result.Set(BottomRightY, value.BottomRightY) } } return result case SizeUnit: return NewRadiusProperty(Params{ X: value, Y: value, }) case string: return NewRadiusProperty(Params{ X: value, Y: value, }) } } return NewRadiusProperty(nil) } func setRadiusProperty(properties Properties, value any) []PropertyName { if value == nil { return propertiesRemove(properties, Radius) } switch value := value.(type) { case RadiusProperty: properties.setRaw(Radius, value) case SizeUnit: properties.setRaw(Radius, value) case BoxRadius: radius := NewRadiusProperty(nil) if value.AllAnglesIsEqual() { radius.Set(X, value.TopLeftX) radius.Set(Y, value.TopLeftY) } else { if value.TopLeftX.Equal(value.TopLeftY) { radius.Set(TopLeft, value.TopLeftX) } else { radius.Set(TopLeftX, value.TopLeftX) radius.Set(TopLeftY, value.TopLeftY) } if value.TopRightX.Equal(value.TopRightY) { radius.Set(TopRight, value.TopRightX) } else { radius.Set(TopRightX, value.TopRightX) radius.Set(TopRightY, value.TopRightY) } if value.BottomLeftX.Equal(value.BottomLeftY) { radius.Set(BottomLeft, value.BottomLeftX) } else { radius.Set(BottomLeftX, value.BottomLeftX) radius.Set(BottomLeftY, value.BottomLeftY) } if value.BottomRightX.Equal(value.BottomRightY) { radius.Set(BottomRight, value.BottomRightX) } else { radius.Set(BottomRightX, value.BottomRightX) radius.Set(BottomRightY, value.BottomRightY) } } properties.setRaw(Radius, radius) case string: if strings.Contains(value, "/") { values := strings.Split(value, "/") if len(values) == 2 { 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 setSizeProperty(properties, Radius, value) } case DataObject: radius := NewRadiusProperty(nil) for _, tag := range []PropertyName{X, Y, TopLeft, TopRight, BottomLeft, BottomRight, TopLeftX, TopLeftY, TopRightX, TopRightY, BottomLeftX, BottomLeftY, BottomRightX, BottomRightY} { if value, ok := value.PropertyValue(string(tag)); ok { radius.Set(tag, value) } } properties.setRaw(Radius, radius) case float32: properties.setRaw(Radius, Px(float64(value))) case float64: properties.setRaw(Radius, Px(value)) default: if n, ok := isInt(value); ok { properties.setRaw(Radius, Px(float64(n))) } else { notCompatibleType(Radius, value) return nil } } 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 setRadiusPropertyElement(properties Properties, tag PropertyName, value any) bool { if value == nil { removeRadiusPropertyElement(properties, tag) return true } radius := getRadiusProperty(properties) if radius.Set(tag, value) { properties.setRaw(Radius, radius) return true } return false } func getRadiusElement(style Properties, tag PropertyName) any { value := style.Get(Radius) if value != nil { switch value := value.(type) { case string: return value case SizeUnit: return value case RadiusProperty: return value.Get(tag) case BoxRadius: switch tag { case RadiusX: if value.TopLeftX.Equal(value.TopRightX) && value.TopLeftX.Equal(value.BottomLeftX) && value.TopLeftX.Equal(value.BottomRightX) { return value.TopLeftX } case RadiusY: if value.TopLeftY.Equal(value.TopRightY) && value.TopLeftY.Equal(value.BottomLeftY) && value.TopLeftY.Equal(value.BottomRightY) { return value.TopLeftY } case RadiusTopLeft: if value.TopLeftX.Equal(value.TopLeftY) { return value.TopLeftY } case RadiusTopRight: if value.TopRightX.Equal(value.TopRightY) { return value.TopRightY } case RadiusBottomLeft: if value.BottomLeftX.Equal(value.BottomLeftY) { return value.BottomLeftY } case RadiusBottomRight: if value.BottomRightX.Equal(value.BottomRightY) { return value.BottomRightY } case RadiusTopLeftX: return value.TopLeftX case RadiusTopLeftY: return value.TopLeftY case RadiusTopRightX: return value.TopRightX case RadiusTopRightY: return value.TopRightY case RadiusBottomLeftX: return value.BottomLeftX case RadiusBottomLeftY: return value.BottomLeftY case RadiusBottomRightX: return value.BottomRightX case RadiusBottomRightY: return value.BottomRightY } } } return nil } func getRadius(properties Properties, session Session) BoxRadius { if value := properties.Get(Radius); value != nil { switch value := value.(type) { case BoxRadius: return value case RadiusProperty: return value.BoxRadius(session) case SizeUnit: return BoxRadius{TopLeftX: value, TopLeftY: value, TopRightX: value, TopRightY: value, BottomLeftX: value, BottomLeftY: value, BottomRightX: value, BottomRightY: value} case string: if text, ok := session.resolveConstants(value); ok { if size, ok := StringToSizeUnit(text); ok { return BoxRadius{TopLeftX: size, TopLeftY: size, TopRightX: size, TopRightY: size, BottomLeftX: size, BottomLeftY: size, BottomRightX: size, BottomRightY: size} } } } } return BoxRadius{} }