From 2f7e1bbab3492528840ad372b948f9e599e7fb3f Mon Sep 17 00:00:00 2001 From: Alexei Anoshenko Date: Mon, 5 Sep 2022 14:00:07 +0300 Subject: [PATCH] Added SizeFunc interface, changed SizeUnit struct --- CHANGELOG.md | 2 + README-ru.md | 62 ++++++- README.md | 56 +++++- background.go | 4 +- backgroundConicGradient.go | 4 +- backgroundGradient.go | 31 ++-- border.go | 9 +- bounds.go | 15 +- canvas.go | 6 +- checkbox.go | 2 +- columnSeparator.go | 5 +- gridLayout.go | 6 +- listView.go | 10 +- outline.go | 8 +- propertyGet.go | 3 + propertySet.go | 9 +- radius.go | 24 +-- resizable.go | 4 +- shadow.go | 14 +- sizeFunc.go | 359 +++++++++++++++++++++++++++++++++++++ sizeFunc_test.go | 50 ++++++ sizeUnit.go | 72 +++++--- tableView.go | 16 +- view.go | 10 +- viewClip.go | 22 +-- viewStyle.go | 10 +- viewTransform.go | 24 +-- 27 files changed, 691 insertions(+), 146 deletions(-) create mode 100644 sizeFunc.go create mode 100644 sizeFunc_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index cccadc9..c87de36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ * Requires go 1.18 or higher * 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", "arrow", "arrow-align", "arrow-size", "arrow-width", and "arrow-offset" properties * Added "@ruiArrowSize" and "@ruiArrowWidth" constants to the default theme diff --git a/README-ru.md b/README-ru.md index 2f00c07..9058210 100644 --- a/README-ru.md +++ b/README-ru.md @@ -57,12 +57,12 @@ SizeUnit объявлена как type SizeUnit struct { - Type SizeUnitType - Value float64 + Type SizeUnitType + Value float64 + Function SizeFunc } -где Type - тип размера; -Value - размер +где Type - тип размера; Value - размер; Function - функция (используется только если Type == SizeFunction, в остальных случаях игнорируется). Тип может принимать следующие значения: @@ -79,12 +79,13 @@ Value - размер | 8 | SizeInMM | поле Value определяет размер в миллиметрах. | | 9 | SizeInCM | поле Value определяет размер в сантиметрах. | | 10 | SizeInFraction | поле Value определяет размер в частях. Используется только для задания размеров ячеек в GridLayout. | +| 11 | SizeFunction | поле Function задает функцию для вычисления размера. Значение поля Value игнорируется | Для более наглядного и простого задания переменных типа 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.Em(n) | rui.SizeUnit{ Type: rui.SizeInEM, 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.Fr(n) | rui.SizeUnit{ Type: rui.SizeInFraction, Value: n } | -Переменные типа SizeUnit имеют текстовое представление (зачем оно нужно будет описано ниже). Текстовое представление состоит из числа (равному значению поля Value) и следующим за ним суффиксом определяющим тип. Исключением является значение типа Auto, которое имеет представление “auto”. Суффиксы перечислены в следующей таблице: +Переменные типа SizeUnit имеют текстовое представление (зачем оно нужно будет описано ниже). +Текстовое представление состоит из числа (равному значению поля Value) и следующим за ним суффиксом определяющим тип. +Исключением является значение типа Auto, которое имеет представление “auto” и +значение типа SizeFunction, которое имеет особое представление. Суффиксы перечислены в следующей таблице: | Суффикс | Тип | |:-------:|----------------| @@ -119,6 +123,52 @@ Value - размер Получить текстовое представление структуры можно свойством 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, ... должны быть текстовым представлением SizeUnit или SizeFunc или константой. Например + + "min(50%, 250px)" + "min(75%, @a1)" + +Интерфейс SizeFunc реализует интерфейс fmt.Stringer. +Функция String() этого интерфейса возвращает текстовое представление SizeFunc. + +Помимо min имеются следующие функции + +| Текстовое представление | Функция для создания | Описание +|------------------------------|--------------------------------------|-------------------------------------------------| +| "min(, , ...)" | MaxSize(arg0, arg1 any, args ...any) | находит минимальное значение среди аргументов | +| "sum(, , ...)" | SumSize(arg0, arg1 any, args ...any) | находит сумму значений аргументов | +| "sub(, )" | SubSize(arg0, arg1 any) | находит разность значений аргументов | +| "mul(, )" | MulSize(arg0, arg1 any) | находит результат умножения значений аргументов | +| "div(, )" | DivSize(arg0, arg1 any) | находит результат деления значений аргументов | +| "clamp(, , )" | ClampSize(min, val, max any) | ограничивает значение заданным диапазоном | + +Дополнительные пояснения к функции "clamp(, , )": результат вычисляется следующим образом: + +* if min ≤ val ≤ max then val; +* if val < min then min; +* if max < val then max; + ### Color Тип Color описывает 32-битный цвет в формате ARGB: diff --git a/README.md b/README.md index 5fadc61..3803ee2 100644 --- a/README.md +++ b/README.md @@ -62,9 +62,10 @@ SizeUnit is declared as type SizeUnit struct { Type SizeUnitType 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: @@ -81,12 +82,13 @@ The Type can take the following values: | 8 | SizeInMM | the Value field specifies the size in millimeters. | | 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. | +| 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. | 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.Em(n) | rui.SizeUnit{ Type: rui.SizeInEM, 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). 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”. -The suffixes are listed in the following table: +a suffix defining the type. +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 | |:------:|----------------| @@ -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 +#### 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(, , ...)" + +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(, , ...)" | MaxSize(arg0, arg1 any, args ...any) | finds the minimum value among the arguments | +| "sum(, , ...)" | SumSize(arg0, arg1 any, args ...any) | calculates the sum of the argument values | +| "sub(, )" | SubSize(arg0, arg1 any) | calculates the subtraction of argument values | +| "mul(, )" | MulSize(arg0, arg1 any) | calculates the result of multiplying the argument values | +| "div(, )" | DivSize(arg0, arg1 any) | calculates the result of dividing the argument values | +| "clamp(, , )" | ClampSize(min, val, max any) | limits value to specified range | + +Additional explanations for the function "clamp(, , )": the result is calculated as follows: + +* if min ≤ val ≤ max then val; +* if val < min then min; +* if max < val then max; + ### Color The Color type describes a 32-bit ARGB color: diff --git a/background.go b/background.go index f1ff1f9..692c73d 100644 --- a/background.go +++ b/background.go @@ -219,9 +219,9 @@ func (image *backgroundImage) cssStyle(session Session) string { if width.Type != Auto || height.Type != Auto { buffer.WriteString(` / `) - buffer.WriteString(width.cssString("auto")) + buffer.WriteString(width.cssString("auto", session)) buffer.WriteRune(' ') - buffer.WriteString(height.cssString("auto")) + buffer.WriteString(height.cssString("auto", session)) } } diff --git a/backgroundConicGradient.go b/backgroundConicGradient.go index b52d30b..00ef8a1 100644 --- a/backgroundConicGradient.go +++ b/backgroundConicGradient.go @@ -316,9 +316,9 @@ func (gradient *backgroundConicGradient) cssStyle(session Session) string { buffer.WriteRune(' ') } buffer.WriteString("at ") - buffer.WriteString(x.cssString("50%")) + buffer.WriteString(x.cssString("50%", session)) buffer.WriteString(" ") - buffer.WriteString(y.cssString("50%")) + buffer.WriteString(y.cssString("50%", session)) comma = true } diff --git a/backgroundGradient.go b/backgroundGradient.go index d7b5eee..fcb0e61 100644 --- a/backgroundGradient.go +++ b/backgroundGradient.go @@ -261,19 +261,18 @@ func (gradient *backgroundGradient) writeGradient(session Session, buffer *strin switch value := point.Pos.(type) { case string: if value != "" { - if value[0] == '@' { - value, _ = session.Constant(value[1:]) - } - if pos, ok := StringToSizeUnit(value); ok && pos.Type != Auto { - buffer.WriteRune(' ') - buffer.WriteString(pos.cssString("")) + if value, ok := session.resolveConstants(value); ok { + if pos, ok := StringToSizeUnit(value); ok && pos.Type != Auto { + buffer.WriteRune(' ') + buffer.WriteString(pos.cssString("", session)) + } } } case SizeUnit: if value.Type != Auto { 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 { buffer.WriteString("ellipse ") shapeText = "" - buffer.WriteString(r.cssString("")) + buffer.WriteString(r.cssString("", session)) buffer.WriteString(" ") - buffer.WriteString(r.cssString("")) + buffer.WriteString(r.cssString("", session)) buffer.WriteString(" ") } else { ErrorLog(`Invalid radial gradient radius: ` + text) @@ -539,9 +538,9 @@ func (gradient *backgroundRadialGradient) cssStyle(session Session) string { if value.Type != Auto { buffer.WriteString("ellipse ") shapeText = "" - buffer.WriteString(value.cssString("")) + buffer.WriteString(value.cssString("", session)) buffer.WriteString(" ") - buffer.WriteString(value.cssString("")) + buffer.WriteString(value.cssString("", session)) buffer.WriteString(" ") } @@ -553,7 +552,7 @@ func (gradient *backgroundRadialGradient) cssStyle(session Session) string { buffer.WriteString("ellipse ") shapeText = "" for i := 0; i < count; i++ { - buffer.WriteString(value[i].cssString("50%")) + buffer.WriteString(value[i].cssString("50%", session)) buffer.WriteString(" ") } @@ -568,13 +567,13 @@ func (gradient *backgroundRadialGradient) cssStyle(session Session) string { if value[i] != nil { switch value := value[i].(type) { case SizeUnit: - buffer.WriteString(value.cssString("50%")) + buffer.WriteString(value.cssString("50%", session)) buffer.WriteString(" ") case string: if text, ok := session.resolveConstants(value); ok { if size, err := stringToSizeUnit(text); err == nil { - buffer.WriteString(size.cssString("50%")) + buffer.WriteString(size.cssString("50%", session)) buffer.WriteString(" ") } else { buffer.WriteString("50% ") @@ -597,9 +596,9 @@ func (gradient *backgroundRadialGradient) cssStyle(session Session) string { buffer.WriteString(shapeText) } buffer.WriteString("at ") - buffer.WriteString(x.cssString("50%")) + buffer.WriteString(x.cssString("50%", session)) buffer.WriteString(" ") - buffer.WriteString(y.cssString("50%")) + buffer.WriteString(y.cssString("50%", session)) } buffer.WriteString(", ") diff --git a/border.go b/border.go index 29b317d..97a62f9 100644 --- a/border.go +++ b/border.go @@ -658,11 +658,14 @@ func (border *borderProperty) cssWidth(builder cssBuilder, session Session) { borders.Top.Width == borders.Left.Width && borders.Top.Width == borders.Bottom.Width { if borders.Top.Width.Type != Auto { - builder.add("border-width", borders.Top.Width.cssString("0")) + builder.add("border-width", borders.Top.Width.cssString("0", session)) } } else { - builder.addValues("border-width", " ", borders.Top.Width.cssString("0"), - borders.Right.Width.cssString("0"), borders.Bottom.Width.cssString("0"), borders.Left.Width.cssString("0")) + builder.addValues("border-width", " ", + borders.Top.Width.cssString("0", session), + borders.Right.Width.cssString("0", session), + borders.Bottom.Width.cssString("0", session), + borders.Left.Width.cssString("0", session)) } } diff --git a/bounds.go b/bounds.go index 3a742bb..5de35bb 100644 --- a/bounds.go +++ b/bounds.go @@ -213,18 +213,21 @@ func (bounds *Bounds) String() 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() { - builder.add(tag, bounds.Top.cssString("0")) + builder.add(tag, bounds.Top.cssString("0", session)) } else { - builder.addValues(tag, " ", bounds.Top.cssString("0"), bounds.Right.cssString("0"), - bounds.Bottom.cssString("0"), bounds.Left.cssString("0")) + builder.addValues(tag, " ", + 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 - bounds.cssValue("", &builder) + bounds.cssValue("", &builder, session) return builder.finish() } diff --git a/canvas.go b/canvas.go index ff92252..d1c4df8 100644 --- a/canvas.go +++ b/canvas.go @@ -592,7 +592,7 @@ func (canvas *canvasData) writeFont(name string, script *strings.Builder) { func (canvas *canvasData) SetFont(name string, size SizeUnit) { 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) } @@ -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 { case Auto: @@ -634,7 +634,7 @@ func (canvas *canvasData) setFontWithParams(name string, size SizeUnit, params F default: script.WriteString("/") - script.WriteString(params.LineHeight.cssString("")) + script.WriteString(params.LineHeight.cssString("", canvas.View().Session())) } canvas.writeFont(name, script) diff --git a/checkbox.go b/checkbox.go index b366066..83bd860 100644 --- a/checkbox.go +++ b/checkbox.go @@ -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 { - builder.add("gap", gap.cssString("0")) + builder.add("gap", gap.cssString("0", session)) } builder.add("align-items", "stretch") diff --git a/columnSeparator.go b/columnSeparator.go index 804269b..8e3a1d4 100644 --- a/columnSeparator.go +++ b/columnSeparator.go @@ -167,8 +167,9 @@ func (separator *columnSeparatorProperty) cssValue(session Session) string { buffer := allocStringBuilder() defer freeStringBuilder(buffer) - if value.Width.Type != Auto && value.Width.Type != SizeInFraction && value.Width.Value > 0 { - buffer.WriteString(value.Width.cssString("")) + if value.Width.Type != Auto && value.Width.Type != SizeInFraction && + (value.Width.Value > 0 || value.Width.Type == SizeFunction) { + buffer.WriteString(value.Width.cssString("", session)) } styles := enumProperties[BorderStyle].cssValues diff --git a/gridLayout.go b/gridLayout.go index 0db165f..384e72b 100644 --- a/gridLayout.go +++ b/gridLayout.go @@ -145,7 +145,7 @@ func (style *viewStyle) gridCellSizesCSS(tag string, session Session) string { case 1: if cellSize[0].Type != Auto { - return `repeat(auto-fill, ` + cellSize[0].cssString(`auto`) + `)` + return `repeat(auto-fill, ` + cellSize[0].cssString(`auto`, session) + `)` } default: @@ -161,14 +161,14 @@ func (style *viewStyle) gridCellSizesCSS(tag string, session Session) string { } if !allAuto { 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() defer freeStringBuilder(buffer) for _, size := range cellSize { buffer.WriteRune(' ') - buffer.WriteString(size.cssString(`auto`)) + buffer.WriteString(size.cssString(`auto`, session)) } return buffer.String() } diff --git a/listView.go b/listView.go index d7ef071..e949c14 100644 --- a/listView.go +++ b/listView.go @@ -563,13 +563,13 @@ func (listView *listViewData) itemAlign(self View, buffer *strings.Builder) { func (listView *listViewData) itemSize(self View, buffer *strings.Builder) { if itemWidth := GetListItemWidth(listView); itemWidth.Type != Auto { buffer.WriteString(` min-width: `) - buffer.WriteString(itemWidth.cssString("")) + buffer.WriteString(itemWidth.cssString("", listView.Session())) buffer.WriteRune(';') } if itemHeight := GetListItemHeight(listView); itemHeight.Type != Auto { buffer.WriteString(` min-height: `) - buffer.WriteString(itemHeight.cssString("")) + buffer.WriteString(itemHeight.cssString("", listView.Session())) 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 { itemStyleBuilder.WriteString(` grid-gap: `) - itemStyleBuilder.WriteString(gap.cssString("auto")) + itemStyleBuilder.WriteString(gap.cssString("auto", listView.Session())) itemStyleBuilder.WriteRune(';') } @@ -896,13 +896,13 @@ func (listView *listViewData) htmlSubviews(self View, buffer *strings.Builder) { if gap := GetListRowGap(listView); gap.Type != Auto { buffer.WriteString(` row-gap: `) - buffer.WriteString(gap.cssString("0")) + buffer.WriteString(gap.cssString("0", listView.Session())) buffer.WriteRune(';') } if gap := GetListColumnGap(listView); gap.Type != Auto { buffer.WriteString(` column-gap: `) - buffer.WriteString(gap.cssString("0")) + buffer.WriteString(gap.cssString("0", listView.Session())) buffer.WriteRune(';') } diff --git a/outline.go b/outline.go index ea2ac95..342fa26 100644 --- a/outline.go +++ b/outline.go @@ -103,18 +103,18 @@ type ViewOutline struct { Width SizeUnit } -func (outline ViewOutline) cssValue(builder cssBuilder) { +func (outline ViewOutline) cssValue(builder cssBuilder, session Session) { values := enumProperties[BorderStyle].cssValues if outline.Style > 0 && outline.Style < len(values) && outline.Color.Alpha() > 0 && outline.Width.Type != Auto && outline.Width.Type != SizeInFraction && 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 - outline.cssValue(&builder) + outline.cssValue(&builder, session) return builder.finish() } diff --git a/propertyGet.go b/propertyGet.go index e0ef75a..cc74a54 100644 --- a/propertyGet.go +++ b/propertyGet.go @@ -38,6 +38,9 @@ func valueToSizeUnit(value any, session Session) (SizeUnit, bool) { case SizeUnit: return value, true + case SizeFunc: + return SizeUnit{Type: SizeFunction, Function: value}, true + case string: if text, ok := session.resolveConstants(value); ok { return StringToSizeUnit(text) diff --git a/propertySet.go b/propertySet.go index e9ce7ee..34ead12 100644 --- a/propertySet.go +++ b/propertySet.go @@ -539,13 +539,20 @@ func (properties *propertyList) setSizeProperty(tag string, value any) bool { switch value := value.(type) { case string: 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) return false } case SizeUnit: size = value + case SizeFunc: + size.Type = SizeFunction + size.Function = value + case float32: size.Type = SizeInPixel size.Value = float64(value) diff --git a/radius.go b/radius.go index 44cf6e9..fee7f1f 100644 --- a/radius.go +++ b/radius.go @@ -455,7 +455,7 @@ func (radius BoxRadius) String() 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) && (radius.TopLeftY.Type == Auto || radius.TopLeftY.Value == 0) && @@ -471,23 +471,23 @@ func (radius BoxRadius) cssValue(builder cssBuilder) { buffer := allocStringBuilder() defer freeStringBuilder(buffer) - buffer.WriteString(radius.TopLeftX.cssString("0")) + buffer.WriteString(radius.TopLeftX.cssString("0", session)) if radius.AllAnglesIsEqual() { if !radius.TopLeftX.Equal(radius.TopLeftY) { buffer.WriteString(" / ") - buffer.WriteString(radius.TopLeftY.cssString("0")) + buffer.WriteString(radius.TopLeftY.cssString("0", session)) } } else { buffer.WriteRune(' ') - buffer.WriteString(radius.TopRightX.cssString("0")) + buffer.WriteString(radius.TopRightX.cssString("0", session)) buffer.WriteRune(' ') - buffer.WriteString(radius.BottomRightX.cssString("0")) + buffer.WriteString(radius.BottomRightX.cssString("0", session)) buffer.WriteRune(' ') - buffer.WriteString(radius.BottomLeftX.cssString("0")) + buffer.WriteString(radius.BottomLeftX.cssString("0", session)) if !radius.TopLeftX.Equal(radius.TopLeftY) || !radius.TopRightX.Equal(radius.TopRightY) || @@ -495,22 +495,22 @@ func (radius BoxRadius) cssValue(builder cssBuilder) { !radius.BottomRightX.Equal(radius.BottomRightY) { buffer.WriteString(" / ") - buffer.WriteString(radius.TopLeftY.cssString("0")) + buffer.WriteString(radius.TopLeftY.cssString("0", session)) buffer.WriteRune(' ') - buffer.WriteString(radius.TopRightY.cssString("0")) + buffer.WriteString(radius.TopRightY.cssString("0", session)) buffer.WriteRune(' ') - buffer.WriteString(radius.BottomRightY.cssString("0")) + buffer.WriteString(radius.BottomRightY.cssString("0", session)) buffer.WriteRune(' ') - buffer.WriteString(radius.BottomLeftY.cssString("0")) + buffer.WriteString(radius.BottomLeftY.cssString("0", session)) } } builder.add("border-radius", buffer.String()) } -func (radius BoxRadius) cssString() string { +func (radius BoxRadius) cssString(session Session) string { var builder cssValueBuilder - radius.cssValue(&builder) + radius.cssValue(&builder, session) return builder.finish() } diff --git a/resizable.go b/resizable.go index f6da563..4a65741 100644 --- a/resizable.go +++ b/resizable.go @@ -340,7 +340,7 @@ func (resizable *resizableData) updateResizeBorderWidth() { } func (resizable *resizableData) cellSizeCSS() (string, string) { - w := resizable.resizeBorderWidth().cssString("4px") + w := resizable.resizeBorderWidth().cssString("4px", resizable.Session()) side := resizable.getSide() column := "1fr" row := "1fr" @@ -384,7 +384,7 @@ func (resizable *resizableData) htmlSubviews(self View, buffer *strings.Builder) top := 1 leftSide := (side & LeftSide) != 0 rightSide := (side & RightSide) != 0 - w := resizable.resizeBorderWidth().cssString("4px") + w := resizable.resizeBorderWidth().cssString("4px", resizable.Session()) if leftSide { left = 2 diff --git a/shadow.go b/shadow.go index 5d2825e..3d0541a 100644 --- a/shadow.go +++ b/shadow.go @@ -151,13 +151,13 @@ func (shadow *viewShadowData) cssStyle(buffer *strings.Builder, session Session, buffer.WriteString("inset ") } - buffer.WriteString(offsetX.cssString("0")) + buffer.WriteString(offsetX.cssString("0", session)) buffer.WriteByte(' ') - buffer.WriteString(offsetY.cssString("0")) + buffer.WriteString(offsetY.cssString("0", session)) buffer.WriteByte(' ') - buffer.WriteString(blurRadius.cssString("0")) + buffer.WriteString(blurRadius.cssString("0", session)) buffer.WriteByte(' ') - buffer.WriteString(spreadRadius.cssString("0")) + buffer.WriteString(spreadRadius.cssString("0", session)) buffer.WriteByte(' ') buffer.WriteString(color.cssString()) return true @@ -177,11 +177,11 @@ func (shadow *viewShadowData) cssTextStyle(buffer *strings.Builder, session Sess } buffer.WriteString(lead) - buffer.WriteString(offsetX.cssString("0")) + buffer.WriteString(offsetX.cssString("0", session)) buffer.WriteByte(' ') - buffer.WriteString(offsetY.cssString("0")) + buffer.WriteString(offsetY.cssString("0", session)) buffer.WriteByte(' ') - buffer.WriteString(blurRadius.cssString("0")) + buffer.WriteString(blurRadius.cssString("0", session)) buffer.WriteByte(' ') buffer.WriteString(color.cssString()) return true diff --git a/sizeFunc.go b/sizeFunc.go new file mode 100644 index 0000000..cd76fd5 --- /dev/null +++ b/sizeFunc.go @@ -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 +} diff --git a/sizeFunc_test.go b/sizeFunc_test.go new file mode 100644 index 0000000..3b508a6 --- /dev/null +++ b/sizeFunc_test.go @@ -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))`) +} diff --git a/sizeUnit.go b/sizeUnit.go index 7e322ce..3106b63 100644 --- a/sizeUnit.go +++ b/sizeUnit.go @@ -14,89 +14,94 @@ import ( type SizeUnitType uint8 const ( - // Auto - default value. + // Auto is the SizeUnit type: default value. Auto SizeUnitType = 0 - // SizeInPixel - size in pixels. + // SizeInPixel is the SizeUnit type: the Value field specifies the size in pixels. SizeInPixel SizeUnitType = 1 - // SizeInEM - size in em. + // SizeInEM is the SizeUnit type: the Value field specifies the size in em. SizeInEM SizeUnitType = 2 - // SizeInEX - size in em. + // SizeInEX is the SizeUnit type: the Value field specifies the size in em. 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 - // 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 - // SizeInPc - size in pc (1pc = 12pt). + // SizeInPc is the SizeUnit type: the Value field specifies the size in pc (1pc = 12pt). SizeInPc SizeUnitType = 6 - // SizeInInch - size in inches. + // SizeInInch is the SizeUnit type: the Value field specifies the size in inches. SizeInInch SizeUnitType = 7 - // SizeInMM - size in millimeters. + // SizeInMM is the SizeUnit type: the Value field specifies the size in millimeters. SizeInMM SizeUnitType = 8 - // SizeInCM - size in centimeters. + // SizeInCM is the SizeUnit type: the Value field specifies the size in centimeters. 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 + // 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). type SizeUnit struct { - Type SizeUnitType - Value float64 + Type SizeUnitType + Value float64 + Function SizeFunc } // AutoSize creates SizeUnit with Auto type func AutoSize() SizeUnit { - return SizeUnit{Auto, 0} + return SizeUnit{Auto, 0, nil} } // Px creates SizeUnit with SizeInPixel type func Px(value float64) SizeUnit { - return SizeUnit{SizeInPixel, value} + return SizeUnit{SizeInPixel, value, nil} } // Em creates SizeUnit with SizeInEM type func Em(value float64) SizeUnit { - return SizeUnit{SizeInEM, value} + return SizeUnit{SizeInEM, value, nil} } // Ex creates SizeUnit with SizeInEX type func Ex(value float64) SizeUnit { - return SizeUnit{SizeInEX, value} + return SizeUnit{SizeInEX, value, nil} } // Percent creates SizeUnit with SizeInDIP type func Percent(value float64) SizeUnit { - return SizeUnit{SizeInPercent, value} + return SizeUnit{SizeInPercent, value, nil} } // Pt creates SizeUnit with SizeInPt type func Pt(value float64) SizeUnit { - return SizeUnit{SizeInPt, value} + return SizeUnit{SizeInPt, value, nil} } // Pc creates SizeUnit with SizeInPc type func Pc(value float64) SizeUnit { - return SizeUnit{SizeInPc, value} + return SizeUnit{SizeInPc, value, nil} } // Mm creates SizeUnit with SizeInMM type func Mm(value float64) SizeUnit { - return SizeUnit{SizeInMM, value} + return SizeUnit{SizeInMM, value, nil} } // Cm creates SizeUnit with SizeInCM type func Cm(value float64) SizeUnit { - return SizeUnit{SizeInCM, value} + return SizeUnit{SizeInCM, value, nil} } // Inch creates SizeUnit with SizeInInch type func Inch(value float64) SizeUnit { - return SizeUnit{SizeInInch, value} + return SizeUnit{SizeInInch, value, nil} } // Fr creates SizeUnit with SizeInFraction type func Fr(value float64) SizeUnit { - return SizeUnit{SizeInFraction, value} + return SizeUnit{SizeInFraction, value, nil} } // 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 } @@ -161,8 +166,15 @@ func stringToSizeUnit(value string) (SizeUnit, error) { // String - convert SizeUnit to string func (size SizeUnit) String() string { - if size.Type == Auto { + switch size.Type { + case Auto: return "auto" + + case SizeFunction: + if size.Function == nil { + return "auto" + } + return size.Function.String() } if suffix, ok := sizeUnitSuffixes()[size.Type]; ok { return fmt.Sprintf("%g%s", size.Value, suffix) @@ -171,13 +183,19 @@ func (size SizeUnit) String() 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 { case Auto: return textForAuto case SizeInEM: return fmt.Sprintf("%grem", size.Value) + + case SizeFunction: + if size.Function == nil { + return textForAuto + } + return size.Function.cssString(session) } if size.Value == 0 { diff --git a/tableView.go b/tableView.go index bd979ff..49151d2 100644 --- a/tableView.go +++ b/tableView.go @@ -594,7 +594,7 @@ func (table *tableViewData) propertyChanged(tag string) { updateCSSProperty(htmlID, "border-spacing", "0", session) updateCSSProperty(htmlID, "border-collapse", "collapse", session) } else { - updateCSSProperty(htmlID, "border-spacing", gap.cssString("0"), session) + updateCSSProperty(htmlID, "border-spacing", gap.cssString("0", session), session) updateCSSProperty(htmlID, "border-collapse", "separate", session) } @@ -1319,24 +1319,26 @@ func (table *tableViewData) getCurrent() CellIndex { } 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 { builder.add("border-spacing", "0") builder.add("border-collapse", "collapse") } else { - builder.add("border-spacing", gap.cssString("0")) + builder.add("border-spacing", gap.cssString("0", session)) builder.add("border-collapse", "separate") } } func (table *tableViewData) ReloadTableData() { + session := table.Session() if content := table.content(); content != nil { - updateProperty(table.htmlID(), "data-rows", strconv.Itoa(content.RowCount()), table.Session()) - updateProperty(table.htmlID(), "data-columns", strconv.Itoa(content.ColumnCount()), table.Session()) + updateProperty(table.htmlID(), "data-rows", strconv.Itoa(content.RowCount()), 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) { diff --git a/view.go b/view.go index 9df58ab..c29a8cb 100644 --- a/view.go +++ b/view.go @@ -446,7 +446,7 @@ func viewPropertyChanged(view *viewData, tag string) { return case Outline, OutlineColor, OutlineStyle, OutlineWidth: - updateCSSProperty(htmlID, Outline, GetOutline(view).cssString(), session) + updateCSSProperty(htmlID, Outline, GetOutline(view).cssString(session), session) return case Shadow: @@ -462,19 +462,19 @@ func viewPropertyChanged(view *viewData, tag string) { RadiusBottomLeft, RadiusBottomLeftX, RadiusBottomLeftY, RadiusBottomRight, RadiusBottomRightX, RadiusBottomRightY: radius := GetRadius(view) - updateCSSProperty(htmlID, "border-radius", radius.cssString(), session) + updateCSSProperty(htmlID, "border-radius", radius.cssString(session), session) return case Margin, MarginTop, MarginRight, MarginBottom, MarginLeft, "top-margin", "right-margin", "bottom-margin", "left-margin": margin := GetMargin(view) - updateCSSProperty(htmlID, Margin, margin.cssString(), session) + updateCSSProperty(htmlID, Margin, margin.cssString(session), session) return case Padding, PaddingTop, PaddingRight, PaddingBottom, PaddingLeft, "top-padding", "right-padding", "bottom-padding", "left-padding": padding := GetPadding(view) - updateCSSProperty(htmlID, Padding, padding.cssString(), session) + updateCSSProperty(htmlID, Padding, padding.cssString(session), session) return case AvoidBreak: @@ -626,7 +626,7 @@ func viewPropertyChanged(view *viewData, tag string) { if cssTag, ok := sizeProperties[tag]; ok { size, _ := sizeProperty(view, tag, session) - updateCSSProperty(htmlID, cssTag, size.cssString(""), session) + updateCSSProperty(htmlID, cssTag, size.cssString("", session), session) return } diff --git a/viewClip.go b/viewClip.go index 2f61010..d8074ea 100644 --- a/viewClip.go +++ b/viewClip.go @@ -146,13 +146,13 @@ func (clip *insetClip) cssStyle(session Session) string { for _, tag := range []string{Top, Right, Bottom, Left} { value, _ := sizeProperty(clip, tag, session) buffer.WriteString(leadText) - buffer.WriteString(value.cssString("0px")) + buffer.WriteString(value.cssString("0px", session)) leadText = " " } if radius := getRadiusProperty(clip); radius != nil { buffer.WriteString(" round ") - buffer.WriteString(radius.BoxRadius(session).cssString()) + buffer.WriteString(radius.BoxRadius(session).cssString(session)) } buffer.WriteRune(')') @@ -211,15 +211,15 @@ func (clip *circleClip) cssStyle(session Session) string { buffer.WriteString("circle(") r, _ := sizeProperty(clip, Radius, session) - buffer.WriteString(r.cssString("50%")) + buffer.WriteString(r.cssString("50%", session)) buffer.WriteString(" at ") x, _ := sizeProperty(clip, X, session) - buffer.WriteString(x.cssString("50%")) + buffer.WriteString(x.cssString("50%", session)) buffer.WriteRune(' ') y, _ := sizeProperty(clip, Y, session) - buffer.WriteString(y.cssString("50%")) + buffer.WriteString(y.cssString("50%", session)) buffer.WriteRune(')') return buffer.String() @@ -280,17 +280,17 @@ func (clip *ellipseClip) cssStyle(session Session) string { rx, _ := sizeProperty(clip, RadiusX, session) ry, _ := sizeProperty(clip, RadiusX, session) buffer.WriteString("ellipse(") - buffer.WriteString(rx.cssString("50%")) + buffer.WriteString(rx.cssString("50%", session)) buffer.WriteRune(' ') - buffer.WriteString(ry.cssString("50%")) + buffer.WriteString(ry.cssString("50%", session)) buffer.WriteString(" at ") x, _ := sizeProperty(clip, X, session) - buffer.WriteString(x.cssString("50%")) + buffer.WriteString(x.cssString("50%", session)) buffer.WriteRune(' ') y, _ := sizeProperty(clip, Y, session) - buffer.WriteString(y.cssString("50%")) + buffer.WriteString(y.cssString("50%", session)) buffer.WriteRune(')') return buffer.String() @@ -427,13 +427,13 @@ func (clip *polygonClip) cssStyle(session Session) string { case string: if val, ok := session.resolveConstants(value); ok { if size, ok := StringToSizeUnit(val); ok { - buffer.WriteString(size.cssString("0px")) + buffer.WriteString(size.cssString("0px", session)) return } } case SizeUnit: - buffer.WriteString(value.cssString("0px")) + buffer.WriteString(value.cssString("0px", session)) return } diff --git a/viewStyle.go b/viewStyle.go index f09aa54..4adecdc 100644 --- a/viewStyle.go +++ b/viewStyle.go @@ -173,11 +173,11 @@ func (style *viewStyle) backgroundCSS(session Session) string { func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) { 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 { - padding.cssValue(Padding, builder) + padding.cssValue(Padding, builder, session) } if border := getBorder(style, Border); border != nil { @@ -187,10 +187,10 @@ func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) { } radius := getRadius(style, session) - radius.cssValue(builder) + radius.cssValue(builder, session) 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 { @@ -215,7 +215,7 @@ func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) { if !ok { cssTag = tag } - builder.add(cssTag, size.cssString("")) + builder.add(cssTag, size.cssString("", session)) } } diff --git a/viewTransform.go b/viewTransform.go index 6625c19..d13f03a 100644 --- a/viewTransform.go +++ b/viewTransform.go @@ -125,11 +125,11 @@ func (style *viewStyle) transform(session Session) string { buffer.WriteRune(' ') } buffer.WriteString(`translate3d(`) - buffer.WriteString(x.cssString("0")) + buffer.WriteString(x.cssString("0", session)) buffer.WriteRune(',') - buffer.WriteString(y.cssString("0")) + buffer.WriteString(y.cssString("0", session)) buffer.WriteRune(',') - buffer.WriteString(z.cssString("0")) + buffer.WriteString(z.cssString("0", session)) buffer.WriteRune(')') } @@ -172,9 +172,9 @@ func (style *viewStyle) transform(session Session) string { buffer.WriteRune(' ') } buffer.WriteString(`translate(`) - buffer.WriteString(x.cssString("0")) + buffer.WriteString(x.cssString("0", session)) buffer.WriteRune(',') - buffer.WriteString(y.cssString("0")) + buffer.WriteString(y.cssString("0", session)) buffer.WriteRune(')') } @@ -205,12 +205,12 @@ func (style *viewStyle) transform(session Session) string { func (style *viewStyle) writeViewTransformCSS(builder cssBuilder, session Session) { if getTransform3D(style, session) { 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) 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 { @@ -223,12 +223,12 @@ func (style *viewStyle) writeViewTransformCSS(builder cssBuilder, session Sessio x, y, z := getOrigin(style, session) 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 { x, y, _ := getOrigin(style, session) 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) value := "" 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) } @@ -267,11 +267,11 @@ func (view *viewData) updateTransformProperty(tag string) bool { value := "" if getTransform3D(view, session) { 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 { 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)