mirror of https://github.com/anoshenko/rui.git
Added SizeFunc interface, changed SizeUnit struct
This commit is contained in:
parent
dcf8240235
commit
2f7e1bbab3
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
* Requires go 1.18 or higher
|
* Requires go 1.18 or higher
|
||||||
* The "interface{}" type replaced by "any"
|
* The "interface{}" type replaced by "any"
|
||||||
|
* Added SizeFunc interface and Function field to SizeUnit struct
|
||||||
|
* Added MaxSize, MinSize, SumSize, SubSize, MulSize, DivSize, ClampSize functions
|
||||||
* Added "list-row-gap", "list-column-gap", "accent-color", "tab-size", "overflow",
|
* Added "list-row-gap", "list-column-gap", "accent-color", "tab-size", "overflow",
|
||||||
"arrow", "arrow-align", "arrow-size", "arrow-width", and "arrow-offset" properties
|
"arrow", "arrow-align", "arrow-size", "arrow-width", and "arrow-offset" properties
|
||||||
* Added "@ruiArrowSize" and "@ruiArrowWidth" constants to the default theme
|
* Added "@ruiArrowSize" and "@ruiArrowWidth" constants to the default theme
|
||||||
|
|
62
README-ru.md
62
README-ru.md
|
@ -57,12 +57,12 @@
|
||||||
SizeUnit объявлена как
|
SizeUnit объявлена как
|
||||||
|
|
||||||
type SizeUnit struct {
|
type SizeUnit struct {
|
||||||
Type SizeUnitType
|
Type SizeUnitType
|
||||||
Value float64
|
Value float64
|
||||||
|
Function SizeFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
где Type - тип размера;
|
где Type - тип размера; Value - размер; Function - функция (используется только если Type == SizeFunction, в остальных случаях игнорируется).
|
||||||
Value - размер
|
|
||||||
|
|
||||||
Тип может принимать следующие значения:
|
Тип может принимать следующие значения:
|
||||||
|
|
||||||
|
@ -79,12 +79,13 @@ Value - размер
|
||||||
| 8 | SizeInMM | поле Value определяет размер в миллиметрах. |
|
| 8 | SizeInMM | поле Value определяет размер в миллиметрах. |
|
||||||
| 9 | SizeInCM | поле Value определяет размер в сантиметрах. |
|
| 9 | SizeInCM | поле Value определяет размер в сантиметрах. |
|
||||||
| 10 | SizeInFraction | поле Value определяет размер в частях. Используется только для задания размеров ячеек в GridLayout. |
|
| 10 | SizeInFraction | поле Value определяет размер в частях. Используется только для задания размеров ячеек в GridLayout. |
|
||||||
|
| 11 | SizeFunction | поле Function задает функцию для вычисления размера. Значение поля Value игнорируется |
|
||||||
|
|
||||||
Для более наглядного и простого задания переменных типа SizeUnit могут использоваться функции приведенные ниже
|
Для более наглядного и простого задания переменных типа SizeUnit могут использоваться функции приведенные ниже
|
||||||
|
|
||||||
| Функция | Эквивалентное определение |
|
| Функция | Эквивалентное определение |
|
||||||
|----------------|----------------------------------------------------|
|
|----------------|----------------------------------------------------|
|
||||||
| rui.AutoSize() | rui.SizeUnit{ Type: rui.Auto, Value: 0 } |
|
| rui.AutoSize() | rui.SizeUnit{ Type: rui.Auto } |
|
||||||
| rui.Px(n) | rui.SizeUnit{ Type: rui.SizeInPixel, Value: n } |
|
| rui.Px(n) | rui.SizeUnit{ Type: rui.SizeInPixel, Value: n } |
|
||||||
| rui.Em(n) | rui.SizeUnit{ Type: rui.SizeInEM, Value: n } |
|
| rui.Em(n) | rui.SizeUnit{ Type: rui.SizeInEM, Value: n } |
|
||||||
| rui.Ex(n) | rui.SizeUnit{ Type: rui.SizeInEX, Value: n } |
|
| rui.Ex(n) | rui.SizeUnit{ Type: rui.SizeInEX, Value: n } |
|
||||||
|
@ -96,7 +97,10 @@ Value - размер
|
||||||
| rui.Cm(n) | rui.SizeUnit{ Type: rui.SizeInCM, Value: n } |
|
| rui.Cm(n) | rui.SizeUnit{ Type: rui.SizeInCM, Value: n } |
|
||||||
| rui.Fr(n) | rui.SizeUnit{ Type: rui.SizeInFraction, Value: n } |
|
| rui.Fr(n) | rui.SizeUnit{ Type: rui.SizeInFraction, Value: n } |
|
||||||
|
|
||||||
Переменные типа SizeUnit имеют текстовое представление (зачем оно нужно будет описано ниже). Текстовое представление состоит из числа (равному значению поля Value) и следующим за ним суффиксом определяющим тип. Исключением является значение типа Auto, которое имеет представление “auto”. Суффиксы перечислены в следующей таблице:
|
Переменные типа SizeUnit имеют текстовое представление (зачем оно нужно будет описано ниже).
|
||||||
|
Текстовое представление состоит из числа (равному значению поля Value) и следующим за ним суффиксом определяющим тип.
|
||||||
|
Исключением является значение типа Auto, которое имеет представление “auto” и
|
||||||
|
значение типа SizeFunction, которое имеет особое представление. Суффиксы перечислены в следующей таблице:
|
||||||
|
|
||||||
| Суффикс | Тип |
|
| Суффикс | Тип |
|
||||||
|:-------:|----------------|
|
|:-------:|----------------|
|
||||||
|
@ -119,6 +123,52 @@ Value - размер
|
||||||
|
|
||||||
Получить текстовое представление структуры можно свойством String()
|
Получить текстовое представление структуры можно свойством String()
|
||||||
|
|
||||||
|
#### SizeFunc
|
||||||
|
|
||||||
|
Интерфейс SizeFunc используется для задания функции вычисляющей SizeUnit. Рассмотрим функции на примере функции min.
|
||||||
|
|
||||||
|
Функция min находит минимальное значение среди заданных аргументов. Данная функция задается с помощью функции MinSize, объявленной как:
|
||||||
|
|
||||||
|
func MinSize(arg0, arg1 any, args ...any) SizeFunc
|
||||||
|
|
||||||
|
Функция имеет 2 и более аргументов, каждый из которых может быть или SizeUnit или SizeFunc или string являющееся константой или
|
||||||
|
текстовым представлением SizeUnit или SizeFunc.
|
||||||
|
|
||||||
|
Примеры
|
||||||
|
|
||||||
|
rui.MizSize(rui.Percent(50), rui.Px(250))
|
||||||
|
rui.MizSize("50%", rui.Px(250), "40em")
|
||||||
|
rui.MizSize(rui.Percent(50), "@a1")
|
||||||
|
|
||||||
|
Функция min имеет следующее текстовое представление
|
||||||
|
|
||||||
|
"min(<arg1>, <arg2>, ...)"
|
||||||
|
|
||||||
|
где arg1, arg2, ... должны быть текстовым представлением SizeUnit или SizeFunc или константой. Например
|
||||||
|
|
||||||
|
"min(50%, 250px)"
|
||||||
|
"min(75%, @a1)"
|
||||||
|
|
||||||
|
Интерфейс SizeFunc реализует интерфейс fmt.Stringer.
|
||||||
|
Функция String() этого интерфейса возвращает текстовое представление SizeFunc.
|
||||||
|
|
||||||
|
Помимо min имеются следующие функции
|
||||||
|
|
||||||
|
| Текстовое представление | Функция для создания | Описание
|
||||||
|
|------------------------------|--------------------------------------|-------------------------------------------------|
|
||||||
|
| "min(<arg1>, <arg2>, ...)" | MaxSize(arg0, arg1 any, args ...any) | находит минимальное значение среди аргументов |
|
||||||
|
| "sum(<arg1>, <arg2>, ...)" | SumSize(arg0, arg1 any, args ...any) | находит сумму значений аргументов |
|
||||||
|
| "sub(<arg1>, <arg2>)" | SubSize(arg0, arg1 any) | находит разность значений аргументов |
|
||||||
|
| "mul(<arg1>, <arg2>)" | MulSize(arg0, arg1 any) | находит результат умножения значений аргументов |
|
||||||
|
| "div(<arg1>, <arg2>)" | DivSize(arg0, arg1 any) | находит результат деления значений аргументов |
|
||||||
|
| "clamp(<min>, <val>, <max>)" | ClampSize(min, val, max any) | ограничивает значение заданным диапазоном |
|
||||||
|
|
||||||
|
Дополнительные пояснения к функции "clamp(<min>, <val>, <max>)": результат вычисляется следующим образом:
|
||||||
|
|
||||||
|
* if min ≤ val ≤ max then val;
|
||||||
|
* if val < min then min;
|
||||||
|
* if max < val then max;
|
||||||
|
|
||||||
### Color
|
### Color
|
||||||
|
|
||||||
Тип Color описывает 32-битный цвет в формате ARGB:
|
Тип Color описывает 32-битный цвет в формате ARGB:
|
||||||
|
|
56
README.md
56
README.md
|
@ -62,9 +62,10 @@ SizeUnit is declared as
|
||||||
type SizeUnit struct {
|
type SizeUnit struct {
|
||||||
Type SizeUnitType
|
Type SizeUnitType
|
||||||
Value float64
|
Value float64
|
||||||
|
Function SizeFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
where Type is the type of size; Value is the size.
|
where Type is the type of size; Value is the size; Function is function (used only if Type == SizeFunction, ignored otherwise)
|
||||||
|
|
||||||
The Type can take the following values:
|
The Type can take the following values:
|
||||||
|
|
||||||
|
@ -81,12 +82,13 @@ The Type can take the following values:
|
||||||
| 8 | SizeInMM | the Value field specifies the size in millimeters. |
|
| 8 | SizeInMM | the Value field specifies the size in millimeters. |
|
||||||
| 9 | SizeInCM | the Value field defines the size in centimeters. |
|
| 9 | SizeInCM | the Value field defines the size in centimeters. |
|
||||||
| 10 | SizeInFraction | the Value field specifies the size in parts. Used only for sizing cells of the GridLayout. |
|
| 10 | SizeInFraction | the Value field specifies the size in parts. Used only for sizing cells of the GridLayout. |
|
||||||
|
| 11 | SizeFunction | the Function field specifies a function for calculating the size. The Value field is ignored |
|
||||||
|
|
||||||
For a more visual and simple setting of variables of the SizeUnit type, the functions below can be used.
|
For a more visual and simple setting of variables of the SizeUnit type, the functions below can be used.
|
||||||
|
|
||||||
| Function | Equivalent definition |
|
| Function | Equivalent definition |
|
||||||
|----------------|----------------------------------------------------|
|
|----------------|----------------------------------------------------|
|
||||||
| rui.AutoSize() | rui.SizeUnit{ Type: rui.Auto, Value: 0 } |
|
| rui.AutoSize() | rui.SizeUnit{ Type: rui.Auto } |
|
||||||
| rui.Px(n) | rui.SizeUnit{ Type: rui.SizeInPixel, Value: n } |
|
| rui.Px(n) | rui.SizeUnit{ Type: rui.SizeInPixel, Value: n } |
|
||||||
| rui.Em(n) | rui.SizeUnit{ Type: rui.SizeInEM, Value: n } |
|
| rui.Em(n) | rui.SizeUnit{ Type: rui.SizeInEM, Value: n } |
|
||||||
| rui.Ex(n) | rui.SizeUnit{ Type: rui.SizeInEX, Value: n } |
|
| rui.Ex(n) | rui.SizeUnit{ Type: rui.SizeInEX, Value: n } |
|
||||||
|
@ -100,8 +102,8 @@ For a more visual and simple setting of variables of the SizeUnit type, the func
|
||||||
|
|
||||||
Variables of the SizeUnit type have a textual representation (why you need it will be described below).
|
Variables of the SizeUnit type have a textual representation (why you need it will be described below).
|
||||||
The textual representation consists of a number (equal to the value of the Value field) followed by
|
The textual representation consists of a number (equal to the value of the Value field) followed by
|
||||||
a suffix defining the type. An exception is a value of type Auto, which has the representation “auto”.
|
a suffix defining the type.
|
||||||
The suffixes are listed in the following table:
|
The exceptions are a value of type Auto, which has the representation "auto", and a value of type SizeFunction, which has a special representation. The suffixes are listed in the following table:
|
||||||
|
|
||||||
| Suffix | Type |
|
| Suffix | Type |
|
||||||
|:------:|----------------|
|
|:------:|----------------|
|
||||||
|
@ -124,6 +126,52 @@ To convert the textual representation to the SizeUnit structure, is used the fun
|
||||||
|
|
||||||
You can get a textual representation of the structure using the String() function of SizeUnit structure
|
You can get a textual representation of the structure using the String() function of SizeUnit structure
|
||||||
|
|
||||||
|
#### SizeFunc
|
||||||
|
|
||||||
|
The SizeFunc interface is used to define a function that calculates SizeUnit. Let's consider functions using the min function as an example.
|
||||||
|
|
||||||
|
The min function finds the minimum value among the given arguments. This function is specified using the MinSize function, declared as:
|
||||||
|
|
||||||
|
func MinSize(arg0, arg1 any, args ...any) SizeFunc
|
||||||
|
|
||||||
|
The function has 2 or more arguments, each of which can be either SizeUnit or SizeFunc or string which is a constant or
|
||||||
|
text representation of SizeUnit or SizeFunc.
|
||||||
|
|
||||||
|
Examples
|
||||||
|
|
||||||
|
rui.MizSize(rui.Percent(50), rui.Px(250))
|
||||||
|
rui.MizSize("50%", rui.Px(250), "40em")
|
||||||
|
rui.MizSize(rui.Percent(50), "@a1")
|
||||||
|
|
||||||
|
The min function has the following text representation
|
||||||
|
|
||||||
|
"min(<arg1>, <arg2>, ...)"
|
||||||
|
|
||||||
|
where arg1, arg2, ... must be a text representation of SizeUnit, or SizeFunc, or a constant. For example
|
||||||
|
|
||||||
|
"min(50%, 250px)"
|
||||||
|
"min(75%, @a1)"
|
||||||
|
|
||||||
|
The SizeFunc interface implements the fmt.Stringer interface.
|
||||||
|
The String() function of this interface returns the textual representation of SizeFunc.
|
||||||
|
|
||||||
|
In addition to "min", there are the following functions
|
||||||
|
|
||||||
|
| Text representation | Function to create | Description |
|
||||||
|
|------------------------------|--------------------------------------|----------------------------------------------------------|
|
||||||
|
| "min(<arg1>, <arg2>, ...)" | MaxSize(arg0, arg1 any, args ...any) | finds the minimum value among the arguments |
|
||||||
|
| "sum(<arg1>, <arg2>, ...)" | SumSize(arg0, arg1 any, args ...any) | calculates the sum of the argument values |
|
||||||
|
| "sub(<arg1>, <arg2>)" | SubSize(arg0, arg1 any) | calculates the subtraction of argument values |
|
||||||
|
| "mul(<arg1>, <arg2>)" | MulSize(arg0, arg1 any) | calculates the result of multiplying the argument values |
|
||||||
|
| "div(<arg1>, <arg2>)" | DivSize(arg0, arg1 any) | calculates the result of dividing the argument values |
|
||||||
|
| "clamp(<min>, <val>, <max>)" | ClampSize(min, val, max any) | limits value to specified range |
|
||||||
|
|
||||||
|
Additional explanations for the function "clamp(<min>, <val>, <max>)": the result is calculated as follows:
|
||||||
|
|
||||||
|
* if min ≤ val ≤ max then val;
|
||||||
|
* if val < min then min;
|
||||||
|
* if max < val then max;
|
||||||
|
|
||||||
### Color
|
### Color
|
||||||
|
|
||||||
The Color type describes a 32-bit ARGB color:
|
The Color type describes a 32-bit ARGB color:
|
||||||
|
|
|
@ -219,9 +219,9 @@ func (image *backgroundImage) cssStyle(session Session) string {
|
||||||
|
|
||||||
if width.Type != Auto || height.Type != Auto {
|
if width.Type != Auto || height.Type != Auto {
|
||||||
buffer.WriteString(` / `)
|
buffer.WriteString(` / `)
|
||||||
buffer.WriteString(width.cssString("auto"))
|
buffer.WriteString(width.cssString("auto", session))
|
||||||
buffer.WriteRune(' ')
|
buffer.WriteRune(' ')
|
||||||
buffer.WriteString(height.cssString("auto"))
|
buffer.WriteString(height.cssString("auto", session))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -316,9 +316,9 @@ func (gradient *backgroundConicGradient) cssStyle(session Session) string {
|
||||||
buffer.WriteRune(' ')
|
buffer.WriteRune(' ')
|
||||||
}
|
}
|
||||||
buffer.WriteString("at ")
|
buffer.WriteString("at ")
|
||||||
buffer.WriteString(x.cssString("50%"))
|
buffer.WriteString(x.cssString("50%", session))
|
||||||
buffer.WriteString(" ")
|
buffer.WriteString(" ")
|
||||||
buffer.WriteString(y.cssString("50%"))
|
buffer.WriteString(y.cssString("50%", session))
|
||||||
comma = true
|
comma = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -261,19 +261,18 @@ func (gradient *backgroundGradient) writeGradient(session Session, buffer *strin
|
||||||
switch value := point.Pos.(type) {
|
switch value := point.Pos.(type) {
|
||||||
case string:
|
case string:
|
||||||
if value != "" {
|
if value != "" {
|
||||||
if value[0] == '@' {
|
if value, ok := session.resolveConstants(value); ok {
|
||||||
value, _ = session.Constant(value[1:])
|
if pos, ok := StringToSizeUnit(value); ok && pos.Type != Auto {
|
||||||
}
|
buffer.WriteRune(' ')
|
||||||
if pos, ok := StringToSizeUnit(value); ok && pos.Type != Auto {
|
buffer.WriteString(pos.cssString("", session))
|
||||||
buffer.WriteRune(' ')
|
}
|
||||||
buffer.WriteString(pos.cssString(""))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case SizeUnit:
|
case SizeUnit:
|
||||||
if value.Type != Auto {
|
if value.Type != Auto {
|
||||||
buffer.WriteRune(' ')
|
buffer.WriteRune(' ')
|
||||||
buffer.WriteString(value.cssString(""))
|
buffer.WriteString(value.cssString("", session))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -512,9 +511,9 @@ func (gradient *backgroundRadialGradient) cssStyle(session Session) string {
|
||||||
if r, ok := StringToSizeUnit(text); ok && r.Type != Auto {
|
if r, ok := StringToSizeUnit(text); ok && r.Type != Auto {
|
||||||
buffer.WriteString("ellipse ")
|
buffer.WriteString("ellipse ")
|
||||||
shapeText = ""
|
shapeText = ""
|
||||||
buffer.WriteString(r.cssString(""))
|
buffer.WriteString(r.cssString("", session))
|
||||||
buffer.WriteString(" ")
|
buffer.WriteString(" ")
|
||||||
buffer.WriteString(r.cssString(""))
|
buffer.WriteString(r.cssString("", session))
|
||||||
buffer.WriteString(" ")
|
buffer.WriteString(" ")
|
||||||
} else {
|
} else {
|
||||||
ErrorLog(`Invalid radial gradient radius: ` + text)
|
ErrorLog(`Invalid radial gradient radius: ` + text)
|
||||||
|
@ -539,9 +538,9 @@ func (gradient *backgroundRadialGradient) cssStyle(session Session) string {
|
||||||
if value.Type != Auto {
|
if value.Type != Auto {
|
||||||
buffer.WriteString("ellipse ")
|
buffer.WriteString("ellipse ")
|
||||||
shapeText = ""
|
shapeText = ""
|
||||||
buffer.WriteString(value.cssString(""))
|
buffer.WriteString(value.cssString("", session))
|
||||||
buffer.WriteString(" ")
|
buffer.WriteString(" ")
|
||||||
buffer.WriteString(value.cssString(""))
|
buffer.WriteString(value.cssString("", session))
|
||||||
buffer.WriteString(" ")
|
buffer.WriteString(" ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -553,7 +552,7 @@ func (gradient *backgroundRadialGradient) cssStyle(session Session) string {
|
||||||
buffer.WriteString("ellipse ")
|
buffer.WriteString("ellipse ")
|
||||||
shapeText = ""
|
shapeText = ""
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
buffer.WriteString(value[i].cssString("50%"))
|
buffer.WriteString(value[i].cssString("50%", session))
|
||||||
buffer.WriteString(" ")
|
buffer.WriteString(" ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -568,13 +567,13 @@ func (gradient *backgroundRadialGradient) cssStyle(session Session) string {
|
||||||
if value[i] != nil {
|
if value[i] != nil {
|
||||||
switch value := value[i].(type) {
|
switch value := value[i].(type) {
|
||||||
case SizeUnit:
|
case SizeUnit:
|
||||||
buffer.WriteString(value.cssString("50%"))
|
buffer.WriteString(value.cssString("50%", session))
|
||||||
buffer.WriteString(" ")
|
buffer.WriteString(" ")
|
||||||
|
|
||||||
case string:
|
case string:
|
||||||
if text, ok := session.resolveConstants(value); ok {
|
if text, ok := session.resolveConstants(value); ok {
|
||||||
if size, err := stringToSizeUnit(text); err == nil {
|
if size, err := stringToSizeUnit(text); err == nil {
|
||||||
buffer.WriteString(size.cssString("50%"))
|
buffer.WriteString(size.cssString("50%", session))
|
||||||
buffer.WriteString(" ")
|
buffer.WriteString(" ")
|
||||||
} else {
|
} else {
|
||||||
buffer.WriteString("50% ")
|
buffer.WriteString("50% ")
|
||||||
|
@ -597,9 +596,9 @@ func (gradient *backgroundRadialGradient) cssStyle(session Session) string {
|
||||||
buffer.WriteString(shapeText)
|
buffer.WriteString(shapeText)
|
||||||
}
|
}
|
||||||
buffer.WriteString("at ")
|
buffer.WriteString("at ")
|
||||||
buffer.WriteString(x.cssString("50%"))
|
buffer.WriteString(x.cssString("50%", session))
|
||||||
buffer.WriteString(" ")
|
buffer.WriteString(" ")
|
||||||
buffer.WriteString(y.cssString("50%"))
|
buffer.WriteString(y.cssString("50%", session))
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.WriteString(", ")
|
buffer.WriteString(", ")
|
||||||
|
|
|
@ -658,11 +658,14 @@ func (border *borderProperty) cssWidth(builder cssBuilder, session Session) {
|
||||||
borders.Top.Width == borders.Left.Width &&
|
borders.Top.Width == borders.Left.Width &&
|
||||||
borders.Top.Width == borders.Bottom.Width {
|
borders.Top.Width == borders.Bottom.Width {
|
||||||
if borders.Top.Width.Type != Auto {
|
if borders.Top.Width.Type != Auto {
|
||||||
builder.add("border-width", borders.Top.Width.cssString("0"))
|
builder.add("border-width", borders.Top.Width.cssString("0", session))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
builder.addValues("border-width", " ", borders.Top.Width.cssString("0"),
|
builder.addValues("border-width", " ",
|
||||||
borders.Right.Width.cssString("0"), borders.Bottom.Width.cssString("0"), borders.Left.Width.cssString("0"))
|
borders.Top.Width.cssString("0", session),
|
||||||
|
borders.Right.Width.cssString("0", session),
|
||||||
|
borders.Bottom.Width.cssString("0", session),
|
||||||
|
borders.Left.Width.cssString("0", session))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
15
bounds.go
15
bounds.go
|
@ -213,18 +213,21 @@ func (bounds *Bounds) String() string {
|
||||||
bounds.Bottom.String() + "," + bounds.Left.String()
|
bounds.Bottom.String() + "," + bounds.Left.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bounds *Bounds) cssValue(tag string, builder cssBuilder) {
|
func (bounds *Bounds) cssValue(tag string, builder cssBuilder, session Session) {
|
||||||
if bounds.allFieldsEqual() {
|
if bounds.allFieldsEqual() {
|
||||||
builder.add(tag, bounds.Top.cssString("0"))
|
builder.add(tag, bounds.Top.cssString("0", session))
|
||||||
} else {
|
} else {
|
||||||
builder.addValues(tag, " ", bounds.Top.cssString("0"), bounds.Right.cssString("0"),
|
builder.addValues(tag, " ",
|
||||||
bounds.Bottom.cssString("0"), bounds.Left.cssString("0"))
|
bounds.Top.cssString("0", session),
|
||||||
|
bounds.Right.cssString("0", session),
|
||||||
|
bounds.Bottom.cssString("0", session),
|
||||||
|
bounds.Left.cssString("0", session))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bounds *Bounds) cssString() string {
|
func (bounds *Bounds) cssString(session Session) string {
|
||||||
var builder cssValueBuilder
|
var builder cssValueBuilder
|
||||||
bounds.cssValue("", &builder)
|
bounds.cssValue("", &builder, session)
|
||||||
return builder.finish()
|
return builder.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -592,7 +592,7 @@ func (canvas *canvasData) writeFont(name string, script *strings.Builder) {
|
||||||
|
|
||||||
func (canvas *canvasData) SetFont(name string, size SizeUnit) {
|
func (canvas *canvasData) SetFont(name string, size SizeUnit) {
|
||||||
canvas.script.WriteString("\nctx.font = '")
|
canvas.script.WriteString("\nctx.font = '")
|
||||||
canvas.script.WriteString(size.cssString("1em"))
|
canvas.script.WriteString(size.cssString("1rem", canvas.View().Session()))
|
||||||
canvas.writeFont(name, &canvas.script)
|
canvas.writeFont(name, &canvas.script)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -616,7 +616,7 @@ func (canvas *canvasData) setFontWithParams(name string, size SizeUnit, params F
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
script.WriteString(size.cssString("1em"))
|
script.WriteString(size.cssString("1rem", canvas.View().Session()))
|
||||||
switch params.LineHeight.Type {
|
switch params.LineHeight.Type {
|
||||||
case Auto:
|
case Auto:
|
||||||
|
|
||||||
|
@ -634,7 +634,7 @@ func (canvas *canvasData) setFontWithParams(name string, size SizeUnit, params F
|
||||||
|
|
||||||
default:
|
default:
|
||||||
script.WriteString("/")
|
script.WriteString("/")
|
||||||
script.WriteString(params.LineHeight.cssString(""))
|
script.WriteString(params.LineHeight.cssString("", canvas.View().Session()))
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.writeFont(name, script)
|
canvas.writeFont(name, script)
|
||||||
|
|
|
@ -236,7 +236,7 @@ func (button *checkboxData) cssStyle(self View, builder cssBuilder) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if gap, ok := sizeConstant(session, "ruiCheckboxGap"); ok && gap.Type != Auto && gap.Value > 0 {
|
if gap, ok := sizeConstant(session, "ruiCheckboxGap"); ok && gap.Type != Auto && gap.Value > 0 {
|
||||||
builder.add("gap", gap.cssString("0"))
|
builder.add("gap", gap.cssString("0", session))
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.add("align-items", "stretch")
|
builder.add("align-items", "stretch")
|
||||||
|
|
|
@ -167,8 +167,9 @@ func (separator *columnSeparatorProperty) cssValue(session Session) string {
|
||||||
buffer := allocStringBuilder()
|
buffer := allocStringBuilder()
|
||||||
defer freeStringBuilder(buffer)
|
defer freeStringBuilder(buffer)
|
||||||
|
|
||||||
if value.Width.Type != Auto && value.Width.Type != SizeInFraction && value.Width.Value > 0 {
|
if value.Width.Type != Auto && value.Width.Type != SizeInFraction &&
|
||||||
buffer.WriteString(value.Width.cssString(""))
|
(value.Width.Value > 0 || value.Width.Type == SizeFunction) {
|
||||||
|
buffer.WriteString(value.Width.cssString("", session))
|
||||||
}
|
}
|
||||||
|
|
||||||
styles := enumProperties[BorderStyle].cssValues
|
styles := enumProperties[BorderStyle].cssValues
|
||||||
|
|
|
@ -145,7 +145,7 @@ func (style *viewStyle) gridCellSizesCSS(tag string, session Session) string {
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
if cellSize[0].Type != Auto {
|
if cellSize[0].Type != Auto {
|
||||||
return `repeat(auto-fill, ` + cellSize[0].cssString(`auto`) + `)`
|
return `repeat(auto-fill, ` + cellSize[0].cssString(`auto`, session) + `)`
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -161,14 +161,14 @@ func (style *viewStyle) gridCellSizesCSS(tag string, session Session) string {
|
||||||
}
|
}
|
||||||
if !allAuto {
|
if !allAuto {
|
||||||
if allEqual {
|
if allEqual {
|
||||||
return fmt.Sprintf(`repeat(%d, %s)`, len(cellSize), cellSize[0].cssString(`auto`))
|
return fmt.Sprintf(`repeat(%d, %s)`, len(cellSize), cellSize[0].cssString(`auto`, session))
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer := allocStringBuilder()
|
buffer := allocStringBuilder()
|
||||||
defer freeStringBuilder(buffer)
|
defer freeStringBuilder(buffer)
|
||||||
for _, size := range cellSize {
|
for _, size := range cellSize {
|
||||||
buffer.WriteRune(' ')
|
buffer.WriteRune(' ')
|
||||||
buffer.WriteString(size.cssString(`auto`))
|
buffer.WriteString(size.cssString(`auto`, session))
|
||||||
}
|
}
|
||||||
return buffer.String()
|
return buffer.String()
|
||||||
}
|
}
|
||||||
|
|
10
listView.go
10
listView.go
|
@ -563,13 +563,13 @@ func (listView *listViewData) itemAlign(self View, buffer *strings.Builder) {
|
||||||
func (listView *listViewData) itemSize(self View, buffer *strings.Builder) {
|
func (listView *listViewData) itemSize(self View, buffer *strings.Builder) {
|
||||||
if itemWidth := GetListItemWidth(listView); itemWidth.Type != Auto {
|
if itemWidth := GetListItemWidth(listView); itemWidth.Type != Auto {
|
||||||
buffer.WriteString(` min-width: `)
|
buffer.WriteString(` min-width: `)
|
||||||
buffer.WriteString(itemWidth.cssString(""))
|
buffer.WriteString(itemWidth.cssString("", listView.Session()))
|
||||||
buffer.WriteRune(';')
|
buffer.WriteRune(';')
|
||||||
}
|
}
|
||||||
|
|
||||||
if itemHeight := GetListItemHeight(listView); itemHeight.Type != Auto {
|
if itemHeight := GetListItemHeight(listView); itemHeight.Type != Auto {
|
||||||
buffer.WriteString(` min-height: `)
|
buffer.WriteString(` min-height: `)
|
||||||
buffer.WriteString(itemHeight.cssString(""))
|
buffer.WriteString(itemHeight.cssString("", listView.Session()))
|
||||||
buffer.WriteRune(';')
|
buffer.WriteRune(';')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -659,7 +659,7 @@ func (listView *listViewData) checkboxItemDiv(self View, checkbox, hCheckboxAlig
|
||||||
|
|
||||||
if gap, ok := sizeConstant(listView.session, "ruiCheckboxGap"); ok && gap.Type != Auto {
|
if gap, ok := sizeConstant(listView.session, "ruiCheckboxGap"); ok && gap.Type != Auto {
|
||||||
itemStyleBuilder.WriteString(` grid-gap: `)
|
itemStyleBuilder.WriteString(` grid-gap: `)
|
||||||
itemStyleBuilder.WriteString(gap.cssString("auto"))
|
itemStyleBuilder.WriteString(gap.cssString("auto", listView.Session()))
|
||||||
itemStyleBuilder.WriteRune(';')
|
itemStyleBuilder.WriteRune(';')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -896,13 +896,13 @@ func (listView *listViewData) htmlSubviews(self View, buffer *strings.Builder) {
|
||||||
|
|
||||||
if gap := GetListRowGap(listView); gap.Type != Auto {
|
if gap := GetListRowGap(listView); gap.Type != Auto {
|
||||||
buffer.WriteString(` row-gap: `)
|
buffer.WriteString(` row-gap: `)
|
||||||
buffer.WriteString(gap.cssString("0"))
|
buffer.WriteString(gap.cssString("0", listView.Session()))
|
||||||
buffer.WriteRune(';')
|
buffer.WriteRune(';')
|
||||||
}
|
}
|
||||||
|
|
||||||
if gap := GetListColumnGap(listView); gap.Type != Auto {
|
if gap := GetListColumnGap(listView); gap.Type != Auto {
|
||||||
buffer.WriteString(` column-gap: `)
|
buffer.WriteString(` column-gap: `)
|
||||||
buffer.WriteString(gap.cssString("0"))
|
buffer.WriteString(gap.cssString("0", listView.Session()))
|
||||||
buffer.WriteRune(';')
|
buffer.WriteRune(';')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -103,18 +103,18 @@ type ViewOutline struct {
|
||||||
Width SizeUnit
|
Width SizeUnit
|
||||||
}
|
}
|
||||||
|
|
||||||
func (outline ViewOutline) cssValue(builder cssBuilder) {
|
func (outline ViewOutline) cssValue(builder cssBuilder, session Session) {
|
||||||
values := enumProperties[BorderStyle].cssValues
|
values := enumProperties[BorderStyle].cssValues
|
||||||
if outline.Style > 0 && outline.Style < len(values) && outline.Color.Alpha() > 0 &&
|
if outline.Style > 0 && outline.Style < len(values) && outline.Color.Alpha() > 0 &&
|
||||||
outline.Width.Type != Auto && outline.Width.Type != SizeInFraction &&
|
outline.Width.Type != Auto && outline.Width.Type != SizeInFraction &&
|
||||||
outline.Width.Type != SizeInPercent && outline.Width.Value > 0 {
|
outline.Width.Type != SizeInPercent && outline.Width.Value > 0 {
|
||||||
builder.addValues("outline", " ", outline.Width.cssString("0"), values[outline.Style], outline.Color.cssString())
|
builder.addValues("outline", " ", outline.Width.cssString("0", session), values[outline.Style], outline.Color.cssString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (outline ViewOutline) cssString() string {
|
func (outline ViewOutline) cssString(session Session) string {
|
||||||
var builder cssValueBuilder
|
var builder cssValueBuilder
|
||||||
outline.cssValue(&builder)
|
outline.cssValue(&builder, session)
|
||||||
return builder.finish()
|
return builder.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,9 @@ func valueToSizeUnit(value any, session Session) (SizeUnit, bool) {
|
||||||
case SizeUnit:
|
case SizeUnit:
|
||||||
return value, true
|
return value, true
|
||||||
|
|
||||||
|
case SizeFunc:
|
||||||
|
return SizeUnit{Type: SizeFunction, Function: value}, true
|
||||||
|
|
||||||
case string:
|
case string:
|
||||||
if text, ok := session.resolveConstants(value); ok {
|
if text, ok := session.resolveConstants(value); ok {
|
||||||
return StringToSizeUnit(text)
|
return StringToSizeUnit(text)
|
||||||
|
|
|
@ -539,13 +539,20 @@ func (properties *propertyList) setSizeProperty(tag string, value any) bool {
|
||||||
switch value := value.(type) {
|
switch value := value.(type) {
|
||||||
case string:
|
case string:
|
||||||
var ok bool
|
var ok bool
|
||||||
if size, ok = StringToSizeUnit(value); !ok {
|
if fn := parseSizeFunc(value); fn != nil {
|
||||||
|
size.Type = SizeFunction
|
||||||
|
size.Function = fn
|
||||||
|
} else if size, ok = StringToSizeUnit(value); !ok {
|
||||||
invalidPropertyValue(tag, value)
|
invalidPropertyValue(tag, value)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case SizeUnit:
|
case SizeUnit:
|
||||||
size = value
|
size = value
|
||||||
|
|
||||||
|
case SizeFunc:
|
||||||
|
size.Type = SizeFunction
|
||||||
|
size.Function = value
|
||||||
|
|
||||||
case float32:
|
case float32:
|
||||||
size.Type = SizeInPixel
|
size.Type = SizeInPixel
|
||||||
size.Value = float64(value)
|
size.Value = float64(value)
|
||||||
|
|
24
radius.go
24
radius.go
|
@ -455,7 +455,7 @@ func (radius BoxRadius) String() string {
|
||||||
return buffer.String()
|
return buffer.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (radius BoxRadius) cssValue(builder cssBuilder) {
|
func (radius BoxRadius) cssValue(builder cssBuilder, session Session) {
|
||||||
|
|
||||||
if (radius.TopLeftX.Type == Auto || radius.TopLeftX.Value == 0) &&
|
if (radius.TopLeftX.Type == Auto || radius.TopLeftX.Value == 0) &&
|
||||||
(radius.TopLeftY.Type == Auto || radius.TopLeftY.Value == 0) &&
|
(radius.TopLeftY.Type == Auto || radius.TopLeftY.Value == 0) &&
|
||||||
|
@ -471,23 +471,23 @@ func (radius BoxRadius) cssValue(builder cssBuilder) {
|
||||||
buffer := allocStringBuilder()
|
buffer := allocStringBuilder()
|
||||||
defer freeStringBuilder(buffer)
|
defer freeStringBuilder(buffer)
|
||||||
|
|
||||||
buffer.WriteString(radius.TopLeftX.cssString("0"))
|
buffer.WriteString(radius.TopLeftX.cssString("0", session))
|
||||||
|
|
||||||
if radius.AllAnglesIsEqual() {
|
if radius.AllAnglesIsEqual() {
|
||||||
|
|
||||||
if !radius.TopLeftX.Equal(radius.TopLeftY) {
|
if !radius.TopLeftX.Equal(radius.TopLeftY) {
|
||||||
buffer.WriteString(" / ")
|
buffer.WriteString(" / ")
|
||||||
buffer.WriteString(radius.TopLeftY.cssString("0"))
|
buffer.WriteString(radius.TopLeftY.cssString("0", session))
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
buffer.WriteRune(' ')
|
buffer.WriteRune(' ')
|
||||||
buffer.WriteString(radius.TopRightX.cssString("0"))
|
buffer.WriteString(radius.TopRightX.cssString("0", session))
|
||||||
buffer.WriteRune(' ')
|
buffer.WriteRune(' ')
|
||||||
buffer.WriteString(radius.BottomRightX.cssString("0"))
|
buffer.WriteString(radius.BottomRightX.cssString("0", session))
|
||||||
buffer.WriteRune(' ')
|
buffer.WriteRune(' ')
|
||||||
buffer.WriteString(radius.BottomLeftX.cssString("0"))
|
buffer.WriteString(radius.BottomLeftX.cssString("0", session))
|
||||||
|
|
||||||
if !radius.TopLeftX.Equal(radius.TopLeftY) ||
|
if !radius.TopLeftX.Equal(radius.TopLeftY) ||
|
||||||
!radius.TopRightX.Equal(radius.TopRightY) ||
|
!radius.TopRightX.Equal(radius.TopRightY) ||
|
||||||
|
@ -495,22 +495,22 @@ func (radius BoxRadius) cssValue(builder cssBuilder) {
|
||||||
!radius.BottomRightX.Equal(radius.BottomRightY) {
|
!radius.BottomRightX.Equal(radius.BottomRightY) {
|
||||||
|
|
||||||
buffer.WriteString(" / ")
|
buffer.WriteString(" / ")
|
||||||
buffer.WriteString(radius.TopLeftY.cssString("0"))
|
buffer.WriteString(radius.TopLeftY.cssString("0", session))
|
||||||
buffer.WriteRune(' ')
|
buffer.WriteRune(' ')
|
||||||
buffer.WriteString(radius.TopRightY.cssString("0"))
|
buffer.WriteString(radius.TopRightY.cssString("0", session))
|
||||||
buffer.WriteRune(' ')
|
buffer.WriteRune(' ')
|
||||||
buffer.WriteString(radius.BottomRightY.cssString("0"))
|
buffer.WriteString(radius.BottomRightY.cssString("0", session))
|
||||||
buffer.WriteRune(' ')
|
buffer.WriteRune(' ')
|
||||||
buffer.WriteString(radius.BottomLeftY.cssString("0"))
|
buffer.WriteString(radius.BottomLeftY.cssString("0", session))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.add("border-radius", buffer.String())
|
builder.add("border-radius", buffer.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (radius BoxRadius) cssString() string {
|
func (radius BoxRadius) cssString(session Session) string {
|
||||||
var builder cssValueBuilder
|
var builder cssValueBuilder
|
||||||
radius.cssValue(&builder)
|
radius.cssValue(&builder, session)
|
||||||
return builder.finish()
|
return builder.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -340,7 +340,7 @@ func (resizable *resizableData) updateResizeBorderWidth() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (resizable *resizableData) cellSizeCSS() (string, string) {
|
func (resizable *resizableData) cellSizeCSS() (string, string) {
|
||||||
w := resizable.resizeBorderWidth().cssString("4px")
|
w := resizable.resizeBorderWidth().cssString("4px", resizable.Session())
|
||||||
side := resizable.getSide()
|
side := resizable.getSide()
|
||||||
column := "1fr"
|
column := "1fr"
|
||||||
row := "1fr"
|
row := "1fr"
|
||||||
|
@ -384,7 +384,7 @@ func (resizable *resizableData) htmlSubviews(self View, buffer *strings.Builder)
|
||||||
top := 1
|
top := 1
|
||||||
leftSide := (side & LeftSide) != 0
|
leftSide := (side & LeftSide) != 0
|
||||||
rightSide := (side & RightSide) != 0
|
rightSide := (side & RightSide) != 0
|
||||||
w := resizable.resizeBorderWidth().cssString("4px")
|
w := resizable.resizeBorderWidth().cssString("4px", resizable.Session())
|
||||||
|
|
||||||
if leftSide {
|
if leftSide {
|
||||||
left = 2
|
left = 2
|
||||||
|
|
14
shadow.go
14
shadow.go
|
@ -151,13 +151,13 @@ func (shadow *viewShadowData) cssStyle(buffer *strings.Builder, session Session,
|
||||||
buffer.WriteString("inset ")
|
buffer.WriteString("inset ")
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.WriteString(offsetX.cssString("0"))
|
buffer.WriteString(offsetX.cssString("0", session))
|
||||||
buffer.WriteByte(' ')
|
buffer.WriteByte(' ')
|
||||||
buffer.WriteString(offsetY.cssString("0"))
|
buffer.WriteString(offsetY.cssString("0", session))
|
||||||
buffer.WriteByte(' ')
|
buffer.WriteByte(' ')
|
||||||
buffer.WriteString(blurRadius.cssString("0"))
|
buffer.WriteString(blurRadius.cssString("0", session))
|
||||||
buffer.WriteByte(' ')
|
buffer.WriteByte(' ')
|
||||||
buffer.WriteString(spreadRadius.cssString("0"))
|
buffer.WriteString(spreadRadius.cssString("0", session))
|
||||||
buffer.WriteByte(' ')
|
buffer.WriteByte(' ')
|
||||||
buffer.WriteString(color.cssString())
|
buffer.WriteString(color.cssString())
|
||||||
return true
|
return true
|
||||||
|
@ -177,11 +177,11 @@ func (shadow *viewShadowData) cssTextStyle(buffer *strings.Builder, session Sess
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.WriteString(lead)
|
buffer.WriteString(lead)
|
||||||
buffer.WriteString(offsetX.cssString("0"))
|
buffer.WriteString(offsetX.cssString("0", session))
|
||||||
buffer.WriteByte(' ')
|
buffer.WriteByte(' ')
|
||||||
buffer.WriteString(offsetY.cssString("0"))
|
buffer.WriteString(offsetY.cssString("0", session))
|
||||||
buffer.WriteByte(' ')
|
buffer.WriteByte(' ')
|
||||||
buffer.WriteString(blurRadius.cssString("0"))
|
buffer.WriteString(blurRadius.cssString("0", session))
|
||||||
buffer.WriteByte(' ')
|
buffer.WriteByte(' ')
|
||||||
buffer.WriteString(color.cssString())
|
buffer.WriteString(color.cssString())
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -0,0 +1,359 @@
|
||||||
|
package rui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SizeFunc describes a function that calculates the SizeUnit size.
|
||||||
|
// Used as the value of the SizeUnit properties.
|
||||||
|
// "min", "max", "clamp", "sum", "sub", "mul", and "div" functions are available.
|
||||||
|
type SizeFunc interface {
|
||||||
|
fmt.Stringer
|
||||||
|
cssString(session Session) string
|
||||||
|
writeCSS(topFunc string, buffer *strings.Builder, session Session)
|
||||||
|
writeString(topFunc string, buffer *strings.Builder)
|
||||||
|
}
|
||||||
|
|
||||||
|
type sizeFuncData struct {
|
||||||
|
tag string
|
||||||
|
args []any
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseSizeFunc(text string) SizeFunc {
|
||||||
|
text = strings.Trim(text, " ")
|
||||||
|
|
||||||
|
for _, tag := range []string{"min", "max", "sum", "sub", "mul", "div", "clamp"} {
|
||||||
|
if strings.HasPrefix(text, tag) {
|
||||||
|
text = strings.Trim(strings.TrimPrefix(text, tag), " ")
|
||||||
|
last := len(text) - 1
|
||||||
|
if text[0] == '(' && text[last] == ')' {
|
||||||
|
text = text[1:last]
|
||||||
|
bracket := 0
|
||||||
|
start := 0
|
||||||
|
args := []any{}
|
||||||
|
for i, ch := range text {
|
||||||
|
switch ch {
|
||||||
|
case ',':
|
||||||
|
if bracket == 0 {
|
||||||
|
args = append(args, text[start:i])
|
||||||
|
start = i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
case '(':
|
||||||
|
bracket++
|
||||||
|
|
||||||
|
case ')':
|
||||||
|
bracket--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if bracket != 0 {
|
||||||
|
ErrorLogF(`Invalid "%s" function`, tag)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
args = append(args, text[start:])
|
||||||
|
switch tag {
|
||||||
|
case "sub", "mul", "div":
|
||||||
|
if len(args) != 2 {
|
||||||
|
ErrorLogF(`"%s" function needs 2 arguments`, tag)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
case "clamp":
|
||||||
|
if len(args) != 3 {
|
||||||
|
ErrorLog(`"clamp" function needs 3 arguments`)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data := new(sizeFuncData)
|
||||||
|
data.tag = tag
|
||||||
|
if data.parseArgs(args, tag == "mul" || tag == "div") {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorLogF(`Invalid "%s" function`, tag)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (data *sizeFuncData) parseArgs(args []any, allowNumber bool) bool {
|
||||||
|
data.args = []any{}
|
||||||
|
|
||||||
|
numberArg := func(index int, value float64) bool {
|
||||||
|
if allowNumber {
|
||||||
|
if index == 1 {
|
||||||
|
if value == 0 && data.tag == "div" {
|
||||||
|
ErrorLog(`Division by 0 in div function`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
data.args = append(data.args, value)
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
ErrorLogF(`Only the second %s function argument can be a number`, data.tag)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorLogF(`The %s function argument cann't be a number`, data.tag)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, arg := range args {
|
||||||
|
switch arg := arg.(type) {
|
||||||
|
case string:
|
||||||
|
if arg = strings.Trim(arg, " \t\n"); arg == "" {
|
||||||
|
ErrorLogF(`Unsupported %s function argument #%d: ""`, data.tag, i)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if arg[0] == '@' {
|
||||||
|
data.args = append(data.args, arg)
|
||||||
|
} else if val, err := strconv.ParseFloat(arg, 64); err == nil {
|
||||||
|
return numberArg(i, val)
|
||||||
|
} else if fn := parseSizeFunc(arg); fn != nil {
|
||||||
|
data.args = append(data.args, fn)
|
||||||
|
} else if size, err := stringToSizeUnit(arg); err == nil {
|
||||||
|
data.args = append(data.args, size)
|
||||||
|
} else {
|
||||||
|
ErrorLogF(`Unsupported %s function argument #%d: "%s"`, data.tag, i, arg)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
case SizeFunc:
|
||||||
|
data.args = append(data.args, arg)
|
||||||
|
|
||||||
|
case SizeUnit:
|
||||||
|
if arg.Type == Auto {
|
||||||
|
ErrorLogF(`Unsupported %s function argument #%d: "auto"`, data.tag, i)
|
||||||
|
}
|
||||||
|
data.args = append(data.args, arg)
|
||||||
|
|
||||||
|
case float64:
|
||||||
|
return numberArg(i, arg)
|
||||||
|
|
||||||
|
case float32:
|
||||||
|
return numberArg(i, float64(arg))
|
||||||
|
|
||||||
|
default:
|
||||||
|
if n, ok := isInt(arg); ok {
|
||||||
|
return numberArg(i, float64(n))
|
||||||
|
}
|
||||||
|
ErrorLogF(`Unsupported %s function argument #%d: %v`, data.tag, i, arg)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (data *sizeFuncData) String() string {
|
||||||
|
buffer := allocStringBuilder()
|
||||||
|
defer freeStringBuilder(buffer)
|
||||||
|
|
||||||
|
data.writeString("", buffer)
|
||||||
|
return buffer.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (data *sizeFuncData) writeString(topFunc string, buffer *strings.Builder) {
|
||||||
|
buffer.WriteString(data.tag)
|
||||||
|
buffer.WriteRune('(')
|
||||||
|
for i, arg := range data.args {
|
||||||
|
if i > 0 {
|
||||||
|
buffer.WriteString(", ")
|
||||||
|
}
|
||||||
|
switch arg := arg.(type) {
|
||||||
|
case string:
|
||||||
|
buffer.WriteString(arg)
|
||||||
|
|
||||||
|
case SizeFunc:
|
||||||
|
arg.writeString(data.tag, buffer)
|
||||||
|
|
||||||
|
case SizeUnit:
|
||||||
|
buffer.WriteString(arg.String())
|
||||||
|
|
||||||
|
case fmt.Stringer:
|
||||||
|
buffer.WriteString(arg.String())
|
||||||
|
|
||||||
|
case float64:
|
||||||
|
buffer.WriteString(fmt.Sprintf("%g", arg))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
buffer.WriteRune(')')
|
||||||
|
}
|
||||||
|
|
||||||
|
func (data *sizeFuncData) cssString(session Session) string {
|
||||||
|
buffer := allocStringBuilder()
|
||||||
|
defer freeStringBuilder(buffer)
|
||||||
|
|
||||||
|
data.writeCSS("", buffer, session)
|
||||||
|
return buffer.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (data *sizeFuncData) writeCSS(topFunc string, buffer *strings.Builder, session Session) {
|
||||||
|
bracket := true
|
||||||
|
sep := ", "
|
||||||
|
|
||||||
|
mathFunc := func(s string) {
|
||||||
|
sep = s
|
||||||
|
switch topFunc {
|
||||||
|
case "":
|
||||||
|
buffer.WriteString("calc(")
|
||||||
|
|
||||||
|
case "min", "max", "clamp":
|
||||||
|
bracket = false
|
||||||
|
|
||||||
|
default:
|
||||||
|
buffer.WriteRune('(')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch data.tag {
|
||||||
|
case "min", "max", "clamp":
|
||||||
|
buffer.WriteString(data.tag)
|
||||||
|
buffer.WriteRune('(')
|
||||||
|
|
||||||
|
case "sum":
|
||||||
|
mathFunc(" + ")
|
||||||
|
|
||||||
|
case "sub":
|
||||||
|
mathFunc(" - ")
|
||||||
|
|
||||||
|
case "mul":
|
||||||
|
mathFunc(" * ")
|
||||||
|
|
||||||
|
case "div":
|
||||||
|
mathFunc(" / ")
|
||||||
|
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, arg := range data.args {
|
||||||
|
if i > 0 {
|
||||||
|
buffer.WriteString(sep)
|
||||||
|
}
|
||||||
|
switch arg := arg.(type) {
|
||||||
|
case string:
|
||||||
|
if arg, ok := session.resolveConstants(arg); ok {
|
||||||
|
if fn := parseSizeFunc(arg); fn != nil {
|
||||||
|
fn.writeCSS(data.tag, buffer, session)
|
||||||
|
} else if size, err := stringToSizeUnit(arg); err == nil {
|
||||||
|
buffer.WriteString(size.cssString("0", session))
|
||||||
|
} else {
|
||||||
|
buffer.WriteString("0")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buffer.WriteString("0")
|
||||||
|
}
|
||||||
|
|
||||||
|
case SizeFunc:
|
||||||
|
arg.writeCSS(data.tag, buffer, session)
|
||||||
|
|
||||||
|
case SizeUnit:
|
||||||
|
buffer.WriteString(arg.cssString("0", session))
|
||||||
|
|
||||||
|
case fmt.Stringer:
|
||||||
|
buffer.WriteString(arg.String())
|
||||||
|
|
||||||
|
case float64:
|
||||||
|
buffer.WriteString(fmt.Sprintf("%g", arg))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if bracket {
|
||||||
|
buffer.WriteRune(')')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxSize creates a SizeUnit function that calculates the maximum argument.
|
||||||
|
// Valid argument types are SizeUnit, SizeFunc and a string which is a text description of SizeUnit or SizeFunc
|
||||||
|
func MaxSize(arg0, arg1 any, args ...any) SizeFunc {
|
||||||
|
data := new(sizeFuncData)
|
||||||
|
data.tag = "max"
|
||||||
|
if !data.parseArgs(append([]any{arg0, arg1}, args...), false) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// MinSize creates a SizeUnit function that calculates the minimum argument.
|
||||||
|
// Valid argument types are SizeUnit, SizeFunc and a string which is a text description of SizeUnit or SizeFunc.
|
||||||
|
func MinSize(arg0, arg1 any, args ...any) SizeFunc {
|
||||||
|
data := new(sizeFuncData)
|
||||||
|
data.tag = "min"
|
||||||
|
if !data.parseArgs(append([]any{arg0, arg1}, args...), false) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// SumSize creates a SizeUnit function that calculates the sum of arguments.
|
||||||
|
// Valid argument types are SizeUnit, SizeFunc and a string which is a text description of SizeUnit or SizeFunc.
|
||||||
|
func SumSize(arg0, arg1 any, args ...any) SizeFunc {
|
||||||
|
data := new(sizeFuncData)
|
||||||
|
data.tag = "sum"
|
||||||
|
if !data.parseArgs(append([]any{arg0, arg1}, args...), false) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// SumSize creates a SizeUnit function that calculates the result of subtracting the arguments (arg1 - arg2).
|
||||||
|
// Valid argument types are SizeUnit, SizeFunc and a string which is a text description of SizeUnit or SizeFunc.
|
||||||
|
func SubSize(arg0, arg1 any) SizeFunc {
|
||||||
|
data := new(sizeFuncData)
|
||||||
|
data.tag = "sub"
|
||||||
|
if !data.parseArgs([]any{arg0, arg1}, false) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// MulSize creates a SizeUnit function that calculates the result of multiplying the arguments (arg1 * arg2).
|
||||||
|
// Valid argument types are SizeUnit, SizeFunc and a string which is a text description of SizeUnit or SizeFunc.
|
||||||
|
// The second argument can also be a number (float32, float32, int, int8...int64, uint, uint8...unit64)
|
||||||
|
// or a string which is a text representation of a number.
|
||||||
|
func MulSize(arg0, arg1 any) SizeFunc {
|
||||||
|
data := new(sizeFuncData)
|
||||||
|
data.tag = "mul"
|
||||||
|
if !data.parseArgs([]any{arg0, arg1}, true) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// DivSize creates a SizeUnit function that calculates the result of dividing the arguments (arg1 / arg2).
|
||||||
|
// Valid argument types are SizeUnit, SizeFunc and a string which is a text description of SizeUnit or SizeFunc.
|
||||||
|
// The second argument can also be a number (float32, float32, int, int8...int64, uint, uint8...unit64)
|
||||||
|
// or a string which is a text representation of a number.
|
||||||
|
func DivSize(arg0, arg1 any) SizeFunc {
|
||||||
|
data := new(sizeFuncData)
|
||||||
|
data.tag = "div"
|
||||||
|
if !data.parseArgs([]any{arg0, arg1}, true) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClampSize creates a SizeUnit function whose the result is calculated as follows:
|
||||||
|
//
|
||||||
|
// min ≤ value ≤ max -> value;
|
||||||
|
// value < min -> min;
|
||||||
|
// max < value -> max;
|
||||||
|
//
|
||||||
|
// Valid argument types are SizeUnit, SizeFunc and a string which is a text description of SizeUnit or SizeFunc.
|
||||||
|
func ClampSize(min, value, max any) SizeFunc {
|
||||||
|
data := new(sizeFuncData)
|
||||||
|
data.tag = "clamp"
|
||||||
|
if !data.parseArgs([]any{min, value, max}, false) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package rui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSizeFunc(t *testing.T) {
|
||||||
|
session := new(sessionData)
|
||||||
|
session.getCurrentTheme().SetConstant("a1", "120px", "120px")
|
||||||
|
|
||||||
|
SetErrorLog(func(text string) {
|
||||||
|
t.Error(text)
|
||||||
|
})
|
||||||
|
SetDebugLog(func(text string) {
|
||||||
|
t.Log(text)
|
||||||
|
})
|
||||||
|
|
||||||
|
testFunc := func(fn SizeFunc, str, css string) {
|
||||||
|
if fn != nil {
|
||||||
|
if text := fn.String(); str != text {
|
||||||
|
t.Error("String() error.\nResult: \"" + text + "\"\nExpected: \"" + str + `"`)
|
||||||
|
}
|
||||||
|
if text := fn.cssString(session); css != text {
|
||||||
|
t.Error("cssString() error.\nResult: \"" + text + "\"\nExpected: \"" + css + `"`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testFunc(MinSize("100%", Px(10)), `min(100%, 10px)`, `min(100%, 10px)`)
|
||||||
|
testFunc(MaxSize(Percent(100), "@a1"), `max(100%, @a1)`, `max(100%, 120px)`)
|
||||||
|
testFunc(SumSize(Percent(100), "@a1"), `sum(100%, @a1)`, `calc(100% + 120px)`)
|
||||||
|
testFunc(SubSize(Percent(100), "@a1"), `sub(100%, @a1)`, `calc(100% - 120px)`)
|
||||||
|
testFunc(MulSize(Percent(100), "@a1"), `mul(100%, @a1)`, `calc(100% * 120px)`)
|
||||||
|
testFunc(DivSize(Percent(100), "@a1"), `div(100%, @a1)`, `calc(100% / 120px)`)
|
||||||
|
testFunc(ClampSize(Percent(20), "@a1", Percent(40)), `clamp(20%, @a1, 40%)`, `clamp(20%, 120px, 40%)`)
|
||||||
|
|
||||||
|
testFunc(MaxSize(SubSize(Percent(100), "@a1"), "@a1"), `max(sub(100%, @a1), @a1)`, `max(100% - 120px, 120px)`)
|
||||||
|
|
||||||
|
testParse := func(str, css string) {
|
||||||
|
if fn := parseSizeFunc(str); fn != nil {
|
||||||
|
testFunc(fn, str, css)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testParse(`min(100%, 10px)`, `min(100%, 10px)`)
|
||||||
|
testParse(`max(100%, @a1)`, `max(100%, 120px)`)
|
||||||
|
testParse(`max(sub(100%, @a1), @a1)`, `max(100% - 120px, 120px)`)
|
||||||
|
testParse(`mul(sub(100%, @a1), @a1)`, `calc((100% - 120px) * 120px)`)
|
||||||
|
testParse(`mul(sub(100%, @a1), div(mul(@a1, 3), 2))`, `calc((100% - 120px) * ((120px * 3) / 2))`)
|
||||||
|
}
|
72
sizeUnit.go
72
sizeUnit.go
|
@ -14,89 +14,94 @@ import (
|
||||||
type SizeUnitType uint8
|
type SizeUnitType uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Auto - default value.
|
// Auto is the SizeUnit type: default value.
|
||||||
Auto SizeUnitType = 0
|
Auto SizeUnitType = 0
|
||||||
// SizeInPixel - size in pixels.
|
// SizeInPixel is the SizeUnit type: the Value field specifies the size in pixels.
|
||||||
SizeInPixel SizeUnitType = 1
|
SizeInPixel SizeUnitType = 1
|
||||||
// SizeInEM - size in em.
|
// SizeInEM is the SizeUnit type: the Value field specifies the size in em.
|
||||||
SizeInEM SizeUnitType = 2
|
SizeInEM SizeUnitType = 2
|
||||||
// SizeInEX - size in em.
|
// SizeInEX is the SizeUnit type: the Value field specifies the size in em.
|
||||||
SizeInEX SizeUnitType = 3
|
SizeInEX SizeUnitType = 3
|
||||||
// SizeInPercent - size in percents of a parant size.
|
// SizeInPercent is the SizeUnit type: the Value field specifies the size in percents of the parent size.
|
||||||
SizeInPercent SizeUnitType = 4
|
SizeInPercent SizeUnitType = 4
|
||||||
// SizeInPt - size in pt (1/72 inch).
|
// SizeInPt is the SizeUnit type: the Value field specifies the size in pt (1/72 inch).
|
||||||
SizeInPt SizeUnitType = 5
|
SizeInPt SizeUnitType = 5
|
||||||
// SizeInPc - size in pc (1pc = 12pt).
|
// SizeInPc is the SizeUnit type: the Value field specifies the size in pc (1pc = 12pt).
|
||||||
SizeInPc SizeUnitType = 6
|
SizeInPc SizeUnitType = 6
|
||||||
// SizeInInch - size in inches.
|
// SizeInInch is the SizeUnit type: the Value field specifies the size in inches.
|
||||||
SizeInInch SizeUnitType = 7
|
SizeInInch SizeUnitType = 7
|
||||||
// SizeInMM - size in millimeters.
|
// SizeInMM is the SizeUnit type: the Value field specifies the size in millimeters.
|
||||||
SizeInMM SizeUnitType = 8
|
SizeInMM SizeUnitType = 8
|
||||||
// SizeInCM - size in centimeters.
|
// SizeInCM is the SizeUnit type: the Value field specifies the size in centimeters.
|
||||||
SizeInCM SizeUnitType = 9
|
SizeInCM SizeUnitType = 9
|
||||||
// SizeInFraction - size in fraction. Used only for "cell-width" and "cell-height" property
|
// SizeInFraction is the SizeUnit type: the Value field specifies the size in fraction.
|
||||||
|
// Used only for "cell-width" and "cell-height" property.
|
||||||
SizeInFraction SizeUnitType = 10
|
SizeInFraction SizeUnitType = 10
|
||||||
|
// SizeFunction is the SizeUnit type: the Function field specifies the size function.
|
||||||
|
// "min", "max", "clamp", "sum", "sub", "mul", and "div" functions are available.
|
||||||
|
SizeFunction = 11
|
||||||
)
|
)
|
||||||
|
|
||||||
// SizeUnit describe a size (Value field) and size unit (Type field).
|
// SizeUnit describe a size (Value field) and size unit (Type field).
|
||||||
type SizeUnit struct {
|
type SizeUnit struct {
|
||||||
Type SizeUnitType
|
Type SizeUnitType
|
||||||
Value float64
|
Value float64
|
||||||
|
Function SizeFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
// AutoSize creates SizeUnit with Auto type
|
// AutoSize creates SizeUnit with Auto type
|
||||||
func AutoSize() SizeUnit {
|
func AutoSize() SizeUnit {
|
||||||
return SizeUnit{Auto, 0}
|
return SizeUnit{Auto, 0, nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Px creates SizeUnit with SizeInPixel type
|
// Px creates SizeUnit with SizeInPixel type
|
||||||
func Px(value float64) SizeUnit {
|
func Px(value float64) SizeUnit {
|
||||||
return SizeUnit{SizeInPixel, value}
|
return SizeUnit{SizeInPixel, value, nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Em creates SizeUnit with SizeInEM type
|
// Em creates SizeUnit with SizeInEM type
|
||||||
func Em(value float64) SizeUnit {
|
func Em(value float64) SizeUnit {
|
||||||
return SizeUnit{SizeInEM, value}
|
return SizeUnit{SizeInEM, value, nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ex creates SizeUnit with SizeInEX type
|
// Ex creates SizeUnit with SizeInEX type
|
||||||
func Ex(value float64) SizeUnit {
|
func Ex(value float64) SizeUnit {
|
||||||
return SizeUnit{SizeInEX, value}
|
return SizeUnit{SizeInEX, value, nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Percent creates SizeUnit with SizeInDIP type
|
// Percent creates SizeUnit with SizeInDIP type
|
||||||
func Percent(value float64) SizeUnit {
|
func Percent(value float64) SizeUnit {
|
||||||
return SizeUnit{SizeInPercent, value}
|
return SizeUnit{SizeInPercent, value, nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pt creates SizeUnit with SizeInPt type
|
// Pt creates SizeUnit with SizeInPt type
|
||||||
func Pt(value float64) SizeUnit {
|
func Pt(value float64) SizeUnit {
|
||||||
return SizeUnit{SizeInPt, value}
|
return SizeUnit{SizeInPt, value, nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pc creates SizeUnit with SizeInPc type
|
// Pc creates SizeUnit with SizeInPc type
|
||||||
func Pc(value float64) SizeUnit {
|
func Pc(value float64) SizeUnit {
|
||||||
return SizeUnit{SizeInPc, value}
|
return SizeUnit{SizeInPc, value, nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mm creates SizeUnit with SizeInMM type
|
// Mm creates SizeUnit with SizeInMM type
|
||||||
func Mm(value float64) SizeUnit {
|
func Mm(value float64) SizeUnit {
|
||||||
return SizeUnit{SizeInMM, value}
|
return SizeUnit{SizeInMM, value, nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cm creates SizeUnit with SizeInCM type
|
// Cm creates SizeUnit with SizeInCM type
|
||||||
func Cm(value float64) SizeUnit {
|
func Cm(value float64) SizeUnit {
|
||||||
return SizeUnit{SizeInCM, value}
|
return SizeUnit{SizeInCM, value, nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inch creates SizeUnit with SizeInInch type
|
// Inch creates SizeUnit with SizeInInch type
|
||||||
func Inch(value float64) SizeUnit {
|
func Inch(value float64) SizeUnit {
|
||||||
return SizeUnit{SizeInInch, value}
|
return SizeUnit{SizeInInch, value, nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fr creates SizeUnit with SizeInFraction type
|
// Fr creates SizeUnit with SizeInFraction type
|
||||||
func Fr(value float64) SizeUnit {
|
func Fr(value float64) SizeUnit {
|
||||||
return SizeUnit{SizeInFraction, value}
|
return SizeUnit{SizeInFraction, value, nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equal compare two SizeUnit. Return true if SizeUnit are equal
|
// Equal compare two SizeUnit. Return true if SizeUnit are equal
|
||||||
|
@ -152,7 +157,7 @@ func stringToSizeUnit(value string) (SizeUnit, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if val, err := strconv.ParseFloat(value, 64); err != nil {
|
if val, err := strconv.ParseFloat(value, 64); err == nil {
|
||||||
return SizeUnit{Type: SizeInPixel, Value: val}, nil
|
return SizeUnit{Type: SizeInPixel, Value: val}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,8 +166,15 @@ func stringToSizeUnit(value string) (SizeUnit, error) {
|
||||||
|
|
||||||
// String - convert SizeUnit to string
|
// String - convert SizeUnit to string
|
||||||
func (size SizeUnit) String() string {
|
func (size SizeUnit) String() string {
|
||||||
if size.Type == Auto {
|
switch size.Type {
|
||||||
|
case Auto:
|
||||||
return "auto"
|
return "auto"
|
||||||
|
|
||||||
|
case SizeFunction:
|
||||||
|
if size.Function == nil {
|
||||||
|
return "auto"
|
||||||
|
}
|
||||||
|
return size.Function.String()
|
||||||
}
|
}
|
||||||
if suffix, ok := sizeUnitSuffixes()[size.Type]; ok {
|
if suffix, ok := sizeUnitSuffixes()[size.Type]; ok {
|
||||||
return fmt.Sprintf("%g%s", size.Value, suffix)
|
return fmt.Sprintf("%g%s", size.Value, suffix)
|
||||||
|
@ -171,13 +183,19 @@ func (size SizeUnit) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// cssString - convert SizeUnit to string
|
// cssString - convert SizeUnit to string
|
||||||
func (size SizeUnit) cssString(textForAuto string) string {
|
func (size SizeUnit) cssString(textForAuto string, session Session) string {
|
||||||
switch size.Type {
|
switch size.Type {
|
||||||
case Auto:
|
case Auto:
|
||||||
return textForAuto
|
return textForAuto
|
||||||
|
|
||||||
case SizeInEM:
|
case SizeInEM:
|
||||||
return fmt.Sprintf("%grem", size.Value)
|
return fmt.Sprintf("%grem", size.Value)
|
||||||
|
|
||||||
|
case SizeFunction:
|
||||||
|
if size.Function == nil {
|
||||||
|
return textForAuto
|
||||||
|
}
|
||||||
|
return size.Function.cssString(session)
|
||||||
}
|
}
|
||||||
|
|
||||||
if size.Value == 0 {
|
if size.Value == 0 {
|
||||||
|
|
16
tableView.go
16
tableView.go
|
@ -594,7 +594,7 @@ func (table *tableViewData) propertyChanged(tag string) {
|
||||||
updateCSSProperty(htmlID, "border-spacing", "0", session)
|
updateCSSProperty(htmlID, "border-spacing", "0", session)
|
||||||
updateCSSProperty(htmlID, "border-collapse", "collapse", session)
|
updateCSSProperty(htmlID, "border-collapse", "collapse", session)
|
||||||
} else {
|
} else {
|
||||||
updateCSSProperty(htmlID, "border-spacing", gap.cssString("0"), session)
|
updateCSSProperty(htmlID, "border-spacing", gap.cssString("0", session), session)
|
||||||
updateCSSProperty(htmlID, "border-collapse", "separate", session)
|
updateCSSProperty(htmlID, "border-collapse", "separate", session)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1319,24 +1319,26 @@ func (table *tableViewData) getCurrent() CellIndex {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (table *tableViewData) cssStyle(self View, builder cssBuilder) {
|
func (table *tableViewData) cssStyle(self View, builder cssBuilder) {
|
||||||
table.viewData.cssViewStyle(builder, table.Session())
|
session := table.Session()
|
||||||
|
table.viewData.cssViewStyle(builder, session)
|
||||||
|
|
||||||
gap, ok := sizeProperty(table, Gap, table.Session())
|
gap, ok := sizeProperty(table, Gap, session)
|
||||||
if !ok || gap.Type == Auto || gap.Value <= 0 {
|
if !ok || gap.Type == Auto || gap.Value <= 0 {
|
||||||
builder.add("border-spacing", "0")
|
builder.add("border-spacing", "0")
|
||||||
builder.add("border-collapse", "collapse")
|
builder.add("border-collapse", "collapse")
|
||||||
} else {
|
} else {
|
||||||
builder.add("border-spacing", gap.cssString("0"))
|
builder.add("border-spacing", gap.cssString("0", session))
|
||||||
builder.add("border-collapse", "separate")
|
builder.add("border-collapse", "separate")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (table *tableViewData) ReloadTableData() {
|
func (table *tableViewData) ReloadTableData() {
|
||||||
|
session := table.Session()
|
||||||
if content := table.content(); content != nil {
|
if content := table.content(); content != nil {
|
||||||
updateProperty(table.htmlID(), "data-rows", strconv.Itoa(content.RowCount()), table.Session())
|
updateProperty(table.htmlID(), "data-rows", strconv.Itoa(content.RowCount()), session)
|
||||||
updateProperty(table.htmlID(), "data-columns", strconv.Itoa(content.ColumnCount()), table.Session())
|
updateProperty(table.htmlID(), "data-columns", strconv.Itoa(content.ColumnCount()), session)
|
||||||
}
|
}
|
||||||
updateInnerHTML(table.htmlID(), table.Session())
|
updateInnerHTML(table.htmlID(), session)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (table *tableViewData) onItemResize(self View, index string, x, y, width, height float64) {
|
func (table *tableViewData) onItemResize(self View, index string, x, y, width, height float64) {
|
||||||
|
|
10
view.go
10
view.go
|
@ -446,7 +446,7 @@ func viewPropertyChanged(view *viewData, tag string) {
|
||||||
return
|
return
|
||||||
|
|
||||||
case Outline, OutlineColor, OutlineStyle, OutlineWidth:
|
case Outline, OutlineColor, OutlineStyle, OutlineWidth:
|
||||||
updateCSSProperty(htmlID, Outline, GetOutline(view).cssString(), session)
|
updateCSSProperty(htmlID, Outline, GetOutline(view).cssString(session), session)
|
||||||
return
|
return
|
||||||
|
|
||||||
case Shadow:
|
case Shadow:
|
||||||
|
@ -462,19 +462,19 @@ func viewPropertyChanged(view *viewData, tag string) {
|
||||||
RadiusBottomLeft, RadiusBottomLeftX, RadiusBottomLeftY,
|
RadiusBottomLeft, RadiusBottomLeftX, RadiusBottomLeftY,
|
||||||
RadiusBottomRight, RadiusBottomRightX, RadiusBottomRightY:
|
RadiusBottomRight, RadiusBottomRightX, RadiusBottomRightY:
|
||||||
radius := GetRadius(view)
|
radius := GetRadius(view)
|
||||||
updateCSSProperty(htmlID, "border-radius", radius.cssString(), session)
|
updateCSSProperty(htmlID, "border-radius", radius.cssString(session), session)
|
||||||
return
|
return
|
||||||
|
|
||||||
case Margin, MarginTop, MarginRight, MarginBottom, MarginLeft,
|
case Margin, MarginTop, MarginRight, MarginBottom, MarginLeft,
|
||||||
"top-margin", "right-margin", "bottom-margin", "left-margin":
|
"top-margin", "right-margin", "bottom-margin", "left-margin":
|
||||||
margin := GetMargin(view)
|
margin := GetMargin(view)
|
||||||
updateCSSProperty(htmlID, Margin, margin.cssString(), session)
|
updateCSSProperty(htmlID, Margin, margin.cssString(session), session)
|
||||||
return
|
return
|
||||||
|
|
||||||
case Padding, PaddingTop, PaddingRight, PaddingBottom, PaddingLeft,
|
case Padding, PaddingTop, PaddingRight, PaddingBottom, PaddingLeft,
|
||||||
"top-padding", "right-padding", "bottom-padding", "left-padding":
|
"top-padding", "right-padding", "bottom-padding", "left-padding":
|
||||||
padding := GetPadding(view)
|
padding := GetPadding(view)
|
||||||
updateCSSProperty(htmlID, Padding, padding.cssString(), session)
|
updateCSSProperty(htmlID, Padding, padding.cssString(session), session)
|
||||||
return
|
return
|
||||||
|
|
||||||
case AvoidBreak:
|
case AvoidBreak:
|
||||||
|
@ -626,7 +626,7 @@ func viewPropertyChanged(view *viewData, tag string) {
|
||||||
|
|
||||||
if cssTag, ok := sizeProperties[tag]; ok {
|
if cssTag, ok := sizeProperties[tag]; ok {
|
||||||
size, _ := sizeProperty(view, tag, session)
|
size, _ := sizeProperty(view, tag, session)
|
||||||
updateCSSProperty(htmlID, cssTag, size.cssString(""), session)
|
updateCSSProperty(htmlID, cssTag, size.cssString("", session), session)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
22
viewClip.go
22
viewClip.go
|
@ -146,13 +146,13 @@ func (clip *insetClip) cssStyle(session Session) string {
|
||||||
for _, tag := range []string{Top, Right, Bottom, Left} {
|
for _, tag := range []string{Top, Right, Bottom, Left} {
|
||||||
value, _ := sizeProperty(clip, tag, session)
|
value, _ := sizeProperty(clip, tag, session)
|
||||||
buffer.WriteString(leadText)
|
buffer.WriteString(leadText)
|
||||||
buffer.WriteString(value.cssString("0px"))
|
buffer.WriteString(value.cssString("0px", session))
|
||||||
leadText = " "
|
leadText = " "
|
||||||
}
|
}
|
||||||
|
|
||||||
if radius := getRadiusProperty(clip); radius != nil {
|
if radius := getRadiusProperty(clip); radius != nil {
|
||||||
buffer.WriteString(" round ")
|
buffer.WriteString(" round ")
|
||||||
buffer.WriteString(radius.BoxRadius(session).cssString())
|
buffer.WriteString(radius.BoxRadius(session).cssString(session))
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.WriteRune(')')
|
buffer.WriteRune(')')
|
||||||
|
@ -211,15 +211,15 @@ func (clip *circleClip) cssStyle(session Session) string {
|
||||||
|
|
||||||
buffer.WriteString("circle(")
|
buffer.WriteString("circle(")
|
||||||
r, _ := sizeProperty(clip, Radius, session)
|
r, _ := sizeProperty(clip, Radius, session)
|
||||||
buffer.WriteString(r.cssString("50%"))
|
buffer.WriteString(r.cssString("50%", session))
|
||||||
|
|
||||||
buffer.WriteString(" at ")
|
buffer.WriteString(" at ")
|
||||||
x, _ := sizeProperty(clip, X, session)
|
x, _ := sizeProperty(clip, X, session)
|
||||||
buffer.WriteString(x.cssString("50%"))
|
buffer.WriteString(x.cssString("50%", session))
|
||||||
buffer.WriteRune(' ')
|
buffer.WriteRune(' ')
|
||||||
|
|
||||||
y, _ := sizeProperty(clip, Y, session)
|
y, _ := sizeProperty(clip, Y, session)
|
||||||
buffer.WriteString(y.cssString("50%"))
|
buffer.WriteString(y.cssString("50%", session))
|
||||||
buffer.WriteRune(')')
|
buffer.WriteRune(')')
|
||||||
|
|
||||||
return buffer.String()
|
return buffer.String()
|
||||||
|
@ -280,17 +280,17 @@ func (clip *ellipseClip) cssStyle(session Session) string {
|
||||||
rx, _ := sizeProperty(clip, RadiusX, session)
|
rx, _ := sizeProperty(clip, RadiusX, session)
|
||||||
ry, _ := sizeProperty(clip, RadiusX, session)
|
ry, _ := sizeProperty(clip, RadiusX, session)
|
||||||
buffer.WriteString("ellipse(")
|
buffer.WriteString("ellipse(")
|
||||||
buffer.WriteString(rx.cssString("50%"))
|
buffer.WriteString(rx.cssString("50%", session))
|
||||||
buffer.WriteRune(' ')
|
buffer.WriteRune(' ')
|
||||||
buffer.WriteString(ry.cssString("50%"))
|
buffer.WriteString(ry.cssString("50%", session))
|
||||||
|
|
||||||
buffer.WriteString(" at ")
|
buffer.WriteString(" at ")
|
||||||
x, _ := sizeProperty(clip, X, session)
|
x, _ := sizeProperty(clip, X, session)
|
||||||
buffer.WriteString(x.cssString("50%"))
|
buffer.WriteString(x.cssString("50%", session))
|
||||||
buffer.WriteRune(' ')
|
buffer.WriteRune(' ')
|
||||||
|
|
||||||
y, _ := sizeProperty(clip, Y, session)
|
y, _ := sizeProperty(clip, Y, session)
|
||||||
buffer.WriteString(y.cssString("50%"))
|
buffer.WriteString(y.cssString("50%", session))
|
||||||
buffer.WriteRune(')')
|
buffer.WriteRune(')')
|
||||||
|
|
||||||
return buffer.String()
|
return buffer.String()
|
||||||
|
@ -427,13 +427,13 @@ func (clip *polygonClip) cssStyle(session Session) string {
|
||||||
case string:
|
case string:
|
||||||
if val, ok := session.resolveConstants(value); ok {
|
if val, ok := session.resolveConstants(value); ok {
|
||||||
if size, ok := StringToSizeUnit(val); ok {
|
if size, ok := StringToSizeUnit(val); ok {
|
||||||
buffer.WriteString(size.cssString("0px"))
|
buffer.WriteString(size.cssString("0px", session))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case SizeUnit:
|
case SizeUnit:
|
||||||
buffer.WriteString(value.cssString("0px"))
|
buffer.WriteString(value.cssString("0px", session))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
viewStyle.go
10
viewStyle.go
|
@ -173,11 +173,11 @@ func (style *viewStyle) backgroundCSS(session Session) string {
|
||||||
func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) {
|
func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) {
|
||||||
|
|
||||||
if margin, ok := boundsProperty(style, Margin, session); ok {
|
if margin, ok := boundsProperty(style, Margin, session); ok {
|
||||||
margin.cssValue(Margin, builder)
|
margin.cssValue(Margin, builder, session)
|
||||||
}
|
}
|
||||||
|
|
||||||
if padding, ok := boundsProperty(style, Padding, session); ok {
|
if padding, ok := boundsProperty(style, Padding, session); ok {
|
||||||
padding.cssValue(Padding, builder)
|
padding.cssValue(Padding, builder, session)
|
||||||
}
|
}
|
||||||
|
|
||||||
if border := getBorder(style, Border); border != nil {
|
if border := getBorder(style, Border); border != nil {
|
||||||
|
@ -187,10 +187,10 @@ func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) {
|
||||||
}
|
}
|
||||||
|
|
||||||
radius := getRadius(style, session)
|
radius := getRadius(style, session)
|
||||||
radius.cssValue(builder)
|
radius.cssValue(builder, session)
|
||||||
|
|
||||||
if outline := getOutline(style); outline != nil {
|
if outline := getOutline(style); outline != nil {
|
||||||
outline.ViewOutline(session).cssValue(builder)
|
outline.ViewOutline(session).cssValue(builder, session)
|
||||||
}
|
}
|
||||||
|
|
||||||
if z, ok := intProperty(style, ZIndex, session, 0); ok {
|
if z, ok := intProperty(style, ZIndex, session, 0); ok {
|
||||||
|
@ -215,7 +215,7 @@ func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) {
|
||||||
if !ok {
|
if !ok {
|
||||||
cssTag = tag
|
cssTag = tag
|
||||||
}
|
}
|
||||||
builder.add(cssTag, size.cssString(""))
|
builder.add(cssTag, size.cssString("", session))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -125,11 +125,11 @@ func (style *viewStyle) transform(session Session) string {
|
||||||
buffer.WriteRune(' ')
|
buffer.WriteRune(' ')
|
||||||
}
|
}
|
||||||
buffer.WriteString(`translate3d(`)
|
buffer.WriteString(`translate3d(`)
|
||||||
buffer.WriteString(x.cssString("0"))
|
buffer.WriteString(x.cssString("0", session))
|
||||||
buffer.WriteRune(',')
|
buffer.WriteRune(',')
|
||||||
buffer.WriteString(y.cssString("0"))
|
buffer.WriteString(y.cssString("0", session))
|
||||||
buffer.WriteRune(',')
|
buffer.WriteRune(',')
|
||||||
buffer.WriteString(z.cssString("0"))
|
buffer.WriteString(z.cssString("0", session))
|
||||||
buffer.WriteRune(')')
|
buffer.WriteRune(')')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,9 +172,9 @@ func (style *viewStyle) transform(session Session) string {
|
||||||
buffer.WriteRune(' ')
|
buffer.WriteRune(' ')
|
||||||
}
|
}
|
||||||
buffer.WriteString(`translate(`)
|
buffer.WriteString(`translate(`)
|
||||||
buffer.WriteString(x.cssString("0"))
|
buffer.WriteString(x.cssString("0", session))
|
||||||
buffer.WriteRune(',')
|
buffer.WriteRune(',')
|
||||||
buffer.WriteString(y.cssString("0"))
|
buffer.WriteString(y.cssString("0", session))
|
||||||
buffer.WriteRune(')')
|
buffer.WriteRune(')')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,12 +205,12 @@ func (style *viewStyle) transform(session Session) string {
|
||||||
func (style *viewStyle) writeViewTransformCSS(builder cssBuilder, session Session) {
|
func (style *viewStyle) writeViewTransformCSS(builder cssBuilder, session Session) {
|
||||||
if getTransform3D(style, session) {
|
if getTransform3D(style, session) {
|
||||||
if perspective, ok := sizeProperty(style, Perspective, session); ok && perspective.Type != Auto && perspective.Value != 0 {
|
if perspective, ok := sizeProperty(style, Perspective, session); ok && perspective.Type != Auto && perspective.Value != 0 {
|
||||||
builder.add(`perspective`, perspective.cssString("0"))
|
builder.add(`perspective`, perspective.cssString("0", session))
|
||||||
}
|
}
|
||||||
|
|
||||||
x, y := getPerspectiveOrigin(style, session)
|
x, y := getPerspectiveOrigin(style, session)
|
||||||
if x.Type != Auto || y.Type != Auto {
|
if x.Type != Auto || y.Type != Auto {
|
||||||
builder.addValues(`perspective-origin`, ` `, x.cssString("50%"), y.cssString("50%"))
|
builder.addValues(`perspective-origin`, ` `, x.cssString("50%", session), y.cssString("50%", session))
|
||||||
}
|
}
|
||||||
|
|
||||||
if backfaceVisible, ok := boolProperty(style, BackfaceVisible, session); ok {
|
if backfaceVisible, ok := boolProperty(style, BackfaceVisible, session); ok {
|
||||||
|
@ -223,12 +223,12 @@ func (style *viewStyle) writeViewTransformCSS(builder cssBuilder, session Sessio
|
||||||
|
|
||||||
x, y, z := getOrigin(style, session)
|
x, y, z := getOrigin(style, session)
|
||||||
if x.Type != Auto || y.Type != Auto || z.Type != Auto {
|
if x.Type != Auto || y.Type != Auto || z.Type != Auto {
|
||||||
builder.addValues(`transform-origin`, ` `, x.cssString("50%"), y.cssString("50%"), z.cssString("0"))
|
builder.addValues(`transform-origin`, ` `, x.cssString("50%", session), y.cssString("50%", session), z.cssString("0", session))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
x, y, _ := getOrigin(style, session)
|
x, y, _ := getOrigin(style, session)
|
||||||
if x.Type != Auto || y.Type != Auto {
|
if x.Type != Auto || y.Type != Auto {
|
||||||
builder.addValues(`transform-origin`, ` `, x.cssString("50%"), y.cssString("50%"))
|
builder.addValues(`transform-origin`, ` `, x.cssString("50%", session), y.cssString("50%", session))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,7 +248,7 @@ func (view *viewData) updateTransformProperty(tag string) bool {
|
||||||
x, y := GetPerspectiveOrigin(view)
|
x, y := GetPerspectiveOrigin(view)
|
||||||
value := ""
|
value := ""
|
||||||
if x.Type != Auto || y.Type != Auto {
|
if x.Type != Auto || y.Type != Auto {
|
||||||
value = x.cssString("50%") + " " + y.cssString("50%")
|
value = x.cssString("50%", session) + " " + y.cssString("50%", session)
|
||||||
}
|
}
|
||||||
updateCSSProperty(htmlID, "perspective-origin", value, session)
|
updateCSSProperty(htmlID, "perspective-origin", value, session)
|
||||||
}
|
}
|
||||||
|
@ -267,11 +267,11 @@ func (view *viewData) updateTransformProperty(tag string) bool {
|
||||||
value := ""
|
value := ""
|
||||||
if getTransform3D(view, session) {
|
if getTransform3D(view, session) {
|
||||||
if x.Type != Auto || y.Type != Auto || z.Type != Auto {
|
if x.Type != Auto || y.Type != Auto || z.Type != Auto {
|
||||||
value = x.cssString("50%") + " " + y.cssString("50%") + " " + z.cssString("50%")
|
value = x.cssString("50%", session) + " " + y.cssString("50%", session) + " " + z.cssString("50%", session)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if x.Type != Auto || y.Type != Auto {
|
if x.Type != Auto || y.Type != Auto {
|
||||||
value = x.cssString("50%") + " " + y.cssString("50%")
|
value = x.cssString("50%", session) + " " + y.cssString("50%", session)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateCSSProperty(htmlID, "transform-origin", value, session)
|
updateCSSProperty(htmlID, "transform-origin", value, session)
|
||||||
|
|
Loading…
Reference in New Issue