mirror of https://github.com/anoshenko/rui.git
Compare commits
50 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
da3d963b40 | |
|
|
469980428c | |
|
|
cccbb75219 | |
|
|
942d7c45d3 | |
|
|
049a2b365f | |
|
|
c4dfba2796 | |
|
|
5cb3841cf8 | |
|
|
01a9bfeb93 | |
|
|
cb7d11ed15 | |
|
|
8fae202d75 | |
|
|
7702221672 | |
|
|
ff8f6f30f2 | |
|
|
7cec6e5736 | |
|
|
7cc553176f | |
|
|
55a86011cd | |
|
|
73cc26b54e | |
|
|
8066fb92ba | |
|
|
4c76000254 | |
|
|
86f3f4c731 | |
|
|
90b1d44597 | |
|
|
824e1b01ad | |
|
|
83ec4f0a20 | |
|
|
0e4068bcfb | |
|
|
d28ee630b6 | |
|
|
f36ee4a4a7 | |
|
|
00ef0b3624 | |
|
|
517809d4a8 | |
|
|
098985b342 | |
|
|
7969124bcd | |
|
|
c3c8b9e858 | |
|
|
3090a0e94f | |
|
|
b0185726db | |
|
|
2dd8d8d256 | |
|
|
4cec7fef26 | |
|
|
e618377c11 | |
|
|
73b14ed78a | |
|
|
bbbaf28aba | |
|
|
0433f460e4 | |
|
|
d633c80155 | |
|
|
cb4d197bb7 | |
|
|
2f07584b37 | |
|
|
24aeeb515b | |
|
|
f2fb948325 | |
|
|
4b00299878 | |
|
|
3c3c09b043 | |
|
|
ec2c5393f1 | |
|
|
4f07435637 | |
|
|
b76e3e56d8 | |
|
|
c9744168ba | |
|
|
4f1969975d |
19
CHANGELOG.md
19
CHANGELOG.md
|
|
@ -1,6 +1,23 @@
|
||||||
|
# v0.20.0
|
||||||
|
|
||||||
|
* Added support of binding
|
||||||
|
* Added "binding" argument to CreateViewFromResources, CreateViewFromText, and CreateViewFromObject functions
|
||||||
|
* Added CreatePopupFromResources, CreatePopupFromText, and CreatePopupFromObject functions
|
||||||
|
* Added All() iterator and IsEmpty() method to Properties interface
|
||||||
|
* Added implementation of Properties interface to Popup
|
||||||
|
* Changed ParseDataText function return values
|
||||||
|
* Added `Properties() iter.Seq[DataNode]` iterator to DataObject interface
|
||||||
|
* Renamed `ArrayElements() []DataValue` method of DataNode interface to `Array() []DataValue`
|
||||||
|
* Added `ArrayElements() iter.Seq[DataValue]` iterator to DataNode interface
|
||||||
|
|
||||||
|
# v0.19.0
|
||||||
|
|
||||||
|
* Added support of drag-and-drop
|
||||||
|
* Added LoadFile method to View interface
|
||||||
|
|
||||||
# v0.18.2
|
# v0.18.2
|
||||||
|
|
||||||
* fixed typo: GetShadowPropertys -> GetShadowProperty
|
* fixed typo: GetShadowProperties -> GetShadowProperty
|
||||||
|
|
||||||
# v0.18.0
|
# v0.18.0
|
||||||
|
|
||||||
|
|
|
||||||
267
README-ru.md
267
README-ru.md
|
|
@ -1086,7 +1086,7 @@ RadiusProperty, а не структура BoxRadius. Получить стру
|
||||||
|
|
||||||
Получить значение данного свойства можно с помощью функции
|
Получить значение данного свойства можно с помощью функции
|
||||||
|
|
||||||
func GetShadowPropertys(view View, subviewID ...string) []ShadowProperty
|
func GetShadowProperties(view View, subviewID ...string) []ShadowProperty
|
||||||
|
|
||||||
Если тень не задана, то данная функция вернет пустой массив
|
Если тень не задана, то данная функция вернет пустой массив
|
||||||
|
|
||||||
|
|
@ -2287,6 +2287,194 @@ radius необходимо передать nil
|
||||||
|
|
||||||
которые прокручивают view, соответственно, в заданную позицию, начало и конец
|
которые прокручивают view, соответственно, в заданную позицию, начало и конец
|
||||||
|
|
||||||
|
### Drag and drop
|
||||||
|
|
||||||
|
#### Свойство "drag-data"
|
||||||
|
|
||||||
|
Для того чтобы сделать View перетаскиваемым ему необходимо задать свойство "drag-data" (константа DragData).
|
||||||
|
Данное свойство задает множество перетаскиваемых данных в виде ключ:значение и имеет тип:
|
||||||
|
|
||||||
|
map[string]string
|
||||||
|
|
||||||
|
В качестве ключей рекомендуется использовать mime-тип значения.
|
||||||
|
Например, если в перетаскиваемыми данными является текст, то ключом должен быть "text/plain", если jpeg-изображение, то "image/jpg" и т.п.
|
||||||
|
Но это только рекомендация, ключом может быть любой текст.
|
||||||
|
|
||||||
|
Пример
|
||||||
|
|
||||||
|
view.Set(rui.DragData, map[string]string {
|
||||||
|
"text/plain": "Drag-and-drop text",
|
||||||
|
"text/html" : "<b>Drag-and-drop<\b> text",
|
||||||
|
"my-key" : "my-data",
|
||||||
|
})
|
||||||
|
|
||||||
|
Получить значение данного свойства можно с помощью функции
|
||||||
|
|
||||||
|
GetDragData(view View, subviewID ...string) map[string]string
|
||||||
|
|
||||||
|
|
||||||
|
#### Свойство "drag-image"
|
||||||
|
|
||||||
|
По умолчанию при перетаскивании перемещается весь View. Часто это бывает не удобно, например если View очень большой.
|
||||||
|
|
||||||
|
Свойство "drag-image" (константа DragImage) типа string позволяет задать картинку, которая будет отображаться вместо View при перетаскивании.
|
||||||
|
В качестве значения "drag-image" задается:
|
||||||
|
* Имя изображения в ресурсах приложения
|
||||||
|
* Константа изображения
|
||||||
|
* URL изображения
|
||||||
|
|
||||||
|
Пример
|
||||||
|
|
||||||
|
view.Set(rui.DragImage, "image.png")
|
||||||
|
|
||||||
|
Получить значение данного свойства можно с помощью функции
|
||||||
|
|
||||||
|
func GetDragImage(view View, subviewID ...string) string {
|
||||||
|
|
||||||
|
#### Свойства "drag-image-x-offset" и "drag-image-y-offset"
|
||||||
|
|
||||||
|
Свойства "drag-image-x-offset" и "drag-image-y-offset" (константы DragImageXOffset и DragImageXOffset) типа float задают смещение в пикселях перетаскиваемой картинки относительно курсора мыши.
|
||||||
|
По умолчанию курсор мыши привязан к верхнему левому углу картинки. Данные свойства используются только если задано свойство "drag-image".
|
||||||
|
|
||||||
|
Пример
|
||||||
|
|
||||||
|
view.SetParams(rui.Params {
|
||||||
|
rui.DragImage : "image.png",
|
||||||
|
rui.DragImageXOffset: 10,
|
||||||
|
rui.DragImageYOffset: -15,
|
||||||
|
})
|
||||||
|
|
||||||
|
Получить значение данных свойств можно с помощью функций
|
||||||
|
|
||||||
|
func GetDragImageXOffset(view View, subviewID ...string) float64
|
||||||
|
func GetDragImageYOffset(view View, subviewID ...string) float64
|
||||||
|
|
||||||
|
#### События
|
||||||
|
|
||||||
|
Слушатели событий drag-and-drop имеет следующий формат:
|
||||||
|
|
||||||
|
func(View, DragAndDropEvent)
|
||||||
|
|
||||||
|
где DragAndDropEvent расширяет MouseEvent и описана как
|
||||||
|
|
||||||
|
type DragAndDropEvent struct {
|
||||||
|
MouseEvent
|
||||||
|
Data map[string]string
|
||||||
|
Files []FileInfo
|
||||||
|
Target View
|
||||||
|
EffectAllowed int
|
||||||
|
DropEffect int
|
||||||
|
}
|
||||||
|
|
||||||
|
Можно также использовать слушателей следующих форматов:
|
||||||
|
|
||||||
|
* func(DragAndDropEvent)
|
||||||
|
* func(View)
|
||||||
|
* func()
|
||||||
|
|
||||||
|
Поля структуры DragAndDropEvent содержат следующие данные
|
||||||
|
|
||||||
|
* Data - данные перетаскивания заданные свойством "drag-data" перетаскиваемого View и дополнительные данные заданные браузером (например, Chrome добавляет html-код элемента). Данное поле может быть пустым, если перетаскиваются файлы
|
||||||
|
|
||||||
|
* Files - список перетаскиваемых файлов. Используется только событием "drop-event" (для всех других событий этот список равен nil). Также если вы перетаскиваете View, то этот список будет равным nil.
|
||||||
|
|
||||||
|
* Target - принимающий View. Имеет значение отличное от nil, если перетаскиваемый объект находится над View который может принять перетаскиваемый элемент
|
||||||
|
|
||||||
|
* EffectAllowed - показывает список разрешенных эффектов перемещения для курсора мыши: DropEffectCopy, DropEffectMove, DropEffectLink, DropEffectCopyMove, DropEffectCopyLink, DropEffectLinkMove и DropEffectAll.
|
||||||
|
|
||||||
|
* DropEffect - используется только в событии "drag-end" и имеет значение DropEffectCopy, DropEffectMove или DropEffectLink если операция перетаскивания завершилась успешно и DropEffectNone в противоположном случае.
|
||||||
|
|
||||||
|
#### Событие "drop-event"
|
||||||
|
|
||||||
|
Для того чтобы View смог принимать перетаскиваемые данные (View или файлы) вы должны определить для него слушателя события "drop-event" (константа DropEvent). Как и все событий drag-and-drop он имеет следующий формат:
|
||||||
|
|
||||||
|
func(View, DragAndDropEvent)
|
||||||
|
|
||||||
|
Система сама ничего не делает с перетаскиваемыми данными. Вы должны добавить код выполняющий какие либо действия с перетаскиваемыми данными. Следующий пример добавляет картинку из ресурсов или из перетаскиваемого файла в ListLayout
|
||||||
|
|
||||||
|
listLayout.Set(rui.DropEvent, func(_ rui.View, event rui.DragAndDropEvent)) {
|
||||||
|
if len(event.Files) > 0 {
|
||||||
|
for _, file := range event.Files {
|
||||||
|
switch file.MimeType {
|
||||||
|
case "image/png", "image/jpeg", "image/gif", "image/svg+xml":
|
||||||
|
listLayout.LoadFile(file, func(file rui.FileInfo, data []byte) {
|
||||||
|
if data != nil {
|
||||||
|
listLayout.Append(rui.NewImageView(session, rui.Params{
|
||||||
|
rui.Source: rui.InlineFileFromData(data, file.MimeType),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, mime := range []string {"image/png", "image/jpeg", "image/gif", "image/svg+xml"} {
|
||||||
|
if src, ok := event.Data[mime]; ok {
|
||||||
|
list.Append(rui.NewImageView(session, rui.Params{
|
||||||
|
rui.Source: src,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Получить список слушателей данного события можно с помощью функции:
|
||||||
|
|
||||||
|
func GetDropEventListeners(view View, subviewID ...string) []func(View, DragAndDropEvent)
|
||||||
|
|
||||||
|
#### События "drag-start-event" и "drag-end-event"
|
||||||
|
|
||||||
|
События "drag-start-event" и "drag-end-event" генерируются только для перетаскиваемого View.
|
||||||
|
Событие "drag-start-event" при старте перетаскивания, а "drag-end-event" по окончании перетаскивания.
|
||||||
|
|
||||||
|
Слушатели данных событий как и всех событий drag-and-drop имеют следующий формат:
|
||||||
|
|
||||||
|
func(View, DragAndDropEvent)
|
||||||
|
|
||||||
|
Для данных событий поле Target структуры DragAndDropEvent, всегда равно nil.
|
||||||
|
|
||||||
|
Для события "drag-end-event" используется поле DropEffect структуры DragAndDropEvent.
|
||||||
|
Оно будет иметь значение DropEffectCopy, DropEffectMove или DropEffectLink если операция перетаскивания завершилась успешно и DropEffectNone в противоположном случае.
|
||||||
|
|
||||||
|
Получить список слушателей данных событий можно с помощью функции:
|
||||||
|
|
||||||
|
func GetDragStartEventListeners(view View, subviewID ...string) []func(View, DragAndDropEvent)
|
||||||
|
func GetDragEndEventListeners(view View, subviewID ...string) []func(View, DragAndDropEvent)
|
||||||
|
|
||||||
|
#### События "drag-enter-event", "drag-leave-event" и "drag-over-event"
|
||||||
|
|
||||||
|
События "drag-enter-event", "drag-leave-event" и "drag-over-event" генерируются только для View приемника перетаскиваемого объекта.
|
||||||
|
|
||||||
|
Событие "drag-enter-event" генерируется когда перетаскиваемый объект входит в область View приемника.
|
||||||
|
|
||||||
|
Событие "drag-leave-event" генерируется когда перетаскиваемый объект покидает область View приемника.
|
||||||
|
|
||||||
|
Событие "drag-over-event" генерируется c определенным интервалом (несколько раз в секунду) пока перетаскиваемый объект находится область View приемника.
|
||||||
|
|
||||||
|
Пример, меняем цвет рамки с серой на красную когда перетаскиваемый объект находится над областью приемника
|
||||||
|
|
||||||
|
view.SetParams(rui.Params{
|
||||||
|
rui.DragEnterEvent: func(view rui.View, event rui.DragAndDropEvent)) {
|
||||||
|
view.Set(rui.Border, rui.NewBorder(rui.Params{
|
||||||
|
rui.Style: rui.SolidLine,
|
||||||
|
rui.ColorTag: rui.Red,
|
||||||
|
rui.Width: rui.Px(1),
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
rui.DragLeaveEvent: func(view rui.View, event rui.DragAndDropEvent)) {
|
||||||
|
view.Set(rui.Border, rui.NewBorder(rui.Params{
|
||||||
|
rui.Style: rui.SolidLine,
|
||||||
|
rui.ColorTag: rui.Gray,
|
||||||
|
rui.Width: rui.Px(1),
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
Получить список слушателей данных событий можно с помощью функции:
|
||||||
|
|
||||||
|
func GetDragEnterEventListeners(view View, subviewID ...string) []func(View, DragAndDropEvent)
|
||||||
|
func GetDragLeaveEventListeners(view View, subviewID ...string) []func(View, DragAndDropEvent)
|
||||||
|
func GetDragOverEventListeners(view View, subviewID ...string) []func(View, DragAndDropEvent)
|
||||||
|
|
||||||
## ViewsContainer
|
## ViewsContainer
|
||||||
|
|
||||||
Интерфейс ViewsContainer, реализующий View, описывает контейнер содержащий несколько
|
Интерфейс ViewsContainer, реализующий View, описывает контейнер содержащий несколько
|
||||||
|
|
@ -3897,7 +4085,7 @@ int свойство "current" (константа Current). Значение "c
|
||||||
с помощью int свойств "checkbox-horizontal-align" и "checkbox-vertical-align" (константы
|
с помощью int свойств "checkbox-horizontal-align" и "checkbox-vertical-align" (константы
|
||||||
CheckboxHorizontalAlign и CheckboxVerticalAlign)
|
CheckboxHorizontalAlign и CheckboxVerticalAlign)
|
||||||
|
|
||||||
Свойство "checkbox-horizontal-align" (константа СheckboxHorizontalAlign) может принимать следующие значения:
|
Свойство "checkbox-horizontal-align" (константа CheckboxHorizontalAlign) может принимать следующие значения:
|
||||||
|
|
||||||
| Значение | Константа | Имя | Расположение чекбокса |
|
| Значение | Константа | Имя | Расположение чекбокса |
|
||||||
|:--------:|--------------|----------|-------------------------------------------------|
|
|:--------:|--------------|----------|-------------------------------------------------|
|
||||||
|
|
@ -4459,7 +4647,7 @@ rotation - угол поворота эллипса относительно ц
|
||||||
Метод NewPath() создает пустую фигуру. Далее вы должны описать фигуру используя методы интерфейса Path
|
Метод NewPath() создает пустую фигуру. Далее вы должны описать фигуру используя методы интерфейса Path
|
||||||
|
|
||||||
Метод NewPathFromSvg(data string) Path создает фигуру описанную в параметре data.
|
Метод 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")
|
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")
|
||||||
|
|
||||||
|
|
@ -5516,6 +5704,79 @@ Safari и Firefox.
|
||||||
Для получения объекта используется метод Object.
|
Для получения объекта используется метод Object.
|
||||||
Для получения элементов массива используются методы ArraySize, ArrayElement и ArrayElements
|
Для получения элементов массива используются методы 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)
|
||||||
|
|
||||||
|
Важное замечание: Все методы вызываемые через связывание должны быть публичными (начинаться с большой буквы)
|
||||||
|
|
||||||
## Ресурсы
|
## Ресурсы
|
||||||
|
|
||||||
Ресурсы (картинки, темы, переводы и т.д.) с которыми работает приложение должны размещаться по
|
Ресурсы (картинки, темы, переводы и т.д.) с которыми работает приложение должны размещаться по
|
||||||
|
|
|
||||||
273
README.md
273
README.md
|
|
@ -520,7 +520,7 @@ For the "edit-text-changed" event, this
|
||||||
* func(newText string)
|
* func(newText string)
|
||||||
* []func(editor EditView, newText string)
|
* []func(editor EditView, newText string)
|
||||||
* []func(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).
|
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
|
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
|
If no shadow is specified, then this function will return an empty array
|
||||||
|
|
||||||
|
|
@ -2265,6 +2265,197 @@ The following global functions can be used for manual scrolling
|
||||||
|
|
||||||
which scroll the view, respectively, to the given position, start and end
|
which scroll the view, respectively, to the given position, start and end
|
||||||
|
|
||||||
|
### Drag and drop
|
||||||
|
|
||||||
|
#### "drag-data" property
|
||||||
|
|
||||||
|
To make a View draggable, it is necessary to set the "drag-data" property (DragData constant).
|
||||||
|
This property specifies a set of draggable data in the form of key:value and has the type:
|
||||||
|
|
||||||
|
map[string]string
|
||||||
|
|
||||||
|
It is recommended to use the mime type of the value as keys.
|
||||||
|
For example, if the dragged data contains text, the key should be "text/plain", if it is a jpeg image, then "image/jpg", etc.
|
||||||
|
But this is only a recommendation, the key can be any text.
|
||||||
|
|
||||||
|
Example
|
||||||
|
|
||||||
|
view.Set(rui.DragData, map[string]string {
|
||||||
|
"text/plain": "Drag-and-drop text",
|
||||||
|
"text/html" : "<b>Drag-and-drop<\b> text",
|
||||||
|
"my-key" : "my-data",
|
||||||
|
})
|
||||||
|
|
||||||
|
You can get the value of this property using the function
|
||||||
|
|
||||||
|
GetDragData(view View, subviewID ...string) map[string]string
|
||||||
|
|
||||||
|
|
||||||
|
#### "drag-image" property
|
||||||
|
|
||||||
|
By default, the entire View is moved when dragging. This is often inconvenient, for example if the View is very large.
|
||||||
|
|
||||||
|
The "drag-image" string property (DragImage constant) allows to specify an image that will be displayed instead of the View when dragging.
|
||||||
|
The "drag-image" value is set to:
|
||||||
|
* Image name in the application resources
|
||||||
|
* Image constant
|
||||||
|
* Image URL
|
||||||
|
|
||||||
|
Example
|
||||||
|
|
||||||
|
view.Set(rui.DragImage, "image.png")
|
||||||
|
|
||||||
|
You can get the value of this property using the function
|
||||||
|
|
||||||
|
func GetDragImage(view View, subviewID ...string) string {
|
||||||
|
|
||||||
|
#### The "drag-image-x-offset" and "drag-image-y-offset" properties
|
||||||
|
|
||||||
|
The "drag-image-x-offset" and "drag-image-y-offset" float properties (the DragImageXOffset and DragImageXOffset constants) specify the offset in pixels of the dragged image relative to the mouse cursor.
|
||||||
|
|
||||||
|
By default, the mouse cursor is anchored to the upper left corner of the image. These properties are used only if the "drag-image" property is set.
|
||||||
|
|
||||||
|
Example
|
||||||
|
|
||||||
|
view.SetParams(rui.Params {
|
||||||
|
rui.DragImage : "image.png",
|
||||||
|
rui.DragImageXOffset: 10,
|
||||||
|
rui.DragImageYOffset: -15,
|
||||||
|
})
|
||||||
|
|
||||||
|
You can get the value of these properties using functions
|
||||||
|
|
||||||
|
func GetDragImageXOffset(view View, subviewID ...string) float64
|
||||||
|
func GetDragImageYOffset(view View, subviewID ...string) float64
|
||||||
|
|
||||||
|
#### Events
|
||||||
|
|
||||||
|
Drag-and-drop event listeners have the following format:
|
||||||
|
|
||||||
|
func(View, DragAndDropEvent)
|
||||||
|
|
||||||
|
where DragAndDropEvent extends MouseEvent and is declared as
|
||||||
|
|
||||||
|
type DragAndDropEvent struct {
|
||||||
|
MouseEvent
|
||||||
|
Data map[string]string
|
||||||
|
Files []FileInfo
|
||||||
|
Target View
|
||||||
|
EffectAllowed int
|
||||||
|
DropEffect int
|
||||||
|
}
|
||||||
|
|
||||||
|
You can also use listeners of the following formats:
|
||||||
|
|
||||||
|
* func(DragAndDropEvent)
|
||||||
|
* func(View)
|
||||||
|
* func()
|
||||||
|
|
||||||
|
The fields of the DragAndDropEvent structure contain the following data
|
||||||
|
|
||||||
|
* Data - drag data specified by the "drag-data" property of the dragged View and additional data specified by the browser (for example, Chrome adds the html code of the element). This field can be empty if files are dragged
|
||||||
|
|
||||||
|
* Files - a list of files to drag. Used only by the "drop-event" event (for all other events, this list is nil). Also, if you drag a View, this list will be nil.
|
||||||
|
|
||||||
|
* Target - the receiving View. Has a value different from nil if the dragged object is above a View that can accept the dragged element
|
||||||
|
|
||||||
|
* EffectAllowed - shows a list of allowed mouse cursor move effects: DropEffectCopy, DropEffectMove, DropEffectLink, DropEffectCopyMove, DropEffectCopyLink, DropEffectLinkMove and DropEffectAll.
|
||||||
|
|
||||||
|
* DropEffect - used only in the "drag-end" event and has a value of DropEffectCopy, DropEffectMove or DropEffectLink if the drag operation was successful and DropEffectNone otherwise.
|
||||||
|
|
||||||
|
#### The "drop-event" event
|
||||||
|
|
||||||
|
In order for the View to accept dragged data (Views or files), you must define a "drop-event" (DropEvent constant) event listener for it.
|
||||||
|
Like all drag-and-drop events, it has the following format:
|
||||||
|
|
||||||
|
func(View, DragAndDropEvent)
|
||||||
|
|
||||||
|
The system itself does nothing with the dragged data. You must add code that performs some actions with the dragged data.
|
||||||
|
The following example adds an image from resources or from a dragged file to the ListLayout
|
||||||
|
|
||||||
|
listLayout.Set(rui.DropEvent, func(_ rui.View, event rui.DragAndDropEvent)) {
|
||||||
|
if len(event.Files) > 0 {
|
||||||
|
for _, file := range event.Files {
|
||||||
|
switch file.MimeType {
|
||||||
|
case "image/png", "image/jpeg", "image/gif", "image/svg+xml":
|
||||||
|
listLayout.LoadFile(file, func(file rui.FileInfo, data []byte) {
|
||||||
|
if data != nil {
|
||||||
|
listLayout.Append(rui.NewImageView(session, rui.Params{
|
||||||
|
rui.Source: rui.InlineFileFromData(data, file.MimeType),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, mime := range []string {"image/png", "image/jpeg", "image/gif", "image/svg+xml"} {
|
||||||
|
if src, ok := event.Data[mime]; ok {
|
||||||
|
list.Append(rui.NewImageView(session, rui.Params{
|
||||||
|
rui.Source: src,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
You can get a list of listeners for a given event using the function:
|
||||||
|
|
||||||
|
func GetDropEventListeners(view View, subviewID ...string) []func(View, DragAndDropEvent)
|
||||||
|
|
||||||
|
#### Events "drag-start-event" and "drag-end-event"
|
||||||
|
|
||||||
|
Events "drag-start-event" and "drag-end-event" are generated only for the dragged View.
|
||||||
|
Event "drag-start-event" when dragging starts, and "drag-end-event" when dragging ends.
|
||||||
|
|
||||||
|
Listeners for these events, like all drag-and-drop events, have the following format:
|
||||||
|
|
||||||
|
func(View, DragAndDropEvent)
|
||||||
|
|
||||||
|
For these events, the Target field of the DragAndDropEvent structure is always nil.
|
||||||
|
|
||||||
|
For the "drag-end-event" event, the DropEffect field of the DragAndDropEvent structure is used.
|
||||||
|
It will have the value DropEffectCopy, DropEffectMove or DropEffectLink if the drag operation was successful and DropEffectNone otherwise.
|
||||||
|
|
||||||
|
You can get a list of listeners for these events using the function:
|
||||||
|
|
||||||
|
func GetDragStartEventListeners(view View, subviewID ...string) []func(View, DragAndDropEvent)
|
||||||
|
func GetDragEndEventListeners(view View, subviewID ...string) []func(View, DragAndDropEvent)
|
||||||
|
|
||||||
|
#### Events "drag-enter-event", "drag-leave-event" and "drag-over-event"
|
||||||
|
|
||||||
|
Events "drag-enter-event", "drag-leave-event" and "drag-over-event" are generated only for the receiver View of the dragged object.
|
||||||
|
|
||||||
|
Event "drag-enter-event" is generated when the dragged object enters the receiver View area.
|
||||||
|
|
||||||
|
Event "drag-leave-event" is generated when the dragged object leaves the receiver View area.
|
||||||
|
|
||||||
|
Event "drag-over-event" is generated at a certain interval (several times per second) while the dragged object is in the receiver View area.
|
||||||
|
|
||||||
|
Example, changing the border color from gray to red when the dragged object is over the receiver area
|
||||||
|
|
||||||
|
view.SetParams(rui.Params{
|
||||||
|
rui.DragEnterEvent: func(view rui.View, event rui.DragAndDropEvent)) {
|
||||||
|
view.Set(rui.Border, rui.NewBorder(rui.Params{
|
||||||
|
rui.Style: rui.SolidLine,
|
||||||
|
rui.ColorTag: rui.Red,
|
||||||
|
rui.Width: rui.Px(1),
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
rui.DragLeaveEvent: func(view rui.View, event rui.DragAndDropEvent)) {
|
||||||
|
view.Set(rui.Border, rui.NewBorder(rui.Params{
|
||||||
|
rui.Style: rui.SolidLine,
|
||||||
|
rui.ColorTag: rui.Gray,
|
||||||
|
rui.Width: rui.Px(1),
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
You can get a list of listeners for these events using the function:
|
||||||
|
|
||||||
|
func GetDragEnterEventListeners(view View, subviewID ...string) []func(View, DragAndDropEvent)
|
||||||
|
func GetDragLeaveEventListeners(view View, subviewID ...string) []func(View, DragAndDropEvent)
|
||||||
|
func GetDragOverEventListeners(view View, subviewID ...string) []func(View, DragAndDropEvent)
|
||||||
|
|
||||||
## ViewsContainer
|
## ViewsContainer
|
||||||
|
|
||||||
The ViewsContainer interface, which implements View, describes a container that contains
|
The ViewsContainer interface, which implements View, describes a container that contains
|
||||||
|
|
@ -5529,6 +5720,80 @@ using the SetResourcePath function before creating the Application:
|
||||||
app.Start("localhost:8000")
|
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
|
## Images for screens with different pixel densities
|
||||||
|
|
||||||
If you need to add separate images to the resources for screens with different pixel densities,
|
If you need to add separate images to the resources for screens with different pixel densities,
|
||||||
|
|
@ -5728,7 +5993,7 @@ The library defines a number of constants and styles. You can override them in y
|
||||||
|
|
||||||
System styles that you can override:
|
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.) |
|
| ruiApp | This style is used to set the default text style (font, size, etc.) |
|
||||||
| ruiView | Default View Style |
|
| ruiView | Default View Style |
|
||||||
|
|
@ -5820,7 +6085,7 @@ Translation files must have the "rui" extension and the following format
|
||||||
<text 2> = <translation 2>,
|
<text 2> = <translation 2>,
|
||||||
…
|
…
|
||||||
},
|
},
|
||||||
<язык 2> = _{
|
<language 2> = _{
|
||||||
<text 1> = <translation 1>,
|
<text 1> = <translation 1>,
|
||||||
<text 2> = <translation 2>,
|
<text 2> = <translation 2>,
|
||||||
…
|
…
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package rui
|
package rui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
@ -86,6 +87,10 @@ func StringToAngleUnit(value string) (AngleUnit, bool) {
|
||||||
func stringToAngleUnit(value string) (AngleUnit, error) {
|
func stringToAngleUnit(value string) (AngleUnit, error) {
|
||||||
value = strings.ToLower(strings.Trim(value, " \t\n\r"))
|
value = strings.ToLower(strings.Trim(value, " \t\n\r"))
|
||||||
|
|
||||||
|
if value == "" {
|
||||||
|
return AngleUnit{}, errors.New(`invalid AngleUnit value: ""`)
|
||||||
|
}
|
||||||
|
|
||||||
setValue := func(suffix string, unitType AngleUnitType) (AngleUnit, error) {
|
setValue := func(suffix string, unitType AngleUnitType) (AngleUnit, error) {
|
||||||
val, err := strconv.ParseFloat(value[:len(value)-len(suffix)], 64)
|
val, err := strconv.ParseFloat(value[:len(value)-len(suffix)], 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
141
animation.go
141
animation.go
|
|
@ -2,6 +2,7 @@ package rui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"maps"
|
||||||
"math"
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -209,7 +210,7 @@ type animationData struct {
|
||||||
usageCounter int
|
usageCounter int
|
||||||
view View
|
view View
|
||||||
listener func(view View, animation AnimationProperty, event PropertyName)
|
listener func(view View, animation AnimationProperty, event PropertyName)
|
||||||
oldListeners map[PropertyName][]func(View, PropertyName)
|
oldListeners map[PropertyName][]oneArgListener[View, PropertyName]
|
||||||
oldAnimation []AnimationProperty
|
oldAnimation []AnimationProperty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -231,6 +232,7 @@ type AnimationProperty interface {
|
||||||
Resume()
|
Resume()
|
||||||
|
|
||||||
writeTransitionString(tag PropertyName, buffer *strings.Builder)
|
writeTransitionString(tag PropertyName, buffer *strings.Builder)
|
||||||
|
writeAnimationString(tag PropertyName, buffer *strings.Builder, indent string)
|
||||||
animationCSS(session Session) string
|
animationCSS(session Session) string
|
||||||
transitionCSS(buffer *strings.Builder, session Session)
|
transitionCSS(buffer *strings.Builder, session Session)
|
||||||
hasAnimatedProperty() bool
|
hasAnimatedProperty() bool
|
||||||
|
|
@ -243,8 +245,7 @@ func parseAnimation(obj DataObject) AnimationProperty {
|
||||||
animation := new(animationData)
|
animation := new(animationData)
|
||||||
animation.init()
|
animation.init()
|
||||||
|
|
||||||
for i := 0; i < obj.PropertyCount(); i++ {
|
for node := range obj.Properties() {
|
||||||
if node := obj.Property(i); node != nil {
|
|
||||||
tag := PropertyName(node.Tag())
|
tag := PropertyName(node.Tag())
|
||||||
if node.Type() == TextNode {
|
if node.Type() == TextNode {
|
||||||
animation.Set(tag, node.Text())
|
animation.Set(tag, node.Text())
|
||||||
|
|
@ -252,7 +253,6 @@ func parseAnimation(obj DataObject) AnimationProperty {
|
||||||
animation.Set(tag, node)
|
animation.Set(tag, node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return animation
|
return animation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -491,8 +491,8 @@ func animationSet(properties Properties, tag PropertyName, value any) []Property
|
||||||
case DataNode:
|
case DataNode:
|
||||||
parseObject := func(obj DataObject) (AnimatedProperty, bool) {
|
parseObject := func(obj DataObject) (AnimatedProperty, bool) {
|
||||||
result := AnimatedProperty{}
|
result := AnimatedProperty{}
|
||||||
for i := 0; i < obj.PropertyCount(); i++ {
|
for node := range obj.Properties() {
|
||||||
if node := obj.Property(i); node.Type() == TextNode {
|
if node.Type() == TextNode {
|
||||||
propTag := strings.ToLower(node.Tag())
|
propTag := strings.ToLower(node.Tag())
|
||||||
switch propTag {
|
switch propTag {
|
||||||
case "from", "0", "0%":
|
case "from", "0", "0%":
|
||||||
|
|
@ -521,6 +521,11 @@ func animationSet(properties Properties, tag PropertyName, value any) []Property
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(obj.Tag()) > 0 {
|
||||||
|
result.Tag = PropertyName(obj.Tag())
|
||||||
|
}
|
||||||
|
|
||||||
if result.From != nil && result.To != nil {
|
if result.From != nil && result.To != nil {
|
||||||
return result, true
|
return result, true
|
||||||
}
|
}
|
||||||
|
|
@ -536,7 +541,7 @@ func animationSet(properties Properties, tag PropertyName, value any) []Property
|
||||||
|
|
||||||
case ArrayNode:
|
case ArrayNode:
|
||||||
props := []AnimatedProperty{}
|
props := []AnimatedProperty{}
|
||||||
for _, val := range value.ArrayElements() {
|
for val := range value.ArrayElements() {
|
||||||
if val.IsObject() {
|
if val.IsObject() {
|
||||||
if prop, ok := parseObject(val.Object()); ok {
|
if prop, ok := parseObject(val.Object()); ok {
|
||||||
props = append(props, prop)
|
props = append(props, prop)
|
||||||
|
|
@ -592,32 +597,38 @@ func (animation *animationData) String() string {
|
||||||
for _, tag := range animation.AllTags() {
|
for _, tag := range animation.AllTags() {
|
||||||
if tag != PropertyTag {
|
if tag != PropertyTag {
|
||||||
if value, ok := animation.properties[tag]; ok && value != nil {
|
if value, ok := animation.properties[tag]; ok && value != nil {
|
||||||
|
text := propertyValueToString(tag, value, "\t")
|
||||||
|
if text != "" {
|
||||||
buffer.WriteString("\n\t")
|
buffer.WriteString("\n\t")
|
||||||
buffer.WriteString(string(tag))
|
buffer.WriteString(string(tag))
|
||||||
buffer.WriteString(" = ")
|
buffer.WriteString(" = ")
|
||||||
writePropertyValue(buffer, tag, value, "\t")
|
buffer.WriteString(text)
|
||||||
buffer.WriteRune(',')
|
buffer.WriteRune(',')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
writeProperty := func(prop AnimatedProperty, indent string) {
|
writeProperty := func(prop AnimatedProperty, indent string) {
|
||||||
buffer.WriteString(string(prop.Tag))
|
buffer.WriteString(string(prop.Tag))
|
||||||
buffer.WriteString("{\n")
|
buffer.WriteString("{\n")
|
||||||
buffer.WriteString(indent)
|
buffer.WriteString(indent)
|
||||||
buffer.WriteString("from = ")
|
buffer.WriteString("from = ")
|
||||||
writePropertyValue(buffer, "from", prop.From, indent)
|
buffer.WriteString(propertyValueToString("from", prop.From, indent))
|
||||||
buffer.WriteString(",\n")
|
buffer.WriteString(",\n")
|
||||||
buffer.WriteString(indent)
|
buffer.WriteString(indent)
|
||||||
buffer.WriteString("to = ")
|
buffer.WriteString("to = ")
|
||||||
writePropertyValue(buffer, "to", prop.To, indent)
|
buffer.WriteString(propertyValueToString("to", prop.To, indent))
|
||||||
for key, value := range prop.KeyFrames {
|
for key, value := range prop.KeyFrames {
|
||||||
|
tag := strconv.Itoa(key) + "%"
|
||||||
|
text := propertyValueToString(PropertyName(tag), value, indent)
|
||||||
|
if text != "" {
|
||||||
buffer.WriteString(",\n")
|
buffer.WriteString(",\n")
|
||||||
buffer.WriteString(indent)
|
buffer.WriteString(indent)
|
||||||
tag := strconv.Itoa(key) + "%"
|
|
||||||
buffer.WriteString(tag)
|
buffer.WriteString(tag)
|
||||||
buffer.WriteString(" = ")
|
buffer.WriteString(" = ")
|
||||||
writePropertyValue(buffer, PropertyName(tag), value, indent)
|
buffer.WriteString(text)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
buffer.WriteString("\n")
|
buffer.WriteString("\n")
|
||||||
buffer.WriteString(indent[1:])
|
buffer.WriteString(indent[1:])
|
||||||
|
|
@ -661,7 +672,7 @@ func (animation *animationData) animationCSS(session Session) string {
|
||||||
buffer.WriteString(animation.keyFramesName)
|
buffer.WriteString(animation.keyFramesName)
|
||||||
|
|
||||||
if duration, ok := floatProperty(animation, Duration, session, 1); ok && duration > 0 {
|
if duration, ok := floatProperty(animation, Duration, session, 1); ok && duration > 0 {
|
||||||
buffer.WriteString(fmt.Sprintf(" %gs ", duration))
|
fmt.Fprintf(buffer, " %gs ", duration)
|
||||||
} else {
|
} else {
|
||||||
buffer.WriteString(" 1s ")
|
buffer.WriteString(" 1s ")
|
||||||
}
|
}
|
||||||
|
|
@ -669,7 +680,7 @@ func (animation *animationData) animationCSS(session Session) string {
|
||||||
buffer.WriteString(timingFunctionCSS(animation, TimingFunction, session))
|
buffer.WriteString(timingFunctionCSS(animation, TimingFunction, session))
|
||||||
|
|
||||||
if delay, ok := floatProperty(animation, Delay, session, 0); ok && delay > 0 {
|
if delay, ok := floatProperty(animation, Delay, session, 0); ok && delay > 0 {
|
||||||
buffer.WriteString(fmt.Sprintf(" %gs", delay))
|
fmt.Fprintf(buffer, " %gs", delay)
|
||||||
} else {
|
} else {
|
||||||
buffer.WriteString(" 0s")
|
buffer.WriteString(" 0s")
|
||||||
}
|
}
|
||||||
|
|
@ -678,7 +689,7 @@ func (animation *animationData) animationCSS(session Session) string {
|
||||||
if iterationCount == 0 {
|
if iterationCount == 0 {
|
||||||
iterationCount = 1
|
iterationCount = 1
|
||||||
}
|
}
|
||||||
buffer.WriteString(fmt.Sprintf(" %d ", iterationCount))
|
fmt.Fprintf(buffer, " %d ", iterationCount)
|
||||||
} else {
|
} else {
|
||||||
buffer.WriteString(" infinite ")
|
buffer.WriteString(" infinite ")
|
||||||
}
|
}
|
||||||
|
|
@ -699,7 +710,7 @@ func (animation *animationData) animationCSS(session Session) string {
|
||||||
func (animation *animationData) transitionCSS(buffer *strings.Builder, session Session) {
|
func (animation *animationData) transitionCSS(buffer *strings.Builder, session Session) {
|
||||||
|
|
||||||
if duration, ok := floatProperty(animation, Duration, session, 1); ok && duration > 0 {
|
if duration, ok := floatProperty(animation, Duration, session, 1); ok && duration > 0 {
|
||||||
buffer.WriteString(fmt.Sprintf(" %gs ", duration))
|
fmt.Fprintf(buffer, " %gs ", duration)
|
||||||
} else {
|
} else {
|
||||||
buffer.WriteString(" 1s ")
|
buffer.WriteString(" 1s ")
|
||||||
}
|
}
|
||||||
|
|
@ -707,7 +718,7 @@ func (animation *animationData) transitionCSS(buffer *strings.Builder, session S
|
||||||
buffer.WriteString(timingFunctionCSS(animation, TimingFunction, session))
|
buffer.WriteString(timingFunctionCSS(animation, TimingFunction, session))
|
||||||
|
|
||||||
if delay, ok := floatProperty(animation, Delay, session, 0); ok && delay > 0 {
|
if delay, ok := floatProperty(animation, Delay, session, 0); ok && delay > 0 {
|
||||||
buffer.WriteString(fmt.Sprintf(" %gs", delay))
|
fmt.Fprintf(buffer, " %gs", delay)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -718,13 +729,16 @@ func (animation *animationData) writeTransitionString(tag PropertyName, buffer *
|
||||||
|
|
||||||
writeFloatProperty := func(name PropertyName) bool {
|
writeFloatProperty := func(name PropertyName) bool {
|
||||||
if value := animation.getRaw(name); value != nil {
|
if value := animation.getRaw(name); value != nil {
|
||||||
|
text := propertyValueToString(name, value, "")
|
||||||
|
if text != "" {
|
||||||
buffer.WriteString(lead)
|
buffer.WriteString(lead)
|
||||||
buffer.WriteString(string(name))
|
buffer.WriteString(string(name))
|
||||||
buffer.WriteString(" = ")
|
buffer.WriteString(" = ")
|
||||||
writePropertyValue(buffer, name, value, "")
|
buffer.WriteString(text)
|
||||||
lead = ", "
|
lead = ", "
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -753,6 +767,78 @@ func (animation *animationData) writeTransitionString(tag PropertyName, buffer *
|
||||||
buffer.WriteString(" }")
|
buffer.WriteString(" }")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (animation *animationData) writeAnimationString(tag PropertyName, buffer *strings.Builder, indent string) {
|
||||||
|
buffer.WriteString(indent)
|
||||||
|
|
||||||
|
writeAnimation := func(animation AnimationProperty, buffer *strings.Builder, indent string) {
|
||||||
|
buffer.WriteString("_{")
|
||||||
|
|
||||||
|
indent2 := indent + "\t"
|
||||||
|
for _, tag := range animation.AllTags() {
|
||||||
|
if tag != PropertyTag {
|
||||||
|
if value := animation.Get(tag); value != nil {
|
||||||
|
text := propertyValueToString(tag, value, indent2)
|
||||||
|
if text != "" {
|
||||||
|
buffer.WriteString("\n" + indent2)
|
||||||
|
buffer.WriteString(string(tag))
|
||||||
|
buffer.WriteString(" = ")
|
||||||
|
buffer.WriteString(text)
|
||||||
|
buffer.WriteRune(',')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeProperty := func(prop AnimatedProperty, indent string) {
|
||||||
|
buffer.WriteString(string(prop.Tag))
|
||||||
|
buffer.WriteString("{\n")
|
||||||
|
indent2 := indent + "\t"
|
||||||
|
buffer.WriteString(indent2)
|
||||||
|
buffer.WriteString("from = ")
|
||||||
|
buffer.WriteString(propertyValueToString("from", prop.From, indent2))
|
||||||
|
buffer.WriteString(",\n")
|
||||||
|
buffer.WriteString(indent2)
|
||||||
|
buffer.WriteString("to = ")
|
||||||
|
buffer.WriteString(propertyValueToString("to", prop.To, indent2))
|
||||||
|
for key, value := range prop.KeyFrames {
|
||||||
|
text := propertyValueToString(PropertyName(tag), value, indent2)
|
||||||
|
if text != "" {
|
||||||
|
buffer.WriteString(",\n")
|
||||||
|
buffer.WriteString(indent2)
|
||||||
|
tag := strconv.Itoa(key) + "%"
|
||||||
|
buffer.WriteString(tag)
|
||||||
|
buffer.WriteString(" = ")
|
||||||
|
buffer.WriteString(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer.WriteString(",\n")
|
||||||
|
buffer.WriteString(indent)
|
||||||
|
buffer.WriteString("},")
|
||||||
|
}
|
||||||
|
|
||||||
|
if props, ok := animation.Get(PropertyTag).([]AnimatedProperty); ok && props != nil && len(props) > 0 {
|
||||||
|
buffer.WriteString("\n" + indent2)
|
||||||
|
buffer.WriteString(string(PropertyTag))
|
||||||
|
buffer.WriteString(" = ")
|
||||||
|
if len(props) > 1 {
|
||||||
|
buffer.WriteString("[\n")
|
||||||
|
for _, prop := range props {
|
||||||
|
buffer.WriteString(indent2)
|
||||||
|
writeProperty(prop, indent2+"\t")
|
||||||
|
buffer.WriteString("\n")
|
||||||
|
}
|
||||||
|
buffer.WriteString(indent2 + "],")
|
||||||
|
} else {
|
||||||
|
writeProperty(props[0], indent2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer.WriteRune('\n')
|
||||||
|
buffer.WriteString(indent + "},\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
writeAnimation(animation, buffer, indent)
|
||||||
|
}
|
||||||
|
|
||||||
func timingFunctionCSS(properties Properties, tag PropertyName, session Session) string {
|
func timingFunctionCSS(properties Properties, tag PropertyName, session Session) string {
|
||||||
if timingFunction, ok := stringProperty(properties, tag, session); ok {
|
if timingFunction, ok := stringProperty(properties, tag, session); ok {
|
||||||
if timingFunction, ok = session.resolveConstants(timingFunction); ok && isTimingFunctionValid(timingFunction) {
|
if timingFunction, ok = session.resolveConstants(timingFunction); ok && isTimingFunctionValid(timingFunction) {
|
||||||
|
|
@ -840,7 +926,7 @@ func (session *sessionData) registerAnimation(props []AnimatedProperty) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
cssBuilder.startAnimationFrame("from")
|
cssBuilder.startAnimationFrame("from")
|
||||||
NewViewStyle(fromParams).cssViewStyle(&cssBuilder, session)
|
writeViewStyleCSS(NewViewStyle(fromParams), &cssBuilder, session, false)
|
||||||
cssBuilder.endAnimationFrame()
|
cssBuilder.endAnimationFrame()
|
||||||
|
|
||||||
if len(frames) > 0 {
|
if len(frames) > 0 {
|
||||||
|
|
@ -856,14 +942,14 @@ func (session *sessionData) registerAnimation(props []AnimatedProperty) string {
|
||||||
|
|
||||||
if len(params) > 0 {
|
if len(params) > 0 {
|
||||||
cssBuilder.startAnimationFrame(strconv.Itoa(frame) + "%")
|
cssBuilder.startAnimationFrame(strconv.Itoa(frame) + "%")
|
||||||
NewViewStyle(params).cssViewStyle(&cssBuilder, session)
|
writeViewStyleCSS(NewViewStyle(params), &cssBuilder, session, false)
|
||||||
cssBuilder.endAnimationFrame()
|
cssBuilder.endAnimationFrame()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cssBuilder.startAnimationFrame("to")
|
cssBuilder.startAnimationFrame("to")
|
||||||
NewViewStyle(toParams).cssViewStyle(&cssBuilder, session)
|
writeViewStyleCSS(NewViewStyle(toParams), &cssBuilder, session, false)
|
||||||
cssBuilder.endAnimationFrame()
|
cssBuilder.endAnimationFrame()
|
||||||
|
|
||||||
cssBuilder.endAnimation()
|
cssBuilder.endAnimation()
|
||||||
|
|
@ -979,11 +1065,10 @@ func (style *viewStyle) Transition(tag PropertyName) AnimationProperty {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (style *viewStyle) Transitions() map[PropertyName]AnimationProperty {
|
func (style *viewStyle) Transitions() map[PropertyName]AnimationProperty {
|
||||||
result := map[PropertyName]AnimationProperty{}
|
if transitions := getTransitionProperty(style); transitions != nil {
|
||||||
for tag, animation := range getTransitionProperty(style) {
|
return maps.Clone(transitions)
|
||||||
result[tag] = animation
|
|
||||||
}
|
}
|
||||||
return result
|
return map[PropertyName]AnimationProperty{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (style *viewStyle) SetTransition(tag PropertyName, animation AnimationProperty) {
|
func (style *viewStyle) SetTransition(tag PropertyName, animation AnimationProperty) {
|
||||||
|
|
@ -1050,7 +1135,7 @@ func setAnimationProperty(properties Properties, tag PropertyName, value any) bo
|
||||||
case DataNode:
|
case DataNode:
|
||||||
animations := []AnimationProperty{}
|
animations := []AnimationProperty{}
|
||||||
result := true
|
result := true
|
||||||
for i := 0; i < value.ArraySize(); i++ {
|
for i := range value.ArraySize() {
|
||||||
if obj := value.ArrayElement(i).Object(); obj != nil {
|
if obj := value.ArrayElement(i).Object(); obj != nil {
|
||||||
if anim := parseAnimation(obj); anim.hasAnimatedProperty() {
|
if anim := parseAnimation(obj); anim.hasAnimatedProperty() {
|
||||||
animations = append(animations, anim)
|
animations = append(animations, anim)
|
||||||
|
|
@ -1083,7 +1168,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.
|
// 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 {
|
func IsAnimationPaused(view View, subviewID ...string) bool {
|
||||||
return boolStyledProperty(view, subviewID, AnimationPaused, false)
|
return boolStyledProperty(view, subviewID, AnimationPaused, false)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -156,45 +156,6 @@ const (
|
||||||
AnimationIterationEvent PropertyName = "animation-iteration-event"
|
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) {
|
func (view *viewData) handleTransitionEvents(tag PropertyName, data DataObject) {
|
||||||
if propertyName, ok := data.PropertyValue("property"); ok {
|
if propertyName, ok := data.PropertyValue("property"); ok {
|
||||||
property := PropertyName(propertyName)
|
property := PropertyName(propertyName)
|
||||||
|
|
@ -208,50 +169,11 @@ func (view *viewData) handleTransitionEvents(tag PropertyName, data DataObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, listener := range getOneArgEventListeners[View, PropertyName](view, nil, tag) {
|
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) {
|
func (view *viewData) handleAnimationEvents(tag PropertyName, data DataObject) {
|
||||||
if listeners := getOneArgEventListeners[View, string](view, nil, tag); len(listeners) > 0 {
|
if listeners := getOneArgEventListeners[View, string](view, nil, tag); len(listeners) > 0 {
|
||||||
id := ""
|
id := ""
|
||||||
|
|
@ -263,63 +185,135 @@ func (view *viewData) handleAnimationEvents(tag PropertyName, data DataObject) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, listener := range listeners {
|
for _, listener := range listeners {
|
||||||
listener(view, id)
|
listener.Run(view, id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTransitionRunListeners returns the "transition-run-event" listener list.
|
// GetTransitionRunListeners returns the "transition-run-event" listener list.
|
||||||
// If there are no listeners then the empty list is returned.
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[View, string](view, subviewID, TransitionRunEvent)
|
// - 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.
|
// GetTransitionStartListeners returns the "transition-start-event" listener list.
|
||||||
// If there are no listeners then the empty list is returned.
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[View, string](view, subviewID, TransitionStartEvent)
|
// - 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.
|
// GetTransitionEndListeners returns the "transition-end-event" listener list.
|
||||||
// If there are no listeners then the empty list is returned.
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[View, string](view, subviewID, TransitionEndEvent)
|
// - 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.
|
// GetTransitionCancelListeners returns the "transition-cancel-event" listener list.
|
||||||
// If there are no listeners then the empty list is returned.
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[View, string](view, subviewID, TransitionCancelEvent)
|
// - 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.
|
// GetAnimationStartListeners returns the "animation-start-event" listener list.
|
||||||
// If there are no listeners then the empty list is returned.
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[View, string](view, subviewID, AnimationStartEvent)
|
// - 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.
|
// GetAnimationEndListeners returns the "animation-end-event" listener list.
|
||||||
// If there are no listeners then the empty list is returned.
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[View, string](view, subviewID, AnimationEndEvent)
|
// - 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.
|
// GetAnimationCancelListeners returns the "animation-cancel-event" listener list.
|
||||||
// If there are no listeners then the empty list is returned.
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[View, string](view, subviewID, AnimationCancelEvent)
|
// - 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.
|
// GetAnimationIterationListeners returns the "animation-iteration-event" listener list.
|
||||||
// If there are no listeners then the empty list is returned.
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[View, string](view, subviewID, AnimationIterationEvent)
|
// - 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
|
package rui
|
||||||
|
|
||||||
|
import "slices"
|
||||||
|
|
||||||
func (animation *animationData) Start(view View, listener func(view View, animation AnimationProperty, event PropertyName)) bool {
|
func (animation *animationData) Start(view View, listener func(view View, animation AnimationProperty, event PropertyName)) bool {
|
||||||
if view == nil {
|
if view == nil {
|
||||||
ErrorLog("nil View in animation.Start() function")
|
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.listener = listener
|
||||||
|
|
||||||
animation.oldAnimation = nil
|
animation.oldAnimation = nil
|
||||||
|
|
||||||
|
//if getOneArgEventListeners[View, PropertyName](view, nil, Animation)
|
||||||
if value := view.Get(Animation); value != nil {
|
if value := view.Get(Animation); value != nil {
|
||||||
if oldAnimation, ok := value.([]AnimationProperty); ok && len(oldAnimation) > 0 {
|
if oldAnimation, ok := value.([]AnimationProperty); ok && len(oldAnimation) > 0 {
|
||||||
animation.oldAnimation = oldAnimation
|
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)) {
|
setListeners := func(event PropertyName, listener func(View, PropertyName)) {
|
||||||
var listeners []func(View, PropertyName) = nil
|
listeners := getOneArgEventListeners[View, PropertyName](view, nil, event)
|
||||||
if value := view.Get(event); value != nil {
|
if len(listeners) > 0 {
|
||||||
if oldListeners, ok := value.([]func(View, PropertyName)); ok && len(oldListeners) > 0 {
|
animation.oldListeners[event] = slices.Clone(listeners)
|
||||||
listeners = oldListeners
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if listeners == nil {
|
|
||||||
view.Set(event, listener)
|
|
||||||
} else {
|
|
||||||
animation.oldListeners[event] = listeners
|
|
||||||
view.Set(event, append(listeners, listener))
|
|
||||||
}
|
}
|
||||||
|
view.Set(event, append(listeners, newOneArgListenerVE(listener)))
|
||||||
}
|
}
|
||||||
|
|
||||||
setListeners(AnimationStartEvent, animation.onAnimationStart)
|
setListeners(AnimationStartEvent, animation.onAnimationStart)
|
||||||
|
|
@ -49,7 +45,7 @@ func (animation *animationData) Start(view View, listener func(view View, animat
|
||||||
func (animation *animationData) finish() {
|
func (animation *animationData) finish() {
|
||||||
if animation.view != nil {
|
if animation.view != nil {
|
||||||
for _, event := range []PropertyName{AnimationStartEvent, AnimationEndEvent, AnimationCancelEvent, AnimationIterationEvent} {
|
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)
|
animation.view.Set(event, listeners)
|
||||||
} else {
|
} else {
|
||||||
animation.view.Remove(event)
|
animation.view.Remove(event)
|
||||||
|
|
@ -63,7 +59,7 @@ func (animation *animationData) finish() {
|
||||||
animation.view.Set(Animation, "")
|
animation.view.Set(Animation, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
animation.oldListeners = map[PropertyName][]func(View, PropertyName){}
|
animation.oldListeners = map[PropertyName][]oneArgListener[View, PropertyName]{}
|
||||||
|
|
||||||
animation.view = nil
|
animation.view = nil
|
||||||
animation.listener = nil
|
animation.listener = nil
|
||||||
|
|
|
||||||
23
appServer.go
23
appServer.go
|
|
@ -106,12 +106,12 @@ func (app *application) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch req.Method {
|
switch req.Method {
|
||||||
case "POST":
|
case http.MethodPost:
|
||||||
if req.URL.Path == "/" {
|
if req.URL.Path == "/" {
|
||||||
app.postHandler(w, req)
|
app.postHandler(w, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
case "GET":
|
case http.MethodGet:
|
||||||
switch req.URL.Path {
|
switch req.URL.Path {
|
||||||
case "/":
|
case "/":
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
|
|
@ -164,7 +164,12 @@ func (app *application) postHandler(w http.ResponseWriter, req *http.Request) {
|
||||||
DebugLog(message)
|
DebugLog(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
if obj := ParseDataText(message); obj != nil {
|
obj, err := ParseDataText(message)
|
||||||
|
if err != nil {
|
||||||
|
ErrorLog(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var session Session = nil
|
var session Session = nil
|
||||||
var response chan string = nil
|
var response chan string = nil
|
||||||
|
|
||||||
|
|
@ -224,7 +229,6 @@ func (app *application) postHandler(w http.ResponseWriter, req *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func (app *application) socketReader(bridge *wsBridge) {
|
func (app *application) socketReader(bridge *wsBridge) {
|
||||||
var session Session
|
var session Session
|
||||||
|
|
@ -241,9 +245,13 @@ func (app *application) socketReader(bridge *wsBridge) {
|
||||||
DebugLog("🖥️ -> " + message)
|
DebugLog("🖥️ -> " + message)
|
||||||
}
|
}
|
||||||
|
|
||||||
if obj := ParseDataText(message); obj != nil {
|
obj, err := ParseDataText(message)
|
||||||
command := obj.Tag()
|
if err != nil {
|
||||||
switch command {
|
ErrorLog(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch command := obj.Tag(); command {
|
||||||
case "startSession":
|
case "startSession":
|
||||||
answer := ""
|
answer := ""
|
||||||
if session, answer = app.startSession(obj, events, bridge, nil); session != nil {
|
if session, answer = app.startSession(obj, events, bridge, nil); session != nil {
|
||||||
|
|
@ -296,7 +304,6 @@ func (app *application) socketReader(bridge *wsBridge) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func sessionEventHandler(session Session, events chan DataObject, bridge bridge) {
|
func sessionEventHandler(session Session, events chan DataObject, bridge bridge) {
|
||||||
for {
|
for {
|
||||||
|
|
|
||||||
172
app_scripts.js
172
app_scripts.js
|
|
@ -1008,10 +1008,8 @@ function setInputValue(elementId, text) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function fileSelectedEvent(element) {
|
function filesTextForMessage(files) {
|
||||||
const files = element.files;
|
let message = "files=[";
|
||||||
if (files) {
|
|
||||||
let message = "fileSelected{session=" + sessionID + ",id=" + element.id + ",files=[";
|
|
||||||
for(let i = 0; i < files.length; i++) {
|
for(let i = 0; i < files.length; i++) {
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
message += ",";
|
message += ",";
|
||||||
|
|
@ -1021,7 +1019,15 @@ function fileSelectedEvent(element) {
|
||||||
",size=" + files[i].size +
|
",size=" + files[i].size +
|
||||||
",mime-type=\"" + files[i].type + "\"}";
|
",mime-type=\"" + files[i].type + "\"}";
|
||||||
}
|
}
|
||||||
sendMessage(message + "]}");
|
message += "]";
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
|
||||||
|
function fileSelectedEvent(element) {
|
||||||
|
const files = element.files;
|
||||||
|
if (files) {
|
||||||
|
let message = "fileSelected{session=" + sessionID + ",id=" + element.id + "," + filesTextForMessage(files) + "}";
|
||||||
|
sendMessage(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2125,3 +2131,159 @@ function createPath2D(svg) {
|
||||||
return new Path2D();
|
return new Path2D();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function stringToBase64(str) {
|
||||||
|
const bytes = new TextEncoder().encode(str);
|
||||||
|
const binString = String.fromCodePoint(...bytes);
|
||||||
|
return btoa(binString);
|
||||||
|
}
|
||||||
|
|
||||||
|
function base64ToString(base64) {
|
||||||
|
const binString = atob(base64);
|
||||||
|
const bytes = Uint8Array.from(binString, (m) => m.codePointAt(0));
|
||||||
|
const result = new TextDecoder().decode(bytes);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dragAndDropEvent(element, event, tag) {
|
||||||
|
event.stopPropagation();
|
||||||
|
//event.preventDefault()
|
||||||
|
|
||||||
|
let message = tag + "{session=" + sessionID + ",id=" + element.id + mouseEventData(element, event);
|
||||||
|
if (event.target) {
|
||||||
|
message += ",target=" + event.target.id;
|
||||||
|
}
|
||||||
|
if (event.dataTransfer) {
|
||||||
|
let dataText = ""
|
||||||
|
for (const item of event.dataTransfer.items) {
|
||||||
|
const data = event.dataTransfer.getData(item.type);
|
||||||
|
if (data) {
|
||||||
|
if (dataText != "") {
|
||||||
|
dataText += ";";
|
||||||
|
}
|
||||||
|
dataText += stringToBase64(item.type) + ":" + stringToBase64(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dataText != "") {
|
||||||
|
message += ',data="' + dataText + '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
const files = event.dataTransfer.files
|
||||||
|
if (files && files.length > 0) {
|
||||||
|
message += "," + filesTextForMessage(files)
|
||||||
|
element["dragFiles"] = files;
|
||||||
|
} else {
|
||||||
|
element["dragFiles"] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.dataTransfer.effectAllowed && event.dataTransfer.effectAllowed != "uninitialized") {
|
||||||
|
message += ',effect-allowed="' + event.dataTransfer.effectAllowed + '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.dataTransfer.dropEffect) {
|
||||||
|
message += ',drop-effect="' + event.dataTransfer.dropEffect + '"';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message += "}";
|
||||||
|
sendMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
function dragStartEvent(element, event) {
|
||||||
|
const data = element.getAttribute("data-drag");
|
||||||
|
if (data) {
|
||||||
|
const elements = data.split(";");
|
||||||
|
for (const line of elements) {
|
||||||
|
const pair = line.split(":");
|
||||||
|
if (pair.length == 2) {
|
||||||
|
event.dataTransfer.setData(base64ToString(pair[0]), base64ToString(pair[1]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const image = element.getAttribute("data-drag-image");
|
||||||
|
if (image) {
|
||||||
|
let x = element.getAttribute("data-drag-image-x");
|
||||||
|
if (!x) {
|
||||||
|
x = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let y = element.getAttribute("data-drag-image-y");
|
||||||
|
if (!y) {
|
||||||
|
y = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let img = new Image();
|
||||||
|
img.src = image;
|
||||||
|
event.dataTransfer.setDragImage(img, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
let allowed = element.getAttribute("data-drop-effect-allowed");
|
||||||
|
if (allowed) {
|
||||||
|
event.dataTransfer.effectAllowed = allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
dragAndDropEvent(element, event, "drag-start-event");
|
||||||
|
}
|
||||||
|
|
||||||
|
function dragEndEvent(element, event) {
|
||||||
|
dragAndDropEvent(element, event, "drag-end-event")
|
||||||
|
}
|
||||||
|
|
||||||
|
function dragEnterEvent(element, event) {
|
||||||
|
let effect = element.getAttribute("data-drop-effect");
|
||||||
|
if (effect) {
|
||||||
|
event.dataTransfer.dropEffect = effect;
|
||||||
|
}
|
||||||
|
|
||||||
|
dragAndDropEvent(element, event, "drag-enter-event")
|
||||||
|
}
|
||||||
|
|
||||||
|
function dragLeaveEvent(element, event) {
|
||||||
|
dragAndDropEvent(element, event, "drag-leave-event")
|
||||||
|
}
|
||||||
|
|
||||||
|
function dragOverEvent(element, event) {
|
||||||
|
event.preventDefault();
|
||||||
|
if (element.getAttribute("data-drag-over") == "1") {
|
||||||
|
dragAndDropEvent(element, event, "drag-over-event")
|
||||||
|
} else {
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function dropEvent(element, event) {
|
||||||
|
event.preventDefault();
|
||||||
|
dragAndDropEvent(element, event, "drop-event")
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadDropFile(elementId, name, size) {
|
||||||
|
const element = document.getElementById(elementId);
|
||||||
|
if (element) {
|
||||||
|
const files = element["dragFiles"];
|
||||||
|
if (files) {
|
||||||
|
for(let i = 0; i < files.length; i++) {
|
||||||
|
const file = files[i]
|
||||||
|
if (file.name == name && file.size == size) {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = function() {
|
||||||
|
sendMessage("fileLoaded{session=" + sessionID + ",id=" + element.id +
|
||||||
|
",name=`" + name +
|
||||||
|
"`,size=" + size +
|
||||||
|
",last-modified=" + file.lastModified +
|
||||||
|
",mime-type=\"" + file.type +
|
||||||
|
"\",data=`" + reader.result + "`}");
|
||||||
|
}
|
||||||
|
reader.onerror = function(error) {
|
||||||
|
sendMessage("fileLoadingError{session=" + sessionID + ",id=" + element.id + ",name=\"" + name + "\",size=" + size + ",error=`" + error + "`}");
|
||||||
|
}
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sendMessage("fileLoadingError{session=" + sessionID + ",id=" + element.id + ",name=`" + name + "`,size=" + size + ",error=`File not found`}");
|
||||||
|
} else {
|
||||||
|
sendMessage("fileLoadingError{session=" + sessionID + ",id=" + element.id + ",name=`" + name + "`,size=" + size + ",error=`Invalid View id`}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -151,6 +151,14 @@ ul:focus {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ruiButton {
|
||||||
|
display: flex;
|
||||||
|
overflow: auto;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-flow: row;
|
||||||
|
}
|
||||||
|
|
||||||
.ruiColumnLayout {
|
.ruiColumnLayout {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,9 +66,8 @@ func createBackground(obj DataObject) BackgroundElement {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
count := obj.PropertyCount()
|
for node := range obj.Properties() {
|
||||||
for i := 0; i < count; i++ {
|
if node.Type() == TextNode {
|
||||||
if node := obj.Property(i); node.Type() == TextNode {
|
|
||||||
if value := node.Text(); value != "" {
|
if value := node.Text(); value != "" {
|
||||||
result.Set(PropertyName(node.Tag()), value)
|
result.Set(PropertyName(node.Tag()), value)
|
||||||
}
|
}
|
||||||
|
|
@ -78,6 +77,16 @@ func createBackground(obj DataObject) BackgroundElement {
|
||||||
return result
|
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 {
|
func parseBackgroundValue(value any) []BackgroundElement {
|
||||||
|
|
||||||
switch value := value.(type) {
|
switch value := value.(type) {
|
||||||
|
|
@ -96,15 +105,11 @@ func parseBackgroundValue(value any) []BackgroundElement {
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
} else if obj := ParseDataText(el.Value()); obj != nil {
|
} else if element := parseBackgroundText(el.Value()); element != nil {
|
||||||
if element := createBackground(obj); element != nil {
|
|
||||||
background = append(background, element)
|
background = append(background, element)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return background
|
return background
|
||||||
|
|
||||||
|
|
@ -125,44 +130,34 @@ func parseBackgroundValue(value any) []BackgroundElement {
|
||||||
return background
|
return background
|
||||||
|
|
||||||
case string:
|
case string:
|
||||||
if obj := ParseDataText(value); obj != nil {
|
if element := parseBackgroundText(value); element != nil {
|
||||||
if element := createBackground(obj); element != nil {
|
|
||||||
return []BackgroundElement{element}
|
return []BackgroundElement{element}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
case []string:
|
case []string:
|
||||||
elements := make([]BackgroundElement, 0, len(value))
|
elements := make([]BackgroundElement, 0, len(value))
|
||||||
for _, element := range value {
|
for _, text := range value {
|
||||||
if obj := ParseDataText(element); obj != nil {
|
if element := parseBackgroundText(text); element != nil {
|
||||||
if element := createBackground(obj); element != nil {
|
|
||||||
elements = append(elements, element)
|
elements = append(elements, element)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return elements
|
return elements
|
||||||
|
|
||||||
case []any:
|
case []any:
|
||||||
elements := make([]BackgroundElement, 0, len(value))
|
elements := make([]BackgroundElement, 0, len(value))
|
||||||
for _, element := range value {
|
for _, val := range value {
|
||||||
switch element := element.(type) {
|
switch val := val.(type) {
|
||||||
case BackgroundElement:
|
case BackgroundElement:
|
||||||
elements = append(elements, element)
|
elements = append(elements, val)
|
||||||
|
|
||||||
case string:
|
case string:
|
||||||
if obj := ParseDataText(element); obj != nil {
|
if element := parseBackgroundText(val); element != nil {
|
||||||
if element := createBackground(obj); element != nil {
|
|
||||||
elements = append(elements, element)
|
elements = append(elements, element)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -268,14 +263,16 @@ func backgroundStyledPropery(view View, subviewID []string, tag PropertyName) []
|
||||||
|
|
||||||
// GetBackground returns the view background.
|
// 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 {
|
func GetBackground(view View, subviewID ...string) []BackgroundElement {
|
||||||
return backgroundStyledPropery(view, subviewID, Background)
|
return backgroundStyledPropery(view, subviewID, Background)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMask returns the view mask.
|
// 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 {
|
func GetMask(view View, subviewID ...string) []BackgroundElement {
|
||||||
return backgroundStyledPropery(view, subviewID, Mask)
|
return backgroundStyledPropery(view, subviewID, Mask)
|
||||||
}
|
}
|
||||||
|
|
@ -284,7 +281,8 @@ func GetMask(view View, subviewID ...string) []BackgroundElement {
|
||||||
//
|
//
|
||||||
// BorderBox (0), PaddingBox (1), ContentBox (2)
|
// 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 {
|
func GetBackgroundClip(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, BackgroundClip, 0, false)
|
return enumStyledProperty(view, subviewID, BackgroundClip, 0, false)
|
||||||
}
|
}
|
||||||
|
|
@ -293,7 +291,8 @@ func GetBackgroundClip(view View, subviewID ...string) int {
|
||||||
//
|
//
|
||||||
// BorderBox (0), PaddingBox (1), ContentBox (2)
|
// 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 {
|
func GetBackgroundOrigin(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, BackgroundOrigin, 0, false)
|
return enumStyledProperty(view, subviewID, BackgroundOrigin, 0, false)
|
||||||
}
|
}
|
||||||
|
|
@ -302,7 +301,8 @@ func GetBackgroundOrigin(view View, subviewID ...string) int {
|
||||||
//
|
//
|
||||||
// BorderBox (0), PaddingBox (1), ContentBox (2)
|
// 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 {
|
func GetMaskClip(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, MaskClip, 0, false)
|
return enumStyledProperty(view, subviewID, MaskClip, 0, false)
|
||||||
}
|
}
|
||||||
|
|
@ -311,7 +311,8 @@ func GetMaskClip(view View, subviewID ...string) int {
|
||||||
//
|
//
|
||||||
// BorderBox (0), PaddingBox (1), ContentBox (2)
|
// 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 {
|
func GetMaskOrigin(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, MaskOrigin, 0, false)
|
return enumStyledProperty(view, subviewID, MaskOrigin, 0, false)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,17 +65,13 @@ func (point *BackgroundGradientAngle) color(session Session) (Color, bool) {
|
||||||
if point.Color != nil {
|
if point.Color != nil {
|
||||||
switch color := point.Color.(type) {
|
switch color := point.Color.(type) {
|
||||||
case string:
|
case string:
|
||||||
if color != "" {
|
if ok, constName := isConstantName(color); ok {
|
||||||
if color[0] == '@' {
|
if clr, ok := session.Color(constName); ok {
|
||||||
if clr, ok := session.Color(color[1:]); ok {
|
|
||||||
return clr, true
|
return clr, true
|
||||||
}
|
}
|
||||||
} else {
|
} else if clr, ok := StringToColor(color); ok {
|
||||||
if clr, ok := StringToColor(color); ok {
|
|
||||||
return clr, true
|
return clr, true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case Color:
|
case Color:
|
||||||
return color, true
|
return color, true
|
||||||
|
|
@ -104,11 +100,8 @@ func (point *BackgroundGradientAngle) cssString(session Session, buffer *strings
|
||||||
if point.Angle != nil {
|
if point.Angle != nil {
|
||||||
switch value := point.Angle.(type) {
|
switch value := point.Angle.(type) {
|
||||||
case string:
|
case string:
|
||||||
if value != "" {
|
if ok, constName := isConstantName(value); ok {
|
||||||
if value[0] == '@' {
|
if value, ok = session.Constant(constName); !ok {
|
||||||
if val, ok := session.Constant(value[1:]); ok {
|
|
||||||
value = val
|
|
||||||
} else {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -117,7 +110,6 @@ func (point *BackgroundGradientAngle) cssString(session Session, buffer *strings
|
||||||
buffer.WriteRune(' ')
|
buffer.WriteRune(' ')
|
||||||
buffer.WriteString(angle.cssString())
|
buffer.WriteString(angle.cssString())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
case AngleUnit:
|
case AngleUnit:
|
||||||
buffer.WriteRune(' ')
|
buffer.WriteRune(' ')
|
||||||
|
|
@ -169,12 +161,12 @@ func backgroundConicGradientSet(properties Properties, tag PropertyName, value a
|
||||||
return propertiesRemove(properties, tag)
|
return propertiesRemove(properties, tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.Contains(value, ",") || strings.Contains(value, " ") {
|
if strings.ContainsAny(value, ", ") {
|
||||||
if vector := parseGradientText(value); vector != nil {
|
if vector := parseGradientText(value); vector != nil {
|
||||||
properties.setRaw(Gradient, vector)
|
properties.setRaw(Gradient, vector)
|
||||||
return []PropertyName{tag}
|
return []PropertyName{tag}
|
||||||
}
|
}
|
||||||
} else if isConstantName(value) {
|
} else if ok, _ := isConstantName(value); ok {
|
||||||
properties.setRaw(Gradient, value)
|
properties.setRaw(Gradient, value)
|
||||||
return []PropertyName{tag}
|
return []PropertyName{tag}
|
||||||
}
|
}
|
||||||
|
|
@ -206,15 +198,6 @@ func backgroundConicGradientSet(properties Properties, tag PropertyName, value a
|
||||||
return propertiesSet(properties, tag, value)
|
return propertiesSet(properties, tag, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gradient *backgroundConicGradient) stringToAngle(text string) (any, bool) {
|
|
||||||
if text == "" {
|
|
||||||
return nil, false
|
|
||||||
} else if text[0] == '@' {
|
|
||||||
return text, true
|
|
||||||
}
|
|
||||||
return StringToAngleUnit(text)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gradient *backgroundConicGradient) stringToGradientPoint(text string) (BackgroundGradientAngle, bool) {
|
func (gradient *backgroundConicGradient) stringToGradientPoint(text string) (BackgroundGradientAngle, bool) {
|
||||||
var result BackgroundGradientAngle
|
var result BackgroundGradientAngle
|
||||||
colorText := ""
|
colorText := ""
|
||||||
|
|
@ -231,7 +214,7 @@ func (gradient *backgroundConicGradient) stringToGradientPoint(text string) (Bac
|
||||||
return result, false
|
return result, false
|
||||||
}
|
}
|
||||||
|
|
||||||
if colorText[0] == '@' {
|
if ok, _ := isConstantName(colorText); ok {
|
||||||
result.Color = colorText
|
result.Color = colorText
|
||||||
} else if color, ok := StringToColor(colorText); ok {
|
} else if color, ok := StringToColor(colorText); ok {
|
||||||
result.Color = color
|
result.Color = color
|
||||||
|
|
@ -240,7 +223,9 @@ func (gradient *backgroundConicGradient) stringToGradientPoint(text string) (Bac
|
||||||
}
|
}
|
||||||
|
|
||||||
if pointText != "" {
|
if pointText != "" {
|
||||||
if angle, ok := gradient.stringToAngle(pointText); ok {
|
if ok, _ := isConstantName(pointText); ok {
|
||||||
|
result.Angle = pointText
|
||||||
|
} else if angle, ok := StringToAngleUnit(text); ok {
|
||||||
result.Angle = angle
|
result.Angle = angle
|
||||||
} else {
|
} else {
|
||||||
return result, false
|
return result, false
|
||||||
|
|
|
||||||
|
|
@ -103,14 +103,14 @@ func backgroundGradientSet(properties Properties, tag PropertyName, value any) [
|
||||||
case Gradient:
|
case Gradient:
|
||||||
switch value := value.(type) {
|
switch value := value.(type) {
|
||||||
case string:
|
case string:
|
||||||
if value != "" {
|
if ok, _ := isConstantName(value); ok {
|
||||||
if strings.Contains(value, " ") || strings.Contains(value, ",") {
|
properties.setRaw(Gradient, value)
|
||||||
if points := parseGradientText(value); len(points) >= 2 {
|
|
||||||
properties.setRaw(Gradient, points)
|
|
||||||
return []PropertyName{tag}
|
return []PropertyName{tag}
|
||||||
}
|
}
|
||||||
} else if value[0] == '@' {
|
|
||||||
properties.setRaw(Gradient, value)
|
if strings.ContainsAny(value, " ,") {
|
||||||
|
if points := parseGradientText(value); len(points) >= 2 {
|
||||||
|
properties.setRaw(Gradient, points)
|
||||||
return []PropertyName{tag}
|
return []PropertyName{tag}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -168,7 +168,7 @@ func (point *BackgroundGradientPoint) setValue(text string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if colorText[0] == '@' {
|
if ok, _ := isConstantName(colorText); ok {
|
||||||
point.Color = colorText
|
point.Color = colorText
|
||||||
} else if color, ok := StringToColor(colorText); ok {
|
} else if color, ok := StringToColor(colorText); ok {
|
||||||
point.Color = color
|
point.Color = color
|
||||||
|
|
@ -178,7 +178,7 @@ func (point *BackgroundGradientPoint) setValue(text string) bool {
|
||||||
|
|
||||||
if pointText == "" {
|
if pointText == "" {
|
||||||
point.Pos = nil
|
point.Pos = nil
|
||||||
} else if pointText[0] == '@' {
|
} else if ok, _ := isConstantName(pointText); ok {
|
||||||
point.Pos = pointText
|
point.Pos = pointText
|
||||||
} else if pos, ok := StringToSizeUnit(pointText); ok {
|
} else if pos, ok := StringToSizeUnit(pointText); ok {
|
||||||
point.Pos = pos
|
point.Pos = pos
|
||||||
|
|
@ -193,17 +193,10 @@ func (point *BackgroundGradientPoint) color(session Session) (Color, bool) {
|
||||||
if point.Color != nil {
|
if point.Color != nil {
|
||||||
switch color := point.Color.(type) {
|
switch color := point.Color.(type) {
|
||||||
case string:
|
case string:
|
||||||
if color != "" {
|
if ok, constName := isConstantName(color); ok {
|
||||||
if color[0] == '@' {
|
return session.Color(constName)
|
||||||
if clr, ok := session.Color(color[1:]); ok {
|
|
||||||
return clr, true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if clr, ok := StringToColor(color); ok {
|
|
||||||
return clr, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return StringToColor(color)
|
||||||
|
|
||||||
case Color:
|
case Color:
|
||||||
return color, true
|
return color, true
|
||||||
|
|
@ -256,8 +249,8 @@ func (gradient *backgroundGradient) writeGradient(session Session, buffer *strin
|
||||||
|
|
||||||
switch value := value.(type) {
|
switch value := value.(type) {
|
||||||
case string:
|
case string:
|
||||||
if value != "" && value[0] == '@' {
|
if ok, constName := isConstantName(value); ok {
|
||||||
if text, ok := session.Constant(value[1:]); ok {
|
if text, ok := session.Constant(constName); ok {
|
||||||
points = parseGradientText(text)
|
points = parseGradientText(text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ type backgroundRadialGradient struct {
|
||||||
//
|
//
|
||||||
// The following properties can be used:
|
// The following properties can be used:
|
||||||
// - "gradient" (Gradient) - Describes gradient stop points. This is a mandatory property while describing background gradients.
|
// - "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-radius" (RadialGradientRadius) - Defines radius of the radial gradient.
|
||||||
// - "radial-gradient-shape" (RadialGradientShape) - Defines shape 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.
|
// - "repeating" (Repeating) - Defines whether stop points needs to be repeated after the last one.
|
||||||
|
|
@ -279,7 +279,7 @@ func (gradient *backgroundRadialGradient) cssStyle(session Session) string {
|
||||||
}
|
}
|
||||||
buffer.WriteString("ellipse ")
|
buffer.WriteString("ellipse ")
|
||||||
shapeText = ""
|
shapeText = ""
|
||||||
for i := 0; i < count; i++ {
|
for i := range count {
|
||||||
buffer.WriteString(value[i].cssString("50%", session))
|
buffer.WriteString(value[i].cssString("50%", session))
|
||||||
buffer.WriteString(" ")
|
buffer.WriteString(" ")
|
||||||
}
|
}
|
||||||
|
|
@ -291,7 +291,7 @@ func (gradient *backgroundRadialGradient) cssStyle(session Session) string {
|
||||||
}
|
}
|
||||||
buffer.WriteString("ellipse ")
|
buffer.WriteString("ellipse ")
|
||||||
shapeText = ""
|
shapeText = ""
|
||||||
for i := 0; i < count; i++ {
|
for i := range count {
|
||||||
if value[i] != nil {
|
if value[i] != nil {
|
||||||
switch value := value[i].(type) {
|
switch value := value[i].(type) {
|
||||||
case SizeUnit:
|
case SizeUnit:
|
||||||
|
|
|
||||||
16
border.go
16
border.go
|
|
@ -383,14 +383,17 @@ func (border *borderProperty) writeString(buffer *strings.Builder, indent string
|
||||||
comma := false
|
comma := false
|
||||||
|
|
||||||
write := func(tag PropertyName, value any) {
|
write := func(tag PropertyName, value any) {
|
||||||
|
text := propertyValueToString(tag, value, indent)
|
||||||
|
if text != "" {
|
||||||
if comma {
|
if comma {
|
||||||
buffer.WriteString(", ")
|
buffer.WriteString(", ")
|
||||||
}
|
}
|
||||||
buffer.WriteString(string(tag))
|
buffer.WriteString(string(tag))
|
||||||
buffer.WriteString(" = ")
|
buffer.WriteString(" = ")
|
||||||
writePropertyValue(buffer, BorderStyle, value, indent)
|
buffer.WriteString(text)
|
||||||
comma = true
|
comma = true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, tag := range []PropertyName{Style, Width, ColorTag} {
|
for _, tag := range []PropertyName{Style, Width, ColorTag} {
|
||||||
if value, ok := border.properties[tag]; ok {
|
if value, ok := border.properties[tag]; ok {
|
||||||
|
|
@ -433,8 +436,7 @@ func (border *borderProperty) String() string {
|
||||||
|
|
||||||
func (border *borderProperty) setBorderObject(obj DataObject) bool {
|
func (border *borderProperty) setBorderObject(obj DataObject) bool {
|
||||||
result := true
|
result := true
|
||||||
for i := 0; i < obj.PropertyCount(); i++ {
|
for node := range obj.Properties() {
|
||||||
if node := obj.Property(i); node != nil {
|
|
||||||
tag := PropertyName(node.Tag())
|
tag := PropertyName(node.Tag())
|
||||||
switch node.Type() {
|
switch node.Type() {
|
||||||
case TextNode:
|
case TextNode:
|
||||||
|
|
@ -450,9 +452,6 @@ func (border *borderProperty) setBorderObject(obj DataObject) bool {
|
||||||
default:
|
default:
|
||||||
result = false
|
result = false
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
result = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
@ -610,7 +609,10 @@ func borderSet(properties Properties, tag PropertyName, value any) []PropertyNam
|
||||||
case Left, Right, Top, Bottom:
|
case Left, Right, Top, Bottom:
|
||||||
switch value := value.(type) {
|
switch value := value.(type) {
|
||||||
case string:
|
case string:
|
||||||
if obj := ParseDataText(value); obj != nil {
|
obj, err := ParseDataText(value)
|
||||||
|
if err != nil {
|
||||||
|
ErrorLog(err.Error())
|
||||||
|
} else {
|
||||||
return setSingleBorderObject(tag, obj)
|
return setSingleBorderObject(tag, obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
21
bounds.go
21
bounds.go
|
|
@ -84,23 +84,6 @@ func (bounds *boundsPropertyData) String() string {
|
||||||
return runStringWriter(bounds)
|
return runStringWriter(bounds)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bounds *boundsPropertyData) writeString(buffer *strings.Builder, indent string) {
|
|
||||||
buffer.WriteString("_{ ")
|
|
||||||
comma := false
|
|
||||||
for _, tag := range []PropertyName{Top, Right, Bottom, Left} {
|
|
||||||
if value, ok := bounds.properties[tag]; ok {
|
|
||||||
if comma {
|
|
||||||
buffer.WriteString(", ")
|
|
||||||
}
|
|
||||||
buffer.WriteString(string(tag))
|
|
||||||
buffer.WriteString(" = ")
|
|
||||||
writePropertyValue(buffer, tag, value, indent)
|
|
||||||
comma = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buffer.WriteString(" }")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bounds *boundsPropertyData) Bounds(session Session) Bounds {
|
func (bounds *boundsPropertyData) Bounds(session Session) Bounds {
|
||||||
top, _ := sizeProperty(bounds, Top, session)
|
top, _ := sizeProperty(bounds, Top, session)
|
||||||
right, _ := sizeProperty(bounds, Right, session)
|
right, _ := sizeProperty(bounds, Right, session)
|
||||||
|
|
@ -199,7 +182,7 @@ func setBoundsProperty(properties Properties, tag PropertyName, value any) []Pro
|
||||||
if !setSimpleProperty(properties, tag, value) {
|
if !setSimpleProperty(properties, tag, value) {
|
||||||
switch value := value.(type) {
|
switch value := value.(type) {
|
||||||
case string:
|
case string:
|
||||||
if strings.Contains(value, ",") {
|
if strings.ContainsRune(value, ',') {
|
||||||
values := split4Values(value)
|
values := split4Values(value)
|
||||||
count := len(values)
|
count := len(values)
|
||||||
switch count {
|
switch count {
|
||||||
|
|
@ -280,7 +263,7 @@ func removeBoundsPropertySide(properties Properties, mainTag, sideTag PropertyNa
|
||||||
if bounds := getBoundsProperty(properties, mainTag); bounds != nil {
|
if bounds := getBoundsProperty(properties, mainTag); bounds != nil {
|
||||||
if bounds.getRaw(sideTag) != nil {
|
if bounds.getRaw(sideTag) != nil {
|
||||||
bounds.Remove(sideTag)
|
bounds.Remove(sideTag)
|
||||||
if bounds.empty() {
|
if bounds.IsEmpty() {
|
||||||
bounds = nil
|
bounds = nil
|
||||||
}
|
}
|
||||||
properties.setRaw(mainTag, bounds)
|
properties.setRaw(mainTag, bounds)
|
||||||
|
|
|
||||||
78
button.go
78
button.go
|
|
@ -1,38 +1,86 @@
|
||||||
package rui
|
package rui
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
// Button represent a Button view
|
// Button represent a Button view
|
||||||
type Button interface {
|
type Button interface {
|
||||||
CustomView
|
ListLayout
|
||||||
}
|
}
|
||||||
|
|
||||||
type buttonData struct {
|
type buttonData struct {
|
||||||
CustomViewData
|
listLayoutData
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewButton create new Button object and return it
|
// NewButton create new Button object and return it
|
||||||
func NewButton(session Session, params Params) Button {
|
func NewButton(session Session, params Params) Button {
|
||||||
button := new(buttonData)
|
button := new(buttonData)
|
||||||
InitCustomView(button, "Button", session, params)
|
button.init(session)
|
||||||
|
setInitParams(button, params)
|
||||||
return button
|
return button
|
||||||
}
|
}
|
||||||
|
|
||||||
func newButton(session Session) View {
|
func newButton(session Session) View {
|
||||||
return NewButton(session, nil)
|
return new(buttonData)
|
||||||
//return new(buttonData)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (button *buttonData) CreateSuperView(session Session) View {
|
func (button *buttonData) init(session Session) {
|
||||||
return NewListLayout(session, Params{
|
button.listLayoutData.init(session)
|
||||||
Semantics: ButtonSemantics,
|
button.tag = "Button"
|
||||||
Style: "ruiButton",
|
button.systemClass = "ruiButton"
|
||||||
StyleDisabled: "ruiDisabledButton",
|
button.setRaw(Style, "ruiEnabledButton")
|
||||||
HorizontalAlign: CenterAlign,
|
button.setRaw(StyleDisabled, "ruiDisabledButton")
|
||||||
VerticalAlign: CenterAlign,
|
button.setRaw(Semantics, ButtonSemantics)
|
||||||
Orientation: StartToEndOrientation,
|
button.setRaw(TabIndex, 0)
|
||||||
TabIndex: 0,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (button *buttonData) Focusable() bool {
|
func (button *buttonData) Focusable() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (button *buttonData) htmlSubviews(self View, buffer *strings.Builder) {
|
||||||
|
if button.views != nil {
|
||||||
|
for _, view := range button.views {
|
||||||
|
view.addToCSSStyle(map[string]string{`flex`: `0 0 auto`})
|
||||||
|
viewHTML(view, buffer, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetButtonVerticalAlign returns the vertical align of a Button subview:
|
||||||
|
// TopAlign (0), BottomAlign (1), CenterAlign (2), or StretchAlign (3)
|
||||||
|
//
|
||||||
|
// 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 GetButtonVerticalAlign(view View, subviewID ...string) int {
|
||||||
|
return enumStyledProperty(view, subviewID, VerticalAlign, CenterAlign, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetButtonHorizontalAlign returns the vertical align of a Button subview:
|
||||||
|
// LeftAlign (0), RightAlign (1), CenterAlign (2), or StretchAlign (3)
|
||||||
|
//
|
||||||
|
// 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 GetButtonHorizontalAlign(view View, subviewID ...string) int {
|
||||||
|
return enumStyledProperty(view, subviewID, HorizontalAlign, CenterAlign, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetButtonOrientation returns the orientation of a Button subview:
|
||||||
|
// TopDownOrientation (0), StartToEndOrientation (1), BottomUpOrientation (2), or EndToStartOrientation (3)
|
||||||
|
//
|
||||||
|
// 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 GetButtonOrientation(view View, subviewID ...string) int {
|
||||||
|
if view = getSubview(view, subviewID); view != nil {
|
||||||
|
if orientation, ok := valueToOrientation(view.Get(Orientation), view.Session()); ok {
|
||||||
|
return orientation
|
||||||
|
}
|
||||||
|
|
||||||
|
if value := valueFromStyle(view, Orientation); value != nil {
|
||||||
|
if orientation, ok := valueToOrientation(value, view.Session()); ok {
|
||||||
|
return orientation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return StartToEndOrientation
|
||||||
|
}
|
||||||
|
|
|
||||||
64
canvas.go
64
canvas.go
|
|
@ -201,7 +201,7 @@ type Canvas interface {
|
||||||
|
|
||||||
// SetConicGradientFillStyle sets a conic gradient around a point
|
// SetConicGradientFillStyle sets a conic gradient around a point
|
||||||
// to use inside shapes
|
// 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.
|
// * 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;
|
// * startColor - the start color;
|
||||||
// * endColor - the end color;
|
// * endColor - the end color;
|
||||||
|
|
@ -210,7 +210,7 @@ type Canvas interface {
|
||||||
|
|
||||||
// SetConicGradientFillStyle sets a conic gradient around a point
|
// SetConicGradientFillStyle sets a conic gradient around a point
|
||||||
// to use inside shapes
|
// 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.
|
// * 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;
|
// * startColor - the start color;
|
||||||
// * endColor - the end color;
|
// * endColor - the end color;
|
||||||
|
|
@ -575,7 +575,7 @@ func (canvas *canvasData) SetLineDash(dash []float64, offset float64) {
|
||||||
for _, val := range dash {
|
for _, val := range dash {
|
||||||
buffer.WriteRune(lead)
|
buffer.WriteRune(lead)
|
||||||
lead = ','
|
lead = ','
|
||||||
buffer.WriteString(fmt.Sprintf("%g", val))
|
fmt.Fprintf(buffer, "%g", val))
|
||||||
}
|
}
|
||||||
buffer.WriteRune(']')
|
buffer.WriteRune(']')
|
||||||
|
|
||||||
|
|
@ -587,28 +587,6 @@ func (canvas *canvasData) SetLineDash(dash []float64, offset float64) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
func (canvas *canvasData) convertFont(name string) string {
|
|
||||||
buffer := allocStringBuilder()
|
|
||||||
defer freeStringBuilder(buffer)
|
|
||||||
|
|
||||||
for i, font := range strings.Split(name, ",") {
|
|
||||||
font = strings.Trim(font, " \n\"'")
|
|
||||||
if i > 0 {
|
|
||||||
buffer.WriteRune(',')
|
|
||||||
}
|
|
||||||
if strings.Contains(font, " ") {
|
|
||||||
buffer.WriteRune('"')
|
|
||||||
buffer.WriteString(font)
|
|
||||||
buffer.WriteRune('"')
|
|
||||||
} else {
|
|
||||||
buffer.WriteString(font)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer.String()
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
func (canvas *canvasData) fontWithParams(name string, size SizeUnit, params FontParams) string {
|
func (canvas *canvasData) fontWithParams(name string, size SizeUnit, params FontParams) string {
|
||||||
buffer := allocStringBuilder()
|
buffer := allocStringBuilder()
|
||||||
defer freeStringBuilder(buffer)
|
defer freeStringBuilder(buffer)
|
||||||
|
|
@ -658,7 +636,7 @@ func (canvas *canvasData) fontWithParams(name string, size SizeUnit, params Font
|
||||||
font = strings.Trim(font, " \n\"'")
|
font = strings.Trim(font, " \n\"'")
|
||||||
buffer.WriteString(lead)
|
buffer.WriteString(lead)
|
||||||
lead = ","
|
lead = ","
|
||||||
if strings.Contains(font, " ") {
|
if strings.ContainsRune(font, ' ') {
|
||||||
buffer.WriteRune('"')
|
buffer.WriteRune('"')
|
||||||
buffer.WriteString(font)
|
buffer.WriteString(font)
|
||||||
buffer.WriteRune('"')
|
buffer.WriteRune('"')
|
||||||
|
|
@ -815,40 +793,6 @@ func (canvas *canvasData) FillAndStrokeEllipse(x, y, radiusX, radiusY, rotation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
func (canvas *canvasData) writePointArgs(x, y float64) {
|
|
||||||
canvas.script.WriteString(strconv.FormatFloat(x, 'g', -1, 64))
|
|
||||||
canvas.script.WriteRune(',')
|
|
||||||
canvas.script.WriteString(strconv.FormatFloat(y, 'g', -1, 64))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (canvas *canvasData) writeStringArgs(text string, script *strings.Builder) {
|
|
||||||
//rText := []rune(text)
|
|
||||||
for _, ch := range text {
|
|
||||||
switch ch {
|
|
||||||
case '\t':
|
|
||||||
script.WriteString(`\t`)
|
|
||||||
case '\n':
|
|
||||||
script.WriteString(`\n`)
|
|
||||||
case '\r':
|
|
||||||
script.WriteString(`\r`)
|
|
||||||
case '\\':
|
|
||||||
script.WriteString(`\\`)
|
|
||||||
case '"':
|
|
||||||
script.WriteString(`\"`)
|
|
||||||
case '\'':
|
|
||||||
script.WriteString(`\'`)
|
|
||||||
default:
|
|
||||||
if ch < ' ' {
|
|
||||||
script.WriteString(fmt.Sprintf("\\x%02X", int(ch)))
|
|
||||||
} else {
|
|
||||||
script.WriteRune(ch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
func (canvas *canvasData) FillText(x, y float64, text string) {
|
func (canvas *canvasData) FillText(x, y float64, text string) {
|
||||||
canvas.session.callCanvasFunc("fillText", text, x, y)
|
canvas.session.callCanvasFunc("fillText", text, x, y)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
package rui
|
package rui
|
||||||
|
|
||||||
|
import "reflect"
|
||||||
|
|
||||||
// DrawFunction is the constant for "draw-function" property tag.
|
// DrawFunction is the constant for "draw-function" property tag.
|
||||||
//
|
//
|
||||||
// Used by `CanvasView`.
|
// Used by `CanvasView`.
|
||||||
|
|
@ -55,7 +57,7 @@ func (canvasView *canvasViewData) removeFunc(tag PropertyName) []PropertyName {
|
||||||
if tag == DrawFunction {
|
if tag == DrawFunction {
|
||||||
if canvasView.getRaw(DrawFunction) != nil {
|
if canvasView.getRaw(DrawFunction) != nil {
|
||||||
canvasView.setRaw(DrawFunction, nil)
|
canvasView.setRaw(DrawFunction, nil)
|
||||||
canvasView.Redraw()
|
//canvasView.Redraw()
|
||||||
return []PropertyName{DrawFunction}
|
return []PropertyName{DrawFunction}
|
||||||
}
|
}
|
||||||
return []PropertyName{}
|
return []PropertyName{}
|
||||||
|
|
@ -66,9 +68,14 @@ func (canvasView *canvasViewData) removeFunc(tag PropertyName) []PropertyName {
|
||||||
|
|
||||||
func (canvasView *canvasViewData) setFunc(tag PropertyName, value any) []PropertyName {
|
func (canvasView *canvasViewData) setFunc(tag PropertyName, value any) []PropertyName {
|
||||||
if tag == DrawFunction {
|
if tag == DrawFunction {
|
||||||
if fn, ok := value.(func(Canvas)); ok {
|
switch value := value.(type) {
|
||||||
canvasView.setRaw(DrawFunction, fn)
|
case func(Canvas):
|
||||||
} else {
|
canvasView.setRaw(DrawFunction, value)
|
||||||
|
|
||||||
|
case string:
|
||||||
|
canvasView.setRaw(DrawFunction, value)
|
||||||
|
|
||||||
|
default:
|
||||||
notCompatibleType(tag, value)
|
notCompatibleType(tag, value)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -94,8 +101,30 @@ func (canvasView *canvasViewData) Redraw() {
|
||||||
canvas := newCanvas(canvasView)
|
canvas := newCanvas(canvasView)
|
||||||
canvas.ClearRect(0, 0, canvasView.frame.Width, canvasView.frame.Height)
|
canvas.ClearRect(0, 0, canvasView.frame.Width, canvasView.frame.Height)
|
||||||
if value := canvasView.getRaw(DrawFunction); value != nil {
|
if value := canvasView.getRaw(DrawFunction); value != nil {
|
||||||
if drawer, ok := value.(func(Canvas)); ok {
|
switch drawer := value.(type) {
|
||||||
|
case func(Canvas):
|
||||||
drawer(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()
|
canvas.finishDraw()
|
||||||
|
|
|
||||||
71
checkbox.go
71
checkbox.go
|
|
@ -49,12 +49,13 @@ func (button *checkboxData) init(session Session) {
|
||||||
button.viewsContainerData.init(session)
|
button.viewsContainerData.init(session)
|
||||||
button.tag = "Checkbox"
|
button.tag = "Checkbox"
|
||||||
button.systemClass = "ruiGridLayout ruiCheckbox"
|
button.systemClass = "ruiGridLayout ruiCheckbox"
|
||||||
|
button.get = button.getFunc
|
||||||
button.set = button.setFunc
|
button.set = button.setFunc
|
||||||
button.remove = button.removeFunc
|
button.remove = button.removeFunc
|
||||||
button.changed = button.propertyChanged
|
button.changed = button.propertyChanged
|
||||||
|
|
||||||
button.setRaw(ClickEvent, []func(View, MouseEvent){checkboxClickListener})
|
button.setRaw(ClickEvent, []oneArgListener[View, MouseEvent]{newOneArgListenerVE(checkboxClickListener)})
|
||||||
button.setRaw(KeyDownEvent, []func(View, KeyEvent){checkboxKeyListener})
|
button.setRaw(KeyDownEvent, []oneArgListener[View, KeyEvent]{newOneArgListenerVE(checkboxKeyListener)})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (button *checkboxData) Focusable() bool {
|
func (button *checkboxData) Focusable() bool {
|
||||||
|
|
@ -67,9 +68,9 @@ func (button *checkboxData) propertyChanged(tag PropertyName) {
|
||||||
case Checked:
|
case Checked:
|
||||||
session := button.Session()
|
session := button.Session()
|
||||||
checked := IsCheckboxChecked(button)
|
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 {
|
for _, listener := range listeners {
|
||||||
listener(button, checked)
|
listener.Run(button, checked)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -99,11 +100,22 @@ func (button *checkboxData) propertyChanged(tag PropertyName) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (button *checkboxData) getFunc(tag PropertyName) any {
|
||||||
|
switch tag {
|
||||||
|
case CheckboxChangedEvent:
|
||||||
|
if listeners := getOneArgEventRawListeners[Checkbox, bool](button, nil, tag); len(listeners) > 0 {
|
||||||
|
return listeners
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return button.viewData.getFunc(tag)
|
||||||
|
}
|
||||||
|
|
||||||
func (button *checkboxData) setFunc(tag PropertyName, value any) []PropertyName {
|
func (button *checkboxData) setFunc(tag PropertyName, value any) []PropertyName {
|
||||||
switch tag {
|
switch tag {
|
||||||
case ClickEvent:
|
case ClickEvent:
|
||||||
if listeners, ok := valueToOneArgEventListeners[View, MouseEvent](value); ok && listeners != nil {
|
if listeners, ok := valueToOneArgEventListeners[View, MouseEvent](value); ok && listeners != nil {
|
||||||
listeners = append(listeners, checkboxClickListener)
|
listeners = append(listeners, newOneArgListenerVE(checkboxClickListener))
|
||||||
button.setRaw(tag, listeners)
|
button.setRaw(tag, listeners)
|
||||||
return []PropertyName{tag}
|
return []PropertyName{tag}
|
||||||
}
|
}
|
||||||
|
|
@ -111,7 +123,7 @@ func (button *checkboxData) setFunc(tag PropertyName, value any) []PropertyName
|
||||||
|
|
||||||
case KeyDownEvent:
|
case KeyDownEvent:
|
||||||
if listeners, ok := valueToOneArgEventListeners[View, KeyEvent](value); ok && listeners != nil {
|
if listeners, ok := valueToOneArgEventListeners[View, KeyEvent](value); ok && listeners != nil {
|
||||||
listeners = append(listeners, checkboxKeyListener)
|
listeners = append(listeners, newOneArgListenerVE(checkboxKeyListener))
|
||||||
button.setRaw(tag, listeners)
|
button.setRaw(tag, listeners)
|
||||||
return []PropertyName{tag}
|
return []PropertyName{tag}
|
||||||
}
|
}
|
||||||
|
|
@ -134,31 +146,17 @@ func (button *checkboxData) setFunc(tag PropertyName, value any) []PropertyName
|
||||||
func (button *checkboxData) removeFunc(tag PropertyName) []PropertyName {
|
func (button *checkboxData) removeFunc(tag PropertyName) []PropertyName {
|
||||||
switch tag {
|
switch tag {
|
||||||
case ClickEvent:
|
case ClickEvent:
|
||||||
button.setRaw(ClickEvent, []func(View, MouseEvent){checkboxClickListener})
|
button.setRaw(ClickEvent, []oneArgListener[View, MouseEvent]{newOneArgListenerVE(checkboxClickListener)})
|
||||||
return []PropertyName{ClickEvent}
|
return []PropertyName{ClickEvent}
|
||||||
|
|
||||||
case KeyDownEvent:
|
case KeyDownEvent:
|
||||||
button.setRaw(KeyDownEvent, []func(View, KeyEvent){checkboxKeyListener})
|
button.setRaw(KeyDownEvent, []oneArgListener[View, KeyEvent]{newOneArgListenerVE(checkboxKeyListener)})
|
||||||
return []PropertyName{ClickEvent}
|
return []PropertyName{ClickEvent}
|
||||||
}
|
}
|
||||||
|
|
||||||
return button.viewsContainerData.removeFunc(tag)
|
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) {
|
func checkboxClickListener(view View, _ MouseEvent) {
|
||||||
view.Set(Checked, !IsCheckboxChecked(view))
|
view.Set(Checked, !IsCheckboxChecked(view))
|
||||||
BlurView(view)
|
BlurView(view)
|
||||||
|
|
@ -302,26 +300,41 @@ func checkboxVerticalAlignCSS(view View) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsCheckboxChecked returns true if the Checkbox is checked, false otherwise.
|
// 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 {
|
func IsCheckboxChecked(view View, subviewID ...string) bool {
|
||||||
return boolStyledProperty(view, subviewID, Checked, false)
|
return boolStyledProperty(view, subviewID, Checked, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCheckboxVerticalAlign return the vertical align of a Checkbox subview: TopAlign (0), BottomAlign (1), CenterAlign (2)
|
// 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 {
|
func GetCheckboxVerticalAlign(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, CheckboxVerticalAlign, LeftAlign, false)
|
return enumStyledProperty(view, subviewID, CheckboxVerticalAlign, LeftAlign, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCheckboxHorizontalAlign return the vertical align of a Checkbox subview: LeftAlign (0), RightAlign (1), CenterAlign (2)
|
// 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 {
|
func GetCheckboxHorizontalAlign(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, CheckboxHorizontalAlign, TopAlign, false)
|
return enumStyledProperty(view, subviewID, CheckboxHorizontalAlign, TopAlign, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCheckboxChangedListeners returns the CheckboxChangedListener list of an Checkbox subview.
|
// GetCheckboxChangedListeners returns the CheckboxChangedListener list of an Checkbox subview.
|
||||||
// If there are no listeners then the empty list is returned
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[Checkbox, bool](view, subviewID, CheckboxChangedEvent)
|
// - 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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
32
clipShape.go
32
clipShape.go
|
|
@ -230,15 +230,18 @@ func (clip *insetClipData) writeString(buffer *strings.Builder, indent string) {
|
||||||
comma := false
|
comma := false
|
||||||
for _, tag := range []PropertyName{Top, Right, Bottom, Left, Radius} {
|
for _, tag := range []PropertyName{Top, Right, Bottom, Left, Radius} {
|
||||||
if value, ok := clip.properties[tag]; ok {
|
if value, ok := clip.properties[tag]; ok {
|
||||||
|
text := propertyValueToString(tag, value, indent)
|
||||||
|
if text != "" {
|
||||||
if comma {
|
if comma {
|
||||||
buffer.WriteString(", ")
|
buffer.WriteString(", ")
|
||||||
}
|
}
|
||||||
buffer.WriteString(string(tag))
|
buffer.WriteString(string(tag))
|
||||||
buffer.WriteString(" = ")
|
buffer.WriteString(" = ")
|
||||||
writePropertyValue(buffer, tag, value, indent)
|
buffer.WriteString(text)
|
||||||
comma = true
|
comma = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buffer.WriteString(" }")
|
buffer.WriteString(" }")
|
||||||
}
|
}
|
||||||
|
|
@ -303,15 +306,18 @@ func (clip *circleClipData) writeString(buffer *strings.Builder, indent string)
|
||||||
comma := false
|
comma := false
|
||||||
for _, tag := range []PropertyName{Radius, X, Y} {
|
for _, tag := range []PropertyName{Radius, X, Y} {
|
||||||
if value, ok := clip.properties[tag]; ok {
|
if value, ok := clip.properties[tag]; ok {
|
||||||
|
text := propertyValueToString(tag, value, indent)
|
||||||
|
if text != "" {
|
||||||
if comma {
|
if comma {
|
||||||
buffer.WriteString(", ")
|
buffer.WriteString(", ")
|
||||||
}
|
}
|
||||||
buffer.WriteString(string(tag))
|
buffer.WriteString(string(tag))
|
||||||
buffer.WriteString(" = ")
|
buffer.WriteString(" = ")
|
||||||
writePropertyValue(buffer, tag, value, indent)
|
buffer.WriteString(text)
|
||||||
comma = true
|
comma = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buffer.WriteString(" }")
|
buffer.WriteString(" }")
|
||||||
}
|
}
|
||||||
|
|
@ -380,15 +386,18 @@ func (clip *ellipseClipData) writeString(buffer *strings.Builder, indent string)
|
||||||
comma := false
|
comma := false
|
||||||
for _, tag := range []PropertyName{RadiusX, RadiusY, X, Y} {
|
for _, tag := range []PropertyName{RadiusX, RadiusY, X, Y} {
|
||||||
if value, ok := clip.properties[tag]; ok {
|
if value, ok := clip.properties[tag]; ok {
|
||||||
|
text := propertyValueToString(tag, value, indent)
|
||||||
|
if text != "" {
|
||||||
if comma {
|
if comma {
|
||||||
buffer.WriteString(", ")
|
buffer.WriteString(", ")
|
||||||
}
|
}
|
||||||
buffer.WriteString(string(tag))
|
buffer.WriteString(string(tag))
|
||||||
buffer.WriteString(" = ")
|
buffer.WriteString(" = ")
|
||||||
writePropertyValue(buffer, tag, value, indent)
|
buffer.WriteString(text)
|
||||||
comma = true
|
comma = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buffer.WriteString(" }")
|
buffer.WriteString(" }")
|
||||||
}
|
}
|
||||||
|
|
@ -441,7 +450,7 @@ func polygonClipDataSet(properties Properties, tag PropertyName, value any) []Pr
|
||||||
for i, val := range value {
|
for i, val := range value {
|
||||||
switch val := val.(type) {
|
switch val := val.(type) {
|
||||||
case string:
|
case string:
|
||||||
if isConstantName(val) {
|
if ok, _ := isConstantName(val); ok {
|
||||||
points[i] = val
|
points[i] = val
|
||||||
} else if size, ok := StringToSizeUnit(val); ok {
|
} else if size, ok := StringToSizeUnit(val); ok {
|
||||||
points[i] = size
|
points[i] = size
|
||||||
|
|
@ -475,7 +484,7 @@ func polygonClipDataSet(properties Properties, tag PropertyName, value any) []Pr
|
||||||
points := make([]any, len(values))
|
points := make([]any, len(values))
|
||||||
for i, val := range values {
|
for i, val := range values {
|
||||||
val = strings.Trim(val, " \t\n\r")
|
val = strings.Trim(val, " \t\n\r")
|
||||||
if isConstantName(val) {
|
if ok, _ := isConstantName(val); ok {
|
||||||
points[i] = val
|
points[i] = val
|
||||||
} else if size, ok := StringToSizeUnit(val); ok {
|
} else if size, ok := StringToSizeUnit(val); ok {
|
||||||
points[i] = size
|
points[i] = size
|
||||||
|
|
@ -511,11 +520,16 @@ func (clip *polygonClipData) writeString(buffer *strings.Builder, indent string)
|
||||||
if points := clip.points(); points != nil {
|
if points := clip.points(); points != nil {
|
||||||
buffer.WriteString(string(Points))
|
buffer.WriteString(string(Points))
|
||||||
buffer.WriteString(` = "`)
|
buffer.WriteString(` = "`)
|
||||||
for i, value := range points {
|
comma := false
|
||||||
if i > 0 {
|
for _, value := range points {
|
||||||
|
text := propertyValueToString("", value, indent)
|
||||||
|
if text != "" {
|
||||||
|
if comma {
|
||||||
buffer.WriteString(", ")
|
buffer.WriteString(", ")
|
||||||
}
|
}
|
||||||
writePropertyValue(buffer, "", value, indent)
|
buffer.WriteString(text)
|
||||||
|
comma = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.WriteString(`" `)
|
buffer.WriteString(`" `)
|
||||||
|
|
@ -620,7 +634,7 @@ func setClipShapePropertyProperty(properties Properties, tag PropertyName, value
|
||||||
return []PropertyName{tag}
|
return []PropertyName{tag}
|
||||||
|
|
||||||
case string:
|
case string:
|
||||||
if isConstantName(value) {
|
if ok, _ := isConstantName(value); ok {
|
||||||
properties.setRaw(tag, value)
|
properties.setRaw(tag, value)
|
||||||
return []PropertyName{tag}
|
return []PropertyName{tag}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ func (picker *colorPickerData) init(session Session) {
|
||||||
picker.hasHtmlDisabled = true
|
picker.hasHtmlDisabled = true
|
||||||
picker.properties[Padding] = Px(0)
|
picker.properties[Padding] = Px(0)
|
||||||
picker.normalize = normalizeColorPickerTag
|
picker.normalize = normalizeColorPickerTag
|
||||||
|
picker.get = picker.getFunc
|
||||||
picker.set = picker.setFunc
|
picker.set = picker.setFunc
|
||||||
picker.changed = picker.propertyChanged
|
picker.changed = picker.propertyChanged
|
||||||
}
|
}
|
||||||
|
|
@ -80,6 +81,17 @@ func normalizeColorPickerTag(tag PropertyName) PropertyName {
|
||||||
return normalizeDataListTag(tag)
|
return normalizeDataListTag(tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (picker *colorPickerData) getFunc(tag PropertyName) any {
|
||||||
|
switch tag {
|
||||||
|
case ColorChangedEvent:
|
||||||
|
if listeners := getTwoArgEventRawListeners[ColorPicker, Color](picker, nil, tag); len(listeners) > 0 {
|
||||||
|
return listeners
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return picker.viewData.getFunc(tag)
|
||||||
|
}
|
||||||
|
|
||||||
func (picker *colorPickerData) setFunc(tag PropertyName, value any) []PropertyName {
|
func (picker *colorPickerData) setFunc(tag PropertyName, value any) []PropertyName {
|
||||||
switch tag {
|
switch tag {
|
||||||
case ColorChangedEvent:
|
case ColorChangedEvent:
|
||||||
|
|
@ -106,13 +118,13 @@ func (picker *colorPickerData) propertyChanged(tag PropertyName) {
|
||||||
color := GetColorPickerValue(picker)
|
color := GetColorPickerValue(picker)
|
||||||
picker.Session().callFunc("setInputValue", picker.htmlID(), color.rgbString())
|
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)
|
oldColor := Color(0)
|
||||||
if value := picker.getRaw("old-color"); value != nil {
|
if value := picker.getRaw("old-color"); value != nil {
|
||||||
oldColor = value.(Color)
|
oldColor = value.(Color)
|
||||||
}
|
}
|
||||||
for _, listener := range listeners {
|
for _, listener := range listeners {
|
||||||
listener(picker, color, oldColor)
|
listener.Run(picker, color, oldColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -156,12 +168,10 @@ func (picker *colorPickerData) handleCommand(self View, command PropertyName, da
|
||||||
oldColor := GetColorPickerValue(picker)
|
oldColor := GetColorPickerValue(picker)
|
||||||
picker.properties[ColorPickerValue] = color
|
picker.properties[ColorPickerValue] = color
|
||||||
if color != oldColor {
|
if color != oldColor {
|
||||||
for _, listener := range GetColorChangedListeners(picker) {
|
for _, listener := range getTwoArgEventListeners[ColorPicker, Color](picker, nil, ColorChangedEvent) {
|
||||||
listener(picker, color, oldColor)
|
listener.Run(picker, color, oldColor)
|
||||||
}
|
|
||||||
if listener, ok := picker.changeListener[ColorPickerValue]; ok {
|
|
||||||
listener(picker, ColorPickerValue)
|
|
||||||
}
|
}
|
||||||
|
picker.runChangeListener(ColorPickerValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -172,7 +182,9 @@ func (picker *colorPickerData) handleCommand(self View, command PropertyName, da
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetColorPickerValue returns the value of ColorPicker subview.
|
// 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 {
|
func GetColorPickerValue(view View, subviewID ...string) Color {
|
||||||
if view = getSubview(view, subviewID); view != nil {
|
if view = getSubview(view, subviewID); view != nil {
|
||||||
if value, ok := colorProperty(view, ColorPickerValue, view.Session()); ok {
|
if value, ok := colorProperty(view, ColorPickerValue, view.Session()); ok {
|
||||||
|
|
@ -191,7 +203,18 @@ func GetColorPickerValue(view View, subviewID ...string) Color {
|
||||||
|
|
||||||
// GetColorChangedListeners returns the ColorChangedListener list of an ColorPicker subview.
|
// GetColorChangedListeners returns the ColorChangedListener list of an ColorPicker subview.
|
||||||
// If there are no listeners then the empty list is returned
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getTwoArgEventListeners[ColorPicker, Color](view, subviewID, ColorChangedEvent)
|
// - 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.
|
// GetColumnFill returns a "column-fill" property value of the subview.
|
||||||
// Returns one of next values: ColumnFillBalance (0) or ColumnFillAuto (1)
|
// 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 {
|
func GetColumnFill(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, ColumnFill, ColumnFillBalance, true)
|
return enumStyledProperty(view, subviewID, ColumnFill, ColumnFillBalance, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsColumnSpanAll returns a "column-span-all" property value of the subview.
|
// 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 {
|
func IsColumnSpanAll(view View, subviewID ...string) bool {
|
||||||
return boolStyledProperty(view, subviewID, ColumnSpanAll, false)
|
return boolStyledProperty(view, subviewID, ColumnSpanAll, false)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ package rui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ColumnSeparatorProperty is the interface of a view separator data
|
// ColumnSeparatorProperty is the interface of a view separator data
|
||||||
|
|
@ -93,12 +92,12 @@ func NewColumnSeparator(style int, color Color, width SizeUnit) ColumnSeparatorP
|
||||||
|
|
||||||
func (separator *columnSeparatorProperty) init() {
|
func (separator *columnSeparatorProperty) init() {
|
||||||
separator.dataProperty.init()
|
separator.dataProperty.init()
|
||||||
separator.normalize = normalizeVolumnSeparatorTag
|
separator.normalize = normalizeColumnSeparatorTag
|
||||||
separator.set = columnSeparatorSet
|
separator.set = columnSeparatorSet
|
||||||
separator.supportedProperties = []PropertyName{Style, Width, ColorTag}
|
separator.supportedProperties = []PropertyName{Style, Width, ColorTag}
|
||||||
}
|
}
|
||||||
|
|
||||||
func normalizeVolumnSeparatorTag(tag PropertyName) PropertyName {
|
func normalizeColumnSeparatorTag(tag PropertyName) PropertyName {
|
||||||
tag = defaultNormalize(tag)
|
tag = defaultNormalize(tag)
|
||||||
switch tag {
|
switch tag {
|
||||||
case ColumnSeparatorStyle, "separator-style":
|
case ColumnSeparatorStyle, "separator-style":
|
||||||
|
|
@ -114,24 +113,6 @@ func normalizeVolumnSeparatorTag(tag PropertyName) PropertyName {
|
||||||
return tag
|
return tag
|
||||||
}
|
}
|
||||||
|
|
||||||
func (separator *columnSeparatorProperty) writeString(buffer *strings.Builder, indent string) {
|
|
||||||
buffer.WriteString("_{ ")
|
|
||||||
comma := false
|
|
||||||
for _, tag := range []PropertyName{Style, Width, ColorTag} {
|
|
||||||
if value, ok := separator.properties[tag]; ok {
|
|
||||||
if comma {
|
|
||||||
buffer.WriteString(", ")
|
|
||||||
}
|
|
||||||
buffer.WriteString(string(tag))
|
|
||||||
buffer.WriteString(" = ")
|
|
||||||
writePropertyValue(buffer, BorderStyle, value, indent)
|
|
||||||
comma = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer.WriteString(" }")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (separator *columnSeparatorProperty) String() string {
|
func (separator *columnSeparatorProperty) String() string {
|
||||||
return runStringWriter(separator)
|
return runStringWriter(separator)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
package rui
|
package rui
|
||||||
|
|
||||||
import "strings"
|
import (
|
||||||
|
"iter"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
// CustomView defines a custom view interface
|
// CustomView defines a custom view interface
|
||||||
type CustomView interface {
|
type CustomView interface {
|
||||||
|
|
@ -47,8 +50,8 @@ func (customView *CustomViewData) SuperView() View {
|
||||||
func (customView *CustomViewData) setSuperView(view View) {
|
func (customView *CustomViewData) setSuperView(view View) {
|
||||||
customView.superView = view
|
customView.superView = view
|
||||||
customView.defaultParams = Params{}
|
customView.defaultParams = Params{}
|
||||||
for _, tag := range view.AllTags() {
|
for tag, value := range view.All() {
|
||||||
if value := view.getRaw(tag); value != nil {
|
if value != nil {
|
||||||
customView.defaultParams[tag] = value
|
customView.defaultParams[tag] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -98,8 +101,8 @@ func (customView *CustomViewData) SetParams(params Params) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetChangeListener set the function to track the change of the View property
|
// SetChangeListener set the function to track the change of the View property
|
||||||
func (customView *CustomViewData) SetChangeListener(tag PropertyName, listener func(View, PropertyName)) {
|
func (customView *CustomViewData) SetChangeListener(tag PropertyName, listener any) bool {
|
||||||
customView.superView.SetChangeListener(tag, listener)
|
return customView.superView.SetChangeListener(tag, listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove removes the property with name defined by the argument
|
// Remove removes the property with name defined by the argument
|
||||||
|
|
@ -107,13 +110,17 @@ func (customView *CustomViewData) Remove(tag PropertyName) {
|
||||||
customView.superView.Remove(tag)
|
customView.superView.Remove(tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AllTags returns an array of the set properties
|
|
||||||
func (customView *CustomViewData) AllTags() []PropertyName {
|
func (customView *CustomViewData) AllTags() []PropertyName {
|
||||||
return customView.superView.AllTags()
|
return customView.superView.AllTags()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (customView *CustomViewData) empty() bool {
|
// AllTags returns an array of the set properties
|
||||||
return customView.superView.empty()
|
func (customView *CustomViewData) All() iter.Seq2[PropertyName, any] {
|
||||||
|
return customView.superView.All()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (customView *CustomViewData) IsEmpty() bool {
|
||||||
|
return customView.superView.IsEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear removes all properties
|
// Clear removes all properties
|
||||||
|
|
@ -121,10 +128,6 @@ func (customView *CustomViewData) Clear() {
|
||||||
customView.superView.Clear()
|
customView.superView.Clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (customView *CustomViewData) cssViewStyle(buffer cssBuilder, session Session) {
|
|
||||||
customView.superView.cssViewStyle(buffer, session)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Session returns a current Session interface
|
// Session returns a current Session interface
|
||||||
func (customView *CustomViewData) Session() Session {
|
func (customView *CustomViewData) Session() Session {
|
||||||
return customView.superView.Session()
|
return customView.superView.Session()
|
||||||
|
|
@ -282,6 +285,15 @@ func (customView *CustomViewData) RemoveView(index int) View {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (customView *CustomViewData) RemoveViewByID(id string) View {
|
||||||
|
if customView.superView != nil {
|
||||||
|
if container, ok := customView.superView.(ViewsContainer); ok {
|
||||||
|
return container.RemoveViewByID(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Remove removes a view from the list of a view children and return it
|
// Remove removes a view from the list of a view children and return it
|
||||||
func (customView *CustomViewData) ViewIndex(view View) int {
|
func (customView *CustomViewData) ViewIndex(view View) int {
|
||||||
if customView.superView != nil {
|
if customView.superView != nil {
|
||||||
|
|
@ -292,15 +304,15 @@ func (customView *CustomViewData) ViewIndex(view View) int {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (customView *CustomViewData) exscludeTags() []PropertyName {
|
func (customView *CustomViewData) excludeTags() []PropertyName {
|
||||||
if customView.superView != nil {
|
if customView.superView != nil {
|
||||||
exsclude := []PropertyName{}
|
exclude := []PropertyName{}
|
||||||
for tag, value := range customView.defaultParams {
|
for tag, value := range customView.defaultParams {
|
||||||
if value == customView.superView.getRaw(tag) {
|
if value == customView.superView.getRaw(tag) {
|
||||||
exsclude = append(exsclude, tag)
|
exclude = append(exclude, tag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return exsclude
|
return exclude
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -310,7 +322,7 @@ func (customView *CustomViewData) String() string {
|
||||||
if customView.superView != nil {
|
if customView.superView != nil {
|
||||||
buffer := allocStringBuilder()
|
buffer := allocStringBuilder()
|
||||||
defer freeStringBuilder(buffer)
|
defer freeStringBuilder(buffer)
|
||||||
writeViewStyle(customView.tag, customView, buffer, "", customView.exscludeTags())
|
writeViewStyle(customView.tag, customView, buffer, "", customView.excludeTags())
|
||||||
return buffer.String()
|
return buffer.String()
|
||||||
}
|
}
|
||||||
return customView.tag + " { }"
|
return customView.tag + " { }"
|
||||||
|
|
@ -346,3 +358,16 @@ func (customView *CustomViewData) SetTransition(tag PropertyName, animation Anim
|
||||||
customView.superView.SetTransition(tag, animation)
|
customView.superView.SetTransition(tag, animation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (customView *CustomViewData) LoadFile(file FileInfo, result func(FileInfo, []byte)) {
|
||||||
|
if customView.superView != nil {
|
||||||
|
customView.superView.LoadFile(file, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (customView *CustomViewData) binding() any {
|
||||||
|
if customView.superView != nil {
|
||||||
|
return customView.superView.binding()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
449
data.go
449
data.go
|
|
@ -1,6 +1,10 @@
|
||||||
package rui
|
package rui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"iter"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
)
|
)
|
||||||
|
|
@ -24,6 +28,9 @@ type DataObject interface {
|
||||||
// Tag returns data object tag
|
// Tag returns data object tag
|
||||||
Tag() string
|
Tag() string
|
||||||
|
|
||||||
|
// Properties() returns an iterator to access the properties
|
||||||
|
Properties() iter.Seq[DataNode]
|
||||||
|
|
||||||
// PropertyCount returns properties count
|
// PropertyCount returns properties count
|
||||||
PropertyCount() int
|
PropertyCount() int
|
||||||
|
|
||||||
|
|
@ -47,16 +54,22 @@ type DataObject interface {
|
||||||
|
|
||||||
// ToParams create a params(map) representation of a data object
|
// ToParams create a params(map) representation of a data object
|
||||||
ToParams() Params
|
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]
|
// Constants which are used to describe a node type, see [DataNode]
|
||||||
const (
|
const (
|
||||||
// TextNode - node is the pair "tag - text value". Syntax: <tag> = <text>
|
// 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 - 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 - node is the pair "tag - object". Syntax: <tag> = [...]
|
||||||
ArrayNode = 2
|
ArrayNode DataNodeType = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
// DataNode interface of a data node
|
// DataNode interface of a data node
|
||||||
|
|
@ -65,7 +78,7 @@ type DataNode interface {
|
||||||
Tag() string
|
Tag() string
|
||||||
|
|
||||||
// Type returns a node type. Possible values are TextNode, ObjectNode and ArrayNode
|
// Type returns a node type. Possible values are TextNode, ObjectNode and ArrayNode
|
||||||
Type() int
|
Type() DataNodeType
|
||||||
|
|
||||||
// Text returns node text
|
// Text returns node text
|
||||||
Text() string
|
Text() string
|
||||||
|
|
@ -80,7 +93,10 @@ type DataNode interface {
|
||||||
ArrayElement(index int) DataValue
|
ArrayElement(index int) DataValue
|
||||||
|
|
||||||
// ArrayElements returns an array of objects if that node is an array
|
// ArrayElements returns an array of objects if that node is an array
|
||||||
ArrayElements() []DataValue
|
Array() []DataValue
|
||||||
|
|
||||||
|
// ArrayElements returns an iterator to access the array elements of objects if that node is an array
|
||||||
|
ArrayElements() iter.Seq[DataValue]
|
||||||
|
|
||||||
// ArrayAsParams returns an array of a params(map) if that node is an array
|
// ArrayAsParams returns an array of a params(map) if that node is an array
|
||||||
ArrayAsParams() []Params
|
ArrayAsParams() []Params
|
||||||
|
|
@ -133,6 +149,16 @@ func (object *dataObject) Tag() string {
|
||||||
return object.tag
|
return object.tag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (object *dataObject) Properties() iter.Seq[DataNode] {
|
||||||
|
return func(yield func(DataNode) bool) {
|
||||||
|
for _, node := range object.property {
|
||||||
|
if !yield(node) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (object *dataObject) PropertyCount() int {
|
func (object *dataObject) PropertyCount() int {
|
||||||
if object.property != nil {
|
if object.property != nil {
|
||||||
return len(object.property)
|
return len(object.property)
|
||||||
|
|
@ -158,6 +184,28 @@ func (object *dataObject) PropertyByTag(tag string) DataNode {
|
||||||
return nil
|
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) {
|
func (object *dataObject) PropertyValue(tag string) (string, bool) {
|
||||||
if node := object.PropertyByTag(tag); node != nil && node.Type() == TextNode {
|
if node := object.PropertyByTag(tag); node != nil && node.Type() == TextNode {
|
||||||
return node.Text(), true
|
return node.Text(), true
|
||||||
|
|
@ -200,10 +248,14 @@ func (object *dataObject) SetPropertyValue(tag, value string) {
|
||||||
|
|
||||||
// SetPropertyObject - set a property with tag by object
|
// SetPropertyObject - set a property with tag by object
|
||||||
func (object *dataObject) SetPropertyObject(tag string, obj DataObject) {
|
func (object *dataObject) SetPropertyObject(tag string, obj DataObject) {
|
||||||
|
if obj != nil {
|
||||||
node := new(dataNode)
|
node := new(dataNode)
|
||||||
node.tag = tag
|
node.tag = tag
|
||||||
node.value = obj
|
node.value = obj
|
||||||
object.setNode(node)
|
object.setNode(node)
|
||||||
|
} else {
|
||||||
|
object.RemovePropertyByTag(tag)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (object *dataObject) ToParams() Params {
|
func (object *dataObject) ToParams() Params {
|
||||||
|
|
@ -222,7 +274,7 @@ func (object *dataObject) ToParams() Params {
|
||||||
|
|
||||||
case ArrayNode:
|
case ArrayNode:
|
||||||
array := []any{}
|
array := []any{}
|
||||||
for i := 0; i < node.ArraySize(); i++ {
|
for i := range node.ArraySize() {
|
||||||
if data := node.ArrayElement(i); data != nil {
|
if data := node.ArrayElement(i); data != nil {
|
||||||
if data.IsObject() {
|
if data.IsObject() {
|
||||||
if obj := data.Object(); obj != nil {
|
if obj := data.Object(); obj != nil {
|
||||||
|
|
@ -253,7 +305,7 @@ func (node *dataNode) Tag() string {
|
||||||
return node.tag
|
return node.tag
|
||||||
}
|
}
|
||||||
|
|
||||||
func (node *dataNode) Type() int {
|
func (node *dataNode) Type() DataNodeType {
|
||||||
if node.array != nil {
|
if node.array != nil {
|
||||||
return ArrayNode
|
return ArrayNode
|
||||||
}
|
}
|
||||||
|
|
@ -291,11 +343,25 @@ func (node *dataNode) ArrayElement(index int) DataValue {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (node *dataNode) ArrayElements() []DataValue {
|
func (node *dataNode) Array() []DataValue {
|
||||||
if node.array != nil {
|
if node.array != nil {
|
||||||
return node.array
|
return slices.Clone(node.array)
|
||||||
|
}
|
||||||
|
return []DataValue{node.value}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (node *dataNode) ArrayElements() iter.Seq[DataValue] {
|
||||||
|
return func(yield func(DataValue) bool) {
|
||||||
|
if node.array != nil {
|
||||||
|
for _, element := range node.array {
|
||||||
|
if !yield(element) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
yield(node.value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return []DataValue{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (node *dataNode) ArrayAsParams() []Params {
|
func (node *dataNode) ArrayAsParams() []Params {
|
||||||
|
|
@ -314,54 +380,49 @@ func (node *dataNode) ArrayAsParams() []Params {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseDataText - parse text and return DataNode
|
type dataParser struct {
|
||||||
func ParseDataText(text string) DataObject {
|
data []rune
|
||||||
|
size int
|
||||||
if strings.ContainsAny(text, "\r") {
|
pos int
|
||||||
text = strings.Replace(text, "\r\n", "\n", -1)
|
line int
|
||||||
text = strings.Replace(text, "\r", "\n", -1)
|
lineStart int
|
||||||
}
|
}
|
||||||
data := append([]rune(text), rune(0))
|
|
||||||
pos := 0
|
|
||||||
size := len(data) - 1
|
|
||||||
line := 1
|
|
||||||
lineStart := 0
|
|
||||||
|
|
||||||
skipSpaces := func(skipNewLine bool) {
|
func (parser *dataParser) skipSpaces(skipNewLine bool) {
|
||||||
for pos < size {
|
for parser.pos < parser.size {
|
||||||
switch data[pos] {
|
switch parser.data[parser.pos] {
|
||||||
case '\n':
|
case '\n':
|
||||||
if !skipNewLine {
|
if !skipNewLine {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
line++
|
parser.line++
|
||||||
lineStart = pos + 1
|
parser.lineStart = parser.pos + 1
|
||||||
|
|
||||||
case '/':
|
case '/':
|
||||||
if pos+1 < size {
|
if parser.pos+1 < parser.size {
|
||||||
switch data[pos+1] {
|
switch parser.data[parser.pos+1] {
|
||||||
case '/':
|
case '/':
|
||||||
pos += 2
|
parser.pos += 2
|
||||||
for pos < size && data[pos] != '\n' {
|
for parser.pos < parser.size && parser.data[parser.pos] != '\n' {
|
||||||
pos++
|
parser.pos++
|
||||||
}
|
}
|
||||||
pos--
|
parser.pos--
|
||||||
|
|
||||||
case '*':
|
case '*':
|
||||||
pos += 3
|
parser.pos += 3
|
||||||
for {
|
for {
|
||||||
if pos >= size {
|
if parser.pos >= parser.size {
|
||||||
ErrorLog("Unexpected end of file")
|
ErrorLog("Unexpected end of file")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if data[pos-1] == '*' && data[pos] == '/' {
|
if parser.data[parser.pos-1] == '*' && parser.data[parser.pos] == '/' {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if data[pos-1] == '\n' {
|
if parser.data[parser.pos-1] == '\n' {
|
||||||
line++
|
parser.line++
|
||||||
lineStart = pos
|
parser.lineStart = parser.pos
|
||||||
}
|
}
|
||||||
pos++
|
parser.pos++
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
@ -373,75 +434,72 @@ func ParseDataText(text string) DataObject {
|
||||||
// do nothing
|
// do nothing
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if !unicode.IsSpace(data[pos]) {
|
if !unicode.IsSpace(parser.data[parser.pos]) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pos++
|
parser.pos++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parseTag := func() (string, bool) {
|
func (parser *dataParser) parseTag() (string, error) {
|
||||||
skipSpaces(true)
|
parser.skipSpaces(true)
|
||||||
startPos := pos
|
startPos := parser.pos
|
||||||
if data[pos] == '`' {
|
switch parser.data[parser.pos] {
|
||||||
pos++
|
case '`':
|
||||||
|
parser.pos++
|
||||||
startPos++
|
startPos++
|
||||||
for data[pos] != '`' {
|
for parser.data[parser.pos] != '`' {
|
||||||
pos++
|
parser.pos++
|
||||||
if pos >= size {
|
if parser.pos >= parser.size {
|
||||||
ErrorLog("Unexpected end of text")
|
return string(parser.data[startPos:parser.size]), errors.New("unexpected end of text")
|
||||||
return string(data[startPos:size]), false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
str := string(data[startPos:pos])
|
str := string(parser.data[startPos:parser.pos])
|
||||||
pos++
|
parser.pos++
|
||||||
return str, true
|
return str, nil
|
||||||
|
|
||||||
} else if data[pos] == '\'' || data[pos] == '"' {
|
case '\'', '"':
|
||||||
|
stopSymbol := parser.data[parser.pos]
|
||||||
stopSymbol := data[pos]
|
parser.pos++
|
||||||
pos++
|
|
||||||
startPos++
|
startPos++
|
||||||
slash := false
|
slash := false
|
||||||
for stopSymbol != data[pos] {
|
for stopSymbol != parser.data[parser.pos] {
|
||||||
if data[pos] == '\\' {
|
if parser.data[parser.pos] == '\\' {
|
||||||
pos += 2
|
parser.pos += 2
|
||||||
slash = true
|
slash = true
|
||||||
} else {
|
} else {
|
||||||
pos++
|
parser.pos++
|
||||||
}
|
}
|
||||||
if pos >= size {
|
if parser.pos >= parser.size {
|
||||||
ErrorLog("Unexpected end of text")
|
return string(parser.data[startPos:parser.size]), errors.New("unexpected end of text")
|
||||||
return string(data[startPos:size]), false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !slash {
|
if !slash {
|
||||||
str := string(data[startPos:pos])
|
str := string(parser.data[startPos:parser.pos])
|
||||||
pos++
|
parser.pos++
|
||||||
skipSpaces(false)
|
parser.skipSpaces(false)
|
||||||
return str, true
|
return str, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer := make([]rune, pos-startPos+1)
|
buffer := make([]rune, parser.pos-startPos+1)
|
||||||
n1 := 0
|
n1 := 0
|
||||||
n2 := startPos
|
n2 := startPos
|
||||||
|
|
||||||
invalidEscape := func() (string, bool) {
|
invalidEscape := func() (string, error) {
|
||||||
str := string(data[startPos:pos])
|
str := string(parser.data[startPos:parser.pos])
|
||||||
pos++
|
parser.pos++
|
||||||
ErrorLogF("Invalid escape sequence in \"%s\" (position %d)", str, n2-2-startPos)
|
return str, fmt.Errorf(`invalid escape sequence in "%s" (position %d)`, str, n2-2-startPos)
|
||||||
return str, false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for n2 < pos {
|
for n2 < parser.pos {
|
||||||
if data[n2] != '\\' {
|
if parser.data[n2] != '\\' {
|
||||||
buffer[n1] = data[n2]
|
buffer[n1] = parser.data[n2]
|
||||||
n2++
|
n2++
|
||||||
} else {
|
} else {
|
||||||
n2 += 2
|
n2 += 2
|
||||||
switch data[n2-1] {
|
switch parser.data[n2-1] {
|
||||||
case 'n':
|
case 'n':
|
||||||
buffer[n1] = '\n'
|
buffer[n1] = '\n'
|
||||||
|
|
||||||
|
|
@ -461,12 +519,12 @@ func ParseDataText(text string) DataObject {
|
||||||
buffer[n1] = '\\'
|
buffer[n1] = '\\'
|
||||||
|
|
||||||
case 'x', 'X':
|
case 'x', 'X':
|
||||||
if n2+2 > pos {
|
if n2+2 > parser.pos {
|
||||||
return invalidEscape()
|
return invalidEscape()
|
||||||
}
|
}
|
||||||
x := 0
|
x := 0
|
||||||
for i := 0; i < 2; i++ {
|
for range 2 {
|
||||||
ch := data[n2]
|
ch := parser.data[n2]
|
||||||
if ch >= '0' && ch <= '9' {
|
if ch >= '0' && ch <= '9' {
|
||||||
x = x*16 + int(ch-'0')
|
x = x*16 + int(ch-'0')
|
||||||
} else if ch >= 'a' && ch <= 'f' {
|
} else if ch >= 'a' && ch <= 'f' {
|
||||||
|
|
@ -481,12 +539,12 @@ func ParseDataText(text string) DataObject {
|
||||||
buffer[n1] = rune(x)
|
buffer[n1] = rune(x)
|
||||||
|
|
||||||
case 'u', 'U':
|
case 'u', 'U':
|
||||||
if n2+4 > pos {
|
if n2+4 > parser.pos {
|
||||||
return invalidEscape()
|
return invalidEscape()
|
||||||
}
|
}
|
||||||
x := 0
|
x := 0
|
||||||
for i := 0; i < 4; i++ {
|
for range 4 {
|
||||||
ch := data[n2]
|
ch := parser.data[n2]
|
||||||
if ch >= '0' && ch <= '9' {
|
if ch >= '0' && ch <= '9' {
|
||||||
x = x*16 + int(ch-'0')
|
x = x*16 + int(ch-'0')
|
||||||
} else if ch >= 'a' && ch <= 'f' {
|
} else if ch >= 'a' && ch <= 'f' {
|
||||||
|
|
@ -501,97 +559,84 @@ func ParseDataText(text string) DataObject {
|
||||||
buffer[n1] = rune(x)
|
buffer[n1] = rune(x)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
str := string(data[startPos:pos])
|
str := string(parser.data[startPos:parser.pos])
|
||||||
ErrorLogF("Invalid escape sequence in \"%s\" (position %d)", str, n2-2-startPos)
|
return str, fmt.Errorf(`invalid escape sequence in "%s" (position %d)`, str, n2-2-startPos)
|
||||||
return str, false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
n1++
|
n1++
|
||||||
}
|
}
|
||||||
|
|
||||||
pos++
|
parser.pos++
|
||||||
skipSpaces(false)
|
parser.skipSpaces(false)
|
||||||
return string(buffer[0:n1]), true
|
return string(buffer[0:n1]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
stopSymbol := func(symbol rune) bool {
|
for parser.pos < parser.size && !parser.stopSymbol(parser.data[parser.pos]) {
|
||||||
if unicode.IsSpace(symbol) {
|
parser.pos++
|
||||||
return true
|
|
||||||
}
|
|
||||||
for _, sym := range []rune{'=', '{', '}', '[', ']', ',', ' ', '\t', '\n', '\'', '"', '`', '/'} {
|
|
||||||
if sym == symbol {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for pos < size && !stopSymbol(data[pos]) {
|
endPos := parser.pos
|
||||||
pos++
|
parser.skipSpaces(false)
|
||||||
}
|
|
||||||
|
|
||||||
endPos := pos
|
|
||||||
skipSpaces(false)
|
|
||||||
if startPos == endPos {
|
if startPos == endPos {
|
||||||
//ErrorLog("empty tag")
|
//ErrorLog("empty tag")
|
||||||
return "", true
|
return "", nil
|
||||||
}
|
}
|
||||||
return string(data[startPos:endPos]), true
|
return string(parser.data[startPos:endPos]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var parseObject func(tag string) DataObject
|
func (parser *dataParser) stopSymbol(symbol rune) bool {
|
||||||
var parseArray func() []DataValue
|
return unicode.IsSpace(symbol) ||
|
||||||
|
slices.Contains([]rune{'=', '{', '}', '[', ']', ',', ' ', '\t', '\n', '\'', '"', '`', '/'}, symbol)
|
||||||
|
}
|
||||||
|
|
||||||
parseNode := func() DataNode {
|
func (parser *dataParser) parseNode() (DataNode, error) {
|
||||||
var tag string
|
var tag string
|
||||||
var ok bool
|
var err error
|
||||||
|
|
||||||
if tag, ok = parseTag(); !ok {
|
if tag, err = parser.parseTag(); err != nil {
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
skipSpaces(true)
|
parser.skipSpaces(true)
|
||||||
if data[pos] != '=' {
|
if parser.data[parser.pos] != '=' {
|
||||||
ErrorLogF("expected '=' after a tag name (line: %d, position: %d)", line, pos-lineStart)
|
return nil, fmt.Errorf("expected '=' after a tag name (line: %d, position: %d)", parser.line, parser.pos-parser.lineStart)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pos++
|
parser.pos++
|
||||||
skipSpaces(true)
|
parser.skipSpaces(true)
|
||||||
switch data[pos] {
|
switch parser.data[parser.pos] {
|
||||||
case '[':
|
case '[':
|
||||||
node := new(dataNode)
|
node := new(dataNode)
|
||||||
node.tag = tag
|
node.tag = tag
|
||||||
|
|
||||||
if node.array = parseArray(); node.array == nil {
|
if node.array, err = parser.parseArray(); err != nil {
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
return node
|
return node, nil
|
||||||
|
|
||||||
case '{':
|
case '{':
|
||||||
node := new(dataNode)
|
node := new(dataNode)
|
||||||
node.tag = tag
|
node.tag = tag
|
||||||
if node.value = parseObject("_"); node.value == nil {
|
if node.value, err = parser.parseObject("_"); err != nil {
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
return node
|
return node, nil
|
||||||
|
|
||||||
case '}', ']', '=':
|
case '}', ']', '=':
|
||||||
ErrorLogF("Expected '[', '{' or a tag name after '=' (line: %d, position: %d)", line, pos-lineStart)
|
return nil, fmt.Errorf(`expected '[', '{' or a tag name after '=' (line: %d, position: %d)`, parser.line, parser.pos-parser.lineStart)
|
||||||
return nil
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
var str string
|
var str string
|
||||||
if str, ok = parseTag(); !ok {
|
if str, err = parser.parseTag(); err != nil {
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
node := new(dataNode)
|
node := new(dataNode)
|
||||||
node.tag = tag
|
node.tag = tag
|
||||||
|
|
||||||
if data[pos] == '{' {
|
if parser.data[parser.pos] == '{' {
|
||||||
if node.value = parseObject(str); node.value == nil {
|
if node.value, err = parser.parseObject(str); err != nil {
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val := new(dataStringValue)
|
val := new(dataStringValue)
|
||||||
|
|
@ -599,91 +644,88 @@ func ParseDataText(text string) DataObject {
|
||||||
node.value = val
|
node.value = val
|
||||||
}
|
}
|
||||||
|
|
||||||
return node
|
return node, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parseObject = func(tag string) DataObject {
|
func (parser *dataParser) parseObject(tag string) (DataObject, error) {
|
||||||
if data[pos] != '{' {
|
if parser.data[parser.pos] != '{' {
|
||||||
ErrorLogF("Expected '{' (line: %d, position: %d)", line, pos-lineStart)
|
return nil, fmt.Errorf(`expected '{' (line: %d, position: %d)`, parser.line, parser.pos-parser.lineStart)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
pos++
|
parser.pos++
|
||||||
|
|
||||||
obj := new(dataObject)
|
obj := new(dataObject)
|
||||||
obj.tag = tag
|
obj.tag = tag
|
||||||
obj.property = []DataNode{}
|
obj.property = []DataNode{}
|
||||||
|
|
||||||
for pos < size {
|
for parser.pos < parser.size {
|
||||||
var node DataNode
|
parser.skipSpaces(true)
|
||||||
|
if parser.data[parser.pos] == '}' {
|
||||||
skipSpaces(true)
|
parser.pos++
|
||||||
if data[pos] == '}' {
|
parser.skipSpaces(false)
|
||||||
pos++
|
return obj, nil
|
||||||
skipSpaces(false)
|
|
||||||
return obj
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if node = parseNode(); node == nil {
|
node, err := parser.parseNode()
|
||||||
return nil
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.property = append(obj.property, node)
|
obj.property = append(obj.property, node)
|
||||||
if data[pos] == '}' {
|
if parser.data[parser.pos] == '}' {
|
||||||
pos++
|
parser.pos++
|
||||||
skipSpaces(true)
|
parser.skipSpaces(true)
|
||||||
return obj
|
return obj, nil
|
||||||
} else if data[pos] != ',' && data[pos] != '\n' {
|
} else if parser.data[parser.pos] != ',' && parser.data[parser.pos] != '\n' {
|
||||||
ErrorLogF(`Expected '}', '\n' or ',' (line: %d, position: %d)`, line, pos-lineStart)
|
return nil, fmt.Errorf(`expected '}', '\n' or ',' (line: %d, position: %d)`, parser.line, parser.pos-parser.lineStart)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
if data[pos] != '\n' {
|
|
||||||
pos++
|
if parser.data[parser.pos] != '\n' {
|
||||||
|
parser.pos++
|
||||||
}
|
}
|
||||||
skipSpaces(true)
|
|
||||||
for data[pos] == ',' {
|
parser.skipSpaces(true)
|
||||||
pos++
|
for parser.data[parser.pos] == ',' {
|
||||||
skipSpaces(true)
|
parser.pos++
|
||||||
|
parser.skipSpaces(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorLog("Unexpected end of text")
|
return nil, errors.New("unexpected end of text")
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
parseArray = func() []DataValue {
|
func (parser *dataParser) parseArray() ([]DataValue, error) {
|
||||||
pos++
|
parser.pos++
|
||||||
skipSpaces(true)
|
parser.skipSpaces(true)
|
||||||
|
|
||||||
array := []DataValue{}
|
array := []DataValue{}
|
||||||
|
|
||||||
for pos < size {
|
for parser.pos < parser.size {
|
||||||
var tag string
|
parser.skipSpaces(true)
|
||||||
var ok bool
|
for parser.data[parser.pos] == ',' && parser.pos < parser.size {
|
||||||
|
parser.pos++
|
||||||
skipSpaces(true)
|
parser.skipSpaces(true)
|
||||||
for data[pos] == ',' && pos < size {
|
|
||||||
pos++
|
|
||||||
skipSpaces(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if pos >= size {
|
if parser.pos >= parser.size {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if data[pos] == ']' {
|
if parser.data[parser.pos] == ']' {
|
||||||
pos++
|
parser.pos++
|
||||||
skipSpaces(true)
|
parser.skipSpaces(true)
|
||||||
return array
|
return array, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if tag, ok = parseTag(); !ok {
|
tag, err := parser.parseTag()
|
||||||
return nil
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if data[pos] == '{' {
|
if parser.data[parser.pos] == '{' {
|
||||||
obj := parseObject(tag)
|
obj, err := parser.parseObject(tag)
|
||||||
if obj == nil {
|
if err != nil {
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
array = append(array, obj)
|
array = append(array, obj)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -692,12 +734,11 @@ func ParseDataText(text string) DataObject {
|
||||||
array = append(array, val)
|
array = append(array, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch data[pos] {
|
switch parser.data[parser.pos] {
|
||||||
case ']', ',', '\n':
|
case ']', ',', '\n':
|
||||||
|
|
||||||
default:
|
default:
|
||||||
ErrorLogF("Expected ']' or ',' (line: %d, position: %d)", line, pos-lineStart)
|
return nil, fmt.Errorf(`expected ']' or ',' (line: %d, position: %d)`, parser.line, parser.pos-parser.lineStart)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -713,12 +754,28 @@ func ParseDataText(text string) DataObject {
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorLog("Unexpected end of text")
|
return nil, errors.New("unexpected end of text")
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if tag, ok := parseTag(); ok {
|
// ParseDataText - parse text and return DataNode
|
||||||
return parseObject(tag)
|
func ParseDataText(text string) (DataObject, error) {
|
||||||
|
|
||||||
|
if strings.ContainsAny(text, "\r") {
|
||||||
|
text = strings.ReplaceAll(text, "\r\n", "\n")
|
||||||
|
text = strings.ReplaceAll(text, "\r", "\n")
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
|
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.
|
// 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 {
|
func GetDataList(view View, subviewID ...string) []string {
|
||||||
if view = getSubview(view, subviewID); view != nil {
|
if view = getSubview(view, subviewID); view != nil {
|
||||||
return getDataListProperty(view)
|
return getDataListProperty(view)
|
||||||
|
|
|
||||||
19
data_test.go
19
data_test.go
|
|
@ -6,10 +6,6 @@ import (
|
||||||
|
|
||||||
func TestParseDataText(t *testing.T) {
|
func TestParseDataText(t *testing.T) {
|
||||||
|
|
||||||
SetErrorLog(func(text string) {
|
|
||||||
t.Error(text)
|
|
||||||
})
|
|
||||||
|
|
||||||
text := `obj1 {
|
text := `obj1 {
|
||||||
key1 = val1,
|
key1 = val1,
|
||||||
key2=obj2{
|
key2=obj2{
|
||||||
|
|
@ -27,8 +23,10 @@ func TestParseDataText(t *testing.T) {
|
||||||
key3 = "\n \t \\ \r \" ' \X4F\x4e \U01Ea",` +
|
key3 = "\n \t \\ \r \" ' \X4F\x4e \U01Ea",` +
|
||||||
"key4=`" + `\n \t \\ \r \" ' \x8F \UF80a` + "`\r}"
|
"key4=`" + `\n \t \\ \r \" ' \x8F \UF80a` + "`\r}"
|
||||||
|
|
||||||
obj := ParseDataText(text)
|
obj, err := ParseDataText(text)
|
||||||
if obj != nil {
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
} else {
|
||||||
if obj.Tag() != "obj1" {
|
if obj.Tag() != "obj1" {
|
||||||
t.Error(`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)
|
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)
|
key := obj.Property(index)
|
||||||
if key == nil {
|
if key == nil {
|
||||||
t.Errorf(`%s.Property(%d) == nil`, obj.Tag(), index)
|
t.Errorf(`%s.Property(%d) == nil`, obj.Tag(), index)
|
||||||
|
|
@ -118,7 +116,7 @@ func TestParseDataText(t *testing.T) {
|
||||||
|
|
||||||
type testKeyData struct {
|
type testKeyData struct {
|
||||||
tag string
|
tag string
|
||||||
nodeType int
|
nodeType DataNodeType
|
||||||
}
|
}
|
||||||
|
|
||||||
data := []testKeyData{
|
data := []testKeyData{
|
||||||
|
|
@ -173,9 +171,6 @@ func TestParseDataText(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SetErrorLog(func(text string) {
|
|
||||||
})
|
|
||||||
|
|
||||||
failText := []string{
|
failText := []string{
|
||||||
" ",
|
" ",
|
||||||
"obj[]",
|
"obj[]",
|
||||||
|
|
@ -204,7 +199,7 @@ func TestParseDataText(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, txt := range failText {
|
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)
|
t.Errorf("result ParseDataText(\"%s\") must be fail", txt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,7 @@ func (picker *datePickerData) init(session Session) {
|
||||||
picker.hasHtmlDisabled = true
|
picker.hasHtmlDisabled = true
|
||||||
picker.normalize = normalizeDatePickerTag
|
picker.normalize = normalizeDatePickerTag
|
||||||
picker.set = picker.setFunc
|
picker.set = picker.setFunc
|
||||||
|
picker.get = picker.getFunc
|
||||||
picker.changed = picker.propertyChanged
|
picker.changed = picker.propertyChanged
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -198,6 +199,17 @@ func stringToDate(value string) (time.Time, bool) {
|
||||||
return time.Now(), false
|
return time.Now(), false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (picker *datePickerData) getFunc(tag PropertyName) any {
|
||||||
|
switch tag {
|
||||||
|
case DateChangedEvent:
|
||||||
|
if listeners := getTwoArgEventRawListeners[DatePicker, time.Time](picker, nil, tag); len(listeners) > 0 {
|
||||||
|
return listeners
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return picker.viewData.getFunc(tag)
|
||||||
|
}
|
||||||
|
|
||||||
func (picker *datePickerData) setFunc(tag PropertyName, value any) []PropertyName {
|
func (picker *datePickerData) setFunc(tag PropertyName, value any) []PropertyName {
|
||||||
|
|
||||||
setDateValue := func(tag PropertyName) []PropertyName {
|
setDateValue := func(tag PropertyName) []PropertyName {
|
||||||
|
|
@ -207,7 +219,7 @@ func (picker *datePickerData) setFunc(tag PropertyName, value any) []PropertyNam
|
||||||
return []PropertyName{tag}
|
return []PropertyName{tag}
|
||||||
|
|
||||||
case string:
|
case string:
|
||||||
if isConstantName(value) {
|
if ok, _ := isConstantName(value); ok {
|
||||||
picker.setRaw(tag, value)
|
picker.setRaw(tag, value)
|
||||||
return []PropertyName{tag}
|
return []PropertyName{tag}
|
||||||
}
|
}
|
||||||
|
|
@ -274,7 +286,7 @@ func (picker *datePickerData) propertyChanged(tag PropertyName) {
|
||||||
date := GetDatePickerValue(picker)
|
date := GetDatePickerValue(picker)
|
||||||
session.callFunc("setInputValue", picker.htmlID(), date.Format(dateFormat))
|
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()
|
oldDate := time.Now()
|
||||||
if value := picker.getRaw("old-date"); value != nil {
|
if value := picker.getRaw("old-date"); value != nil {
|
||||||
if date, ok := value.(time.Time); ok {
|
if date, ok := value.(time.Time); ok {
|
||||||
|
|
@ -282,7 +294,7 @@ func (picker *datePickerData) propertyChanged(tag PropertyName) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, listener := range listeners {
|
for _, listener := range listeners {
|
||||||
listener(picker, date, oldDate)
|
listener.Run(picker, date, oldDate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -348,12 +360,10 @@ func (picker *datePickerData) handleCommand(self View, command PropertyName, dat
|
||||||
oldValue := GetDatePickerValue(picker)
|
oldValue := GetDatePickerValue(picker)
|
||||||
picker.properties[DatePickerValue] = value
|
picker.properties[DatePickerValue] = value
|
||||||
if value != oldValue {
|
if value != oldValue {
|
||||||
for _, listener := range GetDateChangedListeners(picker) {
|
for _, listener := range getTwoArgEventListeners[DatePicker, time.Time](picker, nil, DateChangedEvent) {
|
||||||
listener(picker, value, oldValue)
|
listener.Run(picker, value, oldValue)
|
||||||
}
|
|
||||||
if listener, ok := picker.changeListener[DatePickerValue]; ok {
|
|
||||||
listener(picker, DatePickerValue)
|
|
||||||
}
|
}
|
||||||
|
picker.runChangeListener(DatePickerValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -400,7 +410,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,
|
// 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.
|
// "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) {
|
func GetDatePickerMin(view View, subviewID ...string) (time.Time, bool) {
|
||||||
if view = getSubview(view, subviewID); view != nil {
|
if view = getSubview(view, subviewID); view != nil {
|
||||||
return getDateProperty(view, DatePickerMin, Min)
|
return getDateProperty(view, DatePickerMin, Min)
|
||||||
|
|
@ -410,7 +422,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,
|
// 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.
|
// "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) {
|
func GetDatePickerMax(view View, subviewID ...string) (time.Time, bool) {
|
||||||
if view = getSubview(view, subviewID); view != nil {
|
if view = getSubview(view, subviewID); view != nil {
|
||||||
return getDateProperty(view, DatePickerMax, Max)
|
return getDateProperty(view, DatePickerMax, Max)
|
||||||
|
|
@ -419,13 +433,17 @@ func GetDatePickerMax(view View, subviewID ...string) (time.Time, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDatePickerStep returns the date changing step in days of DatePicker subview.
|
// 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 {
|
func GetDatePickerStep(view View, subviewID ...string) int {
|
||||||
return intStyledProperty(view, subviewID, DatePickerStep, 0)
|
return intStyledProperty(view, subviewID, DatePickerStep, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDatePickerValue returns the date of DatePicker subview.
|
// 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 {
|
func GetDatePickerValue(view View, subviewID ...string) time.Time {
|
||||||
if view = getSubview(view, subviewID); view == nil {
|
if view = getSubview(view, subviewID); view == nil {
|
||||||
return time.Now()
|
return time.Now()
|
||||||
|
|
@ -436,7 +454,18 @@ func GetDatePickerValue(view View, subviewID ...string) time.Time {
|
||||||
|
|
||||||
// GetDateChangedListeners returns the DateChangedListener list of an DatePicker subview.
|
// GetDateChangedListeners returns the DateChangedListener list of an DatePicker subview.
|
||||||
// If there are no listeners then the empty list is returned
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getTwoArgEventListeners[DatePicker, time.Time](view, subviewID, DateChangedEvent)
|
// - 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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,8 +82,7 @@ theme {
|
||||||
background-color = @ruiBackgroundColor,
|
background-color = @ruiBackgroundColor,
|
||||||
accent-color = @ruiHighlightColor,
|
accent-color = @ruiHighlightColor,
|
||||||
},
|
},
|
||||||
ruiButton {
|
ruiEnabledButton {
|
||||||
align = center,
|
|
||||||
padding = "@ruiButtonVerticalPadding, @ruiButtonHorizontalPadding, @ruiButtonVerticalPadding, @ruiButtonHorizontalPadding",
|
padding = "@ruiButtonVerticalPadding, @ruiButtonHorizontalPadding, @ruiButtonVerticalPadding, @ruiButtonHorizontalPadding",
|
||||||
margin = @ruiButtonMargin,
|
margin = @ruiButtonMargin,
|
||||||
radius = @ruiButtonRadius,
|
radius = @ruiButtonRadius,
|
||||||
|
|
@ -92,7 +91,6 @@ theme {
|
||||||
border = _{width = 1px, style = solid, color = @ruiButtonTextColor}
|
border = _{width = 1px, style = solid, color = @ruiButtonTextColor}
|
||||||
},
|
},
|
||||||
ruiDisabledButton {
|
ruiDisabledButton {
|
||||||
align = center,
|
|
||||||
padding = "@ruiButtonVerticalPadding, @ruiButtonHorizontalPadding, @ruiButtonVerticalPadding, @ruiButtonHorizontalPadding",
|
padding = "@ruiButtonVerticalPadding, @ruiButtonHorizontalPadding, @ruiButtonVerticalPadding, @ruiButtonHorizontalPadding",
|
||||||
margin = @ruiButtonMargin,
|
margin = @ruiButtonMargin,
|
||||||
radius = @ruiButtonRadius,
|
radius = @ruiButtonRadius,
|
||||||
|
|
@ -100,14 +98,14 @@ theme {
|
||||||
text-color = @ruiButtonDisabledTextColor,
|
text-color = @ruiButtonDisabledTextColor,
|
||||||
border = _{width = 1px, style = solid, color = @ruiButtonDisabledTextColor}
|
border = _{width = 1px, style = solid, color = @ruiButtonDisabledTextColor}
|
||||||
},
|
},
|
||||||
ruiButton:hover {
|
ruiEnabledButton:hover {
|
||||||
text-color = @ruiTextColor,
|
text-color = @ruiTextColor,
|
||||||
background-color = @ruiBackgroundColor,
|
background-color = @ruiBackgroundColor,
|
||||||
},
|
},
|
||||||
ruiButton:focus {
|
ruiEnabledButton:focus {
|
||||||
shadow = _{spread-radius = @ruiButtonHighlightDilation, blur = @ruiButtonHighlightBlur, color = @ruiHighlightColor },
|
shadow = _{spread-radius = @ruiButtonHighlightDilation, blur = @ruiButtonHighlightBlur, color = @ruiHighlightColor },
|
||||||
},
|
},
|
||||||
ruiButton:active {
|
ruiEnabledButton:active {
|
||||||
background-color = @ruiButtonActiveColor
|
background-color = @ruiButtonActiveColor
|
||||||
},
|
},
|
||||||
ruiDefaultButton {
|
ruiDefaultButton {
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ func (detailsView *detailsViewData) setFunc(tag PropertyName, value any) []Prope
|
||||||
value.setParentID(detailsView.htmlID())
|
value.setParentID(detailsView.htmlID())
|
||||||
|
|
||||||
case DataObject:
|
case DataObject:
|
||||||
if view := CreateViewFromObject(detailsView.Session(), value); view != nil {
|
if view := CreateViewFromObject(detailsView.Session(), value, nil); view != nil {
|
||||||
detailsView.setRaw(Summary, view)
|
detailsView.setRaw(Summary, view)
|
||||||
view.setParentID(detailsView.htmlID())
|
view.setParentID(detailsView.htmlID())
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -192,9 +192,7 @@ func (detailsView *detailsViewData) handleCommand(self View, command PropertyNam
|
||||||
if command == "details-open" {
|
if command == "details-open" {
|
||||||
if n, ok := dataIntProperty(data, "open"); ok {
|
if n, ok := dataIntProperty(data, "open"); ok {
|
||||||
detailsView.properties[Expanded] = (n != 0)
|
detailsView.properties[Expanded] = (n != 0)
|
||||||
if listener, ok := detailsView.changeListener[Expanded]; ok {
|
detailsView.runChangeListener(Expanded)
|
||||||
listener(detailsView, Expanded)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
@ -202,7 +200,9 @@ func (detailsView *detailsViewData) handleCommand(self View, command PropertyNam
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDetailsSummary returns a value of the Summary property of DetailsView.
|
// 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 {
|
func GetDetailsSummary(view View, subviewID ...string) View {
|
||||||
if view = getSubview(view, subviewID); view != nil {
|
if view = getSubview(view, subviewID); view != nil {
|
||||||
if value := view.Get(Summary); value != nil {
|
if value := view.Get(Summary); value != nil {
|
||||||
|
|
@ -219,13 +219,17 @@ func GetDetailsSummary(view View, subviewID ...string) View {
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsDetailsExpanded returns a value of the Expanded property of DetailsView.
|
// 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 {
|
func IsDetailsExpanded(view View, subviewID ...string) bool {
|
||||||
return boolStyledProperty(view, subviewID, Expanded, false)
|
return boolStyledProperty(view, subviewID, Expanded, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsDetailsExpanded returns a value of the HideSummaryMarker property of DetailsView.
|
// 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 {
|
func IsSummaryMarkerHidden(view View, subviewID ...string) bool {
|
||||||
return boolStyledProperty(view, subviewID, HideSummaryMarker, false)
|
return boolStyledProperty(view, subviewID, HideSummaryMarker, false)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,722 @@
|
||||||
|
package rui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"maps"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DragData is the constant for "drag-data" property tag.
|
||||||
|
//
|
||||||
|
// Used by View:
|
||||||
|
//
|
||||||
|
// Supported types: map[string]string.
|
||||||
|
DragData PropertyName = "drag-data"
|
||||||
|
|
||||||
|
// DragImage is the constant for "drag-image" property tag.
|
||||||
|
//
|
||||||
|
// Used by View:
|
||||||
|
// An url of image to use for the drag feedback image.
|
||||||
|
//
|
||||||
|
// Supported type: string.
|
||||||
|
DragImage PropertyName = "drag-image"
|
||||||
|
|
||||||
|
// DragImageXOffset is the constant for "drag-image-x-offset" property tag.
|
||||||
|
//
|
||||||
|
// Used by View:
|
||||||
|
// The horizontal offset in pixels within the drag feedback image.
|
||||||
|
//
|
||||||
|
// Supported types: float, int, string.
|
||||||
|
DragImageXOffset PropertyName = "drag-image-x-offset"
|
||||||
|
|
||||||
|
// DragImageYOffset is the constant for "drag-image-y-offset" property tag.
|
||||||
|
//
|
||||||
|
// Used by View.
|
||||||
|
// The vertical offset in pixels within the drag feedback image.
|
||||||
|
//
|
||||||
|
// Supported types: float, int, string.
|
||||||
|
DragImageYOffset PropertyName = "drag-image-y-offset"
|
||||||
|
|
||||||
|
// DropEffect is the constant for "drag-effect" property tag.
|
||||||
|
//
|
||||||
|
// Used by View.
|
||||||
|
// Controls the feedback (typically visual) the user is given during a drag and drop operation.
|
||||||
|
// It will affect which cursor is displayed while dragging. For example, when the user hovers over a target drop element,
|
||||||
|
// the browser's cursor may indicate which type of operation will occur.
|
||||||
|
//
|
||||||
|
// Supported types: int, string.
|
||||||
|
//
|
||||||
|
// Values:
|
||||||
|
// - 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.
|
||||||
|
DropEffect PropertyName = "drag-effect"
|
||||||
|
|
||||||
|
// DropEffectAllowed is the constant for "drop-effect-allowed" property tag.
|
||||||
|
//
|
||||||
|
// Used by View.
|
||||||
|
// Specifies the effect that is allowed for a drag operation.
|
||||||
|
// The copy operation is used to indicate that the data being dragged will be copied
|
||||||
|
// from its present location to the drop location.
|
||||||
|
// The move operation is used to indicate that the data being dragged will be moved,
|
||||||
|
// and the link operation is used to indicate that some form of relationship
|
||||||
|
// or connection will be created between the source and drop locations.
|
||||||
|
//
|
||||||
|
// Supported types: int, string.
|
||||||
|
//
|
||||||
|
// Values:
|
||||||
|
// - 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.
|
||||||
|
// - 4 (DropEffectCopyMove) or "copy|move" - A copy or move operation is permitted.
|
||||||
|
// - 5 (DropEffectCopyLink) or "copy|link" - A copy or link operation is permitted.
|
||||||
|
// - 6 (DropEffectLinkMove) or "link|move" - A link or move operation is permitted.
|
||||||
|
// - 7 (DropEffectAll) or "all" or "copy|move|link" - All operations are permitted.
|
||||||
|
DropEffectAllowed PropertyName = "drag-effect-allowed"
|
||||||
|
|
||||||
|
// DragStartEvent is the constant for "drag-start-event" property tag.
|
||||||
|
//
|
||||||
|
// Used by View.
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// Used by View.
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// Used by View.
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// Used by View.
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// Used by View.
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// Used by View.
|
||||||
|
// 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).
|
||||||
|
DropEffectUndefined = 0
|
||||||
|
|
||||||
|
// DropEffectNone - the value of the DropEffect field of the DragEvent struct: the item may not be dropped.
|
||||||
|
DropEffectNone = 0
|
||||||
|
|
||||||
|
// DropEffectCopy - the value of the "drop-effect" and "drop-effect-allowed" properties: a copy of the source item may be made at the new location.
|
||||||
|
DropEffectCopy = 1
|
||||||
|
|
||||||
|
// DropEffectMove - the value of the "drop-effect" and "drop-effect-allowed" properties: an item may be moved to a new location.
|
||||||
|
DropEffectMove = 2
|
||||||
|
|
||||||
|
// DropEffectLink - the value of the "drop-effect" and "drop-effect-allowed" properties: a link may be established to the source at the new location.
|
||||||
|
DropEffectLink = 4
|
||||||
|
|
||||||
|
// DropEffectCopyMove - the value of the "drop-effect-allowed" property: a copy or move operation is permitted.
|
||||||
|
DropEffectCopyMove = DropEffectCopy + DropEffectMove
|
||||||
|
|
||||||
|
// DropEffectCopyLink - the value of the "drop-effect-allowed" property: a copy or link operation is permitted.
|
||||||
|
DropEffectCopyLink = DropEffectCopy + DropEffectLink
|
||||||
|
|
||||||
|
// 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 (default value).
|
||||||
|
DropEffectAll = DropEffectCopy + DropEffectMove + DropEffectLink
|
||||||
|
)
|
||||||
|
|
||||||
|
// MouseEvent represent a mouse event
|
||||||
|
type DragAndDropEvent struct {
|
||||||
|
MouseEvent
|
||||||
|
Data map[string]string
|
||||||
|
Files []FileInfo
|
||||||
|
Target View
|
||||||
|
EffectAllowed int
|
||||||
|
DropEffect int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (event *DragAndDropEvent) init(session Session, data DataObject) {
|
||||||
|
event.MouseEvent.init(data)
|
||||||
|
|
||||||
|
event.Data = map[string]string{}
|
||||||
|
if value, ok := data.PropertyValue("data"); ok {
|
||||||
|
data := strings.Split(value, ";")
|
||||||
|
for _, line := range data {
|
||||||
|
pair := strings.Split(line, ":")
|
||||||
|
if len(pair) == 2 {
|
||||||
|
mime, err := base64.StdEncoding.DecodeString(pair[0])
|
||||||
|
if err != nil {
|
||||||
|
ErrorLog(err.Error())
|
||||||
|
} else {
|
||||||
|
val, err := base64.StdEncoding.DecodeString(pair[1])
|
||||||
|
if err == nil {
|
||||||
|
event.Data[string(mime)] = string(val)
|
||||||
|
} else {
|
||||||
|
ErrorLog(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if targetId, ok := data.PropertyValue("target"); ok {
|
||||||
|
event.Target = session.viewByHTMLID(targetId)
|
||||||
|
}
|
||||||
|
|
||||||
|
if effect, ok := data.PropertyValue("effect-allowed"); ok {
|
||||||
|
for i, value := range []string{"undefined", "copy", "move", "copyMove", "link", "copyLink", "linkMove", "all"} {
|
||||||
|
if value == effect {
|
||||||
|
event.EffectAllowed = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if effect, ok := data.PropertyValue("drop-effect"); ok && effect != "" {
|
||||||
|
for i, value := range []string{"none", "copy", "move", "", "link"} {
|
||||||
|
if value == effect {
|
||||||
|
event.DropEffect = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event.Files = parseFilesTag(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringToDropEffect(text string) (int, bool) {
|
||||||
|
text = strings.Trim(text, " \t\n")
|
||||||
|
if n, ok := enumStringToInt(text, []string{"", "copy", "move", "", "link"}, false); ok {
|
||||||
|
switch n {
|
||||||
|
case DropEffectUndefined, DropEffectCopy, DropEffectMove, DropEffectLink:
|
||||||
|
return n, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (view *viewData) setDropEffect(value any) []PropertyName {
|
||||||
|
if !setSimpleProperty(view, DropEffect, value) {
|
||||||
|
if text, ok := value.(string); ok {
|
||||||
|
|
||||||
|
if n, ok := stringToDropEffect(text); ok {
|
||||||
|
if n == DropEffectUndefined {
|
||||||
|
view.setRaw(DropEffect, nil)
|
||||||
|
} else {
|
||||||
|
view.setRaw(DropEffect, n)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
invalidPropertyValue(DropEffect, value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if i, ok := isInt(value); ok {
|
||||||
|
|
||||||
|
switch i {
|
||||||
|
case DropEffectUndefined:
|
||||||
|
view.setRaw(DropEffect, nil)
|
||||||
|
|
||||||
|
case DropEffectCopy, DropEffectMove, DropEffectLink:
|
||||||
|
view.setRaw(DropEffect, i)
|
||||||
|
|
||||||
|
default:
|
||||||
|
invalidPropertyValue(DropEffect, value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
notCompatibleType(DropEffect, value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return []PropertyName{DropEffect}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringToDropEffectAllowed(text string) (int, bool) {
|
||||||
|
if strings.ContainsRune(text, '|') {
|
||||||
|
elements := strings.Split(text, "|")
|
||||||
|
result := 0
|
||||||
|
for _, element := range elements {
|
||||||
|
if n, ok := stringToDropEffect(element); ok && n != DropEffectUndefined {
|
||||||
|
result |= n
|
||||||
|
} else {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, true
|
||||||
|
}
|
||||||
|
|
||||||
|
text = strings.Trim(text, " \t\n")
|
||||||
|
if text != "" {
|
||||||
|
if n, ok := enumStringToInt(text, []string{"undefined", "copy", "move", "", "link", "", "", "all"}, false); ok {
|
||||||
|
return n, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (view *viewData) setDropEffectAllowed(value any) []PropertyName {
|
||||||
|
if !setSimpleProperty(view, DropEffectAllowed, value) {
|
||||||
|
if text, ok := value.(string); ok {
|
||||||
|
|
||||||
|
if n, ok := stringToDropEffectAllowed(text); ok {
|
||||||
|
if n == DropEffectUndefined {
|
||||||
|
view.setRaw(DropEffectAllowed, nil)
|
||||||
|
} else {
|
||||||
|
view.setRaw(DropEffectAllowed, n)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
invalidPropertyValue(DropEffectAllowed, value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
n, ok := isInt(value)
|
||||||
|
if !ok {
|
||||||
|
notCompatibleType(DropEffectAllowed, value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if n == DropEffectUndefined {
|
||||||
|
view.setRaw(DropEffectAllowed, nil)
|
||||||
|
} else if n > DropEffectUndefined && n <= DropEffectAll {
|
||||||
|
view.setRaw(DropEffectAllowed, n)
|
||||||
|
} else {
|
||||||
|
notCompatibleType(DropEffectAllowed, value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return []PropertyName{DropEffectAllowed}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleDragAndDropEvents(view View, tag PropertyName, data DataObject) {
|
||||||
|
listeners := getOneArgEventListeners[View, DragAndDropEvent](view, nil, tag)
|
||||||
|
if len(listeners) > 0 {
|
||||||
|
var event DragAndDropEvent
|
||||||
|
event.init(view.Session(), data)
|
||||||
|
|
||||||
|
for _, listener := range listeners {
|
||||||
|
listener.Run(view, event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func base64DragData(view View) string {
|
||||||
|
if value := view.getRaw(DragData); value != nil {
|
||||||
|
if data, ok := value.(map[string]string); ok && len(data) > 0 {
|
||||||
|
buf := allocStringBuilder()
|
||||||
|
defer freeStringBuilder(buf)
|
||||||
|
|
||||||
|
for mime, value := range data {
|
||||||
|
if buf.Len() > 0 {
|
||||||
|
buf.WriteRune(';')
|
||||||
|
}
|
||||||
|
buf.WriteString(base64.StdEncoding.EncodeToString([]byte(mime)))
|
||||||
|
buf.WriteRune(':')
|
||||||
|
buf.WriteString(base64.StdEncoding.EncodeToString([]byte(value)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func dragAndDropHtml(view View, buffer *strings.Builder) {
|
||||||
|
|
||||||
|
if len(getOneArgEventListeners[View, DragAndDropEvent](view, nil, DropEvent)) > 0 {
|
||||||
|
buffer.WriteString(`ondragover="dragOverEvent(this, event)" ondrop="dropEvent(this, event)" `)
|
||||||
|
if len(getOneArgEventListeners[View, DragAndDropEvent](view, nil, DragOverEvent)) > 0 {
|
||||||
|
buffer.WriteString(`data-drag-over="1" `)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if dragData := base64DragData(view); dragData != "" {
|
||||||
|
buffer.WriteString(`draggable="true" data-drag="`)
|
||||||
|
buffer.WriteString(dragData)
|
||||||
|
buffer.WriteString(`" ondragstart="dragStartEvent(this, event)" `)
|
||||||
|
} else if len(getOneArgEventListeners[View, DragAndDropEvent](view, nil, DragStartEvent)) > 0 {
|
||||||
|
buffer.WriteString(` ondragstart="dragStartEvent(this, event)" `)
|
||||||
|
}
|
||||||
|
|
||||||
|
enterEvent := false
|
||||||
|
switch GetDropEffect(view) {
|
||||||
|
case DropEffectCopy:
|
||||||
|
buffer.WriteString(` data-drop-effect="copy" ondragenter="dragEnterEvent(this, event)"`)
|
||||||
|
enterEvent = true
|
||||||
|
|
||||||
|
case DropEffectMove:
|
||||||
|
buffer.WriteString(` data-drop-effect="move" ondragenter="dragEnterEvent(this, event)"`)
|
||||||
|
enterEvent = true
|
||||||
|
|
||||||
|
case DropEffectLink:
|
||||||
|
buffer.WriteString(` data-drop-effect="link" ondragenter="dragEnterEvent(this, event)"`)
|
||||||
|
enterEvent = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if enterEvent {
|
||||||
|
viewEventsHtml[DragAndDropEvent](view, []PropertyName{DragEndEvent, DragLeaveEvent}, buffer)
|
||||||
|
} else {
|
||||||
|
viewEventsHtml[DragAndDropEvent](view, []PropertyName{DragEndEvent, DragEnterEvent, DragLeaveEvent}, buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
if img := GetDragImage(view); img != "" {
|
||||||
|
buffer.WriteString(` data-drag-image="`)
|
||||||
|
buffer.WriteString(img)
|
||||||
|
buffer.WriteString(`" `)
|
||||||
|
}
|
||||||
|
|
||||||
|
if f := GetDragImageXOffset(view); f != 0 {
|
||||||
|
buffer.WriteString(` data-drag-image-x="`)
|
||||||
|
fmt.Fprintf(buffer, "%g", f)
|
||||||
|
buffer.WriteString(`" `)
|
||||||
|
}
|
||||||
|
|
||||||
|
if f := GetDragImageYOffset(view); f != 0 {
|
||||||
|
buffer.WriteString(` data-drag-image-y="`)
|
||||||
|
fmt.Fprintf(buffer, "%g", f)
|
||||||
|
buffer.WriteString(`" `)
|
||||||
|
}
|
||||||
|
|
||||||
|
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])
|
||||||
|
buffer.WriteString(`" `)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (view *viewData) LoadFile(file FileInfo, result func(FileInfo, []byte)) {
|
||||||
|
if result != nil {
|
||||||
|
view.fileLoader[file.key()] = result
|
||||||
|
view.Session().callFunc("loadDropFile", view.htmlID(), file.Name, file.Size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDragStartEventListeners returns the "drag-start-event" listener list. If there are no listeners then the empty list is returned.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// 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 {
|
||||||
|
if value := view.getRaw(DragData); value != nil {
|
||||||
|
if data, ok := value.(map[string]string); ok {
|
||||||
|
maps.Copy(result, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDragImage returns the drag feedback image.
|
||||||
|
//
|
||||||
|
// 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)
|
||||||
|
if value == nil {
|
||||||
|
value = valueFromStyle(view, DragImage)
|
||||||
|
}
|
||||||
|
|
||||||
|
if value != nil {
|
||||||
|
if img, ok := value.(string); ok {
|
||||||
|
img = strings.Trim(img, " \t")
|
||||||
|
if ok, constName := isConstantName(img); ok {
|
||||||
|
if img, ok = view.Session().ImageConstant(constName); ok {
|
||||||
|
return img
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return img
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDragImageXOffset returns the horizontal offset in pixels within the drag feedback image.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDropEffect returns the effect that is allowed for a drag operation.
|
||||||
|
// Controls the feedback (typically visual) the user is given during a drag and drop operation.
|
||||||
|
// It will affect which cursor is displayed while dragging.
|
||||||
|
//
|
||||||
|
// Returns one of next values:
|
||||||
|
// - 0 (DropEffectUndefined) - The value is not defined (all operations are permitted).
|
||||||
|
// - 1 (DropEffectCopy) - A copy of the source item may be made at the new location.
|
||||||
|
// - 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.
|
||||||
|
//
|
||||||
|
// 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)
|
||||||
|
if value == nil {
|
||||||
|
value = valueFromStyle(view, DropEffect)
|
||||||
|
}
|
||||||
|
|
||||||
|
if value != nil {
|
||||||
|
switch value := value.(type) {
|
||||||
|
case int:
|
||||||
|
return value
|
||||||
|
|
||||||
|
case string:
|
||||||
|
if value, ok := view.Session().resolveConstants(value); ok {
|
||||||
|
if n, ok := stringToDropEffect(value); ok {
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return DropEffectUndefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return DropEffectUndefined
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDropEffectAllowed returns the effect that is allowed for a drag operation.
|
||||||
|
// The copy operation is used to indicate that the data being dragged will be copied from its present location to the drop location.
|
||||||
|
// The move operation is used to indicate that the data being dragged will be moved,
|
||||||
|
// and the link operation is used to indicate that some form of relationship
|
||||||
|
// or connection will be created between the source and drop locations.
|
||||||
|
//
|
||||||
|
// Returns one of next values:
|
||||||
|
// - 0 (DropEffectUndefined) - The value is not defined (all operations are permitted).
|
||||||
|
// - 1 (DropEffectCopy) - A copy of the source item may be made at the new location.
|
||||||
|
// - 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.
|
||||||
|
// - 3 (DropEffectCopyMove) - A copy or move operation is permitted.
|
||||||
|
// - 5 (DropEffectCopyLink) - A copy or link operation is permitted.
|
||||||
|
// - 6 (DropEffectLinkMove) - A link or move operation is permitted.
|
||||||
|
// - 7 (DropEffectAll) - All operations are permitted.
|
||||||
|
//
|
||||||
|
// 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)
|
||||||
|
if value == nil {
|
||||||
|
value = valueFromStyle(view, DropEffectAllowed)
|
||||||
|
}
|
||||||
|
|
||||||
|
if value != nil {
|
||||||
|
switch value := value.(type) {
|
||||||
|
case int:
|
||||||
|
return value
|
||||||
|
|
||||||
|
case string:
|
||||||
|
if value, ok := view.Session().resolveConstants(value); ok {
|
||||||
|
if n, ok := stringToDropEffectAllowed(value); ok {
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return DropEffectUndefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return DropEffectUndefined
|
||||||
|
}
|
||||||
|
|
@ -51,6 +51,7 @@ func (list *dropDownListData) init(session Session) {
|
||||||
list.tag = "DropDownList"
|
list.tag = "DropDownList"
|
||||||
list.hasHtmlDisabled = true
|
list.hasHtmlDisabled = true
|
||||||
list.normalize = normalizeDropDownListTag
|
list.normalize = normalizeDropDownListTag
|
||||||
|
list.get = list.getFunc
|
||||||
list.set = list.setFunc
|
list.set = list.setFunc
|
||||||
list.changed = list.propertyChanged
|
list.changed = list.propertyChanged
|
||||||
}
|
}
|
||||||
|
|
@ -67,6 +68,17 @@ func normalizeDropDownListTag(tag PropertyName) PropertyName {
|
||||||
return tag
|
return tag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (list *dropDownListData) getFunc(tag PropertyName) any {
|
||||||
|
switch tag {
|
||||||
|
case DropDownEvent:
|
||||||
|
if listeners := getTwoArgEventRawListeners[DropDownList, int](list, nil, tag); len(listeners) > 0 {
|
||||||
|
return listeners
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return list.viewData.getFunc(tag)
|
||||||
|
}
|
||||||
|
|
||||||
func (list *dropDownListData) setFunc(tag PropertyName, value any) []PropertyName {
|
func (list *dropDownListData) setFunc(tag PropertyName, value any) []PropertyName {
|
||||||
switch tag {
|
switch tag {
|
||||||
case Items:
|
case Items:
|
||||||
|
|
@ -104,8 +116,8 @@ func (list *dropDownListData) propertyChanged(tag PropertyName) {
|
||||||
list.Session().callFunc("selectDropDownListItem", list.htmlID(), current)
|
list.Session().callFunc("selectDropDownListItem", list.htmlID(), current)
|
||||||
|
|
||||||
oldCurrent, _ := intProperty(list, "old-current", list.Session(), -1)
|
oldCurrent, _ := intProperty(list, "old-current", list.Session(), -1)
|
||||||
for _, listener := range GetDropDownListeners(list) {
|
for _, listener := range getTwoArgEventListeners[DropDownList, int](list, nil, DropDownEvent) {
|
||||||
listener(list, current, oldCurrent)
|
listener.Run(list, current, oldCurrent)
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
@ -139,7 +151,7 @@ func parseIndicesArray(value any) ([]any, bool) {
|
||||||
if val != nil {
|
if val != nil {
|
||||||
switch val := val.(type) {
|
switch val := val.(type) {
|
||||||
case string:
|
case string:
|
||||||
if isConstantName(val) {
|
if ok, _ := isConstantName(val); ok {
|
||||||
items = append(items, val)
|
items = append(items, val)
|
||||||
} else if n, err := strconv.Atoi(val); err == nil {
|
} else if n, err := strconv.Atoi(val); err == nil {
|
||||||
items = append(items, n)
|
items = append(items, n)
|
||||||
|
|
@ -162,7 +174,7 @@ func parseIndicesArray(value any) ([]any, bool) {
|
||||||
items := make([]any, 0, len(value))
|
items := make([]any, 0, len(value))
|
||||||
for _, str := range value {
|
for _, str := range value {
|
||||||
if str = strings.Trim(str, " \t"); str != "" {
|
if str = strings.Trim(str, " \t"); str != "" {
|
||||||
if isConstantName(str) {
|
if ok, _ := isConstantName(str); ok {
|
||||||
items = append(items, str)
|
items = append(items, str)
|
||||||
} else if n, err := strconv.Atoi(str); err == nil {
|
} else if n, err := strconv.Atoi(str); err == nil {
|
||||||
items = append(items, n)
|
items = append(items, n)
|
||||||
|
|
@ -245,12 +257,10 @@ func (list *dropDownListData) handleCommand(self View, command PropertyName, dat
|
||||||
if GetCurrent(list) != number && number >= 0 && number < len(items) {
|
if GetCurrent(list) != number && number >= 0 && number < len(items) {
|
||||||
old := GetCurrent(list)
|
old := GetCurrent(list)
|
||||||
list.properties[Current] = number
|
list.properties[Current] = number
|
||||||
for _, listener := range GetDropDownListeners(list) {
|
for _, listener := range getTwoArgEventListeners[DropDownList, int](list, nil, DropDownEvent) {
|
||||||
listener(list, number, old)
|
listener.Run(list, number, old)
|
||||||
}
|
|
||||||
if listener, ok := list.changeListener[Current]; ok {
|
|
||||||
listener(list, Current)
|
|
||||||
}
|
}
|
||||||
|
list.runChangeListener(Current)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ErrorLog(err.Error())
|
ErrorLog(err.Error())
|
||||||
|
|
@ -264,13 +274,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.
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getTwoArgEventListeners[DropDownList, int](view, subviewID, DropDownEvent)
|
// - 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.
|
// 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 {
|
func GetDropDownItems(view View, subviewID ...string) []string {
|
||||||
if view = getSubview(view, subviewID); view != nil {
|
if view = getSubview(view, subviewID); view != nil {
|
||||||
if value := view.Get(Items); value != nil {
|
if value := view.Get(Items); value != nil {
|
||||||
|
|
@ -295,8 +318,8 @@ func getIndicesArray(view View, tag PropertyName) []int {
|
||||||
result = append(result, value)
|
result = append(result, value)
|
||||||
|
|
||||||
case string:
|
case string:
|
||||||
if value != "" && value[0] == '@' {
|
if ok, constName := isConstantName(value); ok {
|
||||||
if val, ok := view.Session().Constant(value[1:]); ok {
|
if val, ok := view.Session().Constant(constName); ok {
|
||||||
if n, err := strconv.Atoi(val); err == nil {
|
if n, err := strconv.Atoi(val); err == nil {
|
||||||
result = append(result, n)
|
result = append(result, n)
|
||||||
}
|
}
|
||||||
|
|
@ -313,14 +336,18 @@ func getIndicesArray(view View, tag PropertyName) []int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDropDownDisabledItems return an array of disabled(non selectable) items indices of DropDownList.
|
// 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 {
|
func GetDropDownDisabledItems(view View, subviewID ...string) []int {
|
||||||
view = getSubview(view, subviewID)
|
view = getSubview(view, subviewID)
|
||||||
return getIndicesArray(view, DisabledItems)
|
return getIndicesArray(view, DisabledItems)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDropDownItemSeparators return an array of indices of DropDownList items after which a separator should be added.
|
// 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 {
|
func GetDropDownItemSeparators(view View, subviewID ...string) []int {
|
||||||
view = getSubview(view, subviewID)
|
view = getSubview(view, subviewID)
|
||||||
return getIndicesArray(view, ItemSeparators)
|
return getIndicesArray(view, ItemSeparators)
|
||||||
|
|
|
||||||
65
editView.go
65
editView.go
|
|
@ -121,6 +121,7 @@ func (edit *editViewData) init(session Session) {
|
||||||
edit.hasHtmlDisabled = true
|
edit.hasHtmlDisabled = true
|
||||||
edit.tag = "EditView"
|
edit.tag = "EditView"
|
||||||
edit.normalize = normalizeEditViewTag
|
edit.normalize = normalizeEditViewTag
|
||||||
|
edit.get = edit.getFunc
|
||||||
edit.set = edit.setFunc
|
edit.set = edit.setFunc
|
||||||
edit.changed = edit.propertyChanged
|
edit.changed = edit.propertyChanged
|
||||||
}
|
}
|
||||||
|
|
@ -148,6 +149,17 @@ func normalizeEditViewTag(tag PropertyName) PropertyName {
|
||||||
return normalizeDataListTag(tag)
|
return normalizeDataListTag(tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (edit *editViewData) getFunc(tag PropertyName) any {
|
||||||
|
switch tag {
|
||||||
|
case EditTextChangedEvent:
|
||||||
|
if listeners := getTwoArgEventRawListeners[EditView, string](edit, nil, tag); len(listeners) > 0 {
|
||||||
|
return listeners
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return edit.viewData.getFunc(tag)
|
||||||
|
}
|
||||||
|
|
||||||
func (edit *editViewData) setFunc(tag PropertyName, value any) []PropertyName {
|
func (edit *editViewData) setFunc(tag PropertyName, value any) []PropertyName {
|
||||||
switch tag {
|
switch tag {
|
||||||
case Text:
|
case Text:
|
||||||
|
|
@ -261,19 +273,17 @@ func (edit *editViewData) AppendText(text string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
edit.setRaw(Text, text)
|
edit.Set(Text, text)
|
||||||
} else {
|
} else {
|
||||||
edit.setRaw(Text, GetText(edit)+text)
|
edit.Set(Text, GetText(edit)+text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (edit *editViewData) textChanged(newText, oldText string) {
|
func (edit *editViewData) textChanged(newText, oldText string) {
|
||||||
for _, listener := range GetTextChangedListeners(edit) {
|
for _, listener := range getTwoArgEventListeners[EditView, string](edit, nil, EditTextChangedEvent) {
|
||||||
listener(edit, newText, oldText)
|
listener.Run(edit, newText, oldText)
|
||||||
}
|
|
||||||
if listener, ok := edit.changeListener[Text]; ok {
|
|
||||||
listener(edit, Text)
|
|
||||||
}
|
}
|
||||||
|
edit.runChangeListener(Text)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (edit *editViewData) htmlTag() string {
|
func (edit *editViewData) htmlTag() string {
|
||||||
|
|
@ -451,26 +461,43 @@ func IsReadOnly(view View, subviewID ...string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsSpellcheck returns a value of the Spellcheck property of EditView.
|
// 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 {
|
func IsSpellcheck(view View, subviewID ...string) bool {
|
||||||
return boolStyledProperty(view, subviewID, Spellcheck, false)
|
return boolStyledProperty(view, subviewID, Spellcheck, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTextChangedListeners returns the TextChangedListener list of an EditView or MultiLineEditView subview.
|
// GetTextChangedListeners returns the TextChangedListener list of an EditView or MultiLineEditView subview.
|
||||||
// If there are no listeners then the empty list is returned
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getTwoArgEventListeners[EditView, string](view, subviewID, EditTextChangedEvent)
|
// - 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.
|
// 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 {
|
func GetEditViewType(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, EditViewType, SingleLineText, false)
|
return enumStyledProperty(view, subviewID, EditViewType, SingleLineText, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetEditViewPattern returns a value of the Pattern property of EditView.
|
// 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 {
|
func GetEditViewPattern(view View, subviewID ...string) string {
|
||||||
if view = getSubview(view, subviewID); view != nil {
|
if view = getSubview(view, subviewID); view != nil {
|
||||||
if pattern, ok := stringProperty(view, EditViewPattern, view.Session()); ok {
|
if pattern, ok := stringProperty(view, EditViewPattern, view.Session()); ok {
|
||||||
|
|
@ -488,13 +515,17 @@ func GetEditViewPattern(view View, subviewID ...string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsEditViewWrap returns a value of the EditWrap property of MultiLineEditView.
|
// 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 {
|
func IsEditViewWrap(view View, subviewID ...string) bool {
|
||||||
return boolStyledProperty(view, subviewID, EditWrap, false)
|
return boolStyledProperty(view, subviewID, EditWrap, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppendEditText appends the text to the EditView content.
|
// 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) {
|
func AppendEditText(view View, subviewID string, text string) {
|
||||||
if subviewID != "" {
|
if subviewID != "" {
|
||||||
if edit := EditViewByID(view, subviewID); edit != nil {
|
if edit := EditViewByID(view, subviewID); edit != nil {
|
||||||
|
|
@ -509,7 +540,9 @@ func AppendEditText(view View, subviewID string, text string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCaretColor returns the color of the text input caret.
|
// 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 {
|
func GetCaretColor(view View, subviewID ...string) Color {
|
||||||
return colorStyledProperty(view, subviewID, CaretColor, false)
|
return colorStyledProperty(view, subviewID, CaretColor, false)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
659
events.go
659
events.go
|
|
@ -1,6 +1,9 @@
|
||||||
package rui
|
package rui
|
||||||
|
|
||||||
import "strings"
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
var eventJsFunc = map[PropertyName]struct{ jsEvent, jsFunc string }{
|
var eventJsFunc = map[PropertyName]struct{ jsEvent, jsFunc string }{
|
||||||
FocusEvent: {jsEvent: "onfocus", jsFunc: "focusEvent"},
|
FocusEvent: {jsEvent: "onfocus", jsFunc: "focusEvent"},
|
||||||
|
|
@ -33,466 +36,15 @@ var eventJsFunc = map[PropertyName]struct{ jsEvent, jsFunc string }{
|
||||||
AnimationEndEvent: {jsEvent: "onanimationend", jsFunc: "animationEndEvent"},
|
AnimationEndEvent: {jsEvent: "onanimationend", jsFunc: "animationEndEvent"},
|
||||||
AnimationIterationEvent: {jsEvent: "onanimationiteration", jsFunc: "animationIterationEvent"},
|
AnimationIterationEvent: {jsEvent: "onanimationiteration", jsFunc: "animationIterationEvent"},
|
||||||
AnimationCancelEvent: {jsEvent: "onanimationcancel", jsFunc: "animationCancelEvent"},
|
AnimationCancelEvent: {jsEvent: "onanimationcancel", jsFunc: "animationCancelEvent"},
|
||||||
}
|
DragEndEvent: {jsEvent: "ondragend", jsFunc: "dragEndEvent"},
|
||||||
|
DragEnterEvent: {jsEvent: "ondragenter", jsFunc: "dragEnterEvent"},
|
||||||
func valueToNoArgEventListeners[V any](value any) ([]func(V), bool) {
|
DragLeaveEvent: {jsEvent: "ondragleave", jsFunc: "dragLeaveEvent"},
|
||||||
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) {
|
func viewEventsHtml[T any](view View, events []PropertyName, buffer *strings.Builder) {
|
||||||
for _, tag := range events {
|
for _, tag := range events {
|
||||||
if value := view.getRaw(tag); value != nil {
|
|
||||||
if js, ok := eventJsFunc[tag]; ok {
|
if js, ok := eventJsFunc[tag]; ok {
|
||||||
if listeners, ok := value.([]func(View, T)); ok && len(listeners) > 0 {
|
if value := getOneArgEventListeners[View, T](view, nil, tag); len(value) > 0 {
|
||||||
buffer.WriteString(js.jsEvent)
|
buffer.WriteString(js.jsEvent)
|
||||||
buffer.WriteString(`="`)
|
buffer.WriteString(`="`)
|
||||||
buffer.WriteString(js.jsFunc)
|
buffer.WriteString(js.jsFunc)
|
||||||
|
|
@ -501,7 +53,6 @@ func viewEventsHtml[T any](view View, events []PropertyName, buffer *strings.Bui
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func updateEventListenerHtml(view View, tag PropertyName) {
|
func updateEventListenerHtml(view View, tag PropertyName) {
|
||||||
if js, ok := eventJsFunc[tag]; ok {
|
if js, ok := eventJsFunc[tag]; ok {
|
||||||
|
|
@ -515,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.getRaw(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.getRaw(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.getRaw(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 ""
|
||||||
|
}
|
||||||
137
filePicker.go
137
filePicker.go
|
|
@ -1,7 +1,7 @@
|
||||||
package rui
|
package rui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -67,6 +67,8 @@ type FileInfo struct {
|
||||||
|
|
||||||
// MimeType - the file's MIME type.
|
// MimeType - the file's MIME type.
|
||||||
MimeType string
|
MimeType string
|
||||||
|
|
||||||
|
data []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// FilePicker represents the FilePicker view
|
// FilePicker represents the FilePicker view
|
||||||
|
|
@ -83,10 +85,11 @@ type FilePicker interface {
|
||||||
type filePickerData struct {
|
type filePickerData struct {
|
||||||
viewData
|
viewData
|
||||||
files []FileInfo
|
files []FileInfo
|
||||||
loader map[int]func(FileInfo, []byte)
|
//loader map[int]func(FileInfo, []byte)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (file *FileInfo) initBy(node DataValue) {
|
func dataToFileInfo(node DataValue) FileInfo {
|
||||||
|
var file FileInfo
|
||||||
if obj := node.Object(); obj != nil {
|
if obj := node.Object(); obj != nil {
|
||||||
file.Name, _ = obj.PropertyValue("name")
|
file.Name, _ = obj.PropertyValue("name")
|
||||||
file.MimeType, _ = obj.PropertyValue("mime-type")
|
file.MimeType, _ = obj.PropertyValue("mime-type")
|
||||||
|
|
@ -103,6 +106,11 @@ func (file *FileInfo) initBy(node DataValue) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
|
||||||
|
func (file FileInfo) key() string {
|
||||||
|
return fmt.Sprintf("%s:%d", file.Name, int(file.Size))
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFilePicker create new FilePicker object and return it
|
// NewFilePicker create new FilePicker object and return it
|
||||||
|
|
@ -122,7 +130,8 @@ func (picker *filePickerData) init(session Session) {
|
||||||
picker.tag = "FilePicker"
|
picker.tag = "FilePicker"
|
||||||
picker.hasHtmlDisabled = true
|
picker.hasHtmlDisabled = true
|
||||||
picker.files = []FileInfo{}
|
picker.files = []FileInfo{}
|
||||||
picker.loader = map[int]func(FileInfo, []byte){}
|
//picker.loader = map[int]func(FileInfo, []byte){}
|
||||||
|
picker.get = picker.getFunc
|
||||||
picker.set = picker.setFunc
|
picker.set = picker.setFunc
|
||||||
picker.changed = picker.propertyChanged
|
picker.changed = picker.propertyChanged
|
||||||
|
|
||||||
|
|
@ -137,17 +146,35 @@ func (picker *filePickerData) Files() []FileInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (picker *filePickerData) LoadFile(file FileInfo, result func(FileInfo, []byte)) {
|
func (picker *filePickerData) LoadFile(file FileInfo, result func(FileInfo, []byte)) {
|
||||||
if result == nil {
|
if result != nil {
|
||||||
|
for i, info := range picker.files {
|
||||||
|
if info.Name == file.Name && info.Size == file.Size && info.LastModified.Equal(file.LastModified) {
|
||||||
|
if info.data != nil {
|
||||||
|
result(info, info.data)
|
||||||
|
} else {
|
||||||
|
picker.fileLoader[info.key()] = func(file FileInfo, data []byte) {
|
||||||
|
picker.files[i].data = data
|
||||||
|
result(file, data)
|
||||||
|
}
|
||||||
|
picker.Session().callFunc("loadSelectedFile", picker.htmlID(), i)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for i, info := range picker.files {
|
picker.viewData.LoadFile(file, result)
|
||||||
if info.Name == file.Name && info.Size == file.Size && info.LastModified == file.LastModified {
|
|
||||||
picker.loader[i] = result
|
|
||||||
picker.Session().callFunc("loadSelectedFile", picker.htmlID(), i)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (picker *filePickerData) getFunc(tag PropertyName) any {
|
||||||
|
switch tag {
|
||||||
|
case FileSelectedEvent:
|
||||||
|
if listeners := getOneArgEventRawListeners[FilePicker, []FileInfo](picker, nil, tag); len(listeners) > 0 {
|
||||||
|
return listeners
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return picker.viewData.getFunc(tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (picker *filePickerData) setFunc(tag PropertyName, value any) []PropertyName {
|
func (picker *filePickerData) setFunc(tag PropertyName, value any) []PropertyName {
|
||||||
|
|
@ -217,7 +244,7 @@ func acceptPropertyCSS(view View) string {
|
||||||
if buffer.Len() > 0 {
|
if buffer.Len() > 0 {
|
||||||
buffer.WriteString(", ")
|
buffer.WriteString(", ")
|
||||||
}
|
}
|
||||||
if value[0] != '.' && !strings.Contains(value, "/") {
|
if value[0] != '.' && !strings.ContainsRune(value, '/') {
|
||||||
buffer.WriteRune('.')
|
buffer.WriteRune('.')
|
||||||
}
|
}
|
||||||
buffer.WriteString(value)
|
buffer.WriteString(value)
|
||||||
|
|
@ -248,62 +275,27 @@ func (picker *filePickerData) htmlProperties(self View, buffer *strings.Builder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (picker *filePickerData) handleCommand(self View, command PropertyName, data DataObject) bool {
|
func parseFilesTag(data DataObject) []FileInfo {
|
||||||
switch command {
|
|
||||||
case "fileSelected":
|
|
||||||
if node := data.PropertyByTag("files"); node != nil && node.Type() == ArrayNode {
|
if node := data.PropertyByTag("files"); node != nil && node.Type() == ArrayNode {
|
||||||
count := node.ArraySize()
|
count := node.ArraySize()
|
||||||
files := make([]FileInfo, count)
|
files := make([]FileInfo, count)
|
||||||
for i := 0; i < count; i++ {
|
for i := range count {
|
||||||
if value := node.ArrayElement(i); value != nil {
|
if value := node.ArrayElement(i); value != nil {
|
||||||
files[i].initBy(value)
|
files[i] = dataToFileInfo(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return files
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (picker *filePickerData) handleCommand(self View, command PropertyName, data DataObject) bool {
|
||||||
|
switch command {
|
||||||
|
case "fileSelected":
|
||||||
|
if files := parseFilesTag(data); files != nil {
|
||||||
picker.files = files
|
picker.files = files
|
||||||
|
for _, listener := range getOneArgEventListeners[FilePicker, []FileInfo](picker, nil, FileSelectedEvent) {
|
||||||
for _, listener := range GetFileSelectedListeners(picker) {
|
listener.Run(picker, files)
|
||||||
listener(picker, files)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
|
|
||||||
case "fileLoaded":
|
|
||||||
if index, ok := dataIntProperty(data, "index"); ok {
|
|
||||||
if result, ok := picker.loader[index]; ok {
|
|
||||||
var file FileInfo
|
|
||||||
file.initBy(data)
|
|
||||||
|
|
||||||
var fileData []byte = nil
|
|
||||||
if base64Data, ok := data.PropertyValue("data"); ok {
|
|
||||||
if index := strings.LastIndex(base64Data, ","); index >= 0 {
|
|
||||||
base64Data = base64Data[index+1:]
|
|
||||||
}
|
|
||||||
decode, err := base64.StdEncoding.DecodeString(base64Data)
|
|
||||||
if err == nil {
|
|
||||||
fileData = decode
|
|
||||||
} else {
|
|
||||||
ErrorLog(err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result(file, fileData)
|
|
||||||
delete(picker.loader, index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
|
|
||||||
case "fileLoadingError":
|
|
||||||
if error, ok := data.PropertyValue("error"); ok {
|
|
||||||
ErrorLog(error)
|
|
||||||
}
|
|
||||||
if index, ok := dataIntProperty(data, "index"); ok {
|
|
||||||
if result, ok := picker.loader[index]; ok {
|
|
||||||
if index >= 0 && index < len(picker.files) {
|
|
||||||
result(picker.files[index], nil)
|
|
||||||
} else {
|
|
||||||
result(FileInfo{}, nil)
|
|
||||||
}
|
|
||||||
delete(picker.loader, index)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|
@ -337,13 +329,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.
|
// 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 {
|
func IsMultipleFilePicker(view View, subviewID ...string) bool {
|
||||||
return boolStyledProperty(view, subviewID, Multiple, false)
|
return boolStyledProperty(view, subviewID, Multiple, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFilePickerAccept returns sets the list of allowed file extensions or MIME types.
|
// 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 {
|
func GetFilePickerAccept(view View, subviewID ...string) []string {
|
||||||
if view = getSubview(view, subviewID); view != nil {
|
if view = getSubview(view, subviewID); view != nil {
|
||||||
accept, ok := stringProperty(view, Accept, view.Session())
|
accept, ok := stringProperty(view, Accept, view.Session())
|
||||||
|
|
@ -354,7 +350,7 @@ func GetFilePickerAccept(view View, subviewID ...string) []string {
|
||||||
}
|
}
|
||||||
if ok {
|
if ok {
|
||||||
result := strings.Split(accept, ",")
|
result := strings.Split(accept, ",")
|
||||||
for i := 0; i < len(result); i++ {
|
for i := range len(result) {
|
||||||
result[i] = strings.Trim(result[i], " \t\n")
|
result[i] = strings.Trim(result[i], " \t\n")
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
|
@ -365,7 +361,16 @@ func GetFilePickerAccept(view View, subviewID ...string) []string {
|
||||||
|
|
||||||
// GetFileSelectedListeners returns the "file-selected-event" listener list.
|
// GetFileSelectedListeners returns the "file-selected-event" listener list.
|
||||||
// If there are no listeners then the empty list is returned.
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[FilePicker, []FileInfo](view, subviewID, FileSelectedEvent)
|
// - 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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
20
filter.go
20
filter.go
|
|
@ -159,8 +159,7 @@ func NewFilterProperty(params Params) FilterProperty {
|
||||||
func newFilterProperty(obj DataObject) FilterProperty {
|
func newFilterProperty(obj DataObject) FilterProperty {
|
||||||
filter := new(filterData)
|
filter := new(filterData)
|
||||||
filter.init()
|
filter.init()
|
||||||
for i := 0; i < obj.PropertyCount(); i++ {
|
for node := range obj.Properties() {
|
||||||
if node := obj.Property(i); node != nil {
|
|
||||||
tag := node.Tag()
|
tag := node.Tag()
|
||||||
switch node.Type() {
|
switch node.Type() {
|
||||||
case TextNode:
|
case TextNode:
|
||||||
|
|
@ -177,7 +176,6 @@ func newFilterProperty(obj DataObject) FilterProperty {
|
||||||
ErrorLog(`Invalid value of "` + tag + `"`)
|
ErrorLog(`Invalid value of "` + tag + `"`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if len(filter.properties) > 0 {
|
if len(filter.properties) > 0 {
|
||||||
return filter
|
return filter
|
||||||
|
|
@ -218,21 +216,7 @@ func (filter *filterData) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (filter *filterData) writeString(buffer *strings.Builder, indent string) {
|
func (filter *filterData) writeString(buffer *strings.Builder, indent string) {
|
||||||
buffer.WriteString("filter { ")
|
filter.writeToBuffer(buffer, indent, "filter", filter.AllTags())
|
||||||
comma := false
|
|
||||||
tags := filter.AllTags()
|
|
||||||
for _, tag := range tags {
|
|
||||||
if value, ok := filter.properties[tag]; ok {
|
|
||||||
if comma {
|
|
||||||
buffer.WriteString(", ")
|
|
||||||
}
|
|
||||||
buffer.WriteString(string(tag))
|
|
||||||
buffer.WriteString(" = ")
|
|
||||||
writePropertyValue(buffer, tag, value, indent)
|
|
||||||
comma = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buffer.WriteString(" }")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (filter *filterData) cssStyle(session Session) string {
|
func (filter *filterData) cssStyle(session Session) string {
|
||||||
|
|
|
||||||
|
|
@ -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
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getNoArgEventListeners[View](view, subviewID, FocusEvent)
|
// - 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
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getNoArgEventListeners[View](view, subviewID, LostFocusEvent)
|
// - 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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,7 @@ func setGridCellSize(properties Properties, tag PropertyName, value any) []Prope
|
||||||
sizes := make([]any, count)
|
sizes := make([]any, count)
|
||||||
for i, val := range values {
|
for i, val := range values {
|
||||||
val = strings.Trim(val, " \t\n\r")
|
val = strings.Trim(val, " \t\n\r")
|
||||||
if isConstantName(val) {
|
if ok, _ := isConstantName(val); ok {
|
||||||
sizes[i] = val
|
sizes[i] = val
|
||||||
} else if fn := parseSizeFunc(val); fn != nil {
|
} else if fn := parseSizeFunc(val); fn != nil {
|
||||||
sizes[i] = SizeUnit{Type: SizeFunction, Function: fn}
|
sizes[i] = SizeUnit{Type: SizeFunction, Function: fn}
|
||||||
|
|
@ -162,7 +162,7 @@ func setGridCellSize(properties Properties, tag PropertyName, value any) []Prope
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
properties.setRaw(tag, sizes)
|
properties.setRaw(tag, sizes)
|
||||||
} else if isConstantName(values[0]) {
|
} else if ok, _ := isConstantName(values[0]); ok {
|
||||||
properties.setRaw(tag, values[0])
|
properties.setRaw(tag, values[0])
|
||||||
} else if size, err := stringToSizeUnit(values[0]); err == nil {
|
} else if size, err := stringToSizeUnit(values[0]); err == nil {
|
||||||
properties.setRaw(tag, size)
|
properties.setRaw(tag, size)
|
||||||
|
|
@ -220,7 +220,7 @@ func setGridCellSize(properties Properties, tag PropertyName, value any) []Prope
|
||||||
sizes[i] = val
|
sizes[i] = val
|
||||||
|
|
||||||
case string:
|
case string:
|
||||||
if isConstantName(val) {
|
if ok, _ := isConstantName(val); ok {
|
||||||
sizes[i] = val
|
sizes[i] = val
|
||||||
} else if size, err := stringToSizeUnit(val); err == nil {
|
} else if size, err := stringToSizeUnit(val); err == nil {
|
||||||
sizes[i] = size
|
sizes[i] = size
|
||||||
|
|
@ -458,10 +458,7 @@ func (gridLayout *gridLayoutData) UpdateGridContent() {
|
||||||
if gridLayout.created {
|
if gridLayout.created {
|
||||||
updateInnerHTML(gridLayout.htmlID(), gridLayout.session)
|
updateInnerHTML(gridLayout.htmlID(), gridLayout.session)
|
||||||
}
|
}
|
||||||
|
gridLayout.runChangeListener(Content)
|
||||||
if listener, ok := gridLayout.changeListener[Content]; ok {
|
|
||||||
listener(gridLayout, Content)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -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)
|
// 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 {
|
func GetCellVerticalAlign(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, CellVerticalAlign, StretchAlign, false)
|
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)
|
// 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 {
|
func GetCellHorizontalAlign(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, CellHorizontalAlign, StretchAlign, false)
|
return enumStyledProperty(view, subviewID, CellHorizontalAlign, StretchAlign, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetGridAutoFlow returns the value of the "grid-auto-flow" property
|
// 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 {
|
func GetGridAutoFlow(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, GridAutoFlow, 0, false)
|
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.
|
// 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 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 {
|
func GetCellWidth(view View, subviewID ...string) []SizeUnit {
|
||||||
if view = getSubview(view, subviewID); view != nil {
|
if view = getSubview(view, subviewID); view != nil {
|
||||||
return gridCellSizes(view, CellWidth, view.Session())
|
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.
|
// 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 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 {
|
func GetCellHeight(view View, subviewID ...string) []SizeUnit {
|
||||||
if view = getSubview(view, subviewID); view != nil {
|
if view = getSubview(view, subviewID); view != nil {
|
||||||
return gridCellSizes(view, CellHeight, view.Session())
|
return gridCellSizes(view, CellHeight, view.Session())
|
||||||
|
|
@ -544,13 +551,17 @@ func GetCellHeight(view View, subviewID ...string) []SizeUnit {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetGridRowGap returns the gap between GridLayout rows.
|
// 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 {
|
func GetGridRowGap(view View, subviewID ...string) SizeUnit {
|
||||||
return sizeStyledProperty(view, subviewID, GridRowGap, false)
|
return sizeStyledProperty(view, subviewID, GridRowGap, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetGridColumnGap returns the gap between GridLayout columns.
|
// 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 {
|
func GetGridColumnGap(view View, subviewID ...string) SizeUnit {
|
||||||
return sizeStyledProperty(view, subviewID, GridColumnGap, false)
|
return sizeStyledProperty(view, subviewID, GridColumnGap, false)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
28
image.go
28
image.go
|
|
@ -4,34 +4,44 @@ import (
|
||||||
"strconv"
|
"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
|
// Constants which represent return values of the LoadingStatus function of an [Image] view
|
||||||
const (
|
const (
|
||||||
// ImageLoading is the image loading status: in the process of loading
|
// 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 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 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.
|
// Image defines the image that is used for drawing operations on the Canvas.
|
||||||
type Image interface {
|
type Image interface {
|
||||||
// URL returns the url of the image
|
// URL returns the url of the image
|
||||||
URL() string
|
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: if LoadingStatus() == ImageLoadingError then returns the error text, "" otherwise
|
||||||
LoadingError() string
|
LoadingError() string
|
||||||
setLoadingError(err string)
|
setLoadingError(err string)
|
||||||
|
|
||||||
// Width returns the width of the image in pixels. While LoadingStatus() != ImageReady returns 0
|
// Width returns the width of the image in pixels. While LoadingStatus() != ImageReady returns 0
|
||||||
Width() float64
|
Width() float64
|
||||||
|
|
||||||
// Height returns the height of the image in pixels. While LoadingStatus() != ImageReady returns 0
|
// Height returns the height of the image in pixels. While LoadingStatus() != ImageReady returns 0
|
||||||
Height() float64
|
Height() float64
|
||||||
}
|
}
|
||||||
|
|
||||||
type imageData struct {
|
type imageData struct {
|
||||||
url string
|
url string
|
||||||
loadingStatus int
|
loadingStatus ImageLoadingStatus
|
||||||
loadingError string
|
loadingError string
|
||||||
width, height float64
|
width, height float64
|
||||||
listener func(Image)
|
listener func(Image)
|
||||||
|
|
@ -45,7 +55,7 @@ func (image *imageData) URL() string {
|
||||||
return image.url
|
return image.url
|
||||||
}
|
}
|
||||||
|
|
||||||
func (image *imageData) LoadingStatus() int {
|
func (image *imageData) LoadingStatus() ImageLoadingStatus {
|
||||||
return image.loadingStatus
|
return image.loadingStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -133,8 +143,8 @@ func (manager *imageManager) imageLoadError(obj DataObject) {
|
||||||
|
|
||||||
// LoadImage starts the async image loading by url
|
// LoadImage starts the async image loading by url
|
||||||
func LoadImage(url string, onLoaded func(Image), session Session) Image {
|
func LoadImage(url string, onLoaded func(Image), session Session) Image {
|
||||||
if url != "" && url[0] == '@' {
|
if ok, constName := isConstantName(url); ok {
|
||||||
if image, ok := session.ImageConstant(url[1:]); ok {
|
if image, ok := session.ImageConstant(constName); ok {
|
||||||
url = image
|
url = image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
52
imageView.go
52
imageView.go
|
|
@ -97,6 +97,7 @@ func (imageView *imageViewData) init(session Session) {
|
||||||
imageView.tag = "ImageView"
|
imageView.tag = "ImageView"
|
||||||
imageView.systemClass = "ruiImageView"
|
imageView.systemClass = "ruiImageView"
|
||||||
imageView.normalize = normalizeImageViewTag
|
imageView.normalize = normalizeImageViewTag
|
||||||
|
imageView.get = imageView.getFunc
|
||||||
imageView.set = imageView.setFunc
|
imageView.set = imageView.setFunc
|
||||||
imageView.changed = imageView.propertyChanged
|
imageView.changed = imageView.propertyChanged
|
||||||
}
|
}
|
||||||
|
|
@ -122,6 +123,17 @@ func normalizeImageViewTag(tag PropertyName) PropertyName {
|
||||||
return tag
|
return tag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (imageView *imageViewData) getFunc(tag PropertyName) any {
|
||||||
|
switch tag {
|
||||||
|
case LoadedEvent, ErrorEvent:
|
||||||
|
if listeners := getNoArgEventRawListeners[ImageView](imageView, nil, tag); len(listeners) > 0 {
|
||||||
|
return listeners
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return imageView.viewData.getFunc(tag)
|
||||||
|
}
|
||||||
|
|
||||||
func (imageView *imageViewData) setFunc(tag PropertyName, value any) []PropertyName {
|
func (imageView *imageViewData) setFunc(tag PropertyName, value any) []PropertyName {
|
||||||
|
|
||||||
switch tag {
|
switch tag {
|
||||||
|
|
@ -205,7 +217,7 @@ func imageViewSrcSet(view View, path string) string {
|
||||||
buffer.WriteString(", ")
|
buffer.WriteString(", ")
|
||||||
}
|
}
|
||||||
buffer.WriteString(src.path)
|
buffer.WriteString(src.path)
|
||||||
buffer.WriteString(fmt.Sprintf(" %gx", src.scale))
|
fmt.Fprintf(buffer, " %gx", src.scale)
|
||||||
}
|
}
|
||||||
return buffer.String()
|
return buffer.String()
|
||||||
}
|
}
|
||||||
|
|
@ -217,11 +229,11 @@ func (imageView *imageViewData) htmlTag() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func imageViewSrc(view View, src string) (string, string) {
|
func imageViewSrc(view View, src string) (string, string) {
|
||||||
if src != "" && src[0] == '@' {
|
if ok, constName := isConstantName(src); ok {
|
||||||
if image, ok := view.Session().ImageConstant(src[1:]); ok {
|
if image, ok := view.Session().ImageConstant(constName); ok {
|
||||||
src = image
|
src = image
|
||||||
} else {
|
} else {
|
||||||
src = ""
|
return "", ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -300,7 +312,7 @@ func (imageView *imageViewData) handleCommand(self View, command PropertyName, d
|
||||||
switch command {
|
switch command {
|
||||||
case "imageViewError":
|
case "imageViewError":
|
||||||
for _, listener := range getNoArgEventListeners[ImageView](imageView, nil, ErrorEvent) {
|
for _, listener := range getNoArgEventListeners[ImageView](imageView, nil, ErrorEvent) {
|
||||||
listener(imageView)
|
listener.Run(imageView)
|
||||||
}
|
}
|
||||||
|
|
||||||
case "imageViewLoaded":
|
case "imageViewLoaded":
|
||||||
|
|
@ -309,7 +321,7 @@ func (imageView *imageViewData) handleCommand(self View, command PropertyName, d
|
||||||
imageView.currentSrc, _ = data.PropertyValue("current-src")
|
imageView.currentSrc, _ = data.PropertyValue("current-src")
|
||||||
|
|
||||||
for _, listener := range getNoArgEventListeners[ImageView](imageView, nil, LoadedEvent) {
|
for _, listener := range getNoArgEventListeners[ImageView](imageView, nil, LoadedEvent) {
|
||||||
listener(imageView)
|
listener.Run(imageView)
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
@ -370,3 +382,31 @@ func GetImageViewVerticalAlign(view View, subviewID ...string) int {
|
||||||
func GetImageViewHorizontalAlign(view View, subviewID ...string) int {
|
func GetImageViewHorizontalAlign(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, ImageHorizontalAlign, LeftAlign, false)
|
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)
|
||||||
|
}
|
||||||
|
|
|
||||||
53
keyEvents.go
53
keyEvents.go
|
|
@ -434,15 +434,13 @@ func (event *KeyEvent) init(data DataObject) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func keyEventsHtml(view View, buffer *strings.Builder) {
|
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)" `)
|
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)" `)
|
buffer.WriteString(`onkeyup="keyUpEvent(this, event)" `)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -454,12 +452,19 @@ func handleKeyEvents(view View, tag PropertyName, data DataObject) {
|
||||||
|
|
||||||
if len(listeners) > 0 {
|
if len(listeners) > 0 {
|
||||||
for _, listener := range listeners {
|
for _, listener := range listeners {
|
||||||
listener(view, event)
|
listener.Run(view, event)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if tag == KeyDownEvent && view.Focusable() && (event.Key == " " || event.Key == "Enter") && !IsDisabled(view) {
|
if tag == KeyDownEvent && view.Focusable() && (event.Key == " " || event.Key == "Enter") &&
|
||||||
|
!IsDisabled(view) && GetSemantics(view) != ButtonSemantics {
|
||||||
|
|
||||||
|
switch view.Tag() {
|
||||||
|
case "EditView", "ListView", "TableView", "TabsLayout", "TimePicker", "DatePicker", "AudioPlayer", "VideoPlayer":
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if listeners := getOneArgEventListeners[View, MouseEvent](view, nil, ClickEvent); len(listeners) > 0 {
|
if listeners := getOneArgEventListeners[View, MouseEvent](view, nil, ClickEvent); len(listeners) > 0 {
|
||||||
clickEvent := MouseEvent{
|
clickEvent := MouseEvent{
|
||||||
TimeStamp: event.TimeStamp,
|
TimeStamp: event.TimeStamp,
|
||||||
|
|
@ -477,20 +482,38 @@ func handleKeyEvents(view View, tag PropertyName, data DataObject) {
|
||||||
ScreenY: view.Frame().Top + view.Frame().Height/2,
|
ScreenY: view.Frame().Top + view.Frame().Height/2,
|
||||||
}
|
}
|
||||||
for _, listener := range listeners {
|
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.
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[View, KeyEvent](view, subviewID, KeyDownEvent)
|
// - 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.
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[View, KeyEvent](view, subviewID, KeyUpEvent)
|
// - 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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,6 @@ func (listLayout *listLayoutData) init(session Session) {
|
||||||
listLayout.set = listLayout.setFunc
|
listLayout.set = listLayout.setFunc
|
||||||
listLayout.remove = listLayout.removeFunc
|
listLayout.remove = listLayout.removeFunc
|
||||||
listLayout.changed = listLayout.propertyChanged
|
listLayout.changed = listLayout.propertyChanged
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func normalizeListLayoutTag(tag PropertyName) PropertyName {
|
func normalizeListLayoutTag(tag PropertyName) PropertyName {
|
||||||
|
|
@ -115,10 +114,13 @@ func (listLayout *listLayoutData) removeFunc(tag PropertyName) []PropertyName {
|
||||||
return result
|
return result
|
||||||
|
|
||||||
case Content:
|
case Content:
|
||||||
listLayout.viewsContainerData.removeFunc(Content)
|
result := listLayout.viewsContainerData.removeFunc(Content)
|
||||||
|
if listLayout.adapter != nil {
|
||||||
listLayout.adapter = nil
|
listLayout.adapter = nil
|
||||||
return []PropertyName{Content}
|
return []PropertyName{Content}
|
||||||
}
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
return listLayout.viewsContainerData.removeFunc(tag)
|
return listLayout.viewsContainerData.removeFunc(tag)
|
||||||
}
|
}
|
||||||
|
|
@ -176,8 +178,7 @@ func (listLayout *listLayoutData) createContent() bool {
|
||||||
htmlID := listLayout.htmlID()
|
htmlID := listLayout.htmlID()
|
||||||
isDisabled := IsDisabled(listLayout)
|
isDisabled := IsDisabled(listLayout)
|
||||||
|
|
||||||
count := adapter.ListSize()
|
for i := range adapter.ListSize() {
|
||||||
for i := 0; i < count; i++ {
|
|
||||||
if view := adapter.ListItem(i, session); view != nil {
|
if view := adapter.ListItem(i, session); view != nil {
|
||||||
view.setParentID(htmlID)
|
view.setParentID(htmlID)
|
||||||
if isDisabled {
|
if isDisabled {
|
||||||
|
|
@ -197,30 +198,33 @@ func (listLayout *listLayoutData) UpdateContent() {
|
||||||
if listLayout.created {
|
if listLayout.created {
|
||||||
updateInnerHTML(listLayout.htmlID(), listLayout.session)
|
updateInnerHTML(listLayout.htmlID(), listLayout.session)
|
||||||
}
|
}
|
||||||
|
listLayout.runChangeListener(Content)
|
||||||
if listener, ok := listLayout.changeListener[Content]; ok {
|
|
||||||
listener(listLayout, Content)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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)
|
// 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 {
|
func GetListVerticalAlign(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, VerticalAlign, TopAlign, false)
|
return enumStyledProperty(view, subviewID, VerticalAlign, TopAlign, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetListHorizontalAlign returns the vertical align of a ListLayout or ListView subview:
|
// GetListHorizontalAlign returns the vertical align of a ListLayout or ListView subview:
|
||||||
// LeftAlign (0), RightAlign (1), CenterAlign (2), or StretchAlign (3)
|
// 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 {
|
func GetListHorizontalAlign(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, HorizontalAlign, LeftAlign, false)
|
return enumStyledProperty(view, subviewID, HorizontalAlign, LeftAlign, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetListOrientation returns the orientation of a ListLayout or ListView subview:
|
// GetListOrientation returns the orientation of a ListLayout or ListView subview:
|
||||||
// TopDownOrientation (0), StartToEndOrientation (1), BottomUpOrientation (2), or EndToStartOrientation (3)
|
// 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 {
|
func GetListOrientation(view View, subviewID ...string) int {
|
||||||
if view = getSubview(view, subviewID); view != nil {
|
if view = getSubview(view, subviewID); view != nil {
|
||||||
if orientation, ok := valueToOrientation(view.Get(Orientation), view.Session()); ok {
|
if orientation, ok := valueToOrientation(view.Get(Orientation), view.Session()); ok {
|
||||||
|
|
@ -234,24 +238,30 @@ func GetListOrientation(view View, subviewID ...string) int {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return TopDownOrientation
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetListWrap returns the wrap type of a ListLayout or ListView subview:
|
// GetListWrap returns the wrap type of a ListLayout or ListView subview:
|
||||||
// ListWrapOff (0), ListWrapOn (1), or ListWrapReverse (2)
|
// 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 {
|
func GetListWrap(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, ListWrap, ListWrapOff, false)
|
return enumStyledProperty(view, subviewID, ListWrap, ListWrapOff, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetListRowGap returns the gap between ListLayout or ListView rows.
|
// 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 {
|
func GetListRowGap(view View, subviewID ...string) SizeUnit {
|
||||||
return sizeStyledProperty(view, subviewID, ListRowGap, false)
|
return sizeStyledProperty(view, subviewID, ListRowGap, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetListColumnGap returns the gap between ListLayout or ListView columns.
|
// 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 {
|
func GetListColumnGap(view View, subviewID ...string) SizeUnit {
|
||||||
return sizeStyledProperty(view, subviewID, ListColumnGap, false)
|
return sizeStyledProperty(view, subviewID, ListColumnGap, false)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
182
listView.go
182
listView.go
|
|
@ -122,19 +122,13 @@ type ListView interface {
|
||||||
// ReloadListViewData updates ListView content
|
// ReloadListViewData updates ListView content
|
||||||
ReloadListViewData()
|
ReloadListViewData()
|
||||||
|
|
||||||
//getCheckedItems() []int
|
|
||||||
getItemFrames() []Frame
|
getItemFrames() []Frame
|
||||||
}
|
}
|
||||||
|
|
||||||
type listViewData struct {
|
type listViewData struct {
|
||||||
viewData
|
viewData
|
||||||
//adapter ListAdapter
|
|
||||||
//clickedListeners []func(ListView, int)
|
|
||||||
//selectedListeners []func(ListView, int)
|
|
||||||
//checkedListeners []func(ListView, []int)
|
|
||||||
items []View
|
items []View
|
||||||
itemFrame []Frame
|
itemFrame []Frame
|
||||||
//checkedItem []int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewListView creates the new list view
|
// 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 {
|
if listeners := getOneArgEventListeners[ListView, int](listView, nil, ListItemSelectedEvent); len(listeners) > 0 {
|
||||||
current := GetCurrent(listView)
|
current := GetCurrent(listView)
|
||||||
for _, listener := range listeners {
|
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 {
|
if listeners := getOneArgEventListeners[ListView, []int](listView, nil, ListItemCheckedEvent); len(listeners) > 0 {
|
||||||
checked := GetListViewCheckedItems(listView)
|
checked := GetListViewCheckedItems(listView)
|
||||||
for _, listener := range listeners {
|
for _, listener := range listeners {
|
||||||
listener(listView, checked)
|
listener.Run(listView, checked)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -316,6 +310,18 @@ func (listView *listViewData) getFunc(tag PropertyName) any {
|
||||||
return rowGap
|
return rowGap
|
||||||
}
|
}
|
||||||
return AutoSize()
|
return AutoSize()
|
||||||
|
|
||||||
|
case ListItemClickedEvent, ListItemSelectedEvent:
|
||||||
|
if listeners := getOneArgEventRawListeners[ListView, int](listView, nil, tag); len(listeners) > 0 {
|
||||||
|
return listeners
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case ListItemCheckedEvent:
|
||||||
|
if listeners := getOneArgEventRawListeners[ListView, []int](listView, nil, tag); len(listeners) > 0 {
|
||||||
|
return listeners
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
return listView.viewData.getFunc(tag)
|
return listView.viewData.getFunc(tag)
|
||||||
}
|
}
|
||||||
|
|
@ -342,7 +348,7 @@ func (listView *listViewData) setItems(value any) []PropertyName {
|
||||||
items := make([]View, len(value))
|
items := make([]View, len(value))
|
||||||
for i, val := range value {
|
for i, val := range value {
|
||||||
if val.IsObject() {
|
if val.IsObject() {
|
||||||
if view := CreateViewFromObject(session, val.Object()); view != nil {
|
if view := CreateViewFromObject(session, val.Object(), nil); view != nil {
|
||||||
items[i] = view
|
items[i] = view
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -415,6 +421,11 @@ func (listView *listViewData) getAdapter() ListAdapter {
|
||||||
return adapter
|
return adapter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if obj := listView.binding(); obj != nil {
|
||||||
|
if adapter, ok := obj.(ListAdapter); ok {
|
||||||
|
return adapter
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -428,7 +439,7 @@ func (listView *listViewData) ReloadListViewData() {
|
||||||
listView.itemFrame = make([]Frame, itemCount)
|
listView.itemFrame = make([]Frame, itemCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < itemCount; i++ {
|
for i := range itemCount {
|
||||||
listView.items[i] = adapter.ListItem(i, listView.Session())
|
listView.items[i] = adapter.ListItem(i, listView.Session())
|
||||||
}
|
}
|
||||||
} else if len(listView.items) > 0 {
|
} else if len(listView.items) > 0 {
|
||||||
|
|
@ -540,7 +551,7 @@ func (listView *listViewData) getDivs(checkbox, hCheckboxAlign, vCheckboxAlign i
|
||||||
return onDivBuilder.String(), offDivBuilder.String(), contentBuilder.String()
|
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()
|
itemStyleBuilder := allocStringBuilder()
|
||||||
defer freeStringBuilder(itemStyleBuilder)
|
defer freeStringBuilder(itemStyleBuilder)
|
||||||
|
|
||||||
|
|
@ -627,12 +638,12 @@ func (listView *listViewData) checkboxSubviews(adapter ListAdapter, buffer *stri
|
||||||
hCheckboxAlign := GetListViewCheckboxHorizontalAlign(listView)
|
hCheckboxAlign := GetListViewCheckboxHorizontalAlign(listView)
|
||||||
vCheckboxAlign := GetListViewCheckboxVerticalAlign(listView)
|
vCheckboxAlign := GetListViewCheckboxVerticalAlign(listView)
|
||||||
|
|
||||||
itemDiv := listView.checkboxItemDiv(checkbox, hCheckboxAlign, vCheckboxAlign)
|
itemDiv := listView.checkboxItemDiv(hCheckboxAlign, vCheckboxAlign)
|
||||||
onDiv, offDiv, contentDiv := listView.getDivs(checkbox, hCheckboxAlign, vCheckboxAlign)
|
onDiv, offDiv, contentDiv := listView.getDivs(checkbox, hCheckboxAlign, vCheckboxAlign)
|
||||||
|
|
||||||
current := GetCurrent(listView)
|
current := GetCurrent(listView)
|
||||||
checkedItems := GetListViewCheckedItems(listView)
|
checkedItems := GetListViewCheckedItems(listView)
|
||||||
for i := 0; i < count; i++ {
|
for i := range count {
|
||||||
buffer.WriteString(`<div id="`)
|
buffer.WriteString(`<div id="`)
|
||||||
buffer.WriteString(listViewID)
|
buffer.WriteString(listViewID)
|
||||||
buffer.WriteRune('-')
|
buffer.WriteRune('-')
|
||||||
|
|
@ -693,7 +704,7 @@ func (listView *listViewData) noneCheckboxSubviews(adapter ListAdapter, buffer *
|
||||||
itemStyle := itemStyleBuilder.String()
|
itemStyle := itemStyleBuilder.String()
|
||||||
|
|
||||||
current := GetCurrent(listView)
|
current := GetCurrent(listView)
|
||||||
for i := 0; i < count; i++ {
|
for i := range count {
|
||||||
buffer.WriteString(`<div id="`)
|
buffer.WriteString(`<div id="`)
|
||||||
buffer.WriteString(listViewID)
|
buffer.WriteString(listViewID)
|
||||||
buffer.WriteRune('-')
|
buffer.WriteRune('-')
|
||||||
|
|
@ -734,7 +745,7 @@ func (listView *listViewData) updateCheckboxItem(index int, checked bool) {
|
||||||
buffer := allocStringBuilder()
|
buffer := allocStringBuilder()
|
||||||
defer freeStringBuilder(buffer)
|
defer freeStringBuilder(buffer)
|
||||||
|
|
||||||
buffer.WriteString(listView.checkboxItemDiv(checkbox, hCheckboxAlign, vCheckboxAlign))
|
buffer.WriteString(listView.checkboxItemDiv(hCheckboxAlign, vCheckboxAlign))
|
||||||
if checked {
|
if checked {
|
||||||
buffer.WriteString(onDiv)
|
buffer.WriteString(onDiv)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -791,21 +802,7 @@ func (listView *listViewData) cssStyle(self View, builder cssBuilder) {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func (listView *listViewData) htmlSubviews(self View, buffer *strings.Builder) {
|
func listDiv(listView View, buffer *strings.Builder) {
|
||||||
adapter := listView.getAdapter()
|
|
||||||
if adapter == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if listSize := adapter.ListSize(); listSize == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !listView.session.ignoreViewUpdates() {
|
|
||||||
listView.session.setIgnoreViewUpdates(true)
|
|
||||||
defer listView.session.setIgnoreViewUpdates(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer.WriteString(`<div style="display: flex; align-content: stretch;`)
|
buffer.WriteString(`<div style="display: flex; align-content: stretch;`)
|
||||||
|
|
||||||
if gap := GetListRowGap(listView); gap.Type != Auto {
|
if gap := GetListRowGap(listView); gap.Type != Auto {
|
||||||
|
|
@ -928,6 +925,24 @@ func (listView *listViewData) htmlSubviews(self View, buffer *strings.Builder) {
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.WriteString(`">`)
|
buffer.WriteString(`">`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (listView *listViewData) htmlSubviews(self View, buffer *strings.Builder) {
|
||||||
|
adapter := listView.getAdapter()
|
||||||
|
if adapter == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if listSize := adapter.ListSize(); listSize == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !listView.session.ignoreViewUpdates() {
|
||||||
|
listView.session.setIgnoreViewUpdates(true)
|
||||||
|
defer listView.session.setIgnoreViewUpdates(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
listDiv(listView, buffer)
|
||||||
|
|
||||||
checkbox := GetListViewCheckbox(listView)
|
checkbox := GetListViewCheckbox(listView)
|
||||||
if checkbox == NoneCheckbox {
|
if checkbox == NoneCheckbox {
|
||||||
|
|
@ -966,11 +981,9 @@ func (listView *listViewData) handleCommand(self View, command PropertyName, dat
|
||||||
func (listView *listViewData) handleCurrent(number int) {
|
func (listView *listViewData) handleCurrent(number int) {
|
||||||
listView.properties[Current] = number
|
listView.properties[Current] = number
|
||||||
for _, listener := range getOneArgEventListeners[ListView, int](listView, nil, ListItemSelectedEvent) {
|
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)
|
|
||||||
}
|
}
|
||||||
|
listView.runChangeListener(Current)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (listView *listViewData) onItemClick(number int) {
|
func (listView *listViewData) onItemClick(number int) {
|
||||||
|
|
@ -1027,17 +1040,15 @@ func (listView *listViewData) onItemClick(number int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
setArrayPropertyValue(listView, Checked, checkedItem)
|
setArrayPropertyValue(listView, Checked, checkedItem)
|
||||||
if listener, ok := listView.changeListener[Checked]; ok {
|
listView.runChangeListener(Checked)
|
||||||
listener(listView, Checked)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, listener := range getOneArgEventListeners[ListView, []int](listView, nil, ListItemCheckedEvent) {
|
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) {
|
for _, listener := range getOneArgEventListeners[ListView, int](listView, nil, ListItemClickedEvent) {
|
||||||
listener(listView, number)
|
listener.Run(listView, number)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1054,58 +1065,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)
|
// 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 {
|
func GetVerticalAlign(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, VerticalAlign, TopAlign, false)
|
return enumStyledProperty(view, subviewID, VerticalAlign, TopAlign, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetHorizontalAlign return the vertical align of a list/checkbox: LeftAlign (0), RightAlign (1), CenterAlign (2), StretchAlign (3)
|
// 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 {
|
func GetHorizontalAlign(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, HorizontalAlign, LeftAlign, false)
|
return enumStyledProperty(view, subviewID, HorizontalAlign, LeftAlign, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetListItemClickedListeners returns a ListItemClickedListener of the ListView.
|
// GetListItemClickedListeners returns a ListItemClickedListener of the ListView.
|
||||||
// If there are no listeners then the empty list is returned
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[ListView, int](view, subviewID, ListItemClickedEvent)
|
// - 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.
|
// GetListItemSelectedListeners returns a ListItemSelectedListener of the ListView.
|
||||||
// If there are no listeners then the empty list is returned
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[ListView, int](view, subviewID, ListItemSelectedEvent)
|
// - 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.
|
// GetListItemCheckedListeners returns a ListItemCheckedListener of the ListView.
|
||||||
// If there are no listeners then the empty list is returned
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[ListView, []int](view, subviewID, ListItemCheckedEvent)
|
// - 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.
|
// 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 {
|
func GetListItemWidth(view View, subviewID ...string) SizeUnit {
|
||||||
return sizeStyledProperty(view, subviewID, ItemWidth, false)
|
return sizeStyledProperty(view, subviewID, ItemWidth, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetListItemHeight returns the height of a ListView item.
|
// 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 {
|
func GetListItemHeight(view View, subviewID ...string) SizeUnit {
|
||||||
return sizeStyledProperty(view, subviewID, ItemHeight, false)
|
return sizeStyledProperty(view, subviewID, ItemHeight, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetListViewCheckbox returns the ListView checkbox type: NoneCheckbox (0), SingleCheckbox (1), or MultipleCheckbox (2).
|
// 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 {
|
func GetListViewCheckbox(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, ItemCheckbox, 0, false)
|
return enumStyledProperty(view, subviewID, ItemCheckbox, 0, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetListViewCheckedItems returns the array of ListView checked items.
|
// 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 {
|
func GetListViewCheckedItems(view View, subviewID ...string) []int {
|
||||||
if view = getSubview(view, subviewID); view != nil {
|
if view = getSubview(view, subviewID); view != nil {
|
||||||
if value := view.getRaw(Checked); value != nil {
|
if value := view.getRaw(Checked); value != nil {
|
||||||
|
|
@ -1138,34 +1188,44 @@ func IsListViewCheckedItem(view View, subviewID string, index int) bool {
|
||||||
|
|
||||||
// GetListViewCheckboxVerticalAlign returns the vertical align of the ListView checkbox:
|
// GetListViewCheckboxVerticalAlign returns the vertical align of the ListView checkbox:
|
||||||
// TopAlign (0), BottomAlign (1), CenterAlign (2)
|
// 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 {
|
func GetListViewCheckboxVerticalAlign(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, CheckboxVerticalAlign, TopAlign, false)
|
return enumStyledProperty(view, subviewID, CheckboxVerticalAlign, TopAlign, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetListViewCheckboxHorizontalAlign returns the horizontal align of the ListView checkbox:
|
// GetListViewCheckboxHorizontalAlign returns the horizontal align of the ListView checkbox:
|
||||||
// LeftAlign (0), RightAlign (1), CenterAlign (2)
|
// 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 {
|
func GetListViewCheckboxHorizontalAlign(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, CheckboxHorizontalAlign, LeftAlign, false)
|
return enumStyledProperty(view, subviewID, CheckboxHorizontalAlign, LeftAlign, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetListItemVerticalAlign returns the vertical align of the ListView item content:
|
// GetListItemVerticalAlign returns the vertical align of the ListView item content:
|
||||||
// TopAlign (0), BottomAlign (1), CenterAlign (2)
|
// 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 {
|
func GetListItemVerticalAlign(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, ItemVerticalAlign, TopAlign, false)
|
return enumStyledProperty(view, subviewID, ItemVerticalAlign, TopAlign, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ItemHorizontalAlign returns the horizontal align of the ListView item content:
|
// ItemHorizontalAlign returns the horizontal align of the ListView item content:
|
||||||
// LeftAlign (0), RightAlign (1), CenterAlign (2), StretchAlign (3)
|
// 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 {
|
func GetListItemHorizontalAlign(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, ItemHorizontalAlign, LeftAlign, false)
|
return enumStyledProperty(view, subviewID, ItemHorizontalAlign, LeftAlign, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetListItemFrame - returns the location and size of the ListView item in pixels.
|
// 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 {
|
func GetListItemFrame(view View, subviewID string, index int) Frame {
|
||||||
if subviewID != "" {
|
if subviewID != "" {
|
||||||
view = ViewByID(view, subviewID)
|
view = ViewByID(view, subviewID)
|
||||||
|
|
@ -1182,7 +1242,9 @@ func GetListItemFrame(view View, subviewID string, index int) Frame {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetListViewAdapter - returns the ListView adapter.
|
// 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 {
|
func GetListViewAdapter(view View, subviewID ...string) ListAdapter {
|
||||||
if view = getSubview(view, subviewID); view != nil {
|
if view = getSubview(view, subviewID); view != nil {
|
||||||
if value := view.Get(Items); value != nil {
|
if value := view.Get(Items); value != nil {
|
||||||
|
|
|
||||||
454
mediaPlayer.go
454
mediaPlayer.go
|
|
@ -3,6 +3,7 @@ package rui
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
@ -488,7 +489,7 @@ const (
|
||||||
// - player - Interface of a player which generated this event,
|
// - player - Interface of a player which generated this event,
|
||||||
// - code - Error code. See below,
|
// - code - Error code. See below,
|
||||||
// - message - Error message,
|
// - message - Error message,
|
||||||
|
//
|
||||||
// Error codes:
|
// Error codes:
|
||||||
// - 0 (PlayerErrorUnknown) - Unknown error,
|
// - 0 (PlayerErrorUnknown) - Unknown error,
|
||||||
// - 1 (PlayerErrorAborted) - Fetching the associated resource was interrupted by a user request,
|
// - 1 (PlayerErrorAborted) - Fetching the associated resource was interrupted by a user request,
|
||||||
|
|
@ -588,6 +589,7 @@ type MediaSource struct {
|
||||||
func (player *mediaPlayerData) init(session Session) {
|
func (player *mediaPlayerData) init(session Session) {
|
||||||
player.viewData.init(session)
|
player.viewData.init(session)
|
||||||
player.tag = "MediaPlayer"
|
player.tag = "MediaPlayer"
|
||||||
|
player.get = player.getFunc
|
||||||
player.set = player.setFunc
|
player.set = player.setFunc
|
||||||
player.changed = player.propertyChanged
|
player.changed = player.propertyChanged
|
||||||
}
|
}
|
||||||
|
|
@ -596,6 +598,39 @@ func (player *mediaPlayerData) Focusable() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (player *mediaPlayerData) getFunc(tag PropertyName) any {
|
||||||
|
switch tag {
|
||||||
|
case AbortEvent, CanPlayEvent, CanPlayThroughEvent, CompleteEvent, EmptiedEvent, LoadStartEvent,
|
||||||
|
EndedEvent, LoadedDataEvent, LoadedMetadataEvent, PauseEvent, PlayEvent, PlayingEvent,
|
||||||
|
ProgressEvent, SeekedEvent, SeekingEvent, StalledEvent, SuspendEvent, WaitingEvent:
|
||||||
|
|
||||||
|
if listeners := getNoArgEventRawListeners[MediaPlayer](player, nil, tag); len(listeners) > 0 {
|
||||||
|
return listeners
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case DurationChangedEvent, RateChangedEvent, TimeUpdateEvent, VolumeChangedEvent:
|
||||||
|
if listeners := getOneArgEventRawListeners[MediaPlayer, float64](player, nil, tag); len(listeners) > 0 {
|
||||||
|
return listeners
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case PlayerErrorEvent:
|
||||||
|
if value := player.getRaw(tag); value != nil {
|
||||||
|
if listeners, ok := value.([]mediaPlayerErrorListener); ok && len(listeners) > 0 {
|
||||||
|
result := make([]any, 0, len(listeners))
|
||||||
|
for _, listener := range listeners {
|
||||||
|
result = append(result, listener.rawListener())
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return player.viewData.getFunc(tag)
|
||||||
|
}
|
||||||
|
|
||||||
func (player *mediaPlayerData) setFunc(tag PropertyName, value any) []PropertyName {
|
func (player *mediaPlayerData) setFunc(tag PropertyName, value any) []PropertyName {
|
||||||
switch tag {
|
switch tag {
|
||||||
|
|
||||||
|
|
@ -610,7 +645,7 @@ func (player *mediaPlayerData) setFunc(tag PropertyName, value any) []PropertyNa
|
||||||
return setOneArgEventListener[MediaPlayer, float64](player, tag, value)
|
return setOneArgEventListener[MediaPlayer, float64](player, tag, value)
|
||||||
|
|
||||||
case PlayerErrorEvent:
|
case PlayerErrorEvent:
|
||||||
if listeners, ok := valueToPlayerErrorListeners(value); ok {
|
if listeners, ok := valueToMediaPlayerErrorListeners(value); ok {
|
||||||
return setArrayPropertyValue(player, tag, listeners)
|
return setArrayPropertyValue(player, tag, listeners)
|
||||||
}
|
}
|
||||||
notCompatibleType(tag, value)
|
notCompatibleType(tag, value)
|
||||||
|
|
@ -677,6 +712,7 @@ func setMediaPlayerSource(properties Properties, value any) []PropertyName {
|
||||||
return []PropertyName{Source}
|
return []PropertyName{Source}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
func valueToPlayerErrorListeners(value any) ([]func(MediaPlayer, int, string), bool) {
|
func valueToPlayerErrorListeners(value any) ([]func(MediaPlayer, int, string), bool) {
|
||||||
if value == nil {
|
if value == nil {
|
||||||
return nil, true
|
return nil, true
|
||||||
|
|
@ -801,7 +837,7 @@ func valueToPlayerErrorListeners(value any) ([]func(MediaPlayer, int, string), b
|
||||||
|
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
func mediaPlayerEvents() map[PropertyName]string {
|
func mediaPlayerEvents() map[PropertyName]string {
|
||||||
return map[PropertyName]string{
|
return map[PropertyName]string{
|
||||||
AbortEvent: "onabort",
|
AbortEvent: "onabort",
|
||||||
|
|
@ -943,7 +979,6 @@ func (player *mediaPlayerData) htmlProperties(self View, buffer *strings.Builder
|
||||||
|
|
||||||
for tag, cssTag := range mediaPlayerEvents() {
|
for tag, cssTag := range mediaPlayerEvents() {
|
||||||
if value := player.getRaw(tag); value != nil {
|
if value := player.getRaw(tag); value != nil {
|
||||||
if listeners, ok := value.([]func(MediaPlayer)); ok && len(listeners) > 0 {
|
|
||||||
buffer.WriteString(` `)
|
buffer.WriteString(` `)
|
||||||
buffer.WriteString(cssTag)
|
buffer.WriteString(cssTag)
|
||||||
buffer.WriteString(`="playerEvent(this, '`)
|
buffer.WriteString(`="playerEvent(this, '`)
|
||||||
|
|
@ -951,7 +986,6 @@ func (player *mediaPlayerData) htmlProperties(self View, buffer *strings.Builder
|
||||||
buffer.WriteString(`')"`)
|
buffer.WriteString(`')"`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if value := player.getRaw(TimeUpdateEvent); value != nil {
|
if value := player.getRaw(TimeUpdateEvent); value != nil {
|
||||||
buffer.WriteString(` ontimeupdate="playerTimeUpdatedEvent(this)"`)
|
buffer.WriteString(` ontimeupdate="playerTimeUpdatedEvent(this)"`)
|
||||||
|
|
@ -981,31 +1015,23 @@ func (player *mediaPlayerData) handleCommand(self View, command PropertyName, da
|
||||||
PlayingEvent, ProgressEvent, SeekedEvent, SeekingEvent, StalledEvent, SuspendEvent,
|
PlayingEvent, ProgressEvent, SeekedEvent, SeekingEvent, StalledEvent, SuspendEvent,
|
||||||
WaitingEvent:
|
WaitingEvent:
|
||||||
|
|
||||||
if value := player.getRaw(command); value != nil {
|
for _, listener := range getNoArgEventListeners[MediaPlayer](player, nil, command) {
|
||||||
if listeners, ok := value.([]func(MediaPlayer)); ok {
|
listener.Run(player)
|
||||||
for _, listener := range listeners {
|
|
||||||
listener(player)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case TimeUpdateEvent, DurationChangedEvent, RateChangedEvent, VolumeChangedEvent:
|
case TimeUpdateEvent, DurationChangedEvent, RateChangedEvent, VolumeChangedEvent:
|
||||||
if value := player.getRaw(command); value != nil {
|
|
||||||
if listeners, ok := value.([]func(MediaPlayer, float64)); ok {
|
|
||||||
time := dataFloatProperty(data, "value")
|
time := dataFloatProperty(data, "value")
|
||||||
for _, listener := range listeners {
|
for _, listener := range getOneArgEventListeners[MediaPlayer, float64](player, nil, command) {
|
||||||
listener(player, time)
|
listener.Run(player, time)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case PlayerErrorEvent:
|
case PlayerErrorEvent:
|
||||||
if value := player.getRaw(command); value != nil {
|
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")
|
code, _ := dataIntProperty(data, "code")
|
||||||
message, _ := data.PropertyValue("message")
|
message, _ := data.PropertyValue("message")
|
||||||
for _, listener := range listeners {
|
for _, listener := range listeners {
|
||||||
listener(player, code, message)
|
listener.Run(player, code, message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1254,3 +1280,393 @@ func IsMediaPlayerPaused(view View, playerID string) bool {
|
||||||
ErrorLog(`The found View is not MediaPlayer`)
|
ErrorLog(`The found View is not MediaPlayer`)
|
||||||
return false
|
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)
|
event.init(data)
|
||||||
|
|
||||||
for _, listener := range listeners {
|
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.
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[View, MouseEvent](view, subviewID, ClickEvent)
|
// - 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.
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[View, MouseEvent](view, subviewID, DoubleClickEvent)
|
// - 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.
|
// GetContextMenuListeners returns the "context-menu" listener list.
|
||||||
// If there are no listeners then the empty list is returned.
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[View, MouseEvent](view, subviewID, ContextMenuEvent)
|
// - 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.
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[View, MouseEvent](view, subviewID, MouseDown)
|
// - 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.
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[View, MouseEvent](view, subviewID, MouseUp)
|
// - 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.
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[View, MouseEvent](view, subviewID, MouseMove)
|
// - 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.
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[View, MouseEvent](view, subviewID, MouseOver)
|
// - 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.
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[View, MouseEvent](view, subviewID, MouseOut)
|
// - 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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,7 @@ func (picker *numberPickerData) init(session Session) {
|
||||||
picker.tag = "NumberPicker"
|
picker.tag = "NumberPicker"
|
||||||
picker.hasHtmlDisabled = true
|
picker.hasHtmlDisabled = true
|
||||||
picker.normalize = normalizeNumberPickerTag
|
picker.normalize = normalizeNumberPickerTag
|
||||||
|
picker.get = picker.getFunc
|
||||||
picker.set = picker.setFunc
|
picker.set = picker.setFunc
|
||||||
picker.changed = picker.propertyChanged
|
picker.changed = picker.propertyChanged
|
||||||
}
|
}
|
||||||
|
|
@ -147,6 +148,17 @@ func normalizeNumberPickerTag(tag PropertyName) PropertyName {
|
||||||
return normalizeDataListTag(tag)
|
return normalizeDataListTag(tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (picker *numberPickerData) getFunc(tag PropertyName) any {
|
||||||
|
switch tag {
|
||||||
|
case NumberChangedEvent:
|
||||||
|
if listeners := getTwoArgEventRawListeners[NumberPicker, float64](picker, nil, tag); len(listeners) > 0 {
|
||||||
|
return listeners
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return picker.viewData.getFunc(tag)
|
||||||
|
}
|
||||||
|
|
||||||
func (picker *numberPickerData) setFunc(tag PropertyName, value any) []PropertyName {
|
func (picker *numberPickerData) setFunc(tag PropertyName, value any) []PropertyName {
|
||||||
switch tag {
|
switch tag {
|
||||||
case NumberChangedEvent:
|
case NumberChangedEvent:
|
||||||
|
|
@ -166,8 +178,8 @@ func (picker *numberPickerData) setFunc(tag PropertyName, value any) []PropertyN
|
||||||
}
|
}
|
||||||
|
|
||||||
func (picker *numberPickerData) numberFormat() string {
|
func (picker *numberPickerData) numberFormat() string {
|
||||||
if precission := GetNumberPickerPrecision(picker); precission > 0 {
|
if precision := GetNumberPickerPrecision(picker); precision > 0 {
|
||||||
return fmt.Sprintf("%%.%df", precission)
|
return fmt.Sprintf("%%.%df", precision)
|
||||||
}
|
}
|
||||||
return "%g"
|
return "%g"
|
||||||
}
|
}
|
||||||
|
|
@ -201,7 +213,7 @@ func (picker *numberPickerData) propertyChanged(tag PropertyName) {
|
||||||
format := picker.numberFormat()
|
format := picker.numberFormat()
|
||||||
picker.Session().callFunc("setInputValue", picker.htmlID(), fmt.Sprintf(format, value))
|
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
|
old := 0.0
|
||||||
if val := picker.getRaw("old-number"); val != nil {
|
if val := picker.getRaw("old-number"); val != nil {
|
||||||
if n, ok := val.(float64); ok {
|
if n, ok := val.(float64); ok {
|
||||||
|
|
@ -210,7 +222,7 @@ func (picker *numberPickerData) propertyChanged(tag PropertyName) {
|
||||||
}
|
}
|
||||||
if old != value {
|
if old != value {
|
||||||
for _, listener := range listeners {
|
for _, listener := range listeners {
|
||||||
listener(picker, value, old)
|
listener.Run(picker, value, old)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -244,27 +256,27 @@ func (picker *numberPickerData) htmlProperties(self View, buffer *strings.Builde
|
||||||
min, max := GetNumberPickerMinMax(picker)
|
min, max := GetNumberPickerMinMax(picker)
|
||||||
if min != math.Inf(-1) {
|
if min != math.Inf(-1) {
|
||||||
buffer.WriteString(` min="`)
|
buffer.WriteString(` min="`)
|
||||||
buffer.WriteString(fmt.Sprintf(format, min))
|
fmt.Fprintf(buffer, format, min)
|
||||||
buffer.WriteByte('"')
|
buffer.WriteByte('"')
|
||||||
}
|
}
|
||||||
|
|
||||||
if max != math.Inf(1) {
|
if max != math.Inf(1) {
|
||||||
buffer.WriteString(` max="`)
|
buffer.WriteString(` max="`)
|
||||||
buffer.WriteString(fmt.Sprintf(format, max))
|
fmt.Fprintf(buffer, format, max)
|
||||||
buffer.WriteByte('"')
|
buffer.WriteByte('"')
|
||||||
}
|
}
|
||||||
|
|
||||||
step := GetNumberPickerStep(picker)
|
step := GetNumberPickerStep(picker)
|
||||||
if step != 0 {
|
if step != 0 {
|
||||||
buffer.WriteString(` step="`)
|
buffer.WriteString(` step="`)
|
||||||
buffer.WriteString(fmt.Sprintf(format, step))
|
fmt.Fprintf(buffer, format, step)
|
||||||
buffer.WriteByte('"')
|
buffer.WriteByte('"')
|
||||||
} else {
|
} else {
|
||||||
buffer.WriteString(` step="any"`)
|
buffer.WriteString(` step="any"`)
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.WriteString(` value="`)
|
buffer.WriteString(` value="`)
|
||||||
buffer.WriteString(fmt.Sprintf(format, GetNumberPickerValue(picker)))
|
fmt.Fprintf(buffer, format, GetNumberPickerValue(picker))
|
||||||
buffer.WriteByte('"')
|
buffer.WriteByte('"')
|
||||||
|
|
||||||
buffer.WriteString(` oninput="editViewInputEvent(this)"`)
|
buffer.WriteString(` oninput="editViewInputEvent(this)"`)
|
||||||
|
|
@ -280,12 +292,10 @@ func (picker *numberPickerData) handleCommand(self View, command PropertyName, d
|
||||||
oldValue := GetNumberPickerValue(picker)
|
oldValue := GetNumberPickerValue(picker)
|
||||||
picker.properties[NumberPickerValue] = text
|
picker.properties[NumberPickerValue] = text
|
||||||
if value != oldValue {
|
if value != oldValue {
|
||||||
for _, listener := range GetNumberChangedListeners(picker) {
|
for _, listener := range getTwoArgEventListeners[NumberPicker, float64](picker, nil, NumberChangedEvent) {
|
||||||
listener(picker, value, oldValue)
|
listener.Run(picker, value, oldValue)
|
||||||
}
|
|
||||||
if listener, ok := picker.changeListener[NumberPickerValue]; ok {
|
|
||||||
listener(picker, NumberPickerValue)
|
|
||||||
}
|
}
|
||||||
|
picker.runChangeListener(NumberPickerValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -298,13 +308,17 @@ func (picker *numberPickerData) handleCommand(self View, command PropertyName, d
|
||||||
// GetNumberPickerType returns the type of NumberPicker subview. Valid values:
|
// GetNumberPickerType returns the type of NumberPicker subview. Valid values:
|
||||||
// NumberEditor (0) - NumberPicker is presented by editor (default type);
|
// NumberEditor (0) - NumberPicker is presented by editor (default type);
|
||||||
// NumberSlider (1) - NumberPicker is presented by slider.
|
// 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 {
|
func GetNumberPickerType(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, NumberPickerType, NumberEditor, false)
|
return enumStyledProperty(view, subviewID, NumberPickerType, NumberEditor, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNumberPickerMinMax returns the min and max value of NumberPicker subview.
|
// 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) {
|
func GetNumberPickerMinMax(view View, subviewID ...string) (float64, float64) {
|
||||||
view = getSubview(view, subviewID)
|
view = getSubview(view, subviewID)
|
||||||
pickerType := GetNumberPickerType(view)
|
pickerType := GetNumberPickerType(view)
|
||||||
|
|
@ -328,7 +342,9 @@ func GetNumberPickerMinMax(view View, subviewID ...string) (float64, float64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNumberPickerStep returns the value changing step of NumberPicker subview.
|
// 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 {
|
func GetNumberPickerStep(view View, subviewID ...string) float64 {
|
||||||
view = getSubview(view, subviewID)
|
view = getSubview(view, subviewID)
|
||||||
_, max := GetNumberPickerMinMax(view)
|
_, max := GetNumberPickerMinMax(view)
|
||||||
|
|
@ -341,7 +357,9 @@ func GetNumberPickerStep(view View, subviewID ...string) float64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNumberPickerValue returns the value of NumberPicker subview.
|
// 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 {
|
func GetNumberPickerValue(view View, subviewID ...string) float64 {
|
||||||
view = getSubview(view, subviewID)
|
view = getSubview(view, subviewID)
|
||||||
min, _ := GetNumberPickerMinMax(view)
|
min, _ := GetNumberPickerMinMax(view)
|
||||||
|
|
@ -350,13 +368,26 @@ func GetNumberPickerValue(view View, subviewID ...string) float64 {
|
||||||
|
|
||||||
// GetNumberChangedListeners returns the NumberChangedListener list of an NumberPicker subview.
|
// GetNumberChangedListeners returns the NumberChangedListener list of an NumberPicker subview.
|
||||||
// If there are no listeners then the empty list is returned
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getTwoArgEventListeners[NumberPicker, float64](view, subviewID, NumberChangedEvent)
|
// - 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.
|
// 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 {
|
func GetNumberPickerPrecision(view View, subviewID ...string) int {
|
||||||
return intStyledProperty(view, subviewID, NumberPickerPrecision, 0)
|
return intStyledProperty(view, subviewID, NumberPickerPrecision, 0)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
20
outline.go
20
outline.go
|
|
@ -34,30 +34,12 @@ func NewOutlineProperty(params Params) OutlineProperty {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (outline *outlinePropertyData) init() {
|
func (outline *outlinePropertyData) init() {
|
||||||
outline.propertyList.init()
|
outline.dataProperty.init()
|
||||||
outline.normalize = normalizeOutlineTag
|
outline.normalize = normalizeOutlineTag
|
||||||
outline.set = outlineSet
|
outline.set = outlineSet
|
||||||
outline.supportedProperties = []PropertyName{Style, Width, ColorTag}
|
outline.supportedProperties = []PropertyName{Style, Width, ColorTag}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (outline *outlinePropertyData) writeString(buffer *strings.Builder, indent string) {
|
|
||||||
buffer.WriteString("_{ ")
|
|
||||||
comma := false
|
|
||||||
for _, tag := range []PropertyName{Style, Width, ColorTag} {
|
|
||||||
if value, ok := outline.properties[tag]; ok {
|
|
||||||
if comma {
|
|
||||||
buffer.WriteString(", ")
|
|
||||||
}
|
|
||||||
buffer.WriteString(string(tag))
|
|
||||||
buffer.WriteString(" = ")
|
|
||||||
writePropertyValue(buffer, BorderStyle, value, indent)
|
|
||||||
comma = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer.WriteString(" }")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (outline *outlinePropertyData) String() string {
|
func (outline *outlinePropertyData) String() string {
|
||||||
return runStringWriter(outline)
|
return runStringWriter(outline)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
21
params.go
21
params.go
|
|
@ -1,6 +1,9 @@
|
||||||
package rui
|
package rui
|
||||||
|
|
||||||
import "sort"
|
import (
|
||||||
|
"iter"
|
||||||
|
"slices"
|
||||||
|
)
|
||||||
|
|
||||||
// Params defines a type of a parameters list
|
// Params defines a type of a parameters list
|
||||||
type Params map[PropertyName]any
|
type Params map[PropertyName]any
|
||||||
|
|
@ -45,18 +48,26 @@ func (params Params) Clear() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (params Params) All() iter.Seq2[PropertyName, any] {
|
||||||
|
return func(yield func(PropertyName, any) bool) {
|
||||||
|
for tag, value := range params {
|
||||||
|
if !yield(tag, value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// AllTags returns a sorted slice of all properties.
|
// AllTags returns a sorted slice of all properties.
|
||||||
func (params Params) AllTags() []PropertyName {
|
func (params Params) AllTags() []PropertyName {
|
||||||
tags := make([]PropertyName, 0, len(params))
|
tags := make([]PropertyName, 0, len(params))
|
||||||
for t := range params {
|
for t := range params {
|
||||||
tags = append(tags, t)
|
tags = append(tags, t)
|
||||||
}
|
}
|
||||||
sort.Slice(tags, func(i, j int) bool {
|
slices.Sort(tags)
|
||||||
return tags[i] < tags[j]
|
|
||||||
})
|
|
||||||
return tags
|
return tags
|
||||||
}
|
}
|
||||||
|
|
||||||
func (params Params) empty() bool {
|
func (params Params) IsEmpty() bool {
|
||||||
return len(params) == 0
|
return len(params) == 0
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -192,42 +192,96 @@ func handlePointerEvents(view View, tag PropertyName, data DataObject) {
|
||||||
event.init(data)
|
event.init(data)
|
||||||
|
|
||||||
for _, listener := range listeners {
|
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.
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[View, PointerEvent](view, subviewID, PointerDown)
|
// - 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.
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[View, PointerEvent](view, subviewID, PointerUp)
|
// - 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.
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[View, PointerEvent](view, subviewID, PointerMove)
|
// - 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.
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[View, PointerEvent](view, subviewID, PointerCancel)
|
// - 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.
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[View, PointerEvent](view, subviewID, PointerOver)
|
// - 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.
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[View, PointerEvent](view, subviewID, PointerOut)
|
// - 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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -101,13 +101,17 @@ func (progress *progressBarData) htmlProperties(self View, buffer *strings.Build
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetProgressBarMax returns the max value of ProgressBar subview.
|
// 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 {
|
func GetProgressBarMax(view View, subviewID ...string) float64 {
|
||||||
return floatStyledProperty(view, subviewID, ProgressBarMax, 1)
|
return floatStyledProperty(view, subviewID, ProgressBarMax, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetProgressBarValue returns the value of ProgressBar subview.
|
// 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 {
|
func GetProgressBarValue(view View, subviewID ...string) float64 {
|
||||||
return floatStyledProperty(view, subviewID, ProgressBarValue, 0)
|
return floatStyledProperty(view, subviewID, ProgressBarValue, 0)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
package rui
|
package rui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sort"
|
"iter"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -24,18 +25,18 @@ type Properties interface {
|
||||||
// Clear removes all properties
|
// Clear removes all properties
|
||||||
Clear()
|
Clear()
|
||||||
|
|
||||||
|
// All returns an iterator to access the properties
|
||||||
|
All() iter.Seq2[PropertyName, any]
|
||||||
|
|
||||||
// AllTags returns an array of the set properties
|
// AllTags returns an array of the set properties
|
||||||
AllTags() []PropertyName
|
AllTags() []PropertyName
|
||||||
|
|
||||||
empty() bool
|
IsEmpty() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type propertyList struct {
|
type propertyList struct {
|
||||||
properties map[PropertyName]any
|
properties map[PropertyName]any
|
||||||
normalize func(PropertyName) PropertyName
|
normalize func(PropertyName) PropertyName
|
||||||
//getFunc func(PropertyName) any
|
|
||||||
//set func(Properties, PropertyName, any) []PropertyName
|
|
||||||
//remove func(Properties, PropertyName) []PropertyName
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type dataProperty struct {
|
type dataProperty struct {
|
||||||
|
|
@ -58,7 +59,7 @@ func (properties *propertyList) init() {
|
||||||
//properties.remove = propertiesRemove
|
//properties.remove = propertiesRemove
|
||||||
}
|
}
|
||||||
|
|
||||||
func (properties *propertyList) empty() bool {
|
func (properties *propertyList) IsEmpty() bool {
|
||||||
return len(properties.properties) == 0
|
return len(properties.properties) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,17 +87,26 @@ func (properties *propertyList) Clear() {
|
||||||
properties.properties = map[PropertyName]any{}
|
properties.properties = map[PropertyName]any{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (properties *propertyList) All() iter.Seq2[PropertyName, any] {
|
||||||
|
return func(yield func(PropertyName, any) bool) {
|
||||||
|
for tag, value := range properties.properties {
|
||||||
|
if !yield(tag, value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (properties *propertyList) AllTags() []PropertyName {
|
func (properties *propertyList) AllTags() []PropertyName {
|
||||||
tags := make([]PropertyName, 0, len(properties.properties))
|
tags := make([]PropertyName, 0, len(properties.properties))
|
||||||
for tag := range properties.properties {
|
for tag := range properties.properties {
|
||||||
tags = append(tags, tag)
|
tags = append(tags, tag)
|
||||||
}
|
}
|
||||||
sort.Slice(tags, func(i, j int) bool {
|
slices.Sort(tags)
|
||||||
return tags[i] < tags[j]
|
|
||||||
})
|
|
||||||
return tags
|
return tags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
func (properties *propertyList) writeToBuffer(buffer *strings.Builder,
|
func (properties *propertyList) writeToBuffer(buffer *strings.Builder,
|
||||||
indent string, objectTag string, tags []PropertyName) {
|
indent string, objectTag string, tags []PropertyName) {
|
||||||
|
|
||||||
|
|
@ -107,22 +117,24 @@ func (properties *propertyList) writeToBuffer(buffer *strings.Builder,
|
||||||
|
|
||||||
for _, tag := range tags {
|
for _, tag := range tags {
|
||||||
if value, ok := properties.properties[tag]; ok {
|
if value, ok := properties.properties[tag]; ok {
|
||||||
|
text := propertyValueToString(tag, value, indent2)
|
||||||
|
if text != "" {
|
||||||
buffer.WriteString(indent2)
|
buffer.WriteString(indent2)
|
||||||
buffer.WriteString(string(tag))
|
buffer.WriteString(string(tag))
|
||||||
buffer.WriteString(" = ")
|
buffer.WriteString(" = ")
|
||||||
writePropertyValue(buffer, tag, value, indent2)
|
buffer.WriteString(text)
|
||||||
buffer.WriteString(",\n")
|
buffer.WriteString(",\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buffer.WriteString(indent)
|
buffer.WriteString(indent)
|
||||||
buffer.WriteString("}")
|
buffer.WriteString("}")
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
func parseProperties(properties Properties, object DataObject) {
|
func parseProperties(properties Properties, object DataObject) {
|
||||||
count := object.PropertyCount()
|
for node := range object.Properties() {
|
||||||
for i := 0; i < count; i++ {
|
|
||||||
if node := object.Property(i); node != nil {
|
|
||||||
switch node.Type() {
|
switch node.Type() {
|
||||||
case TextNode:
|
case TextNode:
|
||||||
properties.Set(PropertyName(node.Tag()), node.Text())
|
properties.Set(PropertyName(node.Tag()), node.Text())
|
||||||
|
|
@ -131,7 +143,19 @@ func parseProperties(properties Properties, object DataObject) {
|
||||||
properties.Set(PropertyName(node.Tag()), node.Object())
|
properties.Set(PropertyName(node.Tag()), node.Object())
|
||||||
|
|
||||||
case ArrayNode:
|
case ArrayNode:
|
||||||
properties.Set(PropertyName(node.Tag()), node.ArrayElements())
|
switch node.ArraySize() {
|
||||||
|
case 0:
|
||||||
|
// do nothing
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
if v := node.ArrayElement(0); v.IsObject() {
|
||||||
|
properties.Set(PropertyName(node.Tag()), v.Object())
|
||||||
|
} else {
|
||||||
|
properties.Set(PropertyName(node.Tag()), v.Value())
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
properties.Set(PropertyName(node.Tag()), node.Array())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -157,9 +181,34 @@ func (data *dataProperty) init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (data *dataProperty) Get(tag PropertyName) any {
|
func (data *dataProperty) Get(tag PropertyName) any {
|
||||||
return propertiesGet(data, data.normalize(tag))
|
return data.get(data, data.normalize(tag))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (data *dataProperty) Remove(tag PropertyName) {
|
func (data *dataProperty) Remove(tag PropertyName) {
|
||||||
data.remove(data, data.normalize(tag))
|
data.remove(data, data.normalize(tag))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (data *dataProperty) writeToBuffer(buffer *strings.Builder, indent string, objectName string, tags []PropertyName) {
|
||||||
|
buffer.WriteString(objectName)
|
||||||
|
buffer.WriteString("{ ")
|
||||||
|
comma := false
|
||||||
|
for _, tag := range tags {
|
||||||
|
if value, ok := data.properties[tag]; ok {
|
||||||
|
text := propertyValueToString(tag, value, indent)
|
||||||
|
if text != "" {
|
||||||
|
if comma {
|
||||||
|
buffer.WriteString(", ")
|
||||||
|
}
|
||||||
|
buffer.WriteString(string(tag))
|
||||||
|
buffer.WriteString(" = ")
|
||||||
|
buffer.WriteString(text)
|
||||||
|
comma = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer.WriteString(" }")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (data *dataProperty) writeString(buffer *strings.Builder, indent string) {
|
||||||
|
data.writeToBuffer(buffer, indent, "_", data.AllTags())
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,8 @@ func stringProperty(properties Properties, tag PropertyName, session Session) (s
|
||||||
func imageProperty(properties Properties, tag PropertyName, session Session) (string, bool) {
|
func imageProperty(properties Properties, tag PropertyName, session Session) (string, bool) {
|
||||||
if value := properties.getRaw(tag); value != nil {
|
if value := properties.getRaw(tag); value != nil {
|
||||||
if text, ok := value.(string); ok {
|
if text, ok := value.(string); ok {
|
||||||
if text != "" && text[0] == '@' {
|
if ok, constName := isConstantName(text); ok {
|
||||||
if image, ok := session.ImageConstant(text[1:]); ok {
|
if image, ok := session.ImageConstant(constName); ok {
|
||||||
return image, true
|
return image, true
|
||||||
} else {
|
} else {
|
||||||
return "", false
|
return "", false
|
||||||
|
|
@ -88,8 +88,8 @@ func valueToColor(value any, session Session) (Color, bool) {
|
||||||
return value, true
|
return value, true
|
||||||
|
|
||||||
case string:
|
case string:
|
||||||
if len(value) > 1 && value[0] == '@' {
|
if ok, constName := isConstantName(value); ok {
|
||||||
return session.Color(value[1:])
|
return session.Color(constName)
|
||||||
}
|
}
|
||||||
return StringToColor(value)
|
return StringToColor(value)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2235,7 +2235,7 @@ const (
|
||||||
|
|
||||||
// VerticalAlign is the constant for "vertical-align" property tag.
|
// VerticalAlign is the constant for "vertical-align" property tag.
|
||||||
//
|
//
|
||||||
// Used by Checkbox, ListLayout, ListView, Popup, SvgImageView.
|
// Used by Button, Checkbox, ListLayout, ListView, Popup, SvgImageView.
|
||||||
//
|
//
|
||||||
// # Usage in Checkbox
|
// # Usage in Checkbox
|
||||||
//
|
//
|
||||||
|
|
@ -2249,7 +2249,7 @@ const (
|
||||||
// - 2 (CenterAlign) or "center" - Content aligned in the center of the content area.
|
// - 2 (CenterAlign) or "center" - Content aligned in the center of the content area.
|
||||||
// - 3 (StretchAlign) or "stretch" - Content relaxed to fill all content area.
|
// - 3 (StretchAlign) or "stretch" - Content relaxed to fill all content area.
|
||||||
//
|
//
|
||||||
// # Usage in ListLayout and ListView
|
// # Usage in Button, ListLayout and ListView
|
||||||
//
|
//
|
||||||
// Sets the vertical alignment of the content inside a block element.
|
// Sets the vertical alignment of the content inside a block element.
|
||||||
//
|
//
|
||||||
|
|
@ -2287,7 +2287,7 @@ const (
|
||||||
|
|
||||||
// HorizontalAlign is the constant for "horizontal-align" property tag.
|
// HorizontalAlign is the constant for "horizontal-align" property tag.
|
||||||
//
|
//
|
||||||
// Used by Checkbox, ListLayout, ListView, Popup, SvgImageView.
|
// Used by Button, Checkbox, ListLayout, ListView, Popup, SvgImageView.
|
||||||
//
|
//
|
||||||
// # Usage in Checkbox
|
// # Usage in Checkbox
|
||||||
//
|
//
|
||||||
|
|
@ -2305,7 +2305,7 @@ const (
|
||||||
//
|
//
|
||||||
// Sets the horizontal alignment of the content inside a block element.
|
// Sets the horizontal alignment of the content inside a block element.
|
||||||
//
|
//
|
||||||
// Supported types: int, string.
|
// Supported types: Button, int, string.
|
||||||
//
|
//
|
||||||
// Values:
|
// Values:
|
||||||
// - 0 (LeftAlign) or "left" - Left alignment.
|
// - 0 (LeftAlign) or "left" - Left alignment.
|
||||||
|
|
@ -2723,4 +2723,6 @@ const (
|
||||||
//
|
//
|
||||||
// Supported types: string.
|
// Supported types: string.
|
||||||
Tooltip PropertyName = "tooltip"
|
Tooltip PropertyName = "tooltip"
|
||||||
|
|
||||||
|
Binding PropertyName = "binding"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package rui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
@ -22,6 +23,7 @@ var colorProperties = []PropertyName{
|
||||||
AccentColor,
|
AccentColor,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
func isPropertyInList(tag PropertyName, list []PropertyName) bool {
|
func isPropertyInList(tag PropertyName, list []PropertyName) bool {
|
||||||
for _, prop := range list {
|
for _, prop := range list {
|
||||||
if prop == tag {
|
if prop == tag {
|
||||||
|
|
@ -30,7 +32,7 @@ func isPropertyInList(tag PropertyName, list []PropertyName) bool {
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
var angleProperties = []PropertyName{
|
var angleProperties = []PropertyName{
|
||||||
From,
|
From,
|
||||||
}
|
}
|
||||||
|
|
@ -82,6 +84,7 @@ var intProperties = []PropertyName{
|
||||||
|
|
||||||
var floatProperties = map[PropertyName]struct{ min, max float64 }{
|
var floatProperties = map[PropertyName]struct{ min, max float64 }{
|
||||||
Opacity: {min: 0, max: 1},
|
Opacity: {min: 0, max: 1},
|
||||||
|
ShowOpacity: {min: 0, max: 1},
|
||||||
NumberPickerMax: {min: -math.MaxFloat64, max: math.MaxFloat64},
|
NumberPickerMax: {min: -math.MaxFloat64, max: math.MaxFloat64},
|
||||||
NumberPickerMin: {min: -math.MaxFloat64, max: math.MaxFloat64},
|
NumberPickerMin: {min: -math.MaxFloat64, max: math.MaxFloat64},
|
||||||
NumberPickerStep: {min: -math.MaxFloat64, max: math.MaxFloat64},
|
NumberPickerStep: {min: -math.MaxFloat64, max: math.MaxFloat64},
|
||||||
|
|
@ -91,6 +94,9 @@ var floatProperties = map[PropertyName]struct{ min, max float64 }{
|
||||||
VideoWidth: {min: 0, max: 10000},
|
VideoWidth: {min: 0, max: 10000},
|
||||||
VideoHeight: {min: 0, max: 10000},
|
VideoHeight: {min: 0, max: 10000},
|
||||||
PushDuration: {min: 0, max: math.MaxFloat64},
|
PushDuration: {min: 0, max: math.MaxFloat64},
|
||||||
|
ShowDuration: {min: 0, max: math.MaxFloat64},
|
||||||
|
DragImageXOffset: {min: -math.MaxFloat64, max: math.MaxFloat64},
|
||||||
|
DragImageYOffset: {min: -math.MaxFloat64, max: math.MaxFloat64},
|
||||||
}
|
}
|
||||||
|
|
||||||
var sizeProperties = map[PropertyName]string{
|
var sizeProperties = map[PropertyName]string{
|
||||||
|
|
@ -163,6 +169,9 @@ var sizeProperties = map[PropertyName]string{
|
||||||
ItemHeight: string(ItemHeight),
|
ItemHeight: string(ItemHeight),
|
||||||
CenterX: string(CenterX),
|
CenterX: string(CenterX),
|
||||||
CenterY: string(CenterX),
|
CenterY: string(CenterX),
|
||||||
|
ArrowSize: "",
|
||||||
|
ArrowWidth: "",
|
||||||
|
ArrowOffset: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
type enumPropertyData struct {
|
type enumPropertyData struct {
|
||||||
|
|
@ -502,22 +511,14 @@ func invalidPropertyValue(tag PropertyName, value any) {
|
||||||
ErrorLogF(`Invalid value "%v" of "%s" property`, value, string(tag))
|
ErrorLogF(`Invalid value "%v" of "%s" property`, value, string(tag))
|
||||||
}
|
}
|
||||||
|
|
||||||
func isConstantName(text string) bool {
|
func isConstantName(text string) (bool, string) {
|
||||||
len := len(text)
|
len := len(text)
|
||||||
if len <= 1 || text[0] != '@' {
|
if len <= 1 || text[0] != '@' ||
|
||||||
return false
|
strings.ContainsAny(text, ",;|\"'`+(){}[]<>/\\*&%! \t\n\r") {
|
||||||
|
return false, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
if len > 2 {
|
return true, text[1:]
|
||||||
last := len - 1
|
|
||||||
if (text[1] == '`' && text[last] == '`') ||
|
|
||||||
(text[1] == '"' && text[last] == '"') ||
|
|
||||||
(text[1] == '\'' && text[last] == '\'') {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return !strings.ContainsAny(text, ",;|\"'`+(){}[]<>/\\*&%! \t\n\r")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func isInt(value any) (int, bool) {
|
func isInt(value any) (int, bool) {
|
||||||
|
|
@ -570,7 +571,7 @@ func setSimpleProperty(properties Properties, tag PropertyName, value any) bool
|
||||||
properties.setRaw(tag, nil)
|
properties.setRaw(tag, nil)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if isConstantName(text) {
|
if ok, _ := isConstantName(text); ok {
|
||||||
properties.setRaw(tag, text)
|
properties.setRaw(tag, text)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
@ -849,19 +850,19 @@ func propertiesSet(properties Properties, tag PropertyName, value any) []Propert
|
||||||
return setFloatProperty(properties, tag, value, limits.min, limits.max)
|
return setFloatProperty(properties, tag, value, limits.min, limits.max)
|
||||||
}
|
}
|
||||||
|
|
||||||
if isPropertyInList(tag, colorProperties) {
|
if slices.Contains(colorProperties, tag) {
|
||||||
return setColorProperty(properties, tag, value)
|
return setColorProperty(properties, tag, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
if isPropertyInList(tag, angleProperties) {
|
if slices.Contains(angleProperties, tag) {
|
||||||
return setAngleProperty(properties, tag, value)
|
return setAngleProperty(properties, tag, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
if isPropertyInList(tag, boolProperties) {
|
if slices.Contains(boolProperties, tag) {
|
||||||
return setBoolProperty(properties, tag, value)
|
return setBoolProperty(properties, tag, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
if isPropertyInList(tag, intProperties) {
|
if slices.Contains(intProperties, tag) {
|
||||||
return setIntProperty(properties, tag, value)
|
return setIntProperty(properties, tag, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -874,18 +875,6 @@ func propertiesSet(properties Properties, tag PropertyName, value any) []Propert
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
func (properties *propertyList) Set(tag PropertyName, value any) bool {
|
|
||||||
tag = properties.normalize(tag)
|
|
||||||
if value == nil {
|
|
||||||
properties.remove(properties, tag)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return properties.set(properties, tag, value) != nil
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
func (data *dataProperty) Set(tag PropertyName, value any) bool {
|
func (data *dataProperty) Set(tag PropertyName, value any) bool {
|
||||||
if value == nil {
|
if value == nil {
|
||||||
data.Remove(tag)
|
data.Remove(tag)
|
||||||
|
|
@ -893,11 +882,9 @@ func (data *dataProperty) Set(tag PropertyName, value any) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
tag = data.normalize(tag)
|
tag = data.normalize(tag)
|
||||||
for _, supported := range data.supportedProperties {
|
if slices.Contains(data.supportedProperties, tag) {
|
||||||
if tag == supported {
|
|
||||||
return data.set(data, tag, value) != nil
|
return data.set(data, tag, value) != nil
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
ErrorLogF(`"%s" property is not supported`, string(tag))
|
ErrorLogF(`"%s" property is not supported`, string(tag))
|
||||||
return false
|
return false
|
||||||
|
|
|
||||||
24
radius.go
24
radius.go
|
|
@ -475,24 +475,6 @@ func (radius *radiusPropertyData) init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (radius *radiusPropertyData) writeString(buffer *strings.Builder, indent string) {
|
|
||||||
buffer.WriteString("_{ ")
|
|
||||||
comma := false
|
|
||||||
for _, tag := range radius.supportedProperties {
|
|
||||||
if value, ok := radius.properties[tag]; ok {
|
|
||||||
if comma {
|
|
||||||
buffer.WriteString(", ")
|
|
||||||
}
|
|
||||||
buffer.WriteString(string(tag))
|
|
||||||
buffer.WriteString(" = ")
|
|
||||||
writePropertyValue(buffer, tag, value, indent)
|
|
||||||
comma = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer.WriteString(" }")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (radius *radiusPropertyData) String() string {
|
func (radius *radiusPropertyData) String() string {
|
||||||
return runStringWriter(radius)
|
return runStringWriter(radius)
|
||||||
}
|
}
|
||||||
|
|
@ -667,7 +649,7 @@ func radiusPropertySet(radius Properties, tag PropertyName, value any) []Propert
|
||||||
deleteRadiusUnusedTags(radius, result)
|
deleteRadiusUnusedTags(radius, result)
|
||||||
|
|
||||||
case string:
|
case string:
|
||||||
if strings.Contains(value, "/") {
|
if strings.ContainsRune(value, '/') {
|
||||||
if values := strings.Split(value, "/"); len(values) == 2 {
|
if values := strings.Split(value, "/"); len(values) == 2 {
|
||||||
if result = radiusPropertySet(radius, tag+"-x", values[0]); result != nil {
|
if result = radiusPropertySet(radius, tag+"-x", values[0]); result != nil {
|
||||||
if resultY := radiusPropertySet(radius, tag+"-y", values[1]); resultY != nil {
|
if resultY := radiusPropertySet(radius, tag+"-y", values[1]); resultY != nil {
|
||||||
|
|
@ -991,7 +973,7 @@ func setRadiusProperty(properties Properties, value any) []PropertyName {
|
||||||
properties.setRaw(Radius, radius)
|
properties.setRaw(Radius, radius)
|
||||||
|
|
||||||
case string:
|
case string:
|
||||||
if strings.Contains(value, "/") {
|
if strings.ContainsRune(value, '/') {
|
||||||
values := strings.Split(value, "/")
|
values := strings.Split(value, "/")
|
||||||
if len(values) == 2 {
|
if len(values) == 2 {
|
||||||
if setRadiusPropertyElement(properties, RadiusX, values[0]) {
|
if setRadiusPropertyElement(properties, RadiusX, values[0]) {
|
||||||
|
|
@ -1041,7 +1023,7 @@ func removeRadiusPropertyElement(properties Properties, tag PropertyName) bool {
|
||||||
if value := properties.getRaw(Radius); value != nil {
|
if value := properties.getRaw(Radius); value != nil {
|
||||||
radius := getRadiusProperty(properties)
|
radius := getRadiusProperty(properties)
|
||||||
radius.Remove(tag)
|
radius.Remove(tag)
|
||||||
if radius.empty() {
|
if radius.IsEmpty() {
|
||||||
properties.setRaw(Radius, nil)
|
properties.setRaw(Radius, nil)
|
||||||
} else {
|
} else {
|
||||||
properties.setRaw(Radius, radius)
|
properties.setRaw(Radius, radius)
|
||||||
|
|
|
||||||
2
range.go
2
range.go
|
|
@ -21,7 +21,7 @@ func (r Range) String() string {
|
||||||
|
|
||||||
func (r *Range) setValue(value string) bool {
|
func (r *Range) setValue(value string) bool {
|
||||||
var err error
|
var err error
|
||||||
if strings.Contains(value, ":") {
|
if strings.ContainsRune(value, ':') {
|
||||||
values := strings.Split(value, ":")
|
values := strings.Split(value, ":")
|
||||||
if len(values) != 2 {
|
if len(values) != 2 {
|
||||||
ErrorLog("Invalid range value: " + value)
|
ErrorLog("Invalid range value: " + value)
|
||||||
|
|
|
||||||
12
resizable.go
12
resizable.go
|
|
@ -118,7 +118,7 @@ func (resizable *resizableData) setFunc(tag PropertyName, value any) []PropertyN
|
||||||
newContent = value
|
newContent = value
|
||||||
|
|
||||||
case DataObject:
|
case DataObject:
|
||||||
if newContent = CreateViewFromObject(resizable.Session(), value); newContent == nil {
|
if newContent = CreateViewFromObject(resizable.Session(), value, nil); newContent == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -174,7 +174,7 @@ func resizableSide(view View) int {
|
||||||
"all": AllSides,
|
"all": AllSides,
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.Contains(value, "|") {
|
if strings.ContainsRune(value, '|') {
|
||||||
values := strings.Split(value, "|")
|
values := strings.Split(value, "|")
|
||||||
sides := 0
|
sides := 0
|
||||||
for _, val := range values {
|
for _, val := range values {
|
||||||
|
|
@ -226,7 +226,7 @@ func resizableSetSide(properties Properties, value any) []PropertyName {
|
||||||
"left": LeftSide,
|
"left": LeftSide,
|
||||||
"all": AllSides,
|
"all": AllSides,
|
||||||
}
|
}
|
||||||
if strings.Contains(value, "|") {
|
if strings.ContainsRune(value, '|') {
|
||||||
values := strings.Split(value, "|")
|
values := strings.Split(value, "|")
|
||||||
sides := 0
|
sides := 0
|
||||||
hasConst := false
|
hasConst := false
|
||||||
|
|
@ -234,7 +234,7 @@ func resizableSetSide(properties Properties, value any) []PropertyName {
|
||||||
val := strings.Trim(val, " \t\r\n")
|
val := strings.Trim(val, " \t\r\n")
|
||||||
values[i] = val
|
values[i] = val
|
||||||
|
|
||||||
if val[0] == '@' {
|
if ok, _ := isConstantName(val); ok {
|
||||||
hasConst = true
|
hasConst = true
|
||||||
} else if n, err := strconv.Atoi(val); err == nil {
|
} else if n, err := strconv.Atoi(val); err == nil {
|
||||||
if n < 1 || n > AllSides {
|
if n < 1 || n > AllSides {
|
||||||
|
|
@ -262,7 +262,7 @@ func resizableSetSide(properties Properties, value any) []PropertyName {
|
||||||
return []PropertyName{Side}
|
return []PropertyName{Side}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if value[0] == '@' {
|
} else if ok, _ := isConstantName(value); ok {
|
||||||
properties.setRaw(Side, value)
|
properties.setRaw(Side, value)
|
||||||
return []PropertyName{Side}
|
return []PropertyName{Side}
|
||||||
} else if n, ok := validValues[value]; ok {
|
} else if n, ok := validValues[value]; ok {
|
||||||
|
|
@ -354,7 +354,7 @@ func (resizable *resizableData) htmlSubviews(self View, buffer *strings.Builder)
|
||||||
}
|
}
|
||||||
|
|
||||||
writePos := func(x1, x2, y1, y2 int) {
|
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()
|
//htmlID := resizable.htmlID()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,8 @@ func (view *viewData) onResize(self View, x, y, width, height float64) {
|
||||||
view.frame.Top = y
|
view.frame.Top = y
|
||||||
view.frame.Width = width
|
view.frame.Width = width
|
||||||
view.frame.Height = height
|
view.frame.Height = height
|
||||||
for _, listener := range GetResizeListeners(view) {
|
for _, listener := range getOneArgEventListeners[View, Frame](view, nil, ResizeEvent) {
|
||||||
listener(self, view.frame)
|
listener.Run(self, view.frame)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -73,7 +73,9 @@ func (view *viewData) Frame() Frame {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetViewFrame returns the size and location of view's viewport.
|
// 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 {
|
func GetViewFrame(view View, subviewID ...string) Frame {
|
||||||
view = getSubview(view, subviewID)
|
view = getSubview(view, subviewID)
|
||||||
if view == nil {
|
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
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[View, Frame](view, subviewID, ResizeEvent)
|
// - 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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ const (
|
||||||
imageDir = "images"
|
imageDir = "images"
|
||||||
themeDir = "themes"
|
themeDir = "themes"
|
||||||
viewDir = "views"
|
viewDir = "views"
|
||||||
|
popupDir = "popups"
|
||||||
rawDir = "raw"
|
rawDir = "raw"
|
||||||
stringsDir = "strings"
|
stringsDir = "strings"
|
||||||
)
|
)
|
||||||
|
|
@ -401,7 +402,7 @@ func OpenRawResource(filename string) fs.File {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AllRawResources returns the list of all raw resouces
|
// AllRawResources returns the list of all raw resources
|
||||||
func AllRawResources() []string {
|
func AllRawResources() []string {
|
||||||
result := []string{}
|
result := []string{}
|
||||||
|
|
||||||
|
|
@ -448,7 +449,7 @@ func AllRawResources() []string {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// AllImageResources returns the list of all image resouces
|
// AllImageResources returns the list of all image resources
|
||||||
func AllImageResources() []string {
|
func AllImageResources() []string {
|
||||||
result := make([]string, 0, len(resources.images))
|
result := make([]string, 0, len(resources.images))
|
||||||
for image := range resources.images {
|
for image := range resources.images {
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ func (writer *ruiWriterData) writeString(str string) {
|
||||||
{old: "\"", new: `\"`},
|
{old: "\"", new: `\"`},
|
||||||
}
|
}
|
||||||
for _, s := range replace {
|
for _, s := range replace {
|
||||||
str = strings.Replace(str, s.old, s.new, -1)
|
str = strings.ReplaceAll(str, s.old, s.new)
|
||||||
}
|
}
|
||||||
writer.buffer.WriteRune('"')
|
writer.buffer.WriteRune('"')
|
||||||
writer.buffer.WriteString(str)
|
writer.buffer.WriteString(str)
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,8 @@ func (view *viewData) onScroll(self View, x, y, width, height float64) {
|
||||||
view.scroll.Top = y
|
view.scroll.Top = y
|
||||||
view.scroll.Width = width
|
view.scroll.Width = width
|
||||||
view.scroll.Height = height
|
view.scroll.Height = height
|
||||||
for _, listener := range GetScrollListeners(view) {
|
for _, listener := range getOneArgEventListeners[View, Frame](view, nil, ScrollEvent) {
|
||||||
listener(self, view.scroll)
|
listener.Run(self, view.scroll)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,7 +42,9 @@ func (view *viewData) setScroll(x, y, width, height float64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetViewScroll returns ...
|
// 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 {
|
func GetViewScroll(view View, subviewID ...string) Frame {
|
||||||
view = getSubview(view, subviewID)
|
view = getSubview(view, subviewID)
|
||||||
if view == nil {
|
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
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[View, Frame](view, subviewID, ResizeEvent)
|
// - 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.
|
// 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) {
|
func ScrollViewTo(view View, subviewID string, x, y float64) {
|
||||||
if subviewID != "" {
|
if subviewID != "" {
|
||||||
view = ViewByID(view, 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.
|
// 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) {
|
func ScrollViewToStart(view View, subviewID ...string) {
|
||||||
if view = getSubview(view, subviewID); view != nil {
|
if view = getSubview(view, subviewID); view != nil {
|
||||||
view.Session().callFunc("scrollToStart", view.htmlID())
|
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.
|
// 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) {
|
func ScrollViewToEnd(view View, subviewID ...string) {
|
||||||
if view = getSubview(view, subviewID); view != nil {
|
if view = getSubview(view, subviewID); view != nil {
|
||||||
view.Session().callFunc("scrollToEnd", view.htmlID())
|
view.Session().callFunc("scrollToEnd", view.htmlID())
|
||||||
|
|
|
||||||
|
|
@ -266,7 +266,8 @@ func (session *sessionData) setBridge(events chan DataObject, bridge bridge) {
|
||||||
|
|
||||||
func (session *sessionData) close() {
|
func (session *sessionData) close() {
|
||||||
if session.events != nil {
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -657,7 +658,7 @@ func (session *sessionData) handleRootSize(data DataObject) {
|
||||||
|
|
||||||
func (session *sessionData) handleResize(data DataObject) {
|
func (session *sessionData) handleResize(data DataObject) {
|
||||||
if node := data.PropertyByTag("views"); node != nil && node.Type() == ArrayNode {
|
if node := data.PropertyByTag("views"); node != nil && node.Type() == ArrayNode {
|
||||||
for _, el := range node.ArrayElements() {
|
for el := range node.ArrayElements() {
|
||||||
if el.IsObject() {
|
if el.IsObject() {
|
||||||
obj := el.Object()
|
obj := el.Object()
|
||||||
getFloat := func(tag string) float64 {
|
getFloat := func(tag string) float64 {
|
||||||
|
|
@ -734,8 +735,8 @@ func (session *sessionData) handleSessionInfo(params DataObject) {
|
||||||
|
|
||||||
if node := params.PropertyByTag("storage"); node != nil && node.Type() == ObjectNode {
|
if node := params.PropertyByTag("storage"); node != nil && node.Type() == ObjectNode {
|
||||||
if obj := node.Object(); obj != nil {
|
if obj := node.Object(); obj != nil {
|
||||||
for i := 0; i < obj.PropertyCount(); i++ {
|
for element := range obj.Properties() {
|
||||||
if element := obj.Property(i); element.Type() == TextNode {
|
if element.Type() == TextNode {
|
||||||
session.clientStorage[element.Tag()] = element.Text()
|
session.clientStorage[element.Tag()] = element.Text()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,11 +35,9 @@ func (session *sessionData) constant(tag string, prevTags []string) (string, boo
|
||||||
return result, true
|
return result, true
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, separator := range []string{",", " ", ":", ";", "|", "/"} {
|
if strings.ContainsAny(result, ", :;|/") {
|
||||||
if strings.Contains(result, separator) {
|
|
||||||
return session.resolveConstantsNext(result, tags)
|
return session.resolveConstantsNext(result, tags)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if result[0] != '@' {
|
if result[0] != '@' {
|
||||||
return result, true
|
return result, true
|
||||||
|
|
@ -61,7 +59,7 @@ func (session *sessionData) resolveConstants(value string) (string, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (session *sessionData) resolveConstantsNext(value string, prevTags []string) (string, bool) {
|
func (session *sessionData) resolveConstantsNext(value string, prevTags []string) (string, bool) {
|
||||||
if !strings.Contains(value, "@") {
|
if !strings.ContainsRune(value, '@') {
|
||||||
return value, true
|
return value, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
17
shadow.go
17
shadow.go
|
|
@ -281,23 +281,6 @@ func (shadow *shadowPropertyData) String() string {
|
||||||
return runStringWriter(shadow)
|
return runStringWriter(shadow)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (shadow *shadowPropertyData) writeString(buffer *strings.Builder, indent string) {
|
|
||||||
buffer.WriteString("_{ ")
|
|
||||||
comma := false
|
|
||||||
for _, tag := range shadow.AllTags() {
|
|
||||||
if value, ok := shadow.properties[tag]; ok {
|
|
||||||
if comma {
|
|
||||||
buffer.WriteString(", ")
|
|
||||||
}
|
|
||||||
buffer.WriteString(string(tag))
|
|
||||||
buffer.WriteString(" = ")
|
|
||||||
writePropertyValue(buffer, tag, value, indent)
|
|
||||||
comma = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buffer.WriteString(" }")
|
|
||||||
}
|
|
||||||
|
|
||||||
func setShadowProperty(properties Properties, tag PropertyName, value any) bool {
|
func setShadowProperty(properties Properties, tag PropertyName, value any) bool {
|
||||||
|
|
||||||
if value == nil {
|
if value == nil {
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ func (data *sizeFuncData) parseArgs(args []any, allowNumber bool) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if arg[0] == '@' {
|
if ok, _ := isConstantName(arg); ok {
|
||||||
data.args = append(data.args, arg)
|
data.args = append(data.args, arg)
|
||||||
} else if val, err := strconv.ParseFloat(arg, 64); err == nil {
|
} else if val, err := strconv.ParseFloat(arg, 64); err == nil {
|
||||||
return numberArg(i, val)
|
return numberArg(i, val)
|
||||||
|
|
@ -208,7 +208,7 @@ func (data *sizeFuncData) writeString(topFunc string, buffer *strings.Builder) {
|
||||||
buffer.WriteString(arg.String())
|
buffer.WriteString(arg.String())
|
||||||
|
|
||||||
case float64:
|
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())
|
buffer.WriteString(arg.String())
|
||||||
|
|
||||||
case float64:
|
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.remove = layout.removeFunc
|
||||||
layout.changed = layout.propertyChanged
|
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 {
|
if session.TextDirection() == RightToLeftDirection {
|
||||||
layout.setRaw(PushTransform, NewTransformProperty(Params{TranslateX: Percent(-100)}))
|
layout.setRaw(PushTransform, NewTransformProperty(Params{TranslateX: Percent(-100)}))
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -375,7 +377,7 @@ func (layout *stackLayoutData) transitionFinished(view View, tag PropertyName) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete(layout.onPushFinished, viewID)
|
delete(layout.onPushFinished, viewID)
|
||||||
layout.contentChanged()
|
layout.runChangeListener(Content)
|
||||||
}
|
}
|
||||||
|
|
||||||
case "pop":
|
case "pop":
|
||||||
|
|
@ -414,7 +416,7 @@ func (layout *stackLayoutData) transitionFinished(view View, tag PropertyName) {
|
||||||
session.removeProperty(pageID, "ontransitionend")
|
session.removeProperty(pageID, "ontransitionend")
|
||||||
session.removeProperty(pageID, "ontransitioncancel")
|
session.removeProperty(pageID, "ontransitioncancel")
|
||||||
session.finishUpdateScript(pageID)
|
session.finishUpdateScript(pageID)
|
||||||
layout.contentChanged()
|
layout.runChangeListener(Content)
|
||||||
|
|
||||||
if finished, ok := layout.onMoveFinished[viewID]; ok {
|
if finished, ok := layout.onMoveFinished[viewID]; ok {
|
||||||
for _, listener := range finished.listener {
|
for _, listener := range finished.listener {
|
||||||
|
|
@ -434,7 +436,7 @@ func (layout *stackLayoutData) setFunc(tag PropertyName, value any) []PropertyNa
|
||||||
// TODO
|
// TODO
|
||||||
listeners, ok := valueToOneArgEventListeners[View, PropertyName](value)
|
listeners, ok := valueToOneArgEventListeners[View, PropertyName](value)
|
||||||
if ok && listeners != nil {
|
if ok && listeners != nil {
|
||||||
listeners = append(listeners, layout.transitionFinished)
|
listeners = append(listeners, newOneArgListenerVE(layout.transitionFinished))
|
||||||
layout.setRaw(TransitionEndEvent, listeners)
|
layout.setRaw(TransitionEndEvent, listeners)
|
||||||
return []PropertyName{tag}
|
return []PropertyName{tag}
|
||||||
}
|
}
|
||||||
|
|
@ -464,7 +466,9 @@ func (layout *stackLayoutData) propertyChanged(tag PropertyName) {
|
||||||
func (layout *stackLayoutData) removeFunc(tag PropertyName) []PropertyName {
|
func (layout *stackLayoutData) removeFunc(tag PropertyName) []PropertyName {
|
||||||
switch tag {
|
switch tag {
|
||||||
case TransitionEndEvent:
|
case TransitionEndEvent:
|
||||||
layout.setRaw(TransitionEndEvent, []func(View, PropertyName){layout.transitionFinished})
|
layout.setRaw(TransitionEndEvent, []oneArgListener[View, PropertyName]{
|
||||||
|
newOneArgListenerVE(layout.transitionFinished),
|
||||||
|
})
|
||||||
return []PropertyName{tag}
|
return []PropertyName{tag}
|
||||||
}
|
}
|
||||||
return layout.viewsContainerData.removeFunc(tag)
|
return layout.viewsContainerData.removeFunc(tag)
|
||||||
|
|
@ -562,7 +566,7 @@ func (layout *stackLayoutData) moveToFrontByIndex(index int, onShow []func(View)
|
||||||
if transform == nil {
|
if transform == nil {
|
||||||
session.updateCSSProperty(peekPageID, "visibility", "hidden")
|
session.updateCSSProperty(peekPageID, "visibility", "hidden")
|
||||||
session.updateCSSProperty(pageID, "visibility", "visible")
|
session.updateCSSProperty(pageID, "visibility", "visible")
|
||||||
layout.contentChanged()
|
layout.runChangeListener(Content)
|
||||||
for _, listener := range onShow {
|
for _, listener := range onShow {
|
||||||
if listener != nil {
|
if listener != nil {
|
||||||
listener(view)
|
listener(view)
|
||||||
|
|
@ -606,12 +610,6 @@ func (layout *stackLayoutData) moveToFrontByIndex(index int, onShow []func(View)
|
||||||
session.updateCSSProperty(peekPageID, "transform", transformCSS)
|
session.updateCSSProperty(peekPageID, "transform", transformCSS)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (layout *stackLayoutData) contentChanged() {
|
|
||||||
if listener, ok := layout.changeListener[Content]; ok {
|
|
||||||
listener(layout, Content)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (layout *stackLayoutData) RemovePeek() View {
|
func (layout *stackLayoutData) RemovePeek() View {
|
||||||
return layout.RemoveView(len(layout.views) - 1)
|
return layout.RemoveView(len(layout.views) - 1)
|
||||||
}
|
}
|
||||||
|
|
@ -679,9 +677,7 @@ func (layout *stackLayoutData) Append(view View) {
|
||||||
}
|
}
|
||||||
session.appendToInnerHTML(stackID, buffer.String())
|
session.appendToInnerHTML(stackID, buffer.String())
|
||||||
|
|
||||||
if listener, ok := layout.changeListener[Content]; ok {
|
layout.runChangeListener(Content)
|
||||||
listener(layout, Content)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -717,9 +713,7 @@ func (layout *stackLayoutData) Insert(view View, index int) {
|
||||||
session := layout.Session()
|
session := layout.Session()
|
||||||
session.appendToInnerHTML(stackID, buffer.String())
|
session.appendToInnerHTML(stackID, buffer.String())
|
||||||
|
|
||||||
if listener, ok := layout.changeListener[Content]; ok {
|
layout.runChangeListener(Content)
|
||||||
listener(layout, Content)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove removes view from list and return it
|
// 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")
|
layout.Session().callFunc("removeView", view.htmlID()+"page")
|
||||||
|
layout.runChangeListener(Content)
|
||||||
if listener, ok := layout.changeListener[Content]; ok {
|
|
||||||
listener(layout, Content)
|
|
||||||
}
|
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -848,7 +839,7 @@ func (layout *stackLayoutData) Pop(onPopFinished ...func(View)) bool {
|
||||||
view.setParentID("")
|
view.setParentID("")
|
||||||
|
|
||||||
layout.views = layout.views[:peek]
|
layout.views = layout.views[:peek]
|
||||||
layout.contentChanged()
|
layout.runChangeListener(Content)
|
||||||
|
|
||||||
layout.onPopFinished[view.htmlID()] = popFinished{
|
layout.onPopFinished[view.htmlID()] = popFinished{
|
||||||
view: view,
|
view: 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.
|
// 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 {
|
func IsMoveToFrontAnimation(view View, subviewID ...string) bool {
|
||||||
if view = getSubview(view, subviewID); view != nil {
|
if view = getSubview(view, subviewID); view != nil {
|
||||||
if value, ok := boolProperty(view, MoveToFrontAnimation, view.Session()); ok {
|
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)
|
// 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.
|
// for an animated pushing of a child view.
|
||||||
// The default value is nil (no transform).
|
// 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 {
|
func GetPushTransform(view View, subviewID ...string) TransformProperty {
|
||||||
return transformStyledProperty(view, subviewID, PushTransform)
|
return transformStyledProperty(view, subviewID, PushTransform)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
13
strings.go
13
strings.go
|
|
@ -50,8 +50,9 @@ func (resources *resourceManager) scanStringsDir(path string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadStringResources(text string) {
|
func loadStringResources(text string) {
|
||||||
data := ParseDataText(text)
|
data, err := ParseDataText(text)
|
||||||
if data == nil {
|
if err != nil {
|
||||||
|
ErrorLog(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -61,8 +62,8 @@ func loadStringResources(text string) {
|
||||||
table = map[string]string{}
|
table = map[string]string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < obj.PropertyCount(); i++ {
|
for prop := range obj.Properties() {
|
||||||
if prop := obj.Property(i); prop != nil && prop.Type() == TextNode {
|
if prop.Type() == TextNode {
|
||||||
table[prop.Tag()] = prop.Text()
|
table[prop.Tag()] = prop.Text()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -72,8 +73,8 @@ func loadStringResources(text string) {
|
||||||
|
|
||||||
tag := data.Tag()
|
tag := data.Tag()
|
||||||
if tag == "strings" {
|
if tag == "strings" {
|
||||||
for i := 0; i < data.PropertyCount(); i++ {
|
for prop := range data.Properties() {
|
||||||
if prop := data.Property(i); prop != nil && prop.Type() == ObjectNode {
|
if prop.Type() == ObjectNode {
|
||||||
parseStrings(prop.Object(), prop.Tag())
|
parseStrings(prop.Object(), prop.Tag())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
149
tableView.go
149
tableView.go
|
|
@ -592,15 +592,8 @@ func (table *tableViewData) init(session Session) {
|
||||||
table.tag = "TableView"
|
table.tag = "TableView"
|
||||||
table.cellViews = []View{}
|
table.cellViews = []View{}
|
||||||
table.cellFrame = []Frame{}
|
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.normalize = normalizeTableViewTag
|
||||||
|
table.get = table.getFunc
|
||||||
table.set = table.setFunc
|
table.set = table.setFunc
|
||||||
table.changed = table.propertyChanged
|
table.changed = table.propertyChanged
|
||||||
}
|
}
|
||||||
|
|
@ -626,6 +619,23 @@ func (table *tableViewData) Focusable() bool {
|
||||||
return GetTableSelectionMode(table) != NoneSelection
|
return GetTableSelectionMode(table) != NoneSelection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (table *tableViewData) getFunc(tag PropertyName) any {
|
||||||
|
switch tag {
|
||||||
|
case TableCellClickedEvent, TableCellSelectedEvent:
|
||||||
|
if listeners := getTwoArgEventRawListeners[TableView, int](table, nil, tag); len(listeners) > 0 {
|
||||||
|
return listeners
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case TableRowClickedEvent, TableRowSelectedEvent:
|
||||||
|
if listeners := getOneArgEventRawListeners[TableView, int](table, nil, tag); len(listeners) > 0 {
|
||||||
|
return listeners
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return table.viewData.getFunc(tag)
|
||||||
|
}
|
||||||
|
|
||||||
func (table *tableViewData) setFunc(tag PropertyName, value any) []PropertyName {
|
func (table *tableViewData) setFunc(tag PropertyName, value any) []PropertyName {
|
||||||
|
|
||||||
setLineStyle := func() []PropertyName {
|
setLineStyle := func() []PropertyName {
|
||||||
|
|
@ -703,7 +713,7 @@ func (table *tableViewData) setFunc(tag PropertyName, value any) []PropertyName
|
||||||
case HeadHeight, FootHeight:
|
case HeadHeight, FootHeight:
|
||||||
switch value := value.(type) {
|
switch value := value.(type) {
|
||||||
case string:
|
case string:
|
||||||
if isConstantName(value) {
|
if ok, _ := isConstantName(value); ok {
|
||||||
table.setRaw(tag, value)
|
table.setRaw(tag, value)
|
||||||
} else if n, err := strconv.Atoi(value); err == nil {
|
} else if n, err := strconv.Atoi(value); err == nil {
|
||||||
table.setRaw(tag, n)
|
table.setRaw(tag, n)
|
||||||
|
|
@ -739,8 +749,8 @@ func (table *tableViewData) setFunc(tag PropertyName, value any) []PropertyName
|
||||||
|
|
||||||
case DataObject:
|
case DataObject:
|
||||||
params := Params{}
|
params := Params{}
|
||||||
for k := 0; k < value.PropertyCount(); k++ {
|
for prop := range value.Properties() {
|
||||||
if prop := value.Property(k); prop != nil && prop.Type() == TextNode {
|
if prop.Type() == TextNode {
|
||||||
params[PropertyName(prop.Tag())] = prop.Text()
|
params[PropertyName(prop.Tag())] = prop.Text()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -794,10 +804,10 @@ func (table *tableViewData) setFunc(tag PropertyName, value any) []PropertyName
|
||||||
}
|
}
|
||||||
|
|
||||||
case string:
|
case string:
|
||||||
if strings.Contains(value, ",") {
|
if strings.ContainsRune(value, ',') {
|
||||||
if values := strings.Split(value, ","); len(values) == 2 {
|
if values := strings.Split(value, ","); len(values) == 2 {
|
||||||
var n = []int{0, 0}
|
var n = []int{0, 0}
|
||||||
for i := 0; i < 2; i++ {
|
for i := range 2 {
|
||||||
var err error
|
var err error
|
||||||
if n[i], err = strconv.Atoi(values[i]); err != nil {
|
if n[i], err = strconv.Atoi(values[i]); err != nil {
|
||||||
ErrorLog(err.Error())
|
ErrorLog(err.Error())
|
||||||
|
|
@ -853,8 +863,17 @@ func (table *tableViewData) propertyChanged(tag PropertyName) {
|
||||||
current := tableViewCurrent(table)
|
current := tableViewCurrent(table)
|
||||||
session.callFunc("setTableCellCursorByID", htmlID, current.Row, current.Column)
|
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:
|
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:
|
case Gap:
|
||||||
|
|
@ -955,63 +974,6 @@ func tableViewCurrentInactiveStyle(view View) string {
|
||||||
return "ruiCurrentTableCell"
|
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 {
|
func (table *tableViewData) htmlTag() string {
|
||||||
return "table"
|
return "table"
|
||||||
}
|
}
|
||||||
|
|
@ -1326,10 +1288,10 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
|
||||||
buffer.WriteString(string(value))
|
buffer.WriteString(string(value))
|
||||||
|
|
||||||
case float32:
|
case float32:
|
||||||
buffer.WriteString(fmt.Sprintf("%g", float64(value)))
|
fmt.Fprintf(buffer, "%g", float64(value))
|
||||||
|
|
||||||
case float64:
|
case float64:
|
||||||
buffer.WriteString(fmt.Sprintf("%g", value))
|
fmt.Fprintf(buffer, "%g", value)
|
||||||
|
|
||||||
case bool:
|
case bool:
|
||||||
if value {
|
if value {
|
||||||
|
|
@ -1340,7 +1302,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if n, ok := isInt(value); ok {
|
if n, ok := isInt(value); ok {
|
||||||
buffer.WriteString(fmt.Sprintf("%d", n))
|
fmt.Fprintf(buffer, "%d", n)
|
||||||
} else {
|
} else {
|
||||||
buffer.WriteString("<Unsupported value>")
|
buffer.WriteString("<Unsupported value>")
|
||||||
}
|
}
|
||||||
|
|
@ -1359,7 +1321,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
|
||||||
|
|
||||||
if columnStyle := GetTableColumnStyle(table); columnStyle != nil {
|
if columnStyle := GetTableColumnStyle(table); columnStyle != nil {
|
||||||
buffer.WriteString("<colgroup>")
|
buffer.WriteString("<colgroup>")
|
||||||
for column := 0; column < columnCount; column++ {
|
for column := range columnCount {
|
||||||
cssBuilder.buffer.Reset()
|
cssBuilder.buffer.Reset()
|
||||||
if styles := columnStyle.ColumnStyle(column); styles != nil {
|
if styles := columnStyle.ColumnStyle(column); styles != nil {
|
||||||
view.Clear()
|
view.Clear()
|
||||||
|
|
@ -1384,7 +1346,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
|
||||||
footHeight := GetTableFootHeight(table)
|
footHeight := GetTableFootHeight(table)
|
||||||
cellBorder := table.getCellBorder()
|
cellBorder := table.getCellBorder()
|
||||||
cellPadding := getBoundsProperty(table, CellPadding)
|
cellPadding := getBoundsProperty(table, CellPadding)
|
||||||
if cellPadding == nil || len(cellPadding.AllTags()) == 0 {
|
if cellPadding == nil || cellPadding.IsEmpty() {
|
||||||
cellPadding = nil
|
cellPadding = nil
|
||||||
if style, ok := stringProperty(table, Style, table.Session()); ok {
|
if style, ok := stringProperty(table, Style, table.Session()); ok {
|
||||||
if style, ok := table.Session().resolveConstants(style); ok {
|
if style, ok := table.Session().resolveConstants(style); ok {
|
||||||
|
|
@ -1521,7 +1483,7 @@ func (table *tableViewData) cellPaddingFromStyle(style string) BoundsProperty {
|
||||||
|
|
||||||
case string:
|
case string:
|
||||||
if value, ok := table.Session().resolveConstants(value); ok {
|
if value, ok := table.Session().resolveConstants(value); ok {
|
||||||
if strings.Contains(value, ",") {
|
if strings.ContainsRune(value, ',') {
|
||||||
values := split4Values(value)
|
values := split4Values(value)
|
||||||
switch len(values) {
|
switch len(values) {
|
||||||
case 1:
|
case 1:
|
||||||
|
|
@ -1593,10 +1555,10 @@ func (table *tableViewData) writeCellHtml(adapter TableAdapter, row, column int,
|
||||||
buffer.WriteString(string(value))
|
buffer.WriteString(string(value))
|
||||||
|
|
||||||
case float32:
|
case float32:
|
||||||
buffer.WriteString(fmt.Sprintf("%g", float64(value)))
|
fmt.Fprintf(buffer, "%g", float64(value))
|
||||||
|
|
||||||
case float64:
|
case float64:
|
||||||
buffer.WriteString(fmt.Sprintf("%g", value))
|
fmt.Fprintf(buffer, "%g", value)
|
||||||
|
|
||||||
case bool:
|
case bool:
|
||||||
accentColor := Color(0)
|
accentColor := Color(0)
|
||||||
|
|
@ -1611,7 +1573,7 @@ func (table *tableViewData) writeCellHtml(adapter TableAdapter, row, column int,
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if n, ok := isInt(value); ok {
|
if n, ok := isInt(value); ok {
|
||||||
buffer.WriteString(fmt.Sprintf("%d", n))
|
fmt.Fprintf(buffer, "%d", n)
|
||||||
} else {
|
} else {
|
||||||
buffer.WriteString("<Unsupported value>")
|
buffer.WriteString("<Unsupported value>")
|
||||||
}
|
}
|
||||||
|
|
@ -1654,7 +1616,7 @@ func tableViewCurrent(view View) CellIndex {
|
||||||
|
|
||||||
func (table *tableViewData) cssStyle(self View, builder cssBuilder) {
|
func (table *tableViewData) cssStyle(self View, builder cssBuilder) {
|
||||||
session := table.Session()
|
session := table.Session()
|
||||||
table.viewData.cssViewStyle(builder, session)
|
writeViewStyleCSS(table, builder, session, false)
|
||||||
|
|
||||||
gap, ok := sizeProperty(table, Gap, session)
|
gap, ok := sizeProperty(table, Gap, session)
|
||||||
if !ok || gap.Type == Auto || gap.Value <= 0 {
|
if !ok || gap.Type == Auto || gap.Value <= 0 {
|
||||||
|
|
@ -1731,13 +1693,10 @@ func (table *tableViewData) handleCommand(self View, command PropertyName, data
|
||||||
if row, ok := dataIntProperty(data, "row"); ok && row != current.Row {
|
if row, ok := dataIntProperty(data, "row"); ok && row != current.Row {
|
||||||
current.Row = row
|
current.Row = row
|
||||||
table.setRaw(Current, current.Row)
|
table.setRaw(Current, current.Row)
|
||||||
if listener, ok := table.changeListener[Current]; ok {
|
for _, listener := range getOneArgEventListeners[TableView, int](table, nil, TableRowSelectedEvent) {
|
||||||
listener(table, Current)
|
listener.Run(table, row)
|
||||||
}
|
|
||||||
|
|
||||||
for _, listener := range GetTableRowSelectedListeners(table) {
|
|
||||||
listener(table, row)
|
|
||||||
}
|
}
|
||||||
|
table.runChangeListener(Current)
|
||||||
}
|
}
|
||||||
|
|
||||||
case "currentCell":
|
case "currentCell":
|
||||||
|
|
@ -1748,29 +1707,27 @@ func (table *tableViewData) handleCommand(self View, command PropertyName, data
|
||||||
current.Row = row
|
current.Row = row
|
||||||
current.Column = column
|
current.Column = column
|
||||||
table.setRaw(Current, current.Row)
|
table.setRaw(Current, current.Row)
|
||||||
if listener, ok := table.changeListener[Current]; ok {
|
|
||||||
listener(table, Current)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, listener := range GetTableCellSelectedListeners(table) {
|
for _, listener := range getTwoArgEventListeners[TableView, int](table, nil, TableCellSelectedEvent) {
|
||||||
listener(table, row, column)
|
listener.Run(table, row, column)
|
||||||
}
|
}
|
||||||
|
table.runChangeListener(Current)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case "rowClick":
|
case "rowClick":
|
||||||
if row, ok := dataIntProperty(data, "row"); ok {
|
if row, ok := dataIntProperty(data, "row"); ok {
|
||||||
for _, listener := range GetTableRowClickedListeners(table) {
|
for _, listener := range getOneArgEventListeners[TableView, int](table, nil, TableRowClickedEvent) {
|
||||||
listener(table, row)
|
listener.Run(table, row)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case "cellClick":
|
case "cellClick":
|
||||||
if row, ok := dataIntProperty(data, "row"); ok {
|
if row, ok := dataIntProperty(data, "row"); ok {
|
||||||
if column, ok := dataIntProperty(data, "column"); ok {
|
if column, ok := dataIntProperty(data, "column"); ok {
|
||||||
for _, listener := range GetTableCellClickedListeners(table) {
|
for _, listener := range getTwoArgEventListeners[TableView, int](table, nil, TableCellClickedEvent) {
|
||||||
listener(table, row, column)
|
listener.Run(table, row, column)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ func (cell *tableCellView) init(session Session) {
|
||||||
|
|
||||||
func (cell *tableCellView) cssStyle(self View, builder cssBuilder) {
|
func (cell *tableCellView) cssStyle(self View, builder cssBuilder) {
|
||||||
session := cell.Session()
|
session := cell.Session()
|
||||||
cell.viewData.cssViewStyle(builder, session)
|
writeViewStyleCSS(cell, builder, session, false)
|
||||||
|
|
||||||
if value, ok := enumProperty(cell, TableVerticalAlign, session, 0); ok {
|
if value, ok := enumProperty(cell, TableVerticalAlign, session, 0); ok {
|
||||||
builder.add("vertical-align", enumProperties[TableVerticalAlign].values[value])
|
builder.add("vertical-align", enumProperties[TableVerticalAlign].values[value])
|
||||||
|
|
@ -26,7 +26,9 @@ func (cell *tableCellView) cssStyle(self View, builder cssBuilder) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTableContent returns a TableAdapter which defines the TableView content.
|
// 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 {
|
func GetTableContent(view View, subviewID ...string) TableAdapter {
|
||||||
if view = getSubview(view, subviewID); view != nil {
|
if view = getSubview(view, subviewID); view != nil {
|
||||||
if content := view.getRaw(Content); content != nil {
|
if content := view.getRaw(Content); content != nil {
|
||||||
|
|
@ -34,13 +36,20 @@ func GetTableContent(view View, subviewID ...string) TableAdapter {
|
||||||
return adapter
|
return adapter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if obj := view.binding(); obj != nil {
|
||||||
|
if adapter, ok := obj.(TableAdapter); ok {
|
||||||
|
return adapter
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTableRowStyle returns a TableRowStyle which defines styles of TableView rows.
|
// 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 {
|
func GetTableRowStyle(view View, subviewID ...string) TableRowStyle {
|
||||||
if view = getSubview(view, subviewID); view != nil {
|
if view = getSubview(view, subviewID); view != nil {
|
||||||
for _, tag := range []PropertyName{RowStyle, Content} {
|
for _, tag := range []PropertyName{RowStyle, Content} {
|
||||||
|
|
@ -56,7 +65,9 @@ func GetTableRowStyle(view View, subviewID ...string) TableRowStyle {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTableColumnStyle returns a TableColumnStyle which defines styles of TableView columns.
|
// 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 {
|
func GetTableColumnStyle(view View, subviewID ...string) TableColumnStyle {
|
||||||
if view = getSubview(view, subviewID); view != nil {
|
if view = getSubview(view, subviewID); view != nil {
|
||||||
for _, tag := range []PropertyName{ColumnStyle, Content} {
|
for _, tag := range []PropertyName{ColumnStyle, Content} {
|
||||||
|
|
@ -72,7 +83,9 @@ func GetTableColumnStyle(view View, subviewID ...string) TableColumnStyle {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTableCellStyle returns a TableCellStyle which defines styles of TableView cells.
|
// 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 {
|
func GetTableCellStyle(view View, subviewID ...string) TableCellStyle {
|
||||||
if view = getSubview(view, subviewID); view != nil {
|
if view = getSubview(view, subviewID); view != nil {
|
||||||
for _, tag := range []PropertyName{CellStyle, Content} {
|
for _, tag := range []PropertyName{CellStyle, Content} {
|
||||||
|
|
@ -90,26 +103,34 @@ func GetTableCellStyle(view View, subviewID ...string) TableCellStyle {
|
||||||
|
|
||||||
// GetTableSelectionMode returns the mode of the TableView elements selection.
|
// GetTableSelectionMode returns the mode of the TableView elements selection.
|
||||||
// Valid values are NoneSelection (0), CellSelection (1), and RowSelection (2).
|
// 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 {
|
func GetTableSelectionMode(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, SelectionMode, NoneSelection, false)
|
return enumStyledProperty(view, subviewID, SelectionMode, NoneSelection, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTableVerticalAlign returns a vertical align in a TableView cell. Returns one of next values:
|
// GetTableVerticalAlign returns a vertical align in a TableView cell. Returns one of next values:
|
||||||
// TopAlign (0), BottomAlign (1), CenterAlign (2), and BaselineAlign (3)
|
// 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 {
|
func GetTableVerticalAlign(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, TableVerticalAlign, TopAlign, false)
|
return enumStyledProperty(view, subviewID, TableVerticalAlign, TopAlign, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTableHeadHeight returns the number of rows in the table header.
|
// 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 {
|
func GetTableHeadHeight(view View, subviewID ...string) int {
|
||||||
return intStyledProperty(view, subviewID, HeadHeight, 0)
|
return intStyledProperty(view, subviewID, HeadHeight, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTableFootHeight returns the number of rows in the table footer.
|
// 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 {
|
func GetTableFootHeight(view View, subviewID ...string) int {
|
||||||
return intStyledProperty(view, subviewID, FootHeight, 0)
|
return intStyledProperty(view, subviewID, FootHeight, 0)
|
||||||
}
|
}
|
||||||
|
|
@ -118,7 +139,9 @@ func GetTableFootHeight(view View, subviewID ...string) int {
|
||||||
// If there is no selected cell/row or the selection mode is NoneSelection (0),
|
// 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.
|
// 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 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 {
|
func GetTableCurrent(view View, subviewID ...string) CellIndex {
|
||||||
if view = getSubview(view, subviewID); view != nil {
|
if view = getSubview(view, subviewID); view != nil {
|
||||||
if selectionMode := GetTableSelectionMode(view); selectionMode != NoneSelection {
|
if selectionMode := GetTableSelectionMode(view); selectionMode != NoneSelection {
|
||||||
|
|
@ -130,30 +153,70 @@ func GetTableCurrent(view View, subviewID ...string) CellIndex {
|
||||||
|
|
||||||
// GetTableCellClickedListeners returns listeners of event which occurs when the user clicks on a table cell.
|
// 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 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) {
|
// Result elements can be of the following types:
|
||||||
return getTwoArgEventListeners[TableView, int](view, subviewID, TableCellClickedEvent)
|
// - 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.
|
// 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 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) {
|
// Result elements can be of the following types:
|
||||||
return getTwoArgEventListeners[TableView, int](view, subviewID, TableCellSelectedEvent)
|
// - 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.
|
// 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 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[TableView, int](view, subviewID, TableRowClickedEvent)
|
// - 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.
|
// 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 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[TableView, int](view, subviewID, TableRowSelectedEvent)
|
// - 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
|
// ReloadTableViewData updates TableView
|
||||||
|
|
|
||||||
220
tabsLayout.go
220
tabsLayout.go
|
|
@ -163,6 +163,7 @@ func (tabsLayout *tabsLayoutData) init(session Session) {
|
||||||
tabsLayout.viewsContainerData.init(session)
|
tabsLayout.viewsContainerData.init(session)
|
||||||
tabsLayout.tag = "TabsLayout"
|
tabsLayout.tag = "TabsLayout"
|
||||||
tabsLayout.systemClass = "ruiTabsLayout"
|
tabsLayout.systemClass = "ruiTabsLayout"
|
||||||
|
tabsLayout.get = tabsLayout.getFunc
|
||||||
tabsLayout.set = tabsLayout.setFunc
|
tabsLayout.set = tabsLayout.setFunc
|
||||||
tabsLayout.changed = tabsLayout.propertyChanged
|
tabsLayout.changed = tabsLayout.propertyChanged
|
||||||
}
|
}
|
||||||
|
|
@ -172,6 +173,23 @@ func tabsLayoutCurrent(view View, defaultValue int) int {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tabsLayout *tabsLayoutData) getFunc(tag PropertyName) any {
|
||||||
|
switch tag {
|
||||||
|
case CurrentTabChangedEvent:
|
||||||
|
if listeners := getTwoArgEventRawListeners[TabsLayout, int](tabsLayout, nil, tag); len(listeners) > 0 {
|
||||||
|
return listeners
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case TabCloseEvent:
|
||||||
|
if listeners := getOneArgEventRawListeners[TabsLayout, int](tabsLayout, nil, tag); len(listeners) > 0 {
|
||||||
|
return listeners
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return tabsLayout.viewsContainerData.getFunc(tag)
|
||||||
|
}
|
||||||
|
|
||||||
func (tabsLayout *tabsLayoutData) setFunc(tag PropertyName, value any) []PropertyName {
|
func (tabsLayout *tabsLayoutData) setFunc(tag PropertyName, value any) []PropertyName {
|
||||||
switch tag {
|
switch tag {
|
||||||
case CurrentTabChangedEvent:
|
case CurrentTabChangedEvent:
|
||||||
|
|
@ -211,7 +229,7 @@ func (tabsLayout *tabsLayoutData) propertyChanged(tag PropertyName) {
|
||||||
if listeners := getTwoArgEventListeners[TabsLayout, int](tabsLayout, nil, CurrentTabChangedEvent); len(listeners) > 0 {
|
if listeners := getTwoArgEventListeners[TabsLayout, int](tabsLayout, nil, CurrentTabChangedEvent); len(listeners) > 0 {
|
||||||
oldCurrent, _ := intProperty(tabsLayout, "old-current", session, -1)
|
oldCurrent, _ := intProperty(tabsLayout, "old-current", session, -1)
|
||||||
for _, listener := range listeners {
|
for _, listener := range listeners {
|
||||||
listener(tabsLayout, current, oldCurrent)
|
listener.Run(tabsLayout, current, oldCurrent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -238,155 +256,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 {
|
func (tabsLayout *tabsLayoutData) tabsLocation() int {
|
||||||
tabs, _ := enumProperty(tabsLayout, Tabs, tabsLayout.session, 0)
|
tabs, _ := enumProperty(tabsLayout, Tabs, tabsLayout.session, 0)
|
||||||
return tabs
|
return tabs
|
||||||
|
|
@ -504,7 +373,7 @@ func (tabsLayout *tabsLayoutData) ListItem(index int, session Session) View {
|
||||||
Content: "✕",
|
Content: "✕",
|
||||||
ClickEvent: func() {
|
ClickEvent: func() {
|
||||||
for _, listener := range getOneArgEventListeners[TabsLayout, int](tabsLayout, nil, TabCloseEvent) {
|
for _, listener := range getOneArgEventListeners[TabsLayout, int](tabsLayout, nil, TabCloseEvent) {
|
||||||
listener(tabsLayout, index)
|
listener.Run(tabsLayout, index)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
@ -552,9 +421,7 @@ func (tabsLayout *tabsLayoutData) Append(view View) {
|
||||||
view.SetChangeListener(TabCloseButton, tabsLayout.updateTabCloseButton)
|
view.SetChangeListener(TabCloseButton, tabsLayout.updateTabCloseButton)
|
||||||
if tabsLayout.created {
|
if tabsLayout.created {
|
||||||
updateInnerHTML(tabsLayout.htmlID(), tabsLayout.Session())
|
updateInnerHTML(tabsLayout.htmlID(), tabsLayout.Session())
|
||||||
if listener, ok := tabsLayout.changeListener[Content]; ok {
|
tabsLayout.runChangeListener(Content)
|
||||||
listener(tabsLayout, Content)
|
|
||||||
}
|
|
||||||
tabsLayout.Set(Current, len(tabsLayout.views)-1)
|
tabsLayout.Set(Current, len(tabsLayout.views)-1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -576,11 +443,9 @@ func (tabsLayout *tabsLayoutData) Insert(view View, index int) {
|
||||||
|
|
||||||
func (tabsLayout *tabsLayoutData) currentChanged(newCurrent, oldCurrent int) {
|
func (tabsLayout *tabsLayoutData) currentChanged(newCurrent, oldCurrent int) {
|
||||||
for _, listener := range getTwoArgEventListeners[TabsLayout, int](tabsLayout, nil, CurrentTabChangedEvent) {
|
for _, listener := range getTwoArgEventListeners[TabsLayout, int](tabsLayout, nil, CurrentTabChangedEvent) {
|
||||||
listener(tabsLayout, newCurrent, oldCurrent)
|
listener.Run(tabsLayout, newCurrent, oldCurrent)
|
||||||
}
|
|
||||||
if listener, ok := tabsLayout.changeListener[Current]; ok {
|
|
||||||
listener(tabsLayout, Current)
|
|
||||||
}
|
}
|
||||||
|
tabsLayout.runChangeListener(Current)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove removes view from list and return it
|
// Remove removes view from list and return it
|
||||||
|
|
@ -606,12 +471,11 @@ func (tabsLayout *tabsLayoutData) RemoveView(index int) View {
|
||||||
tabsLayout.Set(Current, newCurrent)
|
tabsLayout.Set(Current, newCurrent)
|
||||||
}
|
}
|
||||||
updateInnerHTML(tabsLayout.htmlID(), tabsLayout.Session())
|
updateInnerHTML(tabsLayout.htmlID(), tabsLayout.Session())
|
||||||
if listener, ok := tabsLayout.changeListener[Content]; ok {
|
tabsLayout.runChangeListener(Content)
|
||||||
listener(tabsLayout, Content)
|
|
||||||
}
|
|
||||||
} else if newCurrent != oldCurrent {
|
} else if newCurrent != oldCurrent {
|
||||||
tabsLayout.setRaw(Current, newCurrent)
|
tabsLayout.setRaw(Current, newCurrent)
|
||||||
}
|
}
|
||||||
|
return view
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
|
@ -892,7 +756,7 @@ func (tabsLayout *tabsLayoutData) handleCommand(self View, command PropertyName,
|
||||||
if numberText, ok := data.PropertyValue("number"); ok {
|
if numberText, ok := data.PropertyValue("number"); ok {
|
||||||
if number, err := strconv.Atoi(numberText); err == nil {
|
if number, err := strconv.Atoi(numberText); err == nil {
|
||||||
for _, listener := range getOneArgEventListeners[TabsLayout, int](tabsLayout, nil, TabCloseEvent) {
|
for _, listener := range getOneArgEventListeners[TabsLayout, int](tabsLayout, nil, TabCloseEvent) {
|
||||||
listener(tabsLayout, number)
|
listener.Run(tabsLayout, number)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -900,3 +764,35 @@ func (tabsLayout *tabsLayoutData) handleCommand(self View, command PropertyName,
|
||||||
}
|
}
|
||||||
return tabsLayout.viewsContainerData.handleCommand(self, command, data)
|
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:
|
// GetTextOverflow returns a value of the "text-overflow" property:
|
||||||
// TextOverflowClip (0) or TextOverflowEllipsis (1).
|
// 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 {
|
func GetTextOverflow(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, TextOverflow, SingleLineText, false)
|
return enumStyledProperty(view, subviewID, TextOverflow, SingleLineText, false)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
87
theme.go
87
theme.go
|
|
@ -661,7 +661,7 @@ func (theme *theme) cssText(session Session) string {
|
||||||
for _, tag := range styleList(theme.styles) {
|
for _, tag := range styleList(theme.styles) {
|
||||||
if style := theme.styles[tag]; style != nil {
|
if style := theme.styles[tag]; style != nil {
|
||||||
builder.startStyle(tag)
|
builder.startStyle(tag)
|
||||||
style.cssViewStyle(&builder, session)
|
writeViewStyleCSS(style, &builder, session, false)
|
||||||
builder.endStyle()
|
builder.endStyle()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -671,7 +671,7 @@ func (theme *theme) cssText(session Session) string {
|
||||||
for _, tag := range styleList(media.styles) {
|
for _, tag := range styleList(media.styles) {
|
||||||
if style := media.styles[tag]; style != nil {
|
if style := media.styles[tag]; style != nil {
|
||||||
builder.startStyle(tag)
|
builder.startStyle(tag)
|
||||||
style.cssViewStyle(&builder, session)
|
writeViewStyleCSS(style, &builder, session, false)
|
||||||
builder.endStyle()
|
builder.endStyle()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -686,20 +686,20 @@ func (theme *theme) addText(themeText string) bool {
|
||||||
theme.init()
|
theme.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
data := ParseDataText(themeText)
|
data, err := ParseDataText(themeText)
|
||||||
if data == nil || !data.IsObject() || data.Tag() != "theme" {
|
if err != nil {
|
||||||
|
ErrorLog(err.Error())
|
||||||
|
return false
|
||||||
|
} else if !data.IsObject() || data.Tag() != "theme" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
count := data.PropertyCount()
|
|
||||||
|
|
||||||
objToStyle := func(obj DataObject) ViewStyle {
|
objToStyle := func(obj DataObject) ViewStyle {
|
||||||
params := Params{}
|
params := Params{}
|
||||||
for i := 0; i < obj.PropertyCount(); i++ {
|
for node := range obj.Properties() {
|
||||||
if node := obj.Property(i); node != nil {
|
|
||||||
switch node.Type() {
|
switch node.Type() {
|
||||||
case ArrayNode:
|
case ArrayNode:
|
||||||
params[PropertyName(node.Tag())] = node.ArrayElements()
|
params[PropertyName(node.Tag())] = node.Array()
|
||||||
|
|
||||||
case ObjectNode:
|
case ObjectNode:
|
||||||
params[PropertyName(node.Tag())] = node.Object()
|
params[PropertyName(node.Tag())] = node.Object()
|
||||||
|
|
@ -708,19 +708,16 @@ func (theme *theme) addText(themeText string) bool {
|
||||||
params[PropertyName(node.Tag())] = node.Text()
|
params[PropertyName(node.Tag())] = node.Text()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return NewViewStyle(params)
|
return NewViewStyle(params)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < count; i++ {
|
for d := range data.Properties() {
|
||||||
if d := data.Property(i); d != nil {
|
|
||||||
switch tag := d.Tag(); tag {
|
switch tag := d.Tag(); tag {
|
||||||
case "constants":
|
case "constants":
|
||||||
if d.Type() == ObjectNode {
|
if d.Type() == ObjectNode {
|
||||||
if obj := d.Object(); obj != nil {
|
if obj := d.Object(); obj != nil {
|
||||||
objCount := obj.PropertyCount()
|
for prop := range obj.Properties() {
|
||||||
for k := 0; k < objCount; k++ {
|
if prop.Type() == TextNode {
|
||||||
if prop := obj.Property(k); prop != nil && prop.Type() == TextNode {
|
|
||||||
theme.constants[prop.Tag()] = prop.Text()
|
theme.constants[prop.Tag()] = prop.Text()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -730,9 +727,8 @@ func (theme *theme) addText(themeText string) bool {
|
||||||
case "constants:touch":
|
case "constants:touch":
|
||||||
if d.Type() == ObjectNode {
|
if d.Type() == ObjectNode {
|
||||||
if obj := d.Object(); obj != nil {
|
if obj := d.Object(); obj != nil {
|
||||||
objCount := obj.PropertyCount()
|
for prop := range obj.Properties() {
|
||||||
for k := 0; k < objCount; k++ {
|
if prop.Type() == TextNode {
|
||||||
if prop := obj.Property(k); prop != nil && prop.Type() == TextNode {
|
|
||||||
theme.touchConstants[prop.Tag()] = prop.Text()
|
theme.touchConstants[prop.Tag()] = prop.Text()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -742,9 +738,8 @@ func (theme *theme) addText(themeText string) bool {
|
||||||
case "colors":
|
case "colors":
|
||||||
if d.Type() == ObjectNode {
|
if d.Type() == ObjectNode {
|
||||||
if obj := d.Object(); obj != nil {
|
if obj := d.Object(); obj != nil {
|
||||||
objCount := obj.PropertyCount()
|
for prop := range obj.Properties() {
|
||||||
for k := 0; k < objCount; k++ {
|
if prop.Type() == TextNode {
|
||||||
if prop := obj.Property(k); prop != nil && prop.Type() == TextNode {
|
|
||||||
theme.colors[prop.Tag()] = prop.Text()
|
theme.colors[prop.Tag()] = prop.Text()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -754,9 +749,8 @@ func (theme *theme) addText(themeText string) bool {
|
||||||
case "colors:dark":
|
case "colors:dark":
|
||||||
if d.Type() == ObjectNode {
|
if d.Type() == ObjectNode {
|
||||||
if obj := d.Object(); obj != nil {
|
if obj := d.Object(); obj != nil {
|
||||||
objCount := obj.PropertyCount()
|
for prop := range obj.Properties() {
|
||||||
for k := 0; k < objCount; k++ {
|
if prop.Type() == TextNode {
|
||||||
if prop := obj.Property(k); prop != nil && prop.Type() == TextNode {
|
|
||||||
theme.darkColors[prop.Tag()] = prop.Text()
|
theme.darkColors[prop.Tag()] = prop.Text()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -766,9 +760,8 @@ func (theme *theme) addText(themeText string) bool {
|
||||||
case "images":
|
case "images":
|
||||||
if d.Type() == ObjectNode {
|
if d.Type() == ObjectNode {
|
||||||
if obj := d.Object(); obj != nil {
|
if obj := d.Object(); obj != nil {
|
||||||
objCount := obj.PropertyCount()
|
for prop := range obj.Properties() {
|
||||||
for k := 0; k < objCount; k++ {
|
if prop.Type() == TextNode {
|
||||||
if prop := obj.Property(k); prop != nil && prop.Type() == TextNode {
|
|
||||||
theme.images[prop.Tag()] = prop.Text()
|
theme.images[prop.Tag()] = prop.Text()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -778,9 +771,8 @@ func (theme *theme) addText(themeText string) bool {
|
||||||
case "images:dark":
|
case "images:dark":
|
||||||
if d.Type() == ObjectNode {
|
if d.Type() == ObjectNode {
|
||||||
if obj := d.Object(); obj != nil {
|
if obj := d.Object(); obj != nil {
|
||||||
objCount := obj.PropertyCount()
|
for prop := range obj.Properties() {
|
||||||
for k := 0; k < objCount; k++ {
|
if prop.Type() == TextNode {
|
||||||
if prop := obj.Property(k); prop != nil && prop.Type() == TextNode {
|
|
||||||
theme.darkImages[prop.Tag()] = prop.Text()
|
theme.darkImages[prop.Tag()] = prop.Text()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -790,7 +782,7 @@ func (theme *theme) addText(themeText string) bool {
|
||||||
case "styles":
|
case "styles":
|
||||||
if d.Type() == ArrayNode {
|
if d.Type() == ArrayNode {
|
||||||
arraySize := d.ArraySize()
|
arraySize := d.ArraySize()
|
||||||
for k := 0; k < arraySize; k++ {
|
for k := range arraySize {
|
||||||
if element := d.ArrayElement(k); element != nil && element.IsObject() {
|
if element := d.ArrayElement(k); element != nil && element.IsObject() {
|
||||||
if obj := element.Object(); obj != nil {
|
if obj := element.Object(); obj != nil {
|
||||||
theme.styles[obj.Tag()] = objToStyle(obj)
|
theme.styles[obj.Tag()] = objToStyle(obj)
|
||||||
|
|
@ -803,7 +795,7 @@ func (theme *theme) addText(themeText string) bool {
|
||||||
if d.Type() == ArrayNode && strings.HasPrefix(tag, "styles:") {
|
if d.Type() == ArrayNode && strings.HasPrefix(tag, "styles:") {
|
||||||
if rule, ok := parseMediaRule(tag); ok {
|
if rule, ok := parseMediaRule(tag); ok {
|
||||||
arraySize := d.ArraySize()
|
arraySize := d.ArraySize()
|
||||||
for k := 0; k < arraySize; k++ {
|
for k := range arraySize {
|
||||||
if element := d.ArrayElement(k); element != nil && element.IsObject() {
|
if element := d.ArrayElement(k); element != nil && element.IsObject() {
|
||||||
if obj := element.Object(); obj != nil {
|
if obj := element.Object(); obj != nil {
|
||||||
rule.styles[obj.Tag()] = objToStyle(obj)
|
rule.styles[obj.Tag()] = objToStyle(obj)
|
||||||
|
|
@ -815,7 +807,6 @@ func (theme *theme) addText(themeText string) bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
theme.sortMediaStyles()
|
theme.sortMediaStyles()
|
||||||
return true
|
return true
|
||||||
|
|
@ -899,19 +890,9 @@ func (theme *theme) String() string {
|
||||||
defer freeStringBuilder(buffer)
|
defer freeStringBuilder(buffer)
|
||||||
|
|
||||||
writeString := func(text string) {
|
writeString := func(text string) {
|
||||||
if strings.ContainsAny(text, " \t\n\r\\\"'`,;{}[]()") {
|
if isQuotesNeeded(text) {
|
||||||
replace := []struct{ old, new string }{
|
|
||||||
{old: "\\", new: `\\`},
|
|
||||||
{old: "\t", new: `\t`},
|
|
||||||
{old: "\r", new: `\r`},
|
|
||||||
{old: "\n", new: `\n`},
|
|
||||||
{old: "\"", new: `\"`},
|
|
||||||
}
|
|
||||||
for _, s := range replace {
|
|
||||||
text = strings.Replace(text, s.old, s.new, -1)
|
|
||||||
}
|
|
||||||
buffer.WriteRune('"')
|
buffer.WriteRune('"')
|
||||||
buffer.WriteString(text)
|
buffer.WriteString(replaceEscapeSymbols(text))
|
||||||
buffer.WriteRune('"')
|
buffer.WriteRune('"')
|
||||||
} else {
|
} else {
|
||||||
buffer.WriteString(text)
|
buffer.WriteString(text)
|
||||||
|
|
@ -975,15 +956,15 @@ func (theme *theme) String() string {
|
||||||
buffer.WriteString(":landscape")
|
buffer.WriteString(":landscape")
|
||||||
}
|
}
|
||||||
if maxWidth > 0 {
|
if maxWidth > 0 {
|
||||||
buffer.WriteString(fmt.Sprintf(":width%d", maxWidth))
|
fmt.Fprintf(buffer, ":width%d", maxWidth)
|
||||||
}
|
}
|
||||||
if maxHeight > 0 {
|
if maxHeight > 0 {
|
||||||
buffer.WriteString(fmt.Sprintf(":height%d", maxHeight))
|
fmt.Fprintf(buffer, ":height%d", maxHeight)
|
||||||
}
|
}
|
||||||
buffer.WriteString(" = [\n")
|
buffer.WriteString(" = [\n")
|
||||||
|
|
||||||
for _, tag := range tags {
|
for _, tag := range tags {
|
||||||
if style, ok := styles[tag]; ok && len(style.AllTags()) > 0 {
|
if style, ok := styles[tag]; ok && !style.IsEmpty() {
|
||||||
buffer.WriteString("\t\t")
|
buffer.WriteString("\t\t")
|
||||||
writeViewStyle(tag, style, buffer, "\t\t", nil)
|
writeViewStyle(tag, style, buffer, "\t\t", nil)
|
||||||
buffer.WriteString(",\n")
|
buffer.WriteString(",\n")
|
||||||
|
|
@ -1014,27 +995,27 @@ func (theme *theme) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
if media.MinWidth > 0 {
|
if media.MinWidth > 0 {
|
||||||
buffer.WriteString(fmt.Sprintf(":width%d-", media.MinWidth))
|
fmt.Fprintf(buffer, ":width%d-", media.MinWidth)
|
||||||
if media.MaxWidth > 0 {
|
if media.MaxWidth > 0 {
|
||||||
buffer.WriteString(strconv.Itoa(media.MaxWidth))
|
buffer.WriteString(strconv.Itoa(media.MaxWidth))
|
||||||
}
|
}
|
||||||
} else if media.MaxWidth > 0 {
|
} else if media.MaxWidth > 0 {
|
||||||
buffer.WriteString(fmt.Sprintf(":width%d", media.MaxWidth))
|
fmt.Fprintf(buffer, ":width%d", media.MaxWidth)
|
||||||
}
|
}
|
||||||
|
|
||||||
if media.MinHeight > 0 {
|
if media.MinHeight > 0 {
|
||||||
buffer.WriteString(fmt.Sprintf(":height%d-", media.MinHeight))
|
fmt.Fprintf(buffer, ":height%d-", media.MinHeight)
|
||||||
if media.MaxHeight > 0 {
|
if media.MaxHeight > 0 {
|
||||||
buffer.WriteString(strconv.Itoa(media.MaxHeight))
|
buffer.WriteString(strconv.Itoa(media.MaxHeight))
|
||||||
}
|
}
|
||||||
} else if media.MaxHeight > 0 {
|
} else if media.MaxHeight > 0 {
|
||||||
buffer.WriteString(fmt.Sprintf(":height%d", media.MaxHeight))
|
fmt.Fprintf(buffer, ":height%d", media.MaxHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.WriteString(" = [\n")
|
buffer.WriteString(" = [\n")
|
||||||
|
|
||||||
for _, tag := range tags {
|
for _, tag := range tags {
|
||||||
if style, ok := media.styles[tag]; ok && len(style.AllTags()) > 0 {
|
if style, ok := media.styles[tag]; ok && !style.IsEmpty() {
|
||||||
buffer.WriteString("\t\t")
|
buffer.WriteString("\t\t")
|
||||||
writeViewStyle(tag, style, buffer, "\t\t", nil)
|
writeViewStyle(tag, style, buffer, "\t\t", nil)
|
||||||
buffer.WriteString(",\n")
|
buffer.WriteString(",\n")
|
||||||
|
|
|
||||||
|
|
@ -120,6 +120,7 @@ func (picker *timePickerData) init(session Session) {
|
||||||
picker.tag = "TimePicker"
|
picker.tag = "TimePicker"
|
||||||
picker.hasHtmlDisabled = true
|
picker.hasHtmlDisabled = true
|
||||||
picker.normalize = normalizeTimePickerTag
|
picker.normalize = normalizeTimePickerTag
|
||||||
|
picker.get = picker.getFunc
|
||||||
picker.set = picker.setFunc
|
picker.set = picker.setFunc
|
||||||
picker.changed = picker.propertyChanged
|
picker.changed = picker.propertyChanged
|
||||||
}
|
}
|
||||||
|
|
@ -167,6 +168,17 @@ func stringToTime(value string) (time.Time, bool) {
|
||||||
return result, true
|
return result, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (picker *timePickerData) getFunc(tag PropertyName) any {
|
||||||
|
switch tag {
|
||||||
|
case TimeChangedEvent:
|
||||||
|
if listeners := getTwoArgEventRawListeners[TimePicker, time.Time](picker, nil, tag); len(listeners) > 0 {
|
||||||
|
return listeners
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return picker.viewData.getFunc(tag)
|
||||||
|
}
|
||||||
|
|
||||||
func (picker *timePickerData) setFunc(tag PropertyName, value any) []PropertyName {
|
func (picker *timePickerData) setFunc(tag PropertyName, value any) []PropertyName {
|
||||||
|
|
||||||
setTimeValue := func(tag PropertyName) []PropertyName {
|
setTimeValue := func(tag PropertyName) []PropertyName {
|
||||||
|
|
@ -176,7 +188,7 @@ func (picker *timePickerData) setFunc(tag PropertyName, value any) []PropertyNam
|
||||||
return []PropertyName{tag}
|
return []PropertyName{tag}
|
||||||
|
|
||||||
case string:
|
case string:
|
||||||
if isConstantName(value) {
|
if ok, _ := isConstantName(value); ok {
|
||||||
picker.setRaw(tag, value)
|
picker.setRaw(tag, value)
|
||||||
return []PropertyName{tag}
|
return []PropertyName{tag}
|
||||||
}
|
}
|
||||||
|
|
@ -246,7 +258,7 @@ func (picker *timePickerData) propertyChanged(tag PropertyName) {
|
||||||
value := GetTimePickerValue(picker)
|
value := GetTimePickerValue(picker)
|
||||||
session.callFunc("setInputValue", picker.htmlID(), value.Format(timeFormat))
|
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()
|
oldTime := time.Now()
|
||||||
if val := picker.getRaw("old-time"); val != nil {
|
if val := picker.getRaw("old-time"); val != nil {
|
||||||
if time, ok := val.(time.Time); ok {
|
if time, ok := val.(time.Time); ok {
|
||||||
|
|
@ -254,7 +266,7 @@ func (picker *timePickerData) propertyChanged(tag PropertyName) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, listener := range listeners {
|
for _, listener := range listeners {
|
||||||
listener(picker, value, oldTime)
|
listener.Run(picker, value, oldTime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -320,13 +332,10 @@ func (picker *timePickerData) handleCommand(self View, command PropertyName, dat
|
||||||
oldValue := GetTimePickerValue(picker)
|
oldValue := GetTimePickerValue(picker)
|
||||||
picker.properties[TimePickerValue] = value
|
picker.properties[TimePickerValue] = value
|
||||||
if value != oldValue {
|
if value != oldValue {
|
||||||
for _, listener := range GetTimeChangedListeners(picker) {
|
for _, listener := range getTwoArgEventListeners[TimePicker, time.Time](picker, nil, TimeChangedEvent) {
|
||||||
listener(picker, value, oldValue)
|
listener.Run(picker, value, oldValue)
|
||||||
}
|
}
|
||||||
if listener, ok := picker.changeListener[TimePickerValue]; ok {
|
picker.runChangeListener(TimePickerValue)
|
||||||
listener(picker, TimePickerValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -373,7 +382,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,
|
// 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.
|
// "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) {
|
func GetTimePickerMin(view View, subviewID ...string) (time.Time, bool) {
|
||||||
if view = getSubview(view, subviewID); view != nil {
|
if view = getSubview(view, subviewID); view != nil {
|
||||||
return getTimeProperty(view, TimePickerMin, Min)
|
return getTimeProperty(view, TimePickerMin, Min)
|
||||||
|
|
@ -383,7 +394,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,
|
// 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.
|
// "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) {
|
func GetTimePickerMax(view View, subviewID ...string) (time.Time, bool) {
|
||||||
if view = getSubview(view, subviewID); view != nil {
|
if view = getSubview(view, subviewID); view != nil {
|
||||||
return getTimeProperty(view, TimePickerMax, Max)
|
return getTimeProperty(view, TimePickerMax, Max)
|
||||||
|
|
@ -392,13 +405,17 @@ func GetTimePickerMax(view View, subviewID ...string) (time.Time, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTimePickerStep returns the time changing step in seconds of TimePicker subview.
|
// 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 {
|
func GetTimePickerStep(view View, subviewID ...string) int {
|
||||||
return intStyledProperty(view, subviewID, TimePickerStep, 60)
|
return intStyledProperty(view, subviewID, TimePickerStep, 60)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTimePickerValue returns the time of TimePicker subview.
|
// 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 {
|
func GetTimePickerValue(view View, subviewID ...string) time.Time {
|
||||||
if view = getSubview(view, subviewID); view == nil {
|
if view = getSubview(view, subviewID); view == nil {
|
||||||
return time.Now()
|
return time.Now()
|
||||||
|
|
@ -409,7 +426,18 @@ func GetTimePickerValue(view View, subviewID ...string) time.Time {
|
||||||
|
|
||||||
// GetTimeChangedListeners returns the TimeChangedListener list of an TimePicker subview.
|
// GetTimeChangedListeners returns the TimeChangedListener list of an TimePicker subview.
|
||||||
// If there are no listeners then the empty list is returned
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getTwoArgEventListeners[TimePicker, time.Time](view, subviewID, TimeChangedEvent)
|
// - 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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,7 @@ func (event *TouchEvent) init(data DataObject) {
|
||||||
event.Touches = []Touch{}
|
event.Touches = []Touch{}
|
||||||
event.TimeStamp = getTimeStamp(data)
|
event.TimeStamp = getTimeStamp(data)
|
||||||
if node := data.PropertyByTag("touches"); node != nil && node.Type() == ArrayNode {
|
if node := data.PropertyByTag("touches"); node != nil && node.Type() == ArrayNode {
|
||||||
for i := 0; i < node.ArraySize(); i++ {
|
for i := range node.ArraySize() {
|
||||||
if element := node.ArrayElement(i); element != nil && element.IsObject() {
|
if element := node.ArrayElement(i); element != nil && element.IsObject() {
|
||||||
if obj := element.Object(); obj != nil {
|
if obj := element.Object(); obj != nil {
|
||||||
var touch Touch
|
var touch Touch
|
||||||
|
|
@ -193,30 +193,66 @@ func handleTouchEvents(view View, tag PropertyName, data DataObject) {
|
||||||
event.init(data)
|
event.init(data)
|
||||||
|
|
||||||
for _, listener := range listeners {
|
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.
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[View, TouchEvent](view, subviewID, TouchStart)
|
// - 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.
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[View, TouchEvent](view, subviewID, TouchEnd)
|
// - 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.
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[View, TouchEvent](view, subviewID, TouchMove)
|
// - 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.
|
// 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) {
|
// Result elements can be of the following types:
|
||||||
return getOneArgEventListeners[View, TouchEvent](view, subviewID, TouchCancel)
|
// - 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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
66
transform.go
66
transform.go
|
|
@ -328,23 +328,6 @@ func (transform *transformPropertyData) String() string {
|
||||||
return buffer.String()
|
return buffer.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (transform *transformPropertyData) writeString(buffer *strings.Builder, indent string) {
|
|
||||||
buffer.WriteString("_{ ")
|
|
||||||
comma := false
|
|
||||||
for _, tag := range transform.supportedProperties {
|
|
||||||
if value, ok := transform.properties[tag]; ok {
|
|
||||||
if comma {
|
|
||||||
buffer.WriteString(", ")
|
|
||||||
}
|
|
||||||
buffer.WriteString(string(tag))
|
|
||||||
buffer.WriteString(" = ")
|
|
||||||
writePropertyValue(buffer, tag, value, indent)
|
|
||||||
comma = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buffer.WriteString(" }")
|
|
||||||
}
|
|
||||||
|
|
||||||
func transformSet(properties Properties, tag PropertyName, value any) []PropertyName {
|
func transformSet(properties Properties, tag PropertyName, value any) []PropertyName {
|
||||||
switch tag {
|
switch tag {
|
||||||
|
|
||||||
|
|
@ -369,8 +352,8 @@ func valueToTransformProperty(value any) TransformProperty {
|
||||||
parseObject := func(obj DataObject) TransformProperty {
|
parseObject := func(obj DataObject) TransformProperty {
|
||||||
transform := NewTransformProperty(nil)
|
transform := NewTransformProperty(nil)
|
||||||
ok := true
|
ok := true
|
||||||
for i := 0; i < obj.PropertyCount(); i++ {
|
for prop := range obj.Properties() {
|
||||||
if prop := obj.Property(i); prop.Type() == TextNode {
|
if prop.Type() == TextNode {
|
||||||
if !transform.Set(PropertyName(prop.Tag()), prop.Text()) {
|
if !transform.Set(PropertyName(prop.Tag()), prop.Text()) {
|
||||||
ok = false
|
ok = false
|
||||||
}
|
}
|
||||||
|
|
@ -379,7 +362,7 @@ func valueToTransformProperty(value any) TransformProperty {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ok && transform.empty() {
|
if !ok && transform.IsEmpty() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return transform
|
return transform
|
||||||
|
|
@ -398,7 +381,10 @@ func valueToTransformProperty(value any) TransformProperty {
|
||||||
}
|
}
|
||||||
|
|
||||||
case string:
|
case string:
|
||||||
if obj := ParseDataText(value); obj != nil {
|
obj, err := ParseDataText(value)
|
||||||
|
if err != nil {
|
||||||
|
ErrorLog(err.Error())
|
||||||
|
} else {
|
||||||
return parseObject(obj)
|
return parseObject(obj)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -570,7 +556,7 @@ func (transform *transformPropertyData) transformCSS(session Session) string {
|
||||||
return result[:length-1]
|
return result[:length-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (style *viewStyle) writeViewTransformCSS(builder cssBuilder, session Session) {
|
func writeViewTransformCSS(style Properties, builder cssBuilder, session Session) {
|
||||||
x, y := getPerspectiveOrigin(style, session)
|
x, y := getPerspectiveOrigin(style, session)
|
||||||
z := AutoSize()
|
z := AutoSize()
|
||||||
if css := transformOriginCSS(x, y, z, session); css != "" {
|
if css := transformOriginCSS(x, y, z, session); css != "" {
|
||||||
|
|
@ -647,7 +633,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.
|
// 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)
|
// 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 {
|
func GetTransform(view View, subviewID ...string) TransformProperty {
|
||||||
return transformStyledProperty(view, subviewID, Transform)
|
return transformStyledProperty(view, subviewID, Transform)
|
||||||
}
|
}
|
||||||
|
|
@ -655,14 +643,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
|
// 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.
|
// 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).
|
// 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 {
|
func GetPerspective(view View, subviewID ...string) SizeUnit {
|
||||||
return sizeStyledProperty(view, subviewID, Perspective, false)
|
return sizeStyledProperty(view, subviewID, Perspective, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPerspectiveOrigin returns a x- and y-coordinate of the position at which the viewer is looking.
|
// 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%).
|
// 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) {
|
func GetPerspectiveOrigin(view View, subviewID ...string) (SizeUnit, SizeUnit) {
|
||||||
view = getSubview(view, subviewID)
|
view = getSubview(view, subviewID)
|
||||||
if view == nil {
|
if view == nil {
|
||||||
|
|
@ -675,14 +667,18 @@ func GetPerspectiveOrigin(view View, subviewID ...string) (SizeUnit, SizeUnit) {
|
||||||
// visible when turned towards the user. Values:
|
// visible when turned towards the user. Values:
|
||||||
// true - the back face is visible when turned towards the user (default value).
|
// 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.
|
// 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 {
|
func GetBackfaceVisible(view View, subviewID ...string) bool {
|
||||||
return boolStyledProperty(view, subviewID, BackfaceVisible, false)
|
return boolStyledProperty(view, subviewID, BackfaceVisible, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTransformOrigin returns a x-, y-, and z-coordinate of the point around which a view transformation is applied.
|
// 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%).
|
// 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) {
|
func GetTransformOrigin(view View, subviewID ...string) (SizeUnit, SizeUnit, SizeUnit) {
|
||||||
view = getSubview(view, subviewID)
|
view = getSubview(view, subviewID)
|
||||||
if view == nil {
|
if view == nil {
|
||||||
|
|
@ -692,7 +688,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
|
// 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) {
|
func GetTranslate(view View, subviewID ...string) (SizeUnit, SizeUnit, SizeUnit) {
|
||||||
if transform := GetTransform(view, subviewID...); transform != nil {
|
if transform := GetTransform(view, subviewID...); transform != nil {
|
||||||
return transform.getTranslate(view.Session())
|
return transform.getTranslate(view.Session())
|
||||||
|
|
@ -702,7 +700,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)
|
// 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.
|
// 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) {
|
func GetSkew(view View, subviewID ...string) (AngleUnit, AngleUnit) {
|
||||||
if transform := GetTransform(view, subviewID...); transform != nil {
|
if transform := GetTransform(view, subviewID...); transform != nil {
|
||||||
x, y, _ := transform.getSkew(view.Session())
|
x, y, _ := transform.getSkew(view.Session())
|
||||||
|
|
@ -712,7 +712,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.
|
// 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) {
|
func GetScale(view View, subviewID ...string) (float64, float64, float64) {
|
||||||
if transform := GetTransform(view, subviewID...); transform != nil {
|
if transform := GetTransform(view, subviewID...); transform != nil {
|
||||||
session := view.Session()
|
session := view.Session()
|
||||||
|
|
@ -725,7 +727,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
|
// 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) {
|
func GetRotate(view View, subviewID ...string) (float64, float64, float64, AngleUnit) {
|
||||||
if transform := GetTransform(view, subviewID...); transform != nil {
|
if transform := GetTransform(view, subviewID...); transform != nil {
|
||||||
session := view.Session()
|
session := view.Session()
|
||||||
|
|
|
||||||
9
utils.go
9
utils.go
|
|
@ -105,3 +105,12 @@ func InlineImageFromResource(filename string) (string, bool) {
|
||||||
|
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InlineFileFromData creates inline image from the image data and mimetype.
|
||||||
|
func InlineFileFromData(data []byte, mimeType string) string {
|
||||||
|
if data == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return "data:" + mimeType + ";base64," + base64.StdEncoding.EncodeToString(data)
|
||||||
|
}
|
||||||
|
|
|
||||||
479
view.go
479
view.go
|
|
@ -1,7 +1,9 @@
|
||||||
package rui
|
package rui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"maps"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
@ -28,6 +30,8 @@ func (frame Frame) Bottom() float64 {
|
||||||
return frame.Top + frame.Height
|
return frame.Top + frame.Height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const changeListeners PropertyName = "change-listeners"
|
||||||
|
|
||||||
// View represents a base view interface
|
// View represents a base view interface
|
||||||
type View interface {
|
type View interface {
|
||||||
ViewStyle
|
ViewStyle
|
||||||
|
|
@ -64,12 +68,28 @@ type View interface {
|
||||||
// a description of the error is written to the log
|
// a description of the error is written to the log
|
||||||
SetAnimated(tag PropertyName, value any, animation AnimationProperty) bool
|
SetAnimated(tag PropertyName, value any, animation AnimationProperty) bool
|
||||||
|
|
||||||
// SetChangeListener set the function to track the change of the View property
|
// SetChangeListener set the function (the second argument) to track the change of the View property (the first argument).
|
||||||
SetChangeListener(tag PropertyName, listener func(View, PropertyName))
|
//
|
||||||
|
// 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 returns 'true' if the view has focus
|
||||||
HasFocus() bool
|
HasFocus() bool
|
||||||
|
|
||||||
|
// LoadFile loads the content of the dropped file by drag-and-drop mechanism for all views except FilePicker.
|
||||||
|
// The selected file is loaded for FilePicker view.
|
||||||
|
//
|
||||||
|
// This function is asynchronous. The "result" function will be called after loading the data.
|
||||||
|
LoadFile(file FileInfo, result func(FileInfo, []byte))
|
||||||
|
|
||||||
init(session Session)
|
init(session Session)
|
||||||
handleCommand(self View, command PropertyName, data DataObject) bool
|
handleCommand(self View, command PropertyName, data DataObject) bool
|
||||||
htmlClass(disabled bool) string
|
htmlClass(disabled bool) string
|
||||||
|
|
@ -82,8 +102,9 @@ type View interface {
|
||||||
htmlProperties(self View, buffer *strings.Builder)
|
htmlProperties(self View, buffer *strings.Builder)
|
||||||
cssStyle(self View, builder cssBuilder)
|
cssStyle(self View, builder cssBuilder)
|
||||||
addToCSSStyle(addCSS map[string]string)
|
addToCSSStyle(addCSS map[string]string)
|
||||||
exscludeTags() []PropertyName
|
excludeTags() []PropertyName
|
||||||
htmlDisabledProperty() bool
|
htmlDisabledProperty() bool
|
||||||
|
binding() any
|
||||||
|
|
||||||
onResize(self View, x, y, width, height float64)
|
onResize(self View, x, y, width, height float64)
|
||||||
onItemResize(self View, index string, x, y, width, height float64)
|
onItemResize(self View, index string, x, y, width, height float64)
|
||||||
|
|
@ -101,7 +122,7 @@ type viewData struct {
|
||||||
_htmlID string
|
_htmlID string
|
||||||
parentID string
|
parentID string
|
||||||
systemClass string
|
systemClass string
|
||||||
changeListener map[PropertyName]func(View, PropertyName)
|
changeListener map[PropertyName]oneArgListener[View, PropertyName]
|
||||||
singleTransition map[PropertyName]AnimationProperty
|
singleTransition map[PropertyName]AnimationProperty
|
||||||
addCSS map[string]string
|
addCSS map[string]string
|
||||||
frame Frame
|
frame Frame
|
||||||
|
|
@ -114,6 +135,7 @@ type viewData struct {
|
||||||
set func(tag PropertyName, value any) []PropertyName
|
set func(tag PropertyName, value any) []PropertyName
|
||||||
remove func(tag PropertyName) []PropertyName
|
remove func(tag PropertyName) []PropertyName
|
||||||
changed func(tag PropertyName)
|
changed func(tag PropertyName)
|
||||||
|
fileLoader map[string]func(FileInfo, []byte)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newView(session Session) View {
|
func newView(session Session) View {
|
||||||
|
|
@ -135,13 +157,11 @@ func setInitParams(view View, params Params) {
|
||||||
session.setIgnoreViewUpdates(true)
|
session.setIgnoreViewUpdates(true)
|
||||||
defer session.setIgnoreViewUpdates(false)
|
defer session.setIgnoreViewUpdates(false)
|
||||||
}
|
}
|
||||||
for _, tag := range params.AllTags() {
|
for tag, value := range params.All() {
|
||||||
if value, ok := params[tag]; ok {
|
|
||||||
view.Set(tag, value)
|
view.Set(tag, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func (view *viewData) init(session Session) {
|
func (view *viewData) init(session Session) {
|
||||||
view.viewStyle.init()
|
view.viewStyle.init()
|
||||||
|
|
@ -152,13 +172,14 @@ func (view *viewData) init(session Session) {
|
||||||
view.changed = view.propertyChanged
|
view.changed = view.propertyChanged
|
||||||
view.tag = "View"
|
view.tag = "View"
|
||||||
view.session = session
|
view.session = session
|
||||||
view.changeListener = map[PropertyName]func(View, PropertyName){}
|
view.changeListener = map[PropertyName]oneArgListener[View, PropertyName]{}
|
||||||
view.addCSS = map[string]string{}
|
view.addCSS = map[string]string{}
|
||||||
//view.animation = map[string]AnimationEndListener{}
|
//view.animation = map[string]AnimationEndListener{}
|
||||||
view.singleTransition = map[PropertyName]AnimationProperty{}
|
view.singleTransition = map[PropertyName]AnimationProperty{}
|
||||||
view.noResizeEvent = false
|
view.noResizeEvent = false
|
||||||
view.created = false
|
view.created = false
|
||||||
view.hasHtmlDisabled = false
|
view.hasHtmlDisabled = false
|
||||||
|
view.fileLoader = map[string]func(FileInfo, []byte){}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (view *viewData) Session() Session {
|
func (view *viewData) Session() Session {
|
||||||
|
|
@ -185,6 +206,18 @@ func (view *viewData) ID() string {
|
||||||
return view.viewID
|
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 {
|
func (view *viewData) ViewByID(id string) View {
|
||||||
if id == view.ID() {
|
if id == view.ID() {
|
||||||
if v := view.session.viewByHTMLID(view.htmlID()); v != nil {
|
if v := view.session.viewByHTMLID(view.htmlID()); v != nil {
|
||||||
|
|
@ -213,18 +246,19 @@ func (view *viewData) Focusable() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (view *viewData) runChangeListener(tag PropertyName) {
|
||||||
|
if listener, ok := view.changeListener[tag]; ok {
|
||||||
|
listener.Run(view, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (view *viewData) Remove(tag PropertyName) {
|
func (view *viewData) Remove(tag PropertyName) {
|
||||||
changedTags := view.removeFunc(view.normalize(tag))
|
changedTags := view.removeFunc(view.normalize(tag))
|
||||||
|
|
||||||
if view.created && len(changedTags) > 0 {
|
if view.created && len(changedTags) > 0 {
|
||||||
for _, tag := range changedTags {
|
for _, tag := range changedTags {
|
||||||
view.changed(tag)
|
view.changed(tag)
|
||||||
}
|
view.runChangeListener(tag)
|
||||||
|
|
||||||
for _, tag := range changedTags {
|
|
||||||
if listener, ok := view.changeListener[tag]; ok {
|
|
||||||
listener(view, tag)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -250,12 +284,7 @@ func (view *viewData) Set(tag PropertyName, value any) bool {
|
||||||
if view.created && len(changedTags) > 0 {
|
if view.created && len(changedTags) > 0 {
|
||||||
for _, tag := range changedTags {
|
for _, tag := range changedTags {
|
||||||
view.changed(tag)
|
view.changed(tag)
|
||||||
}
|
view.runChangeListener(tag)
|
||||||
|
|
||||||
for _, tag := range changedTags {
|
|
||||||
if listener, ok := view.changeListener[tag]; ok {
|
|
||||||
listener(view, tag)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -272,16 +301,75 @@ func normalizeViewTag(tag PropertyName) PropertyName {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (view *viewData) getFunc(tag PropertyName) any {
|
func (view *viewData) getFunc(tag PropertyName) any {
|
||||||
if tag == ID {
|
switch tag {
|
||||||
|
case ID:
|
||||||
if id := view.ID(); id != "" {
|
if id := view.ID(); id != "" {
|
||||||
return id
|
return id
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case FocusEvent, LostFocusEvent:
|
||||||
|
if listeners := getNoArgEventRawListeners[View](view, nil, tag); len(listeners) > 0 {
|
||||||
|
return listeners
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case KeyDownEvent, KeyUpEvent:
|
||||||
|
if listeners := getOneArgEventRawListeners[View, KeyEvent](view, nil, tag); len(listeners) > 0 {
|
||||||
|
return listeners
|
||||||
|
}
|
||||||
|
|
||||||
|
case ClickEvent, DoubleClickEvent, MouseDown, MouseUp, MouseMove, MouseOut, MouseOver, ContextMenuEvent:
|
||||||
|
if listeners := getOneArgEventRawListeners[View, MouseEvent](view, nil, tag); len(listeners) > 0 {
|
||||||
|
return listeners
|
||||||
|
}
|
||||||
|
|
||||||
|
case PointerDown, PointerUp, PointerMove, PointerOut, PointerOver, PointerCancel:
|
||||||
|
if listeners := getOneArgEventRawListeners[View, PointerEvent](view, nil, tag); len(listeners) > 0 {
|
||||||
|
return listeners
|
||||||
|
}
|
||||||
|
|
||||||
|
case TouchStart, TouchEnd, TouchMove, TouchCancel:
|
||||||
|
if listeners := getOneArgEventRawListeners[View, TouchEvent](view, nil, tag); len(listeners) > 0 {
|
||||||
|
return listeners
|
||||||
|
}
|
||||||
|
|
||||||
|
case TransitionRunEvent, TransitionStartEvent, TransitionEndEvent, TransitionCancelEvent:
|
||||||
|
if listeners := getOneArgEventRawListeners[View, PropertyName](view, nil, tag); len(listeners) > 0 {
|
||||||
|
return listeners
|
||||||
|
}
|
||||||
|
|
||||||
|
case AnimationStartEvent, AnimationEndEvent, AnimationIterationEvent, AnimationCancelEvent:
|
||||||
|
if listeners := getOneArgEventRawListeners[View, string](view, nil, tag); len(listeners) > 0 {
|
||||||
|
return listeners
|
||||||
|
}
|
||||||
|
|
||||||
|
case ResizeEvent, ScrollEvent:
|
||||||
|
if listeners := getOneArgEventRawListeners[View, Frame](view, nil, tag); len(listeners) > 0 {
|
||||||
|
return listeners
|
||||||
|
}
|
||||||
|
|
||||||
|
case DragStartEvent, DragEndEvent, DragEnterEvent, DragLeaveEvent, DragOverEvent, DropEvent:
|
||||||
|
if listeners := getOneArgEventRawListeners[View, DragAndDropEvent](view, nil, tag); len(listeners) > 0 {
|
||||||
|
return listeners
|
||||||
|
}
|
||||||
|
|
||||||
|
case changeListeners:
|
||||||
|
if len(view.changeListener) > 0 {
|
||||||
|
result := map[PropertyName]any{}
|
||||||
|
for tag, listener := range view.changeListener {
|
||||||
|
result[tag] = listener.rawListener()
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
return viewStyleGet(view, tag)
|
return viewStyleGet(view, tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (view *viewData) removeFunc(tag PropertyName) []PropertyName {
|
func (view *viewData) removeFunc(tag PropertyName) []PropertyName {
|
||||||
var changedTags []PropertyName = nil
|
var changedTags []PropertyName = nil
|
||||||
|
|
||||||
|
|
@ -294,6 +382,14 @@ func (view *viewData) removeFunc(tag PropertyName) []PropertyName {
|
||||||
changedTags = []PropertyName{}
|
changedTags = []PropertyName{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Binding:
|
||||||
|
if view.getRaw(Binding) != nil {
|
||||||
|
view.setRaw(Binding, nil)
|
||||||
|
changedTags = []PropertyName{Binding}
|
||||||
|
} else {
|
||||||
|
changedTags = []PropertyName{}
|
||||||
|
}
|
||||||
|
|
||||||
case Animation:
|
case Animation:
|
||||||
if val := view.getRaw(Animation); val != nil {
|
if val := view.getRaw(Animation); val != nil {
|
||||||
if animations, ok := val.([]AnimationProperty); ok {
|
if animations, ok := val.([]AnimationProperty); ok {
|
||||||
|
|
@ -326,6 +422,32 @@ func (view *viewData) setFunc(tag PropertyName, value any) []PropertyName {
|
||||||
notCompatibleType(ID, value)
|
notCompatibleType(ID, value)
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
case Binding:
|
||||||
|
view.setRaw(Binding, value)
|
||||||
|
return []PropertyName{Binding}
|
||||||
|
|
||||||
|
case changeListeners:
|
||||||
|
switch value := value.(type) {
|
||||||
|
case DataObject:
|
||||||
|
for node := range value.Properties() {
|
||||||
|
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:
|
case Animation:
|
||||||
oldAnimations := []AnimationProperty{}
|
oldAnimations := []AnimationProperty{}
|
||||||
if val := view.getRaw(Animation); val != nil {
|
if val := view.getRaw(Animation); val != nil {
|
||||||
|
|
@ -376,20 +498,15 @@ func (view *viewData) setFunc(tag PropertyName, value any) []PropertyName {
|
||||||
case TransitionRunEvent, TransitionStartEvent, TransitionEndEvent, TransitionCancelEvent:
|
case TransitionRunEvent, TransitionStartEvent, TransitionEndEvent, TransitionCancelEvent:
|
||||||
result := setOneArgEventListener[View, PropertyName](view, tag, value)
|
result := setOneArgEventListener[View, PropertyName](view, tag, value)
|
||||||
if result == nil {
|
if result == nil {
|
||||||
result = setOneArgEventListener[View, string](view, tag, value)
|
if listeners, ok := valueToOneArgEventListeners[View, string](view); ok && len(listeners) > 0 {
|
||||||
if result != nil {
|
newListeners := make([]oneArgListener[View, PropertyName], len(listeners))
|
||||||
if listeners, ok := view.getRaw(tag).([]func(View, string)); ok {
|
|
||||||
newListeners := make([]func(View, PropertyName), len(listeners))
|
|
||||||
for i, listener := range listeners {
|
for i, listener := range listeners {
|
||||||
newListeners[i] = func(view View, name PropertyName) {
|
newListeners[i] = newOneArgListenerVE(func(view View, name PropertyName) {
|
||||||
listener(view, string(name))
|
listener.Run(view, string(name))
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
view.setRaw(tag, newListeners)
|
view.setRaw(tag, newListeners)
|
||||||
return result
|
result = []PropertyName{tag}
|
||||||
}
|
|
||||||
view.setRaw(tag, nil)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
|
@ -399,6 +516,88 @@ func (view *viewData) setFunc(tag PropertyName, value any) []PropertyName {
|
||||||
|
|
||||||
case ResizeEvent, ScrollEvent:
|
case ResizeEvent, ScrollEvent:
|
||||||
return setOneArgEventListener[View, Frame](view, tag, value)
|
return setOneArgEventListener[View, Frame](view, tag, value)
|
||||||
|
|
||||||
|
case DragData:
|
||||||
|
switch value := value.(type) {
|
||||||
|
case map[string]string:
|
||||||
|
if len(value) == 0 {
|
||||||
|
view.setRaw(DragData, nil)
|
||||||
|
} else {
|
||||||
|
view.setRaw(DragData, maps.Clone(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
case string:
|
||||||
|
if value == "" {
|
||||||
|
view.setRaw(DragData, nil)
|
||||||
|
} else {
|
||||||
|
data := map[string]string{}
|
||||||
|
for _, line := range strings.Split(value, ";") {
|
||||||
|
index := strings.IndexRune(line, ':')
|
||||||
|
if index < 0 {
|
||||||
|
invalidPropertyValue(DragData, value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
mime := line[:index]
|
||||||
|
val := line[index+1:]
|
||||||
|
if len(mime) > 0 || len(val) > 0 {
|
||||||
|
data[mime] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(data) == 0 {
|
||||||
|
view.setRaw(DragData, nil)
|
||||||
|
} else {
|
||||||
|
view.setRaw(DragData, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case DataObject:
|
||||||
|
data := map[string]string{}
|
||||||
|
for node := range value.Properties() {
|
||||||
|
if node.Type() == TextNode {
|
||||||
|
data[node.Tag()] = node.Text()
|
||||||
|
} else {
|
||||||
|
invalidPropertyValue(DragData, value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(data) == 0 {
|
||||||
|
view.setRaw(DragData, nil)
|
||||||
|
} else {
|
||||||
|
view.setRaw(DragData, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
case DataNode:
|
||||||
|
switch value.Type() {
|
||||||
|
case TextNode:
|
||||||
|
return view.setFunc(DragData, value.Text())
|
||||||
|
|
||||||
|
case ObjectNode:
|
||||||
|
return view.setFunc(DragData, value.Object())
|
||||||
|
}
|
||||||
|
invalidPropertyValue(DragData, value)
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case DataValue:
|
||||||
|
if value.IsObject() {
|
||||||
|
return view.setFunc(DragData, value.Object())
|
||||||
|
}
|
||||||
|
return view.setFunc(DragData, value.Value())
|
||||||
|
|
||||||
|
default:
|
||||||
|
notCompatibleType(DragData, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return []PropertyName{DragData}
|
||||||
|
|
||||||
|
case DragStartEvent, DragEndEvent, DragEnterEvent, DragLeaveEvent, DragOverEvent, DropEvent:
|
||||||
|
return setOneArgEventListener[View, DragAndDropEvent](view, tag, value)
|
||||||
|
|
||||||
|
case DropEffect:
|
||||||
|
return view.setDropEffect(value)
|
||||||
|
|
||||||
|
case DropEffectAllowed:
|
||||||
|
return view.setDropEffectAllowed(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
return viewStyleSet(view, tag, value)
|
return viewStyleSet(view, tag, value)
|
||||||
|
|
@ -413,11 +612,9 @@ func (view *viewData) SetParams(params Params) bool {
|
||||||
session := view.Session()
|
session := view.Session()
|
||||||
session.startUpdateScript(view.htmlID())
|
session.startUpdateScript(view.htmlID())
|
||||||
result := true
|
result := true
|
||||||
for _, tag := range params.AllTags() {
|
for tag, value := range params.All() {
|
||||||
if value, ok := params[tag]; ok {
|
|
||||||
result = view.Set(tag, value) && result
|
result = view.Set(tag, value) && result
|
||||||
}
|
}
|
||||||
}
|
|
||||||
session.finishUpdateScript(view.htmlID())
|
session.finishUpdateScript(view.htmlID())
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
@ -429,16 +626,16 @@ func (view *viewData) propertyChanged(tag PropertyName) {
|
||||||
|
|
||||||
switch tag {
|
switch tag {
|
||||||
case TabIndex:
|
case TabIndex:
|
||||||
if value, ok := intProperty(view, TabIndex, view.Session(), 0); ok {
|
if value, ok := intProperty(view, TabIndex, session, 0); ok {
|
||||||
session.updateProperty(view.htmlID(), "tabindex", strconv.Itoa(value))
|
session.updateProperty(htmlID, "tabindex", strconv.Itoa(value))
|
||||||
} else if view.Focusable() {
|
} else if view.Focusable() {
|
||||||
session.updateProperty(view.htmlID(), "tabindex", "0")
|
session.updateProperty(htmlID, "tabindex", "0")
|
||||||
} else {
|
} else {
|
||||||
session.updateProperty(view.htmlID(), "tabindex", "-1")
|
session.updateProperty(htmlID, "tabindex", "-1")
|
||||||
}
|
}
|
||||||
|
|
||||||
case Style, StyleDisabled:
|
case Style, StyleDisabled:
|
||||||
session.updateProperty(view.htmlID(), "class", view.htmlClass(IsDisabled(view)))
|
session.updateProperty(htmlID, "class", view.htmlClass(IsDisabled(view)))
|
||||||
|
|
||||||
case Disabled:
|
case Disabled:
|
||||||
tabIndex := GetTabIndex(view, htmlID)
|
tabIndex := GetTabIndex(view, htmlID)
|
||||||
|
|
@ -484,7 +681,7 @@ func (view *viewData) propertyChanged(tag PropertyName) {
|
||||||
session.callFunc("hideTooltip")
|
session.callFunc("hideTooltip")
|
||||||
|
|
||||||
default:
|
default:
|
||||||
session.updateCSSProperty(htmlID, string(Visibility), "visible")
|
session.updateCSSProperty(htmlID, string(Visibility), "")
|
||||||
session.updateCSSProperty(htmlID, "display", "")
|
session.updateCSSProperty(htmlID, "display", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -695,7 +892,7 @@ func (view *viewData) propertyChanged(tag PropertyName) {
|
||||||
|
|
||||||
case PerspectiveOriginX, PerspectiveOriginY:
|
case PerspectiveOriginX, PerspectiveOriginY:
|
||||||
x, y := GetPerspectiveOrigin(view)
|
x, y := GetPerspectiveOrigin(view)
|
||||||
session.updateCSSProperty(htmlID, "perspective-origin", transformOriginCSS(x, y, AutoSize(), view.Session()))
|
session.updateCSSProperty(htmlID, "perspective-origin", transformOriginCSS(x, y, AutoSize(), session))
|
||||||
|
|
||||||
case BackfaceVisible:
|
case BackfaceVisible:
|
||||||
if GetBackfaceVisible(view) {
|
if GetBackfaceVisible(view) {
|
||||||
|
|
@ -706,7 +903,7 @@ func (view *viewData) propertyChanged(tag PropertyName) {
|
||||||
|
|
||||||
case TransformOriginX, TransformOriginY, TransformOriginZ:
|
case TransformOriginX, TransformOriginY, TransformOriginZ:
|
||||||
x, y, z := getTransformOrigin(view, session)
|
x, y, z := getTransformOrigin(view, session)
|
||||||
session.updateCSSProperty(htmlID, "transform-origin", transformOriginCSS(x, y, z, view.Session()))
|
session.updateCSSProperty(htmlID, "transform-origin", transformOriginCSS(x, y, z, session))
|
||||||
|
|
||||||
case Transform:
|
case Transform:
|
||||||
css := ""
|
css := ""
|
||||||
|
|
@ -724,12 +921,105 @@ func (view *viewData) propertyChanged(tag PropertyName) {
|
||||||
PointerDown, PointerUp, PointerMove, PointerOut, PointerOver, PointerCancel,
|
PointerDown, PointerUp, PointerMove, PointerOut, PointerOver, PointerCancel,
|
||||||
TouchStart, TouchEnd, TouchMove, TouchCancel,
|
TouchStart, TouchEnd, TouchMove, TouchCancel,
|
||||||
TransitionRunEvent, TransitionStartEvent, TransitionEndEvent, TransitionCancelEvent,
|
TransitionRunEvent, TransitionStartEvent, TransitionEndEvent, TransitionCancelEvent,
|
||||||
AnimationStartEvent, AnimationEndEvent, AnimationIterationEvent, AnimationCancelEvent:
|
AnimationStartEvent, AnimationEndEvent, AnimationIterationEvent, AnimationCancelEvent,
|
||||||
|
DragEndEvent, DragEnterEvent, DragLeaveEvent:
|
||||||
|
|
||||||
updateEventListenerHtml(view, tag)
|
updateEventListenerHtml(view, tag)
|
||||||
|
|
||||||
|
case DragStartEvent:
|
||||||
|
if view.getRaw(DragStartEvent) != nil || view.getRaw(DragData) != nil {
|
||||||
|
session.updateProperty(htmlID, "ondragstart", "dragStartEvent(this, event)")
|
||||||
|
} else {
|
||||||
|
session.removeProperty(htmlID, "ondragstart")
|
||||||
|
}
|
||||||
|
|
||||||
|
case DropEvent:
|
||||||
|
if view.getRaw(DropEvent) != nil {
|
||||||
|
session.updateProperty(htmlID, "ondrop", "dropEvent(this, event)")
|
||||||
|
session.updateProperty(htmlID, "ondragover", "dragOverEvent(this, event)")
|
||||||
|
if view.getRaw(DragOverEvent) != nil {
|
||||||
|
session.updateProperty(htmlID, "data-drag-over", "1")
|
||||||
|
} else {
|
||||||
|
session.removeProperty(htmlID, "data-drag-over")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
session.removeProperty(htmlID, "ondrop")
|
||||||
|
session.removeProperty(htmlID, "ondragover")
|
||||||
|
}
|
||||||
|
|
||||||
|
case DragOverEvent:
|
||||||
|
if view.getRaw(DragOverEvent) != nil {
|
||||||
|
session.updateProperty(htmlID, "data-drag-over", "1")
|
||||||
|
} else {
|
||||||
|
session.removeProperty(htmlID, "data-drag-over")
|
||||||
|
}
|
||||||
|
|
||||||
|
case DragData:
|
||||||
|
if data := base64DragData(view); data != "" {
|
||||||
|
session.updateProperty(htmlID, "draggable", "true")
|
||||||
|
session.updateProperty(htmlID, "data-drag", data)
|
||||||
|
session.updateProperty(htmlID, "ondragstart", "dragStartEvent(this, event)")
|
||||||
|
} else {
|
||||||
|
session.removeProperty(htmlID, "draggable")
|
||||||
|
session.removeProperty(htmlID, "data-drag")
|
||||||
|
if view.getRaw(DragStartEvent) == nil {
|
||||||
|
session.removeProperty(htmlID, "ondragstart")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case DragImage:
|
||||||
|
if img, ok := stringProperty(view, DragImage, session); ok && img != "" {
|
||||||
|
img = strings.Trim(img, " \t")
|
||||||
|
if ok, constName := isConstantName(img); ok {
|
||||||
|
img, ok = session.ImageConstant(constName)
|
||||||
|
if !ok {
|
||||||
|
session.removeProperty(htmlID, "data-drag-image")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
session.updateProperty(htmlID, "data-drag-image", img)
|
||||||
|
} else {
|
||||||
|
session.removeProperty(htmlID, "data-drag-image")
|
||||||
|
}
|
||||||
|
|
||||||
|
case DragImageXOffset:
|
||||||
|
if f := GetDragImageXOffset(view); f != 0 {
|
||||||
|
session.updateProperty(htmlID, "data-drag-image-x", f)
|
||||||
|
} else {
|
||||||
|
session.removeProperty(htmlID, "data-drag-image-x")
|
||||||
|
}
|
||||||
|
|
||||||
|
case DragImageYOffset:
|
||||||
|
if f := GetDragImageXOffset(view); f != 0 {
|
||||||
|
session.updateProperty(htmlID, "data-drag-image-y", f)
|
||||||
|
} else {
|
||||||
|
session.removeProperty(htmlID, "data-drag-image-y")
|
||||||
|
}
|
||||||
|
|
||||||
|
case DropEffect:
|
||||||
|
effect := GetDropEffect(view)
|
||||||
|
switch effect {
|
||||||
|
case DropEffectCopy:
|
||||||
|
session.updateProperty(htmlID, "data-drop-effect", "copy")
|
||||||
|
case DropEffectMove:
|
||||||
|
session.updateProperty(htmlID, "data-drop-effect", "move")
|
||||||
|
case DropEffectLink:
|
||||||
|
session.updateProperty(htmlID, "data-drop-effect", "link")
|
||||||
|
default:
|
||||||
|
session.removeProperty(htmlID, "data-drop-effect")
|
||||||
|
}
|
||||||
|
|
||||||
|
case DropEffectAllowed:
|
||||||
|
effect := GetDropEffectAllowed(view)
|
||||||
|
if effect >= DropEffectCopy && effect >= DropEffectAll {
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
case DataList:
|
case DataList:
|
||||||
updateInnerHTML(view.htmlID(), view.Session())
|
updateInnerHTML(htmlID, session)
|
||||||
|
|
||||||
case Opacity:
|
case Opacity:
|
||||||
if f, ok := floatTextProperty(view, Opacity, session, 0); ok {
|
if f, ok := floatTextProperty(view, Opacity, session, 0); ok {
|
||||||
|
|
@ -739,7 +1029,7 @@ func (view *viewData) propertyChanged(tag PropertyName) {
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if cssTag, ok := sizeProperties[tag]; ok {
|
if cssTag, ok := sizeProperties[tag]; ok && cssTag != "" {
|
||||||
if size, ok := sizeProperty(view, tag, session); ok {
|
if size, ok := sizeProperty(view, tag, session); ok {
|
||||||
session.updateCSSProperty(htmlID, cssTag, size.cssString("", session))
|
session.updateCSSProperty(htmlID, cssTag, size.cssString("", session))
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -804,7 +1094,7 @@ func (view *viewData) addToCSSStyle(addCSS map[string]string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (view *viewData) cssStyle(self View, builder cssBuilder) {
|
func (view *viewData) cssStyle(self View, builder cssBuilder) {
|
||||||
view.viewStyle.cssViewStyle(builder, view.session)
|
writeViewStyleCSS(view, builder, view.session, view.tag == "ListView")
|
||||||
if view.addCSS != nil {
|
if view.addCSS != nil {
|
||||||
for tag, value := range view.addCSS {
|
for tag, value := range view.addCSS {
|
||||||
builder.add(tag, value)
|
builder.add(tag, value)
|
||||||
|
|
@ -829,8 +1119,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 {
|
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"`,
|
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))
|
view.frame.Left, view.frame.Top, view.frame.Width, view.frame.Height)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -891,18 +1181,12 @@ func viewHTML(view View, buffer *strings.Builder, htmlTag string) {
|
||||||
keyEventsHtml(view, buffer)
|
keyEventsHtml(view, buffer)
|
||||||
|
|
||||||
viewEventsHtml[MouseEvent](view, []PropertyName{ClickEvent, DoubleClickEvent, MouseDown, MouseUp, MouseMove, MouseOut, MouseOver, ContextMenuEvent}, buffer)
|
viewEventsHtml[MouseEvent](view, []PropertyName{ClickEvent, DoubleClickEvent, MouseDown, MouseUp, MouseMove, MouseOut, MouseOver, ContextMenuEvent}, buffer)
|
||||||
//mouseEventsHtml(view, buffer, hasTooltip)
|
|
||||||
|
|
||||||
viewEventsHtml[PointerEvent](view, []PropertyName{PointerDown, PointerUp, PointerMove, PointerOut, PointerOver, PointerCancel}, buffer)
|
viewEventsHtml[PointerEvent](view, []PropertyName{PointerDown, PointerUp, PointerMove, PointerOut, PointerOver, PointerCancel}, buffer)
|
||||||
//pointerEventsHtml(view, buffer)
|
|
||||||
|
|
||||||
viewEventsHtml[TouchEvent](view, []PropertyName{TouchStart, TouchEnd, TouchMove, TouchCancel}, buffer)
|
viewEventsHtml[TouchEvent](view, []PropertyName{TouchStart, TouchEnd, TouchMove, TouchCancel}, buffer)
|
||||||
//touchEventsHtml(view, buffer)
|
|
||||||
|
|
||||||
viewEventsHtml[string](view, []PropertyName{TransitionRunEvent, TransitionStartEvent, TransitionEndEvent, TransitionCancelEvent,
|
viewEventsHtml[string](view, []PropertyName{TransitionRunEvent, TransitionStartEvent, TransitionEndEvent, TransitionCancelEvent,
|
||||||
AnimationStartEvent, AnimationEndEvent, AnimationIterationEvent, AnimationCancelEvent}, buffer)
|
AnimationStartEvent, AnimationEndEvent, AnimationIterationEvent, AnimationCancelEvent}, buffer)
|
||||||
//transitionEventsHtml(view, buffer)
|
|
||||||
//animationEventsHtml(view, buffer)
|
dragAndDropHtml(view, buffer)
|
||||||
|
|
||||||
buffer.WriteRune('>')
|
buffer.WriteRune('>')
|
||||||
view.htmlSubviews(view, buffer)
|
view.htmlSubviews(view, buffer)
|
||||||
|
|
@ -952,16 +1236,22 @@ func (view *viewData) handleCommand(self View, command PropertyName, data DataOb
|
||||||
case TouchStart, TouchEnd, TouchMove, TouchCancel:
|
case TouchStart, TouchEnd, TouchMove, TouchCancel:
|
||||||
handleTouchEvents(self, command, data)
|
handleTouchEvents(self, command, data)
|
||||||
|
|
||||||
|
case DragStartEvent:
|
||||||
|
handleDragAndDropEvents(self, command, data)
|
||||||
|
|
||||||
|
case DragEndEvent, DragEnterEvent, DragLeaveEvent, DragOverEvent, DropEvent:
|
||||||
|
handleDragAndDropEvents(self, command, data)
|
||||||
|
|
||||||
case FocusEvent:
|
case FocusEvent:
|
||||||
view.hasFocus = true
|
view.hasFocus = true
|
||||||
for _, listener := range getNoArgEventListeners[View](view, nil, command) {
|
for _, listener := range getNoArgEventListeners[View](view, nil, command) {
|
||||||
listener(self)
|
listener.Run(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
case LostFocusEvent:
|
case LostFocusEvent:
|
||||||
view.hasFocus = false
|
view.hasFocus = false
|
||||||
for _, listener := range getNoArgEventListeners[View](view, nil, command) {
|
for _, listener := range getNoArgEventListeners[View](view, nil, command) {
|
||||||
listener(self)
|
listener.Run(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
case TransitionRunEvent, TransitionStartEvent, TransitionEndEvent, TransitionCancelEvent:
|
case TransitionRunEvent, TransitionStartEvent, TransitionEndEvent, TransitionCancelEvent:
|
||||||
|
|
@ -1001,6 +1291,42 @@ func (view *viewData) handleCommand(self View, command PropertyName, data DataOb
|
||||||
self.onResize(self, floatProperty("x"), floatProperty("y"), floatProperty("width"), floatProperty("height"))
|
self.onResize(self, floatProperty("x"), floatProperty("y"), floatProperty("width"), floatProperty("height"))
|
||||||
return true
|
return true
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
case "fileLoaded":
|
||||||
|
file := dataToFileInfo(data)
|
||||||
|
key := file.key()
|
||||||
|
|
||||||
|
if listener := view.fileLoader[key]; listener != nil {
|
||||||
|
delete(view.fileLoader, key)
|
||||||
|
|
||||||
|
if base64Data, ok := data.PropertyValue("data"); ok {
|
||||||
|
if index := strings.LastIndex(base64Data, ","); index >= 0 {
|
||||||
|
base64Data = base64Data[index+1:]
|
||||||
|
}
|
||||||
|
decode, err := base64.StdEncoding.DecodeString(base64Data)
|
||||||
|
if err == nil {
|
||||||
|
listener(file, decode)
|
||||||
|
} else {
|
||||||
|
ErrorLog(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
|
||||||
|
case "fileLoadingError":
|
||||||
|
file := dataToFileInfo(data)
|
||||||
|
key := file.key()
|
||||||
|
|
||||||
|
if error, ok := data.PropertyValue("error"); ok {
|
||||||
|
ErrorLogF(`Load "%s" file error: %s`, file.Name, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
if listener := view.fileLoader[key]; listener != nil {
|
||||||
|
delete(view.fileLoader, key)
|
||||||
|
listener(file, nil)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
@ -1008,12 +1334,41 @@ 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 {
|
if listener == nil {
|
||||||
delete(view.changeListener, tag)
|
delete(view.changeListener, tag)
|
||||||
|
if len(view.changeListener) > 0 {
|
||||||
|
view.setRaw(changeListeners, view.changeListener)
|
||||||
} else {
|
} else {
|
||||||
view.changeListener[tag] = listener
|
view.setRaw(changeListeners, nil)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
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:
|
||||||
|
if listener == "" {
|
||||||
|
return view.SetChangeListener(tag, nil)
|
||||||
|
}
|
||||||
|
view.changeListener[tag] = newOneArgListenerBinding[View, PropertyName](listener)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
view.setRaw(changeListeners, view.changeListener)
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (view *viewData) HasFocus() bool {
|
func (view *viewData) HasFocus() bool {
|
||||||
|
|
@ -1027,6 +1382,6 @@ func (view *viewData) String() string {
|
||||||
return buffer.String()
|
return buffer.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (view *viewData) exscludeTags() []PropertyName {
|
func (view *viewData) excludeTags() []PropertyName {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
150
viewFactory.go
150
viewFactory.go
|
|
@ -1,12 +1,13 @@
|
||||||
package rui
|
package rui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"embed"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var viewCreators = map[string]func(Session) View{
|
var systemViewCreators = map[string]func(Session) View{
|
||||||
"View": newView,
|
"View": newView,
|
||||||
"ColumnLayout": newColumnLayout,
|
"ColumnLayout": newColumnLayout,
|
||||||
"ListLayout": newListLayout,
|
"ListLayout": newListLayout,
|
||||||
|
|
@ -36,54 +37,63 @@ var viewCreators = map[string]func(Session) View{
|
||||||
"VideoPlayer": newVideoPlayer,
|
"VideoPlayer": newVideoPlayer,
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterViewCreator register function of creating view
|
// ViewCreateListener is the listener interface of a view create event
|
||||||
func RegisterViewCreator(tag string, creator func(Session) View) bool {
|
type ViewCreateListener interface {
|
||||||
builtinViews := []string{
|
// OnCreate is a function of binding object that is called by the CreateViewFromText, CreateViewFromResources,
|
||||||
"View",
|
// and CreateViewFromObject functions after the creation of a view
|
||||||
"ViewsContainer",
|
OnCreate(view View)
|
||||||
"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 {
|
var viewCreate map[string]func(Session) View = nil
|
||||||
if name == tag {
|
|
||||||
|
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 {
|
||||||
|
loTag := strings.ToLower(tag)
|
||||||
|
for name := range systemViewCreators {
|
||||||
|
if name == loTag {
|
||||||
|
ErrorLog(`It is forbidden to override the function of ` + tag + ` creating`)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
viewCreators[tag] = creator
|
viewCreators()[loTag] = creator
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateViewFromObject create new View and initialize it by Node data
|
// CreateViewFromObject create new View and initialize it by DataObject data. Parameters:
|
||||||
func CreateViewFromObject(session Session, object DataObject) View {
|
// - session - the session to which the view will be attached (should not be nil);
|
||||||
tag := object.Tag()
|
// - 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
|
||||||
|
}
|
||||||
|
|
||||||
|
tag := object.Tag()
|
||||||
|
creator, ok := viewCreators()[strings.ToLower(tag)]
|
||||||
|
if !ok {
|
||||||
|
ErrorLog(`Unknown view type "` + tag + `"`)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if creator, ok := viewCreators[tag]; ok {
|
|
||||||
if !session.ignoreViewUpdates() {
|
if !session.ignoreViewUpdates() {
|
||||||
session.setIgnoreViewUpdates(true)
|
session.setIgnoreViewUpdates(true)
|
||||||
defer session.setIgnoreViewUpdates(false)
|
defer session.setIgnoreViewUpdates(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
view := creator(session)
|
view := creator(session)
|
||||||
view.init(session)
|
view.init(session)
|
||||||
if customView, ok := view.(CustomView); ok {
|
if customView, ok := view.(CustomView); ok {
|
||||||
|
|
@ -92,28 +102,63 @@ func CreateViewFromObject(session Session, object DataObject) View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parseProperties(view, object)
|
parseProperties(view, object)
|
||||||
|
if binding != nil {
|
||||||
|
view.setRaw(Binding, binding)
|
||||||
|
if listener, ok := binding.(ViewCreateListener); ok {
|
||||||
|
listener.OnCreate(view)
|
||||||
|
}
|
||||||
|
}
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorLog(`Unknown view type "` + object.Tag() + `"`)
|
// 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
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateViewFromText create new View and initialize it by content of text
|
var b any = nil
|
||||||
func CreateViewFromText(session Session, text string) View {
|
if len(binding) > 0 {
|
||||||
if data := ParseDataText(text); data != nil {
|
b = binding[0]
|
||||||
return CreateViewFromObject(session, data)
|
|
||||||
}
|
}
|
||||||
return nil
|
return CreateViewFromObject(session, data, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateViewFromResources create new View and initialize it by the content of
|
// CreateViewFromResources create new View and initialize it by the content of
|
||||||
// the resource file from "views" directory
|
// the resource file from "views" directory. Parameters:
|
||||||
func CreateViewFromResources(session Session, name string) View {
|
// - session - the session to which the view will be attached (should not be nil);
|
||||||
|
// - name - file name in the "views" directory 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" {
|
if strings.ToLower(filepath.Ext(name)) != ".rui" {
|
||||||
name += ".rui"
|
name += ".rui"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var b any = nil
|
||||||
|
if len(binding) > 0 {
|
||||||
|
b = binding[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
createEmbed := func(fs *embed.FS, path string) View {
|
||||||
|
if data, err := fs.ReadFile(path); err == nil {
|
||||||
|
data, err := ParseDataText(string(data))
|
||||||
|
if err == nil {
|
||||||
|
return CreateViewFromObject(session, data, b)
|
||||||
|
}
|
||||||
|
ErrorLog(err.Error())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
for _, fs := range resources.embedFS {
|
for _, fs := range resources.embedFS {
|
||||||
rootDirs := resources.embedRootDirs(fs)
|
rootDirs := resources.embedRootDirs(fs)
|
||||||
for _, dir := range rootDirs {
|
for _, dir := range rootDirs {
|
||||||
|
|
@ -122,17 +167,13 @@ func CreateViewFromResources(session Session, name string) View {
|
||||||
// do nothing
|
// do nothing
|
||||||
|
|
||||||
case viewDir:
|
case viewDir:
|
||||||
if data, err := fs.ReadFile(dir + "/" + name); err == nil {
|
if result := createEmbed(fs, dir+"/"+name); result != nil {
|
||||||
if data := ParseDataText(string(data)); data != nil {
|
return result
|
||||||
return CreateViewFromObject(session, data)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if data, err := fs.ReadFile(dir + "/" + viewDir + "/" + name); err == nil {
|
if result := createEmbed(fs, dir+"/"+viewDir+"/"+name); result != nil {
|
||||||
if data := ParseDataText(string(data)); data != nil {
|
return result
|
||||||
return CreateViewFromObject(session, data)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -140,8 +181,11 @@ func CreateViewFromResources(session Session, name string) View {
|
||||||
|
|
||||||
if resources.path != "" {
|
if resources.path != "" {
|
||||||
if data, err := os.ReadFile(resources.path + viewDir + "/" + name); err == nil {
|
if data, err := os.ReadFile(resources.path + viewDir + "/" + name); err == nil {
|
||||||
if data := ParseDataText(string(data)); data != nil {
|
data, err := ParseDataText(string(data))
|
||||||
return CreateViewFromObject(session, data)
|
if err != nil {
|
||||||
|
ErrorLog(err.Error())
|
||||||
|
} else {
|
||||||
|
return CreateViewFromObject(session, data, b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
549
viewStyle.go
549
viewStyle.go
|
|
@ -2,9 +2,10 @@ package rui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ViewStyle interface of the style of view
|
// ViewStyle interface of the style of view
|
||||||
|
|
@ -21,8 +22,6 @@ type ViewStyle interface {
|
||||||
// removes the transition animation of the property if "animation" argument is nil.
|
// removes the transition animation of the property if "animation" argument is nil.
|
||||||
// The "tag" argument is the property name.
|
// The "tag" argument is the property name.
|
||||||
SetTransition(tag PropertyName, animation AnimationProperty)
|
SetTransition(tag PropertyName, animation AnimationProperty)
|
||||||
|
|
||||||
cssViewStyle(buffer cssBuilder, session Session)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type viewStyle struct {
|
type viewStyle struct {
|
||||||
|
|
@ -108,7 +107,87 @@ func split4Values(text string) []string {
|
||||||
return []string{}
|
return []string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) {
|
func writeListStyleCSS(style Properties, builder cssBuilder, session Session) {
|
||||||
|
wrap, _ := enumProperty(style, ListWrap, session, 0)
|
||||||
|
orientation, ok := valueToOrientation(style.Get(Orientation), session)
|
||||||
|
if ok || wrap > 0 {
|
||||||
|
cssText := enumProperties[Orientation].cssValues[orientation]
|
||||||
|
switch wrap {
|
||||||
|
case ListWrapOn:
|
||||||
|
cssText += " wrap"
|
||||||
|
|
||||||
|
case ListWrapReverse:
|
||||||
|
cssText += " wrap-reverse"
|
||||||
|
}
|
||||||
|
builder.add(`flex-flow`, cssText)
|
||||||
|
}
|
||||||
|
|
||||||
|
rows := (orientation == StartToEndOrientation || orientation == EndToStartOrientation)
|
||||||
|
|
||||||
|
var hAlignTag, vAlignTag string
|
||||||
|
if rows {
|
||||||
|
hAlignTag = `justify-content`
|
||||||
|
vAlignTag = `align-items`
|
||||||
|
} else {
|
||||||
|
hAlignTag = `align-items`
|
||||||
|
vAlignTag = `justify-content`
|
||||||
|
}
|
||||||
|
|
||||||
|
if align, ok := enumProperty(style, HorizontalAlign, session, LeftAlign); ok {
|
||||||
|
switch align {
|
||||||
|
case LeftAlign:
|
||||||
|
if (!rows && wrap == ListWrapReverse) || orientation == EndToStartOrientation {
|
||||||
|
builder.add(hAlignTag, `flex-end`)
|
||||||
|
} else {
|
||||||
|
builder.add(hAlignTag, `flex-start`)
|
||||||
|
}
|
||||||
|
case RightAlign:
|
||||||
|
if (!rows && wrap == ListWrapReverse) || orientation == EndToStartOrientation {
|
||||||
|
builder.add(hAlignTag, `flex-start`)
|
||||||
|
} else {
|
||||||
|
builder.add(hAlignTag, `flex-end`)
|
||||||
|
}
|
||||||
|
case CenterAlign:
|
||||||
|
builder.add(hAlignTag, `center`)
|
||||||
|
|
||||||
|
case StretchAlign:
|
||||||
|
if rows {
|
||||||
|
builder.add(hAlignTag, `space-between`)
|
||||||
|
} else {
|
||||||
|
builder.add(hAlignTag, `stretch`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if align, ok := enumProperty(style, VerticalAlign, session, LeftAlign); ok {
|
||||||
|
switch align {
|
||||||
|
case TopAlign:
|
||||||
|
if (rows && wrap == ListWrapReverse) || orientation == BottomUpOrientation {
|
||||||
|
builder.add(vAlignTag, `flex-end`)
|
||||||
|
} else {
|
||||||
|
builder.add(vAlignTag, `flex-start`)
|
||||||
|
}
|
||||||
|
case BottomAlign:
|
||||||
|
if (rows && wrap == ListWrapReverse) || orientation == BottomUpOrientation {
|
||||||
|
builder.add(vAlignTag, `flex-start`)
|
||||||
|
} else {
|
||||||
|
builder.add(vAlignTag, `flex-end`)
|
||||||
|
}
|
||||||
|
case CenterAlign:
|
||||||
|
builder.add(vAlignTag, `center`)
|
||||||
|
|
||||||
|
case StretchAlign:
|
||||||
|
if rows {
|
||||||
|
builder.add(vAlignTag, `stretch`)
|
||||||
|
} else {
|
||||||
|
builder.add(vAlignTag, `space-between`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeViewStyleCSS(style Properties, builder cssBuilder, session Session, ignoreListStyle bool) {
|
||||||
|
|
||||||
if visibility, ok := enumProperty(style, Visibility, session, Visible); ok {
|
if visibility, ok := enumProperty(style, Visibility, session, Visible); ok {
|
||||||
switch visibility {
|
switch visibility {
|
||||||
|
|
@ -274,7 +353,7 @@ func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) {
|
||||||
builder.add("text-shadow", css)
|
builder.add("text-shadow", css)
|
||||||
}
|
}
|
||||||
|
|
||||||
if value, ok := style.properties[ColumnSeparator]; ok {
|
if value := style.getRaw(ColumnSeparator); value != nil {
|
||||||
if separator, ok := value.(ColumnSeparatorProperty); ok {
|
if separator, ok := value.(ColumnSeparatorProperty); ok {
|
||||||
if css := separator.cssValue(session); css != "" {
|
if css := separator.cssValue(session); css != "" {
|
||||||
builder.add("column-rule", css)
|
builder.add("column-rule", css)
|
||||||
|
|
@ -290,81 +369,8 @@ func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wrap, _ := enumProperty(style, ListWrap, session, 0)
|
if !ignoreListStyle {
|
||||||
orientation, ok := valueToOrientation(style.Get(Orientation), session)
|
writeListStyleCSS(style, builder, session)
|
||||||
if ok || wrap > 0 {
|
|
||||||
cssText := enumProperties[Orientation].cssValues[orientation]
|
|
||||||
switch wrap {
|
|
||||||
case ListWrapOn:
|
|
||||||
cssText += " wrap"
|
|
||||||
|
|
||||||
case ListWrapReverse:
|
|
||||||
cssText += " wrap-reverse"
|
|
||||||
}
|
|
||||||
builder.add(`flex-flow`, cssText)
|
|
||||||
}
|
|
||||||
|
|
||||||
rows := (orientation == StartToEndOrientation || orientation == EndToStartOrientation)
|
|
||||||
|
|
||||||
var hAlignTag, vAlignTag string
|
|
||||||
if rows {
|
|
||||||
hAlignTag = `justify-content`
|
|
||||||
vAlignTag = `align-items`
|
|
||||||
} else {
|
|
||||||
hAlignTag = `align-items`
|
|
||||||
vAlignTag = `justify-content`
|
|
||||||
}
|
|
||||||
|
|
||||||
if align, ok := enumProperty(style, HorizontalAlign, session, LeftAlign); ok {
|
|
||||||
switch align {
|
|
||||||
case LeftAlign:
|
|
||||||
if (!rows && wrap == ListWrapReverse) || orientation == EndToStartOrientation {
|
|
||||||
builder.add(hAlignTag, `flex-end`)
|
|
||||||
} else {
|
|
||||||
builder.add(hAlignTag, `flex-start`)
|
|
||||||
}
|
|
||||||
case RightAlign:
|
|
||||||
if (!rows && wrap == ListWrapReverse) || orientation == EndToStartOrientation {
|
|
||||||
builder.add(hAlignTag, `flex-start`)
|
|
||||||
} else {
|
|
||||||
builder.add(hAlignTag, `flex-end`)
|
|
||||||
}
|
|
||||||
case CenterAlign:
|
|
||||||
builder.add(hAlignTag, `center`)
|
|
||||||
|
|
||||||
case StretchAlign:
|
|
||||||
if rows {
|
|
||||||
builder.add(hAlignTag, `space-between`)
|
|
||||||
} else {
|
|
||||||
builder.add(hAlignTag, `stretch`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if align, ok := enumProperty(style, VerticalAlign, session, LeftAlign); ok {
|
|
||||||
switch align {
|
|
||||||
case TopAlign:
|
|
||||||
if (rows && wrap == ListWrapReverse) || orientation == BottomUpOrientation {
|
|
||||||
builder.add(vAlignTag, `flex-end`)
|
|
||||||
} else {
|
|
||||||
builder.add(vAlignTag, `flex-start`)
|
|
||||||
}
|
|
||||||
case BottomAlign:
|
|
||||||
if (rows && wrap == ListWrapReverse) || orientation == BottomUpOrientation {
|
|
||||||
builder.add(vAlignTag, `flex-start`)
|
|
||||||
} else {
|
|
||||||
builder.add(vAlignTag, `flex-end`)
|
|
||||||
}
|
|
||||||
case CenterAlign:
|
|
||||||
builder.add(vAlignTag, `center`)
|
|
||||||
|
|
||||||
case StretchAlign:
|
|
||||||
if rows {
|
|
||||||
builder.add(vAlignTag, `stretch`)
|
|
||||||
} else {
|
|
||||||
builder.add(vAlignTag, `space-between`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if r, ok := rangeProperty(style, Row, session); ok {
|
if r, ok := rangeProperty(style, Row, session); ok {
|
||||||
|
|
@ -380,7 +386,7 @@ func (style *viewStyle) cssViewStyle(builder cssBuilder, session Session) {
|
||||||
builder.add(`grid-template-rows`, text)
|
builder.add(`grid-template-rows`, text)
|
||||||
}
|
}
|
||||||
|
|
||||||
style.writeViewTransformCSS(builder, session)
|
writeViewTransformCSS(style, builder, session)
|
||||||
|
|
||||||
if clip := getClipShapeProperty(style, Clip, session); clip != nil && clip.valid(session) {
|
if clip := getClipShapeProperty(style, Clip, session); clip != nil && clip.valid(session) {
|
||||||
builder.add(`clip-path`, clip.cssStyle(session))
|
builder.add(`clip-path`, clip.cssStyle(session))
|
||||||
|
|
@ -550,50 +556,24 @@ func viewStyleGet(style Properties, tag PropertyName) any {
|
||||||
return style.getRaw(tag)
|
return style.getRaw(tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
func supportedPropertyValue(value any) bool {
|
func isQuotesNeeded(text string) bool {
|
||||||
switch value.(type) {
|
|
||||||
case string:
|
|
||||||
case []string:
|
|
||||||
case bool:
|
|
||||||
case float32:
|
|
||||||
case float64:
|
|
||||||
case int:
|
|
||||||
case stringWriter:
|
|
||||||
case fmt.Stringer:
|
|
||||||
case []ShadowProperty:
|
|
||||||
case []View:
|
|
||||||
case []any:
|
|
||||||
case []BackgroundElement:
|
|
||||||
case []BackgroundGradientPoint:
|
|
||||||
case []BackgroundGradientAngle:
|
|
||||||
case map[PropertyName]AnimationProperty:
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func writePropertyValue(buffer *strings.Builder, tag PropertyName, value any, indent string) {
|
|
||||||
|
|
||||||
writeString := func(text string) {
|
|
||||||
simple := (tag != Text && tag != Title && tag != Summary)
|
|
||||||
if simple {
|
|
||||||
if len(text) == 1 {
|
if len(text) == 1 {
|
||||||
simple = (text[0] >= '0' && text[0] <= '9') || (text[0] >= 'A' && text[0] <= 'Z') || (text[0] >= 'a' && text[0] <= 'z')
|
simple := (text[0] >= '0' && text[0] <= '9') || (text[0] >= 'A' && text[0] <= 'Z') || (text[0] >= 'a' && text[0] <= 'z')
|
||||||
|
return !simple
|
||||||
} else {
|
} else {
|
||||||
for _, ch := range text {
|
for _, ch := range text {
|
||||||
if (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') ||
|
if (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') ||
|
||||||
ch == '+' || ch == '-' || ch == '@' || ch == '/' || ch == '_' || ch == '.' ||
|
ch == '+' || ch == '-' || ch == '@' || ch == '/' || ch == '_' || ch == '.' ||
|
||||||
ch == ':' || ch == '#' || ch == '%' || ch == 'π' || ch == '°' {
|
ch == ':' || ch == '#' || ch == '%' || ch == 'π' || ch == '°' {
|
||||||
} else {
|
} else {
|
||||||
simple = false
|
return true
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if !simple {
|
func replaceEscapeSymbols(text string) string {
|
||||||
replace := []struct{ old, new string }{
|
replace := []struct{ old, new string }{
|
||||||
{old: "\\", new: `\\`},
|
{old: "\\", new: `\\`},
|
||||||
{old: "\t", new: `\t`},
|
{old: "\t", new: `\t`},
|
||||||
|
|
@ -602,34 +582,49 @@ func writePropertyValue(buffer *strings.Builder, tag PropertyName, value any, in
|
||||||
{old: "\"", new: `\"`},
|
{old: "\"", new: `\"`},
|
||||||
}
|
}
|
||||||
for _, s := range replace {
|
for _, s := range replace {
|
||||||
text = strings.Replace(text, s.old, s.new, -1)
|
text = strings.ReplaceAll(text, s.old, s.new)
|
||||||
}
|
}
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
func propertyValueToString(tag PropertyName, value any, indent string) string {
|
||||||
|
|
||||||
|
writeString := func(buffer *strings.Builder, text string) string {
|
||||||
|
if isQuotesNeeded(text) {
|
||||||
buffer.WriteRune('"')
|
buffer.WriteRune('"')
|
||||||
buffer.WriteString(text)
|
buffer.WriteString(replaceEscapeSymbols(text))
|
||||||
buffer.WriteRune('"')
|
buffer.WriteRune('"')
|
||||||
} else {
|
} else {
|
||||||
buffer.WriteString(text)
|
buffer.WriteString(text)
|
||||||
}
|
}
|
||||||
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
switch value := value.(type) {
|
switch value := value.(type) {
|
||||||
case string:
|
case string:
|
||||||
writeString(value)
|
if tag == Text || tag == Title || tag == Summary || isQuotesNeeded(value) {
|
||||||
|
return `"` + replaceEscapeSymbols(value) + `"`
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
|
||||||
case []string:
|
case []string:
|
||||||
if len(value) == 0 {
|
if len(value) == 0 {
|
||||||
buffer.WriteString("[]")
|
return ""
|
||||||
} else {
|
}
|
||||||
|
|
||||||
size := 0
|
size := 0
|
||||||
for _, text := range value {
|
for _, text := range value {
|
||||||
size += len(text) + 2
|
size += len(text) + 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buffer := allocStringBuilder()
|
||||||
|
defer freeStringBuilder(buffer)
|
||||||
|
|
||||||
if size < 80 {
|
if size < 80 {
|
||||||
lead := "["
|
lead := "["
|
||||||
for _, text := range value {
|
for _, text := range value {
|
||||||
buffer.WriteString(lead)
|
buffer.WriteString(lead)
|
||||||
writeString(text)
|
writeString(buffer, text)
|
||||||
lead = ", "
|
lead = ", "
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -637,45 +632,58 @@ func writePropertyValue(buffer *strings.Builder, tag PropertyName, value any, in
|
||||||
for _, text := range value {
|
for _, text := range value {
|
||||||
buffer.WriteString(indent)
|
buffer.WriteString(indent)
|
||||||
buffer.WriteRune('\t')
|
buffer.WriteRune('\t')
|
||||||
writeString(text)
|
writeString(buffer, text)
|
||||||
buffer.WriteString(",\n")
|
buffer.WriteString(",\n")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
buffer.WriteString(indent)
|
buffer.WriteString(indent)
|
||||||
buffer.WriteRune(']')
|
|
||||||
}
|
}
|
||||||
|
buffer.WriteRune(']')
|
||||||
|
return buffer.String()
|
||||||
|
|
||||||
case bool:
|
case bool:
|
||||||
if value {
|
if value {
|
||||||
buffer.WriteString("true")
|
return "true"
|
||||||
} else {
|
|
||||||
buffer.WriteString("false")
|
|
||||||
}
|
}
|
||||||
|
return "false"
|
||||||
|
|
||||||
case float32:
|
case float32:
|
||||||
buffer.WriteString(fmt.Sprintf("%g", float64(value)))
|
return fmt.Sprintf("%g", float64(value))
|
||||||
|
|
||||||
case float64:
|
case float64:
|
||||||
buffer.WriteString(fmt.Sprintf("%g", value))
|
return fmt.Sprintf("%g", value)
|
||||||
|
|
||||||
case int:
|
case int:
|
||||||
if prop, ok := enumProperties[tag]; ok && value >= 0 && value < len(prop.values) {
|
if prop, ok := enumProperties[tag]; ok && value >= 0 && value < len(prop.values) {
|
||||||
buffer.WriteString(prop.values[value])
|
return prop.values[value]
|
||||||
} else {
|
|
||||||
buffer.WriteString(strconv.Itoa(value))
|
|
||||||
}
|
}
|
||||||
|
return strconv.Itoa(value)
|
||||||
|
|
||||||
case stringWriter:
|
case stringWriter:
|
||||||
|
buffer := allocStringBuilder()
|
||||||
|
defer freeStringBuilder(buffer)
|
||||||
value.writeString(buffer, indent+"\t")
|
value.writeString(buffer, indent+"\t")
|
||||||
|
return buffer.String()
|
||||||
|
|
||||||
|
case View:
|
||||||
|
buffer := allocStringBuilder()
|
||||||
|
defer freeStringBuilder(buffer)
|
||||||
|
|
||||||
|
writeViewStyle(value.Tag(), value, buffer, indent, value.excludeTags())
|
||||||
|
return buffer.String()
|
||||||
|
|
||||||
case fmt.Stringer:
|
case fmt.Stringer:
|
||||||
writeString(value.String())
|
return value.String()
|
||||||
|
|
||||||
case []ShadowProperty:
|
case []ShadowProperty:
|
||||||
switch len(value) {
|
size := len(value)
|
||||||
case 0:
|
if size == 0 {
|
||||||
// do nothing
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer := allocStringBuilder()
|
||||||
|
defer freeStringBuilder(buffer)
|
||||||
|
|
||||||
|
switch len(value) {
|
||||||
case 1:
|
case 1:
|
||||||
value[0].writeString(buffer, indent)
|
value[0].writeString(buffer, indent)
|
||||||
|
|
||||||
|
|
@ -691,58 +699,67 @@ func writePropertyValue(buffer *strings.Builder, tag PropertyName, value any, in
|
||||||
buffer.WriteString(indent)
|
buffer.WriteString(indent)
|
||||||
buffer.WriteRune(']')
|
buffer.WriteRune(']')
|
||||||
}
|
}
|
||||||
|
return buffer.String()
|
||||||
|
|
||||||
case []View:
|
case []View:
|
||||||
switch len(value) {
|
size := len(value)
|
||||||
case 0:
|
if size == 0 {
|
||||||
buffer.WriteString("[]")
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
case 1:
|
buffer := allocStringBuilder()
|
||||||
writeViewStyle(value[0].Tag(), value[0], buffer, indent, value[0].exscludeTags())
|
defer freeStringBuilder(buffer)
|
||||||
|
|
||||||
default:
|
if size == 1 {
|
||||||
|
writeViewStyle(value[0].Tag(), value[0], buffer, indent, value[0].excludeTags())
|
||||||
|
} else {
|
||||||
buffer.WriteString("[\n")
|
buffer.WriteString("[\n")
|
||||||
indent2 := indent + "\t"
|
indent2 := indent + "\t"
|
||||||
for _, v := range value {
|
for _, view := range value {
|
||||||
buffer.WriteString(indent2)
|
buffer.WriteString(indent2)
|
||||||
writeViewStyle(v.Tag(), v, buffer, indent2, v.exscludeTags())
|
writeViewStyle(view.Tag(), view, buffer, indent2, view.excludeTags())
|
||||||
buffer.WriteString(",\n")
|
buffer.WriteString(",\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.WriteString(indent)
|
buffer.WriteString(indent)
|
||||||
buffer.WriteRune(']')
|
buffer.WriteRune(']')
|
||||||
}
|
}
|
||||||
|
return buffer.String()
|
||||||
|
|
||||||
case []any:
|
case []any:
|
||||||
switch count := len(value); count {
|
size := len(value)
|
||||||
case 0:
|
if size == 0 {
|
||||||
buffer.WriteString("[]")
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
case 1:
|
text := make([]string, 0, size)
|
||||||
writePropertyValue(buffer, tag, value[0], indent)
|
|
||||||
|
|
||||||
default:
|
|
||||||
buffer.WriteString("[ ")
|
|
||||||
comma := false
|
|
||||||
for _, v := range value {
|
for _, v := range value {
|
||||||
if comma {
|
if txt := propertyValueToString(tag, v, indent); txt != "" {
|
||||||
buffer.WriteString(", ")
|
text = append(text, txt)
|
||||||
}
|
}
|
||||||
writePropertyValue(buffer, tag, v, indent)
|
|
||||||
comma = true
|
|
||||||
}
|
}
|
||||||
buffer.WriteString(" ]")
|
|
||||||
|
switch len(text) {
|
||||||
|
case 0:
|
||||||
|
return ""
|
||||||
|
case 1:
|
||||||
|
return text[0]
|
||||||
|
default:
|
||||||
|
return propertyValueToString(tag, text, indent)
|
||||||
}
|
}
|
||||||
|
|
||||||
case []BackgroundElement:
|
case []BackgroundElement:
|
||||||
switch len(value) {
|
size := len(value)
|
||||||
case 0:
|
if size == 0 {
|
||||||
buffer.WriteString("[]\n")
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
case 1:
|
buffer := allocStringBuilder()
|
||||||
|
defer freeStringBuilder(buffer)
|
||||||
|
|
||||||
|
if size == 1 {
|
||||||
value[0].writeString(buffer, indent)
|
value[0].writeString(buffer, indent)
|
||||||
|
} else {
|
||||||
default:
|
|
||||||
buffer.WriteString("[\n")
|
buffer.WriteString("[\n")
|
||||||
indent2 := indent + "\t"
|
indent2 := indent + "\t"
|
||||||
for _, element := range value {
|
for _, element := range value {
|
||||||
|
|
@ -754,8 +771,12 @@ func writePropertyValue(buffer *strings.Builder, tag PropertyName, value any, in
|
||||||
buffer.WriteString(indent)
|
buffer.WriteString(indent)
|
||||||
buffer.WriteRune(']')
|
buffer.WriteRune(']')
|
||||||
}
|
}
|
||||||
|
return buffer.String()
|
||||||
|
|
||||||
case []BackgroundGradientPoint:
|
case []BackgroundGradientPoint:
|
||||||
|
buffer := allocStringBuilder()
|
||||||
|
defer freeStringBuilder(buffer)
|
||||||
|
|
||||||
buffer.WriteRune('"')
|
buffer.WriteRune('"')
|
||||||
for i, point := range value {
|
for i, point := range value {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
|
|
@ -764,8 +785,12 @@ func writePropertyValue(buffer *strings.Builder, tag PropertyName, value any, in
|
||||||
buffer.WriteString(point.String())
|
buffer.WriteString(point.String())
|
||||||
}
|
}
|
||||||
buffer.WriteRune('"')
|
buffer.WriteRune('"')
|
||||||
|
return buffer.String()
|
||||||
|
|
||||||
case []BackgroundGradientAngle:
|
case []BackgroundGradientAngle:
|
||||||
|
buffer := allocStringBuilder()
|
||||||
|
defer freeStringBuilder(buffer)
|
||||||
|
|
||||||
buffer.WriteRune('"')
|
buffer.WriteRune('"')
|
||||||
for i, point := range value {
|
for i, point := range value {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
|
|
@ -774,26 +799,28 @@ func writePropertyValue(buffer *strings.Builder, tag PropertyName, value any, in
|
||||||
buffer.WriteString(point.String())
|
buffer.WriteString(point.String())
|
||||||
}
|
}
|
||||||
buffer.WriteRune('"')
|
buffer.WriteRune('"')
|
||||||
|
return buffer.String()
|
||||||
|
|
||||||
case map[PropertyName]AnimationProperty:
|
case map[PropertyName]AnimationProperty:
|
||||||
switch count := len(value); count {
|
size := len(value)
|
||||||
case 0:
|
if size == 0 {
|
||||||
buffer.WriteString("[]")
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
case 1:
|
buffer := allocStringBuilder()
|
||||||
|
defer freeStringBuilder(buffer)
|
||||||
|
|
||||||
|
if size == 1 {
|
||||||
for tag, animation := range value {
|
for tag, animation := range value {
|
||||||
animation.writeTransitionString(tag, buffer)
|
animation.writeTransitionString(tag, buffer)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
default:
|
|
||||||
tags := make([]PropertyName, 0, len(value))
|
tags := make([]PropertyName, 0, len(value))
|
||||||
for tag := range value {
|
for tag := range value {
|
||||||
tags = append(tags, tag)
|
tags = append(tags, tag)
|
||||||
}
|
}
|
||||||
sort.Slice(tags, func(i, j int) bool {
|
slices.Sort(tags)
|
||||||
return tags[i] < tags[j]
|
|
||||||
})
|
|
||||||
buffer.WriteString("[\n")
|
buffer.WriteString("[\n")
|
||||||
indent2 := indent + "\t"
|
indent2 := indent + "\t"
|
||||||
for _, tag := range tags {
|
for _, tag := range tags {
|
||||||
|
|
@ -806,6 +833,171 @@ func writePropertyValue(buffer *strings.Builder, tag PropertyName, value any, in
|
||||||
buffer.WriteString(indent)
|
buffer.WriteString(indent)
|
||||||
buffer.WriteRune(']')
|
buffer.WriteRune(']')
|
||||||
}
|
}
|
||||||
|
return buffer.String()
|
||||||
|
|
||||||
|
case []AnimationProperty:
|
||||||
|
size := len(value)
|
||||||
|
if size == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer := allocStringBuilder()
|
||||||
|
defer freeStringBuilder(buffer)
|
||||||
|
|
||||||
|
buffer.WriteString("[\n")
|
||||||
|
indent2 := indent + "\t"
|
||||||
|
for _, anim := range value {
|
||||||
|
if anim != nil {
|
||||||
|
anim.writeAnimationString(Animation, buffer, indent2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer.WriteString(indent)
|
||||||
|
buffer.WriteRune(']')
|
||||||
|
|
||||||
|
return buffer.String()
|
||||||
|
|
||||||
|
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 []popupListener:
|
||||||
|
return getPopupListenerBinding(value)
|
||||||
|
|
||||||
|
case map[PropertyName]any:
|
||||||
|
if len(value) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer := allocStringBuilder()
|
||||||
|
defer freeStringBuilder(buffer)
|
||||||
|
|
||||||
|
indent2 := indent + "\t"
|
||||||
|
buffer.WriteString("_{\n")
|
||||||
|
for key, listener := range value {
|
||||||
|
if text, ok := listener.(string); ok && text != "" {
|
||||||
|
buffer.WriteString(indent2)
|
||||||
|
writeString(buffer, string(key))
|
||||||
|
buffer.WriteString(" = ")
|
||||||
|
writeString(buffer, text)
|
||||||
|
buffer.WriteString(",\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer.WriteString(indent)
|
||||||
|
buffer.WriteRune('}')
|
||||||
|
return buffer.String()
|
||||||
|
|
||||||
|
case []popupButton:
|
||||||
|
if len(value) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer := allocStringBuilder()
|
||||||
|
defer freeStringBuilder(buffer)
|
||||||
|
|
||||||
|
lead := indent + "\t_{ title = "
|
||||||
|
|
||||||
|
buffer.WriteString("[\n")
|
||||||
|
for _, button := range value {
|
||||||
|
buffer.WriteString(lead)
|
||||||
|
writeString(buffer, button.title)
|
||||||
|
switch button.buttonType {
|
||||||
|
case CancelButton:
|
||||||
|
buffer.WriteString(`, type = cancel`)
|
||||||
|
|
||||||
|
case DefaultButton:
|
||||||
|
buffer.WriteString(`, type = default`)
|
||||||
|
}
|
||||||
|
if button.onClick != nil {
|
||||||
|
if text, ok := button.onClick.rawListener().(string); ok {
|
||||||
|
buffer.WriteString(`, click = `)
|
||||||
|
writeString(buffer, text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer.WriteString(" },\n")
|
||||||
|
}
|
||||||
|
buffer.WriteString(indent)
|
||||||
|
buffer.WriteRune(']')
|
||||||
|
return buffer.String()
|
||||||
|
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -815,20 +1007,17 @@ func writeViewStyle(name string, view Properties, buffer *strings.Builder, inden
|
||||||
indent += "\t"
|
indent += "\t"
|
||||||
|
|
||||||
writeProperty := func(tag PropertyName, value any) {
|
writeProperty := func(tag PropertyName, value any) {
|
||||||
for _, exclude := range excludeTags {
|
if !slices.Contains(excludeTags, tag) {
|
||||||
if exclude == tag {
|
text := propertyValueToString(tag, value, indent)
|
||||||
return
|
if text != "" {
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if supportedPropertyValue(value) {
|
|
||||||
buffer.WriteString(indent)
|
buffer.WriteString(indent)
|
||||||
buffer.WriteString(string(tag))
|
buffer.WriteString(string(tag))
|
||||||
buffer.WriteString(" = ")
|
buffer.WriteString(" = ")
|
||||||
writePropertyValue(buffer, tag, value, indent)
|
buffer.WriteString(text)
|
||||||
buffer.WriteString(",\n")
|
buffer.WriteString(",\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tags := view.AllTags()
|
tags := view.AllTags()
|
||||||
removeTag := func(tag PropertyName) {
|
removeTag := func(tag PropertyName) {
|
||||||
|
|
@ -871,7 +1060,7 @@ func writeViewStyle(name string, view Properties, buffer *strings.Builder, inden
|
||||||
finalTags := []PropertyName{
|
finalTags := []PropertyName{
|
||||||
PerspectiveOriginX, PerspectiveOriginY, BackfaceVisible,
|
PerspectiveOriginX, PerspectiveOriginY, BackfaceVisible,
|
||||||
TransformOriginX, TransformOriginY, TransformOriginZ,
|
TransformOriginX, TransformOriginY, TransformOriginZ,
|
||||||
Transform, Clip, Filter, BackdropFilter, Summary, Content, Transition}
|
Transform, Clip, Filter, BackdropFilter, Summary, Content, Transition, Animation}
|
||||||
for _, tag := range finalTags {
|
for _, tag := range finalTags {
|
||||||
removeTag(tag)
|
removeTag(tag)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ func setTransitionProperty(properties Properties, value any) bool {
|
||||||
return false
|
return false
|
||||||
|
|
||||||
case ArrayNode:
|
case ArrayNode:
|
||||||
for i := 0; i < value.ArraySize(); i++ {
|
for i := range value.ArraySize() {
|
||||||
if obj := value.ArrayElement(i).Object(); obj != nil {
|
if obj := value.ArrayElement(i).Object(); obj != nil {
|
||||||
if !setObject(obj) {
|
if !setObject(obj) {
|
||||||
return false
|
return false
|
||||||
|
|
@ -133,7 +133,7 @@ func setTransitionProperty(properties Properties, value any) bool {
|
||||||
|
|
||||||
case ArrayNode:
|
case ArrayNode:
|
||||||
result := true
|
result := true
|
||||||
for i := 0; i < value.ArraySize(); i++ {
|
for i := range value.ArraySize() {
|
||||||
if obj := value.ArrayElement(i).Object(); obj != nil {
|
if obj := value.ArrayElement(i).Object(); obj != nil {
|
||||||
result = setObject(obj) && result
|
result = setObject(obj) && result
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -190,7 +190,7 @@ func viewStyleRemove(properties Properties, tag PropertyName) []PropertyName {
|
||||||
case OutlineStyle, OutlineWidth, OutlineColor:
|
case OutlineStyle, OutlineWidth, OutlineColor:
|
||||||
if outline := getOutlineProperty(properties); outline != nil {
|
if outline := getOutlineProperty(properties); outline != nil {
|
||||||
outline.Remove(tag)
|
outline.Remove(tag)
|
||||||
if outline.empty() {
|
if outline.IsEmpty() {
|
||||||
properties.setRaw(Outline, nil)
|
properties.setRaw(Outline, nil)
|
||||||
}
|
}
|
||||||
return []PropertyName{Outline, tag}
|
return []PropertyName{Outline, tag}
|
||||||
|
|
|
||||||
120
viewUtils.go
120
viewUtils.go
|
|
@ -64,14 +64,10 @@ func SetParams(rootView View, viewID string, params Params) bool {
|
||||||
session := rootView.Session()
|
session := rootView.Session()
|
||||||
session.startUpdateScript(rootView.htmlID())
|
session.startUpdateScript(rootView.htmlID())
|
||||||
result := true
|
result := true
|
||||||
//for tag, value := range params {
|
|
||||||
// result = rootView.Set(tag, value) && result
|
for tag, value := range params.All() {
|
||||||
//}
|
|
||||||
for _, tag := range params.AllTags() {
|
|
||||||
if value, ok := params[tag]; ok {
|
|
||||||
result = rootView.Set(tag, value) && result
|
result = rootView.Set(tag, value) && result
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
session.finishUpdateScript(rootView.htmlID())
|
session.finishUpdateScript(rootView.htmlID())
|
||||||
return result
|
return result
|
||||||
|
|
@ -355,31 +351,41 @@ func GetTextShadows(view View, subviewID ...string) []ShadowProperty {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBackgroundColor returns a background color of the subview.
|
// 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 {
|
func GetBackgroundColor(view View, subviewID ...string) Color {
|
||||||
return colorStyledProperty(view, subviewID, BackgroundColor, false)
|
return colorStyledProperty(view, subviewID, BackgroundColor, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAccentColor returns the accent color for UI controls generated by some elements.
|
// 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 {
|
func GetAccentColor(view View, subviewID ...string) Color {
|
||||||
return colorStyledProperty(view, subviewID, AccentColor, false)
|
return colorStyledProperty(view, subviewID, AccentColor, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFontName returns the subview font.
|
// 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 {
|
func GetFontName(view View, subviewID ...string) string {
|
||||||
return stringStyledProperty(view, nil, FontName, true)
|
return stringStyledProperty(view, nil, FontName, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTextColor returns a text color of the subview.
|
// 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 {
|
func GetTextColor(view View, subviewID ...string) Color {
|
||||||
return colorStyledProperty(view, subviewID, TextColor, true)
|
return colorStyledProperty(view, subviewID, TextColor, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTextSize returns a text size of the subview.
|
// 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 {
|
func GetTextSize(view View, subviewID ...string) SizeUnit {
|
||||||
return sizeStyledProperty(view, subviewID, TextSize, true)
|
return sizeStyledProperty(view, subviewID, TextSize, true)
|
||||||
}
|
}
|
||||||
|
|
@ -392,7 +398,9 @@ func GetTabSize(view View, subviewID ...string) int {
|
||||||
|
|
||||||
// GetTextWeight returns a text weight of the subview. Returns one of next values:
|
// 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
|
// 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 {
|
func GetTextWeight(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, TextWeight, NormalFont, true)
|
return enumStyledProperty(view, subviewID, TextWeight, NormalFont, true)
|
||||||
}
|
}
|
||||||
|
|
@ -401,7 +409,8 @@ func GetTextWeight(view View, subviewID ...string) int {
|
||||||
//
|
//
|
||||||
// LeftAlign = 0, RightAlign = 1, CenterAlign = 2, JustifyAlign = 3
|
// 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 {
|
func GetTextAlign(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, TextAlign, LeftAlign, true)
|
return enumStyledProperty(view, subviewID, TextAlign, LeftAlign, true)
|
||||||
}
|
}
|
||||||
|
|
@ -410,89 +419,116 @@ func GetTextAlign(view View, subviewID ...string) int {
|
||||||
//
|
//
|
||||||
// TextWrapOn = 0, TextWrapOff = 1, TextWrapBalance = 3
|
// 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 {
|
func GetTextWrap(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, TextWrap, TextWrapOn, true)
|
return enumStyledProperty(view, subviewID, TextWrap, TextWrapOn, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTextIndent returns a text indent of the subview.
|
// 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 {
|
func GetTextIndent(view View, subviewID ...string) SizeUnit {
|
||||||
return sizeStyledProperty(view, subviewID, TextIndent, true)
|
return sizeStyledProperty(view, subviewID, TextIndent, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLetterSpacing returns a letter spacing of the subview.
|
// 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 {
|
func GetLetterSpacing(view View, subviewID ...string) SizeUnit {
|
||||||
return sizeStyledProperty(view, subviewID, LetterSpacing, true)
|
return sizeStyledProperty(view, subviewID, LetterSpacing, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetWordSpacing returns a word spacing of the subview.
|
// 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 {
|
func GetWordSpacing(view View, subviewID ...string) SizeUnit {
|
||||||
return sizeStyledProperty(view, subviewID, WordSpacing, true)
|
return sizeStyledProperty(view, subviewID, WordSpacing, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLineHeight returns a height of a text line of the subview.
|
// 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 {
|
func GetLineHeight(view View, subviewID ...string) SizeUnit {
|
||||||
return sizeStyledProperty(view, subviewID, LineHeight, true)
|
return sizeStyledProperty(view, subviewID, LineHeight, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsItalic returns "true" if a text font of the subview is displayed in italics, "false" otherwise.
|
// 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 {
|
func IsItalic(view View, subviewID ...string) bool {
|
||||||
return boolStyledProperty(view, subviewID, Italic, true)
|
return boolStyledProperty(view, subviewID, Italic, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsSmallCaps returns "true" if a text font of the subview is displayed in small caps, "false" otherwise.
|
// 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 {
|
func IsSmallCaps(view View, subviewID ...string) bool {
|
||||||
return boolStyledProperty(view, subviewID, SmallCaps, true)
|
return boolStyledProperty(view, subviewID, SmallCaps, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsStrikethrough returns "true" if a text font of the subview is displayed strikethrough, "false" otherwise.
|
// 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 {
|
func IsStrikethrough(view View, subviewID ...string) bool {
|
||||||
return boolStyledProperty(view, subviewID, Strikethrough, true)
|
return boolStyledProperty(view, subviewID, Strikethrough, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsOverline returns "true" if a text font of the subview is displayed overlined, "false" otherwise.
|
// 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 {
|
func IsOverline(view View, subviewID ...string) bool {
|
||||||
return boolStyledProperty(view, subviewID, Overline, true)
|
return boolStyledProperty(view, subviewID, Overline, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsUnderline returns "true" if a text font of the subview is displayed underlined, "false" otherwise.
|
// 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 {
|
func IsUnderline(view View, subviewID ...string) bool {
|
||||||
return boolStyledProperty(view, subviewID, Underline, true)
|
return boolStyledProperty(view, subviewID, Underline, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTextLineThickness returns the stroke thickness of the decoration line that
|
// 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.
|
// 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 {
|
func GetTextLineThickness(view View, subviewID ...string) SizeUnit {
|
||||||
return sizeStyledProperty(view, subviewID, TextLineThickness, true)
|
return sizeStyledProperty(view, subviewID, TextLineThickness, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTextLineStyle returns the stroke style of the decoration line that
|
// 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.
|
// 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 {
|
func GetTextLineStyle(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, TextLineStyle, SolidLine, true)
|
return enumStyledProperty(view, subviewID, TextLineStyle, SolidLine, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTextLineColor returns the stroke color of the decoration line that
|
// 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.
|
// 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 {
|
func GetTextLineColor(view View, subviewID ...string) Color {
|
||||||
return colorStyledProperty(view, subviewID, TextLineColor, true)
|
return colorStyledProperty(view, subviewID, TextLineColor, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTextTransform returns a text transform of the subview. Return one of next values:
|
// GetTextTransform returns a text transform of the subview. Return one of next values:
|
||||||
// NoneTextTransform (0), CapitalizeTextTransform (1), LowerCaseTextTransform (2) or UpperCaseTextTransform (3)
|
// 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 {
|
func GetTextTransform(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, TextTransform, NoneTextTransform, true)
|
return enumStyledProperty(view, subviewID, TextTransform, NoneTextTransform, true)
|
||||||
}
|
}
|
||||||
|
|
@ -500,14 +536,18 @@ func GetTextTransform(view View, subviewID ...string) int {
|
||||||
// GetWritingMode returns whether lines of text are laid out horizontally or vertically, as well as
|
// 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),
|
// the direction in which blocks progress. Valid values are HorizontalTopToBottom (0),
|
||||||
// HorizontalBottomToTop (1), VerticalRightToLeft (2) and VerticalLeftToRight (3)
|
// 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 {
|
func GetWritingMode(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, WritingMode, HorizontalTopToBottom, true)
|
return enumStyledProperty(view, subviewID, WritingMode, HorizontalTopToBottom, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTextDirection - returns a direction of text, table columns, and horizontal overflow.
|
// GetTextDirection - returns a direction of text, table columns, and horizontal overflow.
|
||||||
// Valid values are SystemTextDirection (0), LeftToRightDirection (1), and RightToLeftDirection (2).
|
// 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 {
|
func GetTextDirection(view View, subviewID ...string) int {
|
||||||
if view == nil {
|
if view == nil {
|
||||||
return SystemTextDirection
|
return SystemTextDirection
|
||||||
|
|
@ -519,7 +559,9 @@ func GetTextDirection(view View, subviewID ...string) int {
|
||||||
// GetVerticalTextOrientation returns a orientation of the text characters in a line. It only affects text
|
// 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").
|
// 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).
|
// 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 {
|
func GetVerticalTextOrientation(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, VerticalTextOrientation, MixedTextOrientation, true)
|
return enumStyledProperty(view, subviewID, VerticalTextOrientation, MixedTextOrientation, true)
|
||||||
}
|
}
|
||||||
|
|
@ -761,7 +803,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).
|
// 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 {
|
func GetCurrent(view View, subviewID ...string) int {
|
||||||
defaultValue := -1
|
defaultValue := -1
|
||||||
if view = getSubview(view, subviewID); view != nil {
|
if view = getSubview(view, subviewID); view != nil {
|
||||||
|
|
@ -775,7 +819,9 @@ func GetCurrent(view View, subviewID ...string) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsUserSelect returns "true" if the user can select text, "false" otherwise.
|
// 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 {
|
func IsUserSelect(view View, subviewID ...string) bool {
|
||||||
if view = getSubview(view, subviewID); view != nil {
|
if view = getSubview(view, subviewID); view != nil {
|
||||||
value, _ := isUserSelect(view)
|
value, _ := isUserSelect(view)
|
||||||
|
|
@ -821,7 +867,8 @@ func isUserSelect(view View) (bool, bool) {
|
||||||
// BlendSoftLight (9), BlendDifference (10), BlendExclusion (11), BlendHue (12),
|
// BlendSoftLight (9), BlendDifference (10), BlendExclusion (11), BlendHue (12),
|
||||||
// BlendSaturation (13), BlendColor (14), BlendLuminosity (15)
|
// 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 {
|
func GetMixBlendMode(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, MixBlendMode, BlendNormal, true)
|
return enumStyledProperty(view, subviewID, MixBlendMode, BlendNormal, true)
|
||||||
}
|
}
|
||||||
|
|
@ -833,13 +880,16 @@ func GetMixBlendMode(view View, subviewID ...string) int {
|
||||||
// BlendSoftLight (9), BlendDifference (10), BlendExclusion (11), BlendHue (12),
|
// BlendSoftLight (9), BlendDifference (10), BlendExclusion (11), BlendHue (12),
|
||||||
// BlendSaturation (13), BlendColor (14), BlendLuminosity (15)
|
// 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 {
|
func GetBackgroundBlendMode(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, BackgroundBlendMode, BlendNormal, true)
|
return enumStyledProperty(view, subviewID, BackgroundBlendMode, BlendNormal, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTooltip returns a tooltip text of the subview.
|
// 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 {
|
func GetTooltip(view View, subviewID ...string) string {
|
||||||
if view = getSubview(view, subviewID); view != nil {
|
if view = getSubview(view, subviewID); view != nil {
|
||||||
if value := view.Get(Tooltip); value != nil {
|
if value := view.Get(Tooltip); value != nil {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,9 @@ type ViewsContainer interface {
|
||||||
// Remove removes a view from the list of a view children and return it
|
// Remove removes a view from the list of a view children and return it
|
||||||
RemoveView(index int) View
|
RemoveView(index int) View
|
||||||
|
|
||||||
|
// Remove removes a view from the list of a view children and return it
|
||||||
|
RemoveViewByID(id string) View
|
||||||
|
|
||||||
// ViewIndex returns the index of view, -1 overwise
|
// ViewIndex returns the index of view, -1 overwise
|
||||||
ViewIndex(view View) int
|
ViewIndex(view View) int
|
||||||
|
|
||||||
|
|
@ -85,10 +88,7 @@ func (container *viewsContainerData) Append(view View) {
|
||||||
|
|
||||||
viewHTML(view, buffer, "")
|
viewHTML(view, buffer, "")
|
||||||
container.Session().appendToInnerHTML(container.htmlID(), buffer.String())
|
container.Session().appendToInnerHTML(container.htmlID(), buffer.String())
|
||||||
|
container.runChangeListener(Content)
|
||||||
if listener, ok := container.changeListener[Content]; ok {
|
|
||||||
listener(container, Content)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -113,9 +113,7 @@ func (container *viewsContainerData) insert(view View, index int) bool {
|
||||||
func (container *viewsContainerData) Insert(view View, index int) {
|
func (container *viewsContainerData) Insert(view View, index int) {
|
||||||
if container.insert(view, index) && container.created {
|
if container.insert(view, index) && container.created {
|
||||||
updateInnerHTML(container.htmlID(), container.Session())
|
updateInnerHTML(container.htmlID(), container.Session())
|
||||||
if listener, ok := container.changeListener[Content]; ok {
|
container.runChangeListener(Content)
|
||||||
listener(container, Content)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -131,11 +129,12 @@ func (container *viewsContainerData) removeView(index int) View {
|
||||||
}
|
}
|
||||||
|
|
||||||
view := container.views[index]
|
view := container.views[index]
|
||||||
if index == 0 {
|
switch index {
|
||||||
|
case 0:
|
||||||
container.views = container.views[1:]
|
container.views = container.views[1:]
|
||||||
} else if index == count-1 {
|
case count - 1:
|
||||||
container.views = container.views[:index]
|
container.views = container.views[:index]
|
||||||
} else {
|
default:
|
||||||
container.views = append(container.views[:index], container.views[index+1:]...)
|
container.views = append(container.views[:index], container.views[index+1:]...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,13 +147,20 @@ func (container *viewsContainerData) RemoveView(index int) View {
|
||||||
view := container.removeView(index)
|
view := container.removeView(index)
|
||||||
if view != nil && container.created {
|
if view != nil && container.created {
|
||||||
container.Session().callFunc("removeView", view.htmlID())
|
container.Session().callFunc("removeView", view.htmlID())
|
||||||
if listener, ok := container.changeListener[Content]; ok {
|
container.runChangeListener(Content)
|
||||||
listener(container, Content)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (container *viewsContainerData) RemoveViewByID(id string) View {
|
||||||
|
for index, view := range container.views {
|
||||||
|
if view.ID() == id {
|
||||||
|
return container.RemoveView(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (container *viewsContainerData) ViewIndex(view View) int {
|
func (container *viewsContainerData) ViewIndex(view View) int {
|
||||||
for index, v := range container.views {
|
for index, v := range container.views {
|
||||||
if v == view {
|
if v == view {
|
||||||
|
|
@ -173,13 +179,15 @@ func (container *viewsContainerData) htmlSubviews(self View, buffer *strings.Bui
|
||||||
}
|
}
|
||||||
|
|
||||||
func viewFromTextValue(text string, session Session) View {
|
func viewFromTextValue(text string, session Session) View {
|
||||||
if strings.Contains(text, "{") && strings.Contains(text, "}") {
|
if data, err := ParseDataText(text); err == nil {
|
||||||
data := ParseDataText(text)
|
if view := CreateViewFromObject(session, data, nil); view != nil {
|
||||||
if data != nil {
|
|
||||||
if view := CreateViewFromObject(session, data); view != nil {
|
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ok, constName := isConstantName(text); ok {
|
||||||
|
if view := CreateViewFromResources(session, constName); view != nil {
|
||||||
|
return view
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return NewTextView(session, Params{Text: text})
|
return NewTextView(session, Params{Text: text})
|
||||||
}
|
}
|
||||||
|
|
@ -277,7 +285,7 @@ func (container *viewsContainerData) setContent(value any) bool {
|
||||||
container.views = views
|
container.views = views
|
||||||
|
|
||||||
case DataObject:
|
case DataObject:
|
||||||
if view := CreateViewFromObject(session, value); view != nil {
|
if view := CreateViewFromObject(session, value, nil); view != nil {
|
||||||
container.views = []View{view}
|
container.views = []View{view}
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
|
|
@ -287,7 +295,7 @@ func (container *viewsContainerData) setContent(value any) bool {
|
||||||
views := []View{}
|
views := []View{}
|
||||||
for _, data := range value {
|
for _, data := range value {
|
||||||
if data.IsObject() {
|
if data.IsObject() {
|
||||||
if view := CreateViewFromObject(session, data.Object()); view != nil {
|
if view := CreateViewFromObject(session, data.Object(), nil); view != nil {
|
||||||
views = append(views, view)
|
views = append(views, view)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -190,7 +190,7 @@ func (bridge *webBridge) argToString(arg any) (string, bool) {
|
||||||
for _, val := range arg {
|
for _, val := range arg {
|
||||||
buffer.WriteRune(lead)
|
buffer.WriteRune(lead)
|
||||||
lead = ','
|
lead = ','
|
||||||
buffer.WriteString(fmt.Sprintf("%g", val))
|
fmt.Fprintf(buffer, "%g", val)
|
||||||
}
|
}
|
||||||
buffer.WriteRune(']')
|
buffer.WriteRune(']')
|
||||||
return buffer.String(), true
|
return buffer.String(), true
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue