package rui import ( "fmt" "strconv" "strings" ) // Constants which represent [ListView] specific properties and events const ( // ListItemClickedEvent is the constant for "list-item-clicked" property tag. // // Used by `ListView`. // Occur when the user clicks on an item in the list. // // General listener format: // `func(list rui.ListView, item int)`. // // where: // list - Interface of a list which generated this event, // item - An index of an item clicked. // // Allowed listener formats: // `func(item int)`, // `func(list rui.ListView)`, // `func()`. ListItemClickedEvent PropertyName = "list-item-clicked" // ListItemSelectedEvent is the constant for "list-item-selected" property tag. // // Used by `ListView`. // Occur when a list item becomes selected. // // General listener format: // `func(list rui.ListView, item int)`. // // where: // list - Interface of a list which generated this event, // item - An index of an item selected. // // Allowed listener formats: ListItemSelectedEvent PropertyName = "list-item-selected" // ListItemCheckedEvent is the constant for "list-item-checked" property tag. // // Used by `ListView`. // Occur when a list item checkbox becomes checked or unchecked. // // General listener format: // `func(list rui.ListView, checkedItems []int)`. // // where: // list - Interface of a list which generated this event, // checkedItems - Array of indices of marked elements. // // Allowed listener formats: // `func(checkedItems []int)`, // `func(list rui.ListView)`, // `func()`. ListItemCheckedEvent PropertyName = "list-item-checked" // ListItemStyle is the constant for "list-item-style" property tag. // // Used by `ListView`. // Defines the style of an unselected item. // // Supported types: `string`. ListItemStyle PropertyName = "list-item-style" // CurrentStyle is the constant for "current-style" property tag. // // Used by `ListView`. // Defines the style of the selected item when the `ListView` is focused. // // Supported types: `string`. CurrentStyle PropertyName = "current-style" // CurrentInactiveStyle is the constant for "current-inactive-style" property tag. // // Used by `ListView`. // Defines the style of the selected item when the `ListView` is unfocused. // // Supported types: `string`. CurrentInactiveStyle PropertyName = "current-inactive-style" ) // Constants which represent values of the "orientation" property of the [ListView]. These are aliases for values used in // [ListLayout] "orientation" property like TopDownOrientation and StartToEndOrientation const ( // VerticalOrientation is the vertical ListView orientation VerticalOrientation = 0 // HorizontalOrientation is the horizontal ListView orientation HorizontalOrientation = 1 ) // Constants which represent values of a "checkbox" property of [ListView] const ( // NoneCheckbox is value of "checkbox" property: no checkbox NoneCheckbox = 0 // SingleCheckbox is value of "checkbox" property: only one item can be checked SingleCheckbox = 1 // MultipleCheckbox is value of "checkbox" property: several items can be checked MultipleCheckbox = 2 ) // ListView represents a ListView view type ListView interface { View ParentView // ReloadListViewData updates ListView content ReloadListViewData() //getCheckedItems() []int getItemFrames() []Frame } type listViewData struct { viewData //adapter ListAdapter //clickedListeners []func(ListView, int) //selectedListeners []func(ListView, int) //checkedListeners []func(ListView, []int) items []View itemFrame []Frame //checkedItem []int } // NewListView creates the new list view func NewListView(session Session, params Params) ListView { view := new(listViewData) view.init(session) setInitParams(view, params) return view } func newListView(session Session) View { return new(listViewData) // NewListView(session, nil) } // Init initialize fields of ViewsContainer by default values func (listView *listViewData) init(session Session) { listView.viewData.init(session) listView.tag = "ListView" listView.systemClass = "ruiListView" listView.items = []View{} listView.itemFrame = []Frame{} listView.normalize = normalizeListViewTag listView.get = listView.getFunc listView.set = listView.setFunc listView.remove = listView.removeFunc listView.changed = listView.propertyChanged } func (listView *listViewData) Views() []View { return listView.items } func normalizeListViewTag(tag PropertyName) PropertyName { tag = defaultNormalize(tag) switch tag { case Content: tag = Items case HorizontalAlign: tag = ItemHorizontalAlign case VerticalAlign: tag = ItemVerticalAlign case "wrap": tag = ListWrap case "row-gap": return ListRowGap case ColumnGap: return ListColumnGap } return tag } func (listView *listViewData) removeFunc(tag PropertyName) []PropertyName { switch tag { case Gap: result := listView.removeFunc(ListRowGap) if result != nil { if result2 := listView.removeFunc(ListColumnGap); result2 != nil { result = append(result, result2...) } } return result } return listView.viewData.removeFunc(tag) } func (listView *listViewData) setFunc(tag PropertyName, value any) []PropertyName { switch tag { case Gap: result := listView.setFunc(ListRowGap, value) if result != nil { if result2 := listView.setFunc(ListColumnGap, value); result2 != nil { result = append(result, result2...) } } return result case ListItemClickedEvent, ListItemSelectedEvent: return setOneArgEventListener[ListView, int](listView, tag, value) case ListItemCheckedEvent: return setOneArgEventListener[ListView, []int](listView, tag, value) case Checked: var checked []int switch value := value.(type) { case string: elements := strings.Split(value, ",") checked = make([]int, 0, len(elements)) for _, val := range elements { if val = strings.Trim(val, " \t"); val != "" { n, err := strconv.Atoi(val) if err != nil { invalidPropertyValue(Checked, value) ErrorLog(err.Error()) return nil } checked = append(checked, n) } } case int: checked = []int{value} case []int: checked = value default: notCompatibleType(tag, value) return nil } return setArrayPropertyValue(listView, Checked, checked) case Items: return listView.setItems(value) case ListItemStyle, CurrentStyle, CurrentInactiveStyle: if text, ok := value.(string); ok { return setStringPropertyValue(listView, tag, text) } notCompatibleType(tag, value) return nil case Current: return setIntProperty(listView, Current, value) } return listView.viewData.setFunc(tag, value) } func (listView *listViewData) propertyChanged(tag PropertyName) { switch tag { case Current: updateInnerHTML(listView.htmlID(), listView.Session()) if listeners := getOneArgEventListeners[ListView, int](listView, nil, ListItemSelectedEvent); len(listeners) > 0 { current := GetCurrent(listView) for _, listener := range listeners { listener(listView, current) } } case Checked: updateInnerHTML(listView.htmlID(), listView.Session()) if listeners := getOneArgEventListeners[ListView, []int](listView, nil, ListItemCheckedEvent); len(listeners) > 0 { checked := GetListViewCheckedItems(listView) for _, listener := range listeners { listener(listView, checked) } } case Items, Orientation, ListWrap, ListRowGap, ListColumnGap, VerticalAlign, HorizontalAlign, Style, StyleDisabled, ItemWidth, ItemHeight, ItemHorizontalAlign, ItemVerticalAlign, ItemCheckbox, CheckboxHorizontalAlign, CheckboxVerticalAlign, ListItemStyle, AccentColor: updateInnerHTML(listView.htmlID(), listView.Session()) case CurrentStyle: listView.Session().updateProperty(listView.htmlID(), "data-focusitemstyle", listViewCurrentStyle(listView)) updateInnerHTML(listView.htmlID(), listView.Session()) case CurrentInactiveStyle: listView.Session().updateProperty(listView.htmlID(), "data-bluritemstyle", listViewCurrentInactiveStyle(listView)) updateInnerHTML(listView.htmlID(), listView.Session()) default: listView.viewData.propertyChanged(tag) } } func (listView *listViewData) getFunc(tag PropertyName) any { switch tag { case Gap: if rowGap := GetListRowGap(listView); rowGap.Equal(GetListColumnGap(listView)) { return rowGap } return AutoSize() } return listView.viewData.getFunc(tag) } func (listView *listViewData) setItems(value any) []PropertyName { var adapter ListAdapter = nil session := listView.session switch value := value.(type) { case []string: adapter = NewTextListAdapter(value, nil) case []DataValue: hasObject := false for _, val := range value { if val.IsObject() { hasObject = true break } } if hasObject { items := make([]View, len(value)) for i, val := range value { if val.IsObject() { if view := CreateViewFromObject(session, val.Object()); view != nil { items[i] = view } else { return nil } } else { items[i] = NewTextView(session, Params{Text: val.Value()}) } } adapter = NewViewListAdapter(items) } else { items := make([]string, len(value)) for i, val := range value { items[i] = val.Value() } adapter = NewTextListAdapter(items, nil) } case []any: items := make([]View, len(value)) for i, val := range value { switch value := val.(type) { case View: items[i] = value case string: items[i] = NewTextView(session, Params{Text: value}) case fmt.Stringer: items[i] = NewTextView(session, Params{Text: value.String()}) case float32: items[i] = NewTextView(session, Params{Text: fmt.Sprintf("%g", float64(value))}) case float64: items[i] = NewTextView(session, Params{Text: fmt.Sprintf("%g", value)}) default: if n, ok := isInt(val); ok { items[i] = NewTextView(session, Params{Text: strconv.Itoa(n)}) } else { notCompatibleType(Items, value) return nil } } } adapter = NewViewListAdapter(items) case []View: adapter = NewViewListAdapter(value) case ListAdapter: adapter = value default: notCompatibleType(Items, value) return nil } listView.setRaw(Items, adapter) return []PropertyName{Items} } func (listView *listViewData) Focusable() bool { return true } func (listView *listViewData) getAdapter() ListAdapter { if value := listView.getRaw(Items); value != nil { if adapter, ok := value.(ListAdapter); ok { return adapter } } return nil } func (listView *listViewData) ReloadListViewData() { itemCount := 0 if adapter := listView.getAdapter(); adapter != nil { itemCount = adapter.ListSize() if itemCount != len(listView.items) { listView.items = make([]View, itemCount) listView.itemFrame = make([]Frame, itemCount) } for i := 0; i < itemCount; i++ { listView.items[i] = adapter.ListItem(i, listView.Session()) } } else if len(listView.items) > 0 { listView.items = []View{} listView.itemFrame = []Frame{} } updateInnerHTML(listView.htmlID(), listView.session) } func (listView *listViewData) getItemFrames() []Frame { return listView.itemFrame } func (listView *listViewData) itemAlign(buffer *strings.Builder) { values := enumProperties[ItemHorizontalAlign].cssValues if hAlign := GetListItemHorizontalAlign(listView); hAlign >= 0 && hAlign < len(values) { buffer.WriteString(" justify-items: ") buffer.WriteString(values[hAlign]) buffer.WriteRune(';') } values = enumProperties[ItemVerticalAlign].cssValues if vAlign := GetListItemVerticalAlign(listView); vAlign >= 0 && vAlign < len(values) { buffer.WriteString(" align-items: ") buffer.WriteString(values[vAlign]) buffer.WriteRune(';') } } func (listView *listViewData) itemSize(buffer *strings.Builder) { if itemWidth := GetListItemWidth(listView); itemWidth.Type != Auto { buffer.WriteString(` min-width: `) buffer.WriteString(itemWidth.cssString("", listView.Session())) buffer.WriteRune(';') } if itemHeight := GetListItemHeight(listView); itemHeight.Type != Auto { buffer.WriteString(` min-height: `) buffer.WriteString(itemHeight.cssString("", listView.Session())) buffer.WriteRune(';') } } func (listView *listViewData) getDivs(checkbox, hCheckboxAlign, vCheckboxAlign int) (string, string, string) { session := listView.Session() contentBuilder := allocStringBuilder() defer freeStringBuilder(contentBuilder) contentBuilder.WriteString(`
`) } else { vCheckboxAlign = TopAlign onDivBuilder.WriteString(`
`) } } else { if hCheckboxAlign == RightAlign { onDivBuilder.WriteString(`
`) } else { onDivBuilder.WriteString(`
`) } switch vCheckboxAlign { case BottomAlign: onDivBuilder.WriteString(` align-items: end;`) case CenterAlign: onDivBuilder.WriteString(` align-items: center;`) default: onDivBuilder.WriteString(` align-items: start;`) } } onDivBuilder.WriteString(`">`) offDivBuilder := allocStringBuilder() defer freeStringBuilder(offDivBuilder) offDivBuilder.WriteString(onDivBuilder.String()) accentColor := Color(0) if color := GetAccentColor(listView, ""); color != 0 { accentColor = color } if checkbox == SingleCheckbox { offDivBuilder.WriteString(session.radiobuttonOffImage()) onDivBuilder.WriteString(session.radiobuttonOnImage(accentColor)) } else { offDivBuilder.WriteString(session.checkboxOffImage(accentColor)) onDivBuilder.WriteString(session.checkboxOnImage(accentColor)) } onDivBuilder.WriteString("
") offDivBuilder.WriteString("
") return onDivBuilder.String(), offDivBuilder.String(), contentBuilder.String() } func (listView *listViewData) checkboxItemDiv(checkbox, hCheckboxAlign, vCheckboxAlign int) string { itemStyleBuilder := allocStringBuilder() defer freeStringBuilder(itemStyleBuilder) itemStyleBuilder.WriteString(`
`) return itemStyleBuilder.String() } func (listView *listViewData) getItemView(adapter ListAdapter, index int) View { if adapter == nil || index < 0 || index >= adapter.ListSize() { return nil } if size := adapter.ListSize(); size != len(listView.items) { listView.items = make([]View, size) listView.itemFrame = make([]Frame, size) } if listView.items[index] == nil { listView.items[index] = adapter.ListItem(index, listView.Session()) } return listView.items[index] } func listViewItemStyle(view View, tag PropertyName, defaultStyle string) string { session := view.Session() if value := view.getRaw(tag); value != nil { if style, ok := value.(string); ok { if style, ok = session.resolveConstants(style); ok { return style } } } if value := valueFromStyle(view, tag); value != nil { if style, ok := value.(string); ok { if style, ok = session.resolveConstants(style); ok { return style } } } return defaultStyle } func (listView *listViewData) listItemStyle() string { return listViewItemStyle(listView, ListItemStyle, "ruiListItem") } func listViewCurrentStyle(view View) string { return listViewItemStyle(view, CurrentStyle, "ruiListItemFocused") } func listViewCurrentInactiveStyle(view View) string { return listViewItemStyle(view, CurrentInactiveStyle, "ruiListItemSelected") } func (listView *listViewData) checkboxSubviews(adapter ListAdapter, buffer *strings.Builder, checkbox int) { count := adapter.ListSize() listViewID := listView.htmlID() hCheckboxAlign := GetListViewCheckboxHorizontalAlign(listView) vCheckboxAlign := GetListViewCheckboxVerticalAlign(listView) itemDiv := listView.checkboxItemDiv(checkbox, hCheckboxAlign, vCheckboxAlign) onDiv, offDiv, contentDiv := listView.getDivs(checkbox, hCheckboxAlign, vCheckboxAlign) current := GetCurrent(listView) checkedItems := GetListViewCheckedItems(listView) for i := 0; i < count; i++ { buffer.WriteString(`
`) buffer.WriteString(itemDiv) checked := false for _, index := range checkedItems { if index == i { buffer.WriteString(onDiv) checked = true break } } if !checked { buffer.WriteString(offDiv) } buffer.WriteString(contentDiv) if view := listView.getItemView(adapter, i); view != nil { //view.setNoResizeEvent() viewHTML(view, buffer, "") } else { buffer.WriteString("ERROR: invalid item view") } buffer.WriteString(`
`) } } func (listView *listViewData) noneCheckboxSubviews(adapter ListAdapter, buffer *strings.Builder) { count := adapter.ListSize() listViewID := listView.htmlID() itemStyleBuilder := allocStringBuilder() defer freeStringBuilder(itemStyleBuilder) itemStyleBuilder.WriteString(`data-left="0" data-top="0" data-width="0" data-height="0" style="max-width: 100%; max-height: 100%; display: grid;`) listView.itemAlign(itemStyleBuilder) listView.itemSize(itemStyleBuilder) itemStyleBuilder.WriteString(`" onclick="listItemClickEvent(this, event)"`) itemStyle := itemStyleBuilder.String() current := GetCurrent(listView) for i := 0; i < count; i++ { buffer.WriteString(`
`) if view := listView.getItemView(adapter, i); view != nil { //view.setNoResizeEvent() viewHTML(view, buffer, "") } else { buffer.WriteString("ERROR: invalid item view") } buffer.WriteString(`
`) } } func (listView *listViewData) updateCheckboxItem(index int, checked bool) { checkbox := GetListViewCheckbox(listView) hCheckboxAlign := GetListViewCheckboxHorizontalAlign(listView) vCheckboxAlign := GetListViewCheckboxVerticalAlign(listView) onDiv, offDiv, contentDiv := listView.getDivs(checkbox, hCheckboxAlign, vCheckboxAlign) buffer := allocStringBuilder() defer freeStringBuilder(buffer) buffer.WriteString(listView.checkboxItemDiv(checkbox, hCheckboxAlign, vCheckboxAlign)) if checked { buffer.WriteString(onDiv) } else { buffer.WriteString(offDiv) } buffer.WriteString(contentDiv) session := listView.Session() if adapter := listView.getAdapter(); adapter != nil { if view := listView.getItemView(adapter, index); view != nil { view.setNoResizeEvent() viewHTML(view, buffer, "") } else { buffer.WriteString("ERROR: invalid item view") } } buffer.WriteString(`
`) session.updateInnerHTML(listView.htmlID()+"-"+strconv.Itoa(index), buffer.String()) } func (listView *listViewData) htmlProperties(self View, buffer *strings.Builder) { listView.viewData.htmlProperties(self, buffer) buffer.WriteString(`onfocus="listViewFocusEvent(this, event)" onblur="listViewBlurEvent(this, event)"`) buffer.WriteString(` onkeydown="listViewKeyDownEvent(this, event)" data-focusitemstyle="`) buffer.WriteString(listViewCurrentStyle(listView)) buffer.WriteString(`" data-bluritemstyle="`) buffer.WriteString(listViewCurrentInactiveStyle(listView)) buffer.WriteString(`"`) if adapter := listView.getAdapter(); adapter != nil { if current := GetCurrent(listView); current >= 0 && current < adapter.ListSize() { buffer.WriteString(` data-current="`) buffer.WriteString(listView.htmlID()) buffer.WriteRune('-') buffer.WriteString(strconv.Itoa(current)) buffer.WriteRune('"') } } listView.viewData.htmlProperties(self, buffer) } /* func (listView *listViewData) cssStyle(self View, builder cssBuilder) { listView.viewData.cssStyle(self, builder) if GetListWrap(listView) != WrapOff { switch GetListOrientation(listView) { case TopDownOrientation, BottomUpOrientation: builder.add(`max-height`, `100%`) default: builder.add(`max-width`, `100%`) } } } */ 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) } buffer.WriteString(`
`) checkbox := GetListViewCheckbox(listView) if checkbox == NoneCheckbox { listView.noneCheckboxSubviews(adapter, buffer) } else { listView.checkboxSubviews(adapter, buffer, checkbox) } buffer.WriteString(`
`) } func (listView *listViewData) handleCommand(self View, command PropertyName, data DataObject) bool { switch command { case "itemSelected": if number, ok := dataIntProperty(data, `number`); ok { listView.handleCurrent(number) } case "itemUnselected": if _, ok := listView.properties[Current]; ok { listView.handleCurrent(-1) } case "itemClick": if number, ok := dataIntProperty(data, `number`); ok { listView.onItemClick(number) } default: return listView.viewData.handleCommand(self, command, data) } return true } func (listView *listViewData) handleCurrent(number int) { listView.properties[Current] = number for _, listener := range getOneArgEventListeners[ListView, int](listView, nil, ListItemSelectedEvent) { listener(listView, number) } if listener, ok := listView.changeListener[Current]; ok { listener(listView, Current) } } func (listView *listViewData) onItemClick(number int) { if IsDisabled(listView) { return } if current := GetCurrent(listView); current != number { listView.Set(Current, number) } if checkbox := GetListViewCheckbox(listView); checkbox != NoneCheckbox { checkedItem := GetListViewCheckedItems(listView) switch checkbox { case SingleCheckbox: if len(checkedItem) == 0 { checkedItem = []int{number} listView.updateCheckboxItem(number, true) } else if checkedItem[0] != number { listView.updateCheckboxItem(checkedItem[0], false) checkedItem = []int{number} listView.updateCheckboxItem(number, true) } else { checkedItem = []int{} listView.updateCheckboxItem(number, false) } case MultipleCheckbox: uncheck := false for i, index := range checkedItem { if index == number { uncheck = true listView.updateCheckboxItem(index, false) count := len(checkedItem) if count == 1 { checkedItem = []int{} } else if i == 0 { checkedItem = checkedItem[1:] } else if i == count-1 { checkedItem = checkedItem[:i] } else { checkedItem = append(checkedItem[:i], checkedItem[i+1:]...) } break } } if !uncheck { listView.updateCheckboxItem(number, true) checkedItem = append(checkedItem, number) } } setArrayPropertyValue(listView, Checked, checkedItem) if listener, ok := listView.changeListener[Checked]; ok { listener(listView, Checked) } for _, listener := range getOneArgEventListeners[ListView, []int](listView, nil, ListItemCheckedEvent) { listener(listView, checkedItem) } } for _, listener := range getOneArgEventListeners[ListView, int](listView, nil, ListItemClickedEvent) { listener(listView, number) } } func (listView *listViewData) onItemResize(self View, index string, x, y, width, height float64) { n, err := strconv.Atoi(index) if err != nil { ErrorLog(err.Error()) } else if n >= 0 && n < len(listView.itemFrame) { listView.itemFrame[n] = Frame{Left: x, Top: y, Width: width, Height: height} } else { ErrorLogF(`Invalid ListView item index: %d`, n) } } // 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. func GetVerticalAlign(view View, subviewID ...string) int { return enumStyledProperty(view, subviewID, VerticalAlign, TopAlign, false) } // GetHorizontalAlign return the vertical align of a list/checkbox: LeftAlign (0), RightAlign (1), CenterAlign (2), StretchAlign (3) // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned. func GetHorizontalAlign(view View, subviewID ...string) int { return enumStyledProperty(view, subviewID, HorizontalAlign, LeftAlign, false) } // GetListItemClickedListeners returns a ListItemClickedListener of the ListView. // If there are no listeners then the empty list is returned // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned. func GetListItemClickedListeners(view View, subviewID ...string) []func(ListView, int) { return getOneArgEventListeners[ListView, int](view, subviewID, ListItemClickedEvent) } // GetListItemSelectedListeners returns a ListItemSelectedListener of the ListView. // If there are no listeners then the empty list is returned // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned. func GetListItemSelectedListeners(view View, subviewID ...string) []func(ListView, int) { return getOneArgEventListeners[ListView, int](view, subviewID, ListItemSelectedEvent) } // GetListItemCheckedListeners returns a ListItemCheckedListener of the ListView. // If there are no listeners then the empty list is returned // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned. func GetListItemCheckedListeners(view View, subviewID ...string) []func(ListView, []int) { return getOneArgEventListeners[ListView, []int](view, subviewID, ListItemCheckedEvent) } // 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. func GetListItemWidth(view View, subviewID ...string) SizeUnit { return sizeStyledProperty(view, subviewID, ItemWidth, false) } // GetListItemHeight returns the height of a ListView item. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned. func GetListItemHeight(view View, subviewID ...string) SizeUnit { return sizeStyledProperty(view, subviewID, ItemHeight, false) } // GetListViewCheckbox returns the ListView checkbox type: NoneCheckbox (0), SingleCheckbox (1), or MultipleCheckbox (2). // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned. func GetListViewCheckbox(view View, subviewID ...string) int { return enumStyledProperty(view, subviewID, ItemCheckbox, 0, false) } // GetListViewCheckedItems returns the array of ListView checked items. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned. func GetListViewCheckedItems(view View, subviewID ...string) []int { if view = getSubview(view, subviewID); view != nil { if value := view.getRaw(Checked); value != nil { if checkedItems, ok := value.([]int); ok { switch GetListViewCheckbox(view) { case MultipleCheckbox: return checkedItems case SingleCheckbox: if len(checkedItems) > 1 { return []int{checkedItems[0]} } } } } } return []int{} } // IsListViewCheckedItem returns true if the ListView item with index is checked, false otherwise. // If the second argument (subviewID) is "" then a value from the first argument (view) is returned. func IsListViewCheckedItem(view View, subviewID string, index int) bool { for _, n := range GetListViewCheckedItems(view, subviewID) { if n == index { return true } } return false } // GetListViewCheckboxVerticalAlign returns the vertical align of the ListView checkbox: // TopAlign (0), BottomAlign (1), CenterAlign (2) // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned. func GetListViewCheckboxVerticalAlign(view View, subviewID ...string) int { return enumStyledProperty(view, subviewID, CheckboxVerticalAlign, TopAlign, false) } // GetListViewCheckboxHorizontalAlign returns the horizontal align of the ListView checkbox: // LeftAlign (0), RightAlign (1), CenterAlign (2) // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned. func GetListViewCheckboxHorizontalAlign(view View, subviewID ...string) int { return enumStyledProperty(view, subviewID, CheckboxHorizontalAlign, LeftAlign, false) } // GetListItemVerticalAlign returns the vertical align of the ListView item content: // TopAlign (0), BottomAlign (1), CenterAlign (2) // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned. func GetListItemVerticalAlign(view View, subviewID ...string) int { return enumStyledProperty(view, subviewID, ItemVerticalAlign, TopAlign, false) } // ItemHorizontalAlign returns the horizontal align of the ListView item content: // LeftAlign (0), RightAlign (1), CenterAlign (2), StretchAlign (3) // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned. func GetListItemHorizontalAlign(view View, subviewID ...string) int { return enumStyledProperty(view, subviewID, ItemHorizontalAlign, LeftAlign, false) } // GetListItemFrame - returns the location and size of the ListView item in pixels. // If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned. func GetListItemFrame(view View, subviewID string, index int) Frame { if subviewID != "" { view = ViewByID(view, subviewID) } if view != nil { if listView, ok := view.(ListView); ok { itemFrames := listView.getItemFrames() if index >= 0 && index < len(itemFrames) { return itemFrames[index] } } } return Frame{Left: 0, Top: 0, Width: 0, Height: 0} } // 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. func GetListViewAdapter(view View, subviewID ...string) ListAdapter { if view = getSubview(view, subviewID); view != nil { if value := view.Get(Items); value != nil { if adapter, ok := value.(ListAdapter); ok { return adapter } } } return nil } // ReloadListViewData updates ListView content // If the second argument (subviewID) is not specified or it is "" then content the first argument (view) is updated. func ReloadListViewData(view View, subviewID ...string) { if view = getSubview(view, subviewID); view != nil { if listView, ok := view.(ListView); ok { listView.ReloadListViewData() } } }