rui_orig/shadow.go

403 lines
13 KiB
Go

package rui
import (
"fmt"
"strings"
)
// Constants for [ShadowProperty] specific properties
const (
// ColorTag is the constant for "color" property tag.
//
// Used by `ColumnSeparatorProperty`, `BorderProperty`, `OutlineProperty`, `ShadowProperty`.
//
// Usage in `ColumnSeparatorProperty`:
// Line color.
//
// Supported types: `Color`, `string`.
//
// Internal type is `Color`, other types converted to it during assignment.
// See `Color` description for more details.
//
// Usage in `BorderProperty`:
// Border line color.
//
// Supported types: `Color`, `string`.
//
// Internal type is `Color`, other types converted to it during assignment.
// See `Color` description for more details.
//
// Usage in `OutlineProperty`:
// Outline line color.
//
// Supported types: `Color`, `string`.
//
// Internal type is `Color`, other types converted to it during assignment.
// See `Color` description for more details.
//
// Usage in `ShadowProperty`:
// Color property of the shadow.
//
// Supported types: `Color`, `string`.
//
// Internal type is `Color`, other types converted to it during assignment.
// See `Color` description for more details.
ColorTag PropertyName = "color"
// Inset is the constant for "inset" property tag.
//
// Used by `ShadowProperty`.
// Controls whether to draw shadow inside the frame or outside. Inset shadows are drawn inside the border(even transparent
// ones), above the background, but below content.
//
// Supported types: `bool`, `int`, `string`.
//
// 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 PropertyName = "inset"
// XOffset is the constant for "x-offset" property tag.
//
// Used by `ShadowProperty`.
// Determines the shadow horizontal offset. Negative values place the shadow to the left of the element.
//
// Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`.
//
// Internal type is `SizeUnit`, other types converted to it during assignment.
// See `SizeUnit` description for more details.
XOffset PropertyName = "x-offset"
// YOffset is the constant for "y-offset" property tag.
//
// Used by `ShadowProperty`.
// Determines the shadow vertical offset. Negative values place the shadow above the element.
//
// Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`.
//
// Internal type is `SizeUnit`, other types converted to it during assignment.
// See `SizeUnit` description for more details.
YOffset PropertyName = "y-offset"
// BlurRadius is the constant for "blur" property tag.
//
// Used by `ShadowProperty`.
// Determines the radius of the blur effect. The larger this value, the bigger the blur, so the shadow becomes bigger and
// lighter. Negative values are not allowed.
//
// Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`.
//
// Internal type is `SizeUnit`, other types converted to it during assignment.
// See `SizeUnit` description for more details.
BlurRadius PropertyName = "blur"
// SpreadRadius is the constant for "spread-radius" property tag.
//
// Used by `ShadowProperty`.
// Positive values will cause the shadow to expand and grow bigger, negative values will cause the shadow to shrink.
//
// Supported types: `SizeUnit`, `SizeFunc`, `string`, `float`, `int`.
//
// Internal type is `SizeUnit`, other types converted to it during assignment.
// See `SizeUnit` description for more details.
SpreadRadius PropertyName = "spread-radius"
)
// ShadowProperty contains attributes of the view shadow
type ShadowProperty interface {
Properties
fmt.Stringer
stringWriter
cssStyle(buffer *strings.Builder, session Session, lead string) bool
cssTextStyle(buffer *strings.Builder, session Session, lead string) bool
visible(session Session) bool
}
type shadowPropertyData struct {
dataProperty
}
// NewShadow create the new shadow property for a view. Arguments:
//
// offsetX, offsetY is x and y offset of the shadow (if the argument is specified as int or float64, the value is considered to be in pixels);
//
// blurRadius is the blur radius of the shadow (if the argument is specified as int or float64, the value is considered to be in pixels);
//
// spreadRadius is the spread radius of the shadow (if the argument is specified as int or float64, the value is considered to be in pixels);
//
// color is the color of the shadow.
func NewShadow[xOffsetType SizeUnit | int | float64, yOffsetType SizeUnit | int | float64, blurType SizeUnit | int | float64, spreadType SizeUnit | int | float64](
xOffset xOffsetType, yOffset yOffsetType, blurRadius blurType, spreadRadius spreadType, color Color) ShadowProperty {
return NewShadowProperty(Params{
XOffset: xOffset,
YOffset: yOffset,
BlurRadius: blurRadius,
SpreadRadius: spreadRadius,
ColorTag: color,
})
}
// NewInsetShadow create the new inset shadow property for a view. Arguments:
//
// offsetX, offsetY is x and y offset of the shadow (if the argument is specified as int or float64, the value is considered to be in pixels);
//
// blurRadius is the blur radius of the shadow (if the argument is specified as int or float64, the value is considered to be in pixels);
//
// spreadRadius is the spread radius of the shadow (if the argument is specified as int or float64, the value is considered to be in pixels);
//
// color is the color of the shadow.
func NewInsetShadow[xOffsetType SizeUnit | int | float64, yOffsetType SizeUnit | int | float64, blurType SizeUnit | int | float64, spreadType SizeUnit | int | float64](
xOffset xOffsetType, yOffset yOffsetType, blurRadius blurType, spreadRadius spreadType, color Color) ShadowProperty {
return NewShadowProperty(Params{
XOffset: xOffset,
YOffset: yOffset,
BlurRadius: blurRadius,
SpreadRadius: spreadRadius,
ColorTag: color,
Inset: true,
})
}
// NewTextShadow create the new text shadow property. Arguments:
//
// offsetX, offsetY is the x- and y-offset of the shadow (if the argument is specified as int or float64, the value is considered to be in pixels);
//
// blurRadius is the blur radius of the shadow (if the argument is specified as int or float64, the value is considered to be in pixels);
//
// color is the color of the shadow.
func NewTextShadow[xOffsetType SizeUnit | int | float64, yOffsetType SizeUnit | int | float64, blurType SizeUnit | int | float64](
xOffset xOffsetType, yOffset yOffsetType, blurRadius blurType, color Color) ShadowProperty {
return NewShadowProperty(Params{
XOffset: xOffset,
YOffset: yOffset,
BlurRadius: blurRadius,
ColorTag: color,
})
}
// NewShadowProperty create the new shadow property for a view.
// The following properties can be used:
//
// "color" (ColorTag). Determines the color of the shadow (Color);
//
// "x-offset" (XOffset). Determines the shadow horizontal offset (SizeUnit);
//
// "y-offset" (YOffset). Determines the shadow vertical offset (SizeUnit);
//
// "blur" (BlurRadius). Determines the radius of the blur effect (SizeUnit);
//
// "spread-radius" (SpreadRadius). Positive values (SizeUnit) will cause the shadow to expand and grow bigger, negative values will cause the shadow to shrink;
//
// "inset" (Inset). Controls (bool) whether to draw shadow inside the frame or outside.
func NewShadowProperty(params Params) ShadowProperty {
shadow := new(shadowPropertyData)
shadow.init()
if params != nil {
for _, tag := range []PropertyName{ColorTag, Inset, XOffset, YOffset, BlurRadius, SpreadRadius} {
if value, ok := params[tag]; ok && value != nil {
shadow.set(shadow, tag, value)
}
}
}
return shadow
}
// parseShadowProperty parse DataObject and create ShadowProperty object
func parseShadowProperty(object DataObject) ShadowProperty {
shadow := new(shadowPropertyData)
shadow.init()
parseProperties(shadow, object)
return shadow
}
func (shadow *shadowPropertyData) init() {
shadow.dataProperty.init()
shadow.supportedProperties = []PropertyName{ColorTag, Inset, XOffset, YOffset, BlurRadius, SpreadRadius}
}
func (shadow *shadowPropertyData) cssStyle(buffer *strings.Builder, session Session, lead string) bool {
color, _ := colorProperty(shadow, ColorTag, session)
offsetX, _ := sizeProperty(shadow, XOffset, session)
offsetY, _ := sizeProperty(shadow, YOffset, session)
blurRadius, _ := sizeProperty(shadow, BlurRadius, session)
spreadRadius, _ := sizeProperty(shadow, SpreadRadius, session)
if color.Alpha() == 0 ||
((offsetX.Type == Auto || offsetX.Value == 0) &&
(offsetY.Type == Auto || offsetY.Value == 0) &&
(blurRadius.Type == Auto || blurRadius.Value == 0) &&
(spreadRadius.Type == Auto || spreadRadius.Value == 0)) {
return false
}
buffer.WriteString(lead)
if inset, _ := boolProperty(shadow, Inset, session); inset {
buffer.WriteString("inset ")
}
buffer.WriteString(offsetX.cssString("0", session))
buffer.WriteByte(' ')
buffer.WriteString(offsetY.cssString("0", session))
buffer.WriteByte(' ')
buffer.WriteString(blurRadius.cssString("0", session))
buffer.WriteByte(' ')
buffer.WriteString(spreadRadius.cssString("0", session))
buffer.WriteByte(' ')
buffer.WriteString(color.cssString())
return true
}
func (shadow *shadowPropertyData) cssTextStyle(buffer *strings.Builder, session Session, lead string) bool {
color, _ := colorProperty(shadow, ColorTag, session)
offsetX, _ := sizeProperty(shadow, XOffset, session)
offsetY, _ := sizeProperty(shadow, YOffset, session)
blurRadius, _ := sizeProperty(shadow, BlurRadius, session)
if color.Alpha() == 0 ||
((offsetX.Type == Auto || offsetX.Value == 0) &&
(offsetY.Type == Auto || offsetY.Value == 0) &&
(blurRadius.Type == Auto || blurRadius.Value == 0)) {
return false
}
buffer.WriteString(lead)
buffer.WriteString(offsetX.cssString("0", session))
buffer.WriteByte(' ')
buffer.WriteString(offsetY.cssString("0", session))
buffer.WriteByte(' ')
buffer.WriteString(blurRadius.cssString("0", session))
buffer.WriteByte(' ')
buffer.WriteString(color.cssString())
return true
}
func (shadow *shadowPropertyData) visible(session Session) bool {
color, _ := colorProperty(shadow, ColorTag, session)
offsetX, _ := sizeProperty(shadow, XOffset, session)
offsetY, _ := sizeProperty(shadow, YOffset, session)
blurRadius, _ := sizeProperty(shadow, BlurRadius, session)
spreadRadius, _ := sizeProperty(shadow, SpreadRadius, session)
if color.Alpha() == 0 ||
((offsetX.Type == Auto || offsetX.Value == 0) &&
(offsetY.Type == Auto || offsetY.Value == 0) &&
(blurRadius.Type == Auto || blurRadius.Value == 0) &&
(spreadRadius.Type == Auto || spreadRadius.Value == 0)) {
return false
}
return true
}
func (shadow *shadowPropertyData) String() string {
return runStringWriter(shadow)
}
func (shadow *shadowPropertyData) writeString(buffer *strings.Builder, indent string) {
buffer.WriteString("_{ ")
comma := false
for _, tag := range shadow.AllTags() {
if value, ok := shadow.properties[tag]; ok {
if comma {
buffer.WriteString(", ")
}
buffer.WriteString(string(tag))
buffer.WriteString(" = ")
writePropertyValue(buffer, tag, value, indent)
comma = true
}
}
buffer.WriteString(" }")
}
func setShadowProperty(properties Properties, tag PropertyName, value any) bool {
if value == nil {
properties.setRaw(tag, nil)
return true
}
switch value := value.(type) {
case ShadowProperty:
properties.setRaw(tag, []ShadowProperty{value})
case []ShadowProperty:
if len(value) == 0 {
properties.setRaw(tag, nil)
} else {
properties.setRaw(tag, value)
}
case DataValue:
if !value.IsObject() {
return false
}
properties.setRaw(tag, []ShadowProperty{parseShadowProperty(value.Object())})
case []DataValue:
shadows := []ShadowProperty{}
for _, data := range value {
if data.IsObject() {
shadows = append(shadows, parseShadowProperty(data.Object()))
}
}
if len(shadows) == 0 {
return false
}
properties.setRaw(tag, shadows)
case string:
obj := NewDataObject(value)
if obj == nil {
notCompatibleType(tag, value)
return false
}
properties.setRaw(tag, []ShadowProperty{parseShadowProperty(obj)})
default:
notCompatibleType(tag, value)
return false
}
return true
}
func getShadows(properties Properties, tag PropertyName) []ShadowProperty {
if value := properties.Get(tag); value != nil {
switch value := value.(type) {
case []ShadowProperty:
return value
case ShadowProperty:
return []ShadowProperty{value}
}
}
return []ShadowProperty{}
}
func shadowCSS(properties Properties, tag PropertyName, session Session) string {
shadows := getShadows(properties, tag)
if len(shadows) == 0 {
return ""
}
buffer := allocStringBuilder()
defer freeStringBuilder(buffer)
lead := ""
if tag == Shadow {
for _, shadow := range shadows {
if shadow.cssStyle(buffer, session, lead) {
lead = ", "
}
}
} else {
for _, shadow := range shadows {
if shadow.cssTextStyle(buffer, session, lead) {
lead = ", "
}
}
}
return buffer.String()
}