forked from mbk-lab/rui_orig
2
0
Fork 0

Added support for height and width range in media styles

This commit is contained in:
anoshenko 2023-05-26 20:08:25 +03:00
parent 3d44aa3ba3
commit 600f56d8ea
4 changed files with 235 additions and 93 deletions

View File

@ -10,6 +10,8 @@
"color-changed", "number-changed", "date-changed", and "time-changed" events. "color-changed", "number-changed", "date-changed", and "time-changed" events.
Old format is "<listener>(<view>, <new value>)", new format is "<listener>(<view>, <new value>, <old value>)" Old format is "<listener>(<view>, <new value>)", new format is "<listener>(<view>, <new value>, <old value>)"
* Changed FocusView function * Changed FocusView function
* Added support for height and width range in media styles.
Changed MediaStyle, SetMediaStyle, and MediaStyles functions of Theme interface
* Bug fixing * Bug fixing
# v0.12.0 # v0.12.0

View File

@ -5509,9 +5509,19 @@ Safari и Firefox.
* ":portrait" или ":landscape" - соответственно стили для портретного или ландшафтного режима программы. * ":portrait" или ":landscape" - соответственно стили для портретного или ландшафтного режима программы.
Внимание имеется ввиду соотношение сторон окна программы, а не экрана. Внимание имеется ввиду соотношение сторон окна программы, а не экрана.
* ":width<размер>" - стили для экрана ширина которого не превышает заданный размер в логических пикселях. * ":width<минимальная ширина>-<максимальная ширина>" - стили для экрана ширина которого находится в заданном в логических пикселях диапазоне.
* ":width<максимальная ширина>" - стили для экрана ширина которого не превышает заданную величину в логических пикселях.
* ":width<минимальная ширина>-" - стили для экрана ширина которого больше заданной величины в логических пикселях.
* ":height<минимальная высота>-<максимальная высота>" - стили для экрана высота которого находится в заданном в логических пикселях диапазоне.
* ":height<максимальная высота>" - стили для экрана высота которого не превышает заданную величину в логических пикселях.
* ":height<минимальная высота>-" - стили для экрана высота которого больше заданной величины в логических пикселях.
* ":height<размер>" - стили для экрана высота которого не превышает заданный размер в логических пикселях.
Например Например
@ -5543,7 +5553,19 @@ Safari и Firefox.
width = 100%, width = 100%,
height = 50%, height = 50%,
}, },
] ],
styles:portrait:width320-640 = [
samplePage {
width = 90%,
height = 60%,
},
],
styles:portrait:width640- = [
samplePage {
width = 80%,
height = 70%,
},
],
} }
## Стандартные константы и стили ## Стандартные константы и стили

View File

@ -5461,9 +5461,17 @@ In addition to general styles, you can add styles for specific work modes. To do
* ":portrait" or ":landscape" are respectively styles for portrait or landscape mode of the program. * ":portrait" or ":landscape" are respectively styles for portrait or landscape mode of the program.
Attention means the aspect ratio of the program window, not the screen. Attention means the aspect ratio of the program window, not the screen.
* ":width< size >" are styles for a screen whose width does not exceed the specified size in logical pixels. * ":width<min-width>-<max-width>" - styles for a screen whose width is in the range specified in logical pixels.
* ":height< size >" are styles for a screen whose height does not exceed the specified size in logical pixels. * ":width<max-width>" - styles for a screen whose width does not exceed the specified value in logical pixels.
* ":width<min-width>-" - styles for a screen whose width is greater than the specified value in logical pixels.
* ":height<min-height>-<max-height>" - styles for a screen whose height is in the range specified in logical pixels.
* ":height<max-height>" - styles for a screen whose height does not exceed the specified value in logical pixels.
* ":height<minimum-height>-" - styles for a screen whose height is greater than the specified value in logical pixels.
For example For example
@ -5495,7 +5503,19 @@ For example
width = 100%, width = 100%,
height = 50%, height = 50%,
}, },
] ],
styles:portrait:width320-640 = [
samplePage {
width = 90%,
height = 60%,
},
],
styles:portrait:width640- = [
samplePage {
width = 80%,
height = 70%,
},
],
} }
## Standard constants and styles ## Standard constants and styles

272
theme.go
View File

