Added drag-and-drop of files

This commit is contained in:
Alexei Anoshenko 2025-06-11 14:20:41 +03:00
parent b76e3e56d8
commit 4f07435637
23 changed files with 206 additions and 124 deletions

View File

@ -243,7 +243,7 @@ func parseAnimation(obj DataObject) AnimationProperty {
animation := new(animationData)
animation.init()
for i := 0; i < obj.PropertyCount(); i++ {
for i := range obj.PropertyCount() {
if node := obj.Property(i); node != nil {
tag := PropertyName(node.Tag())
if node.Type() == TextNode {
@ -491,7 +491,7 @@ func animationSet(properties Properties, tag PropertyName, value any) []Property
case DataNode:
parseObject := func(obj DataObject) (AnimatedProperty, bool) {
result := AnimatedProperty{}
for i := 0; i < obj.PropertyCount(); i++ {
for i := range obj.PropertyCount() {
if node := obj.Property(i); node.Type() == TextNode {
propTag := strings.ToLower(node.Tag())
switch propTag {
@ -1050,7 +1050,7 @@ func setAnimationProperty(properties Properties, tag PropertyName, value any) bo
case DataNode:
animations := []AnimationProperty{}
result := true
for i := 0; i < value.ArraySize(); i++ {
for i := range value.ArraySize() {
if obj := value.ArrayElement(i).Object(); obj != nil {
if anim := parseAnimation(obj); anim.hasAnimatedProperty() {
animations = append(animations, anim)

View File

@ -1008,10 +1008,8 @@ function setInputValue(elementId, text) {
}
}
function fileSelectedEvent(element) {
const files = element.files;
if (files) {
let message = "fileSelected{session=" + sessionID + ",id=" + element.id + ",files=[";
function filesTextForMessage(files) {
let message = "files=[";
for(let i = 0; i < files.length; i++) {
if (i > 0) {
message += ",";
@ -1021,7 +1019,15 @@ function fileSelectedEvent(element) {
",size=" + files[i].size +
",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);
}
}
@ -2162,19 +2168,18 @@ function dragAndDropEvent(element, event, tag) {
message += ',data="' + dataText + '"';
}
dataText = ""
for (const file of event.dataTransfer.files) {
if (dataText != "") {
dataText += ";";
}
dataText += file.name;
}
if (dataText != "") {
message += ',files="' + 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 + '"';
}
@ -2251,3 +2256,34 @@ 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`}");
}
}

View File

@ -67,7 +67,7 @@ func createBackground(obj DataObject) BackgroundElement {
}
count := obj.PropertyCount()
for i := 0; i < count; i++ {
for i := range count {
if node := obj.Property(i); node.Type() == TextNode {
if value := node.Text(); value != "" {
result.Set(PropertyName(node.Tag()), value)

View File

@ -279,7 +279,7 @@ func (gradient *backgroundRadialGradient) cssStyle(session Session) string {
}
buffer.WriteString("ellipse ")
shapeText = ""
for i := 0; i < count; i++ {
for i := range count {
buffer.WriteString(value[i].cssString("50%", session))
buffer.WriteString(" ")
}
@ -291,7 +291,7 @@ func (gradient *backgroundRadialGradient) cssStyle(session Session) string {
}
buffer.WriteString("ellipse ")
shapeText = ""
for i := 0; i < count; i++ {
for i := range count {
if value[i] != nil {
switch value := value[i].(type) {
case SizeUnit:

View File

@ -433,7 +433,7 @@ func (border *borderProperty) String() string {
func (border *borderProperty) setBorderObject(obj DataObject) bool {
result := true
for i := 0; i < obj.PropertyCount(); i++ {
for i := range obj.PropertyCount() {
if node := obj.Property(i); node != nil {
tag := PropertyName(node.Tag())
switch node.Type() {

View File

@ -346,3 +346,9 @@ func (customView *CustomViewData) SetTransition(tag PropertyName, animation Anim
customView.superView.SetTransition(tag, animation)
}
}
func (customView *CustomViewData) LoadFile(file FileInfo, result func(FileInfo, []byte)) {
if customView.superView != nil {
customView.superView.LoadFile(file, result)
}
}

View File

@ -222,7 +222,7 @@ func (object *dataObject) ToParams() Params {
case ArrayNode:
array := []any{}
for i := 0; i < node.ArraySize(); i++ {
for i := range node.ArraySize() {
if data := node.ArrayElement(i); data != nil {
if data.IsObject() {
if obj := data.Object(); obj != nil {
@ -465,7 +465,7 @@ func ParseDataText(text string) DataObject {
return invalidEscape()
}
x := 0
for i := 0; i < 2; i++ {
for range 2 {
ch := data[n2]
if ch >= '0' && ch <= '9' {
x = x*16 + int(ch-'0')
@ -485,7 +485,7 @@ func ParseDataText(text string) DataObject {
return invalidEscape()
}
x := 0
for i := 0; i < 4; i++ {
for range 4 {
ch := data[n2]
if ch >= '0' && ch <= '9' {
x = x*16 + int(ch-'0')

View File

@ -164,7 +164,7 @@ const (
type DragAndDropEvent struct {
MouseEvent
Data map[string]string
Files string
Files []FileInfo
Target View
EffectAllowed int
DropEffect int
@ -216,7 +216,7 @@ func (event *DragAndDropEvent) init(session Session, data DataObject) {
}
}
// TODO files
event.Files = parseFilesTag(data)
}
func stringToDropEffect(text string) (int, bool) {
@ -436,6 +436,13 @@ func dragAndDropHtml(view View, buffer *strings.Builder) {
}
}
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.
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetDragStartEventListeners(view View, subviewID ...string) []func(View, DragAndDropEvent) {

View File

@ -1,7 +1,7 @@
package rui
import (
"encoding/base64"
"fmt"
"strconv"
"strings"
"time"
@ -67,6 +67,8 @@ type FileInfo struct {
// MimeType - the file's MIME type.
MimeType string
data []byte
}
// FilePicker represents the FilePicker view
@ -83,10 +85,11 @@ type FilePicker interface {
type filePickerData struct {
viewData
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 {
file.Name, _ = obj.PropertyValue("name")
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
@ -122,7 +130,7 @@ func (picker *filePickerData) init(session Session) {
picker.tag = "FilePicker"
picker.hasHtmlDisabled = true
picker.files = []FileInfo{}
picker.loader = map[int]func(FileInfo, []byte){}
//picker.loader = map[int]func(FileInfo, []byte){}
picker.set = picker.setFunc
picker.changed = picker.propertyChanged
@ -137,17 +145,24 @@ func (picker *filePickerData) Files() []FileInfo {
}
func (picker *filePickerData) LoadFile(file FileInfo, result func(FileInfo, []byte)) {
if result == nil {
return
}
if result != nil {
for i, info := range picker.files {
if info.Name == file.Name && info.Size == file.Size && info.LastModified == file.LastModified {
picker.loader[i] = result
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
}
}
picker.viewData.LoadFile(file, result)
}
}
func (picker *filePickerData) setFunc(tag PropertyName, value any) []PropertyName {
@ -248,65 +263,30 @@ func (picker *filePickerData) htmlProperties(self View, buffer *strings.Builder)
}
}
func (picker *filePickerData) handleCommand(self View, command PropertyName, data DataObject) bool {
switch command {
case "fileSelected":
func parseFilesTag(data DataObject) []FileInfo {
if node := data.PropertyByTag("files"); node != nil && node.Type() == ArrayNode {
count := node.ArraySize()
files := make([]FileInfo, count)
for i := 0; i < count; i++ {
for i := range count {
if value := node.ArrayElement(i); value != nil {
files[i].initBy(value)
files[i] = dataToFileInfo(value)
}
}
picker.files = files
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
for _, listener := range GetFileSelectedListeners(picker) {
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 picker.viewData.handleCommand(self, command, data)
@ -354,7 +334,7 @@ func GetFilePickerAccept(view View, subviewID ...string) []string {
}
if ok {
result := strings.Split(accept, ",")
for i := 0; i < len(result); i++ {
for i := range len(result) {
result[i] = strings.Trim(result[i], " \t\n")
}
return result

View File

@ -159,7 +159,7 @@ func NewFilterProperty(params Params) FilterProperty {
func newFilterProperty(obj DataObject) FilterProperty {
filter := new(filterData)
filter.init()
for i := 0; i < obj.PropertyCount(); i++ {
for i := range obj.PropertyCount() {
if node := obj.Property(i); node != nil {
tag := node.Tag()
switch node.Type() {

View File

@ -176,8 +176,7 @@ func (listLayout *listLayoutData) createContent() bool {
htmlID := listLayout.htmlID()
isDisabled := IsDisabled(listLayout)
count := adapter.ListSize()
for i := 0; i < count; i++ {
for i := range adapter.ListSize() {
if view := adapter.ListItem(i, session); view != nil {
view.setParentID(htmlID)
if isDisabled {

View File

@ -428,7 +428,7 @@ func (listView *listViewData) ReloadListViewData() {
listView.itemFrame = make([]Frame, itemCount)
}
for i := 0; i < itemCount; i++ {
for i := range itemCount {
listView.items[i] = adapter.ListItem(i, listView.Session())
}
} else if len(listView.items) > 0 {
@ -632,7 +632,7 @@ func (listView *listViewData) checkboxSubviews(adapter ListAdapter, buffer *stri
current := GetCurrent(listView)
checkedItems := GetListViewCheckedItems(listView)
for i := 0; i < count; i++ {
for i := range count {
buffer.WriteString(`<div id="`)
buffer.WriteString(listViewID)
buffer.WriteRune('-')
@ -693,7 +693,7 @@ func (listView *listViewData) noneCheckboxSubviews(adapter ListAdapter, buffer *
itemStyle := itemStyleBuilder.String()
current := GetCurrent(listView)
for i := 0; i < count; i++ {
for i := range count {
buffer.WriteString(`<div id="`)
buffer.WriteString(listViewID)
buffer.WriteRune('-')

View File

@ -740,7 +740,7 @@ func (popup *popupData) init(view View, popupParams Params) {
popupCellHeight = append(popupCellHeight, AutoSize())
gap, _ := sizeConstant(session, "ruiPopupButtonGap")
cellWidth := []SizeUnit{}
for i := 0; i < buttonCount; i++ {
for range buttonCount {
cellWidth = append(cellWidth, Fr(1))
}

View File

@ -121,7 +121,7 @@ func (properties *propertyList) writeToBuffer(buffer *strings.Builder,
func parseProperties(properties Properties, object DataObject) {
count := object.PropertyCount()
for i := 0; i < count; i++ {
for i := range count {
if node := object.Property(i); node != nil {
switch node.Type() {
case TextNode:

View File

@ -734,7 +734,7 @@ func (session *sessionData) handleSessionInfo(params DataObject) {
if node := params.PropertyByTag("storage"); node != nil && node.Type() == ObjectNode {
if obj := node.Object(); obj != nil {
for i := 0; i < obj.PropertyCount(); i++ {
for i := range obj.PropertyCount() {
if element := obj.Property(i); element.Type() == TextNode {
session.clientStorage[element.Tag()] = element.Text()
}

View File

@ -61,7 +61,7 @@ func loadStringResources(text string) {
table = map[string]string{}
}
for i := 0; i < obj.PropertyCount(); i++ {
for i := range obj.PropertyCount() {
if prop := obj.Property(i); prop != nil && prop.Type() == TextNode {
table[prop.Tag()] = prop.Text()
}
@ -72,7 +72,7 @@ func loadStringResources(text string) {
tag := data.Tag()
if tag == "strings" {
for i := 0; i < data.PropertyCount(); i++ {
for i := range data.PropertyCount() {
if prop := data.Property(i); prop != nil && prop.Type() == ObjectNode {
parseStrings(prop.Object(), prop.Tag())
}

View File

@ -739,7 +739,7 @@ func (table *tableViewData) setFunc(tag PropertyName, value any) []PropertyName
case DataObject:
params := Params{}
for k := 0; k < value.PropertyCount(); k++ {
for k := range value.PropertyCount() {
if prop := value.Property(k); prop != nil && prop.Type() == TextNode {
params[PropertyName(prop.Tag())] = prop.Text()
}
@ -797,7 +797,7 @@ func (table *tableViewData) setFunc(tag PropertyName, value any) []PropertyName
if strings.Contains(value, ",") {
if values := strings.Split(value, ","); len(values) == 2 {
var n = []int{0, 0}
for i := 0; i < 2; i++ {
for i := range 2 {
var err error
if n[i], err = strconv.Atoi(values[i]); err != nil {
ErrorLog(err.Error())

View File

@ -695,7 +695,7 @@ func (theme *theme) addText(themeText string) bool {
objToStyle := func(obj DataObject) ViewStyle {
params := Params{}
for i := 0; i < obj.PropertyCount(); i++ {
for i := range obj.PropertyCount() {
if node := obj.Property(i); node != nil {
switch node.Type() {
case ArrayNode:
@ -712,14 +712,14 @@ func (theme *theme) addText(themeText string) bool {
return NewViewStyle(params)
}
for i := 0; i < count; i++ {
for i := range count {
if d := data.Property(i); d != nil {
switch tag := d.Tag(); tag {
case "constants":
if d.Type() == ObjectNode {
if obj := d.Object(); obj != nil {
objCount := obj.PropertyCount()
for k := 0; k < objCount; k++ {
for k := range objCount {
if prop := obj.Property(k); prop != nil && prop.Type() == TextNode {
theme.constants[prop.Tag()] = prop.Text()
}
@ -731,7 +731,7 @@ func (theme *theme) addText(themeText string) bool {
if d.Type() == ObjectNode {
if obj := d.Object(); obj != nil {
objCount := obj.PropertyCount()
for k := 0; k < objCount; k++ {
for k := range objCount {
if prop := obj.Property(k); prop != nil && prop.Type() == TextNode {
theme.touchConstants[prop.Tag()] = prop.Text()
}
@ -743,7 +743,7 @@ func (theme *theme) addText(themeText string) bool {
if d.Type() == ObjectNode {
if obj := d.Object(); obj != nil {
objCount := obj.PropertyCount()
for k := 0; k < objCount; k++ {
for k := range objCount {
if prop := obj.Property(k); prop != nil && prop.Type() == TextNode {
theme.colors[prop.Tag()] = prop.Text()
}
@ -755,7 +755,7 @@ func (theme *theme) addText(themeText string) bool {
if d.Type() == ObjectNode {
if obj := d.Object(); obj != nil {
objCount := obj.PropertyCount()
for k := 0; k < objCount; k++ {
for k := range objCount {
if prop := obj.Property(k); prop != nil && prop.Type() == TextNode {
theme.darkColors[prop.Tag()] = prop.Text()
}
@ -767,7 +767,7 @@ func (theme *theme) addText(themeText string) bool {
if d.Type() == ObjectNode {
if obj := d.Object(); obj != nil {
objCount := obj.PropertyCount()
for k := 0; k < objCount; k++ {
for k := range objCount {
if prop := obj.Property(k); prop != nil && prop.Type() == TextNode {
theme.images[prop.Tag()] = prop.Text()
}
@ -779,7 +779,7 @@ func (theme *theme) addText(themeText string) bool {
if d.Type() == ObjectNode {
if obj := d.Object(); obj != nil {
objCount := obj.PropertyCount()
for k := 0; k < objCount; k++ {
for k := range objCount {
if prop := obj.Property(k); prop != nil && prop.Type() == TextNode {
theme.darkImages[prop.Tag()] = prop.Text()
}
@ -790,7 +790,7 @@ func (theme *theme) addText(themeText string) bool {
case "styles":
if d.Type() == ArrayNode {
arraySize := d.ArraySize()
for k := 0; k < arraySize; k++ {
for k := range arraySize {
if element := d.ArrayElement(k); element != nil && element.IsObject() {
if obj := element.Object(); obj != nil {
theme.styles[obj.Tag()] = objToStyle(obj)
@ -803,7 +803,7 @@ func (theme *theme) addText(themeText string) bool {
if d.Type() == ArrayNode && strings.HasPrefix(tag, "styles:") {
if rule, ok := parseMediaRule(tag); ok {
arraySize := d.ArraySize()
for k := 0; k < arraySize; k++ {
for k := range arraySize {
if element := d.ArrayElement(k); element != nil && element.IsObject() {
if obj := element.Object(); obj != nil {
rule.styles[obj.Tag()] = objToStyle(obj)

View File

@ -155,7 +155,7 @@ func (event *TouchEvent) init(data DataObject) {
event.Touches = []Touch{}
event.TimeStamp = getTimeStamp(data)
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 obj := element.Object(); obj != nil {
var touch Touch

View File

@ -369,7 +369,7 @@ func valueToTransformProperty(value any) TransformProperty {
parseObject := func(obj DataObject) TransformProperty {
transform := NewTransformProperty(nil)
ok := true
for i := 0; i < obj.PropertyCount(); i++ {
for i := range obj.PropertyCount() {
if prop := obj.Property(i); prop.Type() == TextNode {
if !transform.Set(PropertyName(prop.Tag()), prop.Text()) {
ok = false

View File

@ -105,3 +105,12 @@ func InlineImageFromResource(filename string) (string, bool) {
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)
}

45
view.go
View File

@ -1,6 +1,7 @@
package rui
import (
"encoding/base64"
"fmt"
"maps"
"strconv"
@ -71,6 +72,12 @@ type View interface {
// HasFocus returns 'true' if the view has focus
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)
handleCommand(self View, command PropertyName, data DataObject) bool
htmlClass(disabled bool) string
@ -115,6 +122,7 @@ type viewData struct {
set func(tag PropertyName, value any) []PropertyName
remove func(tag PropertyName) []PropertyName
changed func(tag PropertyName)
fileLoader map[string]func(FileInfo, []byte)
}
func newView(session Session) View {
@ -160,6 +168,7 @@ func (view *viewData) init(session Session) {
view.noResizeEvent = false
view.created = false
view.hasHtmlDisabled = false
view.fileLoader = map[string]func(FileInfo, []byte){}
}
func (view *viewData) Session() Session {
@ -1179,6 +1188,42 @@ func (view *viewData) handleCommand(self View, command PropertyName, data DataOb
self.onResize(self, floatProperty("x"), floatProperty("y"), floatProperty("width"), floatProperty("height"))
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:
return false
}

View File

@ -64,7 +64,7 @@ func setTransitionProperty(properties Properties, value any) bool {
return false
case ArrayNode:
for i := 0; i < value.ArraySize(); i++ {
for i := range value.ArraySize() {
if obj := value.ArrayElement(i).Object(); obj != nil {
if !setObject(obj) {
return false
@ -133,7 +133,7 @@ func setTransitionProperty(properties Properties, value any) bool {
case ArrayNode:
result := true
for i := 0; i < value.ArraySize(); i++ {
for i := range value.ArraySize() {
if obj := value.ArrayElement(i).Object(); obj != nil {
result = setObject(obj) && result
} else {