Skip to content
This repository has been archived by the owner on Dec 15, 2022. It is now read-only.

Restrict drag-and-drop actions to events from the Tree View #1293

Merged
merged 6 commits into from
Apr 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions lib/root-drag-and-drop.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class RootDragAndDropHandler
return unless @treeView.list.contains(e.target)

@prevDropTargetIndex = null
e.dataTransfer.setData 'atom-tree-view-event', 'true'
e.dataTransfer.setData 'atom-tree-view-root-event', 'true'
projectRoot = e.target.closest('.project-root')
directory = projectRoot.directory

Expand All @@ -54,26 +54,27 @@ class RootDragAndDropHandler

onDragEnter: (e) ->
return unless @treeView.list.contains(e.target)
return unless @isAtomTreeViewEvent(e)

e.stopPropagation()

onDragLeave: (e) =>
return unless @treeView.list.contains(e.target)
return unless @isAtomTreeViewEvent(e)

e.stopPropagation()
@removePlaceholder() if e.target is e.currentTarget

onDragEnd: (e) =>
return unless e.target.matches('.project-root-header')
return unless @isAtomTreeViewEvent(e)

e.stopPropagation()
@clearDropTarget()

onDragOver: (e) =>
return unless @treeView.list.contains(e.target)

unless @isAtomTreeViewEvent(e)
return
return unless @isAtomTreeViewEvent(e)

e.preventDefault()
e.stopPropagation()
Expand Down Expand Up @@ -115,14 +116,13 @@ class RootDragAndDropHandler

onDrop: (e) =>
return unless @treeView.list.contains(e.target)
return unless @isAtomTreeViewEvent(e)

e.preventDefault()
e.stopPropagation()

{dataTransfer} = e

return unless @isAtomTreeViewEvent(e)

fromWindowId = parseInt(dataTransfer.getData('from-window-id'))
fromRootPath = dataTransfer.getData('from-root-path')
fromIndex = parseInt(dataTransfer.getData('project-root-index'))
Expand Down Expand Up @@ -179,7 +179,7 @@ class RootDragAndDropHandler

isAtomTreeViewEvent: (e) ->
for item in e.dataTransfer.items
if item.type is 'atom-tree-view-event'
if item.type is 'atom-tree-view-root-event'
return true

return false
Expand Down
12 changes: 12 additions & 0 deletions lib/tree-view.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -1037,6 +1037,7 @@ class TreeView
onDragEnter: (e) =>
if entry = e.target.closest('.entry.directory')
return if @rootDragAndDrop.isDragging(e)
return unless @isAtomTreeViewEvent(e)

e.stopPropagation()

Expand All @@ -1049,6 +1050,7 @@ class TreeView
onDragLeave: (e) =>
if entry = e.target.closest('.entry.directory')
return if @rootDragAndDrop.isDragging(e)
return unless @isAtomTreeViewEvent(e)

e.stopPropagation()

Expand Down Expand Up @@ -1096,6 +1098,7 @@ class TreeView
e.dataTransfer.effectAllowed = "move"
e.dataTransfer.setDragImage(dragImage, 0, 0)
e.dataTransfer.setData("initialPaths", initialPaths)
e.dataTransfer.setData("atom-tree-view-event", "true")

window.requestAnimationFrame ->
dragImage.remove()
Expand All @@ -1104,6 +1107,7 @@ class TreeView
onDragOver: (e) ->
if entry = e.target.closest('.entry.directory')
return if @rootDragAndDrop.isDragging(e)
return unless @isAtomTreeViewEvent(e)

e.preventDefault()
e.stopPropagation()
Expand All @@ -1116,6 +1120,7 @@ class TreeView
@dragEventCounts = new WeakMap
if entry = e.target.closest('.entry.directory')
return if @rootDragAndDrop.isDragging(e)
return unless @isAtomTreeViewEvent(e)

e.preventDefault()
e.stopPropagation()
Expand Down Expand Up @@ -1149,5 +1154,12 @@ class TreeView
# Drop event from OS that isn't targeting a folder: add a new project folder
atom.project.addPath(entry.path) for entry in e.dataTransfer.files

