rui_orig/backgroundLinearGradient.go

414 lines
11 KiB
Go
Raw Normal View History

2022-04-30 12:13:16 +03:00
package rui
import "strings"
2024-12-04 18:45:08 +03:00
type LinearGradientDirectionType int
// Constants related to view's background gradient description
2022-04-30 12:13:16 +03:00
const (
// ToTopGradient is value of the Direction property of a linear gradient. The value is equivalent to the 0deg angle
2024-12-04 18:45:08 +03:00
ToTopGradient LinearGradientDirectionType = 0
2024-12-01 12:42:38 +03:00
2022-04-30 12:13:16 +03:00
// ToRightTopGradient is value of the Direction property of a linear gradient.
2024-12-04 18:45:08 +03:00
ToRightTopGradient LinearGradientDirectionType = 1
2024-12-01 12:42:38 +03:00
2022-04-30 12:13:16 +03:00
// ToRightGradient is value of the Direction property of a linear gradient. The value is equivalent to the 90deg angle
2024-12-04 18:45:08 +03:00
ToRightGradient LinearGradientDirectionType = 2
2024-12-01 12:42:38 +03:00
2022-04-30 12:13:16 +03:00
// ToRightBottomGradient is value of the Direction property of a linear gradient.
2024-12-04 18:45:08 +03:00
ToRightBottomGradient LinearGradientDirectionType = 3
2024-12-01 12:42:38 +03:00
2022-04-30 12:13:16 +03:00
// ToBottomGradient is value of the Direction property of a linear gradient. The value is equivalent to the 180deg angle
2024-12-04 18:45:08 +03:00
ToBottomGradient LinearGradientDirectionType = 4
2024-12-01 12:42:38 +03:00
2022-04-30 12:13:16 +03:00
// ToLeftBottomGradient is value of the Direction property of a linear gradient.
2024-12-04 18:45:08 +03:00
ToLeftBottomGradient LinearGradientDirectionType = 5
2024-12-01 12:42:38 +03:00
2022-04-30 12:13:16 +03:00
// ToLeftGradient is value of the Direction property of a linear gradient. The value is equivalent to the 270deg angle
2024-12-04 18:45:08 +03:00
ToLeftGradient LinearGradientDirectionType = 6
2024-12-01 12:42:38 +03:00
2022-04-30 12:13:16 +03:00
// ToLeftTopGradient is value of the Direction property of a linear gradient.
2024-12-04 18:45:08 +03:00
ToLeftTopGradient LinearGradientDirectionType = 7
2022-04-30 12:13:16 +03:00
)
// BackgroundGradientPoint define point on gradient straight line
type BackgroundGradientPoint struct {
2022-04-30 18:02:01 +03:00
// Color - the color of the point. Must not be nil.
// Can take a value of Color type or string (color constant or textual description of the color)
2022-07-26 18:36:00 +03:00
Color any
2022-04-30 18:02:01 +03:00
// Pos - the distance from the start of the gradient straight line. Optional (may be nil).
2024-12-05 20:15:39 +03:00
// Can take a value of SizeUnit type or string (size constant or textual description of the SizeUnit)
2022-07-26 18:36:00 +03:00
Pos any
2022-04-30 12:13:16 +03:00
}
type backgroundGradient struct {
backgroundElement
}
type backgroundLinearGradient struct {
backgroundGradient
}
2024-12-04 18:45:08 +03:00
// NewBackgroundLinearGradient creates the new background linear gradient.
//
2024-12-05 20:15:39 +03:00
// The following properties can be used:
// - "gradient" [Gradient] - Describes gradient stop points. This is a mandatory property while describing background gradients.
// - "direction" [Direction] - Defines the direction of the gradient line.
// - "repeating" [Repeating] - Defines whether stop points needs to be repeated after the last one.
2022-04-30 12:13:16 +03:00
func NewBackgroundLinearGradient(params Params) BackgroundElement {
result := new(backgroundLinearGradient)
2024-11-13 12:56:39 +03:00
result.init()
2022-04-30 12:13:16 +03:00
for tag, value := range params {
result.Set(tag, value)
}
return result
}
2024-12-04 18:45:08 +03:00
// NewLinearGradient creates the new background linear gradient.
func NewLinearGradient[DirectionType LinearGradientDirectionType | AngleUnit](direction DirectionType, repeating bool, point1 GradientPoint, point2 GradientPoint, points ...GradientPoint) BackgroundElement {
params := Params{
Direction: direction,
Gradient: append([]GradientPoint{point1, point2}, points...),
}
if repeating {
params[Repeating] = true
}
return NewBackgroundLinearGradient(params)
}
2024-11-13 12:56:39 +03:00
func parseGradientText(value string) []BackgroundGradientPoint {
2022-04-30 18:02:01 +03:00
elements := strings.Split(value, ",")
count := len(elements)
if count < 2 {
ErrorLog("The gradient must contain at least 2 points")
return nil
}
points := make([]BackgroundGradientPoint, count)
for i, element := range elements {
if !points[i].setValue(element) {
2022-11-23 15:10:29 +03:00
ErrorLogF(`Invalid %d element of the conic gradient: "%s"`, i, element)
2022-04-30 18:02:01 +03:00
return nil
}
}
return points
}
2024-11-13 12:56:39 +03:00
func backgroundGradientSet(properties Properties, tag PropertyName, value any) []PropertyName {
2022-04-30 12:13:16 +03:00
2024-11-13 12:56:39 +03:00
switch tag {
2022-04-30 12:13:16 +03:00
case Repeating:
2024-11-13 12:56:39 +03:00
return setBoolProperty(properties, tag, value)
2022-04-30 12:13:16 +03:00
case Gradient:
switch value := value.(type) {
case string:
if value != "" {
2022-04-30 18:02:01 +03:00
if strings.Contains(value, " ") || strings.Contains(value, ",") {
2024-11-13 12:56:39 +03:00
if points := parseGradientText(value); len(points) >= 2 {
properties.setRaw(Gradient, points)
return []PropertyName{tag}
2022-04-30 12:13:16 +03:00
}
2022-04-30 18:02:01 +03:00
} else if value[0] == '@' {
2024-11-13 12:56:39 +03:00
properties.setRaw(Gradient, value)
return []PropertyName{tag}
2022-04-30 12:13:16 +03:00
}
}
case []BackgroundGradientPoint:
if len(value) >= 2 {
2024-11-13 12:56:39 +03:00
properties.setRaw(Gradient, value)
return []PropertyName{tag}
2022-04-30 12:13:16 +03:00
}
case []Color:
count := len(value)
if count >= 2 {
points := make([]BackgroundGradientPoint, count)
for i, color := range value {
points[i].Color = color
}
2024-11-13 12:56:39 +03:00
properties.setRaw(Gradient, points)
return []PropertyName{tag}
2022-04-30 12:13:16 +03:00
}
case []GradientPoint:
count := len(value)
if count >= 2 {
points := make([]BackgroundGradientPoint, count)
for i, point := range value {
points[i].Color = point.Color
points[i].Pos = Percent(point.Offset * 100)
}
2024-11-13 12:56:39 +03:00
properties.setRaw(Gradient, points)
return []PropertyName{tag}
2022-04-30 12:13:16 +03:00
}
2022-04-30 18:02:01 +03:00
}
2022-04-30 12:13:16 +03:00
2022-04-30 18:02:01 +03:00
ErrorLogF("Invalid gradient %v", value)
2024-11-13 12:56:39 +03:00
return nil
2022-04-30 18:02:01 +03:00
}
2022-05-01 13:27:04 +03:00
ErrorLogF("Property %s is not supported by a background gradient", tag)
2024-11-13 12:56:39 +03:00
return nil
2022-04-30 18:02:01 +03:00
}
2022-04-30 12:13:16 +03:00
2022-04-30 18:02:01 +03:00
func (point *BackgroundGradientPoint) setValue(text string) bool {
text = strings.Trim(text, " ")
2022-04-30 12:13:16 +03:00
2022-04-30 18:02:01 +03:00
colorText := text
pointText := ""
2022-04-30 12:13:16 +03:00
2022-04-30 18:02:01 +03:00
if index := strings.Index(text, " "); index > 0 {
colorText = text[:index]
pointText = strings.Trim(text[index+1:], " ")
}
2022-04-30 12:13:16 +03:00
2022-04-30 18:02:01 +03:00
if colorText == "" {
return false
}
2022-04-30 12:13:16 +03:00
2022-04-30 18:02:01 +03:00
if colorText[0] == '@' {
point.Color = colorText
} else if color, ok := StringToColor(colorText); ok {
point.Color = color
} else {
return false
2022-04-30 12:13:16 +03:00
}
2022-04-30 18:02:01 +03:00
if pointText == "" {
point.Pos = nil
} else if pointText[0] == '@' {
point.Pos = pointText
} else if pos, ok := StringToSizeUnit(pointText); ok {
point.Pos = pos
} else {
return false
}
2022-04-30 12:13:16 +03:00
2022-04-30 18:02:01 +03:00
return true
}
2022-04-30 12:13:16 +03:00
2022-04-30 18:02:01 +03:00
func (point *BackgroundGradientPoint) color(session Session) (Color, bool) {
if point.Color != nil {
switch color := point.Color.(type) {
case string:
if color != "" {
if color[0] == '@' {
if clr, ok := session.Color(color[1:]); ok {
return clr, true
}
} else {
if clr, ok := StringToColor(color); ok {
return clr, true
}
}
}
2022-04-30 12:13:16 +03:00
2022-04-30 18:02:01 +03:00
case Color:
return color, true
2024-11-27 11:32:13 +03:00
default:
if n, ok := isInt(point.Color); ok {
return Color(n), true
}
2022-04-30 12:13:16 +03:00
}
}
2022-04-30 18:02:01 +03:00
return 0, false
2022-04-30 12:13:16 +03:00
}
// String convert internal representation of [BackgroundGradientPoint] into a string.
2024-04-22 19:14:58 +03:00
func (point *BackgroundGradientPoint) String() string {
result := "black"
if point.Color != nil {
switch color := point.Color.(type) {
case string:
result = color
case Color:
result = color.String()
}
}
if point.Pos != nil {
switch value := point.Pos.(type) {
case string:
result += " " + value
case SizeUnit:
if value.Type != Auto {
result += " " + value.String()
}
}
}
return result
}
2022-04-30 12:13:16 +03:00
func (gradient *backgroundGradient) writeGradient(session Session, buffer *strings.Builder) bool {
value, ok := gradient.properties[Gradient]
if !ok {
return false
}
2022-04-30 18:02:01 +03:00
var points []BackgroundGradientPoint = nil
2022-04-30 12:13:16 +03:00
switch value := value.(type) {
case string:
2022-04-30 18:02:01 +03:00
if value != "" && value[0] == '@' {
if text, ok := session.Constant(value[1:]); ok {
2024-11-13 12:56:39 +03:00
points = parseGradientText(text)
2022-04-30 12:13:16 +03:00
}
}
case []BackgroundGradientPoint:
points = value
}
if len(points) > 0 {
for i, point := range points {
if i > 0 {
buffer.WriteString(`, `)
}
2022-04-30 18:02:01 +03:00
if color, ok := point.color(session); ok {
buffer.WriteString(color.cssString())
} else {
return false
}
if point.Pos != nil {
switch value := point.Pos.(type) {
case string:
if value != "" {
if value, ok := session.resolveConstants(value); ok {
if pos, ok := StringToSizeUnit(value); ok && pos.Type != Auto {
buffer.WriteRune(' ')
buffer.WriteString(pos.cssString("", session))
}
2022-04-30 18:02:01 +03:00
}
}
case SizeUnit:
if value.Type != Auto {
buffer.WriteRune(' ')
buffer.WriteString(value.cssString("", session))
2022-04-30 18:02:01 +03:00
}
}
2022-04-30 12:13:16 +03:00
}
}
return true
}
return false
}
2024-11-13 12:56:39 +03:00
func (gradient *backgroundLinearGradient) init() {
gradient.backgroundElement.init()
gradient.set = backgroundLinearGradientSet
2024-11-27 11:32:13 +03:00
gradient.supportedProperties = []PropertyName{Direction, Repeating, Gradient}
2024-11-13 12:56:39 +03:00
}
2022-04-30 12:13:16 +03:00
func (gradient *backgroundLinearGradient) Tag() string {
return "linear-gradient"
}
func (image *backgroundLinearGradient) Clone() BackgroundElement {
result := NewBackgroundLinearGradient(nil)
for tag, value := range image.properties {
result.setRaw(tag, value)
}
return result
}
2024-11-13 12:56:39 +03:00
func backgroundLinearGradientSet(properties Properties, tag PropertyName, value any) []PropertyName {
if tag == Direction {
2022-04-30 12:13:16 +03:00
switch value := value.(type) {
case AngleUnit:
2024-11-13 12:56:39 +03:00
properties.setRaw(Direction, value)
return []PropertyName{tag}
2022-04-30 12:13:16 +03:00
case string:
2024-11-13 12:56:39 +03:00
if setSimpleProperty(properties, tag, value) {
return []PropertyName{tag}
2022-05-01 13:27:04 +03:00
}
if angle, ok := StringToAngleUnit(value); ok {
2024-11-13 12:56:39 +03:00
properties.setRaw(Direction, angle)
return []PropertyName{tag}
2022-04-30 12:13:16 +03:00
}
2024-12-04 18:45:08 +03:00
case LinearGradientDirectionType:
return setEnumProperty(properties, tag, int(value), enumProperties[Direction].values)
2022-04-30 12:13:16 +03:00
}
2024-11-13 12:56:39 +03:00
return setEnumProperty(properties, tag, value, enumProperties[Direction].values)
2022-04-30 12:13:16 +03:00
}
2024-11-13 12:56:39 +03:00
return backgroundGradientSet(properties, tag, value)
2022-04-30 12:13:16 +03:00
}
func (gradient *backgroundLinearGradient) cssStyle(session Session) string {
buffer := allocStringBuilder()
defer freeStringBuilder(buffer)
if repeating, _ := boolProperty(gradient, Repeating, session); repeating {
buffer.WriteString(`repeating-linear-gradient(`)
} else {
buffer.WriteString(`linear-gradient(`)
}
if value, ok := gradient.properties[Direction]; ok {
switch value := value.(type) {
case string:
if text, ok := session.resolveConstants(value); ok {
direction := enumProperties[Direction]
if n, ok := enumStringToInt(text, direction.values, false); ok {
buffer.WriteString(direction.cssValues[n])
buffer.WriteString(", ")
} else {
if angle, ok := StringToAngleUnit(text); ok {
buffer.WriteString(angle.cssString())
buffer.WriteString(", ")
} else {
ErrorLog(`Invalid linear gradient direction: ` + text)
}
}
} else {
ErrorLog(`Invalid linear gradient direction: ` + value)
}
case int:
values := enumProperties[Direction].cssValues
if value >= 0 && value < len(values) {
buffer.WriteString(values[value])
buffer.WriteString(", ")
} else {
ErrorLogF(`Invalid linear gradient direction: %d`, value)
}
case AngleUnit:
buffer.WriteString(value.cssString())
buffer.WriteString(", ")
}
}
if !gradient.writeGradient(session, buffer) {
return ""
}
buffer.WriteString(") ")
return buffer.String()
}
2024-04-22 19:14:58 +03:00
func (gradient *backgroundLinearGradient) writeString(buffer *strings.Builder, indent string) {
2024-11-13 12:56:39 +03:00
gradient.writeToBuffer(buffer, indent, gradient.Tag(), []PropertyName{
2024-04-22 19:14:58 +03:00
Gradient,
Repeating,
Direction,
})
}
func (gradient *backgroundLinearGradient) String() string {
2024-04-22 20:03:40 +03:00
return runStringWriter(gradient)
2024-04-22 19:14:58 +03:00
}