@ -13,11 +13,17 @@ const (
LandscapeMedia = 2 LandscapeMedia = 2
) )
type MediaStyleParams struct {
Orientation int
MinWidth int
MaxWidth int
MinHeight int
MaxHeight int
}
type mediaStyle struct { type mediaStyle struct {
orientation int MediaStyleParams
maxWidth int styles map[string]ViewStyle
maxHeight int
styles map[string]ViewStyle
} }
type theme struct { type theme struct {
@ -50,12 +56,12 @@ type Theme interface {
Style(tag string) ViewStyle Style(tag string) ViewStyle
SetStyle(tag string, style ViewStyle) SetStyle(tag string, style ViewStyle)
RemoveStyle(tag string) RemoveStyle(tag string)
MediaStyle(tag string, orientation, maxWidth, maxHeight int) ViewStyle MediaStyle(tag string, params MediaStyleParams) ViewStyle
SetMediaStyle(tag string, orientation, maxWidth, maxHeight int, style ViewStyle) SetMediaStyle(tag string, params MediaStyleParams, style ViewStyle)
StyleTags() []string StyleTags() []string
MediaStyles(tag string) []struct { MediaStyles(tag string) []struct {
Selectors string Selectors string
Orientation, MaxWidth, MaxHeight int Params MediaStyleParams
} }
Append(anotherTheme Theme) Append(anotherTheme Theme)
@ -71,7 +77,7 @@ func (rule mediaStyle) cssText() string {
builder := allocStringBuilder() builder := allocStringBuilder()
defer freeStringBuilder(builder) defer freeStringBuilder(builder)
switch rule.orientation { switch rule.Orientation {
case PortraitMedia: case PortraitMedia:
builder.WriteString(" and (orientation: portrait)") builder.WriteString(" and (orientation: portrait)")
@ -79,78 +85,107 @@ func (rule mediaStyle) cssText() string {
builder.WriteString(" and (orientation: landscape)") builder.WriteString(" and (orientation: landscape)")
} }
if rule.maxWidth > 0 { writeSize := func(tag string, minSize, maxSize int) {
builder.WriteString(" and (max-width: ") if minSize != maxSize {
builder.WriteString(strconv.Itoa(rule.maxWidth)) if minSize > 0 {
builder.WriteString("px)") 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))
}
} }
if rule.maxHeight > 0 { writeSize("width", rule.MinWidth, rule.MaxWidth)
builder.WriteString(" and (max-height: ") writeSize("height", rule.MinHeight, rule.MaxHeight)
builder.WriteString(strconv.Itoa(rule.maxHeight))
builder.WriteString("px)")
}
return builder.String() return builder.String()
} }
func parseMediaRule(text string) (mediaStyle, bool) { func parseMediaRule(text string) (mediaStyle, bool) {
rule := mediaStyle{ rule := mediaStyle{
orientation: DefaultMedia, styles: map[string]ViewStyle{},
maxWidth: 0,
maxHeight: 0,
styles: map[string]ViewStyle{},
} }
elements := strings.Split(text, ":") elements := strings.Split(text, ":")
for i := 1; i < len(elements); i++ { for i := 1; i < len(elements); i++ {
switch element := elements[i]; element { switch element := elements[i]; element {
case "portrait": case "portrait":
if rule.orientation != DefaultMedia { if rule.Orientation != DefaultMedia {
ErrorLog(`Duplicate orientation tag in the style section "` + text + `"`) ErrorLog(`Duplicate orientation tag in the style section "` + text + `"`)
return rule, false return rule, false
} }
rule.orientation = PortraitMedia rule.Orientation = PortraitMedia
case "landscape": case "landscape":
if rule.orientation != DefaultMedia { if rule.Orientation != DefaultMedia {
ErrorLog(`Duplicate orientation tag in the style section "` + text + `"`) ErrorLog(`Duplicate orientation tag in the style section "` + text + `"`)
return rule, false return rule, false
} }
rule.orientation = LandscapeMedia rule.Orientation = LandscapeMedia
default: default:
elementSize := func(name string) (int, bool) { elementSize := func(name string) (int, int, bool, error) {
if strings.HasPrefix(element, name) { if strings.HasPrefix(element, name) {
size, err := strconv.Atoi(element[len(name):]) var err error = nil
if err == nil && size > 0 { min := 0
return size, true 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)
} }
ErrorLogF(`Invalid style section name "%s": %s`, text, err.Error()) return min, max, true, err
return 0, false
} }
return 0, true return 0, 0, false, nil
} }
if size, ok := elementSize("width"); !ok || size > 0 { if min, max, ok, err := elementSize("width"); ok {
if !ok {
if err != nil {
ErrorLogF(`Invalid style section name "%s": %s`, text, err.Error())
return rule, false return rule, false
} }
if rule.maxWidth != 0 { if rule.MinWidth != 0 || rule.MaxWidth != 0 {
ErrorLog(`Duplicate "width" tag in the style section "` + text + `"`) ErrorLog(`Duplicate "width" tag in the style section "` + text + `"`)
return rule, false return rule, false
} }
rule.maxWidth = size if min == 0 && max == 0 {
} else if size, ok := elementSize("height"); !ok || size > 0 { ErrorLog(`Invalid arguments of "width" tag in the style section "` + text + `"`)
if !ok {
return rule, false return rule, false
} }
if rule.maxHeight != 0 {
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 {
ErrorLog(`Duplicate "height" tag in the style section "` + text + `"`) ErrorLog(`Duplicate "height" tag in the style section "` + text + `"`)
return rule, false return rule, false
} }
rule.maxHeight = size 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
} else { } else {
ErrorLogF(`Unknown element "%s" in the style section name "%s"`, element, text) ErrorLogF(`Unknown element "%s" in the style section name "%s"`, element, text)
return rule, false return rule, false
} }
@ -284,36 +319,53 @@ func (theme *theme) RemoveStyle(tag string) {
} }
} }
func (theme *theme) MediaStyle(tag string, orientation, maxWidth, maxHeight int) ViewStyle { func (theme *theme) MediaStyle(tag string, params MediaStyleParams) ViewStyle {
for _, styles := range theme.mediaStyles { for _, styles := range theme.mediaStyles {
if styles.orientation == orientation && styles.maxWidth == maxWidth && styles.maxHeight == maxHeight { if styles.Orientation == params.Orientation &&
styles.MaxWidth == params.MaxWidth &&
styles.MinWidth == params.MinWidth &&
styles.MaxHeight == params.MaxHeight &&
styles.MinHeight == params.MinHeight {
if style, ok := styles.styles[tag]; ok { if style, ok := styles.styles[tag]; ok {
return style return style
} }
} }
} }
if orientation == 0 && maxWidth <= 0 && maxHeight <= 0 {
if params.Orientation == 0 && params.MaxWidth == 0 && params.MinWidth == 0 &&
params.MaxHeight == 0 && params.MinHeight == 0 {
return theme.style(tag) return theme.style(tag)
} }
return nil return nil
} }
func (theme *theme) SetMediaStyle(tag string, orientation, maxWidth, maxHeight int, style ViewStyle) { func (theme *theme) SetMediaStyle(tag string, params MediaStyleParams, style ViewStyle) {
if maxWidth < 0 { if params.MaxWidth < 0 {
maxWidth = 0 params.MaxWidth = 0
} }
if maxHeight < 0 { if params.MinWidth < 0 {
maxHeight = 0 params.MinWidth = 0
}
if params.MaxHeight < 0 {
params.MaxHeight = 0
}
if params.MinHeight < 0 {
params.MinHeight = 0
} }
if orientation == DefaultMedia && maxWidth == 0 && maxHeight == 0 { if params.Orientation == 0 && params.MaxWidth == 0 && params.MinWidth == 0 &&
params.MaxHeight == 0 && params.MinHeight == 0 {
theme.SetStyle(tag, style) theme.SetStyle(tag, style)
return return
} }
for i, styles := range theme.mediaStyles { for i, styles := range theme.mediaStyles {
if styles.orientation == orientation && styles.maxWidth == maxWidth && styles.maxHeight == maxHeight { if styles.Orientation == params.Orientation &&
styles.MaxWidth == params.MaxWidth &&
styles.MinWidth == params.MinWidth &&
styles.MaxHeight == params.MaxHeight &&
styles.MinHeight == params.MinHeight {
if style != nil { if style != nil {
theme.mediaStyles[i].styles[tag] = style theme.mediaStyles[i].styles[tag] = style
} else { } else {
@ -325,10 +377,8 @@ func (theme *theme) SetMediaStyle(tag string, orientation, maxWidth, maxHeight i
if style != nil { if style != nil {
theme.mediaStyles = append(theme.mediaStyles, mediaStyle{ theme.mediaStyles = append(theme.mediaStyles, mediaStyle{
orientation: orientation, MediaStyleParams: params,
maxWidth: maxWidth, styles: map[string]ViewStyle{tag: style},
maxHeight: maxHeight,
styles: map[string]ViewStyle{tag: style},
}) })
theme.sortMediaStyles() theme.sortMediaStyles()
} }
@ -426,12 +476,12 @@ func (theme *theme) StyleTags() []string {
} }
func (theme *theme) MediaStyles(tag string) []struct { func (theme *theme) MediaStyles(tag string) []struct {
Selectors string Selectors string
Orientation, MaxWidth, MaxHeight int Params MediaStyleParams
} { } {
result := []struct { result := []struct {
Selectors string Selectors string
Orientation, MaxWidth, MaxHeight int Params MediaStyleParams
}{} }{}
prefix := tag + ":" prefix := tag + ":"
@ -439,13 +489,11 @@ func (theme *theme) MediaStyles(tag string) []struct {
for themeTag := range theme.styles { for themeTag := range theme.styles {
if strings.HasPrefix(themeTag, prefix) { if strings.HasPrefix(themeTag, prefix) {
result = append(result, struct { result = append(result, struct {
Selectors string Selectors string
Orientation, MaxWidth, MaxHeight int Params MediaStyleParams
}{ }{
Selectors: themeTag[prefixLen:], Selectors: themeTag[prefixLen:],
Orientation: DefaultMedia, Params: MediaStyleParams{},
MaxWidth: 0,
MaxHeight: 0,
}) })
} }
} }
@ -453,25 +501,21 @@ func (theme *theme) MediaStyles(tag string) []struct {
for _, media := range theme.mediaStyles { for _, media := range theme.mediaStyles {
if _, ok := media.styles[tag]; ok { if _, ok := media.styles[tag]; ok {
result = append(result, struct { result = append(result, struct {
Selectors string Selectors string
Orientation, MaxWidth, MaxHeight int Params MediaStyleParams
}{ }{
Selectors: "", Selectors: "",
Orientation: media.orientation, Params: media.MediaStyleParams,
MaxWidth: media.maxWidth,
MaxHeight: media.maxHeight,
}) })
} }
for themeTag := range media.styles { for themeTag := range media.styles {
if strings.HasPrefix(themeTag, prefix) { if strings.HasPrefix(themeTag, prefix) {
result = append(result, struct { result = append(result, struct {
Selectors string Selectors string
Orientation, MaxWidth, MaxHeight int Params MediaStyleParams
}{ }{
Selectors: themeTag[prefixLen:], Selectors: themeTag[prefixLen:],
Orientation: media.orientation, Params: media.MediaStyleParams,
MaxWidth: media.maxWidth,
MaxHeight: media.maxHeight,
}) })
} }
} }
@ -520,9 +564,11 @@ func (theme *theme) Append(anotherTheme Theme) {
for _, anotherMedia := range another.mediaStyles { for _, anotherMedia := range another.mediaStyles {
exists := false exists := false
for _, media := range theme.mediaStyles { for _, media := range theme.mediaStyles {
if anotherMedia.maxHeight == media.maxHeight && if anotherMedia.MinHeight == media.MinHeight &&
anotherMedia.maxWidth == media.maxWidth && anotherMedia.MaxHeight == media.MaxHeight &&
anotherMedia.orientation == media.orientation { anotherMedia.MinWidth == media.MinWidth &&
anotherMedia.MaxWidth == media.MaxWidth &&
anotherMedia.Orientation == media.Orientation {
for tag, style := range anotherMedia.styles { for tag, style := range anotherMedia.styles {
media.styles[tag] = style media.styles[tag] = style
} }
@ -726,13 +772,19 @@ func (theme *theme) addText(themeText string) bool {
func (theme *theme) sortMediaStyles() { func (theme *theme) sortMediaStyles() {
if len(theme.mediaStyles) > 1 { if len(theme.mediaStyles) > 1 {
sort.SliceStable(theme.mediaStyles, func(i, j int) bool { sort.SliceStable(theme.mediaStyles, func(i, j int) bool {
if theme.mediaStyles[i].orientation != theme.mediaStyles[j].orientation { if theme.mediaStyles[i].Orientation != theme.mediaStyles[j].Orientation {
return theme.mediaStyles[i].orientation < theme.mediaStyles[j].orientation return theme.mediaStyles[i].Orientation < theme.mediaStyles[j].Orientation
} }
if theme.mediaStyles[i].maxWidth != theme.mediaStyles[j].maxWidth { if theme.mediaStyles[i].MinWidth != theme.mediaStyles[j].MinWidth {
return theme.mediaStyles[i].maxWidth < theme.mediaStyles[j].maxWidth return theme.mediaStyles[i].MinWidth < theme.mediaStyles[j].MinWidth
} }
return theme.mediaStyles[i].maxHeight < theme.mediaStyles[j].maxHeight 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
}) })
} }
} }
@ -879,7 +931,7 @@ func (theme *theme) String() string {
buffer.WriteString(" = [\n") buffer.WriteString(" = [\n")
for _, tag := range tags { for _, tag := range tags {
if style, ok := styles[tag]; ok { if style, ok := styles[tag]; ok && len(style.AllTags()) > 0 {
buffer.WriteString("\t\t") buffer.WriteString("\t\t")
writeViewStyle(tag, style, buffer, "\t\t") writeViewStyle(tag, style, buffer, "\t\t")
buffer.WriteString(",\n") buffer.WriteString(",\n")
@ -891,7 +943,53 @@ func (theme *theme) String() string {
writeStyles(0, 0, 0, theme.styles) writeStyles(0, 0, 0, theme.styles)
for _, media := range theme.mediaStyles { for _, media := range theme.mediaStyles {
writeStyles(media.orientation, media.maxWidth, media.maxHeight, media.styles) //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")
buffer.WriteString(",\n")
}
}
buffer.WriteString("\t],\n")
}
} }
buffer.WriteString("}\n") buffer.WriteString("}\n")