isAtomTreeViewEvent: (e) ->
for item in e.dataTransfer.items
if item.type is 'atom-tree-view-event' or item.kind is 'file'
return true

return false

isVisible: ->
@element.offsetWidth isnt 0 or @element.offsetHeight isnt 0
22 changes: 19 additions & 3 deletions spec/event-helpers.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ module.exports.buildInternalDragEvents = (dragged, enterTarget, dropTarget, tree
data: {}
setData: (key, value) -> @data[key] = "#{value}" # Drag events stringify data values
getData: (key) -> @data[key]
clearData: (key) ->
if key
delete @data[key]
else
@data = {}
setDragImage: (@image) -> return

Object.defineProperty(
Expand Down Expand Up @@ -38,13 +43,18 @@ module.exports.buildExternalDropEvent = (filePaths, dropTarget) ->
data: {}
setData: (key, value) -> @data[key] = "#{value}" # Drag events stringify data values
getData: (key) -> @data[key]
clearData: (key) ->
if key
delete @data[key]
else
@data = {}
files: []

Object.defineProperty(
dataTransfer,
'items',
get: ->
Object.keys(dataTransfer.data).map((key) -> {type: key})
Object.keys(dataTransfer.data).map((key) -> {type: key, kind: 'file'})
)

dropEvent = new DragEvent('drop')
Expand All @@ -54,6 +64,7 @@ module.exports.buildExternalDropEvent = (filePaths, dropTarget) ->

for filePath in filePaths
dropEvent.dataTransfer.files.push({path: filePath})
dropEvent.dataTransfer.setData(filePath, 'bla') # Not technically correct, but gets the job done

dropEvent

Expand All @@ -63,7 +74,7 @@ buildElementPositionalDragEvents = (el, dataTransfer, currentTargetSelector) ->

currentTarget = if currentTargetSelector then el.closest(currentTargetSelector) else el

topEvent = new DragEvent('dragstart')
topEvent = new DragEvent('dragover')
Object.defineProperty(topEvent, 'target', value: el)
Object.defineProperty(topEvent, 'currentTarget', value: currentTarget)
Object.defineProperty(topEvent, 'dataTransfer', value: dataTransfer)
Expand All @@ -75,7 +86,7 @@ buildElementPositionalDragEvents = (el, dataTransfer, currentTargetSelector) ->
Object.defineProperty(middleEvent, 'dataTransfer', value: dataTransfer)
Object.defineProperty(middleEvent, 'pageY', value: el.getBoundingClientRect().top + el.offsetHeight * 0.5)

bottomEvent = new DragEvent('dragend')
bottomEvent = new DragEvent('dragover')
Object.defineProperty(bottomEvent, 'target', value: el)
Object.defineProperty(bottomEvent, 'currentTarget', value: currentTarget)
Object.defineProperty(bottomEvent, 'dataTransfer', value: dataTransfer)
Expand All @@ -89,6 +100,11 @@ module.exports.buildPositionalDragEvents = (dragged, target, currentTargetSelect
data: {}
setData: (key, value) -> @data[key] = "#{value}" # Drag events stringify data values
getData: (key) -> @data[key]
clearData: (key) ->
if key
delete @data[key]
else
@data = {}
setDragImage: (@image) -> return

Object.defineProperty(
Expand Down
64 changes: 57 additions & 7 deletions spec/tree-view-package-spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -4245,9 +4245,8 @@ describe "TreeView", ->

dropEvent = eventHelpers.buildExternalDropEvent([deltaFilePath], alphaDir)

runs ->
treeView.onDrop(dropEvent)
expect(alphaDir.children.length).toBe 2
treeView.onDrop(dropEvent)
expect(alphaDir.children.length).toBe 2

waitsFor "directory view contents to refresh", ->
findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length > 2
Expand Down Expand Up @@ -4279,9 +4278,8 @@ describe "TreeView", ->

dropEvent = eventHelpers.buildExternalDropEvent([deltaFilePath, gammaDirPath], alphaDir)

runs ->
treeView.onDrop(dropEvent)
expect(alphaDir.children.length).toBe 2
treeView.onDrop(dropEvent)
expect(alphaDir.children.length).toBe 2

waitsFor "directory view contents to refresh", ->
findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length > 3
Expand Down Expand Up @@ -4555,6 +4553,35 @@ describe "TreeView", ->
expect(fs.existsSync(onlyNewDirPath)).toBe false
expect(fs.existsSync(newAlphaDirPath)).toBe false

describe "when the event does not originate from the Tree View", ->
it "does nothing", ->
alphaDir = findDirectoryContainingText(treeView.roots[0], 'alpha')
alphaDir.expand()

gammaDir = findDirectoryContainingText(treeView.roots[0], 'gamma')
gammaDir.expand()
deltaFile = gammaDir.entries.children[1]

[dragStartEvent, dragEnterEvent, dropEvent] =
eventHelpers.buildInternalDragEvents([deltaFile], alphaDir.querySelector('.header'), alphaDir, treeView)
treeView.onDragStart(dragStartEvent)
dragEnterEvent.dataTransfer.clearData('atom-tree-view-event')
dropEvent.dataTransfer.clearData('atom-tree-view-event')

treeView.onDragEnter(dragEnterEvent)
expect(alphaDir).not.toHaveClass('selected')

treeView.onDragEnter(dragEnterEvent)
treeView.onDragLeave(dragEnterEvent)
expect(alphaDir).not.toHaveClass('selected')

treeView.onDragLeave(dragEnterEvent)
expect(alphaDir).not.toHaveClass('selected')

spyOn(treeView, 'moveEntry')
treeView.onDrop(dropEvent)
expect(treeView.moveEntry).not.toHaveBeenCalled()

describe "the alwaysOpenExisting config option", ->
it "defaults to unset", ->
expect(atom.config.get("tree-view.alwaysOpenExisting")).toBeFalsy()
Expand Down Expand Up @@ -4826,7 +4853,7 @@ describe "TreeView", ->
[_, dragDropEvents] = eventHelpers.buildPositionalDragEvents(null, alphaDir.querySelector('.project-root-header'), '.tree-view')

dropEvent = dragDropEvents.bottom
dropEvent.dataTransfer.setData('atom-tree-view-event', true)
dropEvent.dataTransfer.setData('atom-tree-view-root-event', true)
dropEvent.dataTransfer.setData('from-window-id', treeView.rootDragAndDrop.getWindowId() + 1)
dropEvent.dataTransfer.setData('from-root-path', etaDirPath)

Expand Down Expand Up @@ -4855,6 +4882,29 @@ describe "TreeView", ->
expect(atom.project.getPaths()).toEqual [alphaDirPath, thetaDirPath]
expect(document.querySelector('.placeholder')).not.toExist()

describe "when the event does not originate from the Tree View", ->
it "does nothing", ->
alphaDir = treeView.roots[0]
gammaDir = treeView.roots[1]
[dragStartEvent, dragOverEvents, dragEndEvent] =
eventHelpers.buildPositionalDragEvents(gammaDir.querySelector('.project-root-header'), alphaDir, '.tree-view')

treeView.rootDragAndDrop.onDragStart(dragStartEvent)
dragStartEvent.dataTransfer.clearData('atom-tree-view-root-event')
dragOverEvents.top.dataTransfer.clearData('atom-tree-view-root-event')
dragEndEvent.dataTransfer.clearData('atom-tree-view-root-event')

treeView.rootDragAndDrop.onDragOver(dragOverEvents.top)
expect(alphaDir.previousSibling).not.toHaveClass('placeholder')

treeView.rootDragAndDrop.onDrop(dragOverEvents.top)
projectPaths = atom.project.getPaths()
expect(projectPaths[0]).toEqual(alphaDirPath)
expect(projectPaths[1]).toEqual(gammaDirPath)

treeView.rootDragAndDrop.onDragEnd(dragEndEvent)
expect(document.querySelector('.placeholder')).not.toExist()

describe "when the active file path does not exist in the project", ->
it "deselects all entries", ->
nonProjectPath = path.join(temp.mkdirSync(), 'new-file.txt')
Expand Down