rui_orig/theme.go

1050 lines
25 KiB
Go
Raw Permalink Normal View History

2021-09-07 17:36:50 +03:00
package rui
import (
2022-05-23 15:22:14 +03:00
"fmt"
2021-09-07 17:36:50 +03:00
"sort"
"strconv"
"strings"
)
// Constants used as a values for [MediaStyleParams] member Orientation
2021-09-07 17:36:50 +03:00
const (
// DefaultMedia means that style appliance will not be related to client's window orientation
DefaultMedia = 0
// PortraitMedia means that style apply on clients with portrait window orientation
PortraitMedia = 1
// PortraitMedia means that style apply on clients with landscape window orientation
LandscapeMedia = 2
2021-09-07 17:36:50 +03:00
)
// MediaStyleParams define rules when particular style will be applied
type MediaStyleParams struct {
// Orientation for which particular style will be applied
Orientation int
// MinWidth for which particular style will be applied
MinWidth int
// MaxWidth for which particular style will be applied
MaxWidth int
// MinHeight for which particular style will be applied
MinHeight int
// MaxHeight for which particular style will be applied
MaxHeight int
}
2022-05-25 20:03:32 +03:00
type mediaStyle struct {
MediaStyleParams
styles map[string]ViewStyle
2021-09-07 17:36:50 +03:00
}
type theme struct {
name string
constants map[string]string
touchConstants map[string]string
colors map[string]string
darkColors map[string]string
images map[string]string
darkImages map[string]string
2022-05-23 15:22:14 +03:00
styles map[string]ViewStyle
2022-05-25 20:03:32 +03:00
mediaStyles []mediaStyle
}
// Theme interface to describe application's theme
type Theme interface {
2022-05-23 15:22:14 +03:00
fmt.Stringer
// Name returns a name of the theme
Name() string
// Constant returns normal and touch theme constant value with specific tag
Constant(tag string) (string, string)
// SetConstant sets a value for a constant
SetConstant(tag string, value, touchUIValue string)
// ConstantTags returns the list of all available constants
ConstantTags() []string
// Color returns normal and dark theme color constant value with specific tag
Color(tag string) (string, string)
// SetColor sets normal and dark theme color constant value with specific tag
SetColor(tag, color, darkUIColor string)
// ColorTags returns the list of all available color constants
ColorTags() []string
// Image returns normal and dark theme image constant value with specific tag
Image(tag string) (string, string)
// SetImage sets normal and dark theme image constant value with specific tag
SetImage(tag, image, darkUIImage string)
// ImageConstantTags returns the list of all available image constants
ImageConstantTags() []string
// Style returns view style by its tag
2022-05-25 20:03:32 +03:00
Style(tag string) ViewStyle
// SetStyle sets style for a tag
2022-05-25 20:03:32 +03:00
SetStyle(tag string, style ViewStyle)
// RemoveStyle removes style with provided tag
2023-05-18 12:26:54 +03:00
RemoveStyle(tag string)
// MediaStyle returns media style which correspond to provided media style parameters
MediaStyle(tag string, params MediaStyleParams) ViewStyle
// SetMediaStyle sets media style with provided media style parameters and a tag
SetMediaStyle(tag string, params MediaStyleParams, style ViewStyle)
// StyleTags returns all tags which describe a style
2022-05-25 20:03:32 +03:00
StyleTags() []string
// MediaStyles returns all media style settings which correspond to a style tag
MediaStyles(tag string) []struct {
Selectors string
Params MediaStyleParams
}
// Append theme to a list of themes
Append(anotherTheme Theme)
constant(tag string, touchUI bool) string
color(tag string, darkUI bool) string
image(tag string, darkUI bool) string
2022-05-23 15:22:14 +03:00
style(tag string) ViewStyle
cssText(session Session) string
data() *theme
}
2022-05-25 20:03:32 +03:00
func (rule mediaStyle) cssText() string {
2021-09-07 17:36:50 +03:00
builder := allocStringBuilder()
defer freeStringBuilder(builder)
switch rule.Orientation {
case PortraitMedia:
2021-09-07 17:36:50 +03:00
builder.WriteString(" and (orientation: portrait)")
case LandscapeMedia:
2021-09-07 17:36:50 +03:00
builder.WriteString(" and (orientation: landscape)")
}
writeSize := func(tag string, minSize, maxSize int) {
if minSize != maxSize {
if minSize > 0 {
builder.WriteString(fmt.Sprintf(" and (min-%s: %d.001px)", tag, minSize))
}
if maxSize > 0 {
builder.WriteString(fmt.Sprintf(" and (max-%s: %dpx)", tag, maxSize))
}
} else if minSize > 0 {
builder.WriteString(fmt.Sprintf(" and (%s: %dpx)", tag, minSize))
}
2021-09-07 17:36:50 +03:00
}
writeSize("width", rule.MinWidth, rule.MaxWidth)
writeSize("height", rule.MinHeight, rule.MaxHeight)
2021-09-07 17:36:50 +03:00
return builder.String()
}
2022-05-25 20:03:32 +03:00
func parseMediaRule(text string) (mediaStyle, bool) {
rule := mediaStyle{
styles: map[string]ViewStyle{},
}
2021-09-07 17:36:50 +03:00
elements := strings.Split(text, ":")
for i := 1; i < len(elements); i++ {
switch element := elements[i]; element {
case "portrait":
if rule.Orientation != DefaultMedia {
2021-09-07 17:36:50 +03:00
ErrorLog(`Duplicate orientation tag in the style section "` + text + `"`)
return rule, false
}
rule.Orientation = PortraitMedia
2021-09-07 17:36:50 +03:00
case "landscape":
if rule.Orientation != DefaultMedia {
2021-09-07 17:36:50 +03:00
ErrorLog(`Duplicate orientation tag in the style section "` + text + `"`)
return rule, false
}
rule.Orientation = LandscapeMedia
2021-09-07 17:36:50 +03:00
default:
elementSize := func(name string) (int, int, bool, error) {
2021-09-07 17:36:50 +03:00
if strings.HasPrefix(element, name) {
var err error = nil
min := 0
max := 0
data := element[len(name):]
if pos := strings.Index(data, "-"); pos >= 0 {
if pos > 0 {
min, err = strconv.Atoi(data[:pos])
}
if err == nil && pos+1 < len(data) {
max, err = strconv.Atoi(data[pos+1:])
}
} else {
max, err = strconv.Atoi(data)
2021-09-07 17:36:50 +03:00
}
return min, max, true, err
2021-09-07 17:36:50 +03:00
}
return 0, 0, false, nil
2021-09-07 17:36:50 +03:00
}
if min, max, ok, err := elementSize("width"); ok {
if err != nil {
ErrorLogF(`Invalid style section name "%s": %s`, text, err.Error())
2021-09-07 17:36:50 +03:00
return rule, false
}
if rule.MinWidth != 0 || rule.MaxWidth != 0 {
2021-09-07 17:36:50 +03:00
ErrorLog(`Duplicate "width" tag in the style section "` + text + `"`)
return rule, false
}
if min == 0 && max == 0 {
ErrorLog(`Invalid arguments of "width" tag in the style section "` + text + `"`)
2021-09-07 17:36:50 +03:00
return rule, false
}
rule.MinWidth = min
rule.MaxWidth = max
} else if min, max, ok, err := elementSize("height"); ok {
if err != nil {
ErrorLogF(`Invalid style section name "%s": %s`, text, err.Error())
return rule, false
}
if rule.MinHeight != 0 || rule.MaxHeight != 0 {
2021-09-07 17:36:50 +03:00
ErrorLog(`Duplicate "height" tag in the style section "` + text + `"`)
return rule, false
}
if min == 0 && max == 0 {
ErrorLog(`Invalid arguments of "height" tag in the style section "` + text + `"`)
return rule, false
}
rule.MinHeight = min
rule.MaxHeight = max
2021-09-07 17:36:50 +03:00
} else {
2023-05-10 15:52:56 +03:00
ErrorLogF(`Unknown element "%s" in the style section name "%s"`, element, text)
2021-09-07 17:36:50 +03:00
return rule, false
}
}
}
return rule, true
}
var defaultTheme = NewTheme("")
2021-09-07 17:36:50 +03:00
// NewTheme creates a new theme with specific name and return its interface.
func NewTheme(name string) Theme {
result := new(theme)
result.init()
result.name = name
return result
}
2021-09-07 17:36:50 +03:00
// CreateThemeFromText creates a new theme from text and return its interface on success.
func CreateThemeFromText(text string) (Theme, bool) {
2021-09-07 17:36:50 +03:00
result := new(theme)
result.init()
ok := result.addText(text)
return result, ok
}
func (theme *theme) init() {
theme.constants = map[string]string{}
theme.touchConstants = map[string]string{}
theme.colors = map[string]string{}
theme.darkColors = map[string]string{}
2022-04-23 18:13:35 +03:00
theme.images = map[string]string{}
theme.darkImages = map[string]string{}
2022-05-23 15:22:14 +03:00
theme.styles = map[string]ViewStyle{}
2022-05-25 20:03:32 +03:00
theme.mediaStyles = []mediaStyle{}
}
func (theme *theme) Name() string {
return theme.name
}
func (theme *theme) Constant(tag string) (string, string) {
return theme.constants[tag], theme.touchConstants[tag]
}
func (theme *theme) SetConstant(tag, value, touchUIValue string) {
value = strings.Trim(value, " \t")
if value == "" {
delete(theme.constants, tag)
delete(theme.touchConstants, tag)
} else {
theme.constants[tag] = value
touchUIValue = strings.Trim(touchUIValue, " \t")
if touchUIValue == "" {
delete(theme.touchConstants, tag)
} else {
theme.touchConstants[tag] = touchUIValue
}
}
2021-09-07 17:36:50 +03:00
}
func (theme *theme) Color(tag string) (string, string) {
return theme.colors[tag], theme.darkColors[tag]
}
func (theme *theme) SetColor(tag, color, darkUIColor string) {
color = strings.Trim(color, " \t")
if color == "" {
delete(theme.colors, tag)
delete(theme.darkColors, tag)
} else {
theme.colors[tag] = color
darkUIColor = strings.Trim(darkUIColor, " \t")
if darkUIColor == "" {
delete(theme.darkColors, tag)
} else {
theme.darkColors[tag] = darkUIColor
}
}
}
func (theme *theme) Image(tag string) (string, string) {
return theme.images[tag], theme.darkImages[tag]
}
func (theme *theme) SetImage(tag, image, darkUIImage string) {
image = strings.Trim(image, " \t")
if image == "" {
delete(theme.images, tag)
delete(theme.darkImages, tag)
} else {
theme.images[tag] = image
darkUIImage = strings.Trim(darkUIImage, " \t")
if darkUIImage == "" {
delete(theme.darkImages, tag)
} else {
theme.darkImages[tag] = darkUIImage
}
}
}
2022-05-25 20:03:32 +03:00
func (theme *theme) Style(tag string) ViewStyle {
if style, ok := theme.styles[tag]; ok {
return style
}
return nil
}
func (theme *theme) SetStyle(tag string, style ViewStyle) {
if style != nil {
theme.styles[tag] = style
} else {
delete(theme.styles, tag)
}
}
2023-05-18 12:26:54 +03:00
func (theme *theme) RemoveStyle(tag string) {
tag2 := tag + ":"
remove := func(styles map[string]ViewStyle) {
tags := []string{tag}
for t := range styles {
if strings.HasPrefix(t, tag2) {
tags = append(tags, t)
}
}
for _, t := range tags {
delete(styles, t)
}
}
remove(theme.styles)
for _, mediaStyle := range theme.mediaStyles {
remove(mediaStyle.styles)
}
}
func (theme *theme) MediaStyle(tag string, params MediaStyleParams) ViewStyle {
2022-05-25 20:03:32 +03:00
for _, styles := range theme.mediaStyles {
if styles.Orientation == params.Orientation &&
styles.MaxWidth == params.MaxWidth &&
styles.MinWidth == params.MinWidth &&
styles.MaxHeight == params.MaxHeight &&
styles.MinHeight == params.MinHeight {
2022-05-25 20:03:32 +03:00
if style, ok := styles.styles[tag]; ok {
return style
}
}
}
if params.Orientation == 0 && params.MaxWidth == 0 && params.MinWidth == 0 &&
params.MaxHeight == 0 && params.MinHeight == 0 {
return theme.style(tag)
}
2022-05-25 20:03:32 +03:00
return nil
}
func (theme *theme) SetMediaStyle(tag string, params MediaStyleParams, style ViewStyle) {
if params.MaxWidth < 0 {
params.MaxWidth = 0
}
if params.MinWidth < 0 {
params.MinWidth = 0
}
if params.MaxHeight < 0 {
params.MaxHeight = 0
2022-05-25 20:03:32 +03:00
}
if params.MinHeight < 0 {
params.MinHeight = 0
2022-05-25 20:03:32 +03:00
}
if params.Orientation == 0 && params.MaxWidth == 0 && params.MinWidth == 0 &&
params.MaxHeight == 0 && params.MinHeight == 0 {
2022-05-25 20:03:32 +03:00
theme.SetStyle(tag, style)
return
}
for i, styles := range theme.mediaStyles {
if styles.Orientation == params.Orientation &&
styles.MaxWidth == params.MaxWidth &&
styles.MinWidth == params.MinWidth &&
styles.MaxHeight == params.MaxHeight &&
styles.MinHeight == params.MinHeight {
2022-05-25 20:03:32 +03:00
if style != nil {
theme.mediaStyles[i].styles[tag] = style
} else {
delete(theme.mediaStyles[i].styles, tag)
}
break
}
}
if style != nil {
theme.mediaStyles = append(theme.mediaStyles, mediaStyle{
MediaStyleParams: params,
styles: map[string]ViewStyle{tag: style},
2022-05-25 20:03:32 +03:00
})
theme.sortMediaStyles()
}
}
func (theme *theme) ConstantTags() []string {
keys := make([]string, 0, len(theme.constants))
for k := range theme.constants {
keys = append(keys, k)
}
for tag := range theme.touchConstants {
if _, ok := theme.constants[tag]; !ok {
keys = append(keys, tag)
}
}
sort.Strings(keys)
return keys
}
func (theme *theme) ColorTags() []string {
keys := make([]string, 0, len(theme.colors))
for k := range theme.colors {
keys = append(keys, k)
}
for tag := range theme.darkColors {
if _, ok := theme.colors[tag]; !ok {
keys = append(keys, tag)
}
}
sort.Strings(keys)
return keys
}
func (theme *theme) ImageConstantTags() []string {
keys := make([]string, 0, len(theme.colors))
for k := range theme.images {
keys = append(keys, k)
}
for tag := range theme.darkImages {
if _, ok := theme.images[tag]; !ok {
keys = append(keys, tag)
}
}
sort.Strings(keys)
return keys
}
2022-05-25 20:03:32 +03:00
func (theme *theme) StyleTags() []string {
keys := make([]string, 0, len(theme.styles)*2)
appendTag := func(k string) {
n := sort.SearchStrings(keys, k)
if n >= len(keys) {
keys = append(keys, k)
} else if keys[n] != k {
if n == 0 {
keys = append([]string{k}, keys...)
} else {
keys = append(keys[:n+1], keys[n:]...)
keys[n] = k
}
}
}
for k := range theme.styles {
if index := strings.IndexRune(k, ':'); index < 0 {
keys = append(keys, k)
}
}
sort.Strings(keys)
for k := range theme.styles {
if index := strings.IndexRune(k, ':'); index > 0 {
appendTag(k[:index])
}
}
for _, media := range theme.mediaStyles {
for k := range media.styles {
index := strings.IndexRune(k, ':')
if index > 0 {
appendTag(k[:index])
} else if index < 0 {
appendTag(k)
}
}
}
return keys
}
func (theme *theme) MediaStyles(tag string) []struct {
Selectors string
Params MediaStyleParams
} {
result := []struct {
Selectors string
Params MediaStyleParams
}{}
prefix := tag + ":"
prefixLen := len(prefix)
for themeTag := range theme.styles {
if strings.HasPrefix(themeTag, prefix) {
result = append(result, struct {
Selectors string
Params MediaStyleParams
}{
Selectors: themeTag[prefixLen:],
Params: MediaStyleParams{},
})
}
}
for _, media := range theme.mediaStyles {
if _, ok := media.styles[tag]; ok {
result = append(result, struct {
Selectors string
Params MediaStyleParams
}{
Selectors: "",
Params: media.MediaStyleParams,
})
}
for themeTag := range media.styles {
if strings.HasPrefix(themeTag, prefix) {
result = append(result, struct {
Selectors string
Params MediaStyleParams
}{
Selectors: themeTag[prefixLen:],
Params: media.MediaStyleParams,
})
}
}
}
return result
}
func (theme *theme) data() *theme {
return theme
}
func (theme *theme) Append(anotherTheme Theme) {
2021-09-07 17:36:50 +03:00
if theme.constants == nil {
theme.init()
}
another := anotherTheme.data()
for tag, constant := range another.constants {
2021-09-07 17:36:50 +03:00
theme.constants[tag] = constant
}
for tag, constant := range another.touchConstants {
2021-09-07 17:36:50 +03:00
theme.touchConstants[tag] = constant
}
for tag, color := range another.colors {
2021-09-07 17:36:50 +03:00
theme.colors[tag] = color
}
for tag, color := range another.darkColors {
2021-09-07 17:36:50 +03:00
theme.darkColors[tag] = color
}
for tag, image := range another.images {
2022-04-23 18:13:35 +03:00
theme.images[tag] = image
}
for tag, image := range another.darkImages {
2022-04-23 18:13:35 +03:00
theme.darkImages[tag] = image
}
for tag, style := range another.styles {
2021-09-07 17:36:50 +03:00
theme.styles[tag] = style
}
for _, anotherMedia := range another.mediaStyles {
2021-09-07 17:36:50 +03:00
exists := false
for _, media := range theme.mediaStyles {
if anotherMedia.MinHeight == media.MinHeight &&
anotherMedia.MaxHeight == media.MaxHeight &&
anotherMedia.MinWidth == media.MinWidth &&
anotherMedia.MaxWidth == media.MaxWidth &&
anotherMedia.Orientation == media.Orientation {
2022-05-25 20:03:32 +03:00
for tag, style := range anotherMedia.styles {
media.styles[tag] = style
2021-09-07 17:36:50 +03:00
}
exists = true
break
}
}
if !exists {
theme.mediaStyles = append(theme.mediaStyles, anotherMedia)
}
}
}
func (theme *theme) cssText(session Session) string {
if theme.styles == nil {
theme.init()
return ""
}
var builder cssStyleBuilder
2024-07-01 19:17:03 +03:00
builder.init(16)
2021-09-07 17:36:50 +03:00
2023-04-23 12:32:03 +03:00
styleList := func(styles map[string]ViewStyle) []string {
ruiStyles := []string{}
customStyles := []string{}
2023-05-02 17:20:01 +03:00
for tag := range styles {
2023-04-23 12:32:03 +03:00
if strings.HasPrefix(tag, "rui") {
ruiStyles = append(ruiStyles, tag)
} else {
customStyles = append(customStyles, tag)
}
}
sort.Strings(ruiStyles)
sort.Strings(customStyles)
return append(ruiStyles, customStyles...)
2021-09-07 17:36:50 +03:00
}
2023-04-23 12:32:03 +03:00
for _, tag := range styleList(theme.styles) {
if style := theme.styles[tag]; style != nil {
2021-09-07 17:36:50 +03:00
builder.startStyle(tag)
style.cssViewStyle(&builder, session)
2021-09-07 17:36:50 +03:00
builder.endStyle()
}
2023-04-23 12:32:03 +03:00
}
for _, media := range theme.mediaStyles {
builder.startMedia(media.cssText())
for _, tag := range styleList(media.styles) {
if style := media.styles[tag]; style != nil {
builder.startStyle(tag)
style.cssViewStyle(&builder, session)
builder.endStyle()
}
}
2021-09-07 17:36:50 +03:00
builder.endMedia()
}
return builder.finish()
}
func (theme *theme) addText(themeText string) bool {
if theme.constants == nil {
theme.init()
}
2022-05-25 20:03:32 +03:00
data := ParseDataText(themeText)
if data == nil || !data.IsObject() || data.Tag() != "theme" {
return false
2021-09-07 17:36:50 +03:00
}
count := data.PropertyCount()
2022-05-23 15:22:14 +03:00
objToStyle := func(obj DataObject) ViewStyle {
params := Params{}
for i := 0; i < obj.PropertyCount(); i++ {
if node := obj.Property(i); node != nil {
switch node.Type() {
case ArrayNode:
params[node.Tag()] = node.ArrayElements()
case ObjectNode:
params[node.Tag()] = node.Object()
default:
params[node.Tag()] = node.Text()
}
}
}
2022-05-23 15:22:14 +03:00
return NewViewStyle(params)
}
2021-09-07 17:36:50 +03:00
for i := 0; i < count; i++ {
if d := data.Property(i); d != nil {
switch tag := d.Tag(); tag {
case "constants":
if d.Type() == ObjectNode {
if obj := d.Object(); obj != nil {
objCount := obj.PropertyCount()
for k := 0; k < objCount; k++ {
if prop := obj.Property(k); prop != nil && prop.Type() == TextNode {
theme.constants[prop.Tag()] = prop.Text()
}
}
}
}
case "constants:touch":
if d.Type() == ObjectNode {
if obj := d.Object(); obj != nil {
objCount := obj.PropertyCount()
for k := 0; k < objCount; k++ {
if prop := obj.Property(k); prop != nil && prop.Type() == TextNode {
theme.touchConstants[prop.Tag()] = prop.Text()
}
}
}
}
case "colors":
if d.Type() == ObjectNode {
if obj := d.Object(); obj != nil {
objCount := obj.PropertyCount()
for k := 0; k < objCount; k++ {
if prop := obj.Property(k); prop != nil && prop.Type() == TextNode {
theme.colors[prop.Tag()] = prop.Text()
}
}
}
}
case "colors:dark":
if d.Type() == ObjectNode {
if obj := d.Object(); obj != nil {
objCount := obj.PropertyCount()
for k := 0; k < objCount; k++ {
if prop := obj.Property(k); prop != nil && prop.Type() == TextNode {
theme.darkColors[prop.Tag()] = prop.Text()
}
}
}
}
2022-04-23 18:13:35 +03:00
case "images":
if d.Type() == ObjectNode {
if obj := d.Object(); obj != nil {
objCount := obj.PropertyCount()
for k := 0; k < objCount; k++ {
if prop := obj.Property(k); prop != nil && prop.Type() == TextNode {
theme.images[prop.Tag()] = prop.Text()
}
}
}
}
case "images:dark":
if d.Type() == ObjectNode {
if obj := d.Object(); obj != nil {
objCount := obj.PropertyCount()
for k := 0; k < objCount; k++ {
if prop := obj.Property(k); prop != nil && prop.Type() == TextNode {
theme.darkImages[prop.Tag()] = prop.Text()
}
}
}
}
2021-09-07 17:36:50 +03:00
case "styles":
if d.Type() == ArrayNode {
arraySize := d.ArraySize()
for k := 0; k < arraySize; k++ {
if element := d.ArrayElement(k); element != nil && element.IsObject() {
if obj := element.Object(); obj != nil {
2022-05-23 15:22:14 +03:00
theme.styles[obj.Tag()] = objToStyle(obj)
2021-09-07 17:36:50 +03:00
}
}
}
}
default:
if d.Type() == ArrayNode && strings.HasPrefix(tag, "styles:") {
if rule, ok := parseMediaRule(tag); ok {
arraySize := d.ArraySize()
for k := 0; k < arraySize; k++ {
if element := d.ArrayElement(k); element != nil && element.IsObject() {
if obj := element.Object(); obj != nil {
2022-05-25 20:03:32 +03:00
rule.styles[obj.Tag()] = objToStyle(obj)
2021-09-07 17:36:50 +03:00
}
}
}
theme.mediaStyles = append(theme.mediaStyles, rule)
}
}
}
}
}
2022-05-25 20:03:32 +03:00
theme.sortMediaStyles()
return true
}
func (theme *theme) sortMediaStyles() {
if len(theme.mediaStyles) > 1 {
2021-09-07 17:36:50 +03:00
sort.SliceStable(theme.mediaStyles, func(i, j int) bool {
if theme.mediaStyles[i].Orientation != theme.mediaStyles[j].Orientation {
return theme.mediaStyles[i].Orientation < theme.mediaStyles[j].Orientation
2021-09-07 17:36:50 +03:00
}
if theme.mediaStyles[i].MinWidth != theme.mediaStyles[j].MinWidth {
return theme.mediaStyles[i].MinWidth < theme.mediaStyles[j].MinWidth
2021-09-07 17:36:50 +03:00
}
if theme.mediaStyles[i].MinHeight != theme.mediaStyles[j].MinHeight {
return theme.mediaStyles[i].MinHeight < theme.mediaStyles[j].MinHeight
}
if theme.mediaStyles[i].MaxWidth != theme.mediaStyles[j].MaxWidth {
return theme.mediaStyles[i].MaxWidth < theme.mediaStyles[j].MaxWidth
}
return theme.mediaStyles[i].MaxHeight < theme.mediaStyles[j].MaxHeight
2021-09-07 17:36:50 +03:00
})
}
}
func (theme *theme) constant(tag string, touchUI bool) string {
result := ""
if touchUI {
if value, ok := theme.touchConstants[tag]; ok {
result = value
}
}
if result == "" {
if value, ok := theme.constants[tag]; ok {
result = value
}
}
return result
}
func (theme *theme) color(tag string, darkUI bool) string {
result := ""
if darkUI {
if value, ok := theme.darkColors[tag]; ok {
result = value
}
}
if result == "" {
if value, ok := theme.colors[tag]; ok {
result = value
}
}
return result
}
func (theme *theme) image(tag string, darkUI bool) string {
result := ""
if darkUI {
if value, ok := theme.darkImages[tag]; ok {
result = value
}
}
if result == "" {
if value, ok := theme.images[tag]; ok {
result = value
}
}
return result
}
2022-05-23 15:22:14 +03:00
func (theme *theme) style(tag string) ViewStyle {
if style, ok := theme.styles[tag]; ok {
return style
}
2022-05-23 15:22:14 +03:00
return nil
}
func (theme *theme) String() string {
buffer := allocStringBuilder()
defer freeStringBuilder(buffer)
writeString := func(text string) {
if strings.ContainsAny(text, " \t\n\r\\\"'`,;{}[]()") {
replace := []struct{ old, new string }{
{old: "\\", new: `\\`},
{old: "\t", new: `\t`},
{old: "\r", new: `\r`},
{old: "\n", new: `\n`},
{old: "\"", new: `\"`},
}
for _, s := range replace {
text = strings.Replace(text, s.old, s.new, -1)
}
buffer.WriteRune('"')
buffer.WriteString(text)
buffer.WriteRune('"')
} else {
buffer.WriteString(text)
}
}
writeConstants := func(tag string, constants map[string]string) {
count := len(constants)
if count == 0 {
return
}
buffer.WriteString("\t")
buffer.WriteString(tag)
buffer.WriteString(" = _{\n")
tags := make([]string, 0, count)
for name := range constants {
tags = append(tags, name)
}
sort.Strings(tags)
for _, name := range tags {
if value, ok := constants[name]; ok && value != "" {
buffer.WriteString("\t\t")
writeString(name)
buffer.WriteString(" = ")
writeString(value)
buffer.WriteString(",\n")
}
}
buffer.WriteString("\t},\n")
}
buffer.WriteString("theme {\n")
writeConstants("colors", theme.colors)
writeConstants("colors:dark", theme.darkColors)
writeConstants("images", theme.images)
writeConstants("images:dark", theme.darkImages)
writeConstants("constants", theme.constants)
writeConstants("constants:touch", theme.touchConstants)
2023-05-10 15:52:56 +03:00
writeStyles := func(orientation, maxWidth, maxHeight int, styles map[string]ViewStyle) bool {
2022-05-25 20:03:32 +03:00
count := len(styles)
if count == 0 {
return false
}
tags := make([]string, 0, count)
for name := range styles {
tags = append(tags, name)
}
sort.Strings(tags)
buffer.WriteString("\tstyles")
switch orientation {
case PortraitMedia:
buffer.WriteString(":portrait")
case LandscapeMedia:
buffer.WriteString(":landscape")
}
if maxWidth > 0 {
buffer.WriteString(fmt.Sprintf(":width%d", maxWidth))
}
2023-05-10 15:52:56 +03:00
if maxHeight > 0 {
buffer.WriteString(fmt.Sprintf(":height%d", maxHeight))
2022-05-25 20:03:32 +03:00
}
buffer.WriteString(" = [\n")
for _, tag := range tags {
if style, ok := styles[tag]; ok && len(style.AllTags()) > 0 {
2022-05-25 20:03:32 +03:00
buffer.WriteString("\t\t")
writeViewStyle(tag, style, buffer, "\t\t", nil)
2023-05-10 15:52:56 +03:00
buffer.WriteString(",\n")
2022-05-25 20:03:32 +03:00
}
}
buffer.WriteString("\t],\n")
return true
}
writeStyles(0, 0, 0, theme.styles)
for _, media := range theme.mediaStyles {
//writeStyles(media.orientation, media.maxWidth, media.maxHeight, media.styles)
if count := len(media.styles); count > 0 {
tags := make([]string, 0, count)
for name := range media.styles {
tags = append(tags, name)
}
sort.Strings(tags)
buffer.WriteString("\tstyles")
switch media.Orientation {
case PortraitMedia:
buffer.WriteString(":portrait")
case LandscapeMedia:
buffer.WriteString(":landscape")
}
if media.MinWidth > 0 {
buffer.WriteString(fmt.Sprintf(":width%d-", media.MinWidth))
if media.MaxWidth > 0 {
buffer.WriteString(strconv.Itoa(media.MaxWidth))
}
} else if media.MaxWidth > 0 {
buffer.WriteString(fmt.Sprintf(":width%d", media.MaxWidth))
}
if media.MinHeight > 0 {
buffer.WriteString(fmt.Sprintf(":height%d-", media.MinHeight))
if media.MaxHeight > 0 {
buffer.WriteString(strconv.Itoa(media.MaxHeight))
}
} else if media.MaxHeight > 0 {
buffer.WriteString(fmt.Sprintf(":height%d", media.MaxHeight))
}
buffer.WriteString(" = [\n")
for _, tag := range tags {
if style, ok := media.styles[tag]; ok && len(style.AllTags()) > 0 {
buffer.WriteString("\t\t")
writeViewStyle(tag, style, buffer, "\t\t", nil)
buffer.WriteString(",\n")
}
}
buffer.WriteString("\t],\n")
}
2022-05-25 20:03:32 +03:00
}
2022-05-23 15:22:14 +03:00
buffer.WriteString("}\n")
return buffer.String()
}