diff --git a/animation.go b/animation.go index b170980..122692b 100644 --- a/animation.go +++ b/animation.go @@ -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) diff --git a/app_scripts.js b/app_scripts.js index 4aacd91..0de1018 100644 --- a/app_scripts.js +++ b/app_scripts.js @@ -1008,20 +1008,26 @@ function setInputValue(elementId, text) { } } +function filesTextForMessage(files) { + let message = "files=["; + for(let i = 0; i < files.length; i++) { + if (i > 0) { + message += ","; + } + message += "_{name=\"" + files[i].name + + "\",last-modified=" + files[i].lastModified + + ",size=" + files[i].size + + ",mime-type=\"" + files[i].type + "\"}"; + } + message += "]"; + return message +} + function fileSelectedEvent(element) { const files = element.files; if (files) { - let message = "fileSelected{session=" + sessionID + ",id=" + element.id + ",files=["; - for(let i = 0; i < files.length; i++) { - if (i > 0) { - message += ","; - } - message += "_{name=\"" + files[i].name + - "\",last-modified=" + files[i].lastModified + - ",size=" + files[i].size + - ",mime-type=\"" + files[i].type + "\"}"; - } - sendMessage(message + "]}"); + 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`}"); + } +} diff --git a/background.go b/background.go index 592cb68..79a4c2f 100644 --- a/background.go +++ b/background.go @@ -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) diff --git a/backgroundRadialGradient.go b/backgroundRadialGradient.go index d126899..17cb41a 100644 --- a/backgroundRadialGradient.go +++ b/backgroundRadialGradient.go @@ -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: diff --git a/border.go b/border.go index a4cfef7..45d0db6 100644 --- a/border.go +++ b/border.go @@ -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() { diff --git a/customView.go b/customView.go index e8f8afb..5523e8a 100644 --- a/customView.go +++ b/customView.go @@ -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) + } +} diff --git a/data.go b/data.go index 44bad40..b1cec3d 100644 --- a/data.go +++ b/data.go @@ -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') diff --git a/dragAndDrop.go b/dragAndDrop.go index 156ae62..ae86a9e 100644 --- a/dragAndDrop.go +++ b/dragAndDrop.go @@ -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) { diff --git a/filePicker.go b/filePicker.go index bc2296e..8c8b0b6 100644 --- a/filePicker.go +++ b/filePicker.go @@ -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 @@ -82,11 +84,12 @@ type FilePicker interface { type filePickerData struct { viewData - files []FileInfo - loader map[int]func(FileInfo, []byte) + files []FileInfo + //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,16 +145,23 @@ func (picker *filePickerData) Files() []FileInfo { } func (picker *filePickerData) LoadFile(file FileInfo, result func(FileInfo, []byte)) { - if result == nil { - return - } - - for i, info := range picker.files { - if info.Name == file.Name && info.Size == file.Size && info.LastModified == file.LastModified { - picker.loader[i] = result - picker.Session().callFunc("loadSelectedFile", picker.htmlID(), i) - return + if result != nil { + for i, info := range picker.files { + if info.Name == file.Name && info.Size == file.Size && info.LastModified == file.LastModified { + 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) } } @@ -248,65 +263,30 @@ func (picker *filePickerData) htmlProperties(self View, buffer *strings.Builder) } } +func parseFilesTag(data DataObject) []FileInfo { + if node := data.PropertyByTag("files"); node != nil && node.Type() == ArrayNode { + count := node.ArraySize() + files := make([]FileInfo, count) + for i := range count { + if value := node.ArrayElement(i); value != nil { + files[i] = dataToFileInfo(value) + } + } + return files + } + return nil +} + func (picker *filePickerData) handleCommand(self View, command PropertyName, data DataObject) bool { switch command { case "fileSelected": - if node := data.PropertyByTag("files"); node != nil && node.Type() == ArrayNode { - count := node.ArraySize() - files := make([]FileInfo, count) - for i := 0; i < count; i++ { - if value := node.ArrayElement(i); value != nil { - files[i].initBy(value) - } - } + 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 diff --git a/filter.go b/filter.go index 11d701a..850dc5c 100644 --- a/filter.go +++ b/filter.go @@ -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() { diff --git a/listLayout.go b/listLayout.go index 0709416..55acf8a 100644 --- a/listLayout.go +++ b/listLayout.go @@ -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 { diff --git a/listView.go b/listView.go index d647cf7..c90b392 100644 --- a/listView.go +++ b/listView.go @@ -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(`