mirror of https://github.com/anoshenko/rui.git
Can use ListAdapter as "content" property value of ListLayout
This commit is contained in:
parent
a9877d99b8
commit
f9d7c77500
|
@ -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
|
||||
|
||||
|
|
45
README-ru.md
45
README-ru.md
|
@ -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
|
||||
|
||||
Вы можете реализовать этот интерфейс сами или воспользоваться вспомогательными функциями:
|
||||
|
||||
|
|
43
README.md
43
README.md
|
@ -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).
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
12
listView.go
12
listView.go
|
@ -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(`>`)
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue