Added "drop-effect-allowed" property

This commit is contained in:
Alexei Anoshenko 2025-06-09 19:35:14 +03:00
parent c9744168ba
commit b76e3e56d8
5 changed files with 357 additions and 86 deletions

View File

@ -2144,10 +2144,10 @@ function dragAndDropEvent(element, event, tag) {
//event.preventDefault()
let message = tag + "{session=" + sessionID + ",id=" + element.id + mouseEventData(element, event);
if (event.dataTransfer) {
if (event.target) {
message += ",target=" + event.target.id;
}
if (event.dataTransfer) {
let dataText = ""
for (const item of event.dataTransfer.items) {
const data = event.dataTransfer.getData(item.type);
@ -2161,7 +2161,25 @@ function dragAndDropEvent(element, event, tag) {
if (dataText != "") {
message += ',data="' + dataText + '"';
}
dataText = ""
for (const file of event.dataTransfer.files) {
if (dataText != "") {
dataText += ";";
}
dataText += file.name;
}
if (dataText != "") {
message += ',files="' + dataText + '"';
}
if (event.dataTransfer.effectAllowed && event.dataTransfer.effectAllowed != "uninitialized") {
message += ',effect-allowed="' + event.dataTransfer.effectAllowed + '"';
}
if (event.dataTransfer.dropEffect) {
message += ',drop-effect="' + event.dataTransfer.dropEffect + '"';
}
}
message += "}";
sendMessage(message);
}
@ -2193,14 +2211,12 @@ function dragStartEvent(element, event) {
let img = new Image();
img.src = image;
event.dataTransfer.setDragImage(img, x, y);
let effect = element.getAttribute("data-drag-effect");
if (effect) {
event.dataTransfer.effectAllowed = effect;
}
}
// TODO drag effect
let allowed = element.getAttribute("data-drop-effect-allowed");
if (allowed) {
event.dataTransfer.effectAllowed = allowed;
}
dragAndDropEvent(element, event, "drag-start-event");
}
@ -2210,6 +2226,11 @@ function dragEndEvent(element, event) {
}
function dragEnterEvent(element, event) {
let effect = element.getAttribute("data-drop-effect");
if (effect) {
event.dataTransfer.dropEffect = effect;
}
dragAndDropEvent(element, event, "drag-enter-event")
}

View File

@ -39,7 +39,23 @@ const (
// Supported types: float, int, string.
DragImageYOffset PropertyName = "drag-image-y-offset"
// DragEffect is the constant for "drag-effect" property tag.
// DropEffect is the constant for "drag-effect" property tag.
//
// Used by View.
// Controls the feedback (typically visual) the user is given during a drag and drop operation.
// It will affect which cursor is displayed while dragging. For example, when the user hovers over a target drop element,
// the browser's cursor may indicate which type of operation will occur.
//
// Supported types: int, string.
//
// Values:
// - 0 (DropEffectUndefined) or "undefined" - The property value is not defined (defaut value).
// - 1 (DropEffectCopy) or "copy" - A copy of the source item may be made at the new location.
// - 2 (DropEffectMove) or "move" - An item may be moved to a new location.
// - 4 (DropEffectLink) or "link" - A link may be established to the source at the new location.
DropEffect PropertyName = "drag-effect"
// DropEffectAllowed is the constant for "drop-effect-allowed" property tag.
//
// Used by View.
// Specifies the effect that is allowed for a drag operation.
@ -52,15 +68,15 @@ const (
// Supported types: int, string.
//
// Values:
// - 0 (DragEffectAll) or "all" - All operations are permitted (defaut value).
// - 1 (DragEffectCopy) or "copy" - A copy of the source item may be made at the new location.
// - 2 (DragEffectMove) or "move" - An item may be moved to a new location.
// - 3 (DragEffectLink) or "link" - A link may be established to the source at the new location.
// - 4 (DragEffectCopyMove) or "copyMove" - A copy or move operation is permitted.
// - 5 (DragEffectCopyLink) or "copyLink" - A copy or link operation is permitted.
// - 6 (DragEffectLinkMove) or "linkMove" - A link or move operation is permitted.
// - 7 (DragEffectNone) or "none" - The item may not be dropped.
DragEffect PropertyName = "drag-effect"
// - 0 (DropEffectUndefined) or "undefined" - The property value is not defined (defaut value). Equivalent to DropEffectAll
// - 1 (DropEffectCopy) or "copy" - A copy of the source item may be made at the new location.
// - 2 (DropEffectMove) or "move" - An item may be moved to a new location.
// - 3 (DropEffectLink) or "link" - A link may be established to the source at the new location.
// - 4 (DropEffectCopyMove) or "copy|move" - A copy or move operation is permitted.
// - 5 (DropEffectCopyLink) or "copy|link" - A copy or link operation is permitted.
// - 6 (DropEffectLinkMove) or "link|move" - A link or move operation is permitted.
// - 7 (DropEffectAll) or "all" or "copy|move|link" - All operations are permitted.
DropEffectAllowed PropertyName = "drag-effect-allowed"
// DragStartEvent is the constant for "drag-start-event" property tag.
//
@ -116,36 +132,42 @@ const (
//
DropEvent PropertyName = "drop-event"
// DragEffectAll - the value of the "drag-effect" property: all operations (copy, move, and link) are permitted (defaut value).
DragEffectAll = 0
// DropEffectUndefined - the value of the "drop-effect" and "drop-effect-allowed" properties: the value is not defined (default value).
DropEffectUndefined = 0
// DragEffectCopy - the value of the "drag-effect" property: a copy of the source item may be made at the new location.
DragEffectCopy = 1
// DropEffectNone - the value of the DropEffect field of the DragEvent struct: the item may not be dropped.
DropEffectNone = 0
// DragEffectMove - the value of the "drag-effect" property: an item may be moved to a new location.
DragEffectMove = 2
// DropEffectCopy - the value of the "drop-effect" and "drop-effect-allowed" properties: a copy of the source item may be made at the new location.
DropEffectCopy = 1
// DragEffectLink - the value of the "drag-effect" property: a link may be established to the source at the new location.
DragEffectLink = 3
// DropEffectMove - the value of the "drop-effect" and "drop-effect-allowed" properties: an item may be moved to a new location.
DropEffectMove = 2
// DragEffectCopyMove - the value of the "drag-effect" property: a copy or move operation is permitted.
DragEffectCopyMove = 4
// DropEffectLink - the value of the "drop-effect" and "drop-effect-allowed" properties: a link may be established to the source at the new location.
DropEffectLink = 4
// DragEffectCopyLink - the value of the "drag-effect" property: a copy or link operation is permitted.
DragEffectCopyLink = 5
// DropEffectCopyMove - the value of the "drop-effect-allowed" property: a copy or move operation is permitted.
DropEffectCopyMove = DropEffectCopy + DropEffectMove
// DragEffectLinkMove - the value of the "drag-effect" property: a link or move operation is permitted.
DragEffectLinkMove = 6
// DropEffectCopyLink - the value of the "drop-effect-allowed" property: a copy or link operation is permitted.
DropEffectCopyLink = DropEffectCopy + DropEffectLink
// DragEffectNone - the value of the "drag-effect" property: the item may not be dropped.
DragEffectNone = 7
// DropEffectLinkMove - the value of the "drop-effect-allowed" property: a link or move operation is permitted.
DropEffectLinkMove = DropEffectLink + DropEffectMove
// DropEffectAll - the value of the "drop-effect-allowed" property: all operations (copy, move, and link) are permitted (defaut value).
DropEffectAll = DropEffectCopy + DropEffectMove + DropEffectLink
)
// MouseEvent represent a mouse event
type DragAndDropEvent struct {
MouseEvent
Data map[string]string
Files string
Target View
EffectAllowed int
DropEffect int
}
func (event *DragAndDropEvent) init(session Session, data DataObject) {
@ -175,6 +197,136 @@ func (event *DragAndDropEvent) init(session Session, data DataObject) {
if targetId, ok := data.PropertyValue("target"); ok {
event.Target = session.viewByHTMLID(targetId)
}
if effect, ok := data.PropertyValue("effect-allowed"); ok {
for i, value := range []string{"undefined", "copy", "move", "copyMove", "link", "copyLink", "linkMove", "all"} {
if value == effect {
event.EffectAllowed = i
break
}
}
}
if effect, ok := data.PropertyValue("drop-effect"); ok && effect != "" {
for i, value := range []string{"none", "copy", "move", "", "link"} {
if value == effect {
event.DropEffect = i
break
}
}
}
// TODO files
}
func stringToDropEffect(text string) (int, bool) {
text = strings.Trim(text, " \t\n")
if n, ok := enumStringToInt(text, []string{"", "copy", "move", "", "link"}, false); ok {
switch n {
case DropEffectUndefined, DropEffectCopy, DropEffectMove, DropEffectLink:
return n, true
}
}
return 0, false
}
func (view *viewData) setDropEffect(value any) []PropertyName {
if !setSimpleProperty(view, DropEffect, value) {
if text, ok := value.(string); ok {
if n, ok := stringToDropEffect(text); ok {
if n == DropEffectUndefined {
view.setRaw(DropEffect, nil)
} else {
view.setRaw(DropEffect, n)
}
} else {
invalidPropertyValue(DropEffect, value)
return nil
}
} else if i, ok := isInt(value); ok {
switch i {
case DropEffectUndefined:
view.setRaw(DropEffect, nil)
case DropEffectCopy, DropEffectMove, DropEffectLink:
view.setRaw(DropEffect, i)
default:
invalidPropertyValue(DropEffect, value)
return nil
}
} else {
notCompatibleType(DropEffect, value)
return nil
}
}
return []PropertyName{DropEffect}
}
func stringToDropEffectAllowed(text string) (int, bool) {
if strings.Contains(text, "|") {
elements := strings.Split(text, "|")
result := 0
for _, element := range elements {
if n, ok := stringToDropEffect(element); ok && n != DropEffectUndefined {
result |= n
} else {
return 0, false
}
}
return result, true
}
text = strings.Trim(text, " \t\n")
if text != "" {
if n, ok := enumStringToInt(text, []string{"undefined", "copy", "move", "", "link", "", "", "all"}, false); ok {
return n, true
}
}
return 0, false
}
func (view *viewData) setDropEffectAllowed(value any) []PropertyName {
if !setSimpleProperty(view, DropEffectAllowed, value) {
if text, ok := value.(string); ok {
if n, ok := stringToDropEffectAllowed(text); ok {
if n == DropEffectUndefined {
view.setRaw(DropEffectAllowed, nil)
} else {
view.setRaw(DropEffectAllowed, n)
}
} else {
invalidPropertyValue(DropEffectAllowed, value)
return nil
}
} else {
n, ok := isInt(value)
if !ok {
notCompatibleType(DropEffectAllowed, value)
return nil
}
if n == DropEffectUndefined {
view.setRaw(DropEffectAllowed, nil)
} else if n > DropEffectUndefined && n <= DropEffectAll {
view.setRaw(DropEffectAllowed, n)
} else {
notCompatibleType(DropEffectAllowed, value)
return nil
}
}
}
return []PropertyName{DropEffectAllowed}
}
func handleDragAndDropEvents(view View, tag PropertyName, data DataObject) {
@ -227,7 +379,26 @@ func dragAndDropHtml(view View, buffer *strings.Builder) {
buffer.WriteString(` ondragstart="dragStartEvent(this, event)" `)
}
enterEvent := false
switch GetDropEffect(view) {
case DropEffectCopy:
buffer.WriteString(` data-drop-effect="copy" ondragenter="dragEnterEvent(this, event)"`)
enterEvent = true
case DropEffectMove:
buffer.WriteString(` data-drop-effect="move" ondragenter="dragEnterEvent(this, event)"`)
enterEvent = true
case DropEffectLink:
buffer.WriteString(` data-drop-effect="link" ondragenter="dragEnterEvent(this, event)"`)
enterEvent = true
}
if enterEvent {
viewEventsHtml[DragAndDropEvent](view, []PropertyName{DragEndEvent, DragLeaveEvent}, buffer)
} else {
viewEventsHtml[DragAndDropEvent](view, []PropertyName{DragEndEvent, DragEnterEvent, DragLeaveEvent}, buffer)
}
session := view.Session()
if img, ok := stringProperty(view, DragImage, session); ok && img != "" {
@ -257,9 +428,9 @@ func dragAndDropHtml(view View, buffer *strings.Builder) {
buffer.WriteString(`" `)
}
effects := enumProperties[DragEffect].cssValues
if n := GetDragEffect(view); n > 0 && n < len(effects) {
buffer.WriteString(` data-drag-effect="`)
effects := []string{"undifined", "copy", "move", "copyMove", "link", "copyLink", "linkMove", "all"}
if n := GetDropEffectAllowed(view); n > 0 && n < len(effects) {
buffer.WriteString(` data-drop-effect-allowed="`)
buffer.WriteString(effects[n])
buffer.WriteString(`" `)
}
@ -326,21 +497,84 @@ func GetDragImageYOffset(view View, subviewID ...string) float64 {
return floatStyledProperty(view, subviewID, DragImageYOffset, 0)
}
// GetDragEffect returns the effect that is allowed for a drag operation.
// GetDropEffect returns the effect that is allowed for a drag operation.
// Controls the feedback (typically visual) the user is given during a drag and drop operation.
// It will affect which cursor is displayed while dragging.
//
// Returns one of next values:
// - 0 (DropEffectUndefined) - The value is not defined (all operations are permitted).
// - 1 (DropEffectCopy) - A copy of the source item may be made at the new location.
// - 2 (DropEffectMove) - An item may be moved to a new location.
// - 4 (DropEffectLink) - A link may be established to the source at the new location.
//
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetDropEffect(view View, subviewID ...string) int {
if view = getSubview(view, subviewID); view != nil {
value := view.getRaw(DropEffect)
if value == nil {
value = valueFromStyle(view, DropEffect)
}
if value != nil {
switch value := value.(type) {
case int:
return value
case string:
if value, ok := view.Session().resolveConstants(value); ok {
if n, ok := stringToDropEffect(value); ok {
return n
}
}
default:
return DropEffectUndefined
}
}
}
return DropEffectUndefined
}
// GetDropEffectAllowed returns the effect that is allowed for a drag operation.
// The copy operation is used to indicate that the data being dragged will be copied from its present location to the drop location.
// The move operation is used to indicate that the data being dragged will be moved,
// and the link operation is used to indicate that some form of relationship
// or connection will be created between the source and drop locations.. Returns one of next values:
// - 0 (DragEffectAll) or "all" - All operations are permitted (defaut value).
// - 1 (DragEffectCopy) or "copy" - A copy of the source item may be made at the new location.
// - 2 (DragEffectMove) or "move" - An item may be moved to a new location.
// - 3 (DragEffectLink) or "link" - A link may be established to the source at the new location.
// - 4 (DragEffectCopyMove) or "copyMove" - A copy or move operation is permitted.
// - 5 (DragEffectCopyLink) or "copyLink" - A copy or link operation is permitted.
// - 6 (DragEffectLinkMove) or "linkMove" - A link or move operation is permitted.
// - 7 (DragEffectNone) or "none" - The item may not be dropped.
// or connection will be created between the source and drop locations.
//
// Returns one of next values:
// - 0 (DropEffectUndefined) - The value is not defined (all operations are permitted).
// - 1 (DropEffectCopy) - A copy of the source item may be made at the new location.
// - 2 (DropEffectMove) - An item may be moved to a new location.
// - 4 (DropEffectLink) - A link may be established to the source at the new location.
// - 3 (DropEffectCopyMove) - A copy or move operation is permitted.
// - 5 (DropEffectCopyLink) - A copy or link operation is permitted.
// - 6 (DropEffectLinkMove) - A link or move operation is permitted.
// - 7 (DropEffectAll) - All operations are permitted.
//
// If the second argument (subviewID) is not specified or it is "" then a value from the first argument (view) is returned.
func GetDragEffect(view View, subviewID ...string) int {
return enumStyledProperty(view, subviewID, DragEffect, DragEffectAll, true)
func GetDropEffectAllowed(view View, subviewID ...string) int {
if view = getSubview(view, subviewID); view != nil {
value := view.getRaw(DropEffectAllowed)
if value == nil {
value = valueFromStyle(view, DropEffectAllowed)
}
if value != nil {
switch value := value.(type) {
case int:
return value
case string:
if value, ok := view.Session().resolveConstants(value); ok {
if n, ok := stringToDropEffectAllowed(value); ok {
return n
}
}
default:
return DropEffectUndefined
}
}
}
return DropEffectUndefined
}

View File

@ -261,9 +261,9 @@ func (edit *editViewData) AppendText(text string) {
return
}
}
edit.setRaw(Text, text)
edit.Set(Text, text)
} else {
edit.setRaw(Text, GetText(edit)+text)
edit.Set(Text, GetText(edit)+text)
}
}

View File

@ -496,11 +496,6 @@ var enumProperties = map[PropertyName]enumPropertyData{
string(ColumnFill),
[]string{"balance", "auto"},
},
DragEffect: {
[]string{"all", "copy", "move", "link", "copyMove", "copyLink", "linkMove", "none"},
"",
[]string{"all", "copy", "move", "link", "copyMove", "copyLink", "linkMove", "none"},
},
}
func notCompatibleType(tag PropertyName, value any) {

73
view.go
View File

@ -479,6 +479,11 @@ func (view *viewData) setFunc(tag PropertyName, value any) []PropertyName {
case DragStartEvent, DragEndEvent, DragEnterEvent, DragLeaveEvent, DragOverEvent, DropEvent:
return setOneArgEventListener[View, DragAndDropEvent](view, tag, value)
case DropEffect:
return view.setDropEffect(value)
case DropEffectAllowed:
return view.setDropEffectAllowed(value)
}
return viewStyleSet(view, tag, value)
@ -509,16 +514,16 @@ func (view *viewData) propertyChanged(tag PropertyName) {
switch tag {
case TabIndex:
if value, ok := intProperty(view, TabIndex, view.Session(), 0); ok {
session.updateProperty(view.htmlID(), "tabindex", strconv.Itoa(value))
if value, ok := intProperty(view, TabIndex, session, 0); ok {
session.updateProperty(htmlID, "tabindex", strconv.Itoa(value))
} else if view.Focusable() {
session.updateProperty(view.htmlID(), "tabindex", "0")
session.updateProperty(htmlID, "tabindex", "0")
} else {
session.updateProperty(view.htmlID(), "tabindex", "-1")
session.updateProperty(htmlID, "tabindex", "-1")
}
case Style, StyleDisabled:
session.updateProperty(view.htmlID(), "class", view.htmlClass(IsDisabled(view)))
session.updateProperty(htmlID, "class", view.htmlClass(IsDisabled(view)))
case Disabled:
tabIndex := GetTabIndex(view, htmlID)
@ -775,7 +780,7 @@ func (view *viewData) propertyChanged(tag PropertyName) {
case PerspectiveOriginX, PerspectiveOriginY:
x, y := GetPerspectiveOrigin(view)
session.updateCSSProperty(htmlID, "perspective-origin", transformOriginCSS(x, y, AutoSize(), view.Session()))
session.updateCSSProperty(htmlID, "perspective-origin", transformOriginCSS(x, y, AutoSize(), session))
case BackfaceVisible:
if GetBackfaceVisible(view) {
@ -786,7 +791,7 @@ func (view *viewData) propertyChanged(tag PropertyName) {
case TransformOriginX, TransformOriginY, TransformOriginZ:
x, y, z := getTransformOrigin(view, session)
session.updateCSSProperty(htmlID, "transform-origin", transformOriginCSS(x, y, z, view.Session()))
session.updateCSSProperty(htmlID, "transform-origin", transformOriginCSS(x, y, z, session))
case Transform:
css := ""
@ -813,7 +818,7 @@ func (view *viewData) propertyChanged(tag PropertyName) {
if view.getRaw(DragStartEvent) != nil || view.getRaw(DragData) != nil {
session.updateProperty(htmlID, "ondragstart", "dragStartEvent(this, event)")
} else {
session.removeProperty(view.htmlID(), "ondragstart")
session.removeProperty(htmlID, "ondragstart")
}
case DropEvent:
@ -823,18 +828,18 @@ func (view *viewData) propertyChanged(tag PropertyName) {
if view.getRaw(DragOverEvent) != nil {
session.updateProperty(htmlID, "data-drag-over", "1")
} else {
session.removeProperty(view.htmlID(), "data-drag-over")
session.removeProperty(htmlID, "data-drag-over")
}
} else {
session.removeProperty(view.htmlID(), "ondrop")
session.removeProperty(view.htmlID(), "ondragover")
session.removeProperty(htmlID, "ondrop")
session.removeProperty(htmlID, "ondragover")
}
case DragOverEvent:
if view.getRaw(DragOverEvent) != nil {
session.updateProperty(htmlID, "data-drag-over", "1")
} else {
session.removeProperty(view.htmlID(), "data-drag-over")
session.removeProperty(htmlID, "data-drag-over")
}
case DragData:
@ -843,50 +848,66 @@ func (view *viewData) propertyChanged(tag PropertyName) {
session.updateProperty(htmlID, "data-drag", data)
session.updateProperty(htmlID, "ondragstart", "dragStartEvent(this, event)")
} else {
session.removeProperty(view.htmlID(), "draggable")
session.removeProperty(view.htmlID(), "data-drag")
session.removeProperty(htmlID, "draggable")
session.removeProperty(htmlID, "data-drag")
if view.getRaw(DragStartEvent) == nil {
session.removeProperty(view.htmlID(), "ondragstart")
session.removeProperty(htmlID, "ondragstart")
}
}
case DragImage:
if img, ok := stringProperty(view, DragImage, view.session); ok && img != "" {
if img, ok := stringProperty(view, DragImage, session); ok && img != "" {
img = strings.Trim(img, " \t")
if img[0] == '@' {
img, ok = view.session.ImageConstant(img[1:])
img, ok = session.ImageConstant(img[1:])
if !ok {
session.removeProperty(view.htmlID(), "data-drag-image")
session.removeProperty(htmlID, "data-drag-image")
return
}
}
session.updateProperty(htmlID, "data-drag-image", img)
} else {
session.removeProperty(view.htmlID(), "data-drag-image")
session.removeProperty(htmlID, "data-drag-image")
}
case DragImageXOffset:
if f := GetDragImageXOffset(view); f != 0 {
session.updateProperty(htmlID, "data-drag-image-x", f)
} else {
session.removeProperty(view.htmlID(), "data-drag-image-x")
session.removeProperty(htmlID, "data-drag-image-x")
}
case DragImageYOffset:
if f := GetDragImageXOffset(view); f != 0 {
session.updateProperty(htmlID, "data-drag-image-y", f)
} else {
session.removeProperty(view.htmlID(), "data-drag-image-y")
session.removeProperty(htmlID, "data-drag-image-y")
}
case DragEffect:
effects := enumProperties[DragEffect].cssValues
if n := GetDragEffect(view); n > 0 && n < len(effects) {
session.updateProperty(htmlID, "data-drag-effect", effects[n])
case DropEffect:
effect := GetDropEffect(view)
switch effect {
case DropEffectCopy:
session.updateProperty(htmlID, "data-drop-effect", "copy")
case DropEffectMove:
session.updateProperty(htmlID, "data-drop-effect", "move")
case DropEffectLink:
session.updateProperty(htmlID, "data-drop-effect", "link")
default:
session.removeProperty(htmlID, "data-drop-effect")
}
case DropEffectAllowed:
effect := GetDropEffectAllowed(view)
if effect >= DropEffectCopy && effect >= DropEffectAll {
values := []string{"undifined", "copy", "move", "copyMove", "link", "copyLink", "linkMove", "all"}
session.updateProperty(htmlID, "data-drop-effect-allowed", values[effect])
} else {
session.removeProperty(htmlID, "data-drop-effect-allowed")
}
case DataList:
updateInnerHTML(view.htmlID(), view.Session())
updateInnerHTML(htmlID, session)
case Opacity:
if f, ok := floatTextProperty(view, Opacity, session, 0); ok {