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 | ||||
| * 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 | ||||
|  |  | |||
							
								
								
									
										62
									
								
								README-ru.md
								
								
								
								
							
							
						
						
									
										62
									
								
								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>, ...)" | ||||
| 
 | ||||
| где 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 описывает 32-битный цвет в формате ARGB: | ||||
|  |  | |||
							
								
								
									
										56
									
								
								README.md
								
								
								
								
							
							
						
						
									
										56
									
								
								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(<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 | ||||
| 
 | ||||
| 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 { | ||||
| 				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)) | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
|  |  | |||
|  | @ -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 | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -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(", ") | ||||
|  |  | |||
|  | @ -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)) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										15
									
								
								bounds.go
								
								
								
								
							
							
						
						
									
										15
									
								
								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() | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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") | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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() | ||||
| 		} | ||||
|  |  | |||
							
								
								
									
										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) { | ||||
| 	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(';') | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -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() | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
							
								
								
									
										24
									
								
								radius.go
								
								
								
								
							
							
						
						
									
										24
									
								
								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() | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
							
								
								
									
										14
									
								
								shadow.go
								
								
								
								
							
							
						
						
									
										14
									
								
								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 | ||||
|  |  | |||
|  | @ -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 | ||||
| 
 | ||||
| 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 { | ||||
|  |  | |||
							
								
								
									
										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-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) { | ||||
|  |  | |||
							
								
								
									
										10
									
								
								view.go
								
								
								
								
							
							
						
						
									
										10
									
								
								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 | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										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} { | ||||
| 		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 | ||||
| 		} | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										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) { | ||||
| 
 | ||||
| 	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)) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue