Can use ListAdapter as "content" property value of ListLayout

This commit is contained in:
anoshenko 2024-06-03 18:31:25 +03:00
parent a9877d99b8
commit f9d7c77500
7 changed files with 179 additions and 17 deletions

View File

@ -1,4 +1,9 @@
# v0.13.0
# v0.16.0
* Can use ListAdapter as "content" property value of ListLayout
* The IsListItemEnabled method of the ListAdapter interface has been made optional
* Bug fixing
# v0.15.0
* Added "data-list" property
* Bug fixing

View File

@ -2320,6 +2320,42 @@ ListLayout является контейнером, реализующим ин
Элементы в данном контейнере располагаются в виде списка. Расположением дочерних элементов можно управлять.
Для этого ListLayout имеет ряд свойств
### "content"
Свойство "content" (константа Content) определяет массив дочерних View.
Данное свойство унаследовано от ViewsContainer.
Также как и для ViewsContainer данному свойству можно присваивать следующие типы данных:
* View (преобразуется во []View содержащий один View);
* []View;
* string (преобразуется во []View содержащий один TextView);
* []string (преобразуется во []View содержащий TextView);
* []any - данный массив должен содержать только View и string (преобразуется в TextView).
Однако кроме этих типов данных свойству "content" ListLayout может быть назначена реализация интерфейса ListAdapter.
ListAdapter используется для создания дочерних View и объявлен как
type ListAdapter interface {
ListSize() int
ListItem(index int, session Session) View
}
Соответственно функции этого интерфейса должны возвращать количество элементов и View i-го элемента.
ListAdapter создает дочерние View в момент установки свойства "content".
Для пересоздания дочерних элементов ListLayout имеет свойство UpdateContent().
Данный метод удаляет все дочерние View и создает их заново используя ListAdapter.
Внимание! При вызове метода UpdateContent() данные из старых View не переносятся в заново создаваемые.
Вы должны сделать это в ручную.
Если свойство "content" присвоен не ListAdapter, то метод UpdateContent() ничего не делает.
Вызвать метод UpdateContent можно также с помощью глобальной функции
func UpdateContent(view View, subviewID ...string)
### "orientation"
Свойство "orientation" (константа Orientation) типа int задает то как дочерние элементы будут
@ -3606,11 +3642,14 @@ ListView реализован на основе ListLayout и поэтому о
type ListAdapter interface {
ListSize() int
ListItem(index int, session Session) View
IsListItemEnabled(index int) bool
}
Соответственно функции этого интерфейса должны возвращать количество элементов, View i-го элемента и
статус i-го элемента разрешен/запрещен.
Соответственно функции этого интерфейса должны возвращать количество элементов и View i-го элемента.
Кроме этих двух обязательных методов может быть определен третий опциональный который задает статус i-го элемента разрешен/запрещен.
Данный метод объявлен как
IsListItemEnabled(index int) bool
Вы можете реализовать этот интерфейс сами или воспользоваться вспомогательными функциями:

View File

@ -2294,6 +2294,43 @@ ListLayout is a container that implements the ViewsContainer interface. To creat
Items in this container are arranged as a list. The position of the children can be controlled.
For this, ListLayout has a number of properties
### "content" property
The "content" property (Content constant) defines an array of child Views.
This property is inherited from ViewsContainer.
Just like for ViewsContainer, this property can be assigned the following data types:
* View (converts to []View containing one View);
* []View;
* string (converts to []View containing one TextView);
* []string (converts to []View containing TextView);
* []any - this array must contain only View and string.
However, in addition to these data types, the "content" property of a ListLayout
can be assigned an implementation of the ListAdapter interface.
ListAdapter is used to create child Views and is declared as
type ListAdapter interface {
ListSize() int
ListItem(index int, session Session) View
}
Accordingly, the functions of this interface must return the number of elements and View of the i-th element.
ListAdapter creates child Views when the "content" property is set.
To recreate child elements, ListLayout has the UpdateContent() property.
This method deletes all child Views and creates them again using the ListAdapter.
Attention! When calling the UpdateContent() method, data from old Views is not transferred to newly created ones.
You must do this manually.
If the "content" property is not assigned to the ListAdapter, then the UpdateContent() method does nothing.
You can also call the UpdateContent method using the global function
func UpdateContent(view View, subviewID ...string)
### "orientation" property
The "orientation" int property (Orientation constant) specifies how the children will be positioned
@ -3573,9 +3610,13 @@ The main value of the "items" property is the ListAdapter interface:
type ListAdapter interface {
ListSize() int
ListItem(index int, session Session) View
IsListItemEnabled(index int) bool
}
In addition to these two mandatory methods, a third optional one can be defined which specifies the status of the i-th element as allowed/disabled.
This method is declared as
IsListItemEnabled(index int) bool
Accordingly, the functions of this interface must return the number of elements,
the View of the i-th element and the status of the i-th element (allowed/denied).

View File

@ -7,7 +7,10 @@ type ListAdapter interface {
// ListItem creates a View of a list item at the given index
ListItem(index int, session Session) View
}
// ListItemEnabled implements the optional method of ListAdapter interface
type ListItemEnabled interface {
// IsListItemEnabled returns the status (enabled/disabled) of a list item at the given index
IsListItemEnabled(index int) bool
}

View File

@ -30,10 +30,14 @@ const (
// ListLayout - list-container of View
type ListLayout interface {
ViewsContainer
// UpdateContent updates child Views if the "content" property value is set to ListAdapter,
// otherwise does nothing
UpdateContent()
}
type listLayoutData struct {
viewsContainerData
adapter ListAdapter
}
// NewListLayout create new ListLayout object and return it
@ -94,11 +98,16 @@ func (listLayout *listLayoutData) Remove(tag string) {
}
func (listLayout *listLayoutData) remove(tag string) {
if tag == Gap {
switch tag {
case Gap:
listLayout.remove(ListRowGap)
listLayout.remove(ListColumnGap)
return
case Content:
listLayout.adapter = nil
}
listLayout.viewsContainerData.remove(tag)
if listLayout.created {
switch tag {
@ -118,8 +127,18 @@ func (listLayout *listLayoutData) set(tag string, value any) bool {
return true
}
if tag == Gap {
switch tag {
case Gap:
return listLayout.set(ListRowGap, value) && listLayout.set(ListColumnGap, value)
case Content:
if adapter, ok := value.(ListAdapter); ok {
listLayout.adapter = adapter
listLayout.UpdateContent()
// TODO
return true
}
listLayout.adapter = nil
}
if listLayout.viewsContainerData.set(tag, value) {
@ -143,6 +162,33 @@ func (listLayout *listLayoutData) htmlSubviews(self View, buffer *strings.Builde
}
}
func (listLayout *listLayoutData) UpdateContent() {
if adapter := listLayout.adapter; adapter != nil {
listLayout.views = []View{}
session := listLayout.session
htmlID := listLayout.htmlID()
isDisabled := IsDisabled(listLayout)
count := adapter.ListSize()
for i := 0; i < count; i++ {
if view := adapter.ListItem(i, session); view != nil {
view.setParentID(htmlID)
if isDisabled {
view.Set(Disabled, true)
}
listLayout.views = append(listLayout.views, view)
}
}
if listLayout.created {
updateInnerHTML(htmlID, session)
}
listLayout.propertyChangedEvent(Content)
}
}
// GetListVerticalAlign returns the vertical align of a ListLayout or ListView sibview:
// 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.
@ -198,3 +244,22 @@ func GetListRowGap(view View, subviewID ...string) SizeUnit {
func GetListColumnGap(view View, subviewID ...string) SizeUnit {
return sizeStyledProperty(view, subviewID, ListColumnGap, false)
}
// UpdateContent updates child Views of ListLayout subview if the "content" property value is set to ListAdapter,
// otherwise does nothing.
// If the second argument (subviewID) is not specified or it is "" then the first argument (view) updates.
func UpdateContent(view View, subviewID ...string) {
if len(subviewID) > 0 && subviewID[0] != "" {
view = ViewByID(view, subviewID[0])
}
if view != nil {
switch view := view.(type) {
case ListLayout:
view.UpdateContent()
case ListView:
view.ReloadListViewData()
}
}
}

View File

@ -790,8 +790,10 @@ func (listView *listViewData) checkboxSubviews(buffer *strings.Builder, checkbox
}
buffer.WriteString(`" onclick="listItemClickEvent(this, event)" data-left="0" data-top="0" data-width="0" data-height="0" style="display: grid; justify-items: stretch; align-items: stretch;`)
listView.itemSize(buffer)
if !listView.adapter.IsListItemEnabled(i) {
buffer.WriteString(`" data-disabled="1`)
if ext, ok := listView.adapter.(ListItemEnabled); ok {
if !ext.IsListItemEnabled(i) {
buffer.WriteString(`" data-disabled="1`)
}
}
buffer.WriteString(`">`)
buffer.WriteString(itemDiv)
@ -849,8 +851,10 @@ func (listView *listViewData) noneCheckboxSubviews(buffer *strings.Builder) {
}
buffer.WriteString(`" `)
buffer.WriteString(itemStyle)
if !listView.adapter.IsListItemEnabled(i) {
buffer.WriteString(` data-disabled="1"`)
if ext, ok := listView.adapter.(ListItemEnabled); ok {
if !ext.IsListItemEnabled(i) {
buffer.WriteString(` data-disabled="1"`)
}
}
buffer.WriteString(`>`)

View File

@ -198,7 +198,7 @@ func (container *viewsContainerData) set(tag string, value any) bool {
switch tag {
case Content:
// do nothing
return container.setContent(value)
case Disabled:
oldDisabled := IsDisabled(container)
@ -215,11 +215,12 @@ func (container *viewsContainerData) set(tag string, value any) bool {
return true
}
return false
default:
return container.viewData.set(tag, value)
}
return container.viewData.set(tag, value)
}
func (container *viewsContainerData) setContent(value any) bool {
session := container.Session()
switch value := value.(type) {
case View:
@ -249,7 +250,7 @@ func (container *viewsContainerData) set(tag string, value any) bool {
views = append(views, viewFromTextValue(v, session))
default:
notCompatibleType(tag, value)
notCompatibleType(Content, value)
return false
}
}
@ -276,13 +277,17 @@ func (container *viewsContainerData) set(tag string, value any) bool {
container.views = views
default:
notCompatibleType(tag, value)
notCompatibleType(Content, value)
return false
}
htmlID := container.htmlID()
isDisabled := IsDisabled(container)
for _, view := range container.views {
view.setParentID(htmlID)
if isDisabled {
view.Set(Disabled, true)
}
}
if container.created {