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
|
* Added "data-list" property
|
||||||
* Bug fixing
|
* Bug fixing
|
||||||
|
|
||||||
|
|
45
README-ru.md
45
README-ru.md
|
@ -2320,6 +2320,42 @@ ListLayout является контейнером, реализующим ин
|
||||||
Элементы в данном контейнере располагаются в виде списка. Расположением дочерних элементов можно управлять.
|
Элементы в данном контейнере располагаются в виде списка. Расположением дочерних элементов можно управлять.
|
||||||
Для этого 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" (константа Orientation) типа int задает то как дочерние элементы будут
|
Свойство "orientation" (константа Orientation) типа int задает то как дочерние элементы будут
|
||||||
|
@ -3606,11 +3642,14 @@ ListView реализован на основе ListLayout и поэтому о
|
||||||
type ListAdapter interface {
|
type ListAdapter interface {
|
||||||
ListSize() int
|
ListSize() int
|
||||||
ListItem(index int, session Session) View
|
ListItem(index int, session Session) View
|
||||||
IsListItemEnabled(index int) bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Соответственно функции этого интерфейса должны возвращать количество элементов, View i-го элемента и
|
Соответственно функции этого интерфейса должны возвращать количество элементов и View i-го элемента.
|
||||||
статус 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.
|
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
|
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
|
### "orientation" property
|
||||||
|
|
||||||
The "orientation" int property (Orientation constant) specifies how the children will be positioned
|
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 {
|
type ListAdapter interface {
|
||||||
ListSize() int
|
ListSize() int
|
||||||
ListItem(index int, session Session) View
|
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,
|
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).
|
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 creates a View of a list item at the given index
|
||||||
ListItem(index int, session Session) View
|
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 returns the status (enabled/disabled) of a list item at the given index
|
||||||
IsListItemEnabled(index int) bool
|
IsListItemEnabled(index int) bool
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,10 +30,14 @@ const (
|
||||||
// ListLayout - list-container of View
|
// ListLayout - list-container of View
|
||||||
type ListLayout interface {
|
type ListLayout interface {
|
||||||
ViewsContainer
|
ViewsContainer
|
||||||
|
// UpdateContent updates child Views if the "content" property value is set to ListAdapter,
|
||||||
|
// otherwise does nothing
|
||||||
|
UpdateContent()
|
||||||
}
|
}
|
||||||
|
|
||||||
type listLayoutData struct {
|
type listLayoutData struct {
|
||||||
viewsContainerData
|
viewsContainerData
|
||||||
|
adapter ListAdapter
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewListLayout create new ListLayout object and return it
|
// NewListLayout create new ListLayout object and return it
|
||||||
|
@ -94,11 +98,16 @@ func (listLayout *listLayoutData) Remove(tag string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (listLayout *listLayoutData) remove(tag string) {
|
func (listLayout *listLayoutData) remove(tag string) {
|
||||||
if tag == Gap {
|
switch tag {
|
||||||
|
case Gap:
|
||||||
listLayout.remove(ListRowGap)
|
listLayout.remove(ListRowGap)
|
||||||
listLayout.remove(ListColumnGap)
|
listLayout.remove(ListColumnGap)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
case Content:
|
||||||
|
listLayout.adapter = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
listLayout.viewsContainerData.remove(tag)
|
listLayout.viewsContainerData.remove(tag)
|
||||||
if listLayout.created {
|
if listLayout.created {
|
||||||
switch tag {
|
switch tag {
|
||||||
|
@ -118,8 +127,18 @@ func (listLayout *listLayoutData) set(tag string, value any) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if tag == Gap {
|
switch tag {
|
||||||
|
case Gap:
|
||||||
return listLayout.set(ListRowGap, value) && listLayout.set(ListColumnGap, value)
|
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) {
|
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:
|
// GetListVerticalAlign returns the vertical align of a ListLayout or ListView sibview:
|
||||||
// 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.
|
// 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 {
|
func GetListColumnGap(view View, subviewID ...string) SizeUnit {
|
||||||
return sizeStyledProperty(view, subviewID, ListColumnGap, false)
|
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;`)
|
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)
|
listView.itemSize(buffer)
|
||||||
if !listView.adapter.IsListItemEnabled(i) {
|
if ext, ok := listView.adapter.(ListItemEnabled); ok {
|
||||||
buffer.WriteString(`" data-disabled="1`)
|
if !ext.IsListItemEnabled(i) {
|
||||||
|
buffer.WriteString(`" data-disabled="1`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
buffer.WriteString(`">`)
|
buffer.WriteString(`">`)
|
||||||
buffer.WriteString(itemDiv)
|
buffer.WriteString(itemDiv)
|
||||||
|
@ -849,8 +851,10 @@ func (listView *listViewData) noneCheckboxSubviews(buffer *strings.Builder) {
|
||||||
}
|
}
|
||||||
buffer.WriteString(`" `)
|
buffer.WriteString(`" `)
|
||||||
buffer.WriteString(itemStyle)
|
buffer.WriteString(itemStyle)
|
||||||
if !listView.adapter.IsListItemEnabled(i) {
|
if ext, ok := listView.adapter.(ListItemEnabled); ok {
|
||||||
buffer.WriteString(` data-disabled="1"`)
|
if !ext.IsListItemEnabled(i) {
|
||||||
|
buffer.WriteString(` data-disabled="1"`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
buffer.WriteString(`>`)
|
buffer.WriteString(`>`)
|
||||||
|
|
||||||
|
|
|
@ -198,7 +198,7 @@ func (container *viewsContainerData) set(tag string, value any) bool {
|
||||||
|
|
||||||
switch tag {
|
switch tag {
|
||||||
case Content:
|
case Content:
|
||||||
// do nothing
|
return container.setContent(value)
|
||||||
|
|
||||||
case Disabled:
|
case Disabled:
|
||||||
oldDisabled := IsDisabled(container)
|
oldDisabled := IsDisabled(container)
|
||||||
|
@ -215,11 +215,12 @@ func (container *viewsContainerData) set(tag string, value any) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
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()
|
session := container.Session()
|
||||||
switch value := value.(type) {
|
switch value := value.(type) {
|
||||||
case View:
|
case View:
|
||||||
|
@ -249,7 +250,7 @@ func (container *viewsContainerData) set(tag string, value any) bool {
|
||||||
views = append(views, viewFromTextValue(v, session))
|
views = append(views, viewFromTextValue(v, session))
|
||||||
|
|
||||||
default:
|
default:
|
||||||
notCompatibleType(tag, value)
|
notCompatibleType(Content, value)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -276,13 +277,17 @@ func (container *viewsContainerData) set(tag string, value any) bool {
|
||||||
container.views = views
|
container.views = views
|
||||||
|
|
||||||
default:
|
default:
|
||||||
notCompatibleType(tag, value)
|
notCompatibleType(Content, value)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
htmlID := container.htmlID()
|
htmlID := container.htmlID()
|
||||||
|
isDisabled := IsDisabled(container)
|
||||||
for _, view := range container.views {
|
for _, view := range container.views {
|
||||||
view.setParentID(htmlID)
|
view.setParentID(htmlID)
|
||||||
|
if isDisabled {
|
||||||
|
view.Set(Disabled, true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if container.created {
|
if container.created {
|
||||||
|
|
Loading…
Reference in New Issue