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 := new(animationData)
|
||||||
animation.init()
|
animation.init()
|
||||||
|
|
||||||
for i := 0; i < obj.PropertyCount(); i++ {
|
for i := range obj.PropertyCount() {
|
||||||
if node := obj.Property(i); node != nil {
|
if node := obj.Property(i); node != nil {
|
||||||
tag := PropertyName(node.Tag())
|
tag := PropertyName(node.Tag())
|
||||||
if node.Type() == TextNode {
|
if node.Type() == TextNode {
|
||||||
|
@ -491,7 +491,7 @@ func animationSet(properties Properties, tag PropertyName, value any) []Property
|
||||||
case DataNode:
|
case DataNode:
|
||||||
parseObject := func(obj DataObject) (AnimatedProperty, bool) {
|
parseObject := func(obj DataObject) (AnimatedProperty, bool) {
|
||||||
result := AnimatedProperty{}
|
result := AnimatedProperty{}
|
||||||
for i := 0; i < obj.PropertyCount(); i++ {
|
for i := range obj.PropertyCount() {
|
||||||
if node := obj.Property(i); node.Type() == TextNode {
|
if node := obj.Property(i); node.Type() == TextNode {
|
||||||
propTag := strings.ToLower(node.Tag())
|
propTag := strings.ToLower(node.Tag())
|
||||||
switch propTag {
|
switch propTag {
|
||||||
|
@ -1050,7 +1050,7 @@ func setAnimationProperty(properties Properties, tag PropertyName, value any) bo
|
||||||
case DataNode:
|
case DataNode:
|
||||||
animations := []AnimationProperty{}
|
animations := []AnimationProperty{}
|
||||||
result := true
|
result := true
|
||||||
for i := 0; i < value.ArraySize(); i++ {
|
for i := range value.ArraySize() {
|
||||||
if obj := value.ArrayElement(i).Object(); obj != nil {
|
if obj := value.ArrayElement(i).Object(); obj != nil {
|
||||||
if anim := parseAnimation(obj); anim.hasAnimatedProperty() {
|
if anim := parseAnimation(obj); anim.hasAnimatedProperty() {
|
||||||
animations = append(animations, anim)
|
animations = append(animations, anim)
|
||||||
|
|
|
@ -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) {
|
function fileSelectedEvent(element) {
|
||||||
const files = element.files;
|
const files = element.files;
|
||||||
if (files) {
|
if (files) {
|
||||||
let message = "fileSelected{session=" + sessionID + ",id=" + element.id + ",files=[";
|
let message = "fileSelected{session=" + sessionID + ",id=" + element.id + "," + filesTextForMessage(files) + "}";
|
||||||
for(let i = 0; i < files.length; i++) {
|
sendMessage(message);
|
||||||
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 + "]}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2162,19 +2168,18 @@ function dragAndDropEvent(element, event, tag) {
|
||||||
message += ',data="' + dataText + '"';
|
message += ',data="' + dataText + '"';
|
||||||
}
|
}
|
||||||
|
|
||||||
dataText = ""
|
const files = event.dataTransfer.files
|
||||||
for (const file of event.dataTransfer.files) {
|
if (files && files.length > 0) {
|
||||||
if (dataText != "") {
|
message += "," + filesTextForMessage(files)
|
||||||
dataText += ";";
|
element["dragFiles"] = files;
|
||||||
}
|
} else {
|
||||||
dataText += file.name;
|
element["dragFiles"] = null;
|
||||||
}
|
|
||||||
if (dataText != "") {
|
|
||||||
message += ',files="' + dataText + '"';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.dataTransfer.effectAllowed && event.dataTransfer.effectAllowed != "uninitialized") {
|
if (event.dataTransfer.effectAllowed && event.dataTransfer.effectAllowed != "uninitialized") {
|
||||||
message += ',effect-allowed="' + event.dataTransfer.effectAllowed + '"';
|
message += ',effect-allowed="' + event.dataTransfer.effectAllowed + '"';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.dataTransfer.dropEffect) {
|
if (event.dataTransfer.dropEffect) {
|
||||||
message += ',drop-effect="' + event.dataTransfer.dropEffect + '"';
|
message += ',drop-effect="' + event.dataTransfer.dropEffect + '"';
|
||||||
}
|
}
|
||||||
|
@ -2251,3 +2256,34 @@ function dropEvent(element, event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
dragAndDropEvent(element, event, "drop-event")
|
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()
|
count := obj.PropertyCount()
|
||||||
for i := 0; i < count; i++ {
|
for i := range count {
|
||||||
if node := obj.Property(i); node.Type() == TextNode {
|
if node := obj.Property(i); node.Type() == TextNode {
|
||||||
if value := node.Text(); value != "" {
|
if value := node.Text(); value != "" {
|
||||||
result.Set(PropertyName(node.Tag()), value)
|
result.Set(PropertyName(node.Tag()), value)
|
||||||
|
|
|
@ -279,7 +279,7 @@ func (gradient *backgroundRadialGradient) cssStyle(session Session) string {
|
||||||
}
|
}
|
||||||
buffer.WriteString("ellipse ")
|
buffer.WriteString("ellipse ")
|
||||||
shapeText = ""
|
shapeText = ""
|
||||||
for i := 0; i < count; i++ {
|
for i := range count {
|
||||||
buffer.WriteString(value[i].cssString("50%", session))
|
buffer.WriteString(value[i].cssString("50%", session))
|
||||||
buffer.WriteString(" ")
|
buffer.WriteString(" ")
|
||||||
}
|
}
|
||||||
|
@ -291,7 +291,7 @@ func (gradient *backgroundRadialGradient) cssStyle(session Session) string {
|
||||||
}
|
}
|
||||||
buffer.WriteString("ellipse ")
|
buffer.WriteString("ellipse ")
|
||||||
shapeText = ""
|
shapeText = ""
|
||||||
for i := 0; i < count; i++ {
|
for i := range count {
|
||||||
if value[i] != nil {
|
if value[i] != nil {
|
||||||
switch value := value[i].(type) {
|
switch value := value[i].(type) {
|
||||||
case SizeUnit:
|
case SizeUnit:
|
||||||
|
|
|
@ -433,7 +433,7 @@ func (border *borderProperty) String() string {
|
||||||
|
|
||||||
func (border *borderProperty) setBorderObject(obj DataObject) bool {
|
func (border *borderProperty) setBorderObject(obj DataObject) bool {
|
||||||
result := true
|
result := true
|
||||||
for i := 0; i < obj.PropertyCount(); i++ {
|
for i := range obj.PropertyCount() {
|
||||||
if node := obj.Property(i); node != nil {
|
if node := obj.Property(i); node != nil {
|
||||||
tag := PropertyName(node.Tag())
|
tag := PropertyName(node.Tag())
|
||||||
switch node.Type() {
|
switch node.Type() {
|
||||||
|
|
|
@ -346,3 +346,9 @@ func (customView *CustomViewData) SetTransition(tag PropertyName, animation Anim
|
||||||
customView.superView.SetTransition(tag, animation)
|
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:
|
case ArrayNode:
|
||||||
array := []any{}
|
array := []any{}
|
||||||
for i := 0; i < node.ArraySize(); i++ {
|
for i := range node.ArraySize() {
|
||||||
if data := node.ArrayElement(i); data != nil {
|
if data := node.ArrayElement(i); data != nil {
|
||||||
if data.IsObject() {
|
if data.IsObject() {
|
||||||
if obj := data.Object(); obj != nil {
|
if obj := data.Object(); obj != nil {
|
||||||
|
@ -465,7 +465,7 @@ func ParseDataText(text string) DataObject {
|
||||||
return invalidEscape()
|
return invalidEscape()
|
||||||
}
|
}
|
||||||
x := 0
|
x := 0
|
||||||
for i := 0; i < 2; i++ {
|
for range 2 {
|
||||||
ch := data[n2]
|
ch := data[n2]
|
||||||
if ch >= '0' && ch <= '9' {
|
if ch >= '0' && ch <= '9' {
|
||||||
x = x*16 + int(ch-'0')
|
x = x*16 + int(ch-'0')
|
||||||
|
@ -485,7 +485,7 @@ func ParseDataText(text string) DataObject {
|
||||||
return invalidEscape()
|
return invalidEscape()
|
||||||
}
|
}
|
||||||
x := 0
|
x := 0
|
||||||
for i := 0; i < 4; i++ {
|
for range 4 {
|
||||||
ch := data[n2]
|
ch := data[n2]
|
||||||
if ch >= '0' && ch <= '9' {
|
if ch >= '0' && ch <= '9' {
|
||||||
x = x*16 + int(ch-'0')
|
x = x*16 + int(ch-'0')
|
||||||
|
|
|
@ -164,7 +164,7 @@ const (
|
||||||
type DragAndDropEvent struct {
|
type DragAndDropEvent struct {
|
||||||
MouseEvent
|
MouseEvent
|
||||||
Data map[string]string
|
Data map[string]string
|
||||||
Files string
|
Files []FileInfo
|
||||||
Target View
|
Target View
|
||||||
EffectAllowed int
|
EffectAllowed int
|
||||||
DropEffect 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) {
|
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.
|
// 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.
|
// 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) {
|
func GetDragStartEventListeners(view View, subviewID ...string) []func(View, DragAndDropEvent) {
|
||||||
|
|
110
filePicker.go
110
filePicker.go
|
@ -1,7 +1,7 @@
|
||||||
package rui
|
package rui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -67,6 +67,8 @@ type FileInfo struct {
|
||||||
|
|
||||||
// MimeType - the file's MIME type.
|
// MimeType - the file's MIME type.
|
||||||
MimeType string
|
MimeType string
|
||||||
|
|
||||||
|
data []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// FilePicker represents the FilePicker view
|
// FilePicker represents the FilePicker view
|
||||||
|
@ -82,11 +84,12 @@ type FilePicker interface {
|
||||||
|
|
||||||
type filePickerData struct {
|
type filePickerData struct {
|
||||||
viewData
|
viewData
|
||||||
files []FileInfo
|
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 {
|
if obj := node.Object(); obj != nil {
|
||||||
file.Name, _ = obj.PropertyValue("name")
|
file.Name, _ = obj.PropertyValue("name")
|
||||||
file.MimeType, _ = obj.PropertyValue("mime-type")
|
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
|
// NewFilePicker create new FilePicker object and return it
|
||||||
|
@ -122,7 +130,7 @@ func (picker *filePickerData) init(session Session) {
|
||||||
picker.tag = "FilePicker"
|
picker.tag = "FilePicker"
|
||||||
picker.hasHtmlDisabled = true
|
picker.hasHtmlDisabled = true
|
||||||
picker.files = []FileInfo{}
|
picker.files = []FileInfo{}
|
||||||
picker.loader = map[int]func(FileInfo, []byte){}
|
//picker.loader = map[int]func(FileInfo, []byte){}
|
||||||
picker.set = picker.setFunc
|
picker.set = picker.setFunc
|
||||||
picker.changed = picker.propertyChanged
|
picker.changed = picker.propertyChanged
|
||||||
|
|
||||||
|
@ -137,16 +145,23 @@ func (picker *filePickerData) Files() []FileInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (picker *filePickerData) LoadFile(file FileInfo, result func(FileInfo, []byte)) {
|
func (picker *filePickerData) LoadFile(file FileInfo, result func(FileInfo, []byte)) {
|
||||||
if result == nil {
|
if result != nil {
|
||||||
return
|
for i, info := range picker.files {
|
||||||
}
|
if info.Name == file.Name && info.Size == file.Size && info.LastModified == file.LastModified {
|
||||||
|
if info.data != nil {
|
||||||
for i, info := range picker.files {
|
result(info, info.data)
|
||||||
if info.Name == file.Name && info.Size == file.Size && info.LastModified == file.LastModified {
|
} else {
|
||||||
picker.loader[i] = result
|
picker.fileLoader[info.key()] = func(file FileInfo, data []byte) {
|
||||||
picker.Session().callFunc("loadSelectedFile", picker.htmlID(), i)
|
picker.files[i].data = data
|
||||||
return
|
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 {
|
func (picker *filePickerData) handleCommand(self View, command PropertyName, data DataObject) bool {
|
||||||
switch command {
|
switch command {
|
||||||
case "fileSelected":
|
case "fileSelected":
|
||||||
if node := data.PropertyByTag("files"); node != nil && node.Type() == ArrayNode {
|
if files := parseFilesTag(data); files != nil {
|
||||||
count := node.ArraySize()
|
|
||||||
files := make([]FileInfo, count)
|
|
||||||
for i := 0; i < count; i++ {
|
|
||||||
if value := node.ArrayElement(i); value != nil {
|
|
||||||
files[i].initBy(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
picker.files = files
|
picker.files = files
|
||||||
|
|
||||||
for _, listener := range GetFileSelectedListeners(picker) {
|
for _, listener := range GetFileSelectedListeners(picker) {
|
||||||
listener(picker, files)
|
listener(picker, files)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
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)
|
return picker.viewData.handleCommand(self, command, data)
|
||||||
|
@ -354,7 +334,7 @@ func GetFilePickerAccept(view View, subviewID ...string) []string {
|
||||||
}
|
}
|
||||||
if ok {
|
if ok {
|
||||||
result := strings.Split(accept, ",")
|
result := strings.Split(accept, ",")
|
||||||
for i := 0; i < len(result); i++ {
|
for i := range len(result) {
|
||||||
result[i] = strings.Trim(result[i], " \t\n")
|
result[i] = strings.Trim(result[i], " \t\n")
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -159,7 +159,7 @@ func NewFilterProperty(params Params) FilterProperty {
|
||||||
func newFilterProperty(obj DataObject) FilterProperty {
|
func newFilterProperty(obj DataObject) FilterProperty {
|
||||||
filter := new(filterData)
|
filter := new(filterData)
|
||||||
filter.init()
|
filter.init()
|
||||||
for i := 0; i < obj.PropertyCount(); i++ {
|
for i := range obj.PropertyCount() {
|
||||||
if node := obj.Property(i); node != nil {
|
if node := obj.Property(i); node != nil {
|
||||||
tag := node.Tag()
|
tag := node.Tag()
|
||||||
switch node.Type() {
|
switch node.Type() {
|
||||||
|
|
|
@ -176,8 +176,7 @@ func (listLayout *listLayoutData) createContent() bool {
|
||||||
htmlID := listLayout.htmlID()
|
htmlID := listLayout.htmlID()
|
||||||
isDisabled := IsDisabled(listLayout)
|
isDisabled := IsDisabled(listLayout)
|
||||||
|
|
||||||
count := adapter.ListSize()
|
for i := range adapter.ListSize() {
|
||||||
for i := 0; i < count; i++ {
|
|
||||||
if view := adapter.ListItem(i, session); view != nil {
|
if view := adapter.ListItem(i, session); view != nil {
|
||||||
view.setParentID(htmlID)
|
view.setParentID(htmlID)
|
||||||
if isDisabled {
|
if isDisabled {
|
||||||
|
|
|
@ -428,7 +428,7 @@ func (listView *listViewData) ReloadListViewData() {
|
||||||
listView.itemFrame = make([]Frame, itemCount)
|
listView.itemFrame = make([]Frame, itemCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < itemCount; i++ {
|
for i := range itemCount {
|
||||||
listView.items[i] = adapter.ListItem(i, listView.Session())
|
listView.items[i] = adapter.ListItem(i, listView.Session())
|
||||||
}
|
}
|
||||||
} else if len(listView.items) > 0 {
|
} else if len(listView.items) > 0 {
|
||||||
|
@ -632,7 +632,7 @@ func (listView *listViewData) checkboxSubviews(adapter ListAdapter, buffer *stri
|
||||||
|
|
||||||
current := GetCurrent(listView)
|
current := GetCurrent(listView)
|
||||||
checkedItems := GetListViewCheckedItems(listView)
|
checkedItems := GetListViewCheckedItems(listView)
|
||||||
for i := 0; i < count; i++ {
|
for i := range count {
|
||||||
buffer.WriteString(`<div id="`)
|
buffer.WriteString(`<div id="`)
|
||||||
buffer.WriteString(listViewID)
|
buffer.WriteString(listViewID)
|
||||||
buffer.WriteRune('-')
|
buffer.WriteRune('-')
|
||||||
|
@ -693,7 +693,7 @@ func (listView *listViewData) noneCheckboxSubviews(adapter ListAdapter, buffer *
|
||||||
itemStyle := itemStyleBuilder.String()
|
itemStyle := itemStyleBuilder.String()
|
||||||
|
|
||||||
current := GetCurrent(listView)
|
current := GetCurrent(listView)
|
||||||
for i := 0; i < count; i++ {
|
for i := range count {
|
||||||
buffer.WriteString(`<div id="`)
|
buffer.WriteString(`<div id="`)
|
||||||
buffer.WriteString(listViewID)
|
buffer.WriteString(listViewID)
|
||||||
buffer.WriteRune('-')
|
buffer.WriteRune('-')
|
||||||
|
|
2
popup.go
2
popup.go
|
@ -740,7 +740,7 @@ func (popup *popupData) init(view View, popupParams Params) {
|
||||||
popupCellHeight = append(popupCellHeight, AutoSize())
|
popupCellHeight = append(popupCellHeight, AutoSize())
|
||||||
gap, _ := sizeConstant(session, "ruiPopupButtonGap")
|
gap, _ := sizeConstant(session, "ruiPopupButtonGap")
|
||||||
cellWidth := []SizeUnit{}
|
cellWidth := []SizeUnit{}
|
||||||
for i := 0; i < buttonCount; i++ {
|
for range buttonCount {
|
||||||
cellWidth = append(cellWidth, Fr(1))
|
cellWidth = append(cellWidth, Fr(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -121,7 +121,7 @@ func (properties *propertyList) writeToBuffer(buffer *strings.Builder,
|
||||||
|
|
||||||
func parseProperties(properties Properties, object DataObject) {
|
func parseProperties(properties Properties, object DataObject) {
|
||||||
count := object.PropertyCount()
|
count := object.PropertyCount()
|
||||||
for i := 0; i < count; i++ {
|
for i := range count {
|
||||||
if node := object.Property(i); node != nil {
|
if node := object.Property(i); node != nil {
|
||||||
switch node.Type() {
|
switch node.Type() {
|
||||||
case TextNode:
|
case TextNode:
|
||||||
|
|
|
@ -734,7 +734,7 @@ func (session *sessionData) handleSessionInfo(params DataObject) {
|
||||||
|
|
||||||
if node := params.PropertyByTag("storage"); node != nil && node.Type() == ObjectNode {
|
if node := params.PropertyByTag("storage"); node != nil && node.Type() == ObjectNode {
|
||||||
if obj := node.Object(); obj != nil {
|
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 {
|
if element := obj.Property(i); element.Type() == TextNode {
|
||||||
session.clientStorage[element.Tag()] = element.Text()
|
session.clientStorage[element.Tag()] = element.Text()
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ func loadStringResources(text string) {
|
||||||
table = map[string]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 {
|
if prop := obj.Property(i); prop != nil && prop.Type() == TextNode {
|
||||||
table[prop.Tag()] = prop.Text()
|
table[prop.Tag()] = prop.Text()
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ func loadStringResources(text string) {
|
||||||
|
|
||||||
tag := data.Tag()
|
tag := data.Tag()
|
||||||
if tag == "strings" {
|
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 {
|
if prop := data.Property(i); prop != nil && prop.Type() == ObjectNode {
|
||||||
parseStrings(prop.Object(), prop.Tag())
|
parseStrings(prop.Object(), prop.Tag())
|
||||||
}
|
}
|
||||||
|
|
|
@ -739,7 +739,7 @@ func (table *tableViewData) setFunc(tag PropertyName, value any) []PropertyName
|
||||||
|
|
||||||
case DataObject:
|
case DataObject:
|
||||||
params := Params{}
|
params := Params{}
|
||||||
for k := 0; k < value.PropertyCount(); k++ {
|
for k := range value.PropertyCount() {
|
||||||
if prop := value.Property(k); prop != nil && prop.Type() == TextNode {
|
if prop := value.Property(k); prop != nil && prop.Type() == TextNode {
|
||||||
params[PropertyName(prop.Tag())] = prop.Text()
|
params[PropertyName(prop.Tag())] = prop.Text()
|
||||||
}
|
}
|
||||||
|
@ -797,7 +797,7 @@ func (table *tableViewData) setFunc(tag PropertyName, value any) []PropertyName
|
||||||
if strings.Contains(value, ",") {
|
if strings.Contains(value, ",") {
|
||||||
if values := strings.Split(value, ","); len(values) == 2 {
|
if values := strings.Split(value, ","); len(values) == 2 {
|
||||||
var n = []int{0, 0}
|
var n = []int{0, 0}
|
||||||
for i := 0; i < 2; i++ {
|
for i := range 2 {
|
||||||
var err error
|
var err error
|
||||||
if n[i], err = strconv.Atoi(values[i]); err != nil {
|
if n[i], err = strconv.Atoi(values[i]); err != nil {
|
||||||
ErrorLog(err.Error())
|
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 {
|
objToStyle := func(obj DataObject) ViewStyle {
|
||||||
params := Params{}
|
params := Params{}
|
||||||
for i := 0; i < obj.PropertyCount(); i++ {
|
for i := range obj.PropertyCount() {
|
||||||
if node := obj.Property(i); node != nil {
|
if node := obj.Property(i); node != nil {
|
||||||
switch node.Type() {
|
switch node.Type() {
|
||||||
case ArrayNode:
|
case ArrayNode:
|
||||||
|
@ -712,14 +712,14 @@ func (theme *theme) addText(themeText string) bool {
|
||||||
return NewViewStyle(params)
|
return NewViewStyle(params)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < count; i++ {
|
for i := range count {
|
||||||
if d := data.Property(i); d != nil {
|
if d := data.Property(i); d != nil {
|
||||||
switch tag := d.Tag(); tag {
|
switch tag := d.Tag(); tag {
|
||||||
case "constants":
|
case "constants":
|
||||||
if d.Type() == ObjectNode {
|
if d.Type() == ObjectNode {
|
||||||
if obj := d.Object(); obj != nil {
|
if obj := d.Object(); obj != nil {
|
||||||
objCount := obj.PropertyCount()
|
objCount := obj.PropertyCount()
|
||||||
for k := 0; k < objCount; k++ {
|
for k := range objCount {
|
||||||
if prop := obj.Property(k); prop != nil && prop.Type() == TextNode {
|
if prop := obj.Property(k); prop != nil && prop.Type() == TextNode {
|
||||||
theme.constants[prop.Tag()] = prop.Text()
|
theme.constants[prop.Tag()] = prop.Text()
|
||||||
}
|
}
|
||||||
|
@ -731,7 +731,7 @@ func (theme *theme) addText(themeText string) bool {
|
||||||
if d.Type() == ObjectNode {
|
if d.Type() == ObjectNode {
|
||||||
if obj := d.Object(); obj != nil {
|
if obj := d.Object(); obj != nil {
|
||||||
objCount := obj.PropertyCount()
|
objCount := obj.PropertyCount()
|
||||||
for k := 0; k < objCount; k++ {
|
for k := range objCount {
|
||||||
if prop := obj.Property(k); prop != nil && prop.Type() == TextNode {
|
if prop := obj.Property(k); prop != nil && prop.Type() == TextNode {
|
||||||
theme.touchConstants[prop.Tag()] = prop.Text()
|
theme.touchConstants[prop.Tag()] = prop.Text()
|
||||||
}
|
}
|
||||||
|
@ -743,7 +743,7 @@ func (theme *theme) addText(themeText string) bool {
|
||||||
if d.Type() == ObjectNode {
|
if d.Type() == ObjectNode {
|
||||||
if obj := d.Object(); obj != nil {
|
if obj := d.Object(); obj != nil {
|
||||||
objCount := obj.PropertyCount()
|
objCount := obj.PropertyCount()
|
||||||
for k := 0; k < objCount; k++ {
|
for k := range objCount {
|
||||||
if prop := obj.Property(k); prop != nil && prop.Type() == TextNode {
|
if prop := obj.Property(k); prop != nil && prop.Type() == TextNode {
|
||||||
theme.colors[prop.Tag()] = prop.Text()
|
theme.colors[prop.Tag()] = prop.Text()
|
||||||
}
|
}
|
||||||
|
@ -755,7 +755,7 @@ func (theme *theme) addText(themeText string) bool {
|
||||||
if d.Type() == ObjectNode {
|
if d.Type() == ObjectNode {
|
||||||
if obj := d.Object(); obj != nil {
|
if obj := d.Object(); obj != nil {
|
||||||
objCount := obj.PropertyCount()
|
objCount := obj.PropertyCount()
|
||||||
for k := 0; k < objCount; k++ {
|
for k := range objCount {
|
||||||
if prop := obj.Property(k); prop != nil && prop.Type() == TextNode {
|
if prop := obj.Property(k); prop != nil && prop.Type() == TextNode {
|
||||||
theme.darkColors[prop.Tag()] = prop.Text()
|
theme.darkColors[prop.Tag()] = prop.Text()
|
||||||
}
|
}
|
||||||
|
@ -767,7 +767,7 @@ func (theme *theme) addText(themeText string) bool {
|
||||||
if d.Type() == ObjectNode {
|
if d.Type() == ObjectNode {
|
||||||
if obj := d.Object(); obj != nil {
|
if obj := d.Object(); obj != nil {
|
||||||
objCount := obj.PropertyCount()
|
objCount := obj.PropertyCount()
|
||||||
for k := 0; k < objCount; k++ {
|
for k := range objCount {
|
||||||
if prop := obj.Property(k); prop != nil && prop.Type() == TextNode {
|
if prop := obj.Property(k); prop != nil && prop.Type() == TextNode {
|
||||||
theme.images[prop.Tag()] = prop.Text()
|
theme.images[prop.Tag()] = prop.Text()
|
||||||
}
|
}
|
||||||
|
@ -779,7 +779,7 @@ func (theme *theme) addText(themeText string) bool {
|
||||||
if d.Type() == ObjectNode {
|
if d.Type() == ObjectNode {
|
||||||
if obj := d.Object(); obj != nil {
|
if obj := d.Object(); obj != nil {
|
||||||
objCount := obj.PropertyCount()
|
objCount := obj.PropertyCount()
|
||||||
for k := 0; k < objCount; k++ {
|
for k := range objCount {
|
||||||
if prop := obj.Property(k); prop != nil && prop.Type() == TextNode {
|
if prop := obj.Property(k); prop != nil && prop.Type() == TextNode {
|
||||||
theme.darkImages[prop.Tag()] = prop.Text()
|
theme.darkImages[prop.Tag()] = prop.Text()
|
||||||
}
|
}
|
||||||
|
@ -790,7 +790,7 @@ func (theme *theme) addText(themeText string) bool {
|
||||||
case "styles":
|
case "styles":
|
||||||
if d.Type() == ArrayNode {
|
if d.Type() == ArrayNode {
|
||||||
arraySize := d.ArraySize()
|
arraySize := d.ArraySize()
|
||||||
for k := 0; k < arraySize; k++ {
|
for k := range arraySize {
|
||||||
if element := d.ArrayElement(k); element != nil && element.IsObject() {
|
if element := d.ArrayElement(k); element != nil && element.IsObject() {
|
||||||
if obj := element.Object(); obj != nil {
|
if obj := element.Object(); obj != nil {
|
||||||
theme.styles[obj.Tag()] = objToStyle(obj)
|
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 d.Type() == ArrayNode && strings.HasPrefix(tag, "styles:") {
|
||||||
if rule, ok := parseMediaRule(tag); ok {
|
if rule, ok := parseMediaRule(tag); ok {
|
||||||
arraySize := d.ArraySize()
|
arraySize := d.ArraySize()
|
||||||
for k := 0; k < arraySize; k++ {
|
for k := range arraySize {
|
||||||
if element := d.ArrayElement(k); element != nil && element.IsObject() {
|
if element := d.ArrayElement(k); element != nil && element.IsObject() {
|
||||||
if obj := element.Object(); obj != nil {
|
if obj := element.Object(); obj != nil {
|
||||||
rule.styles[obj.Tag()] = objToStyle(obj)
|
rule.styles[obj.Tag()] = objToStyle(obj)
|
||||||
|
|
|
@ -155,7 +155,7 @@ func (event *TouchEvent) init(data DataObject) {
|
||||||
event.Touches = []Touch{}
|
event.Touches = []Touch{}
|
||||||
event.TimeStamp = getTimeStamp(data)
|
event.TimeStamp = getTimeStamp(data)
|
||||||
if node := data.PropertyByTag("touches"); node != nil && node.Type() == ArrayNode {
|
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 element := node.ArrayElement(i); element != nil && element.IsObject() {
|
||||||
if obj := element.Object(); obj != nil {
|
if obj := element.Object(); obj != nil {
|
||||||
var touch Touch
|
var touch Touch
|
||||||
|
|
|
@ -369,7 +369,7 @@ func valueToTransformProperty(value any) TransformProperty {
|
||||||
parseObject := func(obj DataObject) TransformProperty {
|
parseObject := func(obj DataObject) TransformProperty {
|
||||||
transform := NewTransformProperty(nil)
|
transform := NewTransformProperty(nil)
|
||||||
ok := true
|
ok := true
|
||||||
for i := 0; i < obj.PropertyCount(); i++ {
|
for i := range obj.PropertyCount() {
|
||||||
if prop := obj.Property(i); prop.Type() == TextNode {
|
if prop := obj.Property(i); prop.Type() == TextNode {
|
||||||
if !transform.Set(PropertyName(prop.Tag()), prop.Text()) {
|
if !transform.Set(PropertyName(prop.Tag()), prop.Text()) {
|
||||||
ok = false
|
ok = false
|
||||||
|
|
9
utils.go
9
utils.go
|
@ -105,3 +105,12 @@ func InlineImageFromResource(filename string) (string, bool) {
|
||||||
|
|
||||||
return "", false
|
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
|
package rui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"maps"
|
"maps"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -71,6 +72,12 @@ type View interface {
|
||||||
// HasFocus returns 'true' if the view has focus
|
// HasFocus returns 'true' if the view has focus
|
||||||
HasFocus() bool
|
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)
|
init(session Session)
|
||||||
handleCommand(self View, command PropertyName, data DataObject) bool
|
handleCommand(self View, command PropertyName, data DataObject) bool
|
||||||
htmlClass(disabled bool) string
|
htmlClass(disabled bool) string
|
||||||
|
@ -115,6 +122,7 @@ type viewData struct {
|
||||||
set func(tag PropertyName, value any) []PropertyName
|
set func(tag PropertyName, value any) []PropertyName
|
||||||
remove func(tag PropertyName) []PropertyName
|
remove func(tag PropertyName) []PropertyName
|
||||||
changed func(tag PropertyName)
|
changed func(tag PropertyName)
|
||||||
|
fileLoader map[string]func(FileInfo, []byte)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newView(session Session) View {
|
func newView(session Session) View {
|
||||||
|
@ -160,6 +168,7 @@ func (view *viewData) init(session Session) {
|
||||||
view.noResizeEvent = false
|
view.noResizeEvent = false
|
||||||
view.created = false
|
view.created = false
|
||||||
view.hasHtmlDisabled = false
|
view.hasHtmlDisabled = false
|
||||||
|
view.fileLoader = map[string]func(FileInfo, []byte){}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (view *viewData) Session() Session {
|
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"))
|
self.onResize(self, floatProperty("x"), floatProperty("y"), floatProperty("width"), floatProperty("height"))
|
||||||
return true
|
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:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ func setTransitionProperty(properties Properties, value any) bool {
|
||||||
return false
|
return false
|
||||||
|
|
||||||
case ArrayNode:
|
case ArrayNode:
|
||||||
for i := 0; i < value.ArraySize(); i++ {
|
for i := range value.ArraySize() {
|
||||||
if obj := value.ArrayElement(i).Object(); obj != nil {
|
if obj := value.ArrayElement(i).Object(); obj != nil {
|
||||||
if !setObject(obj) {
|
if !setObject(obj) {
|
||||||
return false
|
return false
|
||||||
|
@ -133,7 +133,7 @@ func setTransitionProperty(properties Properties, value any) bool {
|
||||||
|
|
||||||
case ArrayNode:
|
case ArrayNode:
|
||||||
result := true
|
result := true
|
||||||
for i := 0; i < value.ArraySize(); i++ {
|
for i := range value.ArraySize() {
|
||||||
if obj := value.ArrayElement(i).Object(); obj != nil {
|
if obj := value.ArrayElement(i).Object(); obj != nil {
|
||||||
result = setObject(obj) && result
|
result = setObject(obj) && result
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in New Issue