mirror of https://github.com/anoshenko/rui.git
Compare commits
16 Commits
Author | SHA1 | Date |
---|---|---|
|
c3c8b9e858 | |
|
3090a0e94f | |
|
b0185726db | |
|
2dd8d8d256 | |
|
4cec7fef26 | |
|
e618377c11 | |
|
73b14ed78a | |
|
bbbaf28aba | |
|
0433f460e4 | |
|
d633c80155 | |
|
cb4d197bb7 | |
|
2f07584b37 | |
|
24aeeb515b | |
|
f2fb948325 | |
|
4b00299878 | |
|
3c3c09b043 |
|
@ -1,3 +1,9 @@
|
|||
# v0.20.0
|
||||
|
||||
* Added support of binding
|
||||
* Added "binding" argument to CreateViewFromResources, CreateViewFromText, and CreateViewFromObject functions
|
||||
* Changed ParseDataText function return values
|
||||
|
||||
# v0.19.0
|
||||
|
||||
* Added support of drag-and-drop
|
||||
|
@ -5,7 +11,7 @@
|
|||
|
||||
# v0.18.2
|
||||
|
||||
* fixed typo: GetShadowPropertys -> GetShadowProperty
|
||||
* fixed typo: GetShadowProperties -> GetShadowProperty
|
||||
|
||||
# v0.18.0
|
||||
|
||||
|
|
107
README-ru.md
107
README-ru.md
|
@ -161,8 +161,8 @@ SizeUnit объявлена как
|
|||
| "sub(<arg1>, <arg2>)" | SubSize(arg0, arg1 any) | находит разность значений аргументов |
|
||||
| "mul(<arg1>, <arg2>)" | MulSize(arg0, arg1 any) | находит результат умножения значений аргументов |
|
||||
| "div(<arg1>, <arg2>)" | DivSize(arg0, arg1 any) | находит результат деления значений аргументов |
|
||||
| "rem(<arg1>, <arg2>)" | ModSize(arg0, arg1 any) | находит остаток деления значений аргументов, результат имеет тотже знак что и делимое |
|
||||
| "mod(<arg1>, <arg2>)" | ModSize(arg0, arg1 any) | находит остаток деления значений аргументов, результат имеет тотже знак что и делитель |
|
||||
| "rem(<arg1>, <arg2>)" | ModSize(arg0, arg1 any) | находит остаток деления значений аргументов, результат имеет тот же знак что и делимое |
|
||||
| "mod(<arg1>, <arg2>)" | ModSize(arg0, arg1 any) | находит остаток деления значений аргументов, результат имеет тот же знак что и делитель |
|
||||
| "round(<arg1>, <arg2>)" | RoundSize(arg0, arg1 any) | округляет первый аргумент до ближайшего целого числа кратного второму аргументу |
|
||||
| "round-up(<arg1>, <arg2>)" | RoundUpSize(arg0, arg1 any) | округляет первый аргумент до ближайшего большего целого числа, кратного второму аргументу |
|
||||
| "round-down(<arg1>, <arg2>)" | RoundDownSize(arg0, arg1 any) | округляет первый аргумент до ближайшего меньшего целого числа кратного второму аргументу |
|
||||
|
@ -1086,7 +1086,7 @@ RadiusProperty, а не структура BoxRadius. Получить стру
|
|||
|
||||
Получить значение данного свойства можно с помощью функции
|
||||
|
||||
func GetShadowPropertys(view View, subviewID ...string) []ShadowProperty
|
||||
func GetShadowProperties(view View, subviewID ...string) []ShadowProperty
|
||||
|
||||
Если тень не задана, то данная функция вернет пустой массив
|
||||
|
||||
|
@ -2291,14 +2291,14 @@ radius необходимо передать nil
|
|||
|
||||
#### Свойство "drag-data"
|
||||
|
||||
Для того чтобы сделать View перетаскиваемым ему неоходимо задать свойство "drag-data" (константа DragData).
|
||||
Для того чтобы сделать View перетаскиваемым ему необходимо задать свойство "drag-data" (константа DragData).
|
||||
Данное свойство задает множество перетаскиваемых данных в виде ключ:значение и имеет тип:
|
||||
|
||||
map[string]string
|
||||
|
||||
В качестве ключей рекомендуется использовать mime-тип значения.
|
||||
Например, если в перетаскиваемыми данными асляется текст, то ключем должен быть "text/plain", если jpeg-изображение, то "image/jpg" и т.п.
|
||||
Но это только рекомендация, ключем может быть любой текст.
|
||||
Например, если в перетаскиваемыми данными является текст, то ключом должен быть "text/plain", если jpeg-изображение, то "image/jpg" и т.п.
|
||||
Но это только рекомендация, ключом может быть любой текст.
|
||||
|
||||
Пример
|
||||
|
||||
|
@ -2318,7 +2318,7 @@ radius необходимо передать nil
|
|||
По умолчанию при перетаскивании перемещается весь View. Часто это бывает не удобно, например если View очень большой.
|
||||
|
||||
Свойство "drag-image" (константа DragImage) типа string позволяет задать картинку, которая будет отображаться вместо View при перетаскивании.
|
||||
В качестве значения "drag-image" задаеться:
|
||||
В качестве значения "drag-image" задается:
|
||||
* Имя изображения в ресурсах приложения
|
||||
* Константа изображения
|
||||
* URL изображения
|
||||
|
@ -2442,15 +2442,15 @@ radius необходимо передать nil
|
|||
|
||||
#### События "drag-enter-event", "drag-leave-event" и "drag-over-event"
|
||||
|
||||
События "drag-enter-event", "drag-leave-event" и "drag-over-event" генерируются только для View приемника перетескиваемого объекта.
|
||||
События "drag-enter-event", "drag-leave-event" и "drag-over-event" генерируются только для View приемника перетаскиваемого объекта.
|
||||
|
||||
Событие "drag-enter-event" генерируется когда перетаскиваемый оъект входит в область View приемника.
|
||||
Событие "drag-enter-event" генерируется когда перетаскиваемый объект входит в область View приемника.
|
||||
|
||||
Событие "drag-leave-event" генерируется когда перетаскиваемый оъект покидает область View приемника.
|
||||
Событие "drag-leave-event" генерируется когда перетаскиваемый объект покидает область View приемника.
|
||||
|
||||
Событие "drag-over-event" генерируется c определенным интервалом (несколько раз в секунду) пока перетаскиваемый оъект находится область View приемника.
|
||||
Событие "drag-over-event" генерируется c определенным интервалом (несколько раз в секунду) пока перетаскиваемый объект находится область View приемника.
|
||||
|
||||
Пример, меняем цвет рамки с серой на красную когда перетаскиваемый оъект находится над областью приемника
|
||||
Пример, меняем цвет рамки с серой на красную когда перетаскиваемый объект находится над областью приемника
|
||||
|
||||
view.SetParams(rui.Params{
|
||||
rui.DragEnterEvent: func(view rui.View, event rui.DragAndDropEvent)) {
|
||||
|
@ -3834,7 +3834,7 @@ float32, float64, int, int8…int64, uint, uint8…uint64.
|
|||
|
||||
Между пунктами списка можно добавлять разделителями. Для этого используется свойство "item-separators" (константа ItemSeparators).
|
||||
Данному свойству присваивается массив индексов пунктов после которых необходимо добавить разделители.
|
||||
Свойству "item-separators" могут присваиваться теже типы данных что и свойству "disabled-items".
|
||||
Свойству "item-separators" могут присваиваться те же типы данных что и свойству "disabled-items".
|
||||
Прочитать значение свойства "item-separators" можно с помощью функции
|
||||
|
||||
func GetDropDownItemSeparators(view View, subviewID ...string) []int
|
||||
|
@ -4085,7 +4085,7 @@ int свойство "current" (константа Current). Значение "c
|
|||
с помощью int свойств "checkbox-horizontal-align" и "checkbox-vertical-align" (константы
|
||||
CheckboxHorizontalAlign и CheckboxVerticalAlign)
|
||||
|
||||
Свойство "checkbox-horizontal-align" (константа СheckboxHorizontalAlign) может принимать следующие значения:
|
||||
Свойство "checkbox-horizontal-align" (константа CheckboxHorizontalAlign) может принимать следующие значения:
|
||||
|
||||
| Значение | Константа | Имя | Расположение чекбокса |
|
||||
|:--------:|--------------|----------|-------------------------------------------------|
|
||||
|
@ -4647,7 +4647,7 @@ rotation - угол поворота эллипса относительно ц
|
|||
Метод NewPath() создает пустую фигуру. Далее вы должны описать фигуру используя методы интерфейса Path
|
||||
|
||||
Метод NewPathFromSvg(data string) Path создает фигуру описанную в параметре data.
|
||||
Параметр data является описанием фигуры в формате елемента <path> svg изображения. Например
|
||||
Параметр data является описанием фигуры в формате элемента <path> svg изображения. Например
|
||||
|
||||
path := canvas.NewPathFromSvg("M 30,0 C 30,0 27,8.6486 17,21.622 7,34.595 0,40 0,40 0,40 6,44.3243 17,58.378 28,72.432 30,80 30,80 30,80 37.8387,65.074 43,58.378 53,45.405 60,40 60,40 60,40 53,34.5946 43,21.622 33,8.649 30,0 30,0 Z")
|
||||
|
||||
|
@ -4827,7 +4827,7 @@ AudioPlayer и VideoPlayer это элементы которые предназ
|
|||
|
||||
### Свойство "src"
|
||||
|
||||
Свойство "src" (константа Source) задает один или несколько источников медиафайлов. Свойство "src" может принимать
|
||||
Свойство "src" (константа Source) задает один или несколько источников медиа файлов. Свойство "src" может принимать
|
||||
значение следующих типов:
|
||||
|
||||
* string,
|
||||
|
@ -4872,7 +4872,7 @@ AudioPlayer и VideoPlayer это элементы которые предназ
|
|||
|:--------:|-----------------|------------|----------------------------------------------------------------------------------------|
|
||||
| 0 | PreloadNone | "none" | Медиа файл не должен быть предварительно загружен |
|
||||
| 1 | PreloadMetadata | "metadata" | Предварительно загружаются только метаданные |
|
||||
| 2 | PreloadAuto | "auto" | Весь медиафайл может быть загружен, даже если пользователь не должен его использовать. |
|
||||
| 2 | PreloadAuto | "auto" | Весь медиа файл может быть загружен, даже если пользователь не должен его использовать.|
|
||||
|
||||
Значение по умолчанию PreloadAuto (2)
|
||||
|
||||
|
@ -5704,6 +5704,79 @@ Safari и Firefox.
|
|||
Для получения объекта используется метод Object.
|
||||
Для получения элементов массива используются методы ArraySize, ArrayElement и ArrayElements
|
||||
|
||||
## Связывание (binding)
|
||||
|
||||
Механизм связывания предназначен для задания обработчиков событий и слушателей изменений в ресурсах приложений.
|
||||
|
||||
Рассмотрим пример:
|
||||
|
||||
Файл ресурсов описывающий кнопку button.rui
|
||||
|
||||
Button {
|
||||
click-event = ButtonClick,
|
||||
}
|
||||
|
||||
Код для создания этой кнопки из ресурсов
|
||||
|
||||
type button struct {
|
||||
view rui.View
|
||||
}
|
||||
|
||||
func createButton(session rui.Session) rui.View {
|
||||
return rui.CreateViewFromResources(session, "button.rui", new(button))
|
||||
}
|
||||
|
||||
func (button *button) OnCreate(view rui.View) {
|
||||
button.view = view
|
||||
}
|
||||
|
||||
func (button *button) ButtonClick() {
|
||||
rui.DebugLog("Button clicked")
|
||||
}
|
||||
|
||||
В данном примере в файле ресурсов указано, что в качестве обработчика клика надо использовать функцию с именем "ButtonClick".
|
||||
При создании View из ресурсов с помощью функции CreateViewFromResources в качестве третьего параметра задается объект связывания.
|
||||
При возникновении события "click-event" система будет искать в связанном объекте один из следующих методов
|
||||
|
||||
ButtonClick()
|
||||
ButtonClick(rui.View)
|
||||
ButtonClick(rui.MouseEvent)
|
||||
ButtonClick(rui.View, rui.MouseEvent)
|
||||
|
||||
Система найдет метод ButtonClick() и вызовет его.
|
||||
|
||||
Также для связанного объекта может задаваться опциональный метод
|
||||
|
||||
OnCreate(view rui.View)
|
||||
|
||||
Данный метод вызывается функциями CreateViewFromText, CreateViewFromResources и CreateViewFromObject после создания View.
|
||||
В данном примере этот метод используется для сохранения указателя на созданный View.
|
||||
|
||||
Теперь рассмотрим как добавить отслеживание изменения свойств.
|
||||
Для этого добавим в пример слушателя изменения свойства "background-color" в свойство "change-listeners"
|
||||
|
||||
Button {
|
||||
click-event = ButtonClick,
|
||||
change-listeners = {
|
||||
background-color = BackgroundColorChanged,
|
||||
},
|
||||
}
|
||||
|
||||
И добавим соответствующий метод
|
||||
|
||||
func (button *button) BackgroundColorChanged() {
|
||||
rui.DebugLog("Background color changed")
|
||||
}
|
||||
|
||||
Данный метод может иметь одно из следующих описаний
|
||||
|
||||
BackgroundColorChanged()
|
||||
BackgroundColorChanged(rui.View)
|
||||
BackgroundColorChanged(rui.PropertyName)
|
||||
BackgroundColorChanged(rui.View, rui.PropertyName)
|
||||
|
||||
Важное замечание: Все методы вызываемые через связывание должны быть публичными (начинаться с большой буквы)
|
||||
|
||||
## Ресурсы
|
||||
|
||||
Ресурсы (картинки, темы, переводы и т.д.) с которыми работает приложение должны размещаться по
|
||||
|
|
84
README.md
84
README.md
|
@ -520,7 +520,7 @@ For the "edit-text-changed" event, this
|
|||
* func(newText string)
|
||||
* []func(editor EditView, newText string)
|
||||
* []func(newText string)
|
||||
* []any содержащий только func(editor EditView, newText string) и func(newText string)
|
||||
* []any containing only func(editor EditView, newText string) и func(newText string)
|
||||
|
||||
And the "edit-text-changed" property always stores and returns []func(EditView, string).
|
||||
|
||||
|
@ -1062,7 +1062,7 @@ The ShadowProperty text representation has the following format:
|
|||
|
||||
You can get the value of "shadow" property using the function
|
||||
|
||||
func GetShadowPropertys(view View, subviewID ...string) []ShadowProperty
|
||||
func GetShadowProperties(view View, subviewID ...string) []ShadowProperty
|
||||
|
||||
If no shadow is specified, then this function will return an empty array
|
||||
|
||||
|
@ -2135,7 +2135,7 @@ You can get lists of pointer event listeners using the functions:
|
|||
|
||||
### Touch events
|
||||
|
||||
These events are used to track multipoint touches. Single touches emulate mouse events.
|
||||
These events are used to track multi point touches. Single touches emulate mouse events.
|
||||
If you do not need to track multi-point touches, then it is easier to use mouse events
|
||||
|
||||
| Event | Constant | Description |
|
||||
|
@ -5720,6 +5720,80 @@ using the SetResourcePath function before creating the Application:
|
|||
app.Start("localhost:8000")
|
||||
}
|
||||
|
||||
## Binding
|
||||
|
||||
The binding mechanism is designed to set event handlers and listeners for changes in application resources.
|
||||
|
||||
Let's look at an example:
|
||||
|
||||
The resource file ("button.rui") describing the button:
|
||||
|
||||
Button {
|
||||
click-event = ButtonClick,
|
||||
}
|
||||
|
||||
The code to create this button from resources
|
||||
|
||||
type button struct {
|
||||
view rui.View
|
||||
}
|
||||
|
||||
func createButton(session rui.Session) rui.View {
|
||||
return rui.CreateViewFromResources(session, "button.rui", new(button))
|
||||
}
|
||||
|
||||
func (button *button) OnCreate(view rui.View) {
|
||||
button.view = view
|
||||
}
|
||||
|
||||
func (button *button) ButtonClick() {
|
||||
rui.DebugLog("Button clicked")
|
||||
}
|
||||
|
||||
In this example, the resource file specifies that a function named "ButtonClick" should be used as the click handler.
|
||||
When creating a View from resources using the CreateViewFromResources function, the binding object is specified as the third parameter.
|
||||
When a "click-event" occurs, the system will look for one of the following methods in the binding object
|
||||
|
||||
ButtonClick()
|
||||
ButtonClick(rui.View)
|
||||
ButtonClick(rui.MouseEvent)
|
||||
ButtonClick(rui.View, rui.MouseEvent)
|
||||
|
||||
The system will find the ButtonClick() method and call it.
|
||||
|
||||
The optional method OnCreate can also be specified for the associated object
|
||||
|
||||
OnCreate(view rui.View)
|
||||
|
||||
This method is called by the CreateViewFromText, CreateViewFromResources, and CreateViewFromObject functions after the View has been created.
|
||||
In this example, this method is used to save a pointer to the created View.
|
||||
|
||||
Now let's look at how to add property change tracking.
|
||||
|
||||
To do this, let's add a property change listener "background-color" to the property "change-listeners" in the example.
|
||||
|
||||
Button {
|
||||
click-event = ButtonClick,
|
||||
change-listeners = {
|
||||
background-color = BackgroundColorChanged,
|
||||
},
|
||||
}
|
||||
|
||||
And we will add the corresponding method
|
||||
|
||||
func (button *button) BackgroundColorChanged() {
|
||||
rui.DebugLog("Background color changed")
|
||||
}
|
||||
|
||||
This method can have one of the following descriptions
|
||||
|
||||
BackgroundColorChanged()
|
||||
BackgroundColorChanged(rui.View)
|
||||
BackgroundColorChanged(rui.PropertyName)
|
||||
BackgroundColorChanged(rui.View, rui.PropertyName)
|
||||
|
||||
Important note: All methods called via binding must be public (start with a capital letter)
|
||||
|
||||
## Images for screens with different pixel densities
|
||||
|
||||
If you need to add separate images to the resources for screens with different pixel densities,
|
||||
|
@ -5919,7 +5993,7 @@ The library defines a number of constants and styles. You can override them in y
|
|||
|
||||
System styles that you can override:
|
||||
|
||||
| Style name | Описание |
|
||||
| Style name | Description |
|
||||
|---------------------|---------------------------------------------------------------------|
|
||||
| ruiApp | This style is used to set the default text style (font, size, etc.) |
|
||||
| ruiView | Default View Style |
|
||||
|
@ -6011,7 +6085,7 @@ Translation files must have the "rui" extension and the following format
|
|||
<text 2> = <translation 2>,
|
||||
…
|
||||
},
|
||||
<язык 2> = _{
|
||||
<language 2> = _{
|
||||
<text 1> = <translation 1>,
|
||||
<text 2> = <translation 2>,
|
||||
…
|
||||
|
|
24
animation.go
24
animation.go
|
@ -2,6 +2,7 @@ package rui
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"maps"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -209,7 +210,7 @@ type animationData struct {
|
|||
usageCounter int
|
||||
view View
|
||||
listener func(view View, animation AnimationProperty, event PropertyName)
|
||||
oldListeners map[PropertyName][]func(View, PropertyName)
|
||||
oldListeners map[PropertyName][]oneArgListener[View, PropertyName]
|
||||
oldAnimation []AnimationProperty
|
||||
}
|
||||
|
||||
|
@ -661,7 +662,7 @@ func (animation *animationData) animationCSS(session Session) string {
|
|||
buffer.WriteString(animation.keyFramesName)
|
||||
|
||||
if duration, ok := floatProperty(animation, Duration, session, 1); ok && duration > 0 {
|
||||
buffer.WriteString(fmt.Sprintf(" %gs ", duration))
|
||||
fmt.Fprintf(buffer, " %gs ", duration)
|
||||
} else {
|
||||
buffer.WriteString(" 1s ")
|
||||
}
|
||||
|
@ -669,7 +670,7 @@ func (animation *animationData) animationCSS(session Session) string {
|
|||
buffer.WriteString(timingFunctionCSS(animation, TimingFunction, session))
|
||||
|
||||
if delay, ok := floatProperty(animation, Delay, session, 0); ok && delay > 0 {
|
||||
buffer.WriteString(fmt.Sprintf(" %gs", delay))
|
||||
fmt.Fprintf(buffer, " %gs", delay)
|
||||
} else {
|
||||
buffer.WriteString(" 0s")
|
||||
}
|
||||
|
@ -678,7 +679,7 @@ func (animation *animationData) animationCSS(session Session) string {
|
|||
if iterationCount == 0 {
|
||||
iterationCount = 1
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf(" %d ", iterationCount))
|
||||
fmt.Fprintf(buffer, " %d ", iterationCount)
|
||||
} else {
|
||||
buffer.WriteString(" infinite ")
|
||||
}
|
||||
|
@ -699,7 +700,7 @@ func (animation *animationData) animationCSS(session Session) string {
|
|||
func (animation *animationData) transitionCSS(buffer *strings.Builder, session Session) {
|
||||
|
||||
if duration, ok := floatProperty(animation, Duration, session, 1); ok && duration > 0 {
|
||||
buffer.WriteString(fmt.Sprintf(" %gs ", duration))
|
||||
fmt.Fprintf(buffer, " %gs ", duration)
|
||||
} else {
|
||||
buffer.WriteString(" 1s ")
|
||||
}
|
||||
|
@ -707,7 +708,7 @@ func (animation *animationData) transitionCSS(buffer *strings.Builder, session S
|
|||
buffer.WriteString(timingFunctionCSS(animation, TimingFunction, session))
|
||||
|
||||
if delay, ok := floatProperty(animation, Delay, session, 0); ok && delay > 0 {
|
||||
buffer.WriteString(fmt.Sprintf(" %gs", delay))
|
||||
fmt.Fprintf(buffer, " %gs", delay)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -979,11 +980,10 @@ func (style *viewStyle) Transition(tag PropertyName) AnimationProperty {
|
|||
}
|
||||
|
||||
func (style *viewStyle) Transitions() map[PropertyName]AnimationProperty {
|
||||
result := map[PropertyName]AnimationProperty{}
|
||||
for tag, animation := range getTransitionProperty(style) {
|
||||
result[tag] = animation
|
||||
if transitions := getTransitionProperty(style); transitions != nil {
|
||||
return maps.Clone(transitions)
|
||||
}
|
||||
return result
|
||||
return map[PropertyName]AnimationProperty{}
|
||||
}
|
||||
|
||||
func (style *viewStyle) SetTransition(tag PropertyName, animation AnimationProperty) {
|
||||
|
@ -1083,7 +1083,9 @@ func SetAnimated(rootView View, viewID string, tag PropertyName, value any, anim
|
|||
}
|
||||
|
||||
// IsAnimationPaused returns "true" if an animation of the subview is paused, "false" otherwise.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func IsAnimationPaused(view View, subviewID ...string) bool {
|
||||
return boolStyledProperty(view, subviewID, AnimationPaused, false)
|
||||
}
|
||||
|
|
|
@ -156,45 +156,6 @@ const (
|
|||
AnimationIterationEvent PropertyName = "animation-iteration-event"
|
||||
)
|
||||
|
||||
/*
|
||||
func setTransitionListener(properties Properties, tag PropertyName, value any) bool {
|
||||
if listeners, ok := valueToOneArgEventListeners[View, string](value); ok {
|
||||
if len(listeners) == 0 {
|
||||
properties.setRaw(tag, nil)
|
||||
} else {
|
||||
properties.setRaw(tag, listeners)
|
||||
}
|
||||
return true
|
||||
}
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
|
||||
func (view *viewData) removeTransitionListener(tag PropertyName) {
|
||||
delete(view.properties, tag)
|
||||
if view.created {
|
||||
if js, ok := eventJsFunc[tag]; ok {
|
||||
view.session.removeProperty(view.htmlID(), js.jsEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func transitionEventsHtml(view View, buffer *strings.Builder) {
|
||||
for _, tag := range []PropertyName{TransitionRunEvent, TransitionStartEvent, TransitionEndEvent, TransitionCancelEvent} {
|
||||
if value := view.getRaw(tag); value != nil {
|
||||
if js, ok := eventJsFunc[tag]; ok {
|
||||
if listeners, ok := value.([]func(View, string)); ok && len(listeners) > 0 {
|
||||
buffer.WriteString(js.jsEvent)
|
||||
buffer.WriteString(`="`)
|
||||
buffer.WriteString(js.jsFunc)
|
||||
buffer.WriteString(`(this, event)" `)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func (view *viewData) handleTransitionEvents(tag PropertyName, data DataObject) {
|
||||
if propertyName, ok := data.PropertyValue("property"); ok {
|
||||
property := PropertyName(propertyName)
|
||||
|
@ -208,50 +169,11 @@ func (view *viewData) handleTransitionEvents(tag PropertyName, data DataObject)
|
|||
}
|
||||
|
||||
for _, listener := range getOneArgEventListeners[View, PropertyName](view, nil, tag) {
|
||||
listener(view, property)
|
||||
listener.Run(view, property)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func setAnimationListener(properties Properties, tag PropertyName, value any) bool {
|
||||
if listeners, ok := valueToOneArgEventListeners[View, string](value); ok {
|
||||
if len(listeners) == 0 {
|
||||
properties.setRaw(tag, nil)
|
||||
} else {
|
||||
properties.setRaw(tag, listeners)
|
||||
}
|
||||
return true
|
||||
}
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
|
||||
func (view *viewData) removeAnimationListener(tag PropertyName) {
|
||||
delete(view.properties, tag)
|
||||
if view.created {
|
||||
if js, ok := eventJsFunc[tag]; ok {
|
||||
view.session.removeProperty(view.htmlID(), js.jsEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func animationEventsHtml(view View, buffer *strings.Builder) {
|
||||
for _, tag := range []PropertyName{AnimationStartEvent, AnimationEndEvent, AnimationIterationEvent, AnimationCancelEvent} {
|
||||
if value := view.getRaw(tag); value != nil {
|
||||
if js, ok := eventJsFunc[tag]; ok {
|
||||
if listeners, ok := value.([]func(View, string)); ok && len(listeners) > 0 {
|
||||
buffer.WriteString(js.jsEvent)
|
||||
buffer.WriteString(`="`)
|
||||
buffer.WriteString(js.jsFunc)
|
||||
buffer.WriteString(`(this, event)" `)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func (view *viewData) handleAnimationEvents(tag PropertyName, data DataObject) {
|
||||
if listeners := getOneArgEventListeners[View, string](view, nil, tag); len(listeners) > 0 {
|
||||
id := ""
|
||||
|
@ -263,63 +185,135 @@ func (view *viewData) handleAnimationEvents(tag PropertyName, data DataObject) {
|
|||
}
|
||||
}
|
||||
for _, listener := range listeners {
|
||||
listener(view, id)
|
||||
listener.Run(view, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetTransitionRunListeners returns the "transition-run-event" listener list.
|
||||
// If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetTransitionRunListeners(view View, subviewID ...string) []func(View, string) {
|
||||
return getOneArgEventListeners[View, string](view, subviewID, TransitionRunEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.View, string),
|
||||
// - func(rui.View),
|
||||
// - func(string),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTransitionRunListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, string](view, subviewID, TransitionRunEvent)
|
||||
}
|
||||
|
||||
// GetTransitionStartListeners returns the "transition-start-event" listener list.
|
||||
// If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetTransitionStartListeners(view View, subviewID ...string) []func(View, string) {
|
||||
return getOneArgEventListeners[View, string](view, subviewID, TransitionStartEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.View, string),
|
||||
// - func(rui.View),
|
||||
// - func(string),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTransitionStartListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, string](view, subviewID, TransitionStartEvent)
|
||||
}
|
||||
|
||||
// GetTransitionEndListeners returns the "transition-end-event" listener list.
|
||||
// If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetTransitionEndListeners(view View, subviewID ...string) []func(View, string) {
|
||||
return getOneArgEventListeners[View, string](view, subviewID, TransitionEndEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.View, string),
|
||||
// - func(rui.View),
|
||||
// - func(string),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTransitionEndListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, string](view, subviewID, TransitionEndEvent)
|
||||
}
|
||||
|
||||
// GetTransitionCancelListeners returns the "transition-cancel-event" listener list.
|
||||
// If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetTransitionCancelListeners(view View, subviewID ...string) []func(View, string) {
|
||||
return getOneArgEventListeners[View, string](view, subviewID, TransitionCancelEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.View, string),
|
||||
// - func(rui.View),
|
||||
// - func(string),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTransitionCancelListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, string](view, subviewID, TransitionCancelEvent)
|
||||
}
|
||||
|
||||
// GetAnimationStartListeners returns the "animation-start-event" listener list.
|
||||
// If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetAnimationStartListeners(view View, subviewID ...string) []func(View, string) {
|
||||
return getOneArgEventListeners[View, string](view, subviewID, AnimationStartEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.View, string),
|
||||
// - func(rui.View),
|
||||
// - func(string),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetAnimationStartListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, string](view, subviewID, AnimationStartEvent)
|
||||
}
|
||||
|
||||
// GetAnimationEndListeners returns the "animation-end-event" listener list.
|
||||
// If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetAnimationEndListeners(view View, subviewID ...string) []func(View, string) {
|
||||
return getOneArgEventListeners[View, string](view, subviewID, AnimationEndEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.View, string),
|
||||
// - func(rui.View),
|
||||
// - func(string),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetAnimationEndListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, string](view, subviewID, AnimationEndEvent)
|
||||
}
|
||||
|
||||
// GetAnimationCancelListeners returns the "animation-cancel-event" listener list.
|
||||
// If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetAnimationCancelListeners(view View, subviewID ...string) []func(View, string) {
|
||||
return getOneArgEventListeners[View, string](view, subviewID, AnimationCancelEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.View, string),
|
||||
// - func(rui.View),
|
||||
// - func(string),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetAnimationCancelListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, string](view, subviewID, AnimationCancelEvent)
|
||||
}
|
||||
|
||||
// GetAnimationIterationListeners returns the "animation-iteration-event" listener list.
|
||||
// If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetAnimationIterationListeners(view View, subviewID ...string) []func(View, string) {
|
||||
return getOneArgEventListeners[View, string](view, subviewID, AnimationIterationEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.View, string),
|
||||
// - func(rui.View),
|
||||
// - func(string),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetAnimationIterationListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, string](view, subviewID, AnimationIterationEvent)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package rui
|
||||
|
||||
import "slices"
|
||||
|
||||
func (animation *animationData) Start(view View, listener func(view View, animation AnimationProperty, event PropertyName)) bool {
|
||||
if view == nil {
|
||||
ErrorLog("nil View in animation.Start() function")
|
||||
|
@ -13,28 +15,22 @@ func (animation *animationData) Start(view View, listener func(view View, animat
|
|||
animation.listener = listener
|
||||
|
||||
animation.oldAnimation = nil
|
||||
|
||||
//if getOneArgEventListeners[View, PropertyName](view, nil, Animation)
|
||||
if value := view.Get(Animation); value != nil {
|
||||
if oldAnimation, ok := value.([]AnimationProperty); ok && len(oldAnimation) > 0 {
|
||||
animation.oldAnimation = oldAnimation
|
||||
}
|
||||
}
|
||||
|
||||
animation.oldListeners = map[PropertyName][]func(View, PropertyName){}
|
||||
animation.oldListeners = map[PropertyName][]oneArgListener[View, PropertyName]{}
|
||||
|
||||
setListeners := func(event PropertyName, listener func(View, PropertyName)) {
|
||||
var listeners []func(View, PropertyName) = nil
|
||||
if value := view.Get(event); value != nil {
|
||||
if oldListeners, ok := value.([]func(View, PropertyName)); ok && len(oldListeners) > 0 {
|
||||
listeners = oldListeners
|
||||
}
|
||||
}
|
||||
|
||||
if listeners == nil {
|
||||
view.Set(event, listener)
|
||||
} else {
|
||||
animation.oldListeners[event] = listeners
|
||||
view.Set(event, append(listeners, listener))
|
||||
listeners := getOneArgEventListeners[View, PropertyName](view, nil, event)
|
||||
if len(listeners) > 0 {
|
||||
animation.oldListeners[event] = slices.Clone(listeners)
|
||||
}
|
||||
view.Set(event, append(listeners, newOneArgListenerVE(listener)))
|
||||
}
|
||||
|
||||
setListeners(AnimationStartEvent, animation.onAnimationStart)
|
||||
|
@ -49,7 +45,7 @@ func (animation *animationData) Start(view View, listener func(view View, animat
|
|||
func (animation *animationData) finish() {
|
||||
if animation.view != nil {
|
||||
for _, event := range []PropertyName{AnimationStartEvent, AnimationEndEvent, AnimationCancelEvent, AnimationIterationEvent} {
|
||||
if listeners, ok := animation.oldListeners[event]; ok {
|
||||
if listeners, ok := animation.oldListeners[event]; ok && len(listeners) > 0 {
|
||||
animation.view.Set(event, listeners)
|
||||
} else {
|
||||
animation.view.Remove(event)
|
||||
|
@ -63,7 +59,7 @@ func (animation *animationData) finish() {
|
|||
animation.view.Set(Animation, "")
|
||||
}
|
||||
|
||||
animation.oldListeners = map[PropertyName][]func(View, PropertyName){}
|
||||
animation.oldListeners = map[PropertyName][]oneArgListener[View, PropertyName]{}
|
||||
|
||||
animation.view = nil
|
||||
animation.listener = nil
|
||||
|
|
201
appServer.go
201
appServer.go
|
@ -164,64 +164,68 @@ func (app *application) postHandler(w http.ResponseWriter, req *http.Request) {
|
|||
DebugLog(message)
|
||||
}
|
||||
|
||||
if obj := ParseDataText(message); obj != nil {
|
||||
var session Session = nil
|
||||
var response chan string = nil
|
||||
obj, err := ParseDataText(message)
|
||||
if err != nil {
|
||||
ErrorLog(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if cookie, err := req.Cookie("session"); err == nil {
|
||||
sessionID, err := strconv.Atoi(cookie.Value)
|
||||
if err != nil {
|
||||
ErrorLog(err.Error())
|
||||
} else if info, ok := app.sessions[sessionID]; ok && info.response != nil {
|
||||
response = info.response
|
||||
session = info.session
|
||||
}
|
||||
}
|
||||
|
||||
command := obj.Tag()
|
||||
startSession := false
|
||||
|
||||
if session == nil || command == "startSession" {
|
||||
events := make(chan DataObject, 1024)
|
||||
bridge := createHttpBridge(req)
|
||||
response = bridge.response
|
||||
answer := ""
|
||||
session, answer = app.startSession(obj, events, bridge, response)
|
||||
|
||||
bridge.writeMessage(answer)
|
||||
session.onStart()
|
||||
if command == "session-resume" {
|
||||
session.onResume()
|
||||
}
|
||||
bridge.sendResponse()
|
||||
|
||||
setSessionIDCookie(w, session.ID())
|
||||
startSession = true
|
||||
|
||||
go sessionEventHandler(session, events, bridge)
|
||||
}
|
||||
|
||||
if !startSession {
|
||||
switch command {
|
||||
case "nop":
|
||||
session.sendResponse()
|
||||
|
||||
case "session-close":
|
||||
session.onFinish()
|
||||
session.App().removeSession(session.ID())
|
||||
return
|
||||
|
||||
default:
|
||||
if !session.handleAnswer(command, obj) {
|
||||
session.addToEventsQueue(obj)
|
||||
}
|
||||
var session Session = nil
|
||||
var response chan string = nil
|
||||
|
||||
if cookie, err := req.Cookie("session"); err == nil {
|
||||
sessionID, err := strconv.Atoi(cookie.Value)
|
||||
if err != nil {
|
||||
ErrorLog(err.Error())
|
||||
} else if info, ok := app.sessions[sessionID]; ok && info.response != nil {
|
||||
response = info.response
|
||||
session = info.session
|
||||
}
|
||||
}
|
||||
|
||||
command := obj.Tag()
|
||||
startSession := false
|
||||
|
||||
if session == nil || command == "startSession" {
|
||||
events := make(chan DataObject, 1024)
|
||||
bridge := createHttpBridge(req)
|
||||
response = bridge.response
|
||||
answer := ""
|
||||
session, answer = app.startSession(obj, events, bridge, response)
|
||||
|
||||
bridge.writeMessage(answer)
|
||||
session.onStart()
|
||||
if command == "session-resume" {
|
||||
session.onResume()
|
||||
}
|
||||
bridge.sendResponse()
|
||||
|
||||
setSessionIDCookie(w, session.ID())
|
||||
startSession = true
|
||||
|
||||
go sessionEventHandler(session, events, bridge)
|
||||
}
|
||||
|
||||
if !startSession {
|
||||
switch command {
|
||||
case "nop":
|
||||
session.sendResponse()
|
||||
|
||||
case "session-close":
|
||||
session.onFinish()
|
||||
session.App().removeSession(session.ID())
|
||||
return
|
||||
|
||||
default:
|
||||
if !session.handleAnswer(command, obj) {
|
||||
session.addToEventsQueue(obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
io.WriteString(w, <-response)
|
||||
for len(response) > 0 {
|
||||
io.WriteString(w, <-response)
|
||||
for len(response) > 0 {
|
||||
io.WriteString(w, <-response)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -241,58 +245,61 @@ func (app *application) socketReader(bridge *wsBridge) {
|
|||
DebugLog("🖥️ -> " + message)
|
||||
}
|
||||
|
||||
if obj := ParseDataText(message); obj != nil {
|
||||
command := obj.Tag()
|
||||
switch command {
|
||||
case "startSession":
|
||||
answer := ""
|
||||
obj, err := ParseDataText(message)
|
||||
if err != nil {
|
||||
ErrorLog(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
switch command := obj.Tag(); command {
|
||||
case "startSession":
|
||||
answer := ""
|
||||
if session, answer = app.startSession(obj, events, bridge, nil); session != nil {
|
||||
if !bridge.writeMessage(answer) {
|
||||
return
|
||||
}
|
||||
session.onStart()
|
||||
go sessionEventHandler(session, events, bridge)
|
||||
}
|
||||
|
||||
case "reconnect":
|
||||
session = nil
|
||||
if sessionText, ok := obj.PropertyValue("session"); ok {
|
||||
if sessionID, err := strconv.Atoi(sessionText); err == nil {
|
||||
if info, ok := app.sessions[sessionID]; ok {
|
||||
session = info.session
|
||||
session.setBridge(events, bridge)
|
||||
|
||||
go sessionEventHandler(session, events, bridge)
|
||||
session.onReconnect()
|
||||
} else {
|
||||
DebugLogF("Session #%d not exists", sessionID)
|
||||
}
|
||||
} else {
|
||||
ErrorLog(`strconv.Atoi(sessionText) error: ` + err.Error())
|
||||
}
|
||||
} else {
|
||||
ErrorLog(`"session" key not found`)
|
||||
}
|
||||
|
||||
if session == nil {
|
||||
/* answer := ""
|
||||
if session, answer = app.startSession(obj, events, bridge, nil); session != nil {
|
||||
if !bridge.writeMessage(answer) {
|
||||
return
|
||||
}
|
||||
session.onStart()
|
||||
go sessionEventHandler(session, events, bridge)
|
||||
bridge.writeMessage("restartSession();")
|
||||
}
|
||||
*/
|
||||
bridge.writeMessage("reloadPage();")
|
||||
return
|
||||
}
|
||||
|
||||
case "reconnect":
|
||||
session = nil
|
||||
if sessionText, ok := obj.PropertyValue("session"); ok {
|
||||
if sessionID, err := strconv.Atoi(sessionText); err == nil {
|
||||
if info, ok := app.sessions[sessionID]; ok {
|
||||
session = info.session
|
||||
session.setBridge(events, bridge)
|
||||
|
||||
go sessionEventHandler(session, events, bridge)
|
||||
session.onReconnect()
|
||||
} else {
|
||||
DebugLogF("Session #%d not exists", sessionID)
|
||||
}
|
||||
} else {
|
||||
ErrorLog(`strconv.Atoi(sessionText) error: ` + err.Error())
|
||||
}
|
||||
} else {
|
||||
ErrorLog(`"session" key not found`)
|
||||
}
|
||||
|
||||
if session == nil {
|
||||
/* answer := ""
|
||||
if session, answer = app.startSession(obj, events, bridge, nil); session != nil {
|
||||
if !bridge.writeMessage(answer) {
|
||||
return
|
||||
}
|
||||
session.onStart()
|
||||
go sessionEventHandler(session, events, bridge)
|
||||
bridge.writeMessage("restartSession();")
|
||||
}
|
||||
*/
|
||||
bridge.writeMessage("reloadPage();")
|
||||
return
|
||||
}
|
||||
|
||||
default:
|
||||
if !session.handleAnswer(command, obj) {
|
||||
events <- obj
|
||||
}
|
||||
default:
|
||||
if !session.handleAnswer(command, obj) {
|
||||
events <- obj
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,6 +78,16 @@ func createBackground(obj DataObject) BackgroundElement {
|
|||
return result
|
||||
}
|
||||
|
||||
func parseBackgroundText(text string) BackgroundElement {
|
||||
obj, err := ParseDataText(text)
|
||||
if err != nil {
|
||||
ErrorLog(err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
return createBackground(obj)
|
||||
}
|
||||
|
||||
func parseBackgroundValue(value any) []BackgroundElement {
|
||||
|
||||
switch value := value.(type) {
|
||||
|
@ -96,12 +106,8 @@ func parseBackgroundValue(value any) []BackgroundElement {
|
|||
} else {
|
||||
return nil
|
||||
}
|
||||
} else if obj := ParseDataText(el.Value()); obj != nil {
|
||||
if element := createBackground(obj); element != nil {
|
||||
background = append(background, element)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
} else if element := parseBackgroundText(el.Value()); element != nil {
|
||||
background = append(background, element)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
@ -125,21 +131,15 @@ func parseBackgroundValue(value any) []BackgroundElement {
|
|||
return background
|
||||
|
||||
case string:
|
||||
if obj := ParseDataText(value); obj != nil {
|
||||
if element := createBackground(obj); element != nil {
|
||||
return []BackgroundElement{element}
|
||||
}
|
||||
if element := parseBackgroundText(value); element != nil {
|
||||
return []BackgroundElement{element}
|
||||
}
|
||||
|
||||
case []string:
|
||||
elements := make([]BackgroundElement, 0, len(value))
|
||||
for _, element := range value {
|
||||
if obj := ParseDataText(element); obj != nil {
|
||||
if element := createBackground(obj); element != nil {
|
||||
elements = append(elements, element)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
for _, text := range value {
|
||||
if element := parseBackgroundText(text); element != nil {
|
||||
elements = append(elements, element)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
@ -148,18 +148,14 @@ func parseBackgroundValue(value any) []BackgroundElement {
|
|||
|
||||
case []any:
|
||||
elements := make([]BackgroundElement, 0, len(value))
|
||||
for _, element := range value {
|
||||
switch element := element.(type) {
|
||||
for _, val := range value {
|
||||
switch val := val.(type) {
|
||||
case BackgroundElement:
|
||||
elements = append(elements, element)
|
||||
elements = append(elements, val)
|
||||
|
||||
case string:
|
||||
if obj := ParseDataText(element); obj != nil {
|
||||
if element := createBackground(obj); element != nil {
|
||||
elements = append(elements, element)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
if element := parseBackgroundText(val); element != nil {
|
||||
elements = append(elements, element)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
@ -268,14 +264,16 @@ func backgroundStyledPropery(view View, subviewID []string, tag PropertyName) []
|
|||
|
||||
// GetBackground returns the view background.
|
||||
//
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetBackground(view View, subviewID ...string) []BackgroundElement {
|
||||
return backgroundStyledPropery(view, subviewID, Background)
|
||||
}
|
||||
|
||||
// GetMask returns the view mask.
|
||||
//
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetMask(view View, subviewID ...string) []BackgroundElement {
|
||||
return backgroundStyledPropery(view, subviewID, Mask)
|
||||
}
|
||||
|
@ -284,7 +282,8 @@ func GetMask(view View, subviewID ...string) []BackgroundElement {
|
|||
//
|
||||
// BorderBox (0), PaddingBox (1), ContentBox (2)
|
||||
//
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetBackgroundClip(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, BackgroundClip, 0, false)
|
||||
}
|
||||
|
@ -293,7 +292,8 @@ func GetBackgroundClip(view View, subviewID ...string) int {
|
|||
//
|
||||
// BorderBox (0), PaddingBox (1), ContentBox (2)
|
||||
//
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetBackgroundOrigin(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, BackgroundOrigin, 0, false)
|
||||
}
|
||||
|
@ -302,7 +302,8 @@ func GetBackgroundOrigin(view View, subviewID ...string) int {
|
|||
//
|
||||
// BorderBox (0), PaddingBox (1), ContentBox (2)
|
||||
//
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetMaskClip(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, MaskClip, 0, false)
|
||||
}
|
||||
|
@ -311,7 +312,8 @@ func GetMaskClip(view View, subviewID ...string) int {
|
|||
//
|
||||
// BorderBox (0), PaddingBox (1), ContentBox (2)
|
||||
//
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetMaskOrigin(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, MaskOrigin, 0, false)
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ type backgroundRadialGradient struct {
|
|||
//
|
||||
// The following properties can be used:
|
||||
// - "gradient" (Gradient) - Describes gradient stop points. This is a mandatory property while describing background gradients.
|
||||
// - "center-x" (CenterX), "center-y" (CenterY) - Defines the gradient center point cooordinates.
|
||||
// - "center-x" (CenterX), "center-y" (CenterY) - Defines the gradient center point coordinates.
|
||||
// - "radial-gradient-radius" (RadialGradientRadius) - Defines radius of the radial gradient.
|
||||
// - "radial-gradient-shape" (RadialGradientShape) - Defines shape of the radial gradient.
|
||||
// - "repeating" (Repeating) - Defines whether stop points needs to be repeated after the last one.
|
||||
|
|
|
@ -610,7 +610,10 @@ func borderSet(properties Properties, tag PropertyName, value any) []PropertyNam
|
|||
case Left, Right, Top, Bottom:
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
if obj := ParseDataText(value); obj != nil {
|
||||
obj, err := ParseDataText(value)
|
||||
if err != nil {
|
||||
ErrorLog(err.Error())
|
||||
} else {
|
||||
return setSingleBorderObject(tag, obj)
|
||||
}
|
||||
|
||||
|
|
|
@ -201,7 +201,7 @@ type Canvas interface {
|
|||
|
||||
// SetConicGradientFillStyle sets a conic gradient around a point
|
||||
// to use inside shapes
|
||||
// * x, y - coordinates of the center of the conic gradient in pilels;
|
||||
// * x, y - coordinates of the center of the conic gradient in piles;
|
||||
// * startAngle - the angle at which to begin the gradient, in radians. The angle starts from a line going horizontally right from the center, and proceeds clockwise.
|
||||
// * startColor - the start color;
|
||||
// * endColor - the end color;
|
||||
|
@ -210,7 +210,7 @@ type Canvas interface {
|
|||
|
||||
// SetConicGradientFillStyle sets a conic gradient around a point
|
||||
// to use inside shapes
|
||||
// * x, y - coordinates of the center of the conic gradient in pilels;
|
||||
// * x, y - coordinates of the center of the conic gradient in piles;
|
||||
// * startAngle - the angle at which to begin the gradient, in radians. The angle starts from a line going horizontally right from the center, and proceeds clockwise.
|
||||
// * startColor - the start color;
|
||||
// * endColor - the end color;
|
||||
|
@ -575,7 +575,7 @@ func (canvas *canvasData) SetLineDash(dash []float64, offset float64) {
|
|||
for _, val := range dash {
|
||||
buffer.WriteRune(lead)
|
||||
lead = ','
|
||||
buffer.WriteString(fmt.Sprintf("%g", val))
|
||||
fmt.Fprintf(buffer, "%g", val))
|
||||
}
|
||||
buffer.WriteRune(']')
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package rui
|
||||
|
||||
import "reflect"
|
||||
|
||||
// DrawFunction is the constant for "draw-function" property tag.
|
||||
//
|
||||
// Used by `CanvasView`.
|
||||
|
@ -55,7 +57,7 @@ func (canvasView *canvasViewData) removeFunc(tag PropertyName) []PropertyName {
|
|||
if tag == DrawFunction {
|
||||
if canvasView.getRaw(DrawFunction) != nil {
|
||||
canvasView.setRaw(DrawFunction, nil)
|
||||
canvasView.Redraw()
|
||||
//canvasView.Redraw()
|
||||
return []PropertyName{DrawFunction}
|
||||
}
|
||||
return []PropertyName{}
|
||||
|
@ -66,9 +68,14 @@ func (canvasView *canvasViewData) removeFunc(tag PropertyName) []PropertyName {
|
|||
|
||||
func (canvasView *canvasViewData) setFunc(tag PropertyName, value any) []PropertyName {
|
||||
if tag == DrawFunction {
|
||||
if fn, ok := value.(func(Canvas)); ok {
|
||||
canvasView.setRaw(DrawFunction, fn)
|
||||
} else {
|
||||
switch value := value.(type) {
|
||||
case func(Canvas):
|
||||
canvasView.setRaw(DrawFunction, value)
|
||||
|
||||
case string:
|
||||
canvasView.setRaw(DrawFunction, value)
|
||||
|
||||
default:
|
||||
notCompatibleType(tag, value)
|
||||
return nil
|
||||
}
|
||||
|
@ -94,8 +101,30 @@ func (canvasView *canvasViewData) Redraw() {
|
|||
canvas := newCanvas(canvasView)
|
||||
canvas.ClearRect(0, 0, canvasView.frame.Width, canvasView.frame.Height)
|
||||
if value := canvasView.getRaw(DrawFunction); value != nil {
|
||||
if drawer, ok := value.(func(Canvas)); ok {
|
||||
switch drawer := value.(type) {
|
||||
case func(Canvas):
|
||||
drawer(canvas)
|
||||
|
||||
case string:
|
||||
bind := canvasView.binding()
|
||||
if bind == nil {
|
||||
ErrorLogF(`There is no a binding object for call "%s"`, drawer)
|
||||
break
|
||||
}
|
||||
|
||||
val := reflect.ValueOf(bind)
|
||||
method := val.MethodByName(drawer)
|
||||
if !method.IsValid() {
|
||||
ErrorLogF(`The "%s" method is not valid`, drawer)
|
||||
break
|
||||
}
|
||||
|
||||
methodType := method.Type()
|
||||
if methodType.NumIn() == 1 && equalType(methodType.In(0), reflect.TypeOf(canvas)) {
|
||||
method.Call([]reflect.Value{reflect.ValueOf(canvas)})
|
||||
} else {
|
||||
ErrorLogF(`Unsupported prototype of "%s" method`, drawer)
|
||||
}
|
||||
}
|
||||
}
|
||||
canvas.finishDraw()
|
||||
|
|
59
checkbox.go
59
checkbox.go
|
@ -53,8 +53,8 @@ func (button *checkboxData) init(session Session) {
|
|||
button.remove = button.removeFunc
|
||||
button.changed = button.propertyChanged
|
||||
|
||||
button.setRaw(ClickEvent, []func(View, MouseEvent){checkboxClickListener})
|
||||
button.setRaw(KeyDownEvent, []func(View, KeyEvent){checkboxKeyListener})
|
||||
button.setRaw(ClickEvent, []oneArgListener[View, MouseEvent]{newOneArgListenerVE(checkboxClickListener)})
|
||||
button.setRaw(KeyDownEvent, []oneArgListener[View, KeyEvent]{newOneArgListenerVE(checkboxKeyListener)})
|
||||
}
|
||||
|
||||
func (button *checkboxData) Focusable() bool {
|
||||
|
@ -67,9 +67,9 @@ func (button *checkboxData) propertyChanged(tag PropertyName) {
|
|||
case Checked:
|
||||
session := button.Session()
|
||||
checked := IsCheckboxChecked(button)
|
||||
if listeners := GetCheckboxChangedListeners(button); len(listeners) > 0 {
|
||||
if listeners := getOneArgEventListeners[Checkbox, bool](button, nil, CheckboxChangedEvent); len(listeners) > 0 {
|
||||
for _, listener := range listeners {
|
||||
listener(button, checked)
|
||||
listener.Run(button, checked)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,7 +103,7 @@ func (button *checkboxData) setFunc(tag PropertyName, value any) []PropertyName
|
|||
switch tag {
|
||||
case ClickEvent:
|
||||
if listeners, ok := valueToOneArgEventListeners[View, MouseEvent](value); ok && listeners != nil {
|
||||
listeners = append(listeners, checkboxClickListener)
|
||||
listeners = append(listeners, newOneArgListenerVE(checkboxClickListener))
|
||||
button.setRaw(tag, listeners)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ func (button *checkboxData) setFunc(tag PropertyName, value any) []PropertyName
|
|||
|
||||
case KeyDownEvent:
|
||||
if listeners, ok := valueToOneArgEventListeners[View, KeyEvent](value); ok && listeners != nil {
|
||||
listeners = append(listeners, checkboxKeyListener)
|
||||
listeners = append(listeners, newOneArgListenerVE(checkboxKeyListener))
|
||||
button.setRaw(tag, listeners)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
@ -134,31 +134,17 @@ func (button *checkboxData) setFunc(tag PropertyName, value any) []PropertyName
|
|||
func (button *checkboxData) removeFunc(tag PropertyName) []PropertyName {
|
||||
switch tag {
|
||||
case ClickEvent:
|
||||
button.setRaw(ClickEvent, []func(View, MouseEvent){checkboxClickListener})
|
||||
button.setRaw(ClickEvent, []oneArgListener[View, MouseEvent]{newOneArgListenerVE(checkboxClickListener)})
|
||||
return []PropertyName{ClickEvent}
|
||||
|
||||
case KeyDownEvent:
|
||||
button.setRaw(KeyDownEvent, []func(View, KeyEvent){checkboxKeyListener})
|
||||
button.setRaw(KeyDownEvent, []oneArgListener[View, KeyEvent]{newOneArgListenerVE(checkboxKeyListener)})
|
||||
return []PropertyName{ClickEvent}
|
||||
}
|
||||
|
||||
return button.viewsContainerData.removeFunc(tag)
|
||||
}
|
||||
|
||||
/*
|
||||
func (button *checkboxData) changedCheckboxState(state bool) {
|
||||
for _, listener := range GetCheckboxChangedListeners(button) {
|
||||
listener(button, state)
|
||||
}
|
||||
|
||||
buffer := allocStringBuilder()
|
||||
defer freeStringBuilder(buffer)
|
||||
|
||||
button.htmlCheckbox(buffer, state)
|
||||
button.Session().updateInnerHTML(button.htmlID()+"checkbox", buffer.String())
|
||||
}
|
||||
*/
|
||||
|
||||
func checkboxClickListener(view View, _ MouseEvent) {
|
||||
view.Set(Checked, !IsCheckboxChecked(view))
|
||||
BlurView(view)
|
||||
|
@ -302,26 +288,41 @@ func checkboxVerticalAlignCSS(view View) string {
|
|||
}
|
||||
|
||||
// IsCheckboxChecked returns true if the Checkbox is checked, false otherwise.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func IsCheckboxChecked(view View, subviewID ...string) bool {
|
||||
return boolStyledProperty(view, subviewID, Checked, false)
|
||||
}
|
||||
|
||||
// GetCheckboxVerticalAlign return the vertical align of a Checkbox subview: TopAlign (0), BottomAlign (1), CenterAlign (2)
|
||||
// If the second argument (subviewID) is not specified or it is "" then a left position of the first argument (view) is returned
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetCheckboxVerticalAlign(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, CheckboxVerticalAlign, LeftAlign, false)
|
||||
}
|
||||
|
||||
// GetCheckboxHorizontalAlign return the vertical align of a Checkbox subview: LeftAlign (0), RightAlign (1), CenterAlign (2)
|
||||
// If the second argument (subviewID) is not specified or it is "" then a left position of the first argument (view) is returned
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetCheckboxHorizontalAlign(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, CheckboxHorizontalAlign, TopAlign, false)
|
||||
}
|
||||
|
||||
// GetCheckboxChangedListeners returns the CheckboxChangedListener list of an Checkbox subview.
|
||||
// If there are no listeners then the empty list is returned
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetCheckboxChangedListeners(view View, subviewID ...string) []func(Checkbox, bool) {
|
||||
return getOneArgEventListeners[Checkbox, bool](view, subviewID, CheckboxChangedEvent)
|
||||
// If there are no listeners then the empty list is returned.
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(Checkbox, bool),
|
||||
// - func(Checkbox),
|
||||
// - func(bool),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetCheckboxChangedListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[Checkbox, bool](view, subviewID, CheckboxChangedEvent)
|
||||
}
|
||||
|
|
|
@ -106,13 +106,13 @@ func (picker *colorPickerData) propertyChanged(tag PropertyName) {
|
|||
color := GetColorPickerValue(picker)
|
||||
picker.Session().callFunc("setInputValue", picker.htmlID(), color.rgbString())
|
||||
|
||||
if listeners := GetColorChangedListeners(picker); len(listeners) > 0 {
|
||||
if listeners := getTwoArgEventListeners[ColorPicker, Color](picker, nil, ColorChangedEvent); len(listeners) > 0 {
|
||||
oldColor := Color(0)
|
||||
if value := picker.getRaw("old-color"); value != nil {
|
||||
oldColor = value.(Color)
|
||||
}
|
||||
for _, listener := range listeners {
|
||||
listener(picker, color, oldColor)
|
||||
listener.Run(picker, color, oldColor)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,11 +156,11 @@ func (picker *colorPickerData) handleCommand(self View, command PropertyName, da
|
|||
oldColor := GetColorPickerValue(picker)
|
||||
picker.properties[ColorPickerValue] = color
|
||||
if color != oldColor {
|
||||
for _, listener := range GetColorChangedListeners(picker) {
|
||||
listener(picker, color, oldColor)
|
||||
for _, listener := range getTwoArgEventListeners[ColorPicker, Color](picker, nil, ColorChangedEvent) {
|
||||
listener.Run(picker, color, oldColor)
|
||||
}
|
||||
if listener, ok := picker.changeListener[ColorPickerValue]; ok {
|
||||
listener(picker, ColorPickerValue)
|
||||
listener.Run(picker, ColorPickerValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -172,7 +172,9 @@ func (picker *colorPickerData) handleCommand(self View, command PropertyName, da
|
|||
}
|
||||
|
||||
// GetColorPickerValue returns the value of ColorPicker subview.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetColorPickerValue(view View, subviewID ...string) Color {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
if value, ok := colorProperty(view, ColorPickerValue, view.Session()); ok {
|
||||
|
@ -191,7 +193,18 @@ func GetColorPickerValue(view View, subviewID ...string) Color {
|
|||
|
||||
// GetColorChangedListeners returns the ColorChangedListener list of an ColorPicker subview.
|
||||
// If there are no listeners then the empty list is returned
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetColorChangedListeners(view View, subviewID ...string) []func(ColorPicker, Color, Color) {
|
||||
return getTwoArgEventListeners[ColorPicker, Color](view, subviewID, ColorChangedEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.ColorPicker, rui.Color, rui.Color),
|
||||
// - func(rui.ColorPicker, rui.Color),
|
||||
// - func(rui.ColorPicker),
|
||||
// - func(rui.Color, rui.Color),
|
||||
// - func(rui.Color),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetColorChangedListeners(view View, subviewID ...string) []any {
|
||||
return getTwoArgEventRawListeners[ColorPicker, Color](view, subviewID, ColorChangedEvent)
|
||||
}
|
||||
|
|
|
@ -249,13 +249,17 @@ func GetColumnSeparatorColor(view View, subviewID ...string) Color {
|
|||
|
||||
// GetColumnFill returns a "column-fill" property value of the subview.
|
||||
// Returns one of next values: ColumnFillBalance (0) or ColumnFillAuto (1)
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetColumnFill(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, ColumnFill, ColumnFillBalance, true)
|
||||
}
|
||||
|
||||
// IsColumnSpanAll returns a "column-span-all" property value of the subview.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func IsColumnSpanAll(view View, subviewID ...string) bool {
|
||||
return boolStyledProperty(view, subviewID, ColumnSpanAll, false)
|
||||
}
|
||||
|
|
|
@ -93,12 +93,12 @@ func NewColumnSeparator(style int, color Color, width SizeUnit) ColumnSeparatorP
|
|||
|
||||
func (separator *columnSeparatorProperty) init() {
|
||||
separator.dataProperty.init()
|
||||
separator.normalize = normalizeVolumnSeparatorTag
|
||||
separator.normalize = normalizeColumnSeparatorTag
|
||||
separator.set = columnSeparatorSet
|
||||
separator.supportedProperties = []PropertyName{Style, Width, ColorTag}
|
||||
}
|
||||
|
||||
func normalizeVolumnSeparatorTag(tag PropertyName) PropertyName {
|
||||
func normalizeColumnSeparatorTag(tag PropertyName) PropertyName {
|
||||
tag = defaultNormalize(tag)
|
||||
switch tag {
|
||||
case ColumnSeparatorStyle, "separator-style":
|
||||
|
|
|
@ -98,8 +98,8 @@ func (customView *CustomViewData) SetParams(params Params) bool {
|
|||
}
|
||||
|
||||
// SetChangeListener set the function to track the change of the View property
|
||||
func (customView *CustomViewData) SetChangeListener(tag PropertyName, listener func(View, PropertyName)) {
|
||||
customView.superView.SetChangeListener(tag, listener)
|
||||
func (customView *CustomViewData) SetChangeListener(tag PropertyName, listener any) bool {
|
||||
return customView.superView.SetChangeListener(tag, listener)
|
||||
}
|
||||
|
||||
// Remove removes the property with name defined by the argument
|
||||
|
@ -292,15 +292,15 @@ func (customView *CustomViewData) ViewIndex(view View) int {
|
|||
return -1
|
||||
}
|
||||
|
||||
func (customView *CustomViewData) exscludeTags() []PropertyName {
|
||||
func (customView *CustomViewData) excludeTags() []PropertyName {
|
||||
if customView.superView != nil {
|
||||
exsclude := []PropertyName{}
|
||||
exclude := []PropertyName{}
|
||||
for tag, value := range customView.defaultParams {
|
||||
if value == customView.superView.getRaw(tag) {
|
||||
exsclude = append(exsclude, tag)
|
||||
exclude = append(exclude, tag)
|
||||
}
|
||||
}
|
||||
return exsclude
|
||||
return exclude
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -310,7 +310,7 @@ func (customView *CustomViewData) String() string {
|
|||
if customView.superView != nil {
|
||||
buffer := allocStringBuilder()
|
||||
defer freeStringBuilder(buffer)
|
||||
writeViewStyle(customView.tag, customView, buffer, "", customView.exscludeTags())
|
||||
writeViewStyle(customView.tag, customView, buffer, "", customView.excludeTags())
|
||||
return buffer.String()
|
||||
}
|
||||
return customView.tag + " { }"
|
||||
|
@ -352,3 +352,10 @@ func (customView *CustomViewData) LoadFile(file FileInfo, result func(FileInfo,
|
|||
customView.superView.LoadFile(file, result)
|
||||
}
|
||||
}
|
||||
|
||||
func (customView *CustomViewData) binding() any {
|
||||
if customView.superView != nil {
|
||||
return customView.superView.binding()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
774
data.go
774
data.go
|
@ -1,6 +1,9 @@
|
|||
package rui
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
@ -47,16 +50,22 @@ type DataObject interface {
|
|||
|
||||
// ToParams create a params(map) representation of a data object
|
||||
ToParams() Params
|
||||
|
||||
// PropertyByTag removes a data node corresponding to a property tag and returns it
|
||||
RemovePropertyByTag(tag string) DataNode
|
||||
}
|
||||
|
||||
// DataNodeType defines the type of DataNode
|
||||
type DataNodeType int
|
||||
|
||||
// Constants which are used to describe a node type, see [DataNode]
|
||||
const (
|
||||
// TextNode - node is the pair "tag - text value". Syntax: <tag> = <text>
|
||||
TextNode = 0
|
||||
TextNode DataNodeType = 0
|
||||
// ObjectNode - node is the pair "tag - object". Syntax: <tag> = <object name>{...}
|
||||
ObjectNode = 1
|
||||
ObjectNode DataNodeType = 1
|
||||
// ArrayNode - node is the pair "tag - object". Syntax: <tag> = [...]
|
||||
ArrayNode = 2
|
||||
ArrayNode DataNodeType = 2
|
||||
)
|
||||
|
||||
// DataNode interface of a data node
|
||||
|
@ -65,7 +74,7 @@ type DataNode interface {
|
|||
Tag() string
|
||||
|
||||
// Type returns a node type. Possible values are TextNode, ObjectNode and ArrayNode
|
||||
Type() int
|
||||
Type() DataNodeType
|
||||
|
||||
// Text returns node text
|
||||
Text() string
|
||||
|
@ -158,6 +167,28 @@ func (object *dataObject) PropertyByTag(tag string) DataNode {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (object *dataObject) RemovePropertyByTag(tag string) DataNode {
|
||||
if object.property != nil {
|
||||
for i, node := range object.property {
|
||||
if node.Tag() == tag {
|
||||
switch i {
|
||||
case 0:
|
||||
object.property = object.property[1:]
|
||||
|
||||
case len(object.property) - 1:
|
||||
object.property = object.property[:len(object.property)-1]
|
||||
|
||||
default:
|
||||
object.property = append(object.property[:i], object.property[i+1:]...)
|
||||
}
|
||||
|
||||
return node
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (object *dataObject) PropertyValue(tag string) (string, bool) {
|
||||
if node := object.PropertyByTag(tag); node != nil && node.Type() == TextNode {
|
||||
return node.Text(), true
|
||||
|
@ -253,7 +284,7 @@ func (node *dataNode) Tag() string {
|
|||
return node.tag
|
||||
}
|
||||
|
||||
func (node *dataNode) Type() int {
|
||||
func (node *dataNode) Type() DataNodeType {
|
||||
if node.array != nil {
|
||||
return ArrayNode
|
||||
}
|
||||
|
@ -314,411 +345,402 @@ func (node *dataNode) ArrayAsParams() []Params {
|
|||
return result
|
||||
}
|
||||
|
||||
// ParseDataText - parse text and return DataNode
|
||||
func ParseDataText(text string) DataObject {
|
||||
type dataParser struct {
|
||||
data []rune
|
||||
size int
|
||||
pos int
|
||||
line int
|
||||
lineStart int
|
||||
}
|
||||
|
||||
if strings.ContainsAny(text, "\r") {
|
||||
text = strings.Replace(text, "\r\n", "\n", -1)
|
||||
text = strings.Replace(text, "\r", "\n", -1)
|
||||
}
|
||||
data := append([]rune(text), rune(0))
|
||||
pos := 0
|
||||
size := len(data) - 1
|
||||
line := 1
|
||||
lineStart := 0
|
||||
func (parser *dataParser) skipSpaces(skipNewLine bool) {
|
||||
for parser.pos < parser.size {
|
||||
switch parser.data[parser.pos] {
|
||||
case '\n':
|
||||
if !skipNewLine {
|
||||
return
|
||||
}
|
||||
parser.line++
|
||||
parser.lineStart = parser.pos + 1
|
||||
|
||||
skipSpaces := func(skipNewLine bool) {
|
||||
for pos < size {
|
||||
switch data[pos] {
|
||||
case '\n':
|
||||
if !skipNewLine {
|
||||
return
|
||||
}
|
||||
line++
|
||||
lineStart = pos + 1
|
||||
|
||||
case '/':
|
||||
if pos+1 < size {
|
||||
switch data[pos+1] {
|
||||
case '/':
|
||||
pos += 2
|
||||
for pos < size && data[pos] != '\n' {
|
||||
pos++
|
||||
}
|
||||
pos--
|
||||
|
||||
case '*':
|
||||
pos += 3
|
||||
for {
|
||||
if pos >= size {
|
||||
ErrorLog("Unexpected end of file")
|
||||
return
|
||||
}
|
||||
if data[pos-1] == '*' && data[pos] == '/' {
|
||||
break
|
||||
}
|
||||
if data[pos-1] == '\n' {
|
||||
line++
|
||||
lineStart = pos
|
||||
}
|
||||
pos++
|
||||
}
|
||||
|
||||
default:
|
||||
return
|
||||
case '/':
|
||||
if parser.pos+1 < parser.size {
|
||||
switch parser.data[parser.pos+1] {
|
||||
case '/':
|
||||
parser.pos += 2
|
||||
for parser.pos < parser.size && parser.data[parser.pos] != '\n' {
|
||||
parser.pos++
|
||||
}
|
||||
}
|
||||
parser.pos--
|
||||
|
||||
case ' ', '\t':
|
||||
// do nothing
|
||||
case '*':
|
||||
parser.pos += 3
|
||||
for {
|
||||
if parser.pos >= parser.size {
|
||||
ErrorLog("Unexpected end of file")
|
||||
return
|
||||
}
|
||||
if parser.data[parser.pos-1] == '*' && parser.data[parser.pos] == '/' {
|
||||
break
|
||||
}
|
||||
if parser.data[parser.pos-1] == '\n' {
|
||||
parser.line++
|
||||
parser.lineStart = parser.pos
|
||||
}
|
||||
parser.pos++
|
||||
}
|
||||
|
||||
default:
|
||||
if !unicode.IsSpace(data[pos]) {
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
pos++
|
||||
}
|
||||
}
|
||||
|
||||
parseTag := func() (string, bool) {
|
||||
skipSpaces(true)
|
||||
startPos := pos
|
||||
if data[pos] == '`' {
|
||||
pos++
|
||||
startPos++
|
||||
for data[pos] != '`' {
|
||||
pos++
|
||||
if pos >= size {
|
||||
ErrorLog("Unexpected end of text")
|
||||
return string(data[startPos:size]), false
|
||||
}
|
||||
}
|
||||
str := string(data[startPos:pos])
|
||||
pos++
|
||||
return str, true
|
||||
|
||||
} else if data[pos] == '\'' || data[pos] == '"' {
|
||||
|
||||
stopSymbol := data[pos]
|
||||
pos++
|
||||
startPos++
|
||||
slash := false
|
||||
for stopSymbol != data[pos] {
|
||||
if data[pos] == '\\' {
|
||||
pos += 2
|
||||
slash = true
|
||||
} else {
|
||||
pos++
|
||||
}
|
||||
if pos >= size {
|
||||
ErrorLog("Unexpected end of text")
|
||||
return string(data[startPos:size]), false
|
||||
}
|
||||
}
|
||||
|
||||
if !slash {
|
||||
str := string(data[startPos:pos])
|
||||
pos++
|
||||
skipSpaces(false)
|
||||
return str, true
|
||||
}
|
||||
|
||||
buffer := make([]rune, pos-startPos+1)
|
||||
n1 := 0
|
||||
n2 := startPos
|
||||
|
||||
invalidEscape := func() (string, bool) {
|
||||
str := string(data[startPos:pos])
|
||||
pos++
|
||||
ErrorLogF("Invalid escape sequence in \"%s\" (position %d)", str, n2-2-startPos)
|
||||
return str, false
|
||||
}
|
||||
|
||||
for n2 < pos {
|
||||
if data[n2] != '\\' {
|
||||
buffer[n1] = data[n2]
|
||||
n2++
|
||||
} else {
|
||||
n2 += 2
|
||||
switch data[n2-1] {
|
||||
case 'n':
|
||||
buffer[n1] = '\n'
|
||||
|
||||
case 'r':
|
||||
buffer[n1] = '\r'
|
||||
|
||||
case 't':
|
||||
buffer[n1] = '\t'
|
||||
|
||||
case '"':
|
||||
buffer[n1] = '"'
|
||||
|
||||
case '\'':
|
||||
buffer[n1] = '\''
|
||||
|
||||
case '\\':
|
||||
buffer[n1] = '\\'
|
||||
|
||||
case 'x', 'X':
|
||||
if n2+2 > pos {
|
||||
return invalidEscape()
|
||||
}
|
||||
x := 0
|
||||
for range 2 {
|
||||
ch := data[n2]
|
||||
if ch >= '0' && ch <= '9' {
|
||||
x = x*16 + int(ch-'0')
|
||||
} else if ch >= 'a' && ch <= 'f' {
|
||||
x = x*16 + int(ch-'a'+10)
|
||||
} else if ch >= 'A' && ch <= 'F' {
|
||||
x = x*16 + int(ch-'A'+10)
|
||||
} else {
|
||||
return invalidEscape()
|
||||
}
|
||||
n2++
|
||||
}
|
||||
buffer[n1] = rune(x)
|
||||
|
||||
case 'u', 'U':
|
||||
if n2+4 > pos {
|
||||
return invalidEscape()
|
||||
}
|
||||
x := 0
|
||||
for range 4 {
|
||||
ch := data[n2]
|
||||
if ch >= '0' && ch <= '9' {
|
||||
x = x*16 + int(ch-'0')
|
||||
} else if ch >= 'a' && ch <= 'f' {
|
||||
x = x*16 + int(ch-'a'+10)
|
||||
} else if ch >= 'A' && ch <= 'F' {
|
||||
x = x*16 + int(ch-'A'+10)
|
||||
} else {
|
||||
return invalidEscape()
|
||||
}
|
||||
n2++
|
||||
}
|
||||
buffer[n1] = rune(x)
|
||||
|
||||
default:
|
||||
str := string(data[startPos:pos])
|
||||
ErrorLogF("Invalid escape sequence in \"%s\" (position %d)", str, n2-2-startPos)
|
||||
return str, false
|
||||
}
|
||||
}
|
||||
n1++
|
||||
}
|
||||
|
||||
pos++
|
||||
skipSpaces(false)
|
||||
return string(buffer[0:n1]), true
|
||||
}
|
||||
|
||||
stopSymbol := func(symbol rune) bool {
|
||||
if unicode.IsSpace(symbol) {
|
||||
return true
|
||||
}
|
||||
for _, sym := range []rune{'=', '{', '}', '[', ']', ',', ' ', '\t', '\n', '\'', '"', '`', '/'} {
|
||||
if sym == symbol {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
for pos < size && !stopSymbol(data[pos]) {
|
||||
pos++
|
||||
}
|
||||
|
||||
endPos := pos
|
||||
skipSpaces(false)
|
||||
if startPos == endPos {
|
||||
//ErrorLog("empty tag")
|
||||
return "", true
|
||||
}
|
||||
return string(data[startPos:endPos]), true
|
||||
}
|
||||
|
||||
var parseObject func(tag string) DataObject
|
||||
var parseArray func() []DataValue
|
||||
|
||||
parseNode := func() DataNode {
|
||||
var tag string
|
||||
var ok bool
|
||||
|
||||
if tag, ok = parseTag(); !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
skipSpaces(true)
|
||||
if data[pos] != '=' {
|
||||
ErrorLogF("expected '=' after a tag name (line: %d, position: %d)", line, pos-lineStart)
|
||||
return nil
|
||||
}
|
||||
|
||||
pos++
|
||||
skipSpaces(true)
|
||||
switch data[pos] {
|
||||
case '[':
|
||||
node := new(dataNode)
|
||||
node.tag = tag
|
||||
|
||||
if node.array = parseArray(); node.array == nil {
|
||||
return nil
|
||||
}
|
||||
return node
|
||||
|
||||
case '{':
|
||||
node := new(dataNode)
|
||||
node.tag = tag
|
||||
if node.value = parseObject("_"); node.value == nil {
|
||||
return nil
|
||||
}
|
||||
return node
|
||||
|
||||
case '}', ']', '=':
|
||||
ErrorLogF("Expected '[', '{' or a tag name after '=' (line: %d, position: %d)", line, pos-lineStart)
|
||||
return nil
|
||||
case ' ', '\t':
|
||||
// do nothing
|
||||
|
||||
default:
|
||||
var str string
|
||||
if str, ok = parseTag(); !ok {
|
||||
return nil
|
||||
if !unicode.IsSpace(parser.data[parser.pos]) {
|
||||
return
|
||||
}
|
||||
}
|
||||
parser.pos++
|
||||
}
|
||||
}
|
||||
|
||||
node := new(dataNode)
|
||||
node.tag = tag
|
||||
func (parser *dataParser) parseTag() (string, error) {
|
||||
parser.skipSpaces(true)
|
||||
startPos := parser.pos
|
||||
switch parser.data[parser.pos] {
|
||||
case '`':
|
||||
parser.pos++
|
||||
startPos++
|
||||
for parser.data[parser.pos] != '`' {
|
||||
parser.pos++
|
||||
if parser.pos >= parser.size {
|
||||
return string(parser.data[startPos:parser.size]), errors.New("unexpected end of text")
|
||||
}
|
||||
}
|
||||
str := string(parser.data[startPos:parser.pos])
|
||||
parser.pos++
|
||||
return str, nil
|
||||
|
||||
if data[pos] == '{' {
|
||||
if node.value = parseObject(str); node.value == nil {
|
||||
return nil
|
||||
}
|
||||
case '\'', '"':
|
||||
stopSymbol := parser.data[parser.pos]
|
||||
parser.pos++
|
||||
startPos++
|
||||
slash := false
|
||||
for stopSymbol != parser.data[parser.pos] {
|
||||
if parser.data[parser.pos] == '\\' {
|
||||
parser.pos += 2
|
||||
slash = true
|
||||
} else {
|
||||
val := new(dataStringValue)
|
||||
val.value = str
|
||||
node.value = val
|
||||
parser.pos++
|
||||
}
|
||||
if parser.pos >= parser.size {
|
||||
return string(parser.data[startPos:parser.size]), errors.New("unexpected end of text")
|
||||
}
|
||||
}
|
||||
|
||||
return node
|
||||
if !slash {
|
||||
str := string(parser.data[startPos:parser.pos])
|
||||
parser.pos++
|
||||
parser.skipSpaces(false)
|
||||
return str, nil
|
||||
}
|
||||
|
||||
buffer := make([]rune, parser.pos-startPos+1)
|
||||
n1 := 0
|
||||
n2 := startPos
|
||||
|
||||
invalidEscape := func() (string, error) {
|
||||
str := string(parser.data[startPos:parser.pos])
|
||||
parser.pos++
|
||||
return str, fmt.Errorf(`invalid escape sequence in "%s" (position %d)`, str, n2-2-startPos)
|
||||
}
|
||||
|
||||
for n2 < parser.pos {
|
||||
if parser.data[n2] != '\\' {
|
||||
buffer[n1] = parser.data[n2]
|
||||
n2++
|
||||
} else {
|
||||
n2 += 2
|
||||
switch parser.data[n2-1] {
|
||||
case 'n':
|
||||
buffer[n1] = '\n'
|
||||
|
||||
case 'r':
|
||||
buffer[n1] = '\r'
|
||||
|
||||
case 't':
|
||||
buffer[n1] = '\t'
|
||||
|
||||
case '"':
|
||||
buffer[n1] = '"'
|
||||
|
||||
case '\'':
|
||||
buffer[n1] = '\''
|
||||
|
||||
case '\\':
|
||||
buffer[n1] = '\\'
|
||||
|
||||
case 'x', 'X':
|
||||
if n2+2 > parser.pos {
|
||||
return invalidEscape()
|
||||
}
|
||||
x := 0
|
||||
for range 2 {
|
||||
ch := parser.data[n2]
|
||||
if ch >= '0' && ch <= '9' {
|
||||
x = x*16 + int(ch-'0')
|
||||
} else if ch >= 'a' && ch <= 'f' {
|
||||
x = x*16 + int(ch-'a'+10)
|
||||
} else if ch >= 'A' && ch <= 'F' {
|
||||
x = x*16 + int(ch-'A'+10)
|
||||
} else {
|
||||
return invalidEscape()
|
||||
}
|
||||
n2++
|
||||
}
|
||||
buffer[n1] = rune(x)
|
||||
|
||||
case 'u', 'U':
|
||||
if n2+4 > parser.pos {
|
||||
return invalidEscape()
|
||||
}
|
||||
x := 0
|
||||
for range 4 {
|
||||
ch := parser.data[n2]
|
||||
if ch >= '0' && ch <= '9' {
|
||||
x = x*16 + int(ch-'0')
|
||||
} else if ch >= 'a' && ch <= 'f' {
|
||||
x = x*16 + int(ch-'a'+10)
|
||||
} else if ch >= 'A' && ch <= 'F' {
|
||||
x = x*16 + int(ch-'A'+10)
|
||||
} else {
|
||||
return invalidEscape()
|
||||
}
|
||||
n2++
|
||||
}
|
||||
buffer[n1] = rune(x)
|
||||
|
||||
default:
|
||||
str := string(parser.data[startPos:parser.pos])
|
||||
return str, fmt.Errorf(`invalid escape sequence in "%s" (position %d)`, str, n2-2-startPos)
|
||||
}
|
||||
}
|
||||
n1++
|
||||
}
|
||||
|
||||
parser.pos++
|
||||
parser.skipSpaces(false)
|
||||
return string(buffer[0:n1]), nil
|
||||
}
|
||||
|
||||
for parser.pos < parser.size && !parser.stopSymbol(parser.data[parser.pos]) {
|
||||
parser.pos++
|
||||
}
|
||||
|
||||
endPos := parser.pos
|
||||
parser.skipSpaces(false)
|
||||
if startPos == endPos {
|
||||
//ErrorLog("empty tag")
|
||||
return "", nil
|
||||
}
|
||||
return string(parser.data[startPos:endPos]), nil
|
||||
}
|
||||
|
||||
func (parser *dataParser) stopSymbol(symbol rune) bool {
|
||||
return unicode.IsSpace(symbol) ||
|
||||
slices.Contains([]rune{'=', '{', '}', '[', ']', ',', ' ', '\t', '\n', '\'', '"', '`', '/'}, symbol)
|
||||
}
|
||||
|
||||
func (parser *dataParser) parseNode() (DataNode, error) {
|
||||
var tag string
|
||||
var err error
|
||||
|
||||
if tag, err = parser.parseTag(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parser.skipSpaces(true)
|
||||
if parser.data[parser.pos] != '=' {
|
||||
return nil, fmt.Errorf("expected '=' after a tag name (line: %d, position: %d)", parser.line, parser.pos-parser.lineStart)
|
||||
}
|
||||
|
||||
parser.pos++
|
||||
parser.skipSpaces(true)
|
||||
switch parser.data[parser.pos] {
|
||||
case '[':
|
||||
node := new(dataNode)
|
||||
node.tag = tag
|
||||
|
||||
if node.array, err = parser.parseArray(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return node, nil
|
||||
|
||||
case '{':
|
||||
node := new(dataNode)
|
||||
node.tag = tag
|
||||
if node.value, err = parser.parseObject("_"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return node, nil
|
||||
|
||||
case '}', ']', '=':
|
||||
return nil, fmt.Errorf(`expected '[', '{' or a tag name after '=' (line: %d, position: %d)`, parser.line, parser.pos-parser.lineStart)
|
||||
|
||||
default:
|
||||
var str string
|
||||
if str, err = parser.parseTag(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
node := new(dataNode)
|
||||
node.tag = tag
|
||||
|
||||
if parser.data[parser.pos] == '{' {
|
||||
if node.value, err = parser.parseObject(str); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
val := new(dataStringValue)
|
||||
val.value = str
|
||||
node.value = val
|
||||
}
|
||||
|
||||
return node, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (parser *dataParser) parseObject(tag string) (DataObject, error) {
|
||||
if parser.data[parser.pos] != '{' {
|
||||
return nil, fmt.Errorf(`expected '{' (line: %d, position: %d)`, parser.line, parser.pos-parser.lineStart)
|
||||
}
|
||||
parser.pos++
|
||||
|
||||
obj := new(dataObject)
|
||||
obj.tag = tag
|
||||
obj.property = []DataNode{}
|
||||
|
||||
for parser.pos < parser.size {
|
||||
parser.skipSpaces(true)
|
||||
if parser.data[parser.pos] == '}' {
|
||||
parser.pos++
|
||||
parser.skipSpaces(false)
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
node, err := parser.parseNode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
obj.property = append(obj.property, node)
|
||||
if parser.data[parser.pos] == '}' {
|
||||
parser.pos++
|
||||
parser.skipSpaces(true)
|
||||
return obj, nil
|
||||
} else if parser.data[parser.pos] != ',' && parser.data[parser.pos] != '\n' {
|
||||
return nil, fmt.Errorf(`expected '}', '\n' or ',' (line: %d, position: %d)`, parser.line, parser.pos-parser.lineStart)
|
||||
}
|
||||
|
||||
if parser.data[parser.pos] != '\n' {
|
||||
parser.pos++
|
||||
}
|
||||
|
||||
parser.skipSpaces(true)
|
||||
for parser.data[parser.pos] == ',' {
|
||||
parser.pos++
|
||||
parser.skipSpaces(true)
|
||||
}
|
||||
}
|
||||
|
||||
parseObject = func(tag string) DataObject {
|
||||
if data[pos] != '{' {
|
||||
ErrorLogF("Expected '{' (line: %d, position: %d)", line, pos-lineStart)
|
||||
return nil
|
||||
}
|
||||
pos++
|
||||
return nil, errors.New("unexpected end of text")
|
||||
}
|
||||
|
||||
obj := new(dataObject)
|
||||
obj.tag = tag
|
||||
obj.property = []DataNode{}
|
||||
func (parser *dataParser) parseArray() ([]DataValue, error) {
|
||||
parser.pos++
|
||||
parser.skipSpaces(true)
|
||||
|
||||
for pos < size {
|
||||
var node DataNode
|
||||
array := []DataValue{}
|
||||
|
||||
skipSpaces(true)
|
||||
if data[pos] == '}' {
|
||||
pos++
|
||||
skipSpaces(false)
|
||||
return obj
|
||||
}
|
||||
|
||||
if node = parseNode(); node == nil {
|
||||
return nil
|
||||
}
|
||||
obj.property = append(obj.property, node)
|
||||
if data[pos] == '}' {
|
||||
pos++
|
||||
skipSpaces(true)
|
||||
return obj
|
||||
} else if data[pos] != ',' && data[pos] != '\n' {
|
||||
ErrorLogF(`Expected '}', '\n' or ',' (line: %d, position: %d)`, line, pos-lineStart)
|
||||
return nil
|
||||
}
|
||||
if data[pos] != '\n' {
|
||||
pos++
|
||||
}
|
||||
skipSpaces(true)
|
||||
for data[pos] == ',' {
|
||||
pos++
|
||||
skipSpaces(true)
|
||||
}
|
||||
for parser.pos < parser.size {
|
||||
parser.skipSpaces(true)
|
||||
for parser.data[parser.pos] == ',' && parser.pos < parser.size {
|
||||
parser.pos++
|
||||
parser.skipSpaces(true)
|
||||
}
|
||||
|
||||
ErrorLog("Unexpected end of text")
|
||||
return nil
|
||||
}
|
||||
if parser.pos >= parser.size {
|
||||
break
|
||||
}
|
||||
|
||||
parseArray = func() []DataValue {
|
||||
pos++
|
||||
skipSpaces(true)
|
||||
if parser.data[parser.pos] == ']' {
|
||||
parser.pos++
|
||||
parser.skipSpaces(true)
|
||||
return array, nil
|
||||
}
|
||||
|
||||
array := []DataValue{}
|
||||
tag, err := parser.parseTag()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for pos < size {
|
||||
var tag string
|
||||
var ok bool
|
||||
|
||||
skipSpaces(true)
|
||||
for data[pos] == ',' && pos < size {
|
||||
pos++
|
||||
skipSpaces(true)
|
||||
if parser.data[parser.pos] == '{' {
|
||||
obj, err := parser.parseObject(tag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
array = append(array, obj)
|
||||
} else {
|
||||
val := new(dataStringValue)
|
||||
val.value = tag
|
||||
array = append(array, val)
|
||||
}
|
||||
|
||||
if pos >= size {
|
||||
break
|
||||
}
|
||||
switch parser.data[parser.pos] {
|
||||
case ']', ',', '\n':
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf(`expected ']' or ',' (line: %d, position: %d)`, parser.line, parser.pos-parser.lineStart)
|
||||
}
|
||||
|
||||
/*
|
||||
if data[pos] == ']' {
|
||||
pos++
|
||||
skipSpaces(true)
|
||||
return array
|
||||
}
|
||||
|
||||
if tag, ok = parseTag(); !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
if data[pos] == '{' {
|
||||
obj := parseObject(tag)
|
||||
if obj == nil {
|
||||
return nil
|
||||
}
|
||||
array = append(array, obj)
|
||||
} else {
|
||||
val := new(dataStringValue)
|
||||
val.value = tag
|
||||
array = append(array, val)
|
||||
}
|
||||
|
||||
switch data[pos] {
|
||||
case ']', ',', '\n':
|
||||
|
||||
default:
|
||||
ErrorLogF("Expected ']' or ',' (line: %d, position: %d)", line, pos-lineStart)
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
if data[pos] == ']' {
|
||||
pos++
|
||||
skipSpaces()
|
||||
return array, nil
|
||||
} else if data[pos] != ',' {
|
||||
return nil, fmt.Errorf("Expected ']' or ',' (line: %d, position: %d)", line, pos-lineStart)
|
||||
}
|
||||
pos++
|
||||
skipSpaces()
|
||||
*/
|
||||
}
|
||||
|
||||
ErrorLog("Unexpected end of text")
|
||||
return nil
|
||||
return array, nil
|
||||
} else if data[pos] != ',' {
|
||||
return nil, fmt.Errorf("Expected ']' or ',' (line: %d, position: %d)", line, pos-lineStart)
|
||||
}
|
||||
pos++
|
||||
skipSpaces()
|
||||
*/
|
||||
}
|
||||
|
||||
if tag, ok := parseTag(); ok {
|
||||
return parseObject(tag)
|
||||
}
|
||||
return nil
|
||||
return nil, errors.New("unexpected end of text")
|
||||
}
|
||||
|
||||
// ParseDataText - parse text and return DataNode
|
||||
func ParseDataText(text string) (DataObject, error) {
|
||||
|
||||
if strings.ContainsAny(text, "\r") {
|
||||
text = strings.ReplaceAll(text, "\r\n", "\n")
|
||||
text = strings.ReplaceAll(text, "\r", "\n")
|
||||
}
|
||||
|
||||
parser := dataParser{
|
||||
data: append([]rune(text), rune(0)),
|
||||
pos: 0,
|
||||
line: 1,
|
||||
lineStart: 0,
|
||||
}
|
||||
parser.size = len(parser.data) - 1
|
||||
|
||||
tag, err := parser.parseTag()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return parser.parseObject(tag)
|
||||
}
|
||||
|
|
|
@ -312,7 +312,9 @@ func dataListHtmlProperties(view View, buffer *strings.Builder) {
|
|||
}
|
||||
|
||||
// GetDataList returns the data list of an editor.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetDataList(view View, subviewID ...string) []string {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
return getDataListProperty(view)
|
||||
|
|
19
data_test.go
19
data_test.go
|
@ -6,10 +6,6 @@ import (
|
|||
|
||||
func TestParseDataText(t *testing.T) {
|
||||
|
||||
SetErrorLog(func(text string) {
|
||||
t.Error(text)
|
||||
})
|
||||
|
||||
text := `obj1 {
|
||||
key1 = val1,
|
||||
key2=obj2{
|
||||
|
@ -27,8 +23,10 @@ func TestParseDataText(t *testing.T) {
|
|||
key3 = "\n \t \\ \r \" ' \X4F\x4e \U01Ea",` +
|
||||
"key4=`" + `\n \t \\ \r \" ' \x8F \UF80a` + "`\r}"
|
||||
|
||||
obj := ParseDataText(text)
|
||||
if obj != nil {
|
||||
obj, err := ParseDataText(text)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
if obj.Tag() != "obj1" {
|
||||
t.Error(`obj.Tag() != "obj1"`)
|
||||
}
|
||||
|
@ -75,7 +73,7 @@ func TestParseDataText(t *testing.T) {
|
|||
t.Errorf(`obj.PropertyValue("key5") result: ("%s",%v)`, val, ok)
|
||||
}
|
||||
|
||||
testKey := func(obj DataObject, index int, tag string, nodeType int) DataNode {
|
||||
testKey := func(obj DataObject, index int, tag string, nodeType DataNodeType) DataNode {
|
||||
key := obj.Property(index)
|
||||
if key == nil {
|
||||
t.Errorf(`%s.Property(%d) == nil`, obj.Tag(), index)
|
||||
|
@ -118,7 +116,7 @@ func TestParseDataText(t *testing.T) {
|
|||
|
||||
type testKeyData struct {
|
||||
tag string
|
||||
nodeType int
|
||||
nodeType DataNodeType
|
||||
}
|
||||
|
||||
data := []testKeyData{
|
||||
|
@ -173,9 +171,6 @@ func TestParseDataText(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
SetErrorLog(func(text string) {
|
||||
})
|
||||
|
||||
failText := []string{
|
||||
" ",
|
||||
"obj[]",
|
||||
|
@ -204,7 +199,7 @@ func TestParseDataText(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, txt := range failText {
|
||||
if obj := ParseDataText(txt); obj != nil {
|
||||
if _, err := ParseDataText(txt); err == nil {
|
||||
t.Errorf("result ParseDataText(\"%s\") must be fail", txt)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -274,7 +274,7 @@ func (picker *datePickerData) propertyChanged(tag PropertyName) {
|
|||
date := GetDatePickerValue(picker)
|
||||
session.callFunc("setInputValue", picker.htmlID(), date.Format(dateFormat))
|
||||
|
||||
if listeners := GetDateChangedListeners(picker); len(listeners) > 0 {
|
||||
if listeners := getTwoArgEventListeners[DatePicker, time.Time](picker, nil, DateChangedEvent); len(listeners) > 0 {
|
||||
oldDate := time.Now()
|
||||
if value := picker.getRaw("old-date"); value != nil {
|
||||
if date, ok := value.(time.Time); ok {
|
||||
|
@ -282,7 +282,7 @@ func (picker *datePickerData) propertyChanged(tag PropertyName) {
|
|||
}
|
||||
}
|
||||
for _, listener := range listeners {
|
||||
listener(picker, date, oldDate)
|
||||
listener.Run(picker, date, oldDate)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -348,11 +348,11 @@ func (picker *datePickerData) handleCommand(self View, command PropertyName, dat
|
|||
oldValue := GetDatePickerValue(picker)
|
||||
picker.properties[DatePickerValue] = value
|
||||
if value != oldValue {
|
||||
for _, listener := range GetDateChangedListeners(picker) {
|
||||
listener(picker, value, oldValue)
|
||||
for _, listener := range getTwoArgEventListeners[DatePicker, time.Time](picker, nil, DateChangedEvent) {
|
||||
listener.Run(picker, value, oldValue)
|
||||
}
|
||||
if listener, ok := picker.changeListener[DatePickerValue]; ok {
|
||||
listener(picker, DatePickerValue)
|
||||
listener.Run(picker, DatePickerValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -400,7 +400,9 @@ func getDateProperty(view View, mainTag, shortTag PropertyName) (time.Time, bool
|
|||
|
||||
// GetDatePickerMin returns the min date of DatePicker subview and "true" as the second value if the min date is set,
|
||||
// "false" as the second value otherwise.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetDatePickerMin(view View, subviewID ...string) (time.Time, bool) {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
return getDateProperty(view, DatePickerMin, Min)
|
||||
|
@ -410,7 +412,9 @@ func GetDatePickerMin(view View, subviewID ...string) (time.Time, bool) {
|
|||
|
||||
// GetDatePickerMax returns the max date of DatePicker subview and "true" as the second value if the min date is set,
|
||||
// "false" as the second value otherwise.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetDatePickerMax(view View, subviewID ...string) (time.Time, bool) {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
return getDateProperty(view, DatePickerMax, Max)
|
||||
|
@ -419,13 +423,17 @@ func GetDatePickerMax(view View, subviewID ...string) (time.Time, bool) {
|
|||
}
|
||||
|
||||
// GetDatePickerStep returns the date changing step in days of DatePicker subview.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetDatePickerStep(view View, subviewID ...string) int {
|
||||
return intStyledProperty(view, subviewID, DatePickerStep, 0)
|
||||
}
|
||||
|
||||
// GetDatePickerValue returns the date of DatePicker subview.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetDatePickerValue(view View, subviewID ...string) time.Time {
|
||||
if view = getSubview(view, subviewID); view == nil {
|
||||
return time.Now()
|
||||
|
@ -436,7 +444,18 @@ func GetDatePickerValue(view View, subviewID ...string) time.Time {
|
|||
|
||||
// GetDateChangedListeners returns the DateChangedListener list of an DatePicker subview.
|
||||
// If there are no listeners then the empty list is returned
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetDateChangedListeners(view View, subviewID ...string) []func(DatePicker, time.Time, time.Time) {
|
||||
return getTwoArgEventListeners[DatePicker, time.Time](view, subviewID, DateChangedEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.DatePicker, time.Time, time.Time),
|
||||
// - func(rui.DatePicker, time.Time),
|
||||
// - func(rui.DatePicker),
|
||||
// - func(time.Time, time.Time),
|
||||
// - func(time.Time),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetDateChangedListeners(view View, subviewID ...string) []any {
|
||||
return getTwoArgEventRawListeners[DatePicker, time.Time](view, subviewID, DateChangedEvent)
|
||||
}
|
||||
|
|
|
@ -92,7 +92,7 @@ func (detailsView *detailsViewData) setFunc(tag PropertyName, value any) []Prope
|
|||
value.setParentID(detailsView.htmlID())
|
||||
|
||||
case DataObject:
|
||||
if view := CreateViewFromObject(detailsView.Session(), value); view != nil {
|
||||
if view := CreateViewFromObject(detailsView.Session(), value, nil); view != nil {
|
||||
detailsView.setRaw(Summary, view)
|
||||
view.setParentID(detailsView.htmlID())
|
||||
} else {
|
||||
|
@ -193,7 +193,7 @@ func (detailsView *detailsViewData) handleCommand(self View, command PropertyNam
|
|||
if n, ok := dataIntProperty(data, "open"); ok {
|
||||
detailsView.properties[Expanded] = (n != 0)
|
||||
if listener, ok := detailsView.changeListener[Expanded]; ok {
|
||||
listener(detailsView, Expanded)
|
||||
listener.Run(detailsView, Expanded)
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
@ -202,7 +202,9 @@ func (detailsView *detailsViewData) handleCommand(self View, command PropertyNam
|
|||
}
|
||||
|
||||
// GetDetailsSummary returns a value of the Summary property of DetailsView.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetDetailsSummary(view View, subviewID ...string) View {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
if value := view.Get(Summary); value != nil {
|
||||
|
@ -219,13 +221,17 @@ func GetDetailsSummary(view View, subviewID ...string) View {
|
|||
}
|
||||
|
||||
// IsDetailsExpanded returns a value of the Expanded property of DetailsView.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func IsDetailsExpanded(view View, subviewID ...string) bool {
|
||||
return boolStyledProperty(view, subviewID, Expanded, false)
|
||||
}
|
||||
|
||||
// IsDetailsExpanded returns a value of the HideSummaryMarker property of DetailsView.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func IsSummaryMarkerHidden(view View, subviewID ...string) bool {
|
||||
return boolStyledProperty(view, subviewID, HideSummaryMarker, false)
|
||||
}
|
||||
|
|
180
dragAndDrop.go
180
dragAndDrop.go
|
@ -49,7 +49,7 @@ const (
|
|||
// Supported types: int, string.
|
||||
//
|
||||
// Values:
|
||||
// - 0 (DropEffectUndefined) or "undefined" - The property value is not defined (defaut value).
|
||||
// - 0 (DropEffectUndefined) or "undefined" - The property value is not defined (default value).
|
||||
// - 1 (DropEffectCopy) or "copy" - A copy of the source item may be made at the new location.
|
||||
// - 2 (DropEffectMove) or "move" - An item may be moved to a new location.
|
||||
// - 4 (DropEffectLink) or "link" - A link may be established to the source at the new location.
|
||||
|
@ -68,7 +68,7 @@ const (
|
|||
// Supported types: int, string.
|
||||
//
|
||||
// Values:
|
||||
// - 0 (DropEffectUndefined) or "undefined" - The property value is not defined (defaut value). Equivalent to DropEffectAll
|
||||
// - 0 (DropEffectUndefined) or "undefined" - The property value is not defined (default value). Equivalent to DropEffectAll
|
||||
// - 1 (DropEffectCopy) or "copy" - A copy of the source item may be made at the new location.
|
||||
// - 2 (DropEffectMove) or "move" - An item may be moved to a new location.
|
||||
// - 3 (DropEffectLink) or "link" - A link may be established to the source at the new location.
|
||||
|
@ -84,7 +84,16 @@ const (
|
|||
// Fired when the user starts dragging an element or text selection.
|
||||
//
|
||||
// General listener format:
|
||||
// func(view rui.View, event rui.DragAndDropEvent).
|
||||
//
|
||||
// where:
|
||||
// - view - Interface of a view which generated this event,
|
||||
// - event - event parameters.
|
||||
//
|
||||
// Allowed listener formats:
|
||||
// func(view rui.View)
|
||||
// func(rui.DragAndDropEvent)
|
||||
// func()
|
||||
DragStartEvent PropertyName = "drag-start-event"
|
||||
|
||||
// DragEndEvent is the constant for "drag-end-event" property tag.
|
||||
|
@ -93,7 +102,16 @@ const (
|
|||
// Fired when a drag operation ends (by releasing a mouse button or hitting the escape key).
|
||||
//
|
||||
// General listener format:
|
||||
// func(view rui.View, event rui.DragAndDropEvent).
|
||||
//
|
||||
// where:
|
||||
// - view - Interface of a view which generated this event,
|
||||
// - event - event parameters.
|
||||
//
|
||||
// Allowed listener formats:
|
||||
// func(view rui.View)
|
||||
// func(rui.DragAndDropEvent)
|
||||
// func()
|
||||
DragEndEvent PropertyName = "drag-end-event"
|
||||
|
||||
// DragEnterEvent is the constant for "drag-enter-event" property tag.
|
||||
|
@ -102,7 +120,16 @@ const (
|
|||
// Fired when a dragged element or text selection enters a valid drop target.
|
||||
//
|
||||
// General listener format:
|
||||
// func(view rui.View, event rui.DragAndDropEvent).
|
||||
//
|
||||
// where:
|
||||
// - view - Interface of a view which generated this event,
|
||||
// - event - event parameters.
|
||||
//
|
||||
// Allowed listener formats:
|
||||
// func(view rui.View)
|
||||
// func(rui.DragAndDropEvent)
|
||||
// func()
|
||||
DragEnterEvent PropertyName = "drag-enter-event"
|
||||
|
||||
// DragLeaveEvent is the constant for "drag-leave-event" property tag.
|
||||
|
@ -111,7 +138,16 @@ const (
|
|||
// Fired when a dragged element or text selection leaves a valid drop target.
|
||||
//
|
||||
// General listener format:
|
||||
// func(view rui.View, event rui.DragAndDropEvent).
|
||||
//
|
||||
// where:
|
||||
// - view - Interface of a view which generated this event,
|
||||
// - event - event parameters.
|
||||
//
|
||||
// Allowed listener formats:
|
||||
// func(view rui.View)
|
||||
// func(rui.DragAndDropEvent)
|
||||
// func()
|
||||
DragLeaveEvent PropertyName = "drag-leave-event"
|
||||
|
||||
// DragOverEvent is the constant for "drag-over-event" property tag.
|
||||
|
@ -120,7 +156,16 @@ const (
|
|||
// Fired when an element or text selection is being dragged over a valid drop target (every few hundred milliseconds).
|
||||
//
|
||||
// General listener format:
|
||||
// func(view rui.View, event rui.DragAndDropEvent).
|
||||
//
|
||||
// where:
|
||||
// - view - Interface of a view which generated this event,
|
||||
// - event - event parameters.
|
||||
//
|
||||
// Allowed listener formats:
|
||||
// func(view rui.View)
|
||||
// func(rui.DragAndDropEvent)
|
||||
// func()
|
||||
DragOverEvent PropertyName = "drag-over-event"
|
||||
|
||||
// DropEvent is the constant for "drop-event" property tag.
|
||||
|
@ -129,7 +174,16 @@ const (
|
|||
// Fired when an element or text selection is dropped on a valid drop target.
|
||||
//
|
||||
// General listener format:
|
||||
// func(view rui.View, event rui.DragAndDropEvent).
|
||||
//
|
||||
// where:
|
||||
// - view - Interface of a view which generated this event,
|
||||
// - event - event parameters.
|
||||
//
|
||||
// Allowed listener formats:
|
||||
// func(view rui.View)
|
||||
// func(rui.DragAndDropEvent)
|
||||
// func()
|
||||
DropEvent PropertyName = "drop-event"
|
||||
|
||||
// DropEffectUndefined - the value of the "drop-effect" and "drop-effect-allowed" properties: the value is not defined (default value).
|
||||
|
@ -156,7 +210,7 @@ const (
|
|||
// DropEffectLinkMove - the value of the "drop-effect-allowed" property: a link or move operation is permitted.
|
||||
DropEffectLinkMove = DropEffectLink + DropEffectMove
|
||||
|
||||
// DropEffectAll - the value of the "drop-effect-allowed" property: all operations (copy, move, and link) are permitted (defaut value).
|
||||
// DropEffectAll - the value of the "drop-effect-allowed" property: all operations (copy, move, and link) are permitted (default value).
|
||||
DropEffectAll = DropEffectCopy + DropEffectMove + DropEffectLink
|
||||
)
|
||||
|
||||
|
@ -336,7 +390,7 @@ func handleDragAndDropEvents(view View, tag PropertyName, data DataObject) {
|
|||
event.init(view.Session(), data)
|
||||
|
||||
for _, listener := range listeners {
|
||||
listener(view, event)
|
||||
listener.Run(view, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -408,17 +462,17 @@ func dragAndDropHtml(view View, buffer *strings.Builder) {
|
|||
|
||||
if f := GetDragImageXOffset(view); f != 0 {
|
||||
buffer.WriteString(` data-drag-image-x="`)
|
||||
buffer.WriteString(fmt.Sprintf("%g", f))
|
||||
fmt.Fprintf(buffer, "%g", f)
|
||||
buffer.WriteString(`" `)
|
||||
}
|
||||
|
||||
if f := GetDragImageYOffset(view); f != 0 {
|
||||
buffer.WriteString(` data-drag-image-y="`)
|
||||
buffer.WriteString(fmt.Sprintf("%g", f))
|
||||
fmt.Fprintf(buffer, "%g", f)
|
||||
buffer.WriteString(`" `)
|
||||
}
|
||||
|
||||
effects := []string{"undifined", "copy", "move", "copyMove", "link", "copyLink", "linkMove", "all"}
|
||||
effects := []string{"undefined", "copy", "move", "copyMove", "link", "copyLink", "linkMove", "all"}
|
||||
if n := GetDropEffectAllowed(view); n > 0 && n < len(effects) {
|
||||
buffer.WriteString(` data-drop-effect-allowed="`)
|
||||
buffer.WriteString(effects[n])
|
||||
|
@ -434,41 +488,99 @@ func (view *viewData) LoadFile(file FileInfo, result func(FileInfo, []byte)) {
|
|||
}
|
||||
|
||||
// GetDragStartEventListeners returns the "drag-start-event" listener list. If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetDragStartEventListeners(view View, subviewID ...string) []func(View, DragAndDropEvent) {
|
||||
return getOneArgEventListeners[View, DragAndDropEvent](view, subviewID, DragStartEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.View, rui.DragAndDropEvent),
|
||||
// - func(rui.View),
|
||||
// - func(rui.DragAndDropEvent),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetDragStartEventListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, DragAndDropEvent](view, subviewID, DragStartEvent)
|
||||
}
|
||||
|
||||
// GetDragEndEventListeners returns the "drag-end-event" listener list. If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetDragEndEventListeners(view View, subviewID ...string) []func(View, DragAndDropEvent) {
|
||||
return getOneArgEventListeners[View, DragAndDropEvent](view, subviewID, DragEndEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.View, rui.DragAndDropEvent),
|
||||
// - func(rui.View),
|
||||
// - func(rui.DragAndDropEvent),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetDragEndEventListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, DragAndDropEvent](view, subviewID, DragEndEvent)
|
||||
}
|
||||
|
||||
// GetDragEnterEventListeners returns the "drag-enter-event" listener list. If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetDragEnterEventListeners(view View, subviewID ...string) []func(View, DragAndDropEvent) {
|
||||
return getOneArgEventListeners[View, DragAndDropEvent](view, subviewID, DragEnterEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.View, rui.DragAndDropEvent),
|
||||
// - func(rui.View),
|
||||
// - func(rui.DragAndDropEvent),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetDragEnterEventListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, DragAndDropEvent](view, subviewID, DragEnterEvent)
|
||||
}
|
||||
|
||||
// GetDragLeaveEventListeners returns the "drag-leave-event" listener list. If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetDragLeaveEventListeners(view View, subviewID ...string) []func(View, DragAndDropEvent) {
|
||||
return getOneArgEventListeners[View, DragAndDropEvent](view, subviewID, DragLeaveEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.View, rui.DragAndDropEvent),
|
||||
// - func(rui.View),
|
||||
// - func(rui.DragAndDropEvent),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetDragLeaveEventListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, DragAndDropEvent](view, subviewID, DragLeaveEvent)
|
||||
}
|
||||
|
||||
// GetDragOverEventListeners returns the "drag-over-event" listener list. If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetDragOverEventListeners(view View, subviewID ...string) []func(View, DragAndDropEvent) {
|
||||
return getOneArgEventListeners[View, DragAndDropEvent](view, subviewID, DragOverEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.View, rui.DragAndDropEvent),
|
||||
// - func(rui.View),
|
||||
// - func(rui.DragAndDropEvent),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetDragOverEventListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, DragAndDropEvent](view, subviewID, DragOverEvent)
|
||||
}
|
||||
|
||||
// GetDropEventListeners returns the "drag-start-event" listener list. If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetDropEventListeners(view View, subviewID ...string) []func(View, DragAndDropEvent) {
|
||||
return getOneArgEventListeners[View, DragAndDropEvent](view, subviewID, DropEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.View, rui.DragAndDropEvent),
|
||||
// - func(rui.View),
|
||||
// - func(rui.DragAndDropEvent),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetDropEventListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, DragAndDropEvent](view, subviewID, DropEvent)
|
||||
}
|
||||
|
||||
// GetDropEventListeners returns the "drag-data" data.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetDragData(view View, subviewID ...string) map[string]string {
|
||||
result := map[string]string{}
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
|
@ -483,7 +595,9 @@ func GetDragData(view View, subviewID ...string) map[string]string {
|
|||
}
|
||||
|
||||
// GetDragImage returns the drag feedback image.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetDragImage(view View, subviewID ...string) string {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
value := view.getRaw(DragImage)
|
||||
|
@ -508,13 +622,17 @@ func GetDragImage(view View, subviewID ...string) string {
|
|||
}
|
||||
|
||||
// GetDragImageXOffset returns the horizontal offset in pixels within the drag feedback image.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetDragImageXOffset(view View, subviewID ...string) float64 {
|
||||
return floatStyledProperty(view, subviewID, DragImageXOffset, 0)
|
||||
}
|
||||
|
||||
// GetDragImageYOffset returns the vertical offset in pixels within the drag feedback image.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetDragImageYOffset(view View, subviewID ...string) float64 {
|
||||
return floatStyledProperty(view, subviewID, DragImageYOffset, 0)
|
||||
}
|
||||
|
@ -529,7 +647,8 @@ func GetDragImageYOffset(view View, subviewID ...string) float64 {
|
|||
// - 2 (DropEffectMove) - An item may be moved to a new location.
|
||||
// - 4 (DropEffectLink) - A link may be established to the source at the new location.
|
||||
//
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetDropEffect(view View, subviewID ...string) int {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
value := view.getRaw(DropEffect)
|
||||
|
@ -573,7 +692,8 @@ func GetDropEffect(view View, subviewID ...string) int {
|
|||
// - 6 (DropEffectLinkMove) - A link or move operation is permitted.
|
||||
// - 7 (DropEffectAll) - All operations are permitted.
|
||||
//
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetDropEffectAllowed(view View, subviewID ...string) int {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
value := view.getRaw(DropEffectAllowed)
|
||||
|
|
|
@ -104,8 +104,8 @@ func (list *dropDownListData) propertyChanged(tag PropertyName) {
|
|||
list.Session().callFunc("selectDropDownListItem", list.htmlID(), current)
|
||||
|
||||
oldCurrent, _ := intProperty(list, "old-current", list.Session(), -1)
|
||||
for _, listener := range GetDropDownListeners(list) {
|
||||
listener(list, current, oldCurrent)
|
||||
for _, listener := range getTwoArgEventListeners[DropDownList, int](list, nil, DropDownEvent) {
|
||||
listener.Run(list, current, oldCurrent)
|
||||
}
|
||||
|
||||
default:
|
||||
|
@ -245,11 +245,11 @@ func (list *dropDownListData) handleCommand(self View, command PropertyName, dat
|
|||
if GetCurrent(list) != number && number >= 0 && number < len(items) {
|
||||
old := GetCurrent(list)
|
||||
list.properties[Current] = number
|
||||
for _, listener := range GetDropDownListeners(list) {
|
||||
listener(list, number, old)
|
||||
for _, listener := range getTwoArgEventListeners[DropDownList, int](list, nil, DropDownEvent) {
|
||||
listener.Run(list, number, old)
|
||||
}
|
||||
if listener, ok := list.changeListener[Current]; ok {
|
||||
listener(list, Current)
|
||||
listener.Run(list, Current)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -264,13 +264,26 @@ func (list *dropDownListData) handleCommand(self View, command PropertyName, dat
|
|||
}
|
||||
|
||||
// GetDropDownListeners returns the "drop-down-event" listener list. If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetDropDownListeners(view View, subviewID ...string) []func(DropDownList, int, int) {
|
||||
return getTwoArgEventListeners[DropDownList, int](view, subviewID, DropDownEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.DropDownList, int, int),
|
||||
// - func(rui.DropDownList, int),
|
||||
// - func(rui.DropDownList),
|
||||
// - func(int, int),
|
||||
// - func(int),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetDropDownListeners(view View, subviewID ...string) []any {
|
||||
return getTwoArgEventRawListeners[DropDownList, int](view, subviewID, DropDownEvent)
|
||||
}
|
||||
|
||||
// GetDropDownItems return the DropDownList items list.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetDropDownItems(view View, subviewID ...string) []string {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
if value := view.Get(Items); value != nil {
|
||||
|
@ -313,14 +326,18 @@ func getIndicesArray(view View, tag PropertyName) []int {
|
|||
}
|
||||
|
||||
// GetDropDownDisabledItems return an array of disabled(non selectable) items indices of DropDownList.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetDropDownDisabledItems(view View, subviewID ...string) []int {
|
||||
view = getSubview(view, subviewID)
|
||||
return getIndicesArray(view, DisabledItems)
|
||||
}
|
||||
|
||||
// GetDropDownItemSeparators return an array of indices of DropDownList items after which a separator should be added.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetDropDownItemSeparators(view View, subviewID ...string) []int {
|
||||
view = getSubview(view, subviewID)
|
||||
return getIndicesArray(view, ItemSeparators)
|
||||
|
|
47
editView.go
47
editView.go
|
@ -268,11 +268,11 @@ func (edit *editViewData) AppendText(text string) {
|
|||
}
|
||||
|
||||
func (edit *editViewData) textChanged(newText, oldText string) {
|
||||
for _, listener := range GetTextChangedListeners(edit) {
|
||||
listener(edit, newText, oldText)
|
||||
for _, listener := range getTwoArgEventListeners[EditView, string](edit, nil, EditTextChangedEvent) {
|
||||
listener.Run(edit, newText, oldText)
|
||||
}
|
||||
if listener, ok := edit.changeListener[Text]; ok {
|
||||
listener(edit, Text)
|
||||
listener.Run(edit, Text)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -451,26 +451,43 @@ func IsReadOnly(view View, subviewID ...string) bool {
|
|||
}
|
||||
|
||||
// IsSpellcheck returns a value of the Spellcheck property of EditView.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func IsSpellcheck(view View, subviewID ...string) bool {
|
||||
return boolStyledProperty(view, subviewID, Spellcheck, false)
|
||||
}
|
||||
|
||||
// GetTextChangedListeners returns the TextChangedListener list of an EditView or MultiLineEditView subview.
|
||||
// If there are no listeners then the empty list is returned
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetTextChangedListeners(view View, subviewID ...string) []func(EditView, string, string) {
|
||||
return getTwoArgEventListeners[EditView, string](view, subviewID, EditTextChangedEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.EditView, string, string),
|
||||
// - func(rui.EditView, string),
|
||||
// - func(rui.EditView),
|
||||
// - func(string, string),
|
||||
// - func(string),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTextChangedListeners(view View, subviewID ...string) []any {
|
||||
return getTwoArgEventRawListeners[EditView, string](view, subviewID, EditTextChangedEvent)
|
||||
}
|
||||
|
||||
// GetEditViewType returns a value of the Type property of EditView.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetEditViewType(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, EditViewType, SingleLineText, false)
|
||||
}
|
||||
|
||||
// GetEditViewPattern returns a value of the Pattern property of EditView.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetEditViewPattern(view View, subviewID ...string) string {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
if pattern, ok := stringProperty(view, EditViewPattern, view.Session()); ok {
|
||||
|
@ -488,13 +505,17 @@ func GetEditViewPattern(view View, subviewID ...string) string {
|
|||
}
|
||||
|
||||
// IsEditViewWrap returns a value of the EditWrap property of MultiLineEditView.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func IsEditViewWrap(view View, subviewID ...string) bool {
|
||||
return boolStyledProperty(view, subviewID, EditWrap, false)
|
||||
}
|
||||
|
||||
// AppendEditText appends the text to the EditView content.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func AppendEditText(view View, subviewID string, text string) {
|
||||
if subviewID != "" {
|
||||
if edit := EditViewByID(view, subviewID); edit != nil {
|
||||
|
@ -509,7 +530,9 @@ func AppendEditText(view View, subviewID string, text string) {
|
|||
}
|
||||
|
||||
// GetCaretColor returns the color of the text input caret.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetCaretColor(view View, subviewID ...string) Color {
|
||||
return colorStyledProperty(view, subviewID, CaretColor, false)
|
||||
}
|
||||
|
|
666
events.go
666
events.go
|
@ -1,6 +1,9 @@
|
|||
package rui
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var eventJsFunc = map[PropertyName]struct{ jsEvent, jsFunc string }{
|
||||
FocusEvent: {jsEvent: "onfocus", jsFunc: "focusEvent"},
|
||||
|
@ -38,469 +41,14 @@ var eventJsFunc = map[PropertyName]struct{ jsEvent, jsFunc string }{
|
|||
DragLeaveEvent: {jsEvent: "ondragleave", jsFunc: "dragLeaveEvent"},
|
||||
}
|
||||
|
||||
func valueToNoArgEventListeners[V any](value any) ([]func(V), bool) {
|
||||
if value == nil {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case func(V):
|
||||
return []func(V){value}, true
|
||||
|
||||
case func():
|
||||
fn := func(V) {
|
||||
value()
|
||||
}
|
||||
return []func(V){fn}, true
|
||||
|
||||
case []func(V):
|
||||
if len(value) == 0 {
|
||||
return nil, true
|
||||
}
|
||||
for _, fn := range value {
|
||||
if fn == nil {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return value, true
|
||||
|
||||
case []func():
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V), count)
|
||||
for i, v := range value {
|
||||
if v == nil {
|
||||
return nil, false
|
||||
}
|
||||
listeners[i] = func(V) {
|
||||
v()
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
|
||||
case []any:
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V), count)
|
||||
for i, v := range value {
|
||||
if v == nil {
|
||||
return nil, false
|
||||
}
|
||||
switch v := v.(type) {
|
||||
case func(V):
|
||||
listeners[i] = v
|
||||
|
||||
case func():
|
||||
listeners[i] = func(V) {
|
||||
v()
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func valueToOneArgEventListeners[V View, E any](value any) ([]func(V, E), bool) {
|
||||
if value == nil {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case func(V, E):
|
||||
return []func(V, E){value}, true
|
||||
|
||||
case func(E):
|
||||
fn := func(_ V, event E) {
|
||||
value(event)
|
||||
}
|
||||
return []func(V, E){fn}, true
|
||||
|
||||
case func(V):
|
||||
fn := func(view V, _ E) {
|
||||
value(view)
|
||||
}
|
||||
return []func(V, E){fn}, true
|
||||
|
||||
case func():
|
||||
fn := func(V, E) {
|
||||
value()
|
||||
}
|
||||
return []func(V, E){fn}, true
|
||||
|
||||
case []func(V, E):
|
||||
if len(value) == 0 {
|
||||
return nil, true
|
||||
}
|
||||
for _, fn := range value {
|
||||
if fn == nil {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return value, true
|
||||
|
||||
case []func(E):
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V, E), count)
|
||||
for i, v := range value {
|
||||
if v == nil {
|
||||
return nil, false
|
||||
}
|
||||
listeners[i] = func(_ V, event E) {
|
||||
v(event)
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
|
||||
case []func(V):
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V, E), count)
|
||||
for i, v := range value {
|
||||
if v == nil {
|
||||
return nil, false
|
||||
}
|
||||
listeners[i] = func(view V, _ E) {
|
||||
v(view)
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
|
||||
case []func():
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V, E), count)
|
||||
for i, v := range value {
|
||||
if v == nil {
|
||||
return nil, false
|
||||
}
|
||||
listeners[i] = func(V, E) {
|
||||
v()
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
|
||||
case []any:
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V, E), count)
|
||||
for i, v := range value {
|
||||
if v == nil {
|
||||
return nil, false
|
||||
}
|
||||
switch v := v.(type) {
|
||||
case func(V, E):
|
||||
listeners[i] = v
|
||||
|
||||
case func(E):
|
||||
listeners[i] = func(_ V, event E) {
|
||||
v(event)
|
||||
}
|
||||
|
||||
case func(V):
|
||||
listeners[i] = func(view V, _ E) {
|
||||
v(view)
|
||||
}
|
||||
|
||||
case func():
|
||||
listeners[i] = func(V, E) {
|
||||
v()
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func valueToTwoArgEventListeners[V View, E any](value any) ([]func(V, E, E), bool) {
|
||||
if value == nil {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case func(V, E, E):
|
||||
return []func(V, E, E){value}, true
|
||||
|
||||
case func(V, E):
|
||||
fn := func(v V, val, _ E) {
|
||||
value(v, val)
|
||||
}
|
||||
return []func(V, E, E){fn}, true
|
||||
|
||||
case func(E, E):
|
||||
fn := func(_ V, val, old E) {
|
||||
value(val, old)
|
||||
}
|
||||
return []func(V, E, E){fn}, true
|
||||
|
||||
case func(E):
|
||||
fn := func(_ V, val, _ E) {
|
||||
value(val)
|
||||
}
|
||||
return []func(V, E, E){fn}, true
|
||||
|
||||
case func(V):
|
||||
fn := func(v V, _, _ E) {
|
||||
value(v)
|
||||
}
|
||||
return []func(V, E, E){fn}, true
|
||||
|
||||
case func():
|
||||
fn := func(V, E, E) {
|
||||
value()
|
||||
}
|
||||
return []func(V, E, E){fn}, true
|
||||
|
||||
case []func(V, E, E):
|
||||
if len(value) == 0 {
|
||||
return nil, true
|
||||
}
|
||||
for _, fn := range value {
|
||||
if fn == nil {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return value, true
|
||||
|
||||
case []func(V, E):
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V, E, E), count)
|
||||
for i, fn := range value {
|
||||
if fn == nil {
|
||||
return nil, false
|
||||
}
|
||||
listeners[i] = func(view V, val, _ E) {
|
||||
fn(view, val)
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
|
||||
case []func(E):
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V, E, E), count)
|
||||
for i, fn := range value {
|
||||
if fn == nil {
|
||||
return nil, false
|
||||
}
|
||||
listeners[i] = func(_ V, val, _ E) {
|
||||
fn(val)
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
|
||||
case []func(E, E):
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V, E, E), count)
|
||||
for i, fn := range value {
|
||||
if fn == nil {
|
||||
return nil, false
|
||||
}
|
||||
listeners[i] = func(_ V, val, old E) {
|
||||
fn(val, old)
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
|
||||
case []func(V):
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V, E, E), count)
|
||||
for i, fn := range value {
|
||||
if fn == nil {
|
||||
return nil, false
|
||||
}
|
||||
listeners[i] = func(view V, _, _ E) {
|
||||
fn(view)
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
|
||||
case []func():
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V, E, E), count)
|
||||
for i, fn := range value {
|
||||
if fn == nil {
|
||||
return nil, false
|
||||
}
|
||||
listeners[i] = func(V, E, E) {
|
||||
fn()
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
|
||||
case []any:
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(V, E, E), count)
|
||||
for i, v := range value {
|
||||
if v == nil {
|
||||
return nil, false
|
||||
}
|
||||
switch fn := v.(type) {
|
||||
case func(V, E, E):
|
||||
listeners[i] = fn
|
||||
|
||||
case func(V, E):
|
||||
listeners[i] = func(view V, val, _ E) {
|
||||
fn(view, val)
|
||||
}
|
||||
|
||||
case func(E, E):
|
||||
listeners[i] = func(_ V, val, old E) {
|
||||
fn(val, old)
|
||||
}
|
||||
|
||||
case func(E):
|
||||
listeners[i] = func(_ V, val, _ E) {
|
||||
fn(val)
|
||||
}
|
||||
|
||||
case func(V):
|
||||
listeners[i] = func(view V, _, _ E) {
|
||||
fn(view)
|
||||
}
|
||||
|
||||
case func():
|
||||
listeners[i] = func(V, E, E) {
|
||||
fn()
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func getNoArgEventListeners[V View](view View, subviewID []string, tag PropertyName) []func(V) {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
if value := view.Get(tag); value != nil {
|
||||
if result, ok := value.([]func(V)); ok {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
return []func(V){}
|
||||
}
|
||||
|
||||
func getOneArgEventListeners[V View, E any](view View, subviewID []string, tag PropertyName) []func(V, E) {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
if value := view.Get(tag); value != nil {
|
||||
if result, ok := value.([]func(V, E)); ok {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
return []func(V, E){}
|
||||
}
|
||||
|
||||
func getTwoArgEventListeners[V View, E any](view View, subviewID []string, tag PropertyName) []func(V, E, E) {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
if value := view.Get(tag); value != nil {
|
||||
if result, ok := value.([]func(V, E, E)); ok {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
return []func(V, E, E){}
|
||||
}
|
||||
|
||||
func setNoArgEventListener[V View](properties Properties, tag PropertyName, value any) []PropertyName {
|
||||
if listeners, ok := valueToNoArgEventListeners[V](value); ok {
|
||||
if len(listeners) > 0 {
|
||||
properties.setRaw(tag, listeners)
|
||||
} else if properties.getRaw(tag) != nil {
|
||||
properties.setRaw(tag, nil)
|
||||
} else {
|
||||
return []PropertyName{}
|
||||
}
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
notCompatibleType(tag, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func setOneArgEventListener[V View, T any](properties Properties, tag PropertyName, value any) []PropertyName {
|
||||
if listeners, ok := valueToOneArgEventListeners[V, T](value); ok {
|
||||
if len(listeners) > 0 {
|
||||
properties.setRaw(tag, listeners)
|
||||
} else if properties.getRaw(tag) != nil {
|
||||
properties.setRaw(tag, nil)
|
||||
} else {
|
||||
return []PropertyName{}
|
||||
}
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
notCompatibleType(tag, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func setTwoArgEventListener[V View, T any](properties Properties, tag PropertyName, value any) []PropertyName {
|
||||
listeners, ok := valueToTwoArgEventListeners[V, T](value)
|
||||
if !ok {
|
||||
notCompatibleType(tag, value)
|
||||
return nil
|
||||
} else if len(listeners) > 0 {
|
||||
properties.setRaw(tag, listeners)
|
||||
} else if properties.getRaw(tag) != nil {
|
||||
properties.setRaw(tag, nil)
|
||||
} else {
|
||||
return []PropertyName{}
|
||||
}
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
||||
func viewEventsHtml[T any](view View, events []PropertyName, buffer *strings.Builder) {
|
||||
for _, tag := range events {
|
||||
if value := view.getRaw(tag); value != nil {
|
||||
if js, ok := eventJsFunc[tag]; ok {
|
||||
if listeners, ok := value.([]func(View, T)); ok && len(listeners) > 0 {
|
||||
buffer.WriteString(js.jsEvent)
|
||||
buffer.WriteString(`="`)
|
||||
buffer.WriteString(js.jsFunc)
|
||||
buffer.WriteString(`(this, event)" `)
|
||||
}
|
||||
if js, ok := eventJsFunc[tag]; ok {
|
||||
if value := getOneArgEventListeners[View, T](view, nil, tag); len(value) > 0 {
|
||||
buffer.WriteString(js.jsEvent)
|
||||
buffer.WriteString(`="`)
|
||||
buffer.WriteString(js.jsFunc)
|
||||
buffer.WriteString(`(this, event)" `)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -518,3 +66,197 @@ func updateEventListenerHtml(view View, tag PropertyName) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
type noArgListener[V View] interface {
|
||||
Run(V)
|
||||
rawListener() any
|
||||
}
|
||||
|
||||
type noArgListener0[V View] struct {
|
||||
fn func()
|
||||
}
|
||||
|
||||
type noArgListenerV[V View] struct {
|
||||
fn func(V)
|
||||
}
|
||||
|
||||
type noArgListenerBinding[V View] struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func newNoArgListener0[V View](fn func()) noArgListener[V] {
|
||||
obj := new(noArgListener0[V])
|
||||
obj.fn = fn
|
||||
return obj
|
||||
}
|
||||
|
||||
func (data *noArgListener0[V]) Run(_ V) {
|
||||
data.fn()
|
||||
}
|
||||
|
||||
func (data *noArgListener0[V]) rawListener() any {
|
||||
return data.fn
|
||||
}
|
||||
|
||||
func newNoArgListenerV[V View](fn func(V)) noArgListener[V] {
|
||||
obj := new(noArgListenerV[V])
|
||||
obj.fn = fn
|
||||
return obj
|
||||
}
|
||||
|
||||
func (data *noArgListenerV[V]) Run(view V) {
|
||||
data.fn(view)
|
||||
}
|
||||
|
||||
func (data *noArgListenerV[V]) rawListener() any {
|
||||
return data.fn
|
||||
}
|
||||
|
||||
func newNoArgListenerBinding[V View](name string) noArgListener[V] {
|
||||
obj := new(noArgListenerBinding[V])
|
||||
obj.name = name
|
||||
return obj
|
||||
}
|
||||
|
||||
func (data *noArgListenerBinding[V]) Run(view V) {
|
||||
bind := view.binding()
|
||||
if bind == nil {
|
||||
ErrorLogF(`There is no a binding object for call "%s"`, data.name)
|
||||
return
|
||||
}
|
||||
|
||||
val := reflect.ValueOf(bind)
|
||||
method := val.MethodByName(data.name)
|
||||
if !method.IsValid() {
|
||||
ErrorLogF(`The "%s" method is not valid`, data.name)
|
||||
return
|
||||
}
|
||||
|
||||
methodType := method.Type()
|
||||
var args []reflect.Value = nil
|
||||
switch methodType.NumIn() {
|
||||
case 0:
|
||||
args = []reflect.Value{}
|
||||
|
||||
case 1:
|
||||
if equalType(methodType.In(0), reflect.TypeOf(view)) {
|
||||
args = []reflect.Value{reflect.ValueOf(view)}
|
||||
}
|
||||
}
|
||||
|
||||
if args != nil {
|
||||
method.Call(args)
|
||||
} else {
|
||||
ErrorLogF(`Unsupported prototype of "%s" method`, data.name)
|
||||
}
|
||||
}
|
||||
|
||||
func equalType(inType reflect.Type, argType reflect.Type) bool {
|
||||
return inType == argType || (inType.Kind() == reflect.Interface && argType.Implements(inType))
|
||||
}
|
||||
|
||||
func (data *noArgListenerBinding[V]) rawListener() any {
|
||||
return data.name
|
||||
}
|
||||
|
||||
func valueToNoArgEventListeners[V View](value any) ([]noArgListener[V], bool) {
|
||||
if value == nil {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case []noArgListener[V]:
|
||||
return value, true
|
||||
|
||||
case noArgListener[V]:
|
||||
return []noArgListener[V]{value}, true
|
||||
|
||||
case string:
|
||||
return []noArgListener[V]{newNoArgListenerBinding[V](value)}, true
|
||||
|
||||
case func(V):
|
||||
return []noArgListener[V]{newNoArgListenerV(value)}, true
|
||||
|
||||
case func():
|
||||
return []noArgListener[V]{newNoArgListener0[V](value)}, true
|
||||
|
||||
case []func(V):
|
||||
result := make([]noArgListener[V], 0, len(value))
|
||||
for _, fn := range value {
|
||||
if fn != nil {
|
||||
result = append(result, newNoArgListenerV(fn))
|
||||
}
|
||||
}
|
||||
return result, len(result) > 0
|
||||
|
||||
case []func():
|
||||
result := make([]noArgListener[V], 0, len(value))
|
||||
for _, fn := range value {
|
||||
if fn != nil {
|
||||
result = append(result, newNoArgListener0[V](fn))
|
||||
}
|
||||
}
|
||||
return result, len(result) > 0
|
||||
|
||||
case []any:
|
||||
result := make([]noArgListener[V], 0, len(value))
|
||||
for _, v := range value {
|
||||
if v != nil {
|
||||
switch v := v.(type) {
|
||||
case func(V):
|
||||
result = append(result, newNoArgListenerV(v))
|
||||
|
||||
case func():
|
||||
result = append(result, newNoArgListener0[V](v))
|
||||
|
||||
case string:
|
||||
result = append(result, newNoArgListenerBinding[V](v))
|
||||
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
}
|
||||
return result, len(result) > 0
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func setNoArgEventListener[V View](view View, tag PropertyName, value any) []PropertyName {
|
||||
if listeners, ok := valueToNoArgEventListeners[V](value); ok {
|
||||
return setArrayPropertyValue(view, tag, listeners)
|
||||
}
|
||||
notCompatibleType(tag, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getNoArgEventListeners[V View](view View, subviewID []string, tag PropertyName) []noArgListener[V] {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
if value := view.Get(tag); value != nil {
|
||||
if result, ok := value.([]noArgListener[V]); ok {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
return []noArgListener[V]{}
|
||||
}
|
||||
|
||||
func getNoArgEventRawListeners[V View](view View, subviewID []string, tag PropertyName) []any {
|
||||
listeners := getNoArgEventListeners[V](view, subviewID, tag)
|
||||
result := make([]any, len(listeners))
|
||||
for i, l := range listeners {
|
||||
result[i] = l.rawListener()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func getNoArgBinding[V View](listeners []noArgListener[V]) string {
|
||||
for _, listener := range listeners {
|
||||
raw := listener.rawListener()
|
||||
if text, ok := raw.(string); ok && text != "" {
|
||||
return text
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
|
|
@ -0,0 +1,271 @@
|
|||
package rui
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type oneArgListener[V View, E any] interface {
|
||||
Run(V, E)
|
||||
rawListener() any
|
||||
}
|
||||
|
||||
type oneArgListener0[V View, E any] struct {
|
||||
fn func()
|
||||
}
|
||||
|
||||
type oneArgListenerV[V View, E any] struct {
|
||||
fn func(V)
|
||||
}
|
||||
|
||||
type oneArgListenerE[V View, E any] struct {
|
||||
fn func(E)
|
||||
}
|
||||
|
||||
type oneArgListenerVE[V View, E any] struct {
|
||||
fn func(V, E)
|
||||
}
|
||||
|
||||
type oneArgListenerBinding[V View, E any] struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func newOneArgListener0[V View, E any](fn func()) oneArgListener[V, E] {
|
||||
obj := new(oneArgListener0[V, E])
|
||||
obj.fn = fn
|
||||
return obj
|
||||
}
|
||||
|
||||
func (data *oneArgListener0[V, E]) Run(_ V, _ E) {
|
||||
data.fn()
|
||||
}
|
||||
|
||||
func (data *oneArgListener0[V, E]) rawListener() any {
|
||||
return data.fn
|
||||
}
|
||||
|
||||
func newOneArgListenerV[V View, E any](fn func(V)) oneArgListener[V, E] {
|
||||
obj := new(oneArgListenerV[V, E])
|
||||
obj.fn = fn
|
||||
return obj
|
||||
}
|
||||
|
||||
func (data *oneArgListenerV[V, E]) Run(view V, _ E) {
|
||||
data.fn(view)
|
||||
}
|
||||
|
||||
func (data *oneArgListenerV[V, E]) rawListener() any {
|
||||
return data.fn
|
||||
}
|
||||
|
||||
func newOneArgListenerE[V View, E any](fn func(E)) oneArgListener[V, E] {
|
||||
obj := new(oneArgListenerE[V, E])
|
||||
obj.fn = fn
|
||||
return obj
|
||||
}
|
||||
|
||||
func (data *oneArgListenerE[V, E]) Run(_ V, event E) {
|
||||
data.fn(event)
|
||||
}
|
||||
|
||||
func (data *oneArgListenerE[V, E]) rawListener() any {
|
||||
return data.fn
|
||||
}
|
||||
|
||||
func newOneArgListenerVE[V View, E any](fn func(V, E)) oneArgListener[V, E] {
|
||||
obj := new(oneArgListenerVE[V, E])
|
||||
obj.fn = fn
|
||||
return obj
|
||||
}
|
||||
|
||||
func (data *oneArgListenerVE[V, E]) Run(view V, arg E) {
|
||||
data.fn(view, arg)
|
||||
}
|
||||
|
||||
func (data *oneArgListenerVE[V, E]) rawListener() any {
|
||||
return data.fn
|
||||
}
|
||||
|
||||
func newOneArgListenerBinding[V View, E any](name string) oneArgListener[V, E] {
|
||||
obj := new(oneArgListenerBinding[V, E])
|
||||
obj.name = name
|
||||
return obj
|
||||
}
|
||||
|
||||
func (data *oneArgListenerBinding[V, E]) Run(view V, event E) {
|
||||
bind := view.binding()
|
||||
if bind == nil {
|
||||
ErrorLogF(`There is no a binding object for call "%s"`, data.name)
|
||||
return
|
||||
}
|
||||
|
||||
val := reflect.ValueOf(bind)
|
||||
method := val.MethodByName(data.name)
|
||||
if !method.IsValid() {
|
||||
ErrorLogF(`The "%s" method is not valid`, data.name)
|
||||
return
|
||||
}
|
||||
|
||||
methodType := method.Type()
|
||||
|
||||
var args []reflect.Value = nil
|
||||
switch methodType.NumIn() {
|
||||
case 0:
|
||||
args = []reflect.Value{}
|
||||
|
||||
case 1:
|
||||
inType := methodType.In(0)
|
||||
if equalType(inType, reflect.TypeOf(event)) {
|
||||
args = []reflect.Value{reflect.ValueOf(event)}
|
||||
} else if equalType(inType, reflect.TypeOf(view)) {
|
||||
args = []reflect.Value{reflect.ValueOf(view)}
|
||||
}
|
||||
|
||||
case 2:
|
||||
if equalType(methodType.In(0), reflect.TypeOf(view)) &&
|
||||
equalType(methodType.In(1), reflect.TypeOf(event)) {
|
||||
args = []reflect.Value{reflect.ValueOf(view), reflect.ValueOf(event)}
|
||||
}
|
||||
}
|
||||
|
||||
if args != nil {
|
||||
method.Call(args)
|
||||
} else {
|
||||
ErrorLogF(`Unsupported prototype of "%s" method`, data.name)
|
||||
}
|
||||
}
|
||||
|
||||
func (data *oneArgListenerBinding[V, E]) rawListener() any {
|
||||
return data.name
|
||||
}
|
||||
|
||||
func valueToOneArgEventListeners[V View, E any](value any) ([]oneArgListener[V, E], bool) {
|
||||
if value == nil {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case []oneArgListener[V, E]:
|
||||
return value, true
|
||||
|
||||
case oneArgListener[V, E]:
|
||||
return []oneArgListener[V, E]{value}, true
|
||||
|
||||
case string:
|
||||
return []oneArgListener[V, E]{newOneArgListenerBinding[V, E](value)}, true
|
||||
|
||||
case func(V, E):
|
||||
return []oneArgListener[V, E]{newOneArgListenerVE(value)}, true
|
||||
|
||||
case func(V):
|
||||
return []oneArgListener[V, E]{newOneArgListenerV[V, E](value)}, true
|
||||
|
||||
case func(E):
|
||||
return []oneArgListener[V, E]{newOneArgListenerE[V](value)}, true
|
||||
|
||||
case func():
|
||||
return []oneArgListener[V, E]{newOneArgListener0[V, E](value)}, true
|
||||
|
||||
case []func(V, E):
|
||||
result := make([]oneArgListener[V, E], 0, len(value))
|
||||
for _, fn := range value {
|
||||
if fn != nil {
|
||||
result = append(result, newOneArgListenerVE(fn))
|
||||
}
|
||||
}
|
||||
return result, len(result) > 0
|
||||
|
||||
case []func(E):
|
||||
result := make([]oneArgListener[V, E], 0, len(value))
|
||||
for _, fn := range value {
|
||||
if fn != nil {
|
||||
result = append(result, newOneArgListenerE[V](fn))
|
||||
}
|
||||
}
|
||||
return result, len(result) > 0
|
||||
|
||||
case []func(V):
|
||||
result := make([]oneArgListener[V, E], 0, len(value))
|
||||
for _, fn := range value {
|
||||
if fn != nil {
|
||||
result = append(result, newOneArgListenerV[V, E](fn))
|
||||
}
|
||||
}
|
||||
return result, len(result) > 0
|
||||
|
||||
case []func():
|
||||
result := make([]oneArgListener[V, E], 0, len(value))
|
||||
for _, fn := range value {
|
||||
if fn != nil {
|
||||
result = append(result, newOneArgListener0[V, E](fn))
|
||||
}
|
||||
}
|
||||
return result, len(result) > 0
|
||||
|
||||
case []any:
|
||||
result := make([]oneArgListener[V, E], 0, len(value))
|
||||
for _, v := range value {
|
||||
if v != nil {
|
||||
switch v := v.(type) {
|
||||
case func(V, E):
|
||||
result = append(result, newOneArgListenerVE(v))
|
||||
|
||||
case func(E):
|
||||
result = append(result, newOneArgListenerE[V](v))
|
||||
|
||||
case func(V):
|
||||
result = append(result, newOneArgListenerV[V, E](v))
|
||||
|
||||
case func():
|
||||
result = append(result, newOneArgListener0[V, E](v))
|
||||
|
||||
case string:
|
||||
result = append(result, newOneArgListenerBinding[V, E](v))
|
||||
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
}
|
||||
return result, len(result) > 0
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func setOneArgEventListener[V View, T any](view View, tag PropertyName, value any) []PropertyName {
|
||||
if listeners, ok := valueToOneArgEventListeners[V, T](value); ok {
|
||||
return setArrayPropertyValue(view, tag, listeners)
|
||||
}
|
||||
notCompatibleType(tag, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getOneArgEventListeners[V View, E any](view View, subviewID []string, tag PropertyName) []oneArgListener[V, E] {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
if value := view.Get(tag); value != nil {
|
||||
if result, ok := value.([]oneArgListener[V, E]); ok {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
return []oneArgListener[V, E]{}
|
||||
}
|
||||
|
||||
func getOneArgEventRawListeners[V View, E any](view View, subviewID []string, tag PropertyName) []any {
|
||||
listeners := getOneArgEventListeners[V, E](view, subviewID, tag)
|
||||
result := make([]any, len(listeners))
|
||||
for i, l := range listeners {
|
||||
result[i] = l.rawListener()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func getOneArgBinding[V View, E any](listeners []oneArgListener[V, E]) string {
|
||||
for _, listener := range listeners {
|
||||
raw := listener.rawListener()
|
||||
if text, ok := raw.(string); ok && text != "" {
|
||||
return text
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
|
@ -0,0 +1,345 @@
|
|||
package rui
|
||||
|
||||
import "reflect"
|
||||
|
||||
type twoArgListener[V View, E any] interface {
|
||||
Run(V, E, E)
|
||||
rawListener() any
|
||||
}
|
||||
|
||||
type twoArgListener0[V View, E any] struct {
|
||||
fn func()
|
||||
}
|
||||
|
||||
type twoArgListenerV[V View, E any] struct {
|
||||
fn func(V)
|
||||
}
|
||||
|
||||
type twoArgListenerE[V View, E any] struct {
|
||||
fn func(E)
|
||||
}
|
||||
|
||||
type twoArgListenerVE[V View, E any] struct {
|
||||
fn func(V, E)
|
||||
}
|
||||
|
||||
type twoArgListenerEE[V View, E any] struct {
|
||||
fn func(E, E)
|
||||
}
|
||||
|
||||
type twoArgListenerVEE[V View, E any] struct {
|
||||
fn func(V, E, E)
|
||||
}
|
||||
|
||||
type twoArgListenerBinding[V View, E any] struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func newTwoArgListener0[V View, E any](fn func()) twoArgListener[V, E] {
|
||||
obj := new(twoArgListener0[V, E])
|
||||
obj.fn = fn
|
||||
return obj
|
||||
}
|
||||
|
||||
func (data *twoArgListener0[V, E]) Run(_ V, _ E, _ E) {
|
||||
data.fn()
|
||||
}
|
||||
|
||||
func (data *twoArgListener0[V, E]) rawListener() any {
|
||||
return data.fn
|
||||
}
|
||||
|
||||
func newTwoArgListenerV[V View, E any](fn func(V)) twoArgListener[V, E] {
|
||||
obj := new(twoArgListenerV[V, E])
|
||||
obj.fn = fn
|
||||
return obj
|
||||
}
|
||||
|
||||
func (data *twoArgListenerV[V, E]) Run(view V, _ E, _ E) {
|
||||
data.fn(view)
|
||||
}
|
||||
|
||||
func (data *twoArgListenerV[V, E]) rawListener() any {
|
||||
return data.fn
|
||||
}
|
||||
|
||||
func newTwoArgListenerE[V View, E any](fn func(E)) twoArgListener[V, E] {
|
||||
obj := new(twoArgListenerE[V, E])
|
||||
obj.fn = fn
|
||||
return obj
|
||||
}
|
||||
|
||||
func (data *twoArgListenerE[V, E]) Run(_ V, arg E, _ E) {
|
||||
data.fn(arg)
|
||||
}
|
||||
|
||||
func (data *twoArgListenerE[V, E]) rawListener() any {
|
||||
return data.fn
|
||||
}
|
||||
|
||||
func newTwoArgListenerVE[V View, E any](fn func(V, E)) twoArgListener[V, E] {
|
||||
obj := new(twoArgListenerVE[V, E])
|
||||
obj.fn = fn
|
||||
return obj
|
||||
}
|
||||
|
||||
func (data *twoArgListenerVE[V, E]) Run(view V, arg E, _ E) {
|
||||
data.fn(view, arg)
|
||||
}
|
||||
|
||||
func (data *twoArgListenerVE[V, E]) rawListener() any {
|
||||
return data.fn
|
||||
}
|
||||
|
||||
func newTwoArgListenerEE[V View, E any](fn func(E, E)) twoArgListener[V, E] {
|
||||
obj := new(twoArgListenerEE[V, E])
|
||||
obj.fn = fn
|
||||
return obj
|
||||
}
|
||||
|
||||
func (data *twoArgListenerEE[V, E]) Run(_ V, arg1 E, arg2 E) {
|
||||
data.fn(arg1, arg2)
|
||||
}
|
||||
|
||||
func (data *twoArgListenerEE[V, E]) rawListener() any {
|
||||
return data.fn
|
||||
}
|
||||
|
||||
func newTwoArgListenerVEE[V View, E any](fn func(V, E, E)) twoArgListener[V, E] {
|
||||
obj := new(twoArgListenerVEE[V, E])
|
||||
obj.fn = fn
|
||||
return obj
|
||||
}
|
||||
|
||||
func (data *twoArgListenerVEE[V, E]) Run(view V, arg1 E, arg2 E) {
|
||||
data.fn(view, arg1, arg2)
|
||||
}
|
||||
|
||||
func (data *twoArgListenerVEE[V, E]) rawListener() any {
|
||||
return data.fn
|
||||
}
|
||||
|
||||
func newTwoArgListenerBinding[V View, E any](name string) twoArgListener[V, E] {
|
||||
obj := new(twoArgListenerBinding[V, E])
|
||||
obj.name = name
|
||||
return obj
|
||||
}
|
||||
|
||||
func (data *twoArgListenerBinding[V, E]) Run(view V, arg1 E, arg2 E) {
|
||||
bind := view.binding()
|
||||
if bind == nil {
|
||||
ErrorLogF(`There is no a binding object for call "%s"`, data.name)
|
||||
return
|
||||
}
|
||||
|
||||
val := reflect.ValueOf(bind)
|
||||
method := val.MethodByName(data.name)
|
||||
if !method.IsValid() {
|
||||
ErrorLogF(`The "%s" method is not valid`, data.name)
|
||||
return
|
||||
}
|
||||
|
||||
methodType := method.Type()
|
||||
|
||||
var args []reflect.Value = nil
|
||||
switch methodType.NumIn() {
|
||||
case 0:
|
||||
args = []reflect.Value{}
|
||||
|
||||
case 1:
|
||||
inType := methodType.In(0)
|
||||
if equalType(inType, reflect.TypeOf(arg1)) {
|
||||
args = []reflect.Value{reflect.ValueOf(arg1)}
|
||||
} else if equalType(inType, reflect.TypeOf(view)) {
|
||||
args = []reflect.Value{reflect.ValueOf(view)}
|
||||
}
|
||||
|
||||
case 2:
|
||||
inType0 := methodType.In(0)
|
||||
inType1 := methodType.In(1)
|
||||
if equalType(inType0, reflect.TypeOf(view)) && equalType(inType1, reflect.TypeOf(arg1)) {
|
||||
args = []reflect.Value{reflect.ValueOf(view), reflect.ValueOf(arg1)}
|
||||
} else if equalType(inType0, reflect.TypeOf(arg1)) && equalType(inType1, reflect.TypeOf(arg2)) {
|
||||
args = []reflect.Value{reflect.ValueOf(arg1), reflect.ValueOf(arg2)}
|
||||
}
|
||||
|
||||
case 3:
|
||||
if equalType(methodType.In(0), reflect.TypeOf(view)) &&
|
||||
equalType(methodType.In(1), reflect.TypeOf(arg1)) &&
|
||||
equalType(methodType.In(2), reflect.TypeOf(arg2)) {
|
||||
args = []reflect.Value{reflect.ValueOf(view), reflect.ValueOf(arg1), reflect.ValueOf(arg2)}
|
||||
}
|
||||
}
|
||||
|
||||
if args != nil {
|
||||
method.Call(args)
|
||||
} else {
|
||||
ErrorLogF(`Unsupported prototype of "%s" method`, data.name)
|
||||
}
|
||||
}
|
||||
|
||||
func (data *twoArgListenerBinding[V, E]) rawListener() any {
|
||||
return data.name
|
||||
}
|
||||
|
||||
func valueToTwoArgEventListeners[V View, E any](value any) ([]twoArgListener[V, E], bool) {
|
||||
if value == nil {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case []twoArgListener[V, E]:
|
||||
return value, true
|
||||
|
||||
case twoArgListener[V, E]:
|
||||
return []twoArgListener[V, E]{value}, true
|
||||
|
||||
case string:
|
||||
return []twoArgListener[V, E]{newTwoArgListenerBinding[V, E](value)}, true
|
||||
|
||||
case func(V, E):
|
||||
return []twoArgListener[V, E]{newTwoArgListenerVE(value)}, true
|
||||
|
||||
case func(V):
|
||||
return []twoArgListener[V, E]{newTwoArgListenerV[V, E](value)}, true
|
||||
|
||||
case func(E):
|
||||
return []twoArgListener[V, E]{newTwoArgListenerE[V](value)}, true
|
||||
|
||||
case func():
|
||||
return []twoArgListener[V, E]{newTwoArgListener0[V, E](value)}, true
|
||||
|
||||
case func(E, E):
|
||||
return []twoArgListener[V, E]{newTwoArgListenerEE[V](value)}, true
|
||||
|
||||
case func(V, E, E):
|
||||
return []twoArgListener[V, E]{newTwoArgListenerVEE(value)}, true
|
||||
|
||||
case []func(V, E):
|
||||
result := make([]twoArgListener[V, E], 0, len(value))
|
||||
for _, fn := range value {
|
||||
if fn != nil {
|
||||
result = append(result, newTwoArgListenerVE(fn))
|
||||
}
|
||||
}
|
||||
return result, len(result) > 0
|
||||
|
||||
case []func(E):
|
||||
result := make([]twoArgListener[V, E], 0, len(value))
|
||||
for _, fn := range value {
|
||||
if fn != nil {
|
||||
result = append(result, newTwoArgListenerE[V](fn))
|
||||
}
|
||||
}
|
||||
return result, len(result) > 0
|
||||
|
||||
case []func(V):
|
||||
result := make([]twoArgListener[V, E], 0, len(value))
|
||||
for _, fn := range value {
|
||||
if fn != nil {
|
||||
result = append(result, newTwoArgListenerV[V, E](fn))
|
||||
}
|
||||
}
|
||||
return result, len(result) > 0
|
||||
|
||||
case []func():
|
||||
result := make([]twoArgListener[V, E], 0, len(value))
|
||||
for _, fn := range value {
|
||||
if fn != nil {
|
||||
result = append(result, newTwoArgListener0[V, E](fn))
|
||||
}
|
||||
}
|
||||
return result, len(result) > 0
|
||||
|
||||
case []func(E, E):
|
||||
result := make([]twoArgListener[V, E], 0, len(value))
|
||||
for _, fn := range value {
|
||||
if fn != nil {
|
||||
result = append(result, newTwoArgListenerEE[V](fn))
|
||||
}
|
||||
}
|
||||
return result, len(result) > 0
|
||||
|
||||
case []func(V, E, E):
|
||||
result := make([]twoArgListener[V, E], 0, len(value))
|
||||
for _, fn := range value {
|
||||
if fn != nil {
|
||||
result = append(result, newTwoArgListenerVEE(fn))
|
||||
}
|
||||
}
|
||||
return result, len(result) > 0
|
||||
|
||||
case []any:
|
||||
result := make([]twoArgListener[V, E], 0, len(value))
|
||||
for _, v := range value {
|
||||
if v != nil {
|
||||
switch v := v.(type) {
|
||||
case func(V, E):
|
||||
result = append(result, newTwoArgListenerVE(v))
|
||||
|
||||
case func(E):
|
||||
result = append(result, newTwoArgListenerE[V](v))
|
||||
|
||||
case func(V):
|
||||
result = append(result, newTwoArgListenerV[V, E](v))
|
||||
|
||||
case func():
|
||||
result = append(result, newTwoArgListener0[V, E](v))
|
||||
|
||||
case func(E, E):
|
||||
result = append(result, newTwoArgListenerEE[V](v))
|
||||
|
||||
case func(V, E, E):
|
||||
result = append(result, newTwoArgListenerVEE(v))
|
||||
|
||||
case string:
|
||||
result = append(result, newTwoArgListenerBinding[V, E](v))
|
||||
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
}
|
||||
return result, len(result) > 0
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func setTwoArgEventListener[V View, T any](view View, tag PropertyName, value any) []PropertyName {
|
||||
if listeners, ok := valueToTwoArgEventListeners[V, T](value); ok {
|
||||
return setArrayPropertyValue(view, tag, listeners)
|
||||
}
|
||||
notCompatibleType(tag, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getTwoArgEventListeners[V View, E any](view View, subviewID []string, tag PropertyName) []twoArgListener[V, E] {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
if value := view.Get(tag); value != nil {
|
||||
if result, ok := value.([]twoArgListener[V, E]); ok {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
return []twoArgListener[V, E]{}
|
||||
}
|
||||
|
||||
func getTwoArgEventRawListeners[V View, E any](view View, subviewID []string, tag PropertyName) []any {
|
||||
listeners := getTwoArgEventListeners[V, E](view, subviewID, tag)
|
||||
result := make([]any, len(listeners))
|
||||
for i, l := range listeners {
|
||||
result[i] = l.rawListener()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func getTwoArgBinding[V View, E any](listeners []twoArgListener[V, E]) string {
|
||||
for _, listener := range listeners {
|
||||
raw := listener.rawListener()
|
||||
if text, ok := raw.(string); ok && text != "" {
|
||||
return text
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
|
@ -147,7 +147,7 @@ func (picker *filePickerData) Files() []FileInfo {
|
|||
func (picker *filePickerData) LoadFile(file FileInfo, result func(FileInfo, []byte)) {
|
||||
if result != nil {
|
||||
for i, info := range picker.files {
|
||||
if info.Name == file.Name && info.Size == file.Size && info.LastModified == file.LastModified {
|
||||
if info.Name == file.Name && info.Size == file.Size && info.LastModified.Equal(file.LastModified) {
|
||||
if info.data != nil {
|
||||
result(info, info.data)
|
||||
} else {
|
||||
|
@ -282,8 +282,8 @@ func (picker *filePickerData) handleCommand(self View, command PropertyName, dat
|
|||
case "fileSelected":
|
||||
if files := parseFilesTag(data); files != nil {
|
||||
picker.files = files
|
||||
for _, listener := range GetFileSelectedListeners(picker) {
|
||||
listener(picker, files)
|
||||
for _, listener := range getOneArgEventListeners[FilePicker, []FileInfo](picker, nil, FileSelectedEvent) {
|
||||
listener.Run(picker, files)
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
@ -317,13 +317,17 @@ func LoadFilePickerFile(view View, subviewID string, file FileInfo, result func(
|
|||
}
|
||||
|
||||
// IsMultipleFilePicker returns "true" if multiple files can be selected in the FilePicker, "false" otherwise.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func IsMultipleFilePicker(view View, subviewID ...string) bool {
|
||||
return boolStyledProperty(view, subviewID, Multiple, false)
|
||||
}
|
||||
|
||||
// GetFilePickerAccept returns sets the list of allowed file extensions or MIME types.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetFilePickerAccept(view View, subviewID ...string) []string {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
accept, ok := stringProperty(view, Accept, view.Session())
|
||||
|
@ -345,7 +349,16 @@ func GetFilePickerAccept(view View, subviewID ...string) []string {
|
|||
|
||||
// GetFileSelectedListeners returns the "file-selected-event" listener list.
|
||||
// If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetFileSelectedListeners(view View, subviewID ...string) []func(FilePicker, []FileInfo) {
|
||||
return getOneArgEventListeners[FilePicker, []FileInfo](view, subviewID, FileSelectedEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.View, []rui.FileInfo),
|
||||
// - func(rui.View),
|
||||
// - func([]rui.FileInfo),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetFileSelectedListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[FilePicker, []FileInfo](view, subviewID, FileSelectedEvent)
|
||||
}
|
||||
|
|
|
@ -49,13 +49,27 @@ func focusEventsHtml(view View, buffer *strings.Builder) {
|
|||
}
|
||||
|
||||
// GetFocusListeners returns a FocusListener list. If there are no listeners then the empty list is returned
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetFocusListeners(view View, subviewID ...string) []func(View) {
|
||||
return getNoArgEventListeners[View](view, subviewID, FocusEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.View),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetFocusListeners(view View, subviewID ...string) []any {
|
||||
return getNoArgEventRawListeners[View](view, subviewID, FocusEvent)
|
||||
}
|
||||
|
||||
// GetLostFocusListeners returns a LostFocusListener list. If there are no listeners then the empty list is returned
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetLostFocusListeners(view View, subviewID ...string) []func(View) {
|
||||
return getNoArgEventListeners[View](view, subviewID, LostFocusEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.View),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetLostFocusListeners(view View, subviewID ...string) []any {
|
||||
return getNoArgEventRawListeners[View](view, subviewID, LostFocusEvent)
|
||||
}
|
||||
|
|
|
@ -458,10 +458,7 @@ func (gridLayout *gridLayoutData) UpdateGridContent() {
|
|||
if gridLayout.created {
|
||||
updateInnerHTML(gridLayout.htmlID(), gridLayout.session)
|
||||
}
|
||||
|
||||
if listener, ok := gridLayout.changeListener[Content]; ok {
|
||||
listener(gridLayout, Content)
|
||||
}
|
||||
gridLayout.contentChanged()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -506,26 +503,34 @@ func gridCellSizes(properties Properties, tag PropertyName, session Session) []S
|
|||
}
|
||||
|
||||
// GetCellVerticalAlign returns the vertical align of a GridLayout cell content: TopAlign (0), BottomAlign (1), CenterAlign (2), StretchAlign (3)
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetCellVerticalAlign(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, CellVerticalAlign, StretchAlign, false)
|
||||
}
|
||||
|
||||
// GetCellHorizontalAlign returns the vertical align of a GridLayout cell content: LeftAlign (0), RightAlign (1), CenterAlign (2), StretchAlign (3)
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetCellHorizontalAlign(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, CellHorizontalAlign, StretchAlign, false)
|
||||
}
|
||||
|
||||
// GetGridAutoFlow returns the value of the "grid-auto-flow" property
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetGridAutoFlow(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, GridAutoFlow, 0, false)
|
||||
}
|
||||
|
||||
// GetCellWidth returns the width of a GridLayout cell. If the result is an empty array, then the width is not set.
|
||||
// If the result is a single value array, then the width of all cell is equal.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetCellWidth(view View, subviewID ...string) []SizeUnit {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
return gridCellSizes(view, CellWidth, view.Session())
|
||||
|
@ -535,7 +540,9 @@ func GetCellWidth(view View, subviewID ...string) []SizeUnit {
|
|||
|
||||
// GetCellHeight returns the height of a GridLayout cell. If the result is an empty array, then the height is not set.
|
||||
// If the result is a single value array, then the height of all cell is equal.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetCellHeight(view View, subviewID ...string) []SizeUnit {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
return gridCellSizes(view, CellHeight, view.Session())
|
||||
|
@ -544,13 +551,17 @@ func GetCellHeight(view View, subviewID ...string) []SizeUnit {
|
|||
}
|
||||
|
||||
// GetGridRowGap returns the gap between GridLayout rows.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetGridRowGap(view View, subviewID ...string) SizeUnit {
|
||||
return sizeStyledProperty(view, subviewID, GridRowGap, false)
|
||||
}
|
||||
|
||||
// GetGridColumnGap returns the gap between GridLayout columns.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetGridColumnGap(view View, subviewID ...string) SizeUnit {
|
||||
return sizeStyledProperty(view, subviewID, GridColumnGap, false)
|
||||
}
|
||||
|
|
24
image.go
24
image.go
|
@ -4,34 +4,44 @@ import (
|
|||
"strconv"
|
||||
)
|
||||
|
||||
// ImageLoadingStatus defines type of status of the image loading
|
||||
type ImageLoadingStatus int
|
||||
|
||||
// Constants which represent return values of the LoadingStatus function of an [Image] view
|
||||
const (
|
||||
// ImageLoading is the image loading status: in the process of loading
|
||||
ImageLoading = 0
|
||||
ImageLoading ImageLoadingStatus = 0
|
||||
// ImageReady is the image loading status: the image is loaded successfully
|
||||
ImageReady = 1
|
||||
ImageReady ImageLoadingStatus = 1
|
||||
// ImageLoadingError is the image loading status: an error occurred while loading
|
||||
ImageLoadingError = 2
|
||||
ImageLoadingError ImageLoadingStatus = 2
|
||||
)
|
||||
|
||||
// Image defines the image that is used for drawing operations on the Canvas.
|
||||
type Image interface {
|
||||
// URL returns the url of the image
|
||||
URL() string
|
||||
// LoadingStatus returns the status of the image loading: ImageLoading (0), ImageReady (1), ImageLoadingError (2)
|
||||
LoadingStatus() int
|
||||
|
||||
// LoadingStatus returns the status of the image loading:
|
||||
// - ImageLoading (0) - in the process of loading;
|
||||
// - ImageReady (1) - the image is loaded successfully;
|
||||
// - ImageLoadingError (2) - an error occurred while loading.
|
||||
LoadingStatus() ImageLoadingStatus
|
||||
|
||||
// LoadingError: if LoadingStatus() == ImageLoadingError then returns the error text, "" otherwise
|
||||
LoadingError() string
|
||||
setLoadingError(err string)
|
||||
|
||||
// Width returns the width of the image in pixels. While LoadingStatus() != ImageReady returns 0
|
||||
Width() float64
|
||||
|
||||
// Height returns the height of the image in pixels. While LoadingStatus() != ImageReady returns 0
|
||||
Height() float64
|
||||
}
|
||||
|
||||
type imageData struct {
|
||||
url string
|
||||
loadingStatus int
|
||||
loadingStatus ImageLoadingStatus
|
||||
loadingError string
|
||||
width, height float64
|
||||
listener func(Image)
|
||||
|
@ -45,7 +55,7 @@ func (image *imageData) URL() string {
|
|||
return image.url
|
||||
}
|
||||
|
||||
func (image *imageData) LoadingStatus() int {
|
||||
func (image *imageData) LoadingStatus() ImageLoadingStatus {
|
||||
return image.loadingStatus
|
||||
}
|
||||
|
||||
|
|
34
imageView.go
34
imageView.go
|
@ -205,7 +205,7 @@ func imageViewSrcSet(view View, path string) string {
|
|||
buffer.WriteString(", ")
|
||||
}
|
||||
buffer.WriteString(src.path)
|
||||
buffer.WriteString(fmt.Sprintf(" %gx", src.scale))
|
||||
fmt.Fprintf(buffer, " %gx", src.scale)
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
@ -300,7 +300,7 @@ func (imageView *imageViewData) handleCommand(self View, command PropertyName, d
|
|||
switch command {
|
||||
case "imageViewError":
|
||||
for _, listener := range getNoArgEventListeners[ImageView](imageView, nil, ErrorEvent) {
|
||||
listener(imageView)
|
||||
listener.Run(imageView)
|
||||
}
|
||||
|
||||
case "imageViewLoaded":
|
||||
|
@ -309,7 +309,7 @@ func (imageView *imageViewData) handleCommand(self View, command PropertyName, d
|
|||
imageView.currentSrc, _ = data.PropertyValue("current-src")
|
||||
|
||||
for _, listener := range getNoArgEventListeners[ImageView](imageView, nil, LoadedEvent) {
|
||||
listener(imageView)
|
||||
listener.Run(imageView)
|
||||
}
|
||||
|
||||
default:
|
||||
|
@ -370,3 +370,31 @@ func GetImageViewVerticalAlign(view View, subviewID ...string) int {
|
|||
func GetImageViewHorizontalAlign(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, ImageHorizontalAlign, LeftAlign, false)
|
||||
}
|
||||
|
||||
// GetImageViewErrorEventListeners returns the list of "error-event" event listeners.
|
||||
// If there are no listeners then the empty list is returned
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.ImageView)
|
||||
// - func()
|
||||
// - string
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetImageViewErrorEventListeners(view View, subviewID ...string) []any {
|
||||
return getNoArgEventRawListeners[View](view, subviewID, ErrorEvent)
|
||||
}
|
||||
|
||||
// GetImageViewLoadedEventListeners returns the list of "loaded-event" event listeners.
|
||||
// If there are no listeners then the empty list is returned
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.ImageView)
|
||||
// - func()
|
||||
// - string
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetImageViewLoadedEventListeners(view View, subviewID ...string) []any {
|
||||
return getNoArgEventRawListeners[View](view, subviewID, LoadedEvent)
|
||||
}
|
||||
|
|
44
keyEvents.go
44
keyEvents.go
|
@ -434,15 +434,13 @@ func (event *KeyEvent) init(data DataObject) {
|
|||
}
|
||||
|
||||
func keyEventsHtml(view View, buffer *strings.Builder) {
|
||||
if len(getOneArgEventListeners[View, KeyEvent](view, nil, KeyDownEvent)) > 0 {
|
||||
if len(getOneArgEventListeners[View, KeyEvent](view, nil, KeyDownEvent)) > 0 ||
|
||||
(view.Focusable() && len(getOneArgEventListeners[View, MouseEvent](view, nil, ClickEvent)) > 0) {
|
||||
|
||||
buffer.WriteString(`onkeydown="keyDownEvent(this, event)" `)
|
||||
} else if view.Focusable() {
|
||||
if len(getOneArgEventListeners[View, MouseEvent](view, nil, ClickEvent)) > 0 {
|
||||
buffer.WriteString(`onkeydown="keyDownEvent(this, event)" `)
|
||||
}
|
||||
}
|
||||
|
||||
if listeners := getOneArgEventListeners[View, KeyEvent](view, nil, KeyUpEvent); len(listeners) > 0 {
|
||||
if len(getOneArgEventListeners[View, KeyEvent](view, nil, KeyUpEvent)) > 0 {
|
||||
buffer.WriteString(`onkeyup="keyUpEvent(this, event)" `)
|
||||
}
|
||||
}
|
||||
|
@ -454,7 +452,7 @@ func handleKeyEvents(view View, tag PropertyName, data DataObject) {
|
|||
|
||||
if len(listeners) > 0 {
|
||||
for _, listener := range listeners {
|
||||
listener(view, event)
|
||||
listener.Run(view, event)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -477,20 +475,38 @@ func handleKeyEvents(view View, tag PropertyName, data DataObject) {
|
|||
ScreenY: view.Frame().Top + view.Frame().Height/2,
|
||||
}
|
||||
for _, listener := range listeners {
|
||||
listener(view, clickEvent)
|
||||
listener.Run(view, clickEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetKeyDownListeners returns the "key-down-event" listener list. If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetKeyDownListeners(view View, subviewID ...string) []func(View, KeyEvent) {
|
||||
return getOneArgEventListeners[View, KeyEvent](view, subviewID, KeyDownEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.View, rui.KeyEvent),
|
||||
// - func(rui.View),
|
||||
// - func(rui.KeyEvent),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetKeyDownListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, KeyEvent](view, subviewID, KeyDownEvent)
|
||||
}
|
||||
|
||||
// GetKeyUpListeners returns the "key-up-event" listener list. If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetKeyUpListeners(view View, subviewID ...string) []func(View, KeyEvent) {
|
||||
return getOneArgEventListeners[View, KeyEvent](view, subviewID, KeyUpEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.View, rui.KeyEvent),
|
||||
// - func(rui.View),
|
||||
// - func(rui.KeyEvent),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetKeyUpListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, KeyEvent](view, subviewID, KeyUpEvent)
|
||||
}
|
||||
|
|
|
@ -196,30 +196,33 @@ func (listLayout *listLayoutData) UpdateContent() {
|
|||
if listLayout.created {
|
||||
updateInnerHTML(listLayout.htmlID(), listLayout.session)
|
||||
}
|
||||
|
||||
if listener, ok := listLayout.changeListener[Content]; ok {
|
||||
listener(listLayout, Content)
|
||||
}
|
||||
listLayout.contentChanged()
|
||||
}
|
||||
}
|
||||
|
||||
// GetListVerticalAlign returns the vertical align of a ListLayout or ListView sibview:
|
||||
// GetListVerticalAlign returns the vertical align of a ListLayout or ListView subview:
|
||||
// TopAlign (0), BottomAlign (1), CenterAlign (2), or StretchAlign (3)
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetListVerticalAlign(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, VerticalAlign, TopAlign, false)
|
||||
}
|
||||
|
||||
// GetListHorizontalAlign returns the vertical align of a ListLayout or ListView subview:
|
||||
// LeftAlign (0), RightAlign (1), CenterAlign (2), or StretchAlign (3)
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetListHorizontalAlign(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, HorizontalAlign, LeftAlign, false)
|
||||
}
|
||||
|
||||
// GetListOrientation returns the orientation of a ListLayout or ListView subview:
|
||||
// TopDownOrientation (0), StartToEndOrientation (1), BottomUpOrientation (2), or EndToStartOrientation (3)
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetListOrientation(view View, subviewID ...string) int {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
if orientation, ok := valueToOrientation(view.Get(Orientation), view.Session()); ok {
|
||||
|
@ -238,19 +241,25 @@ func GetListOrientation(view View, subviewID ...string) int {
|
|||
|
||||
// GetListWrap returns the wrap type of a ListLayout or ListView subview:
|
||||
// ListWrapOff (0), ListWrapOn (1), or ListWrapReverse (2)
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetListWrap(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, ListWrap, ListWrapOff, false)
|
||||
}
|
||||
|
||||
// GetListRowGap returns the gap between ListLayout or ListView rows.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetListRowGap(view View, subviewID ...string) SizeUnit {
|
||||
return sizeStyledProperty(view, subviewID, ListRowGap, false)
|
||||
}
|
||||
|
||||
// GetListColumnGap returns the gap between ListLayout or ListView columns.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetListColumnGap(view View, subviewID ...string) SizeUnit {
|
||||
return sizeStyledProperty(view, subviewID, ListColumnGap, false)
|
||||
}
|
||||
|
|
121
listView.go
121
listView.go
|
@ -122,19 +122,13 @@ type ListView interface {
|
|||
// ReloadListViewData updates ListView content
|
||||
ReloadListViewData()
|
||||
|
||||
//getCheckedItems() []int
|
||||
getItemFrames() []Frame
|
||||
}
|
||||
|
||||
type listViewData struct {
|
||||
viewData
|
||||
//adapter ListAdapter
|
||||
//clickedListeners []func(ListView, int)
|
||||
//selectedListeners []func(ListView, int)
|
||||
//checkedListeners []func(ListView, []int)
|
||||
items []View
|
||||
itemFrame []Frame
|
||||
//checkedItem []int
|
||||
}
|
||||
|
||||
// NewListView creates the new list view
|
||||
|
@ -279,7 +273,7 @@ func (listView *listViewData) propertyChanged(tag PropertyName) {
|
|||
if listeners := getOneArgEventListeners[ListView, int](listView, nil, ListItemSelectedEvent); len(listeners) > 0 {
|
||||
current := GetCurrent(listView)
|
||||
for _, listener := range listeners {
|
||||
listener(listView, current)
|
||||
listener.Run(listView, current)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -288,7 +282,7 @@ func (listView *listViewData) propertyChanged(tag PropertyName) {
|
|||
if listeners := getOneArgEventListeners[ListView, []int](listView, nil, ListItemCheckedEvent); len(listeners) > 0 {
|
||||
checked := GetListViewCheckedItems(listView)
|
||||
for _, listener := range listeners {
|
||||
listener(listView, checked)
|
||||
listener.Run(listView, checked)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -342,7 +336,7 @@ func (listView *listViewData) setItems(value any) []PropertyName {
|
|||
items := make([]View, len(value))
|
||||
for i, val := range value {
|
||||
if val.IsObject() {
|
||||
if view := CreateViewFromObject(session, val.Object()); view != nil {
|
||||
if view := CreateViewFromObject(session, val.Object(), nil); view != nil {
|
||||
items[i] = view
|
||||
} else {
|
||||
return nil
|
||||
|
@ -540,7 +534,7 @@ func (listView *listViewData) getDivs(checkbox, hCheckboxAlign, vCheckboxAlign i
|
|||
return onDivBuilder.String(), offDivBuilder.String(), contentBuilder.String()
|
||||
}
|
||||
|
||||
func (listView *listViewData) checkboxItemDiv(checkbox, hCheckboxAlign, vCheckboxAlign int) string {
|
||||
func (listView *listViewData) checkboxItemDiv(hCheckboxAlign, vCheckboxAlign int) string {
|
||||
itemStyleBuilder := allocStringBuilder()
|
||||
defer freeStringBuilder(itemStyleBuilder)
|
||||
|
||||
|
@ -627,7 +621,7 @@ func (listView *listViewData) checkboxSubviews(adapter ListAdapter, buffer *stri
|
|||
hCheckboxAlign := GetListViewCheckboxHorizontalAlign(listView)
|
||||
vCheckboxAlign := GetListViewCheckboxVerticalAlign(listView)
|
||||
|
||||
itemDiv := listView.checkboxItemDiv(checkbox, hCheckboxAlign, vCheckboxAlign)
|
||||
itemDiv := listView.checkboxItemDiv(hCheckboxAlign, vCheckboxAlign)
|
||||
onDiv, offDiv, contentDiv := listView.getDivs(checkbox, hCheckboxAlign, vCheckboxAlign)
|
||||
|
||||
current := GetCurrent(listView)
|
||||
|
@ -734,7 +728,7 @@ func (listView *listViewData) updateCheckboxItem(index int, checked bool) {
|
|||
buffer := allocStringBuilder()
|
||||
defer freeStringBuilder(buffer)
|
||||
|
||||
buffer.WriteString(listView.checkboxItemDiv(checkbox, hCheckboxAlign, vCheckboxAlign))
|
||||
buffer.WriteString(listView.checkboxItemDiv(hCheckboxAlign, vCheckboxAlign))
|
||||
if checked {
|
||||
buffer.WriteString(onDiv)
|
||||
} else {
|
||||
|
@ -966,10 +960,10 @@ func (listView *listViewData) handleCommand(self View, command PropertyName, dat
|
|||
func (listView *listViewData) handleCurrent(number int) {
|
||||
listView.properties[Current] = number
|
||||
for _, listener := range getOneArgEventListeners[ListView, int](listView, nil, ListItemSelectedEvent) {
|
||||
listener(listView, number)
|
||||
listener.Run(listView, number)
|
||||
}
|
||||
if listener, ok := listView.changeListener[Current]; ok {
|
||||
listener(listView, Current)
|
||||
listener.Run(listView, Current)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1028,16 +1022,16 @@ func (listView *listViewData) onItemClick(number int) {
|
|||
|
||||
setArrayPropertyValue(listView, Checked, checkedItem)
|
||||
if listener, ok := listView.changeListener[Checked]; ok {
|
||||
listener(listView, Checked)
|
||||
listener.Run(listView, Checked)
|
||||
}
|
||||
|
||||
for _, listener := range getOneArgEventListeners[ListView, []int](listView, nil, ListItemCheckedEvent) {
|
||||
listener(listView, checkedItem)
|
||||
listener.Run(listView, checkedItem)
|
||||
}
|
||||
}
|
||||
|
||||
for _, listener := range getOneArgEventListeners[ListView, int](listView, nil, ListItemClickedEvent) {
|
||||
listener(listView, number)
|
||||
listener.Run(listView, number)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1054,58 +1048,97 @@ func (listView *listViewData) onItemResize(self View, index string, x, y, width,
|
|||
}
|
||||
|
||||
// GetVerticalAlign return the vertical align of a list: TopAlign (0), BottomAlign (1), CenterAlign (2), StretchAlign (3)
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetVerticalAlign(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, VerticalAlign, TopAlign, false)
|
||||
}
|
||||
|
||||
// GetHorizontalAlign return the vertical align of a list/checkbox: LeftAlign (0), RightAlign (1), CenterAlign (2), StretchAlign (3)
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetHorizontalAlign(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, HorizontalAlign, LeftAlign, false)
|
||||
}
|
||||
|
||||
// GetListItemClickedListeners returns a ListItemClickedListener of the ListView.
|
||||
// If there are no listeners then the empty list is returned
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetListItemClickedListeners(view View, subviewID ...string) []func(ListView, int) {
|
||||
return getOneArgEventListeners[ListView, int](view, subviewID, ListItemClickedEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.ListView, int),
|
||||
// - func(rui.ListView),
|
||||
// - func(int),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetListItemClickedListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[ListView, int](view, subviewID, ListItemClickedEvent)
|
||||
}
|
||||
|
||||
// GetListItemSelectedListeners returns a ListItemSelectedListener of the ListView.
|
||||
// If there are no listeners then the empty list is returned
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetListItemSelectedListeners(view View, subviewID ...string) []func(ListView, int) {
|
||||
return getOneArgEventListeners[ListView, int](view, subviewID, ListItemSelectedEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.ListView, int),
|
||||
// - func(rui.ListView),
|
||||
// - func(int),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetListItemSelectedListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[ListView, int](view, subviewID, ListItemSelectedEvent)
|
||||
}
|
||||
|
||||
// GetListItemCheckedListeners returns a ListItemCheckedListener of the ListView.
|
||||
// If there are no listeners then the empty list is returned
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetListItemCheckedListeners(view View, subviewID ...string) []func(ListView, []int) {
|
||||
return getOneArgEventListeners[ListView, []int](view, subviewID, ListItemCheckedEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.ListView, []int),
|
||||
// - func(rui.ListView),
|
||||
// - func([]int),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetListItemCheckedListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[ListView, []int](view, subviewID, ListItemCheckedEvent)
|
||||
}
|
||||
|
||||
// GetListItemWidth returns the width of a ListView item.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetListItemWidth(view View, subviewID ...string) SizeUnit {
|
||||
return sizeStyledProperty(view, subviewID, ItemWidth, false)
|
||||
}
|
||||
|
||||
// GetListItemHeight returns the height of a ListView item.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetListItemHeight(view View, subviewID ...string) SizeUnit {
|
||||
return sizeStyledProperty(view, subviewID, ItemHeight, false)
|
||||
}
|
||||
|
||||
// GetListViewCheckbox returns the ListView checkbox type: NoneCheckbox (0), SingleCheckbox (1), or MultipleCheckbox (2).
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetListViewCheckbox(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, ItemCheckbox, 0, false)
|
||||
}
|
||||
|
||||
// GetListViewCheckedItems returns the array of ListView checked items.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetListViewCheckedItems(view View, subviewID ...string) []int {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
if value := view.getRaw(Checked); value != nil {
|
||||
|
@ -1138,34 +1171,44 @@ func IsListViewCheckedItem(view View, subviewID string, index int) bool {
|
|||
|
||||
// GetListViewCheckboxVerticalAlign returns the vertical align of the ListView checkbox:
|
||||
// TopAlign (0), BottomAlign (1), CenterAlign (2)
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetListViewCheckboxVerticalAlign(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, CheckboxVerticalAlign, TopAlign, false)
|
||||
}
|
||||
|
||||
// GetListViewCheckboxHorizontalAlign returns the horizontal align of the ListView checkbox:
|
||||
// LeftAlign (0), RightAlign (1), CenterAlign (2)
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetListViewCheckboxHorizontalAlign(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, CheckboxHorizontalAlign, LeftAlign, false)
|
||||
}
|
||||
|
||||
// GetListItemVerticalAlign returns the vertical align of the ListView item content:
|
||||
// TopAlign (0), BottomAlign (1), CenterAlign (2)
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetListItemVerticalAlign(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, ItemVerticalAlign, TopAlign, false)
|
||||
}
|
||||
|
||||
// ItemHorizontalAlign returns the horizontal align of the ListView item content:
|
||||
// LeftAlign (0), RightAlign (1), CenterAlign (2), StretchAlign (3)
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetListItemHorizontalAlign(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, ItemHorizontalAlign, LeftAlign, false)
|
||||
}
|
||||
|
||||
// GetListItemFrame - returns the location and size of the ListView item in pixels.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetListItemFrame(view View, subviewID string, index int) Frame {
|
||||
if subviewID != "" {
|
||||
view = ViewByID(view, subviewID)
|
||||
|
@ -1182,7 +1225,9 @@ func GetListItemFrame(view View, subviewID string, index int) Frame {
|
|||
}
|
||||
|
||||
// GetListViewAdapter - returns the ListView adapter.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetListViewAdapter(view View, subviewID ...string) ListAdapter {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
if value := view.Get(Items); value != nil {
|
||||
|
|
620
mediaPlayer.go
620
mediaPlayer.go
|
@ -3,6 +3,7 @@ package rui
|
|||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
@ -488,7 +489,7 @@ const (
|
|||
// - player - Interface of a player which generated this event,
|
||||
// - code - Error code. See below,
|
||||
// - message - Error message,
|
||||
|
||||
//
|
||||
// Error codes:
|
||||
// - 0 (PlayerErrorUnknown) - Unknown error,
|
||||
// - 1 (PlayerErrorAborted) - Fetching the associated resource was interrupted by a user request,
|
||||
|
@ -610,7 +611,7 @@ func (player *mediaPlayerData) setFunc(tag PropertyName, value any) []PropertyNa
|
|||
return setOneArgEventListener[MediaPlayer, float64](player, tag, value)
|
||||
|
||||
case PlayerErrorEvent:
|
||||
if listeners, ok := valueToPlayerErrorListeners(value); ok {
|
||||
if listeners, ok := valueToMediaPlayerErrorListeners(value); ok {
|
||||
return setArrayPropertyValue(player, tag, listeners)
|
||||
}
|
||||
notCompatibleType(tag, value)
|
||||
|
@ -677,131 +678,132 @@ func setMediaPlayerSource(properties Properties, value any) []PropertyName {
|
|||
return []PropertyName{Source}
|
||||
}
|
||||
|
||||
func valueToPlayerErrorListeners(value any) ([]func(MediaPlayer, int, string), bool) {
|
||||
if value == nil {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case func(MediaPlayer, int, string):
|
||||
return []func(MediaPlayer, int, string){value}, true
|
||||
|
||||
case func(int, string):
|
||||
fn := func(_ MediaPlayer, code int, message string) {
|
||||
value(code, message)
|
||||
}
|
||||
return []func(MediaPlayer, int, string){fn}, true
|
||||
|
||||
case func(MediaPlayer):
|
||||
fn := func(player MediaPlayer, _ int, _ string) {
|
||||
value(player)
|
||||
}
|
||||
return []func(MediaPlayer, int, string){fn}, true
|
||||
|
||||
case func():
|
||||
fn := func(MediaPlayer, int, string) {
|
||||
value()
|
||||
}
|
||||
return []func(MediaPlayer, int, string){fn}, true
|
||||
|
||||
case []func(MediaPlayer, int, string):
|
||||
if len(value) == 0 {
|
||||
/*
|
||||
func valueToPlayerErrorListeners(value any) ([]func(MediaPlayer, int, string), bool) {
|
||||
if value == nil {
|
||||
return nil, true
|
||||
}
|
||||
for _, fn := range value {
|
||||
if fn == nil {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return value, true
|
||||
|
||||
case []func(int, string):
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(MediaPlayer, int, string), count)
|
||||
for i, v := range value {
|
||||
if v == nil {
|
||||
return nil, false
|
||||
}
|
||||
listeners[i] = func(_ MediaPlayer, code int, message string) {
|
||||
v(code, message)
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
switch value := value.(type) {
|
||||
case func(MediaPlayer, int, string):
|
||||
return []func(MediaPlayer, int, string){value}, true
|
||||
|
||||
case []func(MediaPlayer):
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(MediaPlayer, int, string), count)
|
||||
for i, v := range value {
|
||||
if v == nil {
|
||||
return nil, false
|
||||
case func(int, string):
|
||||
fn := func(_ MediaPlayer, code int, message string) {
|
||||
value(code, message)
|
||||
}
|
||||
listeners[i] = func(player MediaPlayer, _ int, _ string) {
|
||||
v(player)
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
return []func(MediaPlayer, int, string){fn}, true
|
||||
|
||||
case []func():
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(MediaPlayer, int, string), count)
|
||||
for i, v := range value {
|
||||
if v == nil {
|
||||
return nil, false
|
||||
case func(MediaPlayer):
|
||||
fn := func(player MediaPlayer, _ int, _ string) {
|
||||
value(player)
|
||||
}
|
||||
listeners[i] = func(MediaPlayer, int, string) {
|
||||
v()
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
return []func(MediaPlayer, int, string){fn}, true
|
||||
|
||||
case []any:
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(MediaPlayer, int, string), count)
|
||||
for i, v := range value {
|
||||
if v == nil {
|
||||
return nil, false
|
||||
case func():
|
||||
fn := func(MediaPlayer, int, string) {
|
||||
value()
|
||||
}
|
||||
switch v := v.(type) {
|
||||
case func(MediaPlayer, int, string):
|
||||
listeners[i] = v
|
||||
return []func(MediaPlayer, int, string){fn}, true
|
||||
|
||||
case func(int, string):
|
||||
case []func(MediaPlayer, int, string):
|
||||
if len(value) == 0 {
|
||||
return nil, true
|
||||
}
|
||||
for _, fn := range value {
|
||||
if fn == nil {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return value, true
|
||||
|
||||
case []func(int, string):
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(MediaPlayer, int, string), count)
|
||||
for i, v := range value {
|
||||
if v == nil {
|
||||
return nil, false
|
||||
}
|
||||
listeners[i] = func(_ MediaPlayer, code int, message string) {
|
||||
v(code, message)
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
|
||||
case func(MediaPlayer):
|
||||
case []func(MediaPlayer):
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(MediaPlayer, int, string), count)
|
||||
for i, v := range value {
|
||||
if v == nil {
|
||||
return nil, false
|
||||
}
|
||||
listeners[i] = func(player MediaPlayer, _ int, _ string) {
|
||||
v(player)
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
|
||||
case func():
|
||||
case []func():
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(MediaPlayer, int, string), count)
|
||||
for i, v := range value {
|
||||
if v == nil {
|
||||
return nil, false
|
||||
}
|
||||
listeners[i] = func(MediaPlayer, int, string) {
|
||||
v()
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
return listeners, true
|
||||
|
||||
case []any:
|
||||
count := len(value)
|
||||
if count == 0 {
|
||||
return nil, true
|
||||
}
|
||||
listeners := make([]func(MediaPlayer, int, string), count)
|
||||
for i, v := range value {
|
||||
if v == nil {
|
||||
return nil, false
|
||||
}
|
||||
switch v := v.(type) {
|
||||
case func(MediaPlayer, int, string):
|
||||
listeners[i] = v
|
||||
|
||||
case func(int, string):
|
||||
listeners[i] = func(_ MediaPlayer, code int, message string) {
|
||||
v(code, message)
|
||||
}
|
||||
|
||||
case func(MediaPlayer):
|
||||
listeners[i] = func(player MediaPlayer, _ int, _ string) {
|
||||
v(player)
|
||||
}
|
||||
|
||||
case func():
|
||||
listeners[i] = func(MediaPlayer, int, string) {
|
||||
v()
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return listeners, true
|
||||
}
|
||||
return listeners, true
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
*/
|
||||
func mediaPlayerEvents() map[PropertyName]string {
|
||||
return map[PropertyName]string{
|
||||
AbortEvent: "onabort",
|
||||
|
@ -981,31 +983,23 @@ func (player *mediaPlayerData) handleCommand(self View, command PropertyName, da
|
|||
PlayingEvent, ProgressEvent, SeekedEvent, SeekingEvent, StalledEvent, SuspendEvent,
|
||||
WaitingEvent:
|
||||
|
||||
if value := player.getRaw(command); value != nil {
|
||||
if listeners, ok := value.([]func(MediaPlayer)); ok {
|
||||
for _, listener := range listeners {
|
||||
listener(player)
|
||||
}
|
||||
}
|
||||
for _, listener := range getNoArgEventListeners[MediaPlayer](player, nil, command) {
|
||||
listener.Run(player)
|
||||
}
|
||||
|
||||
case TimeUpdateEvent, DurationChangedEvent, RateChangedEvent, VolumeChangedEvent:
|
||||
if value := player.getRaw(command); value != nil {
|
||||
if listeners, ok := value.([]func(MediaPlayer, float64)); ok {
|
||||
time := dataFloatProperty(data, "value")
|
||||
for _, listener := range listeners {
|
||||
listener(player, time)
|
||||
}
|
||||
}
|
||||
time := dataFloatProperty(data, "value")
|
||||
for _, listener := range getOneArgEventListeners[MediaPlayer, float64](player, nil, command) {
|
||||
listener.Run(player, time)
|
||||
}
|
||||
|
||||
case PlayerErrorEvent:
|
||||
if value := player.getRaw(command); value != nil {
|
||||
if listeners, ok := value.([]func(MediaPlayer, int, string)); ok {
|
||||
if listeners, ok := value.([]mediaPlayerErrorListener); ok {
|
||||
code, _ := dataIntProperty(data, "code")
|
||||
message, _ := data.PropertyValue("message")
|
||||
for _, listener := range listeners {
|
||||
listener(player, code, message)
|
||||
listener.Run(player, code, message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1254,3 +1248,393 @@ func IsMediaPlayerPaused(view View, playerID string) bool {
|
|||
ErrorLog(`The found View is not MediaPlayer`)
|
||||
return false
|
||||
}
|
||||
|
||||
type mediaPlayerErrorListener interface {
|
||||
Run(MediaPlayer, int, string)
|
||||
rawListener() any
|
||||
}
|
||||
|
||||
type mediaPlayerErrorListener0 struct {
|
||||
fn func()
|
||||
}
|
||||
|
||||
type mediaPlayerErrorListenerP struct {
|
||||
fn func(MediaPlayer)
|
||||
}
|
||||
|
||||
type mediaPlayerErrorListenerI struct {
|
||||
fn func(int)
|
||||
}
|
||||
|
||||
type mediaPlayerErrorListenerS struct {
|
||||
fn func(string)
|
||||
}
|
||||
|
||||
type mediaPlayerErrorListenerPI struct {
|
||||
fn func(MediaPlayer, int)
|
||||
}
|
||||
|
||||
type mediaPlayerErrorListenerPS struct {
|
||||
fn func(MediaPlayer, string)
|
||||
}
|
||||
|
||||
type mediaPlayerErrorListenerIS struct {
|
||||
fn func(int, string)
|
||||
}
|
||||
|
||||
type mediaPlayerErrorListenerPIS struct {
|
||||
fn func(MediaPlayer, int, string)
|
||||
}
|
||||
|
||||
type mediaPlayerErrorListenerBinding struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func newMediaPlayerErrorListener0(fn func()) mediaPlayerErrorListener {
|
||||
obj := new(mediaPlayerErrorListener0)
|
||||
obj.fn = fn
|
||||
return obj
|
||||
}
|
||||
|
||||
func (data *mediaPlayerErrorListener0) Run(_ MediaPlayer, _ int, _ string) {
|
||||
data.fn()
|
||||
}
|
||||
|
||||
func (data *mediaPlayerErrorListener0) rawListener() any {
|
||||
return data.fn
|
||||
}
|
||||
|
||||
func newMediaPlayerErrorListenerP(fn func(MediaPlayer)) mediaPlayerErrorListener {
|
||||
obj := new(mediaPlayerErrorListenerP)
|
||||
obj.fn = fn
|
||||
return obj
|
||||
}
|
||||
|
||||
func (data *mediaPlayerErrorListenerP) Run(player MediaPlayer, _ int, _ string) {
|
||||
data.fn(player)
|
||||
}
|
||||
|
||||
func (data *mediaPlayerErrorListenerP) rawListener() any {
|
||||
return data.fn
|
||||
}
|
||||
|
||||
func newMediaPlayerErrorListenerI(fn func(int)) mediaPlayerErrorListener {
|
||||
obj := new(mediaPlayerErrorListenerI)
|
||||
obj.fn = fn
|
||||
return obj
|
||||
}
|
||||
|
||||
func (data *mediaPlayerErrorListenerI) Run(_ MediaPlayer, code int, _ string) {
|
||||
data.fn(code)
|
||||
}
|
||||
|
||||
func (data *mediaPlayerErrorListenerI) rawListener() any {
|
||||
return data.fn
|
||||
}
|
||||
|
||||
func newMediaPlayerErrorListenerS(fn func(string)) mediaPlayerErrorListener {
|
||||
obj := new(mediaPlayerErrorListenerS)
|
||||
obj.fn = fn
|
||||
return obj
|
||||
}
|
||||
|
||||
func (data *mediaPlayerErrorListenerS) Run(_ MediaPlayer, _ int, message string) {
|
||||
data.fn(message)
|
||||
}
|
||||
|
||||
func (data *mediaPlayerErrorListenerS) rawListener() any {
|
||||
return data.fn
|
||||
}
|
||||
|
||||
func newMediaPlayerErrorListenerPI(fn func(MediaPlayer, int)) mediaPlayerErrorListener {
|
||||
obj := new(mediaPlayerErrorListenerPI)
|
||||
obj.fn = fn
|
||||
return obj
|
||||
}
|
||||
|
||||
func (data *mediaPlayerErrorListenerPI) Run(player MediaPlayer, code int, _ string) {
|
||||
data.fn(player, code)
|
||||
}
|
||||
|
||||
func (data *mediaPlayerErrorListenerPI) rawListener() any {
|
||||
return data.fn
|
||||
}
|
||||
|
||||
func newMediaPlayerErrorListenerPS(fn func(MediaPlayer, string)) mediaPlayerErrorListener {
|
||||
obj := new(mediaPlayerErrorListenerPS)
|
||||
obj.fn = fn
|
||||
return obj
|
||||
}
|
||||
|
||||
func (data *mediaPlayerErrorListenerPS) Run(player MediaPlayer, _ int, message string) {
|
||||
data.fn(player, message)
|
||||
}
|
||||
|
||||
func (data *mediaPlayerErrorListenerPS) rawListener() any {
|
||||
return data.fn
|
||||
}
|
||||
|
||||
func newMediaPlayerErrorListenerIS(fn func(int, string)) mediaPlayerErrorListener {
|
||||
obj := new(mediaPlayerErrorListenerIS)
|
||||
obj.fn = fn
|
||||
return obj
|
||||
}
|
||||
|
||||
func (data *mediaPlayerErrorListenerIS) Run(_ MediaPlayer, code int, message string) {
|
||||
data.fn(code, message)
|
||||
}
|
||||
|
||||
func (data *mediaPlayerErrorListenerIS) rawListener() any {
|
||||
return data.fn
|
||||
}
|
||||
|
||||
func newMediaPlayerErrorListenerPIS(fn func(MediaPlayer, int, string)) mediaPlayerErrorListener {
|
||||
obj := new(mediaPlayerErrorListenerPIS)
|
||||
obj.fn = fn
|
||||
return obj
|
||||
}
|
||||
|
||||
func (data *mediaPlayerErrorListenerPIS) Run(player MediaPlayer, code int, message string) {
|
||||
data.fn(player, code, message)
|
||||
}
|
||||
|
||||
func (data *mediaPlayerErrorListenerPIS) rawListener() any {
|
||||
return data.fn
|
||||
}
|
||||
|
||||
func newMediaPlayerErrorListenerBinding(name string) mediaPlayerErrorListener {
|
||||
obj := new(mediaPlayerErrorListenerBinding)
|
||||
obj.name = name
|
||||
return obj
|
||||
}
|
||||
|
||||
func (data *mediaPlayerErrorListenerBinding) Run(player MediaPlayer, code int, message string) {
|
||||
bind := player.binding()
|
||||
if bind == nil {
|
||||
ErrorLogF(`There is no a binding object for call "%s"`, data.name)
|
||||
return
|
||||
}
|
||||
|
||||
val := reflect.ValueOf(bind)
|
||||
method := val.MethodByName(data.name)
|
||||
if !method.IsValid() {
|
||||
ErrorLogF(`The "%s" method is not valid`, data.name)
|
||||
return
|
||||
}
|
||||
|
||||
methodType := method.Type()
|
||||
var args []reflect.Value = nil
|
||||
|
||||
switch methodType.NumIn() {
|
||||
case 0:
|
||||
args = []reflect.Value{}
|
||||
|
||||
case 1:
|
||||
inType := methodType.In(0)
|
||||
if equalType(inType, reflect.TypeOf(player)) {
|
||||
args = []reflect.Value{reflect.ValueOf(player)}
|
||||
} else if equalType(inType, reflect.TypeOf(code)) {
|
||||
args = []reflect.Value{reflect.ValueOf(code)}
|
||||
} else if equalType(inType, reflect.TypeOf(message)) {
|
||||
args = []reflect.Value{reflect.ValueOf(message)}
|
||||
}
|
||||
|
||||
case 2:
|
||||
in0 := methodType.In(0)
|
||||
in1 := methodType.In(1)
|
||||
if equalType(in0, reflect.TypeOf(player)) {
|
||||
if equalType(in1, reflect.TypeOf(code)) {
|
||||
args = []reflect.Value{reflect.ValueOf(player), reflect.ValueOf(code)}
|
||||
} else if equalType(in1, reflect.TypeOf(message)) {
|
||||
args = []reflect.Value{reflect.ValueOf(player), reflect.ValueOf(message)}
|
||||
}
|
||||
} else if equalType(in0, reflect.TypeOf(code)) && equalType(in1, reflect.TypeOf(message)) {
|
||||
args = []reflect.Value{reflect.ValueOf(code), reflect.ValueOf(message)}
|
||||
}
|
||||
|
||||
case 3:
|
||||
if equalType(methodType.In(0), reflect.TypeOf(player)) &&
|
||||
equalType(methodType.In(1), reflect.TypeOf(code)) &&
|
||||
equalType(methodType.In(2), reflect.TypeOf(message)) {
|
||||
args = []reflect.Value{
|
||||
reflect.ValueOf(player),
|
||||
reflect.ValueOf(code),
|
||||
reflect.ValueOf(message),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if args != nil {
|
||||
method.Call(args)
|
||||
} else {
|
||||
ErrorLogF(`Unsupported prototype of "%s" method`, data.name)
|
||||
}
|
||||
}
|
||||
|
||||
func (data *mediaPlayerErrorListenerBinding) rawListener() any {
|
||||
return data.name
|
||||
}
|
||||
|
||||
func valueToMediaPlayerErrorListeners(value any) ([]mediaPlayerErrorListener, bool) {
|
||||
if value == nil {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case []mediaPlayerErrorListener:
|
||||
return value, true
|
||||
|
||||
case mediaPlayerErrorListener:
|
||||
return []mediaPlayerErrorListener{value}, true
|
||||
|
||||
case string:
|
||||
return []mediaPlayerErrorListener{newMediaPlayerErrorListenerBinding(value)}, true
|
||||
|
||||
case func():
|
||||
return []mediaPlayerErrorListener{newMediaPlayerErrorListener0(value)}, true
|
||||
|
||||
case func(MediaPlayer):
|
||||
return []mediaPlayerErrorListener{newMediaPlayerErrorListenerP(value)}, true
|
||||
|
||||
case func(int):
|
||||
return []mediaPlayerErrorListener{newMediaPlayerErrorListenerI(value)}, true
|
||||
|
||||
case func(string):
|
||||
return []mediaPlayerErrorListener{newMediaPlayerErrorListenerS(value)}, true
|
||||
|
||||
case func(MediaPlayer, int):
|
||||
return []mediaPlayerErrorListener{newMediaPlayerErrorListenerPI(value)}, true
|
||||
|
||||
case func(MediaPlayer, string):
|
||||
return []mediaPlayerErrorListener{newMediaPlayerErrorListenerPS(value)}, true
|
||||
|
||||
case func(int, string):
|
||||
return []mediaPlayerErrorListener{newMediaPlayerErrorListenerIS(value)}, true
|
||||
|
||||
case func(MediaPlayer, int, string):
|
||||
return []mediaPlayerErrorListener{newMediaPlayerErrorListenerPIS(value)}, true
|
||||
|
||||
case []func():
|
||||
result := make([]mediaPlayerErrorListener, 0, len(value))
|
||||
for _, fn := range value {
|
||||
if fn != nil {
|
||||
result = append(result, newMediaPlayerErrorListener0(fn))
|
||||
}
|
||||
}
|
||||
return result, len(result) > 0
|
||||
|
||||
case []func(MediaPlayer):
|
||||
result := make([]mediaPlayerErrorListener, 0, len(value))
|
||||
for _, fn := range value {
|
||||
if fn != nil {
|
||||
result = append(result, newMediaPlayerErrorListenerP(fn))
|
||||
}
|
||||
}
|
||||
return result, len(result) > 0
|
||||
|
||||
case []func(int):
|
||||
result := make([]mediaPlayerErrorListener, 0, len(value))
|
||||
for _, fn := range value {
|
||||
if fn != nil {
|
||||
result = append(result, newMediaPlayerErrorListenerI(fn))
|
||||
}
|
||||
}
|
||||
return result, len(result) > 0
|
||||
|
||||
case []func(string):
|
||||
result := make([]mediaPlayerErrorListener, 0, len(value))
|
||||
for _, fn := range value {
|
||||
if fn != nil {
|
||||
result = append(result, newMediaPlayerErrorListenerS(fn))
|
||||
}
|
||||
}
|
||||
return result, len(result) > 0
|
||||
|
||||
case []func(MediaPlayer, int):
|
||||
result := make([]mediaPlayerErrorListener, 0, len(value))
|
||||
for _, fn := range value {
|
||||
if fn != nil {
|
||||
result = append(result, newMediaPlayerErrorListenerPI(fn))
|
||||
}
|
||||
}
|
||||
return result, len(result) > 0
|
||||
|
||||
case []func(MediaPlayer, string):
|
||||
result := make([]mediaPlayerErrorListener, 0, len(value))
|
||||
for _, fn := range value {
|
||||
if fn != nil {
|
||||
result = append(result, newMediaPlayerErrorListenerPS(fn))
|
||||
}
|
||||
}
|
||||
return result, len(result) > 0
|
||||
|
||||
case []func(int, string):
|
||||
result := make([]mediaPlayerErrorListener, 0, len(value))
|
||||
for _, fn := range value {
|
||||
if fn != nil {
|
||||
result = append(result, newMediaPlayerErrorListenerIS(fn))
|
||||
}
|
||||
}
|
||||
return result, len(result) > 0
|
||||
|
||||
case []func(MediaPlayer, int, string):
|
||||
result := make([]mediaPlayerErrorListener, 0, len(value))
|
||||
for _, fn := range value {
|
||||
if fn != nil {
|
||||
result = append(result, newMediaPlayerErrorListenerPIS(fn))
|
||||
}
|
||||
}
|
||||
return result, len(result) > 0
|
||||
|
||||
case []any:
|
||||
result := make([]mediaPlayerErrorListener, 0, len(value))
|
||||
for _, v := range value {
|
||||
if v != nil {
|
||||
switch v := v.(type) {
|
||||
case func():
|
||||
result = append(result, newMediaPlayerErrorListener0(v))
|
||||
|
||||
case func(MediaPlayer):
|
||||
result = append(result, newMediaPlayerErrorListenerP(v))
|
||||
|
||||
case func(int):
|
||||
result = append(result, newMediaPlayerErrorListenerI(v))
|
||||
|
||||
case func(string):
|
||||
result = append(result, newMediaPlayerErrorListenerS(v))
|
||||
|
||||
case func(MediaPlayer, int):
|
||||
result = append(result, newMediaPlayerErrorListenerPI(v))
|
||||
|
||||
case func(MediaPlayer, string):
|
||||
result = append(result, newMediaPlayerErrorListenerPS(v))
|
||||
|
||||
case func(int, string):
|
||||
result = append(result, newMediaPlayerErrorListenerIS(v))
|
||||
|
||||
case func(MediaPlayer, int, string):
|
||||
result = append(result, newMediaPlayerErrorListenerPIS(v))
|
||||
|
||||
case string:
|
||||
result = append(result, newMediaPlayerErrorListenerBinding(v))
|
||||
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
}
|
||||
return result, len(result) > 0
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func getMediaPlayerErrorListenerBinding(listeners []mediaPlayerErrorListener) string {
|
||||
for _, listener := range listeners {
|
||||
raw := listener.rawListener()
|
||||
if text, ok := raw.(string); ok && text != "" {
|
||||
return text
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
|
122
mouseEvents.go
122
mouseEvents.go
|
@ -280,56 +280,128 @@ func handleMouseEvents(view View, tag PropertyName, data DataObject) {
|
|||
event.init(data)
|
||||
|
||||
for _, listener := range listeners {
|
||||
listener(view, event)
|
||||
listener.Run(view, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetClickListeners returns the "click-event" listener list. If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetClickListeners(view View, subviewID ...string) []func(View, MouseEvent) {
|
||||
return getOneArgEventListeners[View, MouseEvent](view, subviewID, ClickEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(View, MouseEvent),
|
||||
// - func(View),
|
||||
// - func(MouseEvent),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetClickListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, MouseEvent](view, subviewID, ClickEvent)
|
||||
}
|
||||
|
||||
// GetDoubleClickListeners returns the "double-click-event" listener list. If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetDoubleClickListeners(view View, subviewID ...string) []func(View, MouseEvent) {
|
||||
return getOneArgEventListeners[View, MouseEvent](view, subviewID, DoubleClickEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(View, MouseEvent),
|
||||
// - func(View),
|
||||
// - func(MouseEvent),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetDoubleClickListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, MouseEvent](view, subviewID, DoubleClickEvent)
|
||||
}
|
||||
|
||||
// GetContextMenuListeners returns the "context-menu" listener list.
|
||||
// If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetContextMenuListeners(view View, subviewID ...string) []func(View, MouseEvent) {
|
||||
return getOneArgEventListeners[View, MouseEvent](view, subviewID, ContextMenuEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(View, MouseEvent),
|
||||
// - func(View),
|
||||
// - func(MouseEvent),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetContextMenuListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, MouseEvent](view, subviewID, ContextMenuEvent)
|
||||
}
|
||||
|
||||
// GetMouseDownListeners returns the "mouse-down" listener list. If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetMouseDownListeners(view View, subviewID ...string) []func(View, MouseEvent) {
|
||||
return getOneArgEventListeners[View, MouseEvent](view, subviewID, MouseDown)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(View, MouseEvent),
|
||||
// - func(View),
|
||||
// - func(MouseEvent),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetMouseDownListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, MouseEvent](view, subviewID, MouseDown)
|
||||
}
|
||||
|
||||
// GetMouseUpListeners returns the "mouse-up" listener list. If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetMouseUpListeners(view View, subviewID ...string) []func(View, MouseEvent) {
|
||||
return getOneArgEventListeners[View, MouseEvent](view, subviewID, MouseUp)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(View, MouseEvent),
|
||||
// - func(View),
|
||||
// - func(MouseEvent),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetMouseUpListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, MouseEvent](view, subviewID, MouseUp)
|
||||
}
|
||||
|
||||
// GetMouseMoveListeners returns the "mouse-move" listener list. If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetMouseMoveListeners(view View, subviewID ...string) []func(View, MouseEvent) {
|
||||
return getOneArgEventListeners[View, MouseEvent](view, subviewID, MouseMove)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(View, MouseEvent),
|
||||
// - func(View),
|
||||
// - func(MouseEvent),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetMouseMoveListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, MouseEvent](view, subviewID, MouseMove)
|
||||
}
|
||||
|
||||
// GetMouseOverListeners returns the "mouse-over" listener list. If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetMouseOverListeners(view View, subviewID ...string) []func(View, MouseEvent) {
|
||||
return getOneArgEventListeners[View, MouseEvent](view, subviewID, MouseOver)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(View, MouseEvent),
|
||||
// - func(View),
|
||||
// - func(MouseEvent),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetMouseOverListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, MouseEvent](view, subviewID, MouseOver)
|
||||
}
|
||||
|
||||
// GetMouseOutListeners returns the "mouse-out" listener list. If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetMouseOutListeners(view View, subviewID ...string) []func(View, MouseEvent) {
|
||||
return getOneArgEventListeners[View, MouseEvent](view, subviewID, MouseOut)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(View, MouseEvent),
|
||||
// - func(View),
|
||||
// - func(MouseEvent),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetMouseOutListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, MouseEvent](view, subviewID, MouseOut)
|
||||
}
|
||||
|
|
|
@ -166,8 +166,8 @@ func (picker *numberPickerData) setFunc(tag PropertyName, value any) []PropertyN
|
|||
}
|
||||
|
||||
func (picker *numberPickerData) numberFormat() string {
|
||||
if precission := GetNumberPickerPrecision(picker); precission > 0 {
|
||||
return fmt.Sprintf("%%.%df", precission)
|
||||
if precision := GetNumberPickerPrecision(picker); precision > 0 {
|
||||
return fmt.Sprintf("%%.%df", precision)
|
||||
}
|
||||
return "%g"
|
||||
}
|
||||
|
@ -201,7 +201,7 @@ func (picker *numberPickerData) propertyChanged(tag PropertyName) {
|
|||
format := picker.numberFormat()
|
||||
picker.Session().callFunc("setInputValue", picker.htmlID(), fmt.Sprintf(format, value))
|
||||
|
||||
if listeners := GetNumberChangedListeners(picker); len(listeners) > 0 {
|
||||
if listeners := getTwoArgEventListeners[NumberPicker, float64](picker, nil, NumberChangedEvent); len(listeners) > 0 {
|
||||
old := 0.0
|
||||
if val := picker.getRaw("old-number"); val != nil {
|
||||
if n, ok := val.(float64); ok {
|
||||
|
@ -210,7 +210,7 @@ func (picker *numberPickerData) propertyChanged(tag PropertyName) {
|
|||
}
|
||||
if old != value {
|
||||
for _, listener := range listeners {
|
||||
listener(picker, value, old)
|
||||
listener.Run(picker, value, old)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -244,27 +244,27 @@ func (picker *numberPickerData) htmlProperties(self View, buffer *strings.Builde
|
|||
min, max := GetNumberPickerMinMax(picker)
|
||||
if min != math.Inf(-1) {
|
||||
buffer.WriteString(` min="`)
|
||||
buffer.WriteString(fmt.Sprintf(format, min))
|
||||
fmt.Fprintf(buffer, format, min)
|
||||
buffer.WriteByte('"')
|
||||
}
|
||||
|
||||
if max != math.Inf(1) {
|
||||
buffer.WriteString(` max="`)
|
||||
buffer.WriteString(fmt.Sprintf(format, max))
|
||||
fmt.Fprintf(buffer, format, max)
|
||||
buffer.WriteByte('"')
|
||||
}
|
||||
|
||||
step := GetNumberPickerStep(picker)
|
||||
if step != 0 {
|
||||
buffer.WriteString(` step="`)
|
||||
buffer.WriteString(fmt.Sprintf(format, step))
|
||||
fmt.Fprintf(buffer, format, step)
|
||||
buffer.WriteByte('"')
|
||||
} else {
|
||||
buffer.WriteString(` step="any"`)
|
||||
}
|
||||
|
||||
buffer.WriteString(` value="`)
|
||||
buffer.WriteString(fmt.Sprintf(format, GetNumberPickerValue(picker)))
|
||||
fmt.Fprintf(buffer, format, GetNumberPickerValue(picker))
|
||||
buffer.WriteByte('"')
|
||||
|
||||
buffer.WriteString(` oninput="editViewInputEvent(this)"`)
|
||||
|
@ -280,11 +280,11 @@ func (picker *numberPickerData) handleCommand(self View, command PropertyName, d
|
|||
oldValue := GetNumberPickerValue(picker)
|
||||
picker.properties[NumberPickerValue] = text
|
||||
if value != oldValue {
|
||||
for _, listener := range GetNumberChangedListeners(picker) {
|
||||
listener(picker, value, oldValue)
|
||||
for _, listener := range getTwoArgEventListeners[NumberPicker, float64](picker, nil, NumberChangedEvent) {
|
||||
listener.Run(picker, value, oldValue)
|
||||
}
|
||||
if listener, ok := picker.changeListener[NumberPickerValue]; ok {
|
||||
listener(picker, NumberPickerValue)
|
||||
listener.Run(picker, NumberPickerValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -298,13 +298,17 @@ func (picker *numberPickerData) handleCommand(self View, command PropertyName, d
|
|||
// GetNumberPickerType returns the type of NumberPicker subview. Valid values:
|
||||
// NumberEditor (0) - NumberPicker is presented by editor (default type);
|
||||
// NumberSlider (1) - NumberPicker is presented by slider.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetNumberPickerType(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, NumberPickerType, NumberEditor, false)
|
||||
}
|
||||
|
||||
// GetNumberPickerMinMax returns the min and max value of NumberPicker subview.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetNumberPickerMinMax(view View, subviewID ...string) (float64, float64) {
|
||||
view = getSubview(view, subviewID)
|
||||
pickerType := GetNumberPickerType(view)
|
||||
|
@ -328,7 +332,9 @@ func GetNumberPickerMinMax(view View, subviewID ...string) (float64, float64) {
|
|||
}
|
||||
|
||||
// GetNumberPickerStep returns the value changing step of NumberPicker subview.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetNumberPickerStep(view View, subviewID ...string) float64 {
|
||||
view = getSubview(view, subviewID)
|
||||
_, max := GetNumberPickerMinMax(view)
|
||||
|
@ -341,7 +347,9 @@ func GetNumberPickerStep(view View, subviewID ...string) float64 {
|
|||
}
|
||||
|
||||
// GetNumberPickerValue returns the value of NumberPicker subview.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetNumberPickerValue(view View, subviewID ...string) float64 {
|
||||
view = getSubview(view, subviewID)
|
||||
min, _ := GetNumberPickerMinMax(view)
|
||||
|
@ -350,13 +358,26 @@ func GetNumberPickerValue(view View, subviewID ...string) float64 {
|
|||
|
||||
// GetNumberChangedListeners returns the NumberChangedListener list of an NumberPicker subview.
|
||||
// If there are no listeners then the empty list is returned
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetNumberChangedListeners(view View, subviewID ...string) []func(NumberPicker, float64, float64) {
|
||||
return getTwoArgEventListeners[NumberPicker, float64](view, subviewID, NumberChangedEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.NumberPicker, float64, float64),
|
||||
// - func(rui.NumberPicker, float64),
|
||||
// - func(rui.NumberPicker),
|
||||
// - func(float64, float64),
|
||||
// - func(float64),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetNumberChangedListeners(view View, subviewID ...string) []any {
|
||||
return getTwoArgEventRawListeners[NumberPicker, float64](view, subviewID, NumberChangedEvent)
|
||||
}
|
||||
|
||||
// GetNumberPickerPrecision returns the precision of displaying fractional part in editor of NumberPicker subview.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetNumberPickerPrecision(view View, subviewID ...string) int {
|
||||
return intStyledProperty(view, subviewID, NumberPickerPrecision, 0)
|
||||
}
|
||||
|
|
|
@ -192,42 +192,96 @@ func handlePointerEvents(view View, tag PropertyName, data DataObject) {
|
|||
event.init(data)
|
||||
|
||||
for _, listener := range listeners {
|
||||
listener(view, event)
|
||||
listener.Run(view, event)
|
||||
}
|
||||
}
|
||||
|
||||
// GetPointerDownListeners returns the "pointer-down" listener list. If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetPointerDownListeners(view View, subviewID ...string) []func(View, PointerEvent) {
|
||||
return getOneArgEventListeners[View, PointerEvent](view, subviewID, PointerDown)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(View, PointerEvent),
|
||||
// - func(View),
|
||||
// - func(PointerEvent),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetPointerDownListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, PointerEvent](view, subviewID, PointerDown)
|
||||
}
|
||||
|
||||
// GetPointerUpListeners returns the "pointer-up" listener list. If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetPointerUpListeners(view View, subviewID ...string) []func(View, PointerEvent) {
|
||||
return getOneArgEventListeners[View, PointerEvent](view, subviewID, PointerUp)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(View, PointerEvent),
|
||||
// - func(View),
|
||||
// - func(PointerEvent),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetPointerUpListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, PointerEvent](view, subviewID, PointerUp)
|
||||
}
|
||||
|
||||
// GetPointerMoveListeners returns the "pointer-move" listener list. If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetPointerMoveListeners(view View, subviewID ...string) []func(View, PointerEvent) {
|
||||
return getOneArgEventListeners[View, PointerEvent](view, subviewID, PointerMove)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(View, PointerEvent),
|
||||
// - func(View),
|
||||
// - func(PointerEvent),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetPointerMoveListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, PointerEvent](view, subviewID, PointerMove)
|
||||
}
|
||||
|
||||
// GetPointerCancelListeners returns the "pointer-cancel" listener list. If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetPointerCancelListeners(view View, subviewID ...string) []func(View, PointerEvent) {
|
||||
return getOneArgEventListeners[View, PointerEvent](view, subviewID, PointerCancel)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(View, PointerEvent),
|
||||
// - func(View),
|
||||
// - func(PointerEvent),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetPointerCancelListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, PointerEvent](view, subviewID, PointerCancel)
|
||||
}
|
||||
|
||||
// GetPointerOverListeners returns the "pointer-over" listener list. If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetPointerOverListeners(view View, subviewID ...string) []func(View, PointerEvent) {
|
||||
return getOneArgEventListeners[View, PointerEvent](view, subviewID, PointerOver)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(View, PointerEvent),
|
||||
// - func(View),
|
||||
// - func(PointerEvent),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetPointerOverListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, PointerEvent](view, subviewID, PointerOver)
|
||||
}
|
||||
|
||||
// GetPointerOutListeners returns the "pointer-out" listener list. If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetPointerOutListeners(view View, subviewID ...string) []func(View, PointerEvent) {
|
||||
return getOneArgEventListeners[View, PointerEvent](view, subviewID, PointerOut)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(View, PointerEvent),
|
||||
// - func(View),
|
||||
// - func(PointerEvent),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetPointerOutListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, PointerEvent](view, subviewID, PointerOut)
|
||||
}
|
||||
|
|
200
popup.go
200
popup.go
|
@ -2,6 +2,7 @@ package rui
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
@ -150,7 +151,7 @@ const (
|
|||
//
|
||||
// Used by Popup.
|
||||
// Specify start translation, scale and rotation over x, y and z axes as well as a distortion
|
||||
// for an animated Popup showing/hidding.
|
||||
// for an animated Popup showing/hiding.
|
||||
//
|
||||
// Supported types: TransformProperty, string.
|
||||
//
|
||||
|
@ -278,7 +279,24 @@ type Popup interface {
|
|||
viewByHTMLID(id string) View
|
||||
keyEvent(event KeyEvent) bool
|
||||
showAnimation()
|
||||
dissmissAnimation(listener func(PropertyName)) bool
|
||||
dismissAnimation(listener func(PropertyName)) bool
|
||||
}
|
||||
|
||||
type popupListener interface {
|
||||
Run(Popup)
|
||||
rawListener() any
|
||||
}
|
||||
|
||||
type popupListener0 struct {
|
||||
fn func()
|
||||
}
|
||||
|
||||
type popupListener1 struct {
|
||||
fn func(Popup)
|
||||
}
|
||||
|
||||
type popupListenerBinding struct {
|
||||
name string
|
||||
}
|
||||
|
||||
type popupData struct {
|
||||
|
@ -287,7 +305,7 @@ type popupData struct {
|
|||
contentView View
|
||||
buttons []PopupButton
|
||||
cancelable bool
|
||||
dismissListener []func(Popup)
|
||||
dismissListener []popupListener
|
||||
showTransform TransformProperty
|
||||
showOpacity float64
|
||||
showDuration float64
|
||||
|
@ -649,7 +667,7 @@ func (popup *popupData) init(view View, popupParams Params) {
|
|||
}
|
||||
|
||||
case DismissEvent:
|
||||
if listeners, ok := valueToNoArgEventListeners[Popup](value); ok {
|
||||
if listeners, ok := valueToPopupEventListeners(value); ok {
|
||||
if listeners != nil {
|
||||
popup.dismissListener = listeners
|
||||
}
|
||||
|
@ -831,7 +849,7 @@ func (popup *popupData) showAnimation() {
|
|||
}
|
||||
}
|
||||
|
||||
func (popup *popupData) dissmissAnimation(listener func(PropertyName)) bool {
|
||||
func (popup *popupData) dismissAnimation(listener func(PropertyName)) bool {
|
||||
if popup.showOpacity != 1 || popup.showTransform != nil {
|
||||
session := popup.Session()
|
||||
popup.popupView.Set(TransitionEndEvent, listener)
|
||||
|
@ -887,7 +905,7 @@ func (popup *popupData) onDismiss() {
|
|||
popup.Session().callFunc("removeView", popup.layerView.htmlID())
|
||||
|
||||
for _, listener := range popup.dismissListener {
|
||||
listener(popup)
|
||||
listener.Run(popup)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -923,6 +941,25 @@ func NewPopup(view View, param Params) Popup {
|
|||
return popup
|
||||
}
|
||||
|
||||
/*
|
||||
func CreatePopupFromObject(session Session, object DataObject, binding any) Popup {
|
||||
node := object.RemovePropertyByTag(string(Content))
|
||||
if node == nil {
|
||||
ErrorLog(`"content" property not found`)
|
||||
return nil
|
||||
}
|
||||
|
||||
switch node.Type() {
|
||||
case ObjectNode:
|
||||
|
||||
case TextNode:
|
||||
|
||||
default:
|
||||
ErrorLog(`Unsupported data of "content" property`)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
*/
|
||||
// ShowPopup creates a new Popup and shows it
|
||||
func ShowPopup(view View, param Params) Popup {
|
||||
popup := NewPopup(view, param)
|
||||
|
@ -994,23 +1031,162 @@ func (manager *popupManager) dismissPopup(popup Popup) {
|
|||
|
||||
session := popup.Session()
|
||||
listener := func(PropertyName) {
|
||||
if index == count-1 {
|
||||
switch index {
|
||||
case 0:
|
||||
if count == 1 {
|
||||
manager.popups = []Popup{}
|
||||
session.updateCSSProperty("ruiRoot", "pointer-events", "auto")
|
||||
session.updateCSSProperty("ruiPopupLayer", "visibility", "hidden")
|
||||
} else {
|
||||
manager.popups = manager.popups[:count-1]
|
||||
manager.popups = manager.popups[1:]
|
||||
}
|
||||
} else if index == 0 {
|
||||
manager.popups = manager.popups[1:]
|
||||
} else {
|
||||
|
||||
case count - 1:
|
||||
manager.popups = manager.popups[:count-1]
|
||||
|
||||
default:
|
||||
manager.popups = append(manager.popups[:index], manager.popups[index+1:]...)
|
||||
}
|
||||
popup.onDismiss()
|
||||
}
|
||||
|
||||
if !popup.dissmissAnimation(listener) {
|
||||
if !popup.dismissAnimation(listener) {
|
||||
listener("")
|
||||
}
|
||||
}
|
||||
|
||||
func newPopupListener0(fn func()) popupListener {
|
||||
obj := new(popupListener0)
|
||||
obj.fn = fn
|
||||
return obj
|
||||
}
|
||||
|
||||
func (data *popupListener0) Run(_ Popup) {
|
||||
data.fn()
|
||||
}
|
||||
|
||||
func (data *popupListener0) rawListener() any {
|
||||
return data.fn
|
||||
}
|
||||
|
||||
func newPopupListener1(fn func(Popup)) popupListener {
|
||||
obj := new(popupListener1)
|
||||
obj.fn = fn
|
||||
return obj
|
||||
}
|
||||
|
||||
func (data *popupListener1) Run(popup Popup) {
|
||||
data.fn(popup)
|
||||
}
|
||||
|
||||
func (data *popupListener1) rawListener() any {
|
||||
return data.fn
|
||||
}
|
||||
|
||||
func newPopupListenerBinding(name string) popupListener {
|
||||
obj := new(popupListenerBinding)
|
||||
obj.name = name
|
||||
return obj
|
||||
}
|
||||
|
||||
func (data *popupListenerBinding) Run(popup Popup) {
|
||||
bind := popup.View().binding()
|
||||
if bind == nil {
|
||||
ErrorLogF(`There is no a binding object for call "%s"`, data.name)
|
||||
return
|
||||
}
|
||||
|
||||
val := reflect.ValueOf(bind)
|
||||
method := val.MethodByName(data.name)
|
||||
if !method.IsValid() {
|
||||
ErrorLogF(`The "%s" method is not valid`, data.name)
|
||||
return
|
||||
}
|
||||
|
||||
methodType := method.Type()
|
||||
var args []reflect.Value = nil
|
||||
switch methodType.NumIn() {
|
||||
case 0:
|
||||
args = []reflect.Value{}
|
||||
|
||||
case 1:
|
||||
inType := methodType.In(0)
|
||||
if inType == reflect.TypeOf(popup) {
|
||||
args = []reflect.Value{reflect.ValueOf(popup)}
|
||||
}
|
||||
}
|
||||
|
||||
if args != nil {
|
||||
method.Call(args)
|
||||
} else {
|
||||
ErrorLogF(`Unsupported prototype of "%s" method`, data.name)
|
||||
}
|
||||
}
|
||||
|
||||
func (data *popupListenerBinding) rawListener() any {
|
||||
return data.name
|
||||
}
|
||||
|
||||
func valueToPopupEventListeners(value any) ([]popupListener, bool) {
|
||||
if value == nil {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case []popupListener:
|
||||
return value, true
|
||||
|
||||
case popupListener:
|
||||
return []popupListener{value}, true
|
||||
|
||||
case string:
|
||||
return []popupListener{newPopupListenerBinding(value)}, true
|
||||
|
||||
case func(Popup):
|
||||
return []popupListener{newPopupListener1(value)}, true
|
||||
|
||||
case func():
|
||||
return []popupListener{newPopupListener0(value)}, true
|
||||
|
||||
case []func(Popup):
|
||||
result := make([]popupListener, 0, len(value))
|
||||
for _, fn := range value {
|
||||
if fn != nil {
|
||||
result = append(result, newPopupListener1(fn))
|
||||
}
|
||||
}
|
||||
return result, len(result) > 0
|
||||
|
||||
case []func():
|
||||
result := make([]popupListener, 0, len(value))
|
||||
for _, fn := range value {
|
||||
if fn != nil {
|
||||
result = append(result, newPopupListener0(fn))
|
||||
}
|
||||
}
|
||||
return result, len(result) > 0
|
||||
|
||||
case []any:
|
||||
result := make([]popupListener, 0, len(value))
|
||||
for _, v := range value {
|
||||
if v != nil {
|
||||
switch v := v.(type) {
|
||||
case func(Popup):
|
||||
result = append(result, newPopupListener1(v))
|
||||
|
||||
case func():
|
||||
result = append(result, newPopupListener0(v))
|
||||
|
||||
case string:
|
||||
result = append(result, newPopupListenerBinding(v))
|
||||
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
}
|
||||
return result, len(result) > 0
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
|
|
@ -101,13 +101,17 @@ func (progress *progressBarData) htmlProperties(self View, buffer *strings.Build
|
|||
}
|
||||
|
||||
// GetProgressBarMax returns the max value of ProgressBar subview.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetProgressBarMax(view View, subviewID ...string) float64 {
|
||||
return floatStyledProperty(view, subviewID, ProgressBarMax, 1)
|
||||
}
|
||||
|
||||
// GetProgressBarValue returns the value of ProgressBar subview.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetProgressBarValue(view View, subviewID ...string) float64 {
|
||||
return floatStyledProperty(view, subviewID, ProgressBarValue, 0)
|
||||
}
|
||||
|
|
|
@ -2723,4 +2723,6 @@ const (
|
|||
//
|
||||
// Supported types: string.
|
||||
Tooltip PropertyName = "tooltip"
|
||||
|
||||
Binding PropertyName = "binding"
|
||||
)
|
||||
|
|
|
@ -118,7 +118,7 @@ func (resizable *resizableData) setFunc(tag PropertyName, value any) []PropertyN
|
|||
newContent = value
|
||||
|
||||
case DataObject:
|
||||
if newContent = CreateViewFromObject(resizable.Session(), value); newContent == nil {
|
||||
if newContent = CreateViewFromObject(resizable.Session(), value, nil); newContent == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -354,7 +354,7 @@ func (resizable *resizableData) htmlSubviews(self View, buffer *strings.Builder)
|
|||
}
|
||||
|
||||
writePos := func(x1, x2, y1, y2 int) {
|
||||
buffer.WriteString(fmt.Sprintf(` grid-column-start: %d; grid-column-end: %d; grid-row-start: %d; grid-row-end: %d;"></div>`, x1, x2, y1, y2))
|
||||
fmt.Fprintf(buffer, ` grid-column-start: %d; grid-column-end: %d; grid-row-start: %d; grid-row-end: %d;"></div>`, x1, x2, y1, y2)
|
||||
}
|
||||
//htmlID := resizable.htmlID()
|
||||
|
||||
|
|
|
@ -25,8 +25,8 @@ func (view *viewData) onResize(self View, x, y, width, height float64) {
|
|||
view.frame.Top = y
|
||||
view.frame.Width = width
|
||||
view.frame.Height = height
|
||||
for _, listener := range GetResizeListeners(view) {
|
||||
listener(self, view.frame)
|
||||
for _, listener := range getOneArgEventListeners[View, Frame](view, nil, ResizeEvent) {
|
||||
listener.Run(self, view.frame)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,7 +73,9 @@ func (view *viewData) Frame() Frame {
|
|||
}
|
||||
|
||||
// GetViewFrame returns the size and location of view's viewport.
|
||||
// If the second argument (subviewID) is not specified or it is "" then the value of the first argument (view) is returned
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetViewFrame(view View, subviewID ...string) Frame {
|
||||
view = getSubview(view, subviewID)
|
||||
if view == nil {
|
||||
|
@ -83,7 +85,16 @@ func GetViewFrame(view View, subviewID ...string) Frame {
|
|||
}
|
||||
|
||||
// GetResizeListeners returns the list of "resize-event" listeners. If there are no listeners then the empty list is returned
|
||||
// If the second argument (subviewID) is not specified or it is "" then the listeners list of the first argument (view) is returned
|
||||
func GetResizeListeners(view View, subviewID ...string) []func(View, Frame) {
|
||||
return getOneArgEventListeners[View, Frame](view, subviewID, ResizeEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.View, rui.Frame),
|
||||
// - func(rui.View),
|
||||
// - func(rui.Frame),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetResizeListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, Frame](view, subviewID, ResizeEvent)
|
||||
}
|
||||
|
|
|
@ -401,7 +401,7 @@ func OpenRawResource(filename string) fs.File {
|
|||
return nil
|
||||
}
|
||||
|
||||
// AllRawResources returns the list of all raw resouces
|
||||
// AllRawResources returns the list of all raw resources
|
||||
func AllRawResources() []string {
|
||||
result := []string{}
|
||||
|
||||
|
@ -448,7 +448,7 @@ func AllRawResources() []string {
|
|||
return result
|
||||
}
|
||||
|
||||
// AllImageResources returns the list of all image resouces
|
||||
// AllImageResources returns the list of all image resources
|
||||
func AllImageResources() []string {
|
||||
result := make([]string, 0, len(resources.images))
|
||||
for image := range resources.images {
|
||||
|
|
|
@ -25,8 +25,8 @@ func (view *viewData) onScroll(self View, x, y, width, height float64) {
|
|||
view.scroll.Top = y
|
||||
view.scroll.Width = width
|
||||
view.scroll.Height = height
|
||||
for _, listener := range GetScrollListeners(view) {
|
||||
listener(self, view.scroll)
|
||||
for _, listener := range getOneArgEventListeners[View, Frame](view, nil, ScrollEvent) {
|
||||
listener.Run(self, view.scroll)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,9 @@ func (view *viewData) setScroll(x, y, width, height float64) {
|
|||
}
|
||||
|
||||
// GetViewScroll returns ...
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value of the first argument (view) is returned
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetViewScroll(view View, subviewID ...string) Frame {
|
||||
view = getSubview(view, subviewID)
|
||||
if view == nil {
|
||||
|
@ -52,13 +54,24 @@ func GetViewScroll(view View, subviewID ...string) Frame {
|
|||
}
|
||||
|
||||
// GetScrollListeners returns the list of "scroll-event" listeners. If there are no listeners then the empty list is returned
|
||||
// If the second argument (subviewID) is not specified or it is "" then the listeners list of the first argument (view) is returned
|
||||
func GetScrollListeners(view View, subviewID ...string) []func(View, Frame) {
|
||||
return getOneArgEventListeners[View, Frame](view, subviewID, ResizeEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.View, rui.Frame),
|
||||
// - func(rui.View),
|
||||
// - func(rui.Frame),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetScrollListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, Frame](view, subviewID, ScrollEvent)
|
||||
}
|
||||
|
||||
// ScrollTo scrolls the view's content to the given position.
|
||||
// If the second argument (subviewID) is "" then the first argument (view) is used
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func ScrollViewTo(view View, subviewID string, x, y float64) {
|
||||
if subviewID != "" {
|
||||
view = ViewByID(view, subviewID)
|
||||
|
@ -69,7 +82,9 @@ func ScrollViewTo(view View, subviewID string, x, y float64) {
|
|||
}
|
||||
|
||||
// ScrollViewToEnd scrolls the view's content to the start of view.
|
||||
// If the second argument (subviewID) is not specified or it is "" then the first argument (view) is used
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func ScrollViewToStart(view View, subviewID ...string) {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
view.Session().callFunc("scrollToStart", view.htmlID())
|
||||
|
@ -77,7 +92,9 @@ func ScrollViewToStart(view View, subviewID ...string) {
|
|||
}
|
||||
|
||||
// ScrollViewToEnd scrolls the view's content to the end of view.
|
||||
// If the second argument (subviewID) is not specified or it is "" then the first argument (view) is used
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func ScrollViewToEnd(view View, subviewID ...string) {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
view.Session().callFunc("scrollToEnd", view.htmlID())
|
||||
|
|
|
@ -266,7 +266,8 @@ func (session *sessionData) setBridge(events chan DataObject, bridge bridge) {
|
|||
|
||||
func (session *sessionData) close() {
|
||||
if session.events != nil {
|
||||
session.events <- ParseDataText(`session-close{session="` + strconv.Itoa(session.sessionID) + `"}`)
|
||||
obj, _ := ParseDataText(`session-close{session="` + strconv.Itoa(session.sessionID) + `"}`)
|
||||
session.events <- obj
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -208,7 +208,7 @@ func (data *sizeFuncData) writeString(topFunc string, buffer *strings.Builder) {
|
|||
buffer.WriteString(arg.String())
|
||||
|
||||
case float64:
|
||||
buffer.WriteString(fmt.Sprintf("%g", arg))
|
||||
fmt.Fprintf(buffer, "%g", arg)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -302,7 +302,7 @@ func (data *sizeFuncData) writeCSS(topFunc string, buffer *strings.Builder, sess
|
|||
buffer.WriteString(arg.String())
|
||||
|
||||
case float64:
|
||||
buffer.WriteString(fmt.Sprintf("%g", arg))
|
||||
fmt.Fprintf(buffer, "%g", arg)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -334,7 +334,9 @@ func (layout *stackLayoutData) init(session Session) {
|
|||
layout.remove = layout.removeFunc
|
||||
layout.changed = layout.propertyChanged
|
||||
|
||||
layout.setRaw(TransitionEndEvent, []func(View, PropertyName){layout.transitionFinished})
|
||||
layout.setRaw(TransitionEndEvent, []oneArgListener[View, PropertyName]{
|
||||
newOneArgListenerVE(layout.transitionFinished),
|
||||
})
|
||||
if session.TextDirection() == RightToLeftDirection {
|
||||
layout.setRaw(PushTransform, NewTransformProperty(Params{TranslateX: Percent(-100)}))
|
||||
} else {
|
||||
|
@ -434,7 +436,7 @@ func (layout *stackLayoutData) setFunc(tag PropertyName, value any) []PropertyNa
|
|||
// TODO
|
||||
listeners, ok := valueToOneArgEventListeners[View, PropertyName](value)
|
||||
if ok && listeners != nil {
|
||||
listeners = append(listeners, layout.transitionFinished)
|
||||
listeners = append(listeners, newOneArgListenerVE(layout.transitionFinished))
|
||||
layout.setRaw(TransitionEndEvent, listeners)
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
|
@ -464,7 +466,9 @@ func (layout *stackLayoutData) propertyChanged(tag PropertyName) {
|
|||
func (layout *stackLayoutData) removeFunc(tag PropertyName) []PropertyName {
|
||||
switch tag {
|
||||
case TransitionEndEvent:
|
||||
layout.setRaw(TransitionEndEvent, []func(View, PropertyName){layout.transitionFinished})
|
||||
layout.setRaw(TransitionEndEvent, []oneArgListener[View, PropertyName]{
|
||||
newOneArgListenerVE(layout.transitionFinished),
|
||||
})
|
||||
return []PropertyName{tag}
|
||||
}
|
||||
return layout.viewsContainerData.removeFunc(tag)
|
||||
|
@ -606,12 +610,6 @@ func (layout *stackLayoutData) moveToFrontByIndex(index int, onShow []func(View)
|
|||
session.updateCSSProperty(peekPageID, "transform", transformCSS)
|
||||
}
|
||||
|
||||
func (layout *stackLayoutData) contentChanged() {
|
||||
if listener, ok := layout.changeListener[Content]; ok {
|
||||
listener(layout, Content)
|
||||
}
|
||||
}
|
||||
|
||||
func (layout *stackLayoutData) RemovePeek() View {
|
||||
return layout.RemoveView(len(layout.views) - 1)
|
||||
}
|
||||
|
@ -679,9 +677,7 @@ func (layout *stackLayoutData) Append(view View) {
|
|||
}
|
||||
session.appendToInnerHTML(stackID, buffer.String())
|
||||
|
||||
if listener, ok := layout.changeListener[Content]; ok {
|
||||
listener(layout, Content)
|
||||
}
|
||||
layout.contentChanged()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -717,9 +713,7 @@ func (layout *stackLayoutData) Insert(view View, index int) {
|
|||
session := layout.Session()
|
||||
session.appendToInnerHTML(stackID, buffer.String())
|
||||
|
||||
if listener, ok := layout.changeListener[Content]; ok {
|
||||
listener(layout, Content)
|
||||
}
|
||||
layout.contentChanged()
|
||||
}
|
||||
|
||||
// Remove removes view from list and return it
|
||||
|
@ -750,10 +744,7 @@ func (layout *stackLayoutData) RemoveView(index int) View {
|
|||
}
|
||||
|
||||
layout.Session().callFunc("removeView", view.htmlID()+"page")
|
||||
|
||||
if listener, ok := layout.changeListener[Content]; ok {
|
||||
listener(layout, Content)
|
||||
}
|
||||
layout.contentChanged()
|
||||
return view
|
||||
}
|
||||
|
||||
|
@ -914,7 +905,9 @@ func (layout *stackLayoutData) htmlSubviews(self View, buffer *strings.Builder)
|
|||
}
|
||||
|
||||
// IsMoveToFrontAnimation returns "true" if an animation is used when calling the MoveToFront/MoveToFrontByID method of StackLayout interface.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func IsMoveToFrontAnimation(view View, subviewID ...string) bool {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
if value, ok := boolProperty(view, MoveToFrontAnimation, view.Session()); ok {
|
||||
|
@ -950,7 +943,9 @@ func GetPushTiming(view View, subviewID ...string) string {
|
|||
// GetPushTransform returns the start transform (translation, scale and rotation over x, y and z axes as well as a distortion)
|
||||
// for an animated pushing of a child view.
|
||||
// The default value is nil (no transform).
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetPushTransform(view View, subviewID ...string) TransformProperty {
|
||||
return transformStyledProperty(view, subviewID, PushTransform)
|
||||
}
|
||||
|
|
|
@ -50,8 +50,9 @@ func (resources *resourceManager) scanStringsDir(path string) {
|
|||
}
|
||||
|
||||
func loadStringResources(text string) {
|
||||
data := ParseDataText(text)
|
||||
if data == nil {
|
||||
data, err := ParseDataText(text)
|
||||
if err != nil {
|
||||
ErrorLog(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
|
110
tableView.go
110
tableView.go
|
@ -592,14 +592,6 @@ func (table *tableViewData) init(session Session) {
|
|||
table.tag = "TableView"
|
||||
table.cellViews = []View{}
|
||||
table.cellFrame = []Frame{}
|
||||
/*
|
||||
table.cellSelectedListener = []func(TableView, int, int){}
|
||||
table.cellClickedListener = []func(TableView, int, int){}
|
||||
table.rowSelectedListener = []func(TableView, int){}
|
||||
table.rowClickedListener = []func(TableView, int){}
|
||||
table.current.Row = -1
|
||||
table.current.Column = -1
|
||||
*/
|
||||
table.normalize = normalizeTableViewTag
|
||||
table.set = table.setFunc
|
||||
table.changed = table.propertyChanged
|
||||
|
@ -853,8 +845,17 @@ func (table *tableViewData) propertyChanged(tag PropertyName) {
|
|||
current := tableViewCurrent(table)
|
||||
session.callFunc("setTableCellCursorByID", htmlID, current.Row, current.Column)
|
||||
|
||||
for _, listener := range getTwoArgEventListeners[TableView, int](table, nil, TableCellSelectedEvent) {
|
||||
listener.Run(table, current.Row, current.Column)
|
||||
}
|
||||
|
||||
case RowSelection:
|
||||
session.callFunc("setTableRowCursorByID", htmlID, tableViewCurrent(table).Row)
|
||||
current := tableViewCurrent(table)
|
||||
session.callFunc("setTableRowCursorByID", htmlID, current.Row)
|
||||
|
||||
for _, listener := range getOneArgEventListeners[TableView, int](table, nil, TableRowSelectedEvent) {
|
||||
listener.Run(table, current.Row)
|
||||
}
|
||||
}
|
||||
|
||||
case Gap:
|
||||
|
@ -955,63 +956,6 @@ func tableViewCurrentInactiveStyle(view View) string {
|
|||
return "ruiCurrentTableCell"
|
||||
}
|
||||
|
||||
/*
|
||||
func (table *tableViewData) valueToCellListeners(value any) []func(TableView, int, int) {
|
||||
if value == nil {
|
||||
return []func(TableView, int, int){}
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case func(TableView, int, int):
|
||||
return []func(TableView, int, int){value}
|
||||
|
||||
case func(int, int):
|
||||
fn := func(_ TableView, row, column int) {
|
||||
value(row, column)
|
||||
}
|
||||
return []func(TableView, int, int){fn}
|
||||
|
||||
case []func(TableView, int, int):
|
||||
return value
|
||||
|
||||
case []func(int, int):
|
||||
listeners := make([]func(TableView, int, int), len(value))
|
||||
for i, val := range value {
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
listeners[i] = func(_ TableView, row, column int) {
|
||||
val(row, column)
|
||||
}
|
||||
}
|
||||
return listeners
|
||||
|
||||
case []any:
|
||||
listeners := make([]func(TableView, int, int), len(value))
|
||||
for i, val := range value {
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
switch val := val.(type) {
|
||||
case func(TableView, int, int):
|
||||
listeners[i] = val
|
||||
|
||||
case func(int, int):
|
||||
listeners[i] = func(_ TableView, row, column int) {
|
||||
val(row, column)
|
||||
}
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return listeners
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
*/
|
||||
|
||||
func (table *tableViewData) htmlTag() string {
|
||||
return "table"
|
||||
}
|
||||
|
@ -1326,10 +1270,10 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
|
|||
buffer.WriteString(string(value))
|
||||
|
||||
case float32:
|
||||
buffer.WriteString(fmt.Sprintf("%g", float64(value)))
|
||||
fmt.Fprintf(buffer, "%g", float64(value))
|
||||
|
||||
case float64:
|
||||
buffer.WriteString(fmt.Sprintf("%g", value))
|
||||
fmt.Fprintf(buffer, "%g", value)
|
||||
|
||||
case bool:
|
||||
if value {
|
||||
|
@ -1340,7 +1284,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
|
|||
|
||||
default:
|
||||
if n, ok := isInt(value); ok {
|
||||
buffer.WriteString(fmt.Sprintf("%d", n))
|
||||
fmt.Fprintf(buffer, "%d", n)
|
||||
} else {
|
||||
buffer.WriteString("<Unsupported value>")
|
||||
}
|
||||
|
@ -1359,7 +1303,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
|
|||
|
||||
if columnStyle := GetTableColumnStyle(table); columnStyle != nil {
|
||||
buffer.WriteString("<colgroup>")
|
||||
for column := 0; column < columnCount; column++ {
|
||||
for column := range columnCount {
|
||||
cssBuilder.buffer.Reset()
|
||||
if styles := columnStyle.ColumnStyle(column); styles != nil {
|
||||
view.Clear()
|
||||
|
@ -1593,10 +1537,10 @@ func (table *tableViewData) writeCellHtml(adapter TableAdapter, row, column int,
|
|||
buffer.WriteString(string(value))
|
||||
|
||||
case float32:
|
||||
buffer.WriteString(fmt.Sprintf("%g", float64(value)))
|
||||
fmt.Fprintf(buffer, "%g", float64(value))
|
||||
|
||||
case float64:
|
||||
buffer.WriteString(fmt.Sprintf("%g", value))
|
||||
fmt.Fprintf(buffer, "%g", value)
|
||||
|
||||
case bool:
|
||||
accentColor := Color(0)
|
||||
|
@ -1611,7 +1555,7 @@ func (table *tableViewData) writeCellHtml(adapter TableAdapter, row, column int,
|
|||
|
||||
default:
|
||||
if n, ok := isInt(value); ok {
|
||||
buffer.WriteString(fmt.Sprintf("%d", n))
|
||||
fmt.Fprintf(buffer, "%d", n)
|
||||
} else {
|
||||
buffer.WriteString("<Unsupported value>")
|
||||
}
|
||||
|
@ -1732,11 +1676,11 @@ func (table *tableViewData) handleCommand(self View, command PropertyName, data
|
|||
current.Row = row
|
||||
table.setRaw(Current, current.Row)
|
||||
if listener, ok := table.changeListener[Current]; ok {
|
||||
listener(table, Current)
|
||||
listener.Run(table, Current)
|
||||
}
|
||||
|
||||
for _, listener := range GetTableRowSelectedListeners(table) {
|
||||
listener(table, row)
|
||||
for _, listener := range getOneArgEventListeners[TableView, int](table, nil, TableRowSelectedEvent) {
|
||||
listener.Run(table, row)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1749,11 +1693,11 @@ func (table *tableViewData) handleCommand(self View, command PropertyName, data
|
|||
current.Column = column
|
||||
table.setRaw(Current, current.Row)
|
||||
if listener, ok := table.changeListener[Current]; ok {
|
||||
listener(table, Current)
|
||||
listener.Run(table, Current)
|
||||
}
|
||||
|
||||
for _, listener := range GetTableCellSelectedListeners(table) {
|
||||
listener(table, row, column)
|
||||
for _, listener := range getTwoArgEventListeners[TableView, int](table, nil, TableCellSelectedEvent) {
|
||||
listener.Run(table, row, column)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1761,16 +1705,16 @@ func (table *tableViewData) handleCommand(self View, command PropertyName, data
|
|||
|
||||
case "rowClick":
|
||||
if row, ok := dataIntProperty(data, "row"); ok {
|
||||
for _, listener := range GetTableRowClickedListeners(table) {
|
||||
listener(table, row)
|
||||
for _, listener := range getOneArgEventListeners[TableView, int](table, nil, TableRowClickedEvent) {
|
||||
listener.Run(table, row)
|
||||
}
|
||||
}
|
||||
|
||||
case "cellClick":
|
||||
if row, ok := dataIntProperty(data, "row"); ok {
|
||||
if column, ok := dataIntProperty(data, "column"); ok {
|
||||
for _, listener := range GetTableCellClickedListeners(table) {
|
||||
listener(table, row, column)
|
||||
for _, listener := range getTwoArgEventListeners[TableView, int](table, nil, TableCellClickedEvent) {
|
||||
listener.Run(table, row, column)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,9 @@ func (cell *tableCellView) cssStyle(self View, builder cssBuilder) {
|
|||
}
|
||||
|
||||
// GetTableContent returns a TableAdapter which defines the TableView content.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTableContent(view View, subviewID ...string) TableAdapter {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
if content := view.getRaw(Content); content != nil {
|
||||
|
@ -40,7 +42,9 @@ func GetTableContent(view View, subviewID ...string) TableAdapter {
|
|||
}
|
||||
|
||||
// GetTableRowStyle returns a TableRowStyle which defines styles of TableView rows.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTableRowStyle(view View, subviewID ...string) TableRowStyle {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
for _, tag := range []PropertyName{RowStyle, Content} {
|
||||
|
@ -56,7 +60,9 @@ func GetTableRowStyle(view View, subviewID ...string) TableRowStyle {
|
|||
}
|
||||
|
||||
// GetTableColumnStyle returns a TableColumnStyle which defines styles of TableView columns.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTableColumnStyle(view View, subviewID ...string) TableColumnStyle {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
for _, tag := range []PropertyName{ColumnStyle, Content} {
|
||||
|
@ -72,7 +78,9 @@ func GetTableColumnStyle(view View, subviewID ...string) TableColumnStyle {
|
|||
}
|
||||
|
||||
// GetTableCellStyle returns a TableCellStyle which defines styles of TableView cells.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTableCellStyle(view View, subviewID ...string) TableCellStyle {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
for _, tag := range []PropertyName{CellStyle, Content} {
|
||||
|
@ -90,26 +98,34 @@ func GetTableCellStyle(view View, subviewID ...string) TableCellStyle {
|
|||
|
||||
// GetTableSelectionMode returns the mode of the TableView elements selection.
|
||||
// Valid values are NoneSelection (0), CellSelection (1), and RowSelection (2).
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTableSelectionMode(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, SelectionMode, NoneSelection, false)
|
||||
}
|
||||
|
||||
// GetTableVerticalAlign returns a vertical align in a TableView cell. Returns one of next values:
|
||||
// TopAlign (0), BottomAlign (1), CenterAlign (2), and BaselineAlign (3)
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTableVerticalAlign(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, TableVerticalAlign, TopAlign, false)
|
||||
}
|
||||
|
||||
// GetTableHeadHeight returns the number of rows in the table header.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTableHeadHeight(view View, subviewID ...string) int {
|
||||
return intStyledProperty(view, subviewID, HeadHeight, 0)
|
||||
}
|
||||
|
||||
// GetTableFootHeight returns the number of rows in the table footer.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTableFootHeight(view View, subviewID ...string) int {
|
||||
return intStyledProperty(view, subviewID, FootHeight, 0)
|
||||
}
|
||||
|
@ -118,7 +134,9 @@ func GetTableFootHeight(view View, subviewID ...string) int {
|
|||
// If there is no selected cell/row or the selection mode is NoneSelection (0),
|
||||
// then a value of the row and column index less than 0 is returned.
|
||||
// If the selection mode is RowSelection (2) then the returned column index is less than 0.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTableCurrent(view View, subviewID ...string) CellIndex {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
if selectionMode := GetTableSelectionMode(view); selectionMode != NoneSelection {
|
||||
|
@ -130,30 +148,70 @@ func GetTableCurrent(view View, subviewID ...string) CellIndex {
|
|||
|
||||
// GetTableCellClickedListeners returns listeners of event which occurs when the user clicks on a table cell.
|
||||
// If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetTableCellClickedListeners(view View, subviewID ...string) []func(TableView, int, int) {
|
||||
return getTwoArgEventListeners[TableView, int](view, subviewID, TableCellClickedEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.TableView, int, int),
|
||||
// - func(rui.TableView, int),
|
||||
// - func(rui.TableView),
|
||||
// - func(int, int),
|
||||
// - func(int),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTableCellClickedListeners(view View, subviewID ...string) []any {
|
||||
return getTwoArgEventRawListeners[TableView, int](view, subviewID, TableCellClickedEvent)
|
||||
}
|
||||
|
||||
// GetTableCellSelectedListeners returns listeners of event which occurs when a table cell becomes selected.
|
||||
// If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetTableCellSelectedListeners(view View, subviewID ...string) []func(TableView, int, int) {
|
||||
return getTwoArgEventListeners[TableView, int](view, subviewID, TableCellSelectedEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.TableView, int, int),
|
||||
// - func(rui.TableView, int),
|
||||
// - func(rui.TableView),
|
||||
// - func(int, int),
|
||||
// - func(int),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTableCellSelectedListeners(view View, subviewID ...string) []any {
|
||||
return getTwoArgEventRawListeners[TableView, int](view, subviewID, TableCellSelectedEvent)
|
||||
}
|
||||
|
||||
// GetTableRowClickedListeners returns listeners of event which occurs when the user clicks on a table row.
|
||||
// If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetTableRowClickedListeners(view View, subviewID ...string) []func(TableView, int) {
|
||||
return getOneArgEventListeners[TableView, int](view, subviewID, TableRowClickedEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.TableView, int),
|
||||
// - func(rui.TableView),
|
||||
// - func(int),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTableRowClickedListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[TableView, int](view, subviewID, TableRowClickedEvent)
|
||||
}
|
||||
|
||||
// GetTableRowSelectedListeners returns listeners of event which occurs when a table row becomes selected.
|
||||
// If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetTableRowSelectedListeners(view View, subviewID ...string) []func(TableView, int) {
|
||||
return getOneArgEventListeners[TableView, int](view, subviewID, TableRowSelectedEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.TableView, int),
|
||||
// - func(rui.TableView),
|
||||
// - func(int),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTableRowSelectedListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[TableView, int](view, subviewID, TableRowSelectedEvent)
|
||||
}
|
||||
|
||||
// ReloadTableViewData updates TableView
|
||||
|
|
201
tabsLayout.go
201
tabsLayout.go
|
@ -211,7 +211,7 @@ func (tabsLayout *tabsLayoutData) propertyChanged(tag PropertyName) {
|
|||
if listeners := getTwoArgEventListeners[TabsLayout, int](tabsLayout, nil, CurrentTabChangedEvent); len(listeners) > 0 {
|
||||
oldCurrent, _ := intProperty(tabsLayout, "old-current", session, -1)
|
||||
for _, listener := range listeners {
|
||||
listener(tabsLayout, current, oldCurrent)
|
||||
listener.Run(tabsLayout, current, oldCurrent)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -238,155 +238,6 @@ func (tabsLayout *tabsLayoutData) propertyChanged(tag PropertyName) {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func (tabsLayout *tabsLayoutData) valueToTabListeners(value any) []func(TabsLayout, int, int) {
|
||||
if value == nil {
|
||||
return []func(TabsLayout, int, int){}
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case func(TabsLayout, int, int):
|
||||
return []func(TabsLayout, int, int){value}
|
||||
|
||||
case func(TabsLayout, int):
|
||||
fn := func(view TabsLayout, current, _ int) {
|
||||
value(view, current)
|
||||
}
|
||||
return []func(TabsLayout, int, int){fn}
|
||||
|
||||
case func(TabsLayout):
|
||||
fn := func(view TabsLayout, _, _ int) {
|
||||
value(view)
|
||||
}
|
||||
return []func(TabsLayout, int, int){fn}
|
||||
|
||||
case func(int, int):
|
||||
fn := func(_ TabsLayout, current, old int) {
|
||||
value(current, old)
|
||||
}
|
||||
return []func(TabsLayout, int, int){fn}
|
||||
|
||||
case func(int):
|
||||
fn := func(_ TabsLayout, current, _ int) {
|
||||
value(current)
|
||||
}
|
||||
return []func(TabsLayout, int, int){fn}
|
||||
|
||||
case func():
|
||||
fn := func(TabsLayout, int, int) {
|
||||
value()
|
||||
}
|
||||
return []func(TabsLayout, int, int){fn}
|
||||
|
||||
case []func(TabsLayout, int, int):
|
||||
return value
|
||||
|
||||
case []func(TabsLayout, int):
|
||||
listeners := make([]func(TabsLayout, int, int), len(value))
|
||||
for i, val := range value {
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
listeners[i] = func(view TabsLayout, current, _ int) {
|
||||
val(view, current)
|
||||
}
|
||||
}
|
||||
return listeners
|
||||
|
||||
case []func(TabsLayout):
|
||||
listeners := make([]func(TabsLayout, int, int), len(value))
|
||||
for i, val := range value {
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
listeners[i] = func(view TabsLayout, _, _ int) {
|
||||
val(view)
|
||||
}
|
||||
}
|
||||
return listeners
|
||||
|
||||
case []func(int, int):
|
||||
listeners := make([]func(TabsLayout, int, int), len(value))
|
||||
for i, val := range value {
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
listeners[i] = func(_ TabsLayout, current, old int) {
|
||||
val(current, old)
|
||||
}
|
||||
}
|
||||
return listeners
|
||||
|
||||
case []func(int):
|
||||
listeners := make([]func(TabsLayout, int, int), len(value))
|
||||
for i, val := range value {
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
listeners[i] = func(_ TabsLayout, current, _ int) {
|
||||
val(current)
|
||||
}
|
||||
}
|
||||
return listeners
|
||||
|
||||
case []func():
|
||||
listeners := make([]func(TabsLayout, int, int), len(value))
|
||||
for i, val := range value {
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
listeners[i] = func(TabsLayout, int, int) {
|
||||
val()
|
||||
}
|
||||
}
|
||||
return listeners
|
||||
|
||||
case []any:
|
||||
listeners := make([]func(TabsLayout, int, int), len(value))
|
||||
for i, val := range value {
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
switch val := val.(type) {
|
||||
case func(TabsLayout, int, int):
|
||||
listeners[i] = val
|
||||
|
||||
case func(TabsLayout, int):
|
||||
listeners[i] = func(view TabsLayout, current, _ int) {
|
||||
val(view, current)
|
||||
}
|
||||
|
||||
case func(TabsLayout):
|
||||
listeners[i] = func(view TabsLayout, _, _ int) {
|
||||
val(view)
|
||||
}
|
||||
|
||||
case func(int, int):
|
||||
listeners[i] = func(_ TabsLayout, current, old int) {
|
||||
val(current, old)
|
||||
}
|
||||
|
||||
case func(int):
|
||||
listeners[i] = func(_ TabsLayout, current, _ int) {
|
||||
val(current)
|
||||
}
|
||||
|
||||
case func():
|
||||
listeners[i] = func(TabsLayout, int, int) {
|
||||
val()
|
||||
}
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return listeners
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
*/
|
||||
|
||||
func (tabsLayout *tabsLayoutData) tabsLocation() int {
|
||||
tabs, _ := enumProperty(tabsLayout, Tabs, tabsLayout.session, 0)
|
||||
return tabs
|
||||
|
@ -504,7 +355,7 @@ func (tabsLayout *tabsLayoutData) ListItem(index int, session Session) View {
|
|||
Content: "✕",
|
||||
ClickEvent: func() {
|
||||
for _, listener := range getOneArgEventListeners[TabsLayout, int](tabsLayout, nil, TabCloseEvent) {
|
||||
listener(tabsLayout, index)
|
||||
listener.Run(tabsLayout, index)
|
||||
}
|
||||
},
|
||||
}))
|
||||
|
@ -552,9 +403,7 @@ func (tabsLayout *tabsLayoutData) Append(view View) {
|
|||
view.SetChangeListener(TabCloseButton, tabsLayout.updateTabCloseButton)
|
||||
if tabsLayout.created {
|
||||
updateInnerHTML(tabsLayout.htmlID(), tabsLayout.Session())
|
||||
if listener, ok := tabsLayout.changeListener[Content]; ok {
|
||||
listener(tabsLayout, Content)
|
||||
}
|
||||
tabsLayout.contentChanged()
|
||||
tabsLayout.Set(Current, len(tabsLayout.views)-1)
|
||||
}
|
||||
}
|
||||
|
@ -576,11 +425,9 @@ func (tabsLayout *tabsLayoutData) Insert(view View, index int) {
|
|||
|
||||
func (tabsLayout *tabsLayoutData) currentChanged(newCurrent, oldCurrent int) {
|
||||
for _, listener := range getTwoArgEventListeners[TabsLayout, int](tabsLayout, nil, CurrentTabChangedEvent) {
|
||||
listener(tabsLayout, newCurrent, oldCurrent)
|
||||
}
|
||||
if listener, ok := tabsLayout.changeListener[Current]; ok {
|
||||
listener(tabsLayout, Current)
|
||||
listener.Run(tabsLayout, newCurrent, oldCurrent)
|
||||
}
|
||||
tabsLayout.contentChanged()
|
||||
}
|
||||
|
||||
// Remove removes view from list and return it
|
||||
|
@ -606,9 +453,7 @@ func (tabsLayout *tabsLayoutData) RemoveView(index int) View {
|
|||
tabsLayout.Set(Current, newCurrent)
|
||||
}
|
||||
updateInnerHTML(tabsLayout.htmlID(), tabsLayout.Session())
|
||||
if listener, ok := tabsLayout.changeListener[Content]; ok {
|
||||
listener(tabsLayout, Content)
|
||||
}
|
||||
tabsLayout.contentChanged()
|
||||
} else if newCurrent != oldCurrent {
|
||||
tabsLayout.setRaw(Current, newCurrent)
|
||||
}
|
||||
|
@ -892,7 +737,7 @@ func (tabsLayout *tabsLayoutData) handleCommand(self View, command PropertyName,
|
|||
if numberText, ok := data.PropertyValue("number"); ok {
|
||||
if number, err := strconv.Atoi(numberText); err == nil {
|
||||
for _, listener := range getOneArgEventListeners[TabsLayout, int](tabsLayout, nil, TabCloseEvent) {
|
||||
listener(tabsLayout, number)
|
||||
listener.Run(tabsLayout, number)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -900,3 +745,35 @@ func (tabsLayout *tabsLayoutData) handleCommand(self View, command PropertyName,
|
|||
}
|
||||
return tabsLayout.viewsContainerData.handleCommand(self, command, data)
|
||||
}
|
||||
|
||||
// GetTabCloseEventListeners returns the "tab-close-event" listener list. If there are no listeners then the empty list is returned.
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.TabsLayout, int),
|
||||
// - func(rui.TabsLayout),
|
||||
// - func(int),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTabCloseEventListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[TabsLayout, int](view, subviewID, TabCloseEvent)
|
||||
}
|
||||
|
||||
// GetCurrentTabChangedEventListeners returns the "current-tab-changed" listener list. If there are no listeners then the empty list is returned.
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.TabsLayout, int, int),
|
||||
// - func(rui.TabsLayout, int),
|
||||
// - func(rui.TabsLayout),
|
||||
// - func(int, int),
|
||||
// - func(int),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetCurrentTabChangedEventListeners(view View, subviewID ...string) []any {
|
||||
return getTwoArgEventRawListeners[TabsLayout, int](view, subviewID, CurrentTabChangedEvent)
|
||||
}
|
||||
|
|
|
@ -111,7 +111,9 @@ func (textView *textViewData) htmlSubviews(self View, buffer *strings.Builder) {
|
|||
|
||||
// GetTextOverflow returns a value of the "text-overflow" property:
|
||||
// TextOverflowClip (0) or TextOverflowEllipsis (1).
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTextOverflow(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, TextOverflow, SingleLineText, false)
|
||||
}
|
||||
|
|
19
theme.go
19
theme.go
|
@ -686,8 +686,11 @@ func (theme *theme) addText(themeText string) bool {
|
|||
theme.init()
|
||||
}
|
||||
|
||||
data := ParseDataText(themeText)
|
||||
if data == nil || !data.IsObject() || data.Tag() != "theme" {
|
||||
data, err := ParseDataText(themeText)
|
||||
if err != nil {
|
||||
ErrorLog(err.Error())
|
||||
return false
|
||||
} else if !data.IsObject() || data.Tag() != "theme" {
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -975,10 +978,10 @@ func (theme *theme) String() string {
|
|||
buffer.WriteString(":landscape")
|
||||
}
|
||||
if maxWidth > 0 {
|
||||
buffer.WriteString(fmt.Sprintf(":width%d", maxWidth))
|
||||
fmt.Fprintf(buffer, ":width%d", maxWidth)
|
||||
}
|
||||
if maxHeight > 0 {
|
||||
buffer.WriteString(fmt.Sprintf(":height%d", maxHeight))
|
||||
fmt.Fprintf(buffer, ":height%d", maxHeight)
|
||||
}
|
||||
buffer.WriteString(" = [\n")
|
||||
|
||||
|
@ -1014,21 +1017,21 @@ func (theme *theme) String() string {
|
|||
}
|
||||
|
||||
if media.MinWidth > 0 {
|
||||
buffer.WriteString(fmt.Sprintf(":width%d-", media.MinWidth))
|
||||
fmt.Fprintf(buffer, ":width%d-", media.MinWidth)
|
||||
if media.MaxWidth > 0 {
|
||||
buffer.WriteString(strconv.Itoa(media.MaxWidth))
|
||||
}
|
||||
} else if media.MaxWidth > 0 {
|
||||
buffer.WriteString(fmt.Sprintf(":width%d", media.MaxWidth))
|
||||
fmt.Fprintf(buffer, ":width%d", media.MaxWidth)
|
||||
}
|
||||
|
||||
if media.MinHeight > 0 {
|
||||
buffer.WriteString(fmt.Sprintf(":height%d-", media.MinHeight))
|
||||
fmt.Fprintf(buffer, ":height%d-", media.MinHeight)
|
||||
if media.MaxHeight > 0 {
|
||||
buffer.WriteString(strconv.Itoa(media.MaxHeight))
|
||||
}
|
||||
} else if media.MaxHeight > 0 {
|
||||
buffer.WriteString(fmt.Sprintf(":height%d", media.MaxHeight))
|
||||
fmt.Fprintf(buffer, ":height%d", media.MaxHeight)
|
||||
}
|
||||
|
||||
buffer.WriteString(" = [\n")
|
||||
|
|
|
@ -246,7 +246,7 @@ func (picker *timePickerData) propertyChanged(tag PropertyName) {
|
|||
value := GetTimePickerValue(picker)
|
||||
session.callFunc("setInputValue", picker.htmlID(), value.Format(timeFormat))
|
||||
|
||||
if listeners := GetTimeChangedListeners(picker); len(listeners) > 0 {
|
||||
if listeners := getTwoArgEventListeners[TimePicker, time.Time](picker, nil, TimeChangedEvent); len(listeners) > 0 {
|
||||
oldTime := time.Now()
|
||||
if val := picker.getRaw("old-time"); val != nil {
|
||||
if time, ok := val.(time.Time); ok {
|
||||
|
@ -254,7 +254,7 @@ func (picker *timePickerData) propertyChanged(tag PropertyName) {
|
|||
}
|
||||
}
|
||||
for _, listener := range listeners {
|
||||
listener(picker, value, oldTime)
|
||||
listener.Run(picker, value, oldTime)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -320,11 +320,11 @@ func (picker *timePickerData) handleCommand(self View, command PropertyName, dat
|
|||
oldValue := GetTimePickerValue(picker)
|
||||
picker.properties[TimePickerValue] = value
|
||||
if value != oldValue {
|
||||
for _, listener := range GetTimeChangedListeners(picker) {
|
||||
listener(picker, value, oldValue)
|
||||
for _, listener := range getTwoArgEventListeners[TimePicker, time.Time](picker, nil, TimeChangedEvent) {
|
||||
listener.Run(picker, value, oldValue)
|
||||
}
|
||||
if listener, ok := picker.changeListener[TimePickerValue]; ok {
|
||||
listener(picker, TimePickerValue)
|
||||
listener.Run(picker, TimePickerValue)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -373,7 +373,9 @@ func getTimeProperty(view View, mainTag, shortTag PropertyName) (time.Time, bool
|
|||
|
||||
// GetTimePickerMin returns the min time of TimePicker subview and "true" as the second value if the min time is set,
|
||||
// "false" as the second value otherwise.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTimePickerMin(view View, subviewID ...string) (time.Time, bool) {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
return getTimeProperty(view, TimePickerMin, Min)
|
||||
|
@ -383,7 +385,9 @@ func GetTimePickerMin(view View, subviewID ...string) (time.Time, bool) {
|
|||
|
||||
// GetTimePickerMax returns the max time of TimePicker subview and "true" as the second value if the min time is set,
|
||||
// "false" as the second value otherwise.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTimePickerMax(view View, subviewID ...string) (time.Time, bool) {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
return getTimeProperty(view, TimePickerMax, Max)
|
||||
|
@ -392,13 +396,17 @@ func GetTimePickerMax(view View, subviewID ...string) (time.Time, bool) {
|
|||
}
|
||||
|
||||
// GetTimePickerStep returns the time changing step in seconds of TimePicker subview.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTimePickerStep(view View, subviewID ...string) int {
|
||||
return intStyledProperty(view, subviewID, TimePickerStep, 60)
|
||||
}
|
||||
|
||||
// GetTimePickerValue returns the time of TimePicker subview.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTimePickerValue(view View, subviewID ...string) time.Time {
|
||||
if view = getSubview(view, subviewID); view == nil {
|
||||
return time.Now()
|
||||
|
@ -409,7 +417,18 @@ func GetTimePickerValue(view View, subviewID ...string) time.Time {
|
|||
|
||||
// GetTimeChangedListeners returns the TimeChangedListener list of an TimePicker subview.
|
||||
// If there are no listeners then the empty list is returned
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetTimeChangedListeners(view View, subviewID ...string) []func(TimePicker, time.Time, time.Time) {
|
||||
return getTwoArgEventListeners[TimePicker, time.Time](view, subviewID, TimeChangedEvent)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.TimePicker, time.Time, time.Time),
|
||||
// - func(rui.TimePicker, time.Time),
|
||||
// - func(rui.TimePicker),
|
||||
// - func(time.Time, time.Time),
|
||||
// - func(time.Time),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTimeChangedListeners(view View, subviewID ...string) []any {
|
||||
return getTwoArgEventRawListeners[TimePicker, time.Time](view, subviewID, TimeChangedEvent)
|
||||
}
|
||||
|
|
|
@ -193,30 +193,66 @@ func handleTouchEvents(view View, tag PropertyName, data DataObject) {
|
|||
event.init(data)
|
||||
|
||||
for _, listener := range listeners {
|
||||
listener(view, event)
|
||||
listener.Run(view, event)
|
||||
}
|
||||
}
|
||||
|
||||
// GetTouchStartListeners returns the "touch-start" listener list. If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetTouchStartListeners(view View, subviewID ...string) []func(View, TouchEvent) {
|
||||
return getOneArgEventListeners[View, TouchEvent](view, subviewID, TouchStart)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.View, rui.TouchEvent),
|
||||
// - func(rui.View),
|
||||
// - func(rui.TouchEvent),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTouchStartListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, TouchEvent](view, subviewID, TouchStart)
|
||||
}
|
||||
|
||||
// GetTouchEndListeners returns the "touch-end" listener list. If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetTouchEndListeners(view View, subviewID ...string) []func(View, TouchEvent) {
|
||||
return getOneArgEventListeners[View, TouchEvent](view, subviewID, TouchEnd)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.View, rui.TouchEvent),
|
||||
// - func(rui.View),
|
||||
// - func(rui.TouchEvent),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTouchEndListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, TouchEvent](view, subviewID, TouchEnd)
|
||||
}
|
||||
|
||||
// GetTouchMoveListeners returns the "touch-move" listener list. If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetTouchMoveListeners(view View, subviewID ...string) []func(View, TouchEvent) {
|
||||
return getOneArgEventListeners[View, TouchEvent](view, subviewID, TouchMove)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.View, rui.TouchEvent),
|
||||
// - func(rui.View),
|
||||
// - func(rui.TouchEvent),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTouchMoveListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, TouchEvent](view, subviewID, TouchMove)
|
||||
}
|
||||
|
||||
// GetTouchCancelListeners returns the "touch-cancel" listener list. If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
func GetTouchCancelListeners(view View, subviewID ...string) []func(View, TouchEvent) {
|
||||
return getOneArgEventListeners[View, TouchEvent](view, subviewID, TouchCancel)
|
||||
//
|
||||
// Result elements can be of the following types:
|
||||
// - func(rui.View, rui.TouchEvent),
|
||||
// - func(rui.View),
|
||||
// - func(rui.TouchEvent),
|
||||
// - func(),
|
||||
// - string.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTouchCancelListeners(view View, subviewID ...string) []any {
|
||||
return getOneArgEventRawListeners[View, TouchEvent](view, subviewID, TouchCancel)
|
||||
}
|
||||
|
|
41
transform.go
41
transform.go
|
@ -398,7 +398,10 @@ func valueToTransformProperty(value any) TransformProperty {
|
|||
}
|
||||
|
||||
case string:
|
||||
if obj := ParseDataText(value); obj != nil {
|
||||
obj, err := ParseDataText(value)
|
||||
if err != nil {
|
||||
ErrorLog(err.Error())
|
||||
} else {
|
||||
return parseObject(obj)
|
||||
}
|
||||
}
|
||||
|
@ -647,7 +650,9 @@ func transformOriginCSS(x, y, z SizeUnit, session Session) string {
|
|||
|
||||
// GetTransform returns a view transform: translation, scale and rotation over x, y and z axes as well as a distortion of a view along x and y axes.
|
||||
// The default value is nil (no transform)
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTransform(view View, subviewID ...string) TransformProperty {
|
||||
return transformStyledProperty(view, subviewID, Transform)
|
||||
}
|
||||
|
@ -655,14 +660,18 @@ func GetTransform(view View, subviewID ...string) TransformProperty {
|
|||
// GetPerspective returns a distance between the z = 0 plane and the user in order to give a 3D-positioned
|
||||
// element some perspective. Each 3D element with z > 0 becomes larger; each 3D-element with z < 0 becomes smaller.
|
||||
// The default value is 0 (no 3D effects).
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetPerspective(view View, subviewID ...string) SizeUnit {
|
||||
return sizeStyledProperty(view, subviewID, Perspective, false)
|
||||
}
|
||||
|
||||
// GetPerspectiveOrigin returns a x- and y-coordinate of the position at which the viewer is looking.
|
||||
// It is used as the vanishing point by the Perspective property. The default value is (50%, 50%).
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetPerspectiveOrigin(view View, subviewID ...string) (SizeUnit, SizeUnit) {
|
||||
view = getSubview(view, subviewID)
|
||||
if view == nil {
|
||||
|
@ -675,14 +684,18 @@ func GetPerspectiveOrigin(view View, subviewID ...string) (SizeUnit, SizeUnit) {
|
|||
// visible when turned towards the user. Values:
|
||||
// true - the back face is visible when turned towards the user (default value).
|
||||
// false - the back face is hidden, effectively making the element invisible when turned away from the user.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetBackfaceVisible(view View, subviewID ...string) bool {
|
||||
return boolStyledProperty(view, subviewID, BackfaceVisible, false)
|
||||
}
|
||||
|
||||
// GetTransformOrigin returns a x-, y-, and z-coordinate of the point around which a view transformation is applied.
|
||||
// The default value is (50%, 50%, 50%).
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTransformOrigin(view View, subviewID ...string) (SizeUnit, SizeUnit, SizeUnit) {
|
||||
view = getSubview(view, subviewID)
|
||||
if view == nil {
|
||||
|
@ -692,7 +705,9 @@ func GetTransformOrigin(view View, subviewID ...string) (SizeUnit, SizeUnit, Siz
|
|||
}
|
||||
|
||||
// GetTranslate returns a x-, y-, and z-axis translation value of a 2D/3D translation
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTranslate(view View, subviewID ...string) (SizeUnit, SizeUnit, SizeUnit) {
|
||||
if transform := GetTransform(view, subviewID...); transform != nil {
|
||||
return transform.getTranslate(view.Session())
|
||||
|
@ -702,7 +717,9 @@ func GetTranslate(view View, subviewID ...string) (SizeUnit, SizeUnit, SizeUnit)
|
|||
|
||||
// GetSkew returns a angles to use to distort the element along the abscissa (x-axis)
|
||||
// and the ordinate (y-axis). The default value is 0.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetSkew(view View, subviewID ...string) (AngleUnit, AngleUnit) {
|
||||
if transform := GetTransform(view, subviewID...); transform != nil {
|
||||
x, y, _ := transform.getSkew(view.Session())
|
||||
|
@ -712,7 +729,9 @@ func GetSkew(view View, subviewID ...string) (AngleUnit, AngleUnit) {
|
|||
}
|
||||
|
||||
// GetScale returns a x-, y-, and z-axis scaling value of a 2D/3D scale. The default value is 1.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetScale(view View, subviewID ...string) (float64, float64, float64) {
|
||||
if transform := GetTransform(view, subviewID...); transform != nil {
|
||||
session := view.Session()
|
||||
|
@ -725,7 +744,9 @@ func GetScale(view View, subviewID ...string) (float64, float64, float64) {
|
|||
}
|
||||
|
||||
// GetRotate returns a x-, y, z-coordinate of the vector denoting the axis of rotation, and the angle of the view rotation
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetRotate(view View, subviewID ...string) (float64, float64, float64, AngleUnit) {
|
||||
if transform := GetTransform(view, subviewID...); transform != nil {
|
||||
session := view.Session()
|
||||
|
|
141
view.go
141
view.go
|
@ -30,6 +30,8 @@ func (frame Frame) Bottom() float64 {
|
|||
return frame.Top + frame.Height
|
||||
}
|
||||
|
||||
const changeListeners PropertyName = "change-listeners"
|
||||
|
||||
// View represents a base view interface
|
||||
type View interface {
|
||||
ViewStyle
|
||||
|
@ -66,8 +68,18 @@ type View interface {
|
|||
// a description of the error is written to the log
|
||||
SetAnimated(tag PropertyName, value any, animation AnimationProperty) bool
|
||||
|
||||
// SetChangeListener set the function to track the change of the View property
|
||||
SetChangeListener(tag PropertyName, listener func(View, PropertyName))
|
||||
// SetChangeListener set the function (the second argument) to track the change of the View property (the first argument).
|
||||
//
|
||||
// Allowed listener function formats:
|
||||
//
|
||||
// func(view rui.View, property rui.PropertyName)
|
||||
// func(view rui.View)
|
||||
// func(property rui.PropertyName)
|
||||
// func()
|
||||
// string
|
||||
//
|
||||
// If the second argument is given as a string, it specifies the name of the binding function.
|
||||
SetChangeListener(tag PropertyName, listener any) bool
|
||||
|
||||
// HasFocus returns 'true' if the view has focus
|
||||
HasFocus() bool
|
||||
|
@ -90,8 +102,9 @@ type View interface {
|
|||
htmlProperties(self View, buffer *strings.Builder)
|
||||
cssStyle(self View, builder cssBuilder)
|
||||
addToCSSStyle(addCSS map[string]string)
|
||||
exscludeTags() []PropertyName
|
||||
excludeTags() []PropertyName
|
||||
htmlDisabledProperty() bool
|
||||
binding() any
|
||||
|
||||
onResize(self View, x, y, width, height float64)
|
||||
onItemResize(self View, index string, x, y, width, height float64)
|
||||
|
@ -109,7 +122,7 @@ type viewData struct {
|
|||
_htmlID string
|
||||
parentID string
|
||||
systemClass string
|
||||
changeListener map[PropertyName]func(View, PropertyName)
|
||||
changeListener map[PropertyName]oneArgListener[View, PropertyName]
|
||||
singleTransition map[PropertyName]AnimationProperty
|
||||
addCSS map[string]string
|
||||
frame Frame
|
||||
|
@ -161,7 +174,7 @@ func (view *viewData) init(session Session) {
|
|||
view.changed = view.propertyChanged
|
||||
view.tag = "View"
|
||||
view.session = session
|
||||
view.changeListener = map[PropertyName]func(View, PropertyName){}
|
||||
view.changeListener = map[PropertyName]oneArgListener[View, PropertyName]{}
|
||||
view.addCSS = map[string]string{}
|
||||
//view.animation = map[string]AnimationEndListener{}
|
||||
view.singleTransition = map[PropertyName]AnimationProperty{}
|
||||
|
@ -195,6 +208,18 @@ func (view *viewData) ID() string {
|
|||
return view.viewID
|
||||
}
|
||||
|
||||
func (view *viewData) binding() any {
|
||||
if result := view.getRaw(Binding); result != nil {
|
||||
return result
|
||||
}
|
||||
|
||||
if parent := view.Parent(); parent != nil {
|
||||
return parent.binding()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (view *viewData) ViewByID(id string) View {
|
||||
if id == view.ID() {
|
||||
if v := view.session.viewByHTMLID(view.htmlID()); v != nil {
|
||||
|
@ -229,11 +254,8 @@ func (view *viewData) Remove(tag PropertyName) {
|
|||
if view.created && len(changedTags) > 0 {
|
||||
for _, tag := range changedTags {
|
||||
view.changed(tag)
|
||||
}
|
||||
|
||||
for _, tag := range changedTags {
|
||||
if listener, ok := view.changeListener[tag]; ok {
|
||||
listener(view, tag)
|
||||
listener.Run(view, tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -260,11 +282,8 @@ func (view *viewData) Set(tag PropertyName, value any) bool {
|
|||
if view.created && len(changedTags) > 0 {
|
||||
for _, tag := range changedTags {
|
||||
view.changed(tag)
|
||||
}
|
||||
|
||||
for _, tag := range changedTags {
|
||||
if listener, ok := view.changeListener[tag]; ok {
|
||||
listener(view, tag)
|
||||
listener.Run(view, tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -282,7 +301,8 @@ func normalizeViewTag(tag PropertyName) PropertyName {
|
|||
}
|
||||
|
||||
func (view *viewData) getFunc(tag PropertyName) any {
|
||||
if tag == ID {
|
||||
switch tag {
|
||||
case ID:
|
||||
if id := view.ID(); id != "" {
|
||||
return id
|
||||
} else {
|
||||
|
@ -304,6 +324,14 @@ func (view *viewData) removeFunc(tag PropertyName) []PropertyName {
|
|||
changedTags = []PropertyName{}
|
||||
}
|
||||
|
||||
case Binding:
|
||||
if view.getRaw(Binding) != nil {
|
||||
view.setRaw(Binding, nil)
|
||||
changedTags = []PropertyName{Binding}
|
||||
} else {
|
||||
changedTags = []PropertyName{}
|
||||
}
|
||||
|
||||
case Animation:
|
||||
if val := view.getRaw(Animation); val != nil {
|
||||
if animations, ok := val.([]AnimationProperty); ok {
|
||||
|
@ -336,6 +364,33 @@ func (view *viewData) setFunc(tag PropertyName, value any) []PropertyName {
|
|||
notCompatibleType(ID, value)
|
||||
return nil
|
||||
|
||||
case Binding:
|
||||
view.setRaw(Binding, value)
|
||||
return []PropertyName{Binding}
|
||||
|
||||
case changeListeners:
|
||||
switch value := value.(type) {
|
||||
case DataObject:
|
||||
for i := range value.PropertyCount() {
|
||||
node := value.Property(i)
|
||||
if node.Type() == TextNode {
|
||||
if text := node.Text(); text != "" {
|
||||
view.changeListener[PropertyName(node.Tag())] = newOneArgListenerBinding[View, PropertyName](text)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(view.changeListener) > 0 {
|
||||
view.setRaw(changeListeners, view.changeListener)
|
||||
}
|
||||
return []PropertyName{tag}
|
||||
|
||||
case DataNode:
|
||||
if value.Type() == ObjectNode {
|
||||
return view.setFunc(tag, value.Object())
|
||||
}
|
||||
}
|
||||
return []PropertyName{}
|
||||
|
||||
case Animation:
|
||||
oldAnimations := []AnimationProperty{}
|
||||
if val := view.getRaw(Animation); val != nil {
|
||||
|
@ -386,20 +441,15 @@ func (view *viewData) setFunc(tag PropertyName, value any) []PropertyName {
|
|||
case TransitionRunEvent, TransitionStartEvent, TransitionEndEvent, TransitionCancelEvent:
|
||||
result := setOneArgEventListener[View, PropertyName](view, tag, value)
|
||||
if result == nil {
|
||||
result = setOneArgEventListener[View, string](view, tag, value)
|
||||
if result != nil {
|
||||
if listeners, ok := view.getRaw(tag).([]func(View, string)); ok {
|
||||
newListeners := make([]func(View, PropertyName), len(listeners))
|
||||
for i, listener := range listeners {
|
||||
newListeners[i] = func(view View, name PropertyName) {
|
||||
listener(view, string(name))
|
||||
}
|
||||
}
|
||||
view.setRaw(tag, newListeners)
|
||||
return result
|
||||
if listeners, ok := valueToOneArgEventListeners[View, string](view); ok && len(listeners) > 0 {
|
||||
newListeners := make([]oneArgListener[View, PropertyName], len(listeners))
|
||||
for i, listener := range listeners {
|
||||
newListeners[i] = newOneArgListenerVE(func(view View, name PropertyName) {
|
||||
listener.Run(view, string(name))
|
||||
})
|
||||
}
|
||||
view.setRaw(tag, nil)
|
||||
return nil
|
||||
view.setRaw(tag, newListeners)
|
||||
result = []PropertyName{tag}
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
@ -909,7 +959,7 @@ func (view *viewData) propertyChanged(tag PropertyName) {
|
|||
case DropEffectAllowed:
|
||||
effect := GetDropEffectAllowed(view)
|
||||
if effect >= DropEffectCopy && effect >= DropEffectAll {
|
||||
values := []string{"undifined", "copy", "move", "copyMove", "link", "copyLink", "linkMove", "all"}
|
||||
values := []string{"undefined", "copy", "move", "copyMove", "link", "copyLink", "linkMove", "all"}
|
||||
session.updateProperty(htmlID, "data-drop-effect-allowed", values[effect])
|
||||
} else {
|
||||
session.removeProperty(htmlID, "data-drop-effect-allowed")
|
||||
|
@ -1016,8 +1066,8 @@ func (view *viewData) htmlProperties(self View, buffer *strings.Builder) {
|
|||
}
|
||||
|
||||
if view.frame.Left != 0 || view.frame.Top != 0 || view.frame.Width != 0 || view.frame.Height != 0 {
|
||||
buffer.WriteString(fmt.Sprintf(` data-left="%g" data-top="%g" data-width="%g" data-height="%g"`,
|
||||
view.frame.Left, view.frame.Top, view.frame.Width, view.frame.Height))
|
||||
fmt.Fprintf(buffer, ` data-left="%g" data-top="%g" data-width="%g" data-height="%g"`,
|
||||
view.frame.Left, view.frame.Top, view.frame.Width, view.frame.Height)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1142,13 +1192,13 @@ func (view *viewData) handleCommand(self View, command PropertyName, data DataOb
|
|||
case FocusEvent:
|
||||
view.hasFocus = true
|
||||
for _, listener := range getNoArgEventListeners[View](view, nil, command) {
|
||||
listener(self)
|
||||
listener.Run(self)
|
||||
}
|
||||
|
||||
case LostFocusEvent:
|
||||
view.hasFocus = false
|
||||
for _, listener := range getNoArgEventListeners[View](view, nil, command) {
|
||||
listener(self)
|
||||
listener.Run(self)
|
||||
}
|
||||
|
||||
case TransitionRunEvent, TransitionStartEvent, TransitionEndEvent, TransitionCancelEvent:
|
||||
|
@ -1231,12 +1281,33 @@ func (view *viewData) handleCommand(self View, command PropertyName, data DataOb
|
|||
|
||||
}
|
||||
|
||||
func (view *viewData) SetChangeListener(tag PropertyName, listener func(View, PropertyName)) {
|
||||
func (view *viewData) SetChangeListener(tag PropertyName, listener any) bool {
|
||||
if listener == nil {
|
||||
delete(view.changeListener, tag)
|
||||
} else {
|
||||
view.changeListener[tag] = listener
|
||||
switch listener := listener.(type) {
|
||||
case func():
|
||||
view.changeListener[tag] = newOneArgListener0[View, PropertyName](listener)
|
||||
|
||||
case func(View):
|
||||
view.changeListener[tag] = newOneArgListenerV[View, PropertyName](listener)
|
||||
|
||||
case func(PropertyName):
|
||||
view.changeListener[tag] = newOneArgListenerE[View](listener)
|
||||
|
||||
case func(View, PropertyName):
|
||||
view.changeListener[tag] = newOneArgListenerVE(listener)
|
||||
|
||||
case string:
|
||||
view.changeListener[tag] = newOneArgListenerBinding[View, PropertyName](listener)
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
view.setRaw(changeListeners, view.changeListener)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (view *viewData) HasFocus() bool {
|
||||
|
@ -1250,6 +1321,6 @@ func (view *viewData) String() string {
|
|||
return buffer.String()
|
||||
}
|
||||
|
||||
func (view *viewData) exscludeTags() []PropertyName {
|
||||
func (view *viewData) excludeTags() []PropertyName {
|
||||
return nil
|
||||
}
|
||||
|
|
170
viewFactory.go
170
viewFactory.go
|
@ -6,7 +6,7 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
var viewCreators = map[string]func(Session) View{
|
||||
var systemViewCreators = map[string]func(Session) View{
|
||||
"View": newView,
|
||||
"ColumnLayout": newColumnLayout,
|
||||
"ListLayout": newListLayout,
|
||||
|
@ -36,84 +36,117 @@ var viewCreators = map[string]func(Session) View{
|
|||
"VideoPlayer": newVideoPlayer,
|
||||
}
|
||||
|
||||
// ViewCreateListener is the listener interface of a view create event
|
||||
type ViewCreateListener interface {
|
||||
// OnCreate is a function of binding object that is called by the CreateViewFromText, CreateViewFromResources,
|
||||
// and CreateViewFromObject functions after the creation of a view
|
||||
OnCreate(view View)
|
||||
}
|
||||
|
||||
var viewCreate map[string]func(Session) View = nil
|
||||
|
||||
func viewCreators() map[string]func(Session) View {
|
||||
if viewCreate == nil {
|
||||
viewCreate = map[string]func(Session) View{}
|
||||
for tag, fn := range systemViewCreators {
|
||||
viewCreate[strings.ToLower(tag)] = fn
|
||||
}
|
||||
}
|
||||
return viewCreate
|
||||
}
|
||||
|
||||
// RegisterViewCreator register function of creating view
|
||||
func RegisterViewCreator(tag string, creator func(Session) View) bool {
|
||||
builtinViews := []string{
|
||||
"View",
|
||||
"ViewsContainer",
|
||||
"ColumnLayout",
|
||||
"ListLayout",
|
||||
"GridLayout",
|
||||
"StackLayout",
|
||||
"TabsLayout",
|
||||
"AbsoluteLayout",
|
||||
"Resizable",
|
||||
"DetailsView",
|
||||
"TextView",
|
||||
"Button",
|
||||
"Checkbox",
|
||||
"DropDownList",
|
||||
"ProgressBar",
|
||||
"NumberPicker",
|
||||
"ColorPicker",
|
||||
"DatePicker",
|
||||
"TimePicker",
|
||||
"EditView",
|
||||
"ListView",
|
||||
"CanvasView",
|
||||
"ImageView",
|
||||
"TableView",
|
||||
}
|
||||
|
||||
for _, name := range builtinViews {
|
||||
if name == tag {
|
||||
loTag := strings.ToLower(tag)
|
||||
for name := range systemViewCreators {
|
||||
if name == loTag {
|
||||
ErrorLog(`It is forbidden to override the function of ` + tag + ` creating`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
viewCreators[tag] = creator
|
||||
viewCreators()[loTag] = creator
|
||||
return true
|
||||
}
|
||||
|
||||
// CreateViewFromObject create new View and initialize it by Node data
|
||||
func CreateViewFromObject(session Session, object DataObject) View {
|
||||
tag := object.Tag()
|
||||
|
||||
if creator, ok := viewCreators[tag]; ok {
|
||||
if !session.ignoreViewUpdates() {
|
||||
session.setIgnoreViewUpdates(true)
|
||||
defer session.setIgnoreViewUpdates(false)
|
||||
}
|
||||
view := creator(session)
|
||||
view.init(session)
|
||||
if customView, ok := view.(CustomView); ok {
|
||||
if !InitCustomView(customView, tag, session, nil) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
parseProperties(view, object)
|
||||
return view
|
||||
// CreateViewFromObject create new View and initialize it by DataObject data. Parameters:
|
||||
// - session - the session to which the view will be attached (should not be nil);
|
||||
// - object - data describing View;
|
||||
// - binding - object assigned to the Binding property (may be nil).
|
||||
//
|
||||
// If the function fails, it returns nil and an error message is written to the log.
|
||||
func CreateViewFromObject(session Session, object DataObject, binding any) View {
|
||||
if session == nil {
|
||||
ErrorLog(`Session must not be nil`)
|
||||
return nil
|
||||
}
|
||||
|
||||
ErrorLog(`Unknown view type "` + object.Tag() + `"`)
|
||||
return nil
|
||||
tag := object.Tag()
|
||||
creator, ok := viewCreators()[strings.ToLower(tag)]
|
||||
if !ok {
|
||||
ErrorLog(`Unknown view type "` + tag + `"`)
|
||||
return nil
|
||||
}
|
||||
|
||||
if !session.ignoreViewUpdates() {
|
||||
session.setIgnoreViewUpdates(true)
|
||||
defer session.setIgnoreViewUpdates(false)
|
||||
}
|
||||
|
||||
view := creator(session)
|
||||
view.init(session)
|
||||
if customView, ok := view.(CustomView); ok {
|
||||
if !InitCustomView(customView, tag, session, nil) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
parseProperties(view, object)
|
||||
if binding != nil {
|
||||
view.setRaw(Binding, binding)
|
||||
if listener, ok := binding.(ViewCreateListener); ok {
|
||||
listener.OnCreate(view)
|
||||
}
|
||||
}
|
||||
return view
|
||||
}
|
||||
|
||||
// CreateViewFromText create new View and initialize it by content of text
|
||||
func CreateViewFromText(session Session, text string) View {
|
||||
if data := ParseDataText(text); data != nil {
|
||||
return CreateViewFromObject(session, data)
|
||||
// CreateViewFromText create new View and initialize it by content of text. Parameters:
|
||||
// - session - the session to which the view will be attached (should not be nil);
|
||||
// - text - text describing View;
|
||||
// - binding - object assigned to the Binding property (optional parameter).
|
||||
//
|
||||
// If the function fails, it returns nil and an error message is written to the log.
|
||||
func CreateViewFromText(session Session, text string, binding ...any) View {
|
||||
data, err := ParseDataText(text)
|
||||
if err != nil {
|
||||
ErrorLog(err.Error())
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
|
||||
var b any = nil
|
||||
if len(binding) > 0 {
|
||||
b = binding[0]
|
||||
}
|
||||
return CreateViewFromObject(session, data, b)
|
||||
}
|
||||
|
||||
// CreateViewFromResources create new View and initialize it by the content of
|
||||
// the resource file from "views" directory
|
||||
func CreateViewFromResources(session Session, name string) View {
|
||||
// the resource file from "views" directory. Parameters:
|
||||
// - session - the session to which the view will be attached (should not be nil);
|
||||
// - name - file name in the views folder of the application resources (it is not necessary to specify the .rui extension, it is added automatically);
|
||||
// - binding - object assigned to the Binding property (optional parameter).
|
||||
//
|
||||
// If the function fails, it returns nil and an error message is written to the log.
|
||||
func CreateViewFromResources(session Session, name string, binding ...any) View {
|
||||
if strings.ToLower(filepath.Ext(name)) != ".rui" {
|
||||
name += ".rui"
|
||||
}
|
||||
|
||||
var b any = nil
|
||||
if len(binding) > 0 {
|
||||
b = binding[0]
|
||||
}
|
||||
|
||||
for _, fs := range resources.embedFS {
|
||||
rootDirs := resources.embedRootDirs(fs)
|
||||
for _, dir := range rootDirs {
|
||||
|
@ -123,15 +156,21 @@ func CreateViewFromResources(session Session, name string) View {
|
|||
|
||||
case viewDir:
|
||||
if data, err := fs.ReadFile(dir + "/" + name); err == nil {
|
||||
if data := ParseDataText(string(data)); data != nil {
|
||||
return CreateViewFromObject(session, data)
|
||||
data, err := ParseDataText(string(data))
|
||||
if err != nil {
|
||||
ErrorLog(err.Error())
|
||||
} else {
|
||||
return CreateViewFromObject(session, data, b)
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
if data, err := fs.ReadFile(dir + "/" + viewDir + "/" + name); err == nil {
|
||||
if data := ParseDataText(string(data)); data != nil {
|
||||
return CreateViewFromObject(session, data)
|
||||
data, err := ParseDataText(string(data))
|
||||
if err != nil {
|
||||
ErrorLog(err.Error())
|
||||
} else {
|
||||
return CreateViewFromObject(session, data, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -140,8 +179,11 @@ func CreateViewFromResources(session Session, name string) View {
|
|||
|
||||
if resources.path != "" {
|
||||
if data, err := os.ReadFile(resources.path + viewDir + "/" + name); err == nil {
|
||||
if data := ParseDataText(string(data)); data != nil {
|
||||
return CreateViewFromObject(session, data)
|
||||
data, err := ParseDataText(string(data))
|
||||
if err != nil {
|
||||
ErrorLog(err.Error())
|
||||
} else {
|
||||
return CreateViewFromObject(session, data, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
251
viewStyle.go
251
viewStyle.go
|
@ -2,9 +2,10 @@ package rui
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ViewStyle interface of the style of view
|
||||
|
@ -551,26 +552,126 @@ func viewStyleGet(style Properties, tag PropertyName) any {
|
|||
}
|
||||
|
||||
func supportedPropertyValue(value any) bool {
|
||||
switch value.(type) {
|
||||
case string:
|
||||
switch value := value.(type) {
|
||||
case string, bool, float32, float64, int, stringWriter, fmt.Stringer:
|
||||
return true
|
||||
|
||||
case []string:
|
||||
case bool:
|
||||
case float32:
|
||||
case float64:
|
||||
case int:
|
||||
case stringWriter:
|
||||
case fmt.Stringer:
|
||||
return len(value) > 0
|
||||
|
||||
case []ShadowProperty:
|
||||
return len(value) > 0
|
||||
|
||||
case []View:
|
||||
return len(value) > 0
|
||||
|
||||
case []any:
|
||||
return len(value) > 0
|
||||
|
||||
case []BackgroundElement:
|
||||
return len(value) > 0
|
||||
|
||||
case []BackgroundGradientPoint:
|
||||
return len(value) > 0
|
||||
|
||||
case []BackgroundGradientAngle:
|
||||
return len(value) > 0
|
||||
|
||||
case map[PropertyName]AnimationProperty:
|
||||
return len(value) > 0
|
||||
|
||||
case []noArgListener[View]:
|
||||
return getNoArgBinding(value) != ""
|
||||
|
||||
case []noArgListener[ImageView]:
|
||||
return getNoArgBinding(value) != ""
|
||||
|
||||
case []noArgListener[MediaPlayer]:
|
||||
return getNoArgBinding(value) != ""
|
||||
|
||||
case []oneArgListener[View, KeyEvent]:
|
||||
return getOneArgBinding(value) != ""
|
||||
|
||||
case []oneArgListener[View, MouseEvent]:
|
||||
return getOneArgBinding(value) != ""
|
||||
|
||||
case []oneArgListener[View, TouchEvent]:
|
||||
return getOneArgBinding(value) != ""
|
||||
|
||||
case []oneArgListener[View, PointerEvent]:
|
||||
return getOneArgBinding(value) != ""
|
||||
|
||||
case []oneArgListener[View, PropertyName]:
|
||||
return getOneArgBinding(value) != ""
|
||||
|
||||
case []oneArgListener[View, string]:
|
||||
return getOneArgBinding(value) != ""
|
||||
|
||||
case []oneArgListener[View, Frame]:
|
||||
return getOneArgBinding(value) != ""
|
||||
|
||||
case []oneArgListener[View, DragAndDropEvent]:
|
||||
return getOneArgBinding(value) != ""
|
||||
|
||||
case []oneArgListener[Checkbox, bool]:
|
||||
return getOneArgBinding(value) != ""
|
||||
|
||||
case []oneArgListener[FilePicker, []FileInfo]:
|
||||
return getOneArgBinding(value) != ""
|
||||
|
||||
case []oneArgListener[ListView, int]:
|
||||
return getOneArgBinding(value) != ""
|
||||
|
||||
case []oneArgListener[ListView, []int]:
|
||||
return getOneArgBinding(value) != ""
|
||||
|
||||
case []oneArgListener[MediaPlayer, float64]:
|
||||
return getOneArgBinding(value) != ""
|
||||
|
||||
case []oneArgListener[TableView, int]:
|
||||
return getOneArgBinding(value) != ""
|
||||
|
||||
case []oneArgListener[TabsLayout, int]:
|
||||
return getOneArgBinding(value) != ""
|
||||
|
||||
case []twoArgListener[ColorPicker, Color]:
|
||||
return getTwoArgBinding(value) != ""
|
||||
|
||||
case []twoArgListener[DatePicker, time.Time]:
|
||||
return getTwoArgBinding(value) != ""
|
||||
|
||||
case []twoArgListener[TimePicker, time.Time]:
|
||||
return getTwoArgBinding(value) != ""
|
||||
|
||||
case []twoArgListener[DropDownList, int]:
|
||||
return getTwoArgBinding(value) != ""
|
||||
|
||||
case []twoArgListener[EditView, string]:
|
||||
return getTwoArgBinding(value) != ""
|
||||
|
||||
case []twoArgListener[NumberPicker, float64]:
|
||||
return getTwoArgBinding(value) != ""
|
||||
|
||||
case []twoArgListener[TableView, int]:
|
||||
return getTwoArgBinding(value) != ""
|
||||
|
||||
case []twoArgListener[TabsLayout, int]:
|
||||
return getTwoArgBinding(value) != ""
|
||||
|
||||
case []mediaPlayerErrorListener:
|
||||
return getMediaPlayerErrorListenerBinding(value) != ""
|
||||
|
||||
case map[PropertyName]oneArgListener[View, PropertyName]:
|
||||
for _, listener := range value {
|
||||
if text, ok := listener.rawListener().(string); ok && text != "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func writePropertyValue(buffer *strings.Builder, tag PropertyName, value any, indent string) {
|
||||
|
@ -640,8 +741,8 @@ func writePropertyValue(buffer *strings.Builder, tag PropertyName, value any, in
|
|||
writeString(text)
|
||||
buffer.WriteString(",\n")
|
||||
}
|
||||
buffer.WriteString(indent)
|
||||
}
|
||||
buffer.WriteString(indent)
|
||||
buffer.WriteRune(']')
|
||||
}
|
||||
|
||||
|
@ -653,10 +754,10 @@ func writePropertyValue(buffer *strings.Builder, tag PropertyName, value any, in
|
|||
}
|
||||
|
||||
case float32:
|
||||
buffer.WriteString(fmt.Sprintf("%g", float64(value)))
|
||||
fmt.Fprintf(buffer, "%g", float64(value))
|
||||
|
||||
case float64:
|
||||
buffer.WriteString(fmt.Sprintf("%g", value))
|
||||
fmt.Fprintf(buffer, "%g", value)
|
||||
|
||||
case int:
|
||||
if prop, ok := enumProperties[tag]; ok && value >= 0 && value < len(prop.values) {
|
||||
|
@ -698,14 +799,14 @@ func writePropertyValue(buffer *strings.Builder, tag PropertyName, value any, in
|
|||
buffer.WriteString("[]")
|
||||
|
||||
case 1:
|
||||
writeViewStyle(value[0].Tag(), value[0], buffer, indent, value[0].exscludeTags())
|
||||
writeViewStyle(value[0].Tag(), value[0], buffer, indent, value[0].excludeTags())
|
||||
|
||||
default:
|
||||
buffer.WriteString("[\n")
|
||||
indent2 := indent + "\t"
|
||||
for _, v := range value {
|
||||
buffer.WriteString(indent2)
|
||||
writeViewStyle(v.Tag(), v, buffer, indent2, v.exscludeTags())
|
||||
writeViewStyle(v.Tag(), v, buffer, indent2, v.excludeTags())
|
||||
buffer.WriteString(",\n")
|
||||
}
|
||||
|
||||
|
@ -791,9 +892,7 @@ func writePropertyValue(buffer *strings.Builder, tag PropertyName, value any, in
|
|||
for tag := range value {
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
sort.Slice(tags, func(i, j int) bool {
|
||||
return tags[i] < tags[j]
|
||||
})
|
||||
slices.Sort(tags)
|
||||
buffer.WriteString("[\n")
|
||||
indent2 := indent + "\t"
|
||||
for _, tag := range tags {
|
||||
|
@ -806,6 +905,104 @@ func writePropertyValue(buffer *strings.Builder, tag PropertyName, value any, in
|
|||
buffer.WriteString(indent)
|
||||
buffer.WriteRune(']')
|
||||
}
|
||||
|
||||
case []noArgListener[View]:
|
||||
buffer.WriteString(getNoArgBinding(value))
|
||||
|
||||
case []noArgListener[ImageView]:
|
||||
buffer.WriteString(getNoArgBinding(value))
|
||||
|
||||
case []noArgListener[MediaPlayer]:
|
||||
buffer.WriteString(getNoArgBinding(value))
|
||||
|
||||
case []oneArgListener[View, KeyEvent]:
|
||||
buffer.WriteString(getOneArgBinding(value))
|
||||
|
||||
case []oneArgListener[View, MouseEvent]:
|
||||
buffer.WriteString(getOneArgBinding(value))
|
||||
|
||||
case []oneArgListener[View, TouchEvent]:
|
||||
buffer.WriteString(getOneArgBinding(value))
|
||||
|
||||
case []oneArgListener[View, PointerEvent]:
|
||||
buffer.WriteString(getOneArgBinding(value))
|
||||
|
||||
case []oneArgListener[View, PropertyName]:
|
||||
buffer.WriteString(getOneArgBinding(value))
|
||||
|
||||
case []oneArgListener[View, string]:
|
||||
buffer.WriteString(getOneArgBinding(value))
|
||||
|
||||
case []oneArgListener[View, Frame]:
|
||||
buffer.WriteString(getOneArgBinding(value))
|
||||
|
||||
case []oneArgListener[View, DragAndDropEvent]:
|
||||
buffer.WriteString(getOneArgBinding(value))
|
||||
|
||||
case []oneArgListener[Checkbox, bool]:
|
||||
buffer.WriteString(getOneArgBinding(value))
|
||||
|
||||
case []oneArgListener[FilePicker, []FileInfo]:
|
||||
buffer.WriteString(getOneArgBinding(value))
|
||||
|
||||
case []oneArgListener[ListView, int]:
|
||||
buffer.WriteString(getOneArgBinding(value))
|
||||
|
||||
case []oneArgListener[ListView, []int]:
|
||||
buffer.WriteString(getOneArgBinding(value))
|
||||
|
||||
case []oneArgListener[MediaPlayer, float64]:
|
||||
buffer.WriteString(getOneArgBinding(value))
|
||||
|
||||
case []oneArgListener[TableView, int]:
|
||||
buffer.WriteString(getOneArgBinding(value))
|
||||
|
||||
case []oneArgListener[TabsLayout, int]:
|
||||
buffer.WriteString(getOneArgBinding(value))
|
||||
|
||||
case []twoArgListener[ColorPicker, Color]:
|
||||
buffer.WriteString(getTwoArgBinding(value))
|
||||
|
||||
case []twoArgListener[DatePicker, time.Time]:
|
||||
buffer.WriteString(getTwoArgBinding(value))
|
||||
|
||||
case []twoArgListener[TimePicker, time.Time]:
|
||||
buffer.WriteString(getTwoArgBinding(value))
|
||||
|
||||
case []twoArgListener[DropDownList, int]:
|
||||
buffer.WriteString(getTwoArgBinding(value))
|
||||
|
||||
case []twoArgListener[EditView, string]:
|
||||
buffer.WriteString(getTwoArgBinding(value))
|
||||
|
||||
case []twoArgListener[NumberPicker, float64]:
|
||||
buffer.WriteString(getTwoArgBinding(value))
|
||||
|
||||
case []twoArgListener[TableView, int]:
|
||||
buffer.WriteString(getTwoArgBinding(value))
|
||||
|
||||
case []twoArgListener[TabsLayout, int]:
|
||||
buffer.WriteString(getTwoArgBinding(value))
|
||||
|
||||
case []mediaPlayerErrorListener:
|
||||
buffer.WriteString(getMediaPlayerErrorListenerBinding(value))
|
||||
|
||||
case map[PropertyName]oneArgListener[View, PropertyName]:
|
||||
buffer.WriteString("_{")
|
||||
for key, listener := range value {
|
||||
if text, ok := listener.rawListener().(string); ok && text != "" {
|
||||
buffer.WriteRune('\n')
|
||||
buffer.WriteString(indent)
|
||||
buffer.WriteRune('\t')
|
||||
writeString(string(key))
|
||||
buffer.WriteString(" = ")
|
||||
writeString(text)
|
||||
buffer.WriteRune(',')
|
||||
}
|
||||
buffer.WriteRune('\n')
|
||||
buffer.WriteString(indent)
|
||||
buffer.WriteRune('}')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -815,19 +1012,15 @@ func writeViewStyle(name string, view Properties, buffer *strings.Builder, inden
|
|||
indent += "\t"
|
||||
|
||||
writeProperty := func(tag PropertyName, value any) {
|
||||
for _, exclude := range excludeTags {
|
||||
if exclude == tag {
|
||||
return
|
||||
if !slices.Contains(excludeTags, tag) {
|
||||
if supportedPropertyValue(value) {
|
||||
buffer.WriteString(indent)
|
||||
buffer.WriteString(string(tag))
|
||||
buffer.WriteString(" = ")
|
||||
writePropertyValue(buffer, tag, value, indent)
|
||||
buffer.WriteString(",\n")
|
||||
}
|
||||
}
|
||||
|
||||
if supportedPropertyValue(value) {
|
||||
buffer.WriteString(indent)
|
||||
buffer.WriteString(string(tag))
|
||||
buffer.WriteString(" = ")
|
||||
writePropertyValue(buffer, tag, value, indent)
|
||||
buffer.WriteString(",\n")
|
||||
}
|
||||
}
|
||||
|
||||
tags := view.AllTags()
|
||||
|
|
112
viewUtils.go
112
viewUtils.go
|
@ -355,31 +355,41 @@ func GetTextShadows(view View, subviewID ...string) []ShadowProperty {
|
|||
}
|
||||
|
||||
// GetBackgroundColor returns a background color of the subview.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetBackgroundColor(view View, subviewID ...string) Color {
|
||||
return colorStyledProperty(view, subviewID, BackgroundColor, false)
|
||||
}
|
||||
|
||||
// GetAccentColor returns the accent color for UI controls generated by some elements.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetAccentColor(view View, subviewID ...string) Color {
|
||||
return colorStyledProperty(view, subviewID, AccentColor, false)
|
||||
}
|
||||
|
||||
// GetFontName returns the subview font.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetFontName(view View, subviewID ...string) string {
|
||||
return stringStyledProperty(view, nil, FontName, true)
|
||||
}
|
||||
|
||||
// GetTextColor returns a text color of the subview.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTextColor(view View, subviewID ...string) Color {
|
||||
return colorStyledProperty(view, subviewID, TextColor, true)
|
||||
}
|
||||
|
||||
// GetTextSize returns a text size of the subview.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTextSize(view View, subviewID ...string) SizeUnit {
|
||||
return sizeStyledProperty(view, subviewID, TextSize, true)
|
||||
}
|
||||
|
@ -392,7 +402,9 @@ func GetTabSize(view View, subviewID ...string) int {
|
|||
|
||||
// GetTextWeight returns a text weight of the subview. Returns one of next values:
|
||||
// 1, 2, 3, 4 (normal text), 5, 6, 7 (bold text), 8 and 9
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTextWeight(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, TextWeight, NormalFont, true)
|
||||
}
|
||||
|
@ -401,7 +413,8 @@ func GetTextWeight(view View, subviewID ...string) int {
|
|||
//
|
||||
// LeftAlign = 0, RightAlign = 1, CenterAlign = 2, JustifyAlign = 3
|
||||
//
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTextAlign(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, TextAlign, LeftAlign, true)
|
||||
}
|
||||
|
@ -410,89 +423,116 @@ func GetTextAlign(view View, subviewID ...string) int {
|
|||
//
|
||||
// TextWrapOn = 0, TextWrapOff = 1, TextWrapBalance = 3
|
||||
//
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTextWrap(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, TextWrap, TextWrapOn, true)
|
||||
}
|
||||
|
||||
// GetTextIndent returns a text indent of the subview.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTextIndent(view View, subviewID ...string) SizeUnit {
|
||||
return sizeStyledProperty(view, subviewID, TextIndent, true)
|
||||
}
|
||||
|
||||
// GetLetterSpacing returns a letter spacing of the subview.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetLetterSpacing(view View, subviewID ...string) SizeUnit {
|
||||
return sizeStyledProperty(view, subviewID, LetterSpacing, true)
|
||||
}
|
||||
|
||||
// GetWordSpacing returns a word spacing of the subview.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetWordSpacing(view View, subviewID ...string) SizeUnit {
|
||||
return sizeStyledProperty(view, subviewID, WordSpacing, true)
|
||||
}
|
||||
|
||||
// GetLineHeight returns a height of a text line of the subview.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetLineHeight(view View, subviewID ...string) SizeUnit {
|
||||
return sizeStyledProperty(view, subviewID, LineHeight, true)
|
||||
}
|
||||
|
||||
// IsItalic returns "true" if a text font of the subview is displayed in italics, "false" otherwise.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func IsItalic(view View, subviewID ...string) bool {
|
||||
return boolStyledProperty(view, subviewID, Italic, true)
|
||||
}
|
||||
|
||||
// IsSmallCaps returns "true" if a text font of the subview is displayed in small caps, "false" otherwise.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func IsSmallCaps(view View, subviewID ...string) bool {
|
||||
return boolStyledProperty(view, subviewID, SmallCaps, true)
|
||||
}
|
||||
|
||||
// IsStrikethrough returns "true" if a text font of the subview is displayed strikethrough, "false" otherwise.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func IsStrikethrough(view View, subviewID ...string) bool {
|
||||
return boolStyledProperty(view, subviewID, Strikethrough, true)
|
||||
}
|
||||
|
||||
// IsOverline returns "true" if a text font of the subview is displayed overlined, "false" otherwise.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func IsOverline(view View, subviewID ...string) bool {
|
||||
return boolStyledProperty(view, subviewID, Overline, true)
|
||||
}
|
||||
|
||||
// IsUnderline returns "true" if a text font of the subview is displayed underlined, "false" otherwise.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func IsUnderline(view View, subviewID ...string) bool {
|
||||
return boolStyledProperty(view, subviewID, Underline, true)
|
||||
}
|
||||
|
||||
// GetTextLineThickness returns the stroke thickness of the decoration line that
|
||||
// is used on text in an element, such as a line-through, underline, or overline.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTextLineThickness(view View, subviewID ...string) SizeUnit {
|
||||
return sizeStyledProperty(view, subviewID, TextLineThickness, true)
|
||||
}
|
||||
|
||||
// GetTextLineStyle returns the stroke style of the decoration line that
|
||||
// is used on text in an element, such as a line-through, underline, or overline.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTextLineStyle(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, TextLineStyle, SolidLine, true)
|
||||
}
|
||||
|
||||
// GetTextLineColor returns the stroke color of the decoration line that
|
||||
// is used on text in an element, such as a line-through, underline, or overline.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTextLineColor(view View, subviewID ...string) Color {
|
||||
return colorStyledProperty(view, subviewID, TextLineColor, true)
|
||||
}
|
||||
|
||||
// GetTextTransform returns a text transform of the subview. Return one of next values:
|
||||
// NoneTextTransform (0), CapitalizeTextTransform (1), LowerCaseTextTransform (2) or UpperCaseTextTransform (3)
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTextTransform(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, TextTransform, NoneTextTransform, true)
|
||||
}
|
||||
|
@ -500,14 +540,18 @@ func GetTextTransform(view View, subviewID ...string) int {
|
|||
// GetWritingMode returns whether lines of text are laid out horizontally or vertically, as well as
|
||||
// the direction in which blocks progress. Valid values are HorizontalTopToBottom (0),
|
||||
// HorizontalBottomToTop (1), VerticalRightToLeft (2) and VerticalLeftToRight (3)
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetWritingMode(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, WritingMode, HorizontalTopToBottom, true)
|
||||
}
|
||||
|
||||
// GetTextDirection - returns a direction of text, table columns, and horizontal overflow.
|
||||
// Valid values are SystemTextDirection (0), LeftToRightDirection (1), and RightToLeftDirection (2).
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTextDirection(view View, subviewID ...string) int {
|
||||
if view == nil {
|
||||
return SystemTextDirection
|
||||
|
@ -519,7 +563,9 @@ func GetTextDirection(view View, subviewID ...string) int {
|
|||
// GetVerticalTextOrientation returns a orientation of the text characters in a line. It only affects text
|
||||
// in vertical mode (when "writing-mode" is "vertical-right-to-left" or "vertical-left-to-right").
|
||||
// Valid values are MixedTextOrientation (0), UprightTextOrientation (1), and SidewaysTextOrientation (2).
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetVerticalTextOrientation(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, VerticalTextOrientation, MixedTextOrientation, true)
|
||||
}
|
||||
|
@ -761,7 +807,9 @@ func BlurViewByID(viewID string, session Session) {
|
|||
}
|
||||
|
||||
// GetCurrent returns the index of the selected item (<0 if there is no a selected item) or the current view index (StackLayout, TabsLayout).
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetCurrent(view View, subviewID ...string) int {
|
||||
defaultValue := -1
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
|
@ -775,7 +823,9 @@ func GetCurrent(view View, subviewID ...string) int {
|
|||
}
|
||||
|
||||
// IsUserSelect returns "true" if the user can select text, "false" otherwise.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func IsUserSelect(view View, subviewID ...string) bool {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
value, _ := isUserSelect(view)
|
||||
|
@ -821,7 +871,8 @@ func isUserSelect(view View) (bool, bool) {
|
|||
// BlendSoftLight (9), BlendDifference (10), BlendExclusion (11), BlendHue (12),
|
||||
// BlendSaturation (13), BlendColor (14), BlendLuminosity (15)
|
||||
//
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetMixBlendMode(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, MixBlendMode, BlendNormal, true)
|
||||
}
|
||||
|
@ -833,13 +884,16 @@ func GetMixBlendMode(view View, subviewID ...string) int {
|
|||
// BlendSoftLight (9), BlendDifference (10), BlendExclusion (11), BlendHue (12),
|
||||
// BlendSaturation (13), BlendColor (14), BlendLuminosity (15)
|
||||
//
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetBackgroundBlendMode(view View, subviewID ...string) int {
|
||||
return enumStyledProperty(view, subviewID, BackgroundBlendMode, BlendNormal, true)
|
||||
}
|
||||
|
||||
// GetTooltip returns a tooltip text of the subview.
|
||||
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
|
||||
//
|
||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||
// If it is not specified then a value from the first argument (view) is returned.
|
||||
func GetTooltip(view View, subviewID ...string) string {
|
||||
if view = getSubview(view, subviewID); view != nil {
|
||||
if value := view.Get(Tooltip); value != nil {
|
||||
|
|
|
@ -85,10 +85,13 @@ func (container *viewsContainerData) Append(view View) {
|
|||
|
||||
viewHTML(view, buffer, "")
|
||||
container.Session().appendToInnerHTML(container.htmlID(), buffer.String())
|
||||
container.contentChanged()
|
||||
}
|
||||
}
|
||||
|
||||
if listener, ok := container.changeListener[Content]; ok {
|
||||
listener(container, Content)
|
||||
}
|
||||
func (container *viewsContainerData) contentChanged() {
|
||||
if listener, ok := container.changeListener[Content]; ok {
|
||||
listener.Run(container, Content)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,9 +116,7 @@ func (container *viewsContainerData) insert(view View, index int) bool {
|
|||
func (container *viewsContainerData) Insert(view View, index int) {
|
||||
if container.insert(view, index) && container.created {
|
||||
updateInnerHTML(container.htmlID(), container.Session())
|
||||
if listener, ok := container.changeListener[Content]; ok {
|
||||
listener(container, Content)
|
||||
}
|
||||
container.contentChanged()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,11 +132,12 @@ func (container *viewsContainerData) removeView(index int) View {
|
|||
}
|
||||
|
||||
view := container.views[index]
|
||||
if index == 0 {
|
||||
switch index {
|
||||
case 0:
|
||||
container.views = container.views[1:]
|
||||
} else if index == count-1 {
|
||||
case count - 1:
|
||||
container.views = container.views[:index]
|
||||
} else {
|
||||
default:
|
||||
container.views = append(container.views[:index], container.views[index+1:]...)
|
||||
}
|
||||
|
||||
|
@ -148,9 +150,7 @@ func (container *viewsContainerData) RemoveView(index int) View {
|
|||
view := container.removeView(index)
|
||||
if view != nil && container.created {
|
||||
container.Session().callFunc("removeView", view.htmlID())
|
||||
if listener, ok := container.changeListener[Content]; ok {
|
||||
listener(container, Content)
|
||||
}
|
||||
container.contentChanged()
|
||||
}
|
||||
return view
|
||||
}
|
||||
|
@ -173,12 +173,9 @@ func (container *viewsContainerData) htmlSubviews(self View, buffer *strings.Bui
|
|||
}
|
||||
|
||||
func viewFromTextValue(text string, session Session) View {
|
||||
if strings.Contains(text, "{") && strings.Contains(text, "}") {
|
||||
data := ParseDataText(text)
|
||||
if data != nil {
|
||||
if view := CreateViewFromObject(session, data); view != nil {
|
||||
return view
|
||||
}
|
||||
if data, err := ParseDataText(text); err == nil {
|
||||
if view := CreateViewFromObject(session, data, nil); view != nil {
|
||||
return view
|
||||
}
|
||||
}
|
||||
return NewTextView(session, Params{Text: text})
|
||||
|
@ -277,7 +274,7 @@ func (container *viewsContainerData) setContent(value any) bool {
|
|||
container.views = views
|
||||
|
||||
case DataObject:
|
||||
if view := CreateViewFromObject(session, value); view != nil {
|
||||
if view := CreateViewFromObject(session, value, nil); view != nil {
|
||||
container.views = []View{view}
|
||||
} else {
|
||||
return false
|
||||
|
@ -287,7 +284,7 @@ func (container *viewsContainerData) setContent(value any) bool {
|
|||
views := []View{}
|
||||
for _, data := range value {
|
||||
if data.IsObject() {
|
||||
if view := CreateViewFromObject(session, data.Object()); view != nil {
|
||||
if view := CreateViewFromObject(session, data.Object(), nil); view != nil {
|
||||
views = append(views, view)
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -190,7 +190,7 @@ func (bridge *webBridge) argToString(arg any) (string, bool) {
|
|||
for _, val := range arg {
|
||||
buffer.WriteRune(lead)
|
||||
lead = ','
|
||||
buffer.WriteString(fmt.Sprintf("%g", val))
|
||||
fmt.Fprintf(buffer, "%g", val)
|
||||
}
|
||||
buffer.WriteRune(']')
|
||||
return buffer.String(), true
|
||||
|
|
Loading…
Reference in New Issue