rui_orig/border.go

737 lines
19 KiB
Go

package rui
import (
"fmt"
"strings"
)
const (
// NoneLine constant specifies that there is no border
NoneLine = 0
// SolidLine constant specifies the border/line as a solid line
SolidLine = 1
// DashedLine constant specifies the border/line as a dashed line
DashedLine = 2
// DottedLine constant specifies the border/line as a dotted line
DottedLine = 3
// DoubleLine constant specifies the border/line as a double solid line
DoubleLine = 4
// DoubleLine constant specifies the border/line as a double solid line
WavyLine = 5
// LeftStyle is the constant for "left-style" property tag.
LeftStyle = "left-style"
// RightStyle is the constant for "-right-style" property tag.
RightStyle = "right-style"
// TopStyle is the constant for "top-style" property tag.
TopStyle = "top-style"
// BottomStyle is the constant for "bottom-style" property tag.
BottomStyle = "bottom-style"
// LeftWidth is the constant for "left-width" property tag.
LeftWidth = "left-width"
// RightWidth is the constant for "-right-width" property tag.
RightWidth = "right-width"
// TopWidth is the constant for "top-width" property tag.
TopWidth = "top-width"
// BottomWidth is the constant for "bottom-width" property tag.
BottomWidth = "bottom-width"
// LeftColor is the constant for "left-color" property tag.
LeftColor = "left-color"
// RightColor is the constant for "-right-color" property tag.
RightColor = "right-color"
// TopColor is the constant for "top-color" property tag.
TopColor = "top-color"
// BottomColor is the constant for "bottom-color" property tag.
BottomColor = "bottom-color"
)
// BorderProperty is the interface of a view border data
type BorderProperty interface {
Properties
fmt.Stringer
stringWriter
ViewBorders(session Session) ViewBorders
delete(tag string)
cssStyle(builder cssBuilder, session Session)
cssWidth(builder cssBuilder, session Session)
cssColor(builder cssBuilder, session Session)
cssStyleValue(session Session) string
cssWidthValue(session Session) string
cssColorValue(session Session) string
}
type borderProperty struct {
propertyList
}
func newBorderProperty(value any) BorderProperty {
border := new(borderProperty)
border.properties = map[string]any{}
if value != nil {
switch value := value.(type) {
case BorderProperty:
return value
case DataNode:
if value.Type() == ObjectNode {
_ = border.setBorderObject(value.Object())
} else {
return nil
}
case DataObject:
_ = border.setBorderObject(value)
case ViewBorder:
border.properties[Style] = value.Style
border.properties[Width] = value.Width
border.properties[ColorTag] = value.Color
case ViewBorders:
if value.Left.Style == value.Right.Style &&
value.Left.Style == value.Top.Style &&
value.Left.Style == value.Bottom.Style {
border.properties[Style] = value.Left.Style
} else {
border.properties[LeftStyle] = value.Left.Style
border.properties[RightStyle] = value.Right.Style
border.properties[TopStyle] = value.Top.Style
border.properties[BottomStyle] = value.Bottom.Style
}
if value.Left.Width.Equal(value.Right.Width) &&
value.Left.Width.Equal(value.Top.Width) &&
value.Left.Width.Equal(value.Bottom.Width) {
border.properties[Width] = value.Left.Width
} else {
border.properties[LeftWidth] = value.Left.Width
border.properties[RightWidth] = value.Right.Width
border.properties[TopWidth] = value.Top.Width
border.properties[BottomWidth] = value.Bottom.Width
}
if value.Left.Color == value.Right.Color &&
value.Left.Color == value.Top.Color &&
value.Left.Color == value.Bottom.Color {
border.properties[ColorTag] = value.Left.Color
} else {
border.properties[LeftColor] = value.Left.Color
border.properties[RightColor] = value.Right.Color
border.properties[TopColor] = value.Top.Color
border.properties[BottomColor] = value.Bottom.Color
}
default:
invalidPropertyValue(Border, value)
return nil
}
}
return border
}
// NewBorder creates the new BorderProperty
func NewBorder(params Params) BorderProperty {
border := new(borderProperty)
border.properties = map[string]any{}
if params != nil {
for _, tag := range []string{Style, Width, ColorTag, Left, Right, Top, Bottom,
LeftStyle, RightStyle, TopStyle, BottomStyle,
LeftWidth, RightWidth, TopWidth, BottomWidth,
LeftColor, RightColor, TopColor, BottomColor} {
if value, ok := params[tag]; ok && value != nil {
border.Set(tag, value)
}
}
}
return border
}
func (border *borderProperty) normalizeTag(tag string) string {
tag = strings.ToLower(tag)
switch tag {
case BorderLeft, CellBorderLeft:
return Left
case BorderRight, CellBorderRight:
return Right
case BorderTop, CellBorderTop:
return Top
case BorderBottom, CellBorderBottom:
return Bottom
case BorderStyle, CellBorderStyle:
return Style
case BorderLeftStyle, CellBorderLeftStyle, "style-left":
return LeftStyle
case BorderRightStyle, CellBorderRightStyle, "style-right":
return RightStyle
case BorderTopStyle, CellBorderTopStyle, "style-top":
return TopStyle
case BorderBottomStyle, CellBorderBottomStyle, "style-bottom":
return BottomStyle
case BorderWidth, CellBorderWidth:
return Width
case BorderLeftWidth, CellBorderLeftWidth, "width-left":
return LeftWidth
case BorderRightWidth, CellBorderRightWidth, "width-right":
return RightWidth
case BorderTopWidth, CellBorderTopWidth, "width-top":
return TopWidth
case BorderBottomWidth, CellBorderBottomWidth, "width-bottom":
return BottomWidth
case BorderColor, CellBorderColor:
return ColorTag
case BorderLeftColor, CellBorderLeftColor, "color-left":
return LeftColor
case BorderRightColor, CellBorderRightColor, "color-right":
return RightColor
case BorderTopColor, CellBorderTopColor, "color-top":
return TopColor
case BorderBottomColor, CellBorderBottomColor, "color-bottom":
return BottomColor
}
return tag
}
func (border *borderProperty) writeString(buffer *strings.Builder, indent string) {
buffer.WriteString("_{ ")
comma := false
write := func(tag string, value any) {
if comma {
buffer.WriteString(", ")
}
buffer.WriteString(tag)
buffer.WriteString(" = ")
writePropertyValue(buffer, BorderStyle, value, indent)
comma = true
}
for _, tag := range []string{Style, Width, ColorTag} {
if value, ok := border.properties[tag]; ok {
write(tag, value)
}
}
for _, side := range []string{Top, Right, Bottom, Left} {
style, okStyle := border.properties[side+"-"+Style]
width, okWidth := border.properties[side+"-"+Width]
color, okColor := border.properties[side+"-"+ColorTag]
if okStyle || okWidth || okColor {
if comma {
buffer.WriteString(", ")
comma = false
}
buffer.WriteString(side)
buffer.WriteString(" = _{ ")
if okStyle {
write(Style, style)
}
if okWidth {
write(Width, width)
}
if okColor {
write(ColorTag, color)
}
buffer.WriteString(" }")
comma = true
}
}
buffer.WriteString(" }")
}
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()) {
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) {
result = false
}
}
default:
notCompatibleType(Style, text)
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)
switch tag {
case Style:
for _, t := range []string{tag, TopStyle, RightStyle, BottomStyle, LeftStyle} {
delete(border.properties, t)
}
case Width:
for _, t := range []string{tag, TopWidth, RightWidth, BottomWidth, LeftWidth} {
delete(border.properties, t)
}
case ColorTag:
for _, t := range []string{tag, TopColor, RightColor, BottomColor, LeftColor} {
delete(border.properties, t)
}
case Left, Right, Top, Bottom:
border.Remove(tag + "-style")
border.Remove(tag + "-width")
border.Remove(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} {
if t != tag {
if _, ok := border.properties[t]; !ok {
border.properties[t] = style
}
}
}
}
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} {
if t != tag {
if _, ok := border.properties[t]; !ok {
border.properties[t] = width
}
}
}
}
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} {
if t != tag {
if _, ok := border.properties[t]; !ok {
border.properties[t] = color
}
}
}
}
default:
ErrorLogF(`"%s" property is not compatible with the BorderProperty`, tag)
}
}
func (border *borderProperty) Set(tag string, value any) bool {
if value == nil {
border.Remove(tag)
return true
}
tag = border.normalizeTag(tag)
switch tag {
case Style:
if border.setEnumProperty(Style, value, enumProperties[BorderStyle].values) {
for _, side := range []string{TopStyle, RightStyle, BottomStyle, LeftStyle} {
delete(border.properties, side)
}
return true
}
case Width:
if border.setSizeProperty(Width, value) {
for _, side := range []string{TopWidth, RightWidth, BottomWidth, LeftWidth} {
delete(border.properties, side)
}
return true
}
case ColorTag:
if border.setColorProperty(ColorTag, value) {
for _, side := range []string{TopColor, RightColor, BottomColor, LeftColor} {
delete(border.properties, side)
}
return true
}
case LeftStyle, RightStyle, TopStyle, BottomStyle:
return border.setEnumProperty(tag, value, enumProperties[BorderStyle].values)
case LeftWidth, RightWidth, TopWidth, BottomWidth:
return border.setSizeProperty(tag, value)
case LeftColor, RightColor, TopColor, BottomColor:
return border.setColorProperty(tag, value)
case Left, Right, Top, Bottom:
switch value := value.(type) {
case string:
if obj := ParseDataText(value); obj != nil {
return border.setSingleBorderObject(tag, obj)
}
case DataObject:
return border.setSingleBorderObject(tag, value)
case BorderProperty:
styleTag := tag + "-" + Style
if style := value.Get(styleTag); value != nil {
border.properties[styleTag] = style
}
colorTag := tag + "-" + ColorTag
if color := value.Get(colorTag); value != nil {
border.properties[colorTag] = color
}
widthTag := tag + "-" + Width
if width := value.Get(widthTag); value != nil {
border.properties[widthTag] = width
}
return true
case ViewBorder:
border.properties[tag+"-"+Style] = value.Style
border.properties[tag+"-"+Width] = value.Width
border.properties[tag+"-"+ColorTag] = value.Color
return true
}
fallthrough
default:
ErrorLogF(`"%s" property is not compatible with the BorderProperty`, tag)
}
return false
}
func (border *borderProperty) Get(tag string) any {
tag = border.normalizeTag(tag)
if result, ok := border.properties[tag]; ok {
return result
}
switch tag {
case Left, Right, Top, Bottom:
result := newBorderProperty(nil)
if style, ok := border.properties[tag+"-"+Style]; ok {
result.Set(Style, style)
} else if style, ok := border.properties[Style]; ok {
result.Set(Style, style)
}
if width, ok := border.properties[tag+"-"+Width]; ok {
result.Set(Width, width)
} else if width, ok := border.properties[Width]; ok {
result.Set(Width, width)
}
if color, ok := border.properties[tag+"-"+ColorTag]; ok {
result.Set(ColorTag, color)
} else if color, ok := border.properties[ColorTag]; ok {
result.Set(ColorTag, color)
}
return result
case LeftStyle, RightStyle, TopStyle, BottomStyle:
if style, ok := border.properties[tag]; ok {
return style
}
return border.properties[Style]
case LeftWidth, RightWidth, TopWidth, BottomWidth:
if width, ok := border.properties[tag]; ok {
return width
}
return border.properties[Width]
case LeftColor, RightColor, TopColor, BottomColor:
if color, ok := border.properties[tag]; ok {
return color
}
return border.properties[ColorTag]
}
return nil
}
func (border *borderProperty) delete(tag string) {
tag = border.normalizeTag(tag)
remove := []string{}
switch tag {
case Style:
remove = []string{Style, LeftStyle, RightStyle, TopStyle, BottomStyle}
case Width:
remove = []string{Width, LeftWidth, RightWidth, TopWidth, BottomWidth}
case ColorTag:
remove = []string{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}
} else {
remove = []string{tag + "-" + Style, tag + "-" + ColorTag, tag + "-" + Width}
}
case LeftStyle, RightStyle, TopStyle, BottomStyle:
if border.Get(Style) != nil {
border.properties[tag] = 0
} else {
remove = []string{tag}
}
case LeftWidth, RightWidth, TopWidth, BottomWidth:
if border.Get(Width) != nil {
border.properties[tag] = AutoSize()
} else {
remove = []string{tag}
}
case LeftColor, RightColor, TopColor, BottomColor:
if border.Get(ColorTag) != nil {
border.properties[tag] = 0
} else {
remove = []string{tag}
}
}
for _, tag := range remove {
delete(border.properties, tag)
}
}
func (border *borderProperty) ViewBorders(session Session) ViewBorders {
defStyle, _ := valueToEnum(border.getRaw(Style), BorderStyle, session, NoneLine)
defWidth, _ := sizeProperty(border, Width, session)
defColor, _ := colorProperty(border, ColorTag, session)
getBorder := func(prefix string) ViewBorder {
var result ViewBorder
var ok bool
if result.Style, ok = valueToEnum(border.getRaw(prefix+Style), BorderStyle, session, NoneLine); !ok {
result.Style = defStyle
}
if result.Width, ok = sizeProperty(border, prefix+Width, session); !ok {
result.Width = defWidth
}
if result.Color, ok = colorProperty(border, prefix+ColorTag, session); !ok {
result.Color = defColor
}
return result
}
return ViewBorders{
Top: getBorder("top-"),
Left: getBorder("left-"),
Right: getBorder("right-"),
Bottom: getBorder("bottom-"),
}
}
func (border *borderProperty) cssStyle(builder cssBuilder, session Session) {
borders := border.ViewBorders(session)
values := enumProperties[BorderStyle].cssValues
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])
} else {
builder.addValues(BorderStyle, " ", values[borders.Top.Style],
values[borders.Right.Style], values[borders.Bottom.Style], values[borders.Left.Style])
}
}
func (border *borderProperty) cssWidth(builder cssBuilder, session Session) {
borders := border.ViewBorders(session)
if borders.Top.Width == borders.Right.Width &&
borders.Top.Width == borders.Left.Width &&
borders.Top.Width == borders.Bottom.Width {
if borders.Top.Width.Type != Auto {
builder.add("border-width", borders.Top.Width.cssString("0", session))
}
} else {
builder.addValues("border-width", " ",
borders.Top.Width.cssString("0", session),
borders.Right.Width.cssString("0", session),
borders.Bottom.Width.cssString("0", session),
borders.Left.Width.cssString("0", session))
}
}
func (border *borderProperty) cssColor(builder cssBuilder, session Session) {
borders := border.ViewBorders(session)
if borders.Top.Color == borders.Right.Color &&
borders.Top.Color == borders.Left.Color &&
borders.Top.Color == borders.Bottom.Color {
if borders.Top.Color != 0 {
builder.add("border-color", borders.Top.Color.cssString())
}
} else {
builder.addValues("border-color", " ", borders.Top.Color.cssString(),
borders.Right.Color.cssString(), borders.Bottom.Color.cssString(), borders.Left.Color.cssString())
}
}
func (border *borderProperty) cssStyleValue(session Session) string {
var builder cssValueBuilder
border.cssStyle(&builder, session)
return builder.finish()
}
func (border *borderProperty) cssWidthValue(session Session) string {
var builder cssValueBuilder
border.cssWidth(&builder, session)
return builder.finish()
}
func (border *borderProperty) cssColorValue(session Session) string {
var builder cssValueBuilder
border.cssColor(&builder, session)
return builder.finish()
}
// ViewBorder describes parameters of a view border
type ViewBorder struct {
Style int
Color Color
Width SizeUnit
}
// ViewBorders describes the top, right, bottom, and left border of a view
type ViewBorders struct {
Top, Right, Bottom, Left ViewBorder
}
// AllTheSame returns true if all borders are the same
func (border *ViewBorders) AllTheSame() bool {
return border.Top.Style == border.Right.Style &&
border.Top.Style == border.Left.Style &&
border.Top.Style == border.Bottom.Style &&
border.Top.Color == border.Right.Color &&
border.Top.Color == border.Left.Color &&
border.Top.Color == border.Bottom.Color &&
border.Top.Width.Equal(border.Right.Width) &&
border.Top.Width.Equal(border.Left.Width) &&
border.Top.Width.Equal(border.Bottom.Width)
}
func getBorder(style Properties, tag string) BorderProperty {
if value := style.Get(tag); value != nil {
if border, ok := value.(BorderProperty); ok {
return border
}
}
return nil
}