mirror of https://github.com/anoshenko/rui.git
Added drag-and-drop of files
This commit is contained in:
parent
b76e3e56d8
commit
4f07435637
|
@ -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)
|
||||
|
|
|
@ -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`}");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
6
data.go
6
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')
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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('-')
|
||||
|
|
2
popup.go
2
popup.go
|
@ -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))
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
20
theme.go
20
theme.go
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
9
utils.go
9
utils.go
|
@ -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
45
view.go
